mirror of
https://github.com/hanzoai/mobile.git
synced 2026-10-30 14:53:17 +00:00
46 lines
1.6 KiB
TypeScript
46 lines
1.6 KiB
TypeScript
import native from '../modules/widget'
|
|
import * as runs from './runs'
|
|
|
|
// Home-screen widget state. The native half (modules/widget) persists
|
|
// {snippet, updatedAt} where the platform widget reads it — the App Group on
|
|
// iOS, app prefs on Android — and asks for a redraw; wherever the native
|
|
// module is absent — web, Expo Go — every call is a silent no-op so callers
|
|
// never branch on platform.
|
|
|
|
export type State = { snippet: string; updatedAt: number }
|
|
|
|
export async function setState(state: State): Promise<void> {
|
|
if (!native) return
|
|
try {
|
|
native.set(JSON.stringify(state))
|
|
} catch {
|
|
// A stale widget must never surface as an app failure.
|
|
}
|
|
}
|
|
|
|
// The chat screen calls this after each assistant turn. The widget shows two
|
|
// or three lines, so only the head of the text matters; whitespace collapses
|
|
// because answers are often markdown with hard newlines.
|
|
export function setLastMessage(text: string): void {
|
|
const snippet = text.trim().replace(/\s+/g, ' ').slice(0, 200)
|
|
if (!snippet) return
|
|
void setState({ snippet, updatedAt: Date.now() })
|
|
}
|
|
|
|
// While the app is doing long work the widget follows the run registry. The
|
|
// registry emits on every patch and WidgetKit reload budgets are real, so
|
|
// only a changed line is pushed; a finished run writes nothing, leaving the
|
|
// last message in place.
|
|
let shown = ''
|
|
|
|
export function follow(): () => void {
|
|
return runs.subscribe(() => {
|
|
const run = runs.active()
|
|
if (!run) return
|
|
const line = `${run.title} — ${run.phase}`
|
|
if (line === shown) return
|
|
shown = line
|
|
void setState({ snippet: line, updatedAt: Date.now() })
|
|
})
|
|
}
|