@mihe-dev/js 0.2.6 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../protocol/src/version.ts", "../../../js/src/client.ts", "../../../js/src/widget/format.ts", "../../../js/src/card-view.ts", "../../../js/src/highlight.ts", "../../../js/src/commands.ts", "../../../js/src/debug.ts", "../../../js/src/chart.ts", "../../../js/src/declarations.ts", "../../../js/src/store.ts", "../../../js/src/audio.ts", "../../../js/src/voice.ts", "../../../js/src/mihe.ts", "../../../js/src/widget/dom.ts", "../../../js/src/widget/mark.ts", "../../../js/src/widget/styles.ts", "../../../js/src/widget/launcher.ts", "../../../js/src/widget/card.ts", "../../../js/src/widget/icons.ts", "../../../js/src/widget/orb.ts", "../../../js/src/markdown.ts", "../../../js/src/widget/markdown.ts", "../../../js/src/widget/panel.ts", "../../../js/src/widget/element.ts"],
4
- "sourcesContent": ["/**\n * Wire protocol version, sent by both sides in the handshake.\n *\n * Must match `protocol::PROTOCOL_VERSION` in the runtime. Additive changes\n * (new optional fields, new message variants) do not bump this \u2014 receivers\n * ignore unknown fields and reject unknown message types without closing the\n * socket. Breaking changes bump it.\n */\nexport const PROTOCOL_VERSION = 1;\n\n/**\n * Sample rate of every audio frame on the voice socket, in both directions.\n *\n * Must match `protocol::VOICE_SAMPLE_RATE` in the runtime, which\n * scripts/sync-protocol.sh checks. It is the provider's native rate, so\n * agreeing on it here is what keeps a resampler out of a latency-critical\n * path \u2014 and disagreeing does not error, it just plays the assistant at the\n * wrong pitch, which is why the check exists.\n */\nexport const VOICE_SAMPLE_RATE = 24000;\n", "/**\n * The WebSocket client (doc 02).\n *\n * One persistent socket per session carries chat, commands, and\n * confirmations. Two properties drive the design:\n *\n * - A frontend action is a tool call that round-trips through the browser, so\n * the runtime pushes a command and blocks until this client acks it. Every\n * command MUST be answered, including on failure \u2014 an unanswered command\n * stalls the assistant's turn until the runtime's timeout fires.\n * - Confirming an action needs server-to-client push, which is why this is a\n * socket rather than a request per message.\n *\n * The client owns transport only. It never decides what a command means; the\n * provider does that and hands back a result.\n */\n\nimport type { ContextReport } from \"./debug\";\nimport {\n PROTOCOL_VERSION,\n type AssistantCommand,\n type ClientMessage,\n type KnowledgeSourceRef,\n type ProposalStatus,\n type ScreenContext,\n type ServerMessage,\n type WidgetSettings,\n} from \"@mihe-dev/protocol\";\n\nexport type ConnectionState =\n | \"idle\"\n | \"connecting\"\n | \"open\"\n | \"reconnecting\"\n | \"closed\";\n\n/** What the provider needs to react to. */\nexport type ClientEvent =\n | { type: \"state\"; state: ConnectionState }\n /**\n * Answered on every handshake, so it cannot drift out of step with the\n * connection the way a one-shot probe could.\n */\n | { type: \"voice_available\"; available: boolean }\n /**\n * What the assistant calls itself, from the app's settings.\n *\n * Re-sent on every handshake for the same reason `voice_available` is: a\n * rename takes effect on the next session rather than on the next deploy of\n * the host app.\n */\n | { type: \"assistant_name\"; name: string }\n /**\n * The customer's launcher icon from the app's settings, or `null` for the\n * Mihe mark. Emitted on every handshake, absent or not: unlike the name,\n * removing an icon has to take effect, and an absent field is how the\n * runtime says \"the default\".\n */\n | { type: \"launcher_icon\"; url: string | null }\n /**\n * How the widget presents itself, from the app's settings (amendment 61).\n * `null` from a runtime too old to send any, which leaves the SDK's own\n * defaults and the host's props in charge.\n */\n | { type: \"widget_settings\"; settings: WidgetSettings | null }\n | { type: \"turn_start\"; turnId: string }\n | { type: \"delta\"; turnId: string; text: string }\n | {\n type: \"complete\";\n turnId: string;\n message: string;\n /** Documentation the answer drew on. Empty when none was used. */\n sources: KnowledgeSourceRef[];\n }\n | { type: \"command\"; commandId: string; command: AssistantCommand }\n | { type: \"proposal\"; proposal: ProposalPayload }\n | {\n type: \"action_status\";\n proposalId: string;\n status: ProposalStatus;\n result?: unknown;\n error?: string;\n }\n | { type: \"error\"; code: string; message: string; recoverable: boolean }\n /**\n * A single-use permit for the voice socket, in reply to\n * `requestVoiceToken`. Short-lived by design \u2014 see doc 03.\n */\n | { type: \"voice_token\"; token: string; expiresAt: string }\n /** What one answer on this screen re-sends, when `debug` is on. */\n | { type: \"context_report\"; report: ContextReport };\n\n/**\n * A confirmation card's contents.\n *\n * Every human-readable field here comes from the runtime's registry, never\n * from model output \u2014 that is the prompt-injection firebreak, and it is why\n * the widget renders exclusively from this payload and never from the\n * assistant's prose.\n */\nexport interface ProposalPayload {\n proposalId: string;\n actionName: string;\n label: string;\n description: string;\n risk: \"low\" | \"medium\" | \"high\";\n input: Record<string, unknown>;\n inputSchema: unknown;\n expiresAt: string;\n uiConfirmationRequired: boolean;\n}\n\nexport interface ClientOptions {\n /** Runtime WebSocket URL, e.g. `wss://runtime.example.com/v1/ws`. */\n url: string;\n publicKey: string;\n /**\n * Fetches a user token from the host app's own backend, which mints it\n * signed with the app secret.\n *\n * Called on every connect and reconnect rather than once, so a token that\n * expired while the tab was backgrounded is replaced rather than replayed.\n */\n getUserToken: () => Promise<string>;\n sdkVersion: string;\n onEvent: (event: ClientEvent) => void;\n /**\n * Ask the runtime what each screen costs, reported after every screen\n * update (amendment 67). For building the integration, not for production.\n */\n debug?: boolean;\n /** Injectable for tests. */\n socketFactory?: (url: string) => WebSocketLike;\n /** Injectable for tests; defaults to exponential backoff with jitter. */\n reconnectDelay?: (attempt: number) => number;\n}\n\n/** The slice of `WebSocket` this client uses, so tests can supply a fake. */\nexport interface WebSocketLike {\n send(data: string): void;\n close(): void;\n onopen: ((event: unknown) => void) | null;\n onclose: ((event: unknown) => void) | null;\n onerror: ((event: unknown) => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n}\n\nconst MAX_RECONNECT_DELAY_MS = 30_000;\n\nexport class AssistantClient {\n private socket: WebSocketLike | null = null;\n private state: ConnectionState = \"idle\";\n private attempt = 0;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n /** Set when the caller closed deliberately, so we do not reconnect. */\n private stopped = false;\n private conversationId: string | null = null;\n /** Re-sent after every reconnect, so the runtime is never blind to context. */\n private lastScreen: ScreenContext | null = null;\n\n constructor(private readonly options: ClientOptions) {}\n\n get connectionState(): ConnectionState {\n return this.state;\n }\n\n async connect(): Promise<void> {\n if (this.state === \"connecting\" || this.state === \"open\") return;\n this.stopped = false;\n await this.open();\n }\n\n close(): void {\n this.stopped = true;\n this.clearTimers();\n this.socket?.close();\n this.socket = null;\n this.setState(\"closed\");\n }\n\n /** Send a user message. Silently dropped when offline \u2014 the widget shows the state. */\n sendMessage(text: string, clientMsgId: string): void {\n this.send({ type: \"message\", data: { text, clientMsgId } });\n }\n\n /**\n * Publish the current screen. Idempotent: a full snapshot that replaces\n * whatever the runtime held.\n */\n /**\n * Tell the runtime where the user is, or that they are somewhere it does not\n * know about.\n *\n * `null` matters as much as a screen. The host app registers the screens it\n * chooses to; every other page is one the runtime must be told it cannot\n * see. Left unsaid, the runtime keeps the last screen it heard about, and\n * the assistant names a page the user left \u2014 which is worse than admitting\n * it does not know, and quietly keeps that screen's scoped actions callable.\n */\n updateScreen(screen: ScreenContext | null): void {\n this.lastScreen = screen;\n this.send({ type: \"screen_update\", data: { screen } });\n }\n\n /**\n * Answer a command. Must be called for every command received, including\n * failures \u2014 the runtime is blocking a turn on this.\n */\n ackCommand(\n commandId: string,\n ok: boolean,\n result?: unknown,\n error?: string,\n ): void {\n this.send({\n type: \"command_result\",\n data: {\n commandId,\n ok,\n result: (result ?? null) as never,\n error: error ?? null,\n },\n });\n }\n\n confirmAction(\n proposalId: string,\n confirmed: boolean,\n editedInput?: Record<string, unknown>,\n ): void {\n this.send({\n type: \"action_confirm\",\n data: {\n proposalId,\n confirmed,\n editedInput: (editedInput ?? null) as never,\n },\n });\n }\n\n cancelTurn(turnId: string): void {\n this.send({ type: \"cancel\", data: { turnId } });\n }\n\n /**\n * Ask for a permit to open the voice socket.\n *\n * Requested on the chat socket rather than fetched over HTTP because this\n * session is what the permit is scoped to: the runtime hands back a token\n * bound to the very session actor holding this connection, so the voice\n * socket joins the conversation already in progress instead of starting a\n * second one beside it.\n *\n * Answered by a `voice_token` event, or by an `error` when voice is not\n * available for this app \u2014 which is the signal to hide the microphone\n * rather than offer one that fails on the first press.\n */\n requestVoiceToken(): void {\n this.send({ type: \"voice_token_request\" });\n }\n\n // ------------------------------------------------------------- internals\n\n private async open(): Promise<void> {\n this.setState(this.attempt === 0 ? \"connecting\" : \"reconnecting\");\n\n let token: string;\n try {\n token = await this.options.getUserToken();\n } catch (cause) {\n if (this.stopped) return;\n // The host app's token endpoint is down. Retrying is right \u2014 this is\n // usually transient \u2014 but the user should be told why it is quiet.\n this.emit({\n type: \"error\",\n code: \"user_token_unavailable\",\n message: \"Could not get a session token from the app.\",\n recoverable: true,\n });\n this.scheduleReconnect();\n return;\n }\n\n // Closed while the token was on its way. Opening anyway left a socket\n // nothing would ever close: a second live session, which React's\n // StrictMode made on every development mount by connecting, closing and\n // connecting again.\n if (this.stopped) return;\n\n const factory =\n this.options.socketFactory ??\n ((url: string) => new WebSocket(url) as unknown as WebSocketLike);\n\n const socket = factory(this.options.url);\n this.socket = socket;\n\n socket.onopen = () => {\n const auth: ClientMessage = {\n type: \"auth\",\n data: {\n publicKey: this.options.publicKey,\n userToken: token,\n sdkVersion: this.options.sdkVersion,\n protocolVersion: PROTOCOL_VERSION,\n conversationId: this.conversationId,\n ...(this.options.debug ? { debug: true } : {}),\n },\n };\n socket.send(JSON.stringify(auth));\n };\n\n socket.onmessage = (event) => {\n if (typeof event.data !== \"string\") return;\n let message: ServerMessage;\n try {\n message = JSON.parse(event.data) as ServerMessage;\n } catch {\n return;\n }\n this.handle(message);\n };\n\n socket.onclose = () => {\n this.clearTimers();\n if (this.stopped) {\n this.setState(\"closed\");\n return;\n }\n this.scheduleReconnect();\n };\n\n socket.onerror = () => {\n // `onclose` always follows, and that is where reconnect is handled.\n };\n }\n\n private handle(message: ServerMessage): void {\n switch (message.type) {\n case \"auth_ok\": {\n this.attempt = 0;\n this.conversationId = message.data.conversationId;\n this.emit({\n type: \"voice_available\",\n available: message.data.voiceAvailable ?? false,\n });\n // Only when the runtime actually sent one. An older runtime omits the\n // field, and overwriting a good name with \"\" would rename the panel to\n // nothing \u2014 worse than leaving the SDK's own default in place.\n if (message.data.assistantName) {\n this.emit({ type: \"assistant_name\", name: message.data.assistantName });\n }\n this.emit({\n type: \"launcher_icon\",\n url: message.data.launcherIconUrl ?? null,\n });\n this.emit({ type: \"widget_settings\", settings: message.data.widget ?? null });\n this.setState(\"open\");\n this.startHeartbeat(message.data.heartbeatIntervalMs);\n // The runtime replays nothing, so re-publish context immediately.\n if (this.lastScreen) this.updateScreen(this.lastScreen);\n break;\n }\n\n case \"auth_error\": {\n this.emit({\n type: \"error\",\n code: message.data.code,\n message: message.data.message,\n // A bad key or an unsupported protocol version will not fix itself.\n recoverable: false,\n });\n this.stopped = true;\n break;\n }\n\n case \"auth_expired\": {\n // Re-authenticate in place rather than tearing down the socket.\n void this.options\n .getUserToken()\n .then((token) =>\n this.send({\n type: \"auth\",\n data: {\n publicKey: this.options.publicKey,\n userToken: token,\n sdkVersion: this.options.sdkVersion,\n protocolVersion: PROTOCOL_VERSION,\n conversationId: this.conversationId,\n ...(this.options.debug ? { debug: true } : {}),\n },\n }),\n )\n .catch(() => this.socket?.close());\n break;\n }\n\n case \"turn_start\":\n this.emit({ type: \"turn_start\", turnId: message.data.turnId });\n break;\n\n case \"delta\":\n this.emit({\n type: \"delta\",\n turnId: message.data.turnId,\n text: message.data.text,\n });\n break;\n\n case \"complete\":\n this.emit({\n type: \"complete\",\n turnId: message.data.turnId,\n message: message.data.message,\n sources: message.data.sources ?? [],\n });\n break;\n\n case \"command\":\n this.emit({\n type: \"command\",\n commandId: message.data.commandId,\n command: message.data.command,\n });\n break;\n\n case \"action_proposal\":\n this.emit({\n type: \"proposal\",\n proposal: message.data as unknown as ProposalPayload,\n });\n break;\n\n case \"action_status\":\n this.emit({\n type: \"action_status\",\n proposalId: message.data.proposalId,\n status: message.data.status,\n result: message.data.result ?? undefined,\n error: message.data.error ?? undefined,\n });\n break;\n\n case \"error\":\n this.emit({\n type: \"error\",\n code: message.data.code,\n message: message.data.message,\n recoverable: message.data.recoverable,\n });\n break;\n\n case \"voice_token\":\n this.emit({\n type: \"voice_token\",\n token: message.data.token,\n expiresAt: message.data.expiresAt,\n });\n break;\n\n case \"context_report\":\n this.emit({ type: \"context_report\", report: message.data });\n break;\n\n case \"pong\":\n break;\n }\n }\n\n private send(message: ClientMessage): void {\n if (this.state !== \"open\" && message.type !== \"auth\") return;\n try {\n this.socket?.send(JSON.stringify(message));\n } catch {\n // A send on a closing socket throws; `onclose` handles recovery.\n }\n }\n\n private scheduleReconnect(): void {\n this.setState(\"reconnecting\");\n const delay =\n this.options.reconnectDelay?.(this.attempt) ?? backoff(this.attempt);\n this.attempt += 1;\n this.reconnectTimer = setTimeout(() => void this.open(), delay);\n }\n\n private startHeartbeat(intervalMs: number): void {\n this.clearHeartbeat();\n this.heartbeatTimer = setInterval(() => {\n this.send({ type: \"ping\" });\n }, intervalMs);\n }\n\n private clearHeartbeat(): void {\n if (this.heartbeatTimer !== null) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n }\n\n private clearTimers(): void {\n this.clearHeartbeat();\n if (this.reconnectTimer !== null) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n }\n\n private setState(state: ConnectionState): void {\n if (this.state === state) return;\n this.state = state;\n this.emit({ type: \"state\", state });\n }\n\n private emit(event: ClientEvent): void {\n this.options.onEvent(event);\n }\n}\n\n/**\n * Exponential backoff with jitter, capped.\n *\n * Jitter matters more than it looks: without it, every tab of every customer\n * reconnects in lockstep after a runtime restart and stampedes it.\n */\nfunction backoff(attempt: number): number {\n const base = Math.min(1000 * 2 ** attempt, MAX_RECONNECT_DELAY_MS);\n return base / 2 + Math.random() * (base / 2);\n}\n", "/**\n * How a proposal's input reads on the approval card, for every card Mihe\n * draws: the web component's and the React SDK's.\n *\n * One copy, because the card is the last thing a user reads before approving\n * something consequential, and two copies would drift in exactly the place\n * where \"what they were shown\" has to mean one thing.\n */\n\n/**\n * Render one field of a proposal for a person to read.\n *\n * This is the last thing a user sees before approving something consequential,\n * so `JSON.stringify` is not good enough \u2014 it was showing\n * `[{\"productName\":\"Chocolate Protein Loaf\",\"quantity\":3}]` where the sentence\n * \"3 \u00D7 Chocolate Protein Loaf\" was wanted. Someone who cannot read the card\n * cannot meaningfully consent to what it describes.\n *\n * Values still come entirely from the schema-validated input, never from model\n * prose, so this changes how the payload is *shown* and not what it can say.\n */\nexport function formatValue(value: unknown): string {\n if (value === null || value === undefined) return \"\u2014\";\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n if (Array.isArray(value)) {\n return value.map(formatValue).join(\", \") || \"\u2014\";\n }\n\n if (typeof value === \"object\") {\n const entries = Object.entries(value as Record<string, unknown>);\n\n // A quantity paired with a name is common enough \u2014 order lines, stock\n // adjustments, recipe amounts \u2014 to be worth reading as one.\n const quantity = entries.find(([key]) => /^(quantity|qty|count)$/i.test(key));\n // An id is not a product name. Only compact a labelled quantity; all\n // remaining fields still have to be visible before the user consents.\n const name = entries.find(([key, nested]) =>\n /(?:name|title|label)$|^(?:product|item)$/i.test(key) &&\n typeof nested === \"string\",\n );\n if (quantity && name) {\n const details = entries\n .filter(([key]) => key !== quantity[0] && key !== name[0])\n .map(([key, nested]) => `${humanizeKey(key)}: ${formatValue(nested)}`);\n return [\n `${formatValue(quantity[1])} \u00D7 ${formatValue(name[1])}`,\n ...details,\n ].join(\"; \");\n }\n\n return entries\n .map(([key, nested]) => `${humanizeKey(key)}: ${formatValue(nested)}`)\n .join(\", \");\n }\n\n return String(value);\n}\n\n/** `productName` and `order_number` both become \"Product name\". */\nexport function humanizeKey(key: string): string {\n const spaced = key\n .replace(/[_-]+/g, \" \")\n .replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n .trim();\n return spaced.charAt(0).toUpperCase() + spaced.slice(1).toLowerCase();\n}\n", "/**\n * What a confirmation card says (amendment 99), worked out once per proposal\n * and drawn by every card Mihe has: the web component's and the React SDK's.\n *\n * Two sources, both trusted, neither the model:\n *\n * 1. **The app's own description.** `describeProposal` (an option) is handed\n * the proposal, exactly the input that will run, and returns the lines to\n * show: before and after, money in the store's currency, thumbnails. It is\n * the app's code over the app's data, so it can say what a generic card\n * can't. It must describe the input it was given, never anything else.\n * 2. **The registry's display hints**, for an app that doesn't describe a\n * card. They live in the action's input schema as annotations: a summary\n * template at the root (`x-mihe-summary`), and on each field a `title`,\n * `x-mihe-format` (`money`, `yesno`, `date`) and `x-mihe-hidden`. Fields\n * are shown in the schema's order. Hide only an opaque id that has a\n * readable name beside it which the app checks: everything else that will\n * run must be on the card.\n *\n * With neither, the card is what it always was: every field, by its key.\n */\n\nimport type { ProposalPayload } from \"./client\";\nimport { formatValue, humanizeKey } from \"./widget/format\";\n\n/** One line of a card. */\nexport interface CardLine {\n /** What the line is about: \"Price\", \"Amber Soy Candle\". */\n label?: string;\n /** The value, or what it becomes. */\n text?: string;\n /** What it was before, shown struck through ahead of `text`. */\n before?: string;\n /** A list, each entry on its own line so each can be checked. */\n items?: string[];\n /** Pictures of what the line is about, small. http(s), blob: and data:image only. */\n images?: { src: string; alt?: string }[];\n}\n\n/** What `describeProposal` returns. */\nexport interface ProposalDescription {\n /** One line saying what will happen: \"Change Linen Shirt's price\". */\n summary?: string;\n lines: CardLine[];\n /** A short note under the lines: \"All start as drafts.\" */\n note?: string;\n /** The confirm button's words: \"Make 3 products\". Defaults to \"Confirm\". */\n confirmLabel?: string;\n}\n\n/**\n * Describes a proposal for its card, from the app's own data. Return null (or\n * throw) to fall back to the registry's hints. Called once per proposal; the\n * card waits for it, with Confirm disabled, for up to four seconds.\n */\nexport type DescribeProposal = (proposal: ProposalPayload) => ProposalDescription | null | undefined | Promise<ProposalDescription | null | undefined>;\n\n/** A card ready to draw. */\nexport interface CardView extends ProposalDescription {\n /** Where it came from: the app, the registry's hints, or neither. */\n source: \"app\" | \"hints\" | \"plain\";\n}\n\ntype Schema = { title?: unknown; properties?: Record<string, Schema>; items?: Schema; \"x-mihe-summary\"?: unknown; \"x-mihe-format\"?: unknown; \"x-mihe-hidden\"?: unknown };\nconst asSchema = (v: unknown): Schema => (v && typeof v === \"object\" ? (v as Schema) : {});\nconst hidden = (s: Schema) => s[\"x-mihe-hidden\"] === true;\nconst title = (key: string, s: Schema) => (typeof s.title === \"string\" && s.title.trim() ? s.title : humanizeKey(key));\n\n/** One value, as its field's hints say to show it. */\nexport function formatField(value: unknown, schema: unknown): string {\n const s = asSchema(schema);\n const fmt = s[\"x-mihe-format\"];\n if (fmt === \"yesno\" && typeof value === \"boolean\") return value ? \"Yes\" : \"No\";\n if (fmt === \"money\" && typeof value === \"number\") return value.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });\n if (fmt === \"date\" && typeof value === \"string\" && !Number.isNaN(Date.parse(value))) return new Date(value).toLocaleDateString(undefined, { day: \"numeric\", month: \"short\", year: \"numeric\" });\n if (Array.isArray(value)) return value.map((v) => formatField(v, s.items)).join(\", \") || \"\u2014\";\n if (value && typeof value === \"object\") {\n const props = s.properties ?? {};\n const keys = [...Object.keys(props).filter((k) => k in (value as object)), ...Object.keys(value as object).filter((k) => !(k in props))];\n const visible = keys.filter((k) => !hidden(asSchema(props[k])));\n // With hints, an object reads as its fields in order, labelled by their titles (\"Amber Candle \u00B7 Price 18\").\n if (Object.keys(props).length) return visible.map((k) => (k === visible[0] && typeof (value as any)[k] === \"string\" ? String((value as any)[k]) : `${title(k, asSchema(props[k]))} ${formatField((value as any)[k], props[k])}`)).join(\" \u00B7 \");\n return formatValue(value);\n }\n return formatValue(value);\n}\n\n/** `x-mihe-summary`, filled from the input: \"Change {productName}\" \u2192 \"Change Linen Shirt\". Null if a field it names is missing. */\nexport function fillSummary(template: unknown, input: Record<string, unknown>, schema: Schema): string | null {\n if (typeof template !== \"string\" || !template.trim()) return null;\n let missing = false;\n const out = template.replace(/\\{([A-Za-z0-9_]+)\\}/g, (_, key: string) => {\n const v = input[key];\n if (v === undefined || v === null || v === \"\") { missing = true; return \"\"; }\n return Array.isArray(v) ? String(v.length) : formatField(v, schema.properties?.[key]);\n });\n return missing ? null : out;\n}\n\n/** The card from the registry's hints (or, with none, every field by its key). */\nexport function hintedView(proposal: ProposalPayload): CardView {\n const schema = asSchema(proposal.inputSchema);\n const props = schema.properties ?? {};\n const input = proposal.input ?? {};\n const hinted = !!schema[\"x-mihe-summary\"] || Object.values(props).some((p) => { const s = asSchema(p); return typeof s.title === \"string\" || hidden(s) || s[\"x-mihe-format\"] !== undefined; });\n const keys = [...Object.keys(props).filter((k) => k in input), ...Object.keys(input).filter((k) => !(k in props))];\n const lines: CardLine[] = keys.filter((k) => !hidden(asSchema(props[k]))).map((k) => {\n const v = input[k];\n const label = hinted ? title(k, asSchema(props[k])) : humanizeKey(k);\n return Array.isArray(v) && v.length > 0 ? { label, items: v.map((x) => formatField(x, asSchema(props[k]).items)) } : { label, text: formatField(v, props[k]) };\n });\n const summary = fillSummary(schema[\"x-mihe-summary\"], input, schema) ?? undefined;\n return { source: hinted ? \"hints\" : \"plain\", ...(summary ? { summary } : {}), lines };\n}\n\nconst SAFE_SRC = /^(https?:|blob:|data:image\\/)/i;\n\n/** The app's description, made safe to draw (only plain strings, only picture addresses that can't run anything). */\nexport function appView(d: ProposalDescription): CardView {\n const str = (v: unknown) => (typeof v === \"string\" ? v : v === undefined || v === null ? undefined : String(v));\n return {\n source: \"app\",\n ...(str(d.summary) ? { summary: str(d.summary) } : {}),\n ...(str(d.note) ? { note: str(d.note) } : {}),\n ...(str(d.confirmLabel) ? { confirmLabel: str(d.confirmLabel) } : {}),\n lines: (Array.isArray(d.lines) ? d.lines : []).map((l) => ({\n ...(str(l.label) ? { label: str(l.label) } : {}),\n ...(str(l.text) !== undefined ? { text: str(l.text) } : {}),\n ...(str(l.before) !== undefined ? { before: str(l.before) } : {}),\n ...(Array.isArray(l.items) ? { items: l.items.map((x) => String(x)) } : {}),\n ...(Array.isArray(l.images) ? { images: l.images.filter((im) => im && typeof im.src === \"string\" && SAFE_SRC.test(im.src)).slice(0, 12).map((im) => ({ src: im.src, ...(str(im.alt) ? { alt: str(im.alt) } : {}) })) } : {}),\n })),\n };\n}\n\n/** How long a card waits for the app's description before drawing from the hints. */\nexport const DESCRIBE_TIMEOUT_MS = 4000;\n", "/**\n * The highlight and focus engine (spec \u00A712).\n *\n * Draws an overlay over a registered element rather than mutating the\n * element's own styles. Two reasons: the host app's CSS is not ours to touch,\n * and an overlay cannot leave residue if the SDK unmounts mid-tutorial.\n *\n * Elements are found by the `data-mihe-component` attribute that\n * `useAssistantComponent` spreads onto them. Never by CSS selector or\n * position \u2014 the assistant points at things the app has declared, and at\n * nothing else.\n */\n\nexport const COMPONENT_ATTRIBUTE = \"data-mihe-component\";\n\nconst OVERLAY_ID = \"mihe-highlight\";\nconst PADDING = 6;\n\n/**\n * Whether clicking this element does something, rather than merely selecting\n * it. Buttons and links act; inputs and containers receive.\n *\n * Read from the element rather than from the registry's `ComponentType`\n * because the overlay is drawn over whatever the app spread the attribute\n * onto, which is not always the element the declaration describes \u2014 a label\n * carrying the props for the input beside it, for instance.\n */\nfunction activates(element: HTMLElement | null): boolean {\n if (!element) return false;\n if (element.closest(\"button, a[href], [role='button'], [role='link']\")) return true;\n return false;\n}\n\nexport function findComponent(componentKey: string): HTMLElement | null {\n return document.querySelector<HTMLElement>(\n `[${COMPONENT_ATTRIBUTE}=\"${CSS.escape(componentKey)}\"]`,\n );\n}\n\nexport class HighlightEngine {\n private overlay: HTMLElement | null = null;\n private caption: HTMLElement | null = null;\n private target: HTMLElement | null = null;\n private tracking = false;\n private observer: MutationObserver | null = null;\n private frame = 0;\n\n /** Draw attention to a component. Returns false if it is not on the page. */\n highlight(componentKey: string, message?: string): boolean {\n const target = findComponent(componentKey);\n if (!target) return false;\n\n this.target = target;\n this.ensureOverlay();\n this.setCaption(message);\n this.position();\n this.startTracking();\n\n target.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n return true;\n }\n\n /** Move keyboard focus into a component, highlighting it too. */\n focus(componentKey: string, message?: string): boolean {\n const target = findComponent(componentKey);\n if (!target) return false;\n\n this.highlight(componentKey, message);\n // A wrapper may be registered rather than the input itself; focus the\n // first focusable descendant when the element cannot take focus.\n const focusable =\n target.matches(\"input, textarea, select, button, [tabindex]\")\n ? target\n : target.querySelector<HTMLElement>(\n \"input, textarea, select, button, [tabindex]\",\n );\n focusable?.focus();\n return true;\n }\n\n scrollTo(componentKey: string): boolean {\n const target = findComponent(componentKey);\n if (!target) return false;\n target.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n return true;\n }\n\n clear(): void {\n this.stopTracking();\n this.overlay?.remove();\n this.overlay = null;\n this.caption = null;\n this.target = null;\n }\n\n private ensureOverlay(): void {\n if (this.overlay) return;\n\n const overlay = document.createElement(\"div\");\n overlay.id = OVERLAY_ID;\n // `pointer-events: none` is load-bearing: the highlight must never\n // intercept the click it is asking the user to make.\n Object.assign(overlay.style, {\n position: \"fixed\",\n zIndex: \"2147483000\",\n pointerEvents: \"none\",\n border: \"2px solid #4f7cff\",\n borderRadius: \"8px\",\n boxShadow: \"0 0 0 9999px rgba(15, 23, 42, 0.45)\",\n transition: \"top 120ms ease, left 120ms ease, width 120ms ease, height 120ms ease\",\n } satisfies Partial<CSSStyleDeclaration>);\n\n const caption = document.createElement(\"div\");\n Object.assign(caption.style, {\n position: \"absolute\",\n left: \"0\",\n top: \"100%\",\n marginTop: \"8px\",\n maxWidth: \"260px\",\n padding: \"8px 10px\",\n borderRadius: \"8px\",\n background: \"#0f172a\",\n color: \"#f8fafc\",\n font: \"500 13px/1.4 system-ui, -apple-system, sans-serif\",\n display: \"none\",\n } satisfies Partial<CSSStyleDeclaration>);\n\n overlay.appendChild(caption);\n document.body.appendChild(overlay);\n\n this.overlay = overlay;\n this.caption = caption;\n }\n\n private setCaption(message?: string): void {\n if (!this.caption) return;\n if (message) {\n this.caption.textContent = message;\n this.caption.style.display = \"block\";\n } else {\n this.caption.textContent = \"\";\n this.caption.style.display = \"none\";\n }\n }\n\n private position = (): void => {\n if (!this.overlay || !this.target) return;\n\n // The element may have unmounted since the highlight was drawn.\n if (!this.target.isConnected) {\n this.clear();\n return;\n }\n\n const rect = this.target.getBoundingClientRect();\n Object.assign(this.overlay.style, {\n top: `${rect.top - PADDING}px`,\n left: `${rect.left - PADDING}px`,\n width: `${rect.width + PADDING * 2}px`,\n height: `${rect.height + PADDING * 2}px`,\n });\n };\n\n /** Coalesced, because a MutationObserver can fire many times per render. */\n private reposition = (): void => {\n if (this.frame) return;\n this.frame = requestAnimationFrame(() => {\n this.frame = 0;\n this.position();\n });\n };\n\n /** Escape is the universal \"get this off my screen\". */\n private onKeyDown = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") this.clear();\n };\n\n /**\n * Any click outside the target dismisses.\n *\n * The overlay dims the whole page, so it reads as a modal \u2014 but it is\n * `pointer-events: none`, so clicks reach the app underneath. Without this\n * the user clicks away, the app responds, and the dim stays: it looks\n * dismissible and is not. Clicks on the target itself are left alone, since\n * that is the click the highlight is asking for and the app's own handler\n * usually removes the highlight's reason for existing anyway.\n */\n /**\n * A click ends the highlight, with one exception that has an exception.\n *\n * Clicking away dismisses it: the user has moved on and a ring around\n * something they are no longer looking at is clutter.\n *\n * Clicking *inside* the target does not, because the target is often an\n * input the user has just been asked to fill \u2014 losing the marker the moment\n * they click into the field would undo the pointing.\n *\n * But if the target is something clicking *activates* \u2014 a button, a link \u2014\n * then the click was the action, and the highlight has done its job. Without\n * this the ring survives the thing it pointed at: the assistant highlighted\n * \"Receive Stock\", the user clicked, the form opened, and the outline stayed\n * behind the dialog pointing at a button nobody needed any more.\n */\n private onPointerDown = (event: Event): void => {\n const node = event.target;\n const inside = node instanceof Node && this.target?.contains(node);\n if (inside && !activates(this.target)) return;\n this.clear();\n };\n\n /**\n * Keep the overlay glued to its target, and remove it when the target goes.\n *\n * Scroll and resize are listened to rather than polled; `capture: true` on\n * scroll catches scrolling inside containers, not just the window.\n *\n * The MutationObserver is what makes the overlay mortal. `position` already\n * clears when the target has left the DOM, but it only ran on scroll and\n * resize \u2014 so a highlighted element that unmounted while the page sat still\n * (a form closing after submit, say) left the overlay stranded over the page\n * with no way to dismiss it.\n */\n private startTracking(): void {\n if (this.tracking) return;\n this.tracking = true;\n window.addEventListener(\"scroll\", this.position, { passive: true, capture: true });\n window.addEventListener(\"resize\", this.position, { passive: true });\n window.addEventListener(\"keydown\", this.onKeyDown);\n // Capture, so the app's own handlers cannot stop it from reaching us.\n window.addEventListener(\"pointerdown\", this.onPointerDown, { capture: true });\n\n this.observer = new MutationObserver(this.reposition);\n this.observer.observe(document.body, { childList: true, subtree: true });\n }\n\n private stopTracking(): void {\n if (!this.tracking) return;\n this.tracking = false;\n window.removeEventListener(\"scroll\", this.position, { capture: true });\n window.removeEventListener(\"resize\", this.position);\n window.removeEventListener(\"keydown\", this.onKeyDown);\n window.removeEventListener(\"pointerdown\", this.onPointerDown, { capture: true });\n\n this.observer?.disconnect();\n this.observer = null;\n if (this.frame) {\n cancelAnimationFrame(this.frame);\n this.frame = 0;\n }\n }\n}\n", "/**\n * Executing the commands the runtime sends.\n *\n * Every command names a registered target \u2014 a route the runtime resolved from\n * its screen catalogue, or a component key the app declared. Nothing here\n * takes a CSS selector or a coordinate, so there is no path by which the model\n * drives the UI directly (spec \u00A75.2).\n *\n * Every branch returns a result, including failures, because the runtime is\n * blocking a turn on the ack and the model needs to learn the truth. Reporting\n * \"the element was not on screen\" is far better than reporting nothing.\n */\n\nimport type { AssistantCommand } from \"@mihe-dev/protocol\";\n\nimport type { HighlightEngine } from \"./highlight\";\nimport { findComponent } from \"./highlight\";\nimport type { AssistantStore, QueryParamDeclaration, TableRow } from \"./store\";\n\n/** When a frontend action is warned about as slow. */\nexport const ACTION_SLOW_MS = 5_000;\n\n/**\n * How long the runtime waits for a command's answer by default\n * (`COMMAND_ACK_TIMEOUT` in the runtime's session.rs); an action's\n * `timeoutSeconds` or the app's setting can make it longer (amendment 103).\n * Past it, the assistant is told the command failed.\n */\nexport const RUNTIME_ACK_WAIT_MS = 10_000;\n\nexport interface CommandResult {\n ok: boolean;\n result?: unknown;\n error?: string;\n}\n\nexport interface CommandContext {\n store: AssistantStore;\n highlighter: HighlightEngine;\n /**\n * How the host app changes route. Supplied by the customer, because only\n * they know whether that is Next.js's router, React Router, or something\n * else. Defaults to a full page load, which works everywhere and is\n * noticeably worse \u2014 the docs should push people to wire this up.\n */\n navigate: (route: string) => void;\n /**\n * Resolves once the screen at `route` has published its context to the\n * runtime, or `false` if it has not done so within the timeout.\n *\n * Navigating is the one command whose effects are not finished when the\n * function returns: the router swaps the tree, React mounts the destination,\n * its hooks register, and only then does the new context travel. The runtime\n * re-reads the screen after every tool call to decide what the next step may\n * do, so acking before that lands would tell it the user is still on the\n * screen they just left \u2014 and the action they navigated *for* would be\n * filtered out as off-screen.\n *\n * Optional so a hand-built context still works; the provider always supplies\n * it.\n */\n screenPublished?: (route: string, options?: { fresh?: boolean }) => Promise<boolean>;\n /**\n * Resolves once a published screen shows `controlKey` set to `value`, or\n * `false` if none does within the timeout. A switch is acked only then, for\n * `screenPublished`'s reason: the runtime's next step reads the screen.\n */\n viewPublished?: (controlKey: string, value: string) => Promise<boolean>;\n /** Opens a registered modal, when the host app has modals to open. */\n /** Return `false` when the key is not one this app can open. */\n openModal?: (modalKey: string) => void | boolean;\n}\n\n/** Why a set of search parameters is not what the table declared, or null. */\nexport function checkQueryParams(\n declared: QueryParamDeclaration[],\n params: Record<string, string>,\n): string | null {\n for (const [key, value] of Object.entries(params)) {\n const param = declared.find((p) => p.key === key);\n if (!param) return `\"${key}\" is not something this table can be searched by.`;\n if (typeof value !== \"string\") return `The value for \"${key}\" must be a string.`;\n if (param.kind === \"enum\" && !param.values.includes(value)) {\n return `\"${value}\" is not one of \"${key}\"'s values.`;\n }\n if (param.kind === \"date\" && !isIsoDate(value)) {\n return `\"${value}\" is not a date in YYYY-MM-DD form.`;\n }\n if (param.kind === \"text\" && (value.length > MAX_QUERY_TEXT || /[\\u0000-\\u001f\\u007f]/.test(value))) {\n return `The search for \"${key}\" is too long or contains control characters.`;\n }\n }\n return null;\n}\n\n/** Matches the runtime's cap, `QUERY_TEXT_MAX_CHARS`. */\nconst MAX_QUERY_TEXT = 100;\n\nfunction isIsoDate(value: string): boolean {\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) return false;\n const parsed = new Date(`${value}T00:00:00Z`);\n return !Number.isNaN(parsed.getTime()) && parsed.toISOString().slice(0, 10) === value;\n}\n\nfunction currentPath(): string | null {\n return typeof window === \"undefined\" ? null : window.location.pathname;\n}\n\nfunction currentUrl(): string | null {\n return typeof window === \"undefined\"\n ? null\n : window.location.pathname + window.location.search;\n}\n\nexport async function executeCommand(\n command: AssistantCommand,\n ctx: CommandContext,\n): Promise<CommandResult> {\n switch (command.type) {\n case \"navigate\": {\n const { route } = command.data;\n // A query means the page's state is about to change \u2014 usually the same\n // page with new filters. The screen already published is then the\n // *old* view, and acking on it would have the model read the chart it\n // was asked to replace. So wait for a fresh publish, unless the URL is\n // already exactly this, in which case nothing will change and nothing\n // will be published.\n const fresh = route.includes(\"?\") && currentUrl() !== route;\n ctx.navigate(route);\n const arrived = (await ctx.screenPublished?.(route, { fresh })) ?? true;\n return arrived\n ? { ok: true, result: { route } }\n : {\n ok: true,\n result: {\n route,\n note: \"Navigation started, but the destination has not reported its context yet. Anything scoped to that screen may not be available for another moment.\",\n },\n };\n }\n\n case \"highlight\": {\n const { componentKey, message } = command.data;\n return ctx.highlighter.highlight(componentKey, message ?? undefined)\n ? { ok: true }\n : { ok: false, error: notOnPage(componentKey) };\n }\n\n case \"focus\": {\n const { componentKey, message } = command.data;\n return ctx.highlighter.focus(componentKey, message ?? undefined)\n ? { ok: true }\n : { ok: false, error: notOnPage(componentKey) };\n }\n\n case \"scroll_to\": {\n const { componentKey } = command.data;\n return ctx.highlighter.scrollTo(componentKey)\n ? { ok: true }\n : { ok: false, error: notOnPage(componentKey) };\n }\n\n case \"open_modal\": {\n const { modalKey } = command.data;\n if (!ctx.openModal) {\n return {\n ok: false,\n error:\n \"This app has not wired up modal opening, so that modal cannot be opened.\",\n };\n }\n // An explicit `false` means the host app knows about modals but not this\n // one \u2014 usually because the dialog belongs to a screen the user has\n // left. Reported as a failure rather than swallowed: the model's\n // standing rule is never to claim something happened without a tool\n // result saying so, and \"ok\" to an unopened form is exactly that claim.\n // `undefined` stays a success, so an implementation returning nothing\n // behaves as it always did.\n if (ctx.openModal(modalKey) === false) {\n return {\n ok: false,\n error: `There is no \\`${modalKey}\\` dialog on this screen. It may belong to a different one.`,\n };\n }\n return { ok: true };\n }\n\n case \"run_frontend_action\": {\n const { actionName, input } = command.data;\n const action = ctx.store.getAction(actionName);\n if (!action) {\n // The runtime believes this action is registered but no handler\n // mounted \u2014 usually a component that owns it is not on this screen.\n return {\n ok: false,\n error: `No handler is currently mounted for \"${actionName}\".`,\n };\n }\n\n // A handler that waits on the user (a confirmation dialog, a password,\n // a file picker) outlives the runtime's wait, and the assistant then\n // reports as failed an action the user goes on to finish. Nothing else\n // tells the developer, so the console does.\n const started = Date.now();\n const slow = setTimeout(() => {\n console.warn(\n `[mihe] The \"${actionName}\" handler has run for ${ACTION_SLOW_MS / 1000}s. ` +\n `The runtime waits ${RUNTIME_ACK_WAIT_MS / 1000}s unless the action (timeoutSeconds) or the app sets longer, then tells the assistant it failed. ` +\n \"If it is waiting for the user, open the UI they need and return at once (say what you opened); \" +\n \"they finish it there.\",\n );\n }, ACTION_SLOW_MS);\n const late = () => {\n if (Date.now() - started > RUNTIME_ACK_WAIT_MS) {\n // The SDK doesn't know this action's limit (amendment 103), only the default.\n console.warn(\n `[mihe] The \"${actionName}\" handler finished after ${Math.round((Date.now() - started) / 1000)}s. ` +\n `If that was past its time limit (${RUNTIME_ACK_WAIT_MS / 1000}s unless the action sets timeoutSeconds or the app sets longer), ` +\n \"the assistant was told it failed whatever the outcome. If it waits on the user, return before they act; if it is slow by nature, give it timeoutSeconds.\",\n );\n }\n };\n\n try {\n const result = await action.handler(\n (input ?? {}) as Record<string, unknown>,\n );\n late();\n return { ok: true, result: result ?? { done: true } };\n } catch (cause) {\n late();\n // The host app's handler threw. Its message is the most useful thing\n // to hand back, so the assistant can tell the user what went wrong.\n return { ok: false, error: describeError(cause) };\n } finally {\n clearTimeout(slow);\n }\n }\n\n case \"set_view\": {\n const { controlKey, value } = command.data;\n const control = ctx.store.getControl(controlKey);\n if (!control) {\n return { ok: false, error: `\"${controlKey}\" is not on the page right now.` };\n }\n // Checked here as well as in the runtime, so the app's `onChange` is\n // only ever handed a value it declared, whatever sent the command.\n if (!control.options.some((option) => option.value === value)) {\n return { ok: false, error: `\"${value}\" is not one of \"${controlKey}\"'s options.` };\n }\n\n try {\n await control.onChange(value);\n } catch (cause) {\n return { ok: false, error: describeError(cause) };\n }\n\n const shown = (await ctx.viewPublished?.(controlKey, value)) ?? true;\n return shown\n ? { ok: true, result: { showing: value } }\n : {\n ok: false,\n error: `The app was asked to show \"${value}\", but \"${controlKey}\" still reports another tab.`,\n };\n }\n\n case \"read_table\": {\n const { tableKey, offset, limit } = command.data;\n const table = ctx.store.getTable(tableKey);\n if (!table) {\n return { ok: false, error: `\"${tableKey}\" is not on the page right now.` };\n }\n try {\n const rows = table.read(offset, limit);\n return {\n ok: true,\n result: {\n rows,\n offset,\n loaded: table.loadedRows,\n ...(table.total === undefined ? {} : { total: table.total }),\n },\n };\n } catch (cause) {\n return { ok: false, error: describeError(cause) };\n }\n }\n\n case \"query_table\": {\n const { tableKey, params, limit, show } = command.data;\n const table = ctx.store.getTable(tableKey);\n if (!table) {\n return { ok: false, error: `\"${tableKey}\" is not on the page right now.` };\n }\n if (!table.query) {\n return { ok: false, error: `\"${tableKey}\" cannot be searched.` };\n }\n // Checked here as well as in the runtime, so the app's `run` is only\n // ever handed parameters it declared, whatever sent the command.\n const invalid = checkQueryParams(table.query.params, params);\n if (invalid) return { ok: false, error: invalid };\n\n let found: { rows: TableRow[]; total?: number };\n try {\n found = await table.query.run(params);\n } catch (cause) {\n return { ok: false, error: describeError(cause) };\n }\n\n let shown = false;\n if (show && table.query.show) {\n try {\n await table.query.show(params);\n // Acked once the page has published again, for `navigate`'s\n // reason: the model's next step reads the screen.\n shown = (await ctx.screenPublished?.(currentPath() ?? \"\", { fresh: true })) ?? true;\n } catch (cause) {\n return { ok: false, error: describeError(cause) };\n }\n }\n\n return {\n ok: true,\n result: {\n rows: found.rows.slice(0, limit),\n ...(found.total === undefined ? {} : { total: found.total }),\n shown,\n },\n };\n }\n\n case \"read_chart\": {\n const { chartKey, series, limit } = command.data;\n const chart = ctx.store.getChart(chartKey);\n if (!chart) {\n return { ok: false, error: `\"${chartKey}\" is not on the page right now.` };\n }\n try {\n return { ok: true, result: { series: chart.read(series ?? undefined, limit) } };\n } catch (cause) {\n return { ok: false, error: describeError(cause) };\n }\n }\n\n case \"clear_highlight\": {\n ctx.highlighter.clear();\n return { ok: true };\n }\n\n default: {\n // An unknown command type means the runtime is ahead of this SDK.\n // Reporting it beats silently doing nothing.\n const unknown = command as { type: string };\n return {\n ok: false,\n error: `This app's SDK does not understand the \"${unknown.type}\" command. It may need updating.`,\n };\n }\n }\n}\n\nfunction notOnPage(componentKey: string): string {\n const registeredElsewhere = Boolean(findComponent(componentKey));\n return registeredElsewhere\n ? `\"${componentKey}\" is on the page but not visible.`\n : `\"${componentKey}\" is not on the page right now.`;\n}\n\nfunction describeError(cause: unknown): string {\n if (cause instanceof Error) return cause.message;\n if (typeof cause === \"string\") return cause;\n return \"The action failed.\";\n}\n", "/**\n * The developer's view of what each screen costs (amendment 67).\n *\n * With `debug` on the provider, the runtime reports what one answer on the\n * current screen re-sends to the model, and this turns that report into a\n * console line someone can act on: which part is large, and which tools make\n * it so. Estimates, about four characters a token. The dashboard's usage\n * page has the real bill.\n */\n\nimport type { ContextSize, ScreenId } from \"@mihe-dev/protocol\";\n\nexport interface ContextReport {\n screenId?: ScreenId;\n chat: ContextSize;\n voice?: ContextSize;\n}\n\n/** How many of the largest tools to name. The rest are counted. */\nconst LARGEST = 5;\n\nexport function formatContextReport(report: ContextReport): string {\n const where = report.screenId ? `\\`${report.screenId}\\`` : \"an unregistered page\";\n const lines = [`[mihe] Each answer on ${where} re-sends about:`];\n lines.push(` typed: ${describe(report.chat)}`);\n if (report.voice) lines.push(` voice: ${describe(report.voice)}`);\n\n const largest = (report.voice ?? report.chat).tools.slice(0, LARGEST);\n if (largest.length > 0) {\n lines.push(\n ` largest tools: ${largest.map((tool) => `${tool.name} ${tool.tokens}`).join(\", \")}`,\n );\n }\n return lines.join(\"\\n\");\n}\n\nfunction describe(size: ContextSize): string {\n const total = size.instructionTokens + size.toolTokens + size.screenTokens;\n return (\n `${total.toLocaleString(\"en\")} tokens \u2014 instructions ${size.instructionTokens}, ` +\n `${size.tools.length} tools ${size.toolTokens}, this screen ${size.screenTokens}`\n );\n}\n", "/**\n * Chart summaries, worked out where the numbers are (amendment 67, step 4).\n *\n * The model explains a trend from these figures, so it is handed the start,\n * end, high, low, total and largest steps rather than a list to do arithmetic\n * over, which models get wrong.\n */\n\nimport type { ChartPoint, ChartStep, SeriesSummary } from \"@mihe-dev/protocol\";\n\nexport function summarise(points: ChartPoint[], ordered: boolean): SeriesSummary | undefined {\n const [first] = points;\n if (!first) return undefined;\n const last = points[points.length - 1]!;\n\n let min = first;\n let max = first;\n let total = 0;\n let rise: ChartStep | undefined;\n let fall: ChartStep | undefined;\n\n points.forEach((point, i) => {\n total += point.y;\n if (point.y < min.y) min = point;\n if (point.y > max.y) max = point;\n if (!ordered || i === 0) return;\n const previous = points[i - 1]!;\n const change = point.y - previous.y;\n if (change > 0 && (!rise || change > rise.change)) {\n rise = { from: previous.x, to: point.x, change };\n }\n if (change < 0 && (!fall || change < fall.change)) {\n fall = { from: previous.x, to: point.x, change };\n }\n });\n\n return {\n points: points.length,\n first,\n last,\n min,\n max,\n total: Math.round(total * 100) / 100,\n ...(rise ? { largestRise: rise } : {}),\n ...(fall ? { largestFall: fall } : {}),\n };\n}\n\n/**\n * At most `limit` points, evenly spaced, keeping both ends. The summary\n * already carries the high and low, which thinning could skip.\n */\nexport function thin<T>(points: T[], limit: number): T[] {\n if (points.length <= limit) return points;\n if (limit <= 1) return points.slice(0, 1);\n const step = (points.length - 1) / (limit - 1);\n return Array.from({ length: limit }, (_, i) => points[Math.round(i * step)]!);\n}\n", "/**\n * What an app declares about tabs, tables and charts, and how each becomes\n * what the store holds (amendment 67, doc 08).\n *\n * Shared by the JavaScript API (`mihe.registerTable(\u2026)`) and the React hooks\n * (`useAssistantTable(\u2026)`), so the two can't send the model different things\n * for the same table. Each builder takes `latest`, a function returning the\n * declaration as it is now: rows, closures and data are read at the moment of\n * a read rather than captured at registration, so a table that re-renders\n * every second doesn't re-register every second.\n */\n\nimport type { ChartPoint } from \"@mihe-dev/protocol\";\n\nimport { summarise, thin } from \"./chart\";\nimport type {\n ChartDeclaration,\n QueryParamDeclaration,\n TableDeclaration,\n ViewControlDeclaration,\n} from \"./store\";\n\n/** A set of tabs the assistant can see and switch. */\nexport interface AssistantTabs {\n /** Unique on the screen, e.g. `reports.tabs`. The model refers to it by this. */\n id: string;\n /** What the tabs choose between, for the model: \"Report\", \"Period\". */\n label: string;\n tabs: { value: string; label: string }[];\n /** The tab showing now. */\n value: string | undefined;\n /** Switch to `value`. Called with one of `tabs`' values and nothing else. */\n onChange: (value: string) => void | Promise<void>;\n}\n\n/** A column a table exposes. Undeclared fields are never sent. */\nexport interface AssistantTableColumn<Row> {\n key: string;\n /** What the model calls it, and the key each row's cell is read under. */\n label: string;\n /** The cell's value. Defaults to `row[key]`. */\n value?: (row: Row) => unknown;\n}\n\n/** A table the assistant can read. */\nexport interface AssistantTable<Row> {\n /** Unique on the screen, e.g. `orders.table`. */\n id: string;\n label: string;\n columns: AssistantTableColumn<Row>[];\n /** The rows loaded now \u2014 the page on screen, for a paged table. */\n rows: Row[];\n rowId: (row: Row) => string;\n /**\n * The action input a row's id fills, such as `orderId`. The runtime tells\n * the model which actions accept it, from your registry and the user's\n * permissions. Leave it out for rows nothing acts on.\n */\n rowIdName?: string;\n /**\n * The screen a row opens, and the route params that open it:\n * `{ screenId: \"orders.detail\", params: (order) => ({ id: order.id }) }`.\n * Each row the model reads then carries exactly those params, so it never\n * has to guess which of a record's ids the link wants.\n */\n opens?: { screenId: string; params: (row: Row) => Record<string, string> };\n /** Rows in the whole list, when `rows` is one page of it. */\n total?: number;\n /**\n * Search past the loaded rows, with your own API and the user's own\n * session (amendment 67, step 3). The model fills in `params`; only the\n * keys you declare, and for an enum only its values, ever reach `run`.\n *\n * ```ts\n * query: {\n * params: {\n * search: { kind: \"text\", description: \"order number or customer name\" },\n * status: { kind: \"enum\", values: [\"pending\", \"paid\"] },\n * from: { kind: \"date\" },\n * },\n * run: async (p) => {\n * const res = await fetchOrders({ search: p.search, status: p.status, dateFrom: p.from });\n * return { rows: res.data, total: res.meta.total };\n * },\n * // Optional. Make the page show this search \u2014 set its filters and\n * // search box. Without it, results are read but not shown.\n * show: (p) => { setSearch(p.search ?? \"\"); setStatus(p.status ?? \"all\"); },\n * }\n * ```\n *\n * It only reads. A search is in `navigate`'s class: it changes what is\n * shown, never what is stored.\n */\n query?: AssistantTableQuery<Row>;\n}\n\n/** A search parameter: what the model may fill in. */\nexport type AssistantQueryParam =\n | { kind: \"text\"; description?: string }\n | { kind: \"enum\"; values: readonly string[]; description?: string }\n | { kind: \"date\"; description?: string };\n\n/** How a table is searched beyond the rows it has loaded. */\nexport interface AssistantTableQuery<Row> {\n params: Record<string, AssistantQueryParam>;\n run: (params: Record<string, string>) => Promise<{ rows: Row[]; total?: number }>;\n show?: (params: Record<string, string>) => void | Promise<void>;\n}\n\n/** A chart the assistant can explain. */\nexport interface AssistantChart<Datum> {\n /** Unique on the screen, e.g. `stock.trend`. */\n id: string;\n title: string;\n /** The rows the chart is drawn from: the same array your chart library gets. */\n data: Datum[];\n x: {\n key: keyof Datum & string;\n /** What the axis is: \"Month\", \"Product\". */\n label: string;\n /**\n * `ordered` for time and anything else with a sequence, `category` for a\n * set of things. Rises and falls are worked out only for the first.\n * Defaults to `ordered`.\n */\n kind?: \"ordered\" | \"category\";\n /** How an x value reads, such as \"Mar 26\" for \"2026-03-01\". */\n format?: (value: unknown) => string;\n };\n series: { key: keyof Datum & string; name: string; unit?: string }[];\n}\n\n/** Long cells are cut here: a notes field should not cost a page of tokens. */\nconst MAX_CELL_CHARS = 200;\n\nexport function toCell(value: unknown): string | number | boolean | null {\n if (value === null || value === undefined) return null;\n if (typeof value === \"number\" || typeof value === \"boolean\") return value;\n const text =\n value instanceof Date\n ? value.toISOString()\n : typeof value === \"string\"\n ? value\n : JSON.stringify(value);\n return text.length > MAX_CELL_CHARS ? `${text.slice(0, MAX_CELL_CHARS)}\u2026` : text;\n}\n\n/** The tabs as the store holds them. `onChange` is read at call time. */\nexport function tabsDeclaration(latest: () => AssistantTabs): ViewControlDeclaration {\n const tabs = latest();\n return {\n kind: \"tabs\",\n label: tabs.label,\n options: tabs.tabs.map(({ value, label }) => ({ value, label })),\n value: tabs.value,\n onChange: (value) => latest().onChange(value),\n };\n}\n\n/**\n * What identifies a tab set's declaration, without its closure: when this\n * changes, the store needs the new declaration.\n */\nexport function tabsSignature(tabs: AssistantTabs): string {\n return JSON.stringify({\n label: tabs.label,\n tabs: tabs.tabs.map(({ value, label }) => ({ value, label })),\n value: tabs.value,\n });\n}\n\n/** The query parameters a table accepts, as the store holds them. */\nfunction queryParams(query: AssistantTableQuery<unknown>): QueryParamDeclaration[] {\n return Object.entries(query.params).map(([key, param]) => ({\n key,\n kind: param.kind,\n values: param.kind === \"enum\" ? [...param.values] : [],\n description: param.description ?? \"\",\n }));\n}\n\n/**\n * What identifies a table's declaration, without its rows or closures: when\n * this changes, the store needs the new declaration. The rows' count is in\n * it, because the screen context reports how many are loaded.\n */\nexport function tableSignature<Row>(table: AssistantTable<Row>): string {\n return JSON.stringify({\n label: table.label,\n columns: table.columns.map(({ key, label }) => ({ key, label })),\n rowIdName: table.rowIdName,\n opens: table.opens?.screenId,\n total: table.total,\n loaded: table.rows.length,\n query: table.query\n ? {\n params: queryParams(table.query as AssistantTableQuery<unknown>),\n canShow: table.query.show !== undefined,\n }\n : undefined,\n });\n}\n\n/** The table as the store holds it. Rows and closures are read at call time. */\nexport function tableDeclaration<Row>(latest: () => AssistantTable<Row>): TableDeclaration {\n const table = latest();\n\n // One rendering for rows read and rows found, so a search result looks\n // exactly like a read: declared columns only, cells cut, `open` params.\n const render = (row: Row) => {\n const { rowId, columns, opens } = latest();\n const out: Record<string, string | number | boolean | null> = {};\n for (const column of columns) {\n const raw = column.value ? column.value(row) : (row as Record<string, unknown>)[column.key];\n out[column.label] = toCell(raw);\n }\n return {\n id: rowId(row),\n ...(opens ? { open: opens.params(row) } : {}),\n ...out,\n };\n };\n\n return {\n label: table.label,\n columns: table.columns.map(({ key, label }) => ({ key, label })),\n rowIdName: table.rowIdName,\n opens: table.opens?.screenId,\n total: table.total,\n loadedRows: table.rows.length,\n read: (offset, limit) => latest().rows.slice(offset, offset + limit).map(render),\n ...(table.query\n ? {\n query: {\n params: queryParams(table.query as AssistantTableQuery<unknown>),\n run: async (params: Record<string, string>) => {\n const query = latest().query;\n if (!query) throw new Error(\"This table can no longer be searched.\");\n const found = await query.run(params);\n return {\n rows: found.rows.map(render),\n ...(found.total === undefined ? {} : { total: found.total }),\n };\n },\n ...(table.query.show\n ? { show: (params: Record<string, string>) => latest().query?.show?.(params) }\n : {}),\n },\n }\n : {}),\n };\n}\n\n/** Each series' points, read from the chart as it is now. */\nfunction chartPoints<Datum>(chart: AssistantChart<Datum>, key: string): ChartPoint[] {\n const format = chart.x.format;\n return chart.data.flatMap((datum) => {\n const raw = (datum as Record<string, unknown>)[key];\n const y = typeof raw === \"number\" ? raw : raw == null ? NaN : Number(raw);\n if (!Number.isFinite(y)) return [];\n const xRaw = (datum as Record<string, unknown>)[chart.x.key];\n return [{ x: format ? format(xRaw) : String(xRaw), y }];\n });\n}\n\n/**\n * The chart as the store holds it: each series summarised here, in the\n * browser, and the points read at call time when an exact value is wanted.\n */\nexport function chartDeclaration<Datum>(latest: () => AssistantChart<Datum>): ChartDeclaration {\n const chart = latest();\n const ordered = chart.x.kind !== \"category\";\n return {\n title: chart.title,\n xLabel: chart.x.label,\n xKind: ordered ? \"ordered\" : \"category\",\n series: chart.series.map((s) => {\n const summary = summarise(chartPoints(chart, s.key), ordered);\n return {\n name: s.name,\n ...(s.unit ? { unit: s.unit } : {}),\n ...(summary ? { summary } : {}),\n };\n }),\n read: (name, limit) => {\n const current = latest();\n return current.series\n .filter((s) => name === undefined || s.name === name)\n .map((s) => ({ name: s.name, points: thin(chartPoints(current, s.key), limit) }));\n },\n };\n}\n\n/** What identifies a chart's declaration: its summaries, not its closures. */\nexport function chartSignature<Datum>(chart: AssistantChart<Datum>): string {\n const { read: _read, ...declared } = chartDeclaration(() => chart);\n return JSON.stringify(declared);\n}\n", "/**\n * The app's declaration of itself.\n *\n * Spec \u00A75.1 \u2014 the host app publishes assistant-readable context rather than\n * the assistant scraping the DOM. This store is where those declarations\n * accumulate: the current screen from `useAssistantScreen`, the components\n * that mounted under it from `useAssistantComponent`, and the action handlers\n * from `useAssistantAction`.\n *\n * Deliberately framework-free and dependency-free. React binds to it through\n * `useSyncExternalStore`, which keeps the store itself testable without a\n * renderer and avoids pulling a state library into a published SDK.\n */\n\nimport type {\n ChartPoint,\n ChartSeries,\n ChartXKind,\n ComponentContext,\n ScreenContext,\n ViewControl,\n} from \"@mihe-dev/protocol\";\n\n/** What a screen declares about itself, minus the components. */\nexport interface ScreenDeclaration {\n screenId: string;\n route: string;\n title: string;\n description?: string;\n keywords?: string[];\n /**\n * Summary values visible on screen. Keep this small and scalar \u2014 counts,\n * totals, statuses. It is context for the assistant, not a data dump, and\n * every byte here is sent on every screen change.\n */\n visibleData?: Record<string, unknown>;\n /**\n * The page's data has not arrived yet. While true, `visibleData` is not\n * sent, and a navigation to this screen is not reported done.\n *\n * Without it, the counts a page declares before its queries resolve are\n * zeros, and an assistant that has just navigated here reads them as the\n * truth: \"you have no raw materials\", on a page about to show sixteen.\n */\n loading?: boolean;\n}\n\n/** What a component declares about itself. */\nexport interface ComponentDeclaration {\n type: ComponentContext[\"type\"];\n label: string;\n description?: string;\n actions?: string[];\n}\n\n/**\n * A tab set, or another control that changes what the screen shows\n * (amendment 67). `onChange` is the app's own code, which is what lets this\n * work however the tabs are built.\n */\nexport interface ViewControlDeclaration {\n kind: ViewControl[\"kind\"];\n label: string;\n options: { value: string; label: string }[];\n value?: string;\n onChange: (value: string) => void | Promise<void>;\n}\n\n/**\n * A table the assistant can read (amendment 67, step 2). The rows stay here,\n * read through `read_table` when a question needs them.\n */\nexport interface TableDeclaration {\n label: string;\n columns: { key: string; label: string }[];\n /** The input name a row's id fills, such as `orderId`. */\n rowIdName?: string;\n /** The screen a row opens. Each row read carries its `open` params. */\n opens?: string;\n /** Rows in the whole list, when the page shows only some. */\n total?: number;\n /** How many rows are loaded, for the screen context. */\n loadedRows: number;\n /** The rows as they are right now, rendered to declared columns only. */\n read: (offset: number, limit: number) => TableRow[];\n /** How the table can be searched past its loaded rows (amendment 67, step 3). */\n query?: TableQueryDeclaration;\n}\n\n/** One parameter a table search accepts, as the page declared it. */\nexport interface QueryParamDeclaration {\n key: string;\n kind: \"text\" | \"enum\" | \"date\";\n /** For `enum`: the values accepted. */\n values: string[];\n description: string;\n}\n\n/** A table's search: what it takes, and the app's code that runs it. */\nexport interface TableQueryDeclaration {\n params: QueryParamDeclaration[];\n /** Runs the app's own search and renders the result to declared columns. */\n run: (params: Record<string, string>) => Promise<{ rows: TableRow[]; total?: number }>;\n /** Makes the table on screen show this search, when the page can. */\n show?: (params: Record<string, string>) => void | Promise<void>;\n}\n\n/**\n * One row as the model sees it: its id, the params that open it when the\n * table opens a screen, and each declared column by label.\n */\nexport type TableRow = { id: string; open?: Record<string, string> } & Record<\n string,\n string | number | boolean | null | Record<string, string> | undefined\n>;\n\n/** A chart the assistant can explain (amendment 67, step 4). */\nexport interface ChartDeclaration {\n title: string;\n xLabel: string;\n xKind: ChartXKind;\n /** Names, units and summaries, as the screen context carries them. */\n series: ChartSeries[];\n /** The points as they are right now, thinned to `limit` per series. */\n read: (series: string | undefined, limit: number) => { name: string; points: ChartPoint[] }[];\n}\n\n/** A frontend action the assistant may run. */\nexport interface ActionHandler {\n name: string;\n handler: (input: Record<string, unknown>) => unknown | Promise<unknown>;\n}\n\ntype Listener = () => void;\n\nexport class AssistantStore {\n private screen: ScreenDeclaration | null = null;\n /** Insertion-ordered, so the context sent to the model is stable. */\n private components = new Map<string, ComponentDeclaration>();\n private controls = new Map<string, ViewControlDeclaration>();\n private tables = new Map<string, TableDeclaration>();\n private charts = new Map<string, ChartDeclaration>();\n /** Values a component on the screen adds to its `visibleData`, by key. */\n private values = new Map<string, Record<string, unknown>>();\n private actions = new Map<string, ActionHandler>();\n private listeners = new Set<Listener>();\n\n /** Cached so `useSyncExternalStore` sees a stable reference between changes. */\n private snapshot: ScreenContext | null = null;\n private snapshotDirty = true;\n\n subscribe = (listener: Listener): (() => void) => {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n };\n\n /**\n * The current screen context, or null before any screen has registered.\n *\n * Returns the same object until something actually changes, so React can\n * bail out of re-rendering and the provider can skip republishing.\n */\n getScreenContext = (): ScreenContext | null => {\n if (!this.snapshotDirty) return this.snapshot;\n\n this.snapshot = this.screen\n ? {\n screenId: this.screen.screenId,\n route: this.screen.route,\n title: this.screen.title,\n description: this.screen.description ?? \"\",\n keywords: this.screen.keywords ?? [],\n visibleData: (this.screen.loading\n ? {}\n : {\n ...(this.screen.visibleData ?? {}),\n ...Object.fromEntries(this.values),\n }) as ScreenContext[\"visibleData\"],\n components: [...this.components.entries()].map(([componentKey, meta]) => ({\n componentKey,\n type: meta.type,\n label: meta.label,\n description: meta.description ?? \"\",\n actions: meta.actions ?? [],\n })),\n controls: [...this.controls.entries()].map(([controlKey, control]) => ({\n controlKey,\n kind: control.kind,\n label: control.label,\n options: control.options.map(({ value, label }) => ({ value, label })),\n ...(control.value === undefined ? {} : { value: control.value }),\n })),\n tables: [...this.tables.entries()].map(([tableKey, table]) => ({\n tableKey,\n label: table.label,\n columns: table.columns.map(({ key, label }) => ({ key, label })),\n loadedRows: table.loadedRows,\n ...(table.rowIdName ? { rowId: table.rowIdName } : {}),\n ...(table.opens ? { opens: table.opens } : {}),\n ...(table.total === undefined ? {} : { totalRows: table.total }),\n ...(table.query\n ? {\n query: {\n params: table.query.params.map(({ key, kind, values, description }) => ({\n key,\n kind,\n values,\n description,\n })),\n canShow: table.query.show !== undefined,\n },\n }\n : {}),\n })),\n charts: [...this.charts.entries()].map(([chartKey, chart]) => ({\n chartKey,\n title: chart.title,\n xLabel: chart.xLabel,\n xKind: chart.xKind,\n series: chart.series,\n })),\n }\n : null;\n\n this.snapshotDirty = false;\n return this.snapshot;\n };\n\n setScreen(screen: ScreenDeclaration): void {\n const previous = this.screen;\n this.screen = screen;\n\n // Components belong to the screen that registered them. Moving to a new\n // screen clears them, so the assistant is never told it can point at an\n // element from the page the user just left.\n if (previous && previous.screenId !== screen.screenId) {\n this.components.clear();\n this.controls.clear();\n this.tables.clear();\n this.charts.clear();\n this.values.clear();\n }\n this.invalidate();\n }\n\n clearScreen(screenId: string): void {\n // Ignore a stale unmount: React mounts the next screen before unmounting\n // the previous one, so a late cleanup must not wipe the new screen.\n if (this.screen?.screenId !== screenId) return;\n this.screen = null;\n this.components.clear();\n this.controls.clear();\n this.tables.clear();\n this.charts.clear();\n this.values.clear();\n this.invalidate();\n }\n\n registerComponent(componentKey: string, meta: ComponentDeclaration): void {\n this.components.set(componentKey, meta);\n this.invalidate();\n }\n\n unregisterComponent(componentKey: string): void {\n if (this.components.delete(componentKey)) this.invalidate();\n }\n\n registerControl(controlKey: string, control: ViewControlDeclaration): void {\n this.controls.set(controlKey, control);\n this.invalidate();\n }\n\n unregisterControl(controlKey: string): void {\n if (this.controls.delete(controlKey)) this.invalidate();\n }\n\n getControl(controlKey: string): ViewControlDeclaration | undefined {\n return this.controls.get(controlKey);\n }\n\n registerTable(tableKey: string, table: TableDeclaration): void {\n this.tables.set(tableKey, table);\n this.invalidate();\n }\n\n unregisterTable(tableKey: string): void {\n if (this.tables.delete(tableKey)) this.invalidate();\n }\n\n getTable(tableKey: string): TableDeclaration | undefined {\n return this.tables.get(tableKey);\n }\n\n setValues(key: string, values: Record<string, unknown>): void {\n this.values.set(key, values);\n this.invalidate();\n }\n\n clearValues(key: string): void {\n if (this.values.delete(key)) this.invalidate();\n }\n\n registerChart(chartKey: string, chart: ChartDeclaration): void {\n this.charts.set(chartKey, chart);\n this.invalidate();\n }\n\n unregisterChart(chartKey: string): void {\n if (this.charts.delete(chartKey)) this.invalidate();\n }\n\n getChart(chartKey: string): ChartDeclaration | undefined {\n return this.charts.get(chartKey);\n }\n\n registerAction(action: ActionHandler): void {\n if (this.actions.has(action.name)) {\n console.warn(\n `[assistant] Two handlers registered for action \"${action.name}\". ` +\n `The most recent one wins; the earlier one will never run.`,\n );\n }\n this.actions.set(action.name, action);\n // Handlers are not part of the published context, so no invalidate.\n }\n\n unregisterAction(name: string): void {\n this.actions.delete(name);\n }\n\n getAction(name: string): ActionHandler | undefined {\n return this.actions.get(name);\n }\n\n /** Whether the current screen says its data is still loading. */\n isScreenLoading(): boolean {\n return this.screen?.loading === true;\n }\n\n hasComponent(componentKey: string): boolean {\n return this.components.has(componentKey);\n }\n\n private invalidate(): void {\n this.snapshotDirty = true;\n for (const listener of this.listeners) listener();\n }\n}\n", "/**\n * Microphone capture and speaker playback for voice sessions.\n *\n * One format everywhere: **16-bit PCM, 24 kHz, mono, little endian**, matching\n * the voice socket and the provider behind it. Nothing on this path transcodes,\n * which is the point \u2014 a resampler in a latency-critical loop buys nothing when\n * every participant already agrees on the rate.\n *\n * Two browser realities shape the code:\n *\n * - **iOS Safari will not start audio without a user gesture.** Push-to-talk\n * makes that free: the button press *is* the gesture. `resume()` is called on\n * press rather than at construction for exactly this reason, and calling it\n * from anywhere else will produce a context stuck in `suspended` with no\n * error.\n * - **`AudioContext` may refuse the sample rate we ask for.** Chrome, Firefox,\n * and current Safari honour it; older Safari does not. So capture resamples\n * when it has to rather than assuming, and playback hands the browser a\n * buffer tagged 24 kHz and lets the graph resample it.\n */\n\nimport { VOICE_SAMPLE_RATE } from \"@mihe-dev/protocol\";\n\n/**\n * Frames sent up per second. 50 works out to 20 ms per frame \u2014 small enough\n * that the end of an utterance is not padded by a buffer waiting to fill, and\n * large enough that the socket is not doing a send per millisecond.\n */\nconst FRAME_MS = 20;\n\n/**\n * Playback runs this far ahead of the speaker.\n *\n * Without it every chunk that arrives a few milliseconds late produces an\n * audible click as the graph runs dry between buffers. With it, network jitter\n * up to this much is inaudible, at the cost of the same delay before the first\n * word. 200 ms is well under the threshold where a reply feels slow.\n */\nconst JITTER_BUFFER_MS = 200;\n\n/** The AudioWorklet processor, injected as a Blob so the package ships one file. */\nconst CAPTURE_WORKLET = `\nclass MiheCapture extends AudioWorkletProcessor {\n constructor(options) {\n super();\n // A render quantum is 128 samples \u2014 about 5 ms at 24 kHz \u2014 so posting one\n // per call means roughly 190 messages a second, each becoming its own\n // WebSocket frame and its own JSON envelope upstream. Under push-to-talk\n // that was merely wasteful; with the microphone open continuously it\n // saturated the relay and audio started being dropped, which reads to the\n // user as the assistant simply not answering.\n // Defaulted rather than assumed. An absent option would make \\`target\\`\n // undefined, every arithmetic result NaN, and the fill loop exit without\n // ever posting \u2014 total silence, no error, and nothing in any log to say\n // the microphone had stopped working.\n this.target = options?.processorOptions?.frameSamples || 480;\n this.buffer = new Float32Array(this.target);\n this.filled = 0;\n }\n\n process(inputs) {\n const channel = inputs[0] && inputs[0][0];\n // No input yet, or the track ended. Keep the node alive either way:\n // returning false here permanently removes it from the graph, and the\n // user is only between utterances.\n if (!channel) return true;\n\n let offset = 0;\n while (offset < channel.length) {\n const take = Math.min(this.target - this.filled, channel.length - offset);\n this.buffer.set(channel.subarray(offset, offset + take), this.filled);\n this.filled += take;\n offset += take;\n\n if (this.filled === this.target) {\n this.port.postMessage(this.buffer.slice());\n this.filled = 0;\n }\n }\n\n return true;\n }\n}\nregisterProcessor('mihe-capture', MiheCapture);\n`;\n\n/** Float samples in [-1, 1] to little-endian 16-bit PCM. */\nexport function toPcm16(samples: Float32Array): ArrayBuffer {\n const buffer = new ArrayBuffer(samples.length * 2);\n const view = new DataView(buffer);\n for (let i = 0; i < samples.length; i += 1) {\n // Clamped before scaling: a sample above 1 would wrap to a large negative\n // number, which is heard as a loud click rather than as clipping.\n const clamped = Math.max(-1, Math.min(1, samples[i] ?? 0));\n view.setInt16(i * 2, clamped * 0x7fff, true);\n }\n return buffer;\n}\n\n/** Little-endian 16-bit PCM back to float samples. */\nexport function fromPcm16(buffer: ArrayBuffer): Float32Array {\n const view = new DataView(buffer);\n const samples = new Float32Array(buffer.byteLength / 2);\n for (let i = 0; i < samples.length; i += 1) {\n samples[i] = view.getInt16(i * 2, true) / 0x7fff;\n }\n return samples;\n}\n\n/**\n * How loud a chunk of speech is, from 0 (silence) to 1 (as loud as speech gets).\n *\n * RMS rather than peak, because a peak jumps on every consonant and a ring\n * drawn from it flickers instead of breathing. Scaled by eye: ordinary speech\n * into a laptop microphone sits around 0.05 to 0.15 RMS, so a square root and\n * a gain put a normal voice in the middle of the range rather than at its foot.\n */\nexport function pcmLevel(pcm: ArrayBuffer): number {\n const samples = fromPcm16(pcm);\n if (samples.length === 0) return 0;\n let sum = 0;\n for (const sample of samples) sum += sample * sample;\n const rms = Math.sqrt(sum / samples.length);\n return Math.min(1, Math.sqrt(rms) * 1.8);\n}\n\n/**\n * Linear resample, used only when the browser refused our sample rate.\n *\n * Linear rather than anything better on purpose: the input is a voice at\n * 44.1 or 48 kHz going to 24 kHz, the output is fed to a speech model rather\n * than to a listener, and a windowed-sinc filter would cost more than it could\n * possibly be worth here. When the rates already match this is not called at\n * all.\n */\nexport function resample(\n samples: Float32Array,\n fromRate: number,\n toRate: number,\n): Float32Array {\n if (fromRate === toRate) return samples;\n\n const ratio = fromRate / toRate;\n const length = Math.floor(samples.length / ratio);\n const out = new Float32Array(length);\n\n for (let i = 0; i < length; i += 1) {\n const position = i * ratio;\n const index = Math.floor(position);\n const next = Math.min(index + 1, samples.length - 1);\n const fraction = position - index;\n out[i] = (samples[index] ?? 0) * (1 - fraction) + (samples[next] ?? 0) * fraction;\n }\n return out;\n}\n\n/**\n * The microphone, as a stream of PCM frames.\n *\n * Capture runs continuously once started and is *gated* rather than stopped\n * between utterances: `getUserMedia` takes hundreds of milliseconds and shows a\n * permission prompt the first time, so tearing it down on every button release\n * would clip the beginning of the next sentence \u2014 which is exactly where the\n * word the model needs most usually is.\n */\nexport class MicrophoneCapture {\n private context: AudioContext | null = null;\n private stream: MediaStream | null = null;\n private node: AudioWorkletNode | ScriptProcessorNode | null = null;\n private source: MediaStreamAudioSourceNode | null = null;\n private open = false;\n\n constructor(private readonly onFrame: (pcm: ArrayBuffer) => void) {}\n\n /** True while frames are being delivered. */\n get capturing(): boolean {\n return this.open;\n }\n\n /**\n * Ask for the microphone and build the graph. Must be called from a user\n * gesture on iOS Safari.\n */\n async start(): Promise<void> {\n if (this.context) {\n await this.context.resume();\n return;\n }\n\n this.stream = await navigator.mediaDevices.getUserMedia({\n audio: {\n channelCount: 1,\n // All three on: the assistant's own voice comes out of the same device\n // in a hands-free session, and without cancellation the model hears\n // itself and answers its own last sentence.\n echoCancellation: true,\n noiseSuppression: true,\n autoGainControl: true,\n },\n });\n\n const context = new AudioContext({ sampleRate: VOICE_SAMPLE_RATE });\n this.context = context;\n this.source = context.createMediaStreamSource(this.stream);\n\n const deliver = (samples: Float32Array) => {\n if (!this.open) return;\n this.onFrame(\n toPcm16(resample(samples, context.sampleRate, VOICE_SAMPLE_RATE)),\n );\n };\n\n try {\n const url = URL.createObjectURL(\n new Blob([CAPTURE_WORKLET], { type: \"application/javascript\" }),\n );\n await context.audioWorklet.addModule(url);\n URL.revokeObjectURL(url);\n\n const worklet = new AudioWorkletNode(context, \"mihe-capture\", {\n processorOptions: {\n // Measured in the context's own samples: the resample to 24 kHz\n // happens after this, on the main thread.\n frameSamples: Math.round((context.sampleRate * FRAME_MS) / 1000),\n },\n });\n worklet.port.onmessage = (event) =>\n deliver(event.data as Float32Array);\n this.source.connect(worklet);\n // Worklets are not pulled unless something downstream asks for their\n // output. A zero-gain node is the standard way to run the graph without\n // routing the microphone to the speaker.\n const sink = context.createGain();\n sink.gain.value = 0;\n worklet.connect(sink).connect(context.destination);\n this.node = worklet;\n } catch {\n // Older Safari, and any browser that blocks Blob worklet modules under a\n // strict CSP. Deprecated but universally present, and it runs on the\n // main thread, which for one mono voice track is fine.\n const frames = Math.max(\n 256,\n 2 ** Math.round(Math.log2((context.sampleRate * FRAME_MS) / 1000)),\n );\n const processor = context.createScriptProcessor(frames, 1, 1);\n processor.onaudioprocess = (event) =>\n deliver(new Float32Array(event.inputBuffer.getChannelData(0)));\n this.source.connect(processor);\n processor.connect(context.destination);\n this.node = processor;\n }\n\n await context.resume();\n }\n\n /** Begin delivering frames. */\n unmute(): void {\n this.open = true;\n }\n\n /** Stop delivering frames, without releasing the microphone. */\n mute(): void {\n this.open = false;\n }\n\n /** Release the device and tear down the graph. */\n async stop(): Promise<void> {\n this.open = false;\n this.node?.disconnect();\n this.source?.disconnect();\n this.stream?.getTracks().forEach((track) => track.stop());\n await this.context?.close().catch(() => undefined);\n this.node = null;\n this.source = null;\n this.stream = null;\n this.context = null;\n }\n}\n\n/**\n * The assistant's voice, as a queue of scheduled buffers.\n *\n * Chunks are scheduled back to back against the audio clock rather than played\n * on arrival, because `AudioBufferSourceNode.start()` with no time argument\n * plays *now* \u2014 and forty of those a second produce overlapping speech. The\n * running `nextStart` cursor is what turns a stream of chunks into continuous\n * audio.\n */\nexport class SpeakerQueue {\n /**\n * Called with each chunk's level at the moment it starts playing, not when\n * it arrives. The provider streams seconds ahead of the speaker, so a level\n * taken on arrival would animate words the user has not heard yet.\n */\n constructor(private readonly onLevel?: (level: number) => void) {}\n\n private levelTimers = new Set<ReturnType<typeof setTimeout>>();\n private context: AudioContext | null = null;\n private gain: GainNode | null = null;\n private sources = new Set<AudioBufferSourceNode>();\n /** Audio-clock time the next chunk should begin at. */\n private nextStart = 0;\n /** Audio-clock time the current run of speech began. */\n private startedAt = 0;\n\n /** Must be called from a user gesture on iOS Safari. */\n async start(): Promise<void> {\n if (this.context) {\n await this.context.resume();\n return;\n }\n const context = new AudioContext({ sampleRate: VOICE_SAMPLE_RATE });\n this.gain = context.createGain();\n this.gain.connect(context.destination);\n this.context = context;\n await context.resume();\n }\n\n /** Queue a chunk of assistant speech. */\n enqueue(pcm: ArrayBuffer): void {\n const context = this.context;\n if (!context || !this.gain) return;\n\n const samples = fromPcm16(pcm);\n if (samples.length === 0) return;\n\n // Tagged 24 kHz regardless of the context's own rate, so a browser that\n // refused our request resamples on playback instead of playing the\n // assistant at the wrong pitch.\n const buffer = context.createBuffer(1, samples.length, VOICE_SAMPLE_RATE);\n buffer.getChannelData(0).set(samples);\n\n const source = context.createBufferSource();\n source.buffer = buffer;\n source.connect(this.gain);\n\n const now = context.currentTime;\n if (this.nextStart < now) {\n // Either the first chunk of an utterance or a gap the network opened up.\n // Restarting the cursor ahead of `now` is what the jitter buffer is:\n // scheduling at `now` exactly means anything arriving late is already\n // too late.\n this.nextStart = now + JITTER_BUFFER_MS / 1000;\n this.startedAt = this.nextStart;\n }\n\n source.start(this.nextStart);\n\n if (this.onLevel) {\n const level = pcmLevel(pcm);\n const report = this.onLevel;\n const timer = setTimeout(\n () => {\n this.levelTimers.delete(timer);\n report(level);\n },\n Math.max(0, (this.nextStart - now) * 1000),\n );\n this.levelTimers.add(timer);\n }\n\n this.nextStart += buffer.duration;\n\n this.sources.add(source);\n source.onended = () => this.sources.delete(source);\n }\n\n /**\n * How much of the current utterance the user has actually heard, in\n * milliseconds.\n *\n * This is the number the runtime needs to truncate the model's history on a\n * barge-in, and it is not the same as how much has been *received*: the\n * provider streams several seconds ahead of the speaker, so trusting bytes\n * delivered would tell the model the user heard a sentence they were\n * interrupted three words into.\n */\n heardMs(): number {\n if (!this.context || this.startedAt === 0) return 0;\n const heard = this.context.currentTime - this.startedAt;\n return Math.max(0, Math.round(heard * 1000));\n }\n\n /** True while anything is scheduled or playing. */\n get speaking(): boolean {\n return this.sources.size > 0;\n }\n\n /** Drop everything queued \u2014 an interruption, or the end of a session. */\n flush(): void {\n for (const source of this.sources) {\n try {\n source.stop();\n } catch {\n // Already ended. `stop()` on a finished node throws in some browsers.\n }\n }\n this.sources.clear();\n this.nextStart = 0;\n this.startedAt = 0;\n\n // Levels for audio that will now never play, and a last zero so a ring\n // following them settles instead of freezing mid-word.\n for (const timer of this.levelTimers) clearTimeout(timer);\n this.levelTimers.clear();\n this.onLevel?.(0);\n }\n\n async stop(): Promise<void> {\n this.flush();\n this.gain?.disconnect();\n await this.context?.close().catch(() => undefined);\n this.gain = null;\n this.context = null;\n }\n}\n", "/**\n * The voice session (doc 03, phase 5a).\n *\n * A second, short-lived WebSocket carrying audio only. Commands and\n * confirmation cards keep arriving on the chat socket, which is the whole\n * design: voice is an input modality, not a second command channel, so a\n * confirmation card is the same card whether the user typed or spoke and there\n * is exactly one place that draws it.\n *\n * Push-to-talk, deliberately, before hands-free. It has no VAD edge cases, it\n * works on a phone the day it ships, and the button press doubles as the user\n * gesture iOS Safari requires before any audio can start. Hands-free is 5b.\n */\n\nimport {\n VOICE_SAMPLE_RATE,\n type VoiceClientMessage,\n type VoiceEndReason,\n type VoiceServerMessage,\n} from \"@mihe-dev/protocol\";\n\nimport { MicrophoneCapture, SpeakerQueue, pcmLevel } from \"./audio\";\n\nexport type VoiceState =\n | \"idle\"\n | \"connecting\"\n /** Connected, microphone closed, waiting for the user to press. */\n | \"listening\"\n /** The button is held down and audio is going up. */\n | \"recording\"\n /** The utterance is committed and the model has not answered yet. */\n | \"thinking\"\n | \"speaking\"\n | \"error\";\n\nexport type VoiceEvent =\n | { type: \"state\"; state: VoiceState }\n | {\n type: \"transcript\";\n role: \"user\" | \"assistant\";\n text: string;\n final: boolean;\n }\n | {\n type: \"error\";\n code: string;\n message: string;\n /** True when the session survived and the user can simply try again. */\n recoverable: boolean;\n fallbackToChat: boolean;\n }\n /** The duration ceiling is near. The session is still running. */\n | { type: \"ending_soon\"; secondsRemaining: number }\n /**\n * The session finished the way it was meant to \u2014 the minutes ran out, or\n * nobody spoke. Deliberately not an `error`: the chat socket is untouched and\n * the transcript is already in the conversation, so the user has lost\n * nothing and should not be told they have.\n */\n | { type: \"ended\"; reason: VoiceEndReason; message: string };\n\nexport interface VoiceSessionOptions {\n /** Voice socket URL, already carrying the single-use token. */\n url: string;\n /**\n * Hands-free: the microphone stays open, the provider decides when an\n * utterance ends, and the user can talk over the assistant.\n *\n * The trade is echo. Under push-to-talk the microphone is shut except while\n * the button is held, so the assistant physically cannot hear itself; here\n * it can, and browser echo cancellation is the only thing between the two.\n * On a laptop that is usually enough. On a phone with the speaker beside the\n * microphone it may not be, and the failure is the assistant answering its\n * own last sentence.\n */\n handsFree?: boolean;\n onEvent: (event: VoiceEvent) => void;\n /**\n * How loud each side is, 0 to 1, as it happens: the user's voice as it is\n * captured, the assistant's as it plays. Deliberately not a `VoiceEvent`:\n * it fires tens of times a second, and whatever listens should move a ring\n * rather than re-render a component tree.\n */\n onLevel?: (source: \"user\" | \"assistant\", level: number) => void;\n /** Injectable for tests. */\n socketFactory?: (url: string) => VoiceSocketLike;\n}\n\n/** The slice of `WebSocket` a voice session uses. */\nexport interface VoiceSocketLike {\n binaryType: string;\n send(data: string | ArrayBuffer): void;\n close(): void;\n onopen: ((event: unknown) => void) | null;\n onclose: ((event: unknown) => void) | null;\n onerror: ((event: unknown) => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n}\n\nexport class VoiceSession {\n private socket: VoiceSocketLike | null = null;\n private state: VoiceState = \"idle\";\n private mic: MicrophoneCapture;\n private speaker: SpeakerQueue;\n /**\n * Audio captured since a held press began, not yet sent.\n *\n * A press on a draggable orb is ambiguous for its first moment: it may be\n * the start of speech or the start of a drag. Holding the frames here means\n * neither answer costs anything. Confirmed, they go up in order and the first\n * word survives. Discarded, they never leave the browser, because the voice\n * socket has no way to take back audio it has been sent.\n */\n private held: ArrayBuffer[] | null = null;\n private stopped = false;\n /**\n * Milliseconds of audio captured since the last commit.\n *\n * Counted rather than timed from the button press, because those differ:\n * the microphone graph delivers in render quanta, so a press held for\n * exactly the floor produces slightly less audio than the clock suggests.\n * The provider measures the buffer, so we measure the buffer too.\n */\n private utteranceMs = 0;\n /** The assistant's line so far, joined from streamed transcript deltas. */\n private assistantLine = \"\";\n /** Reports playback position while speaking, for barge-in truncation. */\n private playbackTimer: ReturnType<typeof setInterval> | null = null;\n /**\n * The provider's shortest acceptable utterance, learned at `ready`.\n *\n * Defaults high enough to be safe before the runtime has told us, since\n * guessing low means the first press of a session is the one that fails.\n */\n private minUtteranceMs = 150;\n\n constructor(private readonly options: VoiceSessionOptions) {\n this.speaker = new SpeakerQueue((level) => options.onLevel?.(\"assistant\", level));\n this.mic = new MicrophoneCapture((pcm) => this.sendAudio(pcm));\n }\n\n get currentState(): VoiceState {\n return this.state;\n }\n\n /**\n * Open the socket and the audio devices.\n *\n * Must be called from a user gesture. Both the microphone permission prompt\n * and iOS Safari's audio unlock require one, and a session started from an\n * effect will connect happily and then be silent in both directions with no\n * error anywhere.\n */\n async start(): Promise<void> {\n if (this.socket) return;\n this.stopped = false;\n this.setState(\"connecting\");\n\n try {\n // Devices before the socket: a user who declines the microphone prompt\n // should not have consumed their single-use voice token, because the\n // next attempt would then fail for a second, unrelated reason.\n await this.mic.start();\n await this.speaker.start();\n } catch (cause) {\n this.emit({\n type: \"error\",\n code: \"microphone_unavailable\",\n message:\n cause instanceof Error && cause.name === \"NotAllowedError\"\n ? \"Microphone access was declined, so voice cannot start.\"\n : \"No microphone is available.\",\n // No microphone means no voice at all, so this one really is the end.\n recoverable: false,\n fallbackToChat: true,\n });\n this.setState(\"error\");\n return;\n }\n\n const factory =\n this.options.socketFactory ??\n ((url: string) => new WebSocket(url) as unknown as VoiceSocketLike);\n\n const socket = factory(this.options.url);\n socket.binaryType = \"arraybuffer\";\n this.socket = socket;\n\n socket.onmessage = (event) => {\n if (typeof event.data === \"string\") {\n this.handleControl(event.data);\n } else if (event.data instanceof ArrayBuffer) {\n this.speaker.enqueue(event.data);\n // The runtime never says `speaking`; the first audio of a reply is how\n // the client knows. Without this a push-to-talk session sat in\n // `thinking` after its first answer, `speech_done` only moves\n // `speaking` on, and every later press was refused.\n if (this.state === \"thinking\") this.setState(\"speaking\");\n }\n };\n\n socket.onclose = () => {\n // No reconnect, unlike the chat socket. A voice token is single use and\n // a provider session is not resumable, so reconnecting would mean a new\n // session with none of this one's context \u2014 better to end cleanly and\n // let the user press the button again.\n void this.stop();\n };\n\n socket.onerror = () => {\n // `onclose` always follows.\n };\n }\n\n /**\n * The user pressed the button. Push-to-talk only.\n *\n * With `hold`, capture starts but nothing is sent, and the assistant is not\n * interrupted, until `confirmUtterance`. `discardUtterance` drops it\n * instead. For a control where a press might turn out to be a drag.\n */\n beginUtterance(options?: { hold?: boolean }): void {\n if (this.options.handsFree) return;\n if (this.state !== \"listening\" && this.state !== \"speaking\") return;\n\n this.utteranceMs = 0;\n\n if (options?.hold) {\n this.held = [];\n this.mic.unmute();\n this.setState(\"recording\");\n return;\n }\n\n this.interruptPlayback();\n this.mic.unmute();\n this.setState(\"recording\");\n }\n\n /** A held press was speech after all: send what was captured, and carry on. */\n confirmUtterance(): void {\n const held = this.held;\n if (!held) return;\n this.held = null;\n this.interruptPlayback();\n for (const pcm of held) this.sendAudio(pcm);\n }\n\n /** A held press was a drag: drop what was captured, and send nothing. */\n discardUtterance(): void {\n if (!this.held) return;\n this.held = null;\n this.mic.mute();\n this.utteranceMs = 0;\n this.options.onLevel?.(\"user\", 0);\n this.setState(this.speaker.speaking ? \"speaking\" : \"listening\");\n }\n\n /**\n * Pressing while the assistant is talking is an interruption. Split out so a\n * held press does not cut the assistant off before it is known to be speech.\n */\n private interruptPlayback(): void {\n // Stop the playback the user has clearly stopped listening to, and tell\n // the runtime how much of it they actually heard so the model's history\n // matches their memory of the conversation.\n if (this.speaker.speaking) {\n this.sendControl({\n type: \"playback\",\n data: { ms: this.speaker.heardMs() },\n });\n this.speaker.flush();\n }\n }\n\n /** The user released the button. Push-to-talk only. */\n endUtterance(): void {\n if (this.options.handsFree) return;\n if (this.state !== \"recording\") return;\n // Released before anyone decided: a press that never moved is speech.\n this.confirmUtterance();\n this.mic.mute();\n this.options.onLevel?.(\"user\", 0);\n\n // A tap, not speech. Committing this comes back from the provider as\n // \"buffer too small\", which is a true statement about something the user\n // should never have been able to send \u2014 so it is caught here instead, and\n // again in the runtime, which is the authority for clients that are not\n // this one.\n if (this.utteranceMs < this.minUtteranceMs) {\n this.emit({\n type: \"error\",\n code: \"utterance_too_short\",\n message: \"Hold the button while you speak.\",\n recoverable: true,\n fallbackToChat: false,\n });\n this.setState(\"listening\");\n return;\n }\n\n this.sendControl({ type: \"commit\" });\n this.setState(\"thinking\");\n }\n\n async stop(): Promise<void> {\n if (this.stopped) return;\n this.stopped = true;\n\n if (this.playbackTimer !== null) {\n clearInterval(this.playbackTimer);\n this.playbackTimer = null;\n }\n\n this.sendControl({ type: \"end\" });\n this.socket?.close();\n this.socket = null;\n\n await Promise.all([this.mic.stop(), this.speaker.stop()]);\n this.setState(\"idle\");\n }\n\n // ------------------------------------------------------------- internals\n\n private handleControl(raw: string): void {\n let message: VoiceServerMessage;\n try {\n message = JSON.parse(raw) as VoiceServerMessage;\n } catch {\n return;\n }\n\n switch (message.type) {\n case \"ready\":\n // Tolerating an absent value keeps a newer SDK working against an older\n // runtime, where guessing low would fail the first press of a session.\n this.minUtteranceMs = message.data.minUtteranceMs ?? this.minUtteranceMs;\n\n // Hands-free opens the microphone here and not in `start`, and the\n // difference is a whole class of bug. Unmuting early meant capturing\n // from before the socket existed until the provider accepted its\n // configuration \u2014 a second or two of the room \u2014 and every frame after\n // the socket opened was sent, queued behind a relay still sitting in\n // `wait_for_ready`. It arrived in one burst the moment the loop began\n // reading, the provider's VAD read the burst as an utterance, and the\n // session answered something nobody said. Reliably at the start of a\n // session and never afterwards, because it can only happen once.\n //\n // The runtime already holds this rule for its own upstream: nothing is\n // sent until the provider has accepted the configuration. This is the\n // client keeping the same rule.\n //\n // Still open for the whole session once it opens \u2014 there is no press\n // to open it on, and opening per detected utterance would clip the\n // first word, which is usually the one carrying the intent.\n if (this.options.handsFree) this.mic.unmute();\n\n this.setState(\"listening\");\n break;\n\n case \"transcript\": {\n // The runtime forwards the provider's deltas as they arrive, a few\n // characters each, so the line so far is built here. Emitting the raw\n // delta made the caption show only the latest fragment, which read as\n // gibberish flickering over the orb.\n let text = message.data.text;\n if (message.data.role === \"assistant\") {\n if (message.data.final) {\n this.assistantLine = \"\";\n } else {\n this.assistantLine += text;\n text = this.assistantLine;\n }\n }\n this.emit({\n type: \"transcript\",\n role: message.data.role,\n text,\n final: message.data.final,\n });\n break;\n }\n\n case \"status\":\n // A status of `listening` means one thing: the user interrupted. The\n // queued audio has to go, or the assistant carries on out of the\n // speaker after being cut off \u2014 and the runtime has already truncated\n // its history to what was heard, so the two would disagree about the\n // conversation.\n //\n // This is safe only because a *finished* reply arrives as\n // `speech_done` instead. The relay used to send both, and flushing on\n // a completed response threw away everything still queued \u2014 heard as\n // every answer breaking off part-way through.\n if (\n message.data.state === \"listening\" &&\n this.speaker.speaking\n ) {\n this.speaker.flush();\n }\n // An interrupted line may never be marked final, and its leftovers\n // must not prefix the next reply's caption.\n if (message.data.state === \"listening\") this.assistantLine = \"\";\n\n // The server's view of the session, except while the button is down \u2014\n // the client knows about a press before the server does, and letting a\n // status frame overwrite `recording` makes the button flicker.\n if (this.state !== \"recording\") {\n this.setState(\n message.data.state === \"reconnecting\"\n ? \"connecting\"\n : message.data.state,\n );\n }\n break;\n\n case \"speech_done\":\n // From `thinking` too: a reply can finish with no audio at all, a tool\n // call and nothing said, and the session must still take the next\n // press.\n if (this.state === \"speaking\" || this.state === \"thinking\") {\n this.setState(\"listening\");\n }\n break;\n\n case \"ending_soon\":\n this.emit({\n type: \"ending_soon\",\n secondsRemaining: message.data.secondsRemaining,\n });\n break;\n\n case \"ended\":\n this.emit({\n type: \"ended\",\n reason: message.data.reason,\n message: message.data.message,\n });\n // `stop` leaves the state `idle`, which is the point: the microphone\n // button goes back to its resting look rather than the failed one, and\n // pressing it again starts a fresh session on the same conversation.\n void this.stop();\n break;\n\n case \"error\":\n this.emit({\n type: \"error\",\n code: message.data.code,\n message: message.data.message,\n recoverable: message.data.recoverable,\n fallbackToChat: message.data.fallbackToChat,\n });\n // A recoverable error is one operation failing, not the call ending.\n // Tearing the session down here was the bug: a mistimed press hung up\n // on the user and made them start over.\n if (!message.data.recoverable) {\n this.setState(\"error\");\n void this.stop();\n }\n break;\n }\n }\n\n private sendAudio(pcm: ArrayBuffer): void {\n // Hands-free sends throughout: the provider's VAD is what decides where an\n // utterance begins, and it cannot do that on audio it never receives.\n if (!this.options.handsFree && this.state !== \"recording\") return;\n if (this.held) {\n this.held.push(pcm);\n this.options.onLevel?.(\"user\", pcmLevel(pcm));\n return;\n }\n this.options.onLevel?.(\"user\", pcmLevel(pcm));\n // Two bytes per sample, one channel.\n this.utteranceMs += (pcm.byteLength * 1000) / (2 * VOICE_SAMPLE_RATE);\n try {\n this.socket?.send(pcm);\n } catch {\n // A send on a closing socket throws; `onclose` handles the rest.\n }\n }\n\n private sendControl(message: VoiceClientMessage): void {\n try {\n this.socket?.send(JSON.stringify(message));\n } catch {\n // Same.\n }\n }\n\n private setState(state: VoiceState): void {\n if (this.state === state) return;\n this.state = state;\n this.reportPlaybackWhileSpeaking();\n this.options.onEvent({ type: \"state\", state });\n }\n\n /**\n * Tell the runtime how much of the current reply has actually been heard,\n * a few times a second, for as long as the assistant is speaking.\n *\n * Push-to-talk does not need this: the interruption *is* a button press, so\n * the position can be measured at exactly that moment. Hands-free has no\n * such moment on this side \u2014 the provider notices the user talking, upstream\n * \u2014 so the runtime has to already hold a recent figure when it truncates.\n *\n * Without it, truncation falls back to nothing and the model's history keeps\n * the whole reply, including the part the user cut off. It then answers\n * follow-ups about words nobody heard.\n *\n * Four frames a second of small JSON, against fifty of audio.\n */\n private reportPlaybackWhileSpeaking(): void {\n const shouldReport =\n Boolean(this.options.handsFree) && this.state === \"speaking\";\n\n if (!shouldReport) {\n if (this.playbackTimer !== null) {\n clearInterval(this.playbackTimer);\n this.playbackTimer = null;\n }\n return;\n }\n\n if (this.playbackTimer !== null) return;\n\n this.playbackTimer = setInterval(() => {\n if (!this.speaker.speaking) return;\n this.sendControl({\n type: \"playback\",\n data: { ms: this.speaker.heardMs() },\n });\n }, 250);\n }\n\n private emit(event: VoiceEvent): void {\n this.options.onEvent(event);\n }\n}\n\n/**\n * Derive the voice socket URL from the chat socket URL.\n *\n * One `url` prop for the whole SDK rather than two: the two sockets are always\n * served by the same runtime, and a second prop would be a second thing to get\n * wrong in every integration for no configurability anyone wants.\n */\nexport function voiceUrl(\n chatUrl: string,\n token: string,\n handsFree = false,\n): string {\n const base = chatUrl.replace(/\\/v1\\/ws\\/?$/, \"/v1/voice\");\n const separator = base.includes(\"?\") ? \"&\" : \"?\";\n const mode = handsFree ? \"&handsFree=true\" : \"\";\n return `${base}${separator}token=${encodeURIComponent(token)}${mode}`;\n}\n", "/**\n * `Mihe` \u2014 the assistant without a framework (doc 08).\n *\n * The wiring between the store, the client and whatever UI draws it. It owns\n * three responsibilities and nothing else:\n *\n * 1. Publish the screen context whenever the app's declaration changes.\n * 2. Execute commands the runtime sends, and ack every one of them.\n * 3. Hold the conversation the UI renders.\n *\n * State is an immutable snapshot, replaced on every change, so React's\n * `useSyncExternalStore` reads it with no adapter: the React provider is a\n * thin wrapper over this class, and the two cannot disagree about what a\n * proposal or a voice event does.\n */\n\nimport type {\n KnowledgeSourceRef,\n ProposalStatus,\n ScreenContext,\n WidgetSettings,\n} from \"@mihe-dev/protocol\";\n\nimport {\n AssistantClient,\n type ClientEvent,\n type ConnectionState,\n type ProposalPayload,\n} from \"./client\";\nimport { appView, DESCRIBE_TIMEOUT_MS, hintedView, type CardView, type DescribeProposal, type ProposalDescription } from \"./card-view\";\nimport { executeCommand } from \"./commands\";\nimport { formatContextReport } from \"./debug\";\nimport {\n chartDeclaration,\n chartSignature,\n tableDeclaration,\n tableSignature,\n tabsDeclaration,\n tabsSignature,\n type AssistantChart,\n type AssistantTable,\n type AssistantTabs,\n} from \"./declarations\";\nimport { COMPONENT_ATTRIBUTE, HighlightEngine } from \"./highlight\";\nimport {\n AssistantStore,\n type ActionHandler,\n type ComponentDeclaration,\n type ScreenDeclaration,\n} from \"./store\";\nimport { VoiceSession, voiceUrl, type VoiceEvent, type VoiceState } from \"./voice\";\n\n/** How long to coalesce screen changes before publishing. */\nconst SCREEN_DEBOUNCE_MS = 60;\n\n/**\n * How long a navigate ack waits for the destination to publish. Comfortably\n * under the runtime's 10s command timeout, and long enough for a route that\n * loads data before registering itself.\n */\nconst SCREEN_ARRIVAL_TIMEOUT_MS = 2500;\n\nexport const SDK_VERSION = \"0.2.6\";\n\n/**\n * DOM id of the assistant panel, so a custom trigger can point\n * `aria-controls` at it.\n */\nexport const ASSISTANT_PANEL_ID = \"mihe-panel\";\n\nexport interface ChatMessage {\n id: string;\n role: \"user\" | \"assistant\";\n text: string;\n /** True while the assistant is still streaming into this message. */\n streaming?: boolean;\n /**\n * Documentation this answer drew on, if any.\n *\n * Worth surfacing rather than keeping for debugging: an answer a user can\n * trace to a named section of their own handbook is one they can check, and\n * \"where did that come from?\" is the first question anyone asks of an\n * assistant that suddenly knows their internal policy.\n */\n sources?: KnowledgeSourceRef[];\n /** Pictures the person attached, shown with their words. Mihe was sent the app's description, not these. */\n attachments?: { name: string; previewUrl?: string }[];\n}\n\n/**\n * A picture waiting to go with the next message (amendment 102). The app's\n * `describeAttachment` turns it into words; only the words reach the runtime.\n */\nexport interface PendingAttachment {\n id: string;\n name: string;\n /** A local preview (`blob:`), for the chip and the sent bubble. */\n previewUrl?: string;\n status: \"reading\" | \"ready\" | \"failed\";\n /** The app's description, once ready. */\n text?: string;\n /** Why it couldn't be read, when it failed. */\n error?: string;\n}\n\n/**\n * Turn a file the person attached into words for the assistant (amendment\n * 102). The paperclip appears only when the app passes this: images never go\n * anywhere the app didn't send them. Throw with a message the person can read\n * to refuse one.\n */\nexport type DescribeAttachment = (file: File) => Promise<string>;\n\n/** At most this many waiting at once; a picture is read for meaning, not collected. */\nexport const MAX_ATTACHMENTS = 4;\n/** How long the app has to describe one before the chip says it couldn't. */\nexport const ATTACHMENT_TIMEOUT_MS = 90_000;\n\n/**\n * The voice session, as the UI sees it.\n *\n * `available` is answered by the runtime rather than assumed: voice is a\n * per-app setting with a real price attached, so a microphone button that\n * appears and then fails on the first press is worse than no button. It stays\n * false until the runtime has actually issued a permit.\n */\nexport interface VoiceControls {\n available: boolean;\n state: VoiceState;\n /** True while the session is hands-free rather than push-to-talk. */\n handsFree: boolean;\n /** The line currently being spoken, either side. Empty between utterances. */\n caption: string;\n /**\n * Something about the session worth saying that is not a failure \u2014 the\n * ceiling approaching, or a session that has just reached it.\n *\n * Separate from `lastError` so a host app's own UI can draw the two\n * differently, which is the whole point: a session ending on time is the\n * ordinary case, and putting it in the same red box as a dead provider tells\n * the user their conversation broke when it did not.\n */\n notice: string | null;\n /**\n * Open the socket and the microphone. Must be called from a user gesture \u2014\n * both the permission prompt and iOS Safari's audio unlock require one.\n *\n * `handsFree` leaves the microphone open and lets the provider decide where\n * utterances begin and end, so the user can interrupt mid-reply. The cost is\n * that the assistant can hear itself; see `VoiceSessionOptions`.\n */\n start: (options?: {\n handsFree?: boolean;\n /**\n * Open the conversation panel as the session starts, so what is said\n * appears there as the session goes. True by default. The orb passes false, because it\n * shows voice in itself and the point of it is that the page stays visible.\n */\n openPanel?: boolean;\n }) => void;\n stop: () => void;\n /**\n * Push-to-talk: press and release.\n *\n * `press({ hold: true })` captures without sending until `confirm()`, and\n * `discard()` drops it: for a control where a press may turn into a drag.\n * Releasing a held press confirms it.\n */\n press: (options?: { hold?: boolean }) => void;\n release: () => void;\n confirm: () => void;\n discard: () => void;\n /**\n * Follow how loud each side is, 0 to 1, as it happens. Returns the\n * unsubscribe. Outside the state snapshot on purpose: it fires tens of\n * times a second, and a listener should move an element directly, not\n * re-render.\n */\n onLevel: (listener: (source: \"user\" | \"assistant\", level: number) => void) => () => void;\n}\n\n/** Everything a UI draws, as one immutable snapshot. */\nexport interface MiheState {\n connection: ConnectionState;\n messages: ChatMessage[];\n /** Confirmation cards awaiting a decision. */\n proposals: ProposalPayload[];\n /**\n * What each card says, by proposal id (amendment 99): the app's description\n * or the registry's hints. `\"pending\"` while the app is describing it, when\n * the card shows its title and keeps Confirm disabled.\n */\n cardViews: Record<string, CardView | \"pending\">;\n /** True from the moment a turn starts until it completes or fails. */\n busy: boolean;\n lastError: string | null;\n /**\n * Whether the assistant UI is showing.\n *\n * Held here rather than inside the widget so that anything in the host\n * app \u2014 a nav button, a command palette, a keyboard shortcut, a plain div \u2014\n * can open the assistant.\n */\n isOpen: boolean;\n /**\n * What the assistant calls itself, from the app's settings in the dashboard.\n * `null` until the handshake answers; a title passed in code still wins.\n */\n assistantName: string | null;\n /**\n * The customer's launcher icon, uploaded in the dashboard (amendment 58).\n * `null` means draw the Mihe mark.\n */\n launcherIconUrl: string | null;\n /**\n * How the widget presents itself, from the app's settings in the dashboard\n * (amendment 61). `null` until the handshake answers.\n */\n widgetSettings: WidgetSettings | null;\n /** Realtime voice (doc 03). */\n voice: VoiceControls;\n /** What the app takes as attachments (an `accept` list), or null when it takes none (no `describeAttachment`). */\n attachAccept: string | null;\n /** Pictures waiting to go with the next message. */\n attachments: PendingAttachment[];\n}\n\nexport interface MiheOptions {\n /** Runtime WebSocket URL. */\n url: string;\n /** The app's public key. Safe to ship in a browser bundle. */\n publicKey: string;\n /**\n * Fetches a signed user token from the host app's own backend.\n *\n * This is the app's authority, not the browser's: the runtime reads the\n * user's id and permissions from this token's verified claims and from\n * nothing else, so a user cannot grant themselves access by editing\n * anything on this side.\n */\n getUserToken: () => Promise<string>;\n /**\n * Route change handler. Wire this to your router \u2014 the default is a full\n * page load, which works but throws away app state.\n */\n navigate?: (route: string) => void;\n /**\n * Open a registered dialog. Return `false` when the key is not one this\n * app can open, so the assistant is told rather than reporting success for\n * a form that never appeared.\n */\n openModal?: (modalKey: string) => void | boolean;\n /**\n * Called when a confirmed action finishes, whatever the outcome.\n *\n * Exists because the assistant changes data the host app is already\n * displaying, and an app with a client-side cache has no way to know. The\n * usual implementation is one line \u2014 refetch what the page shows \u2014 and\n * without it a user watches an action succeed while the page goes on\n * showing what was true before it.\n */\n onActionComplete?: (action: {\n actionName: string;\n status: ProposalStatus;\n result?: unknown;\n }) => void;\n /**\n * Log what each screen costs to the console: how many tokens one answer\n * re-sends, and which tools make up most of it (amendment 67). Turn it on\n * while building an integration; leave it off in production.\n */\n debug?: boolean;\n /**\n * Say what a confirmation card means, from the app's own data (amendment\n * 99): before and after (\"$12 \u2192 $13\"), money in the right currency,\n * thumbnails of the photos involved. Handed exactly the input that will run;\n * describe that and nothing else. Without it, or when it returns null, the\n * card uses the registry's display hints. See `ProposalDescription`.\n */\n describeProposal?: DescribeProposal;\n /**\n * Let people attach pictures (amendment 102): the chat box gets a paperclip\n * and takes pasted images, and each is handed here to be described. The\n * assistant gets the description with the message; the picture stays with\n * the app. Without it there's no paperclip.\n */\n describeAttachment?: DescribeAttachment;\n /** What the paperclip offers, as an `accept` list. Default `image/*`. */\n attachmentAccept?: string;\n}\n\n/**\n * How the approaching ceiling is put to the user.\n *\n * Phrased here rather than by the runtime because it is a countdown, and the\n * runtime sends a number. Rounded to five seconds: the figure is approximate\n * by the time it has crossed a socket and been rendered, and a notice reading\n * \"27 seconds left\" claims a precision nobody has.\n */\nfunction endingSoonNotice(secondsRemaining: number): string {\n const rounded = Math.max(5, Math.round(secondsRemaining / 5) * 5);\n return `About ${rounded} seconds left of voice. The conversation carries on in text.`;\n}\n\nfunction messageId(prefix: string): string {\n return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n}\n\n/**\n * A declaration made through one of the `register\u2026` methods. Calling the\n * method again with the same id updates this entry, so the undo from the\n * first call still removes it; an undo from before an unregister finds a\n * different entry and does nothing, so a late cleanup cannot remove what the\n * next page declared.\n */\ninterface Declared<T, D> {\n latest: T;\n signature: string;\n declaration: D;\n}\n\n/** Create the assistant and connect it. */\nexport function createMihe(options: MiheOptions): Mihe {\n const mihe = new Mihe(options);\n mihe.connect();\n return mihe;\n}\n\nexport class Mihe {\n /** The app's declaration of itself. */\n readonly store = new AssistantStore();\n private readonly highlighter = new HighlightEngine();\n private options: MiheOptions;\n private state: MiheState;\n private readonly listeners = new Set<() => void>();\n\n private client: AssistantClient | null = null;\n private voiceSession: VoiceSession | null = null;\n /**\n * Set while the user is waiting on a permit they asked for.\n *\n * A token arriving with this false is one the runtime issued for a probe at\n * connect time, and starting a session on it would turn the microphone on\n * without anyone having asked.\n */\n private voiceRequested = false;\n /** The mode asked for, read when the permit comes back. */\n private requestedHandsFree = false;\n /** Whether the session being requested should open the panel. */\n private requestedOpenPanel = true;\n private readonly levelListeners = new Set<\n (source: \"user\" | \"assistant\", level: number) => void\n >();\n\n /** The route of the last screen context actually sent to the runtime. */\n private publishedRoute: string | null = null;\n /** The last screen context actually sent, for acks that check a view. */\n private publishedScreen: ScreenContext | null = null;\n /** Whether that screen had finished loading its data when it was sent. */\n private publishedSettled = false;\n /** Navigate and set_view acks waiting for the screen to report in. */\n private readonly screenWaiters = new Set<(screen: ScreenContext) => void>();\n private publishTimer: ReturnType<typeof setTimeout> | null = null;\n private unsubscribeStore: (() => void) | null = null;\n\n private readonly tabs = new Map<string, Declared<AssistantTabs, unknown>>();\n private readonly tables = new Map<string, Declared<AssistantTable<unknown>, unknown>>();\n private readonly charts = new Map<string, Declared<AssistantChart<unknown>, unknown>>();\n private readonly actions = new Map<string, Declared<ActionHandler[\"handler\"], ActionHandler>>();\n private lastScreen: string | null = null;\n\n constructor(options: MiheOptions) {\n this.options = { ...options };\n this.state = {\n connection: \"idle\",\n messages: [],\n proposals: [],\n cardViews: {},\n busy: false,\n lastError: null,\n isOpen: false,\n assistantName: null,\n launcherIconUrl: null,\n widgetSettings: null,\n attachAccept: options.describeAttachment ? options.attachmentAccept ?? \"image/*\" : null,\n attachments: [],\n voice: {\n available: false,\n state: \"idle\",\n handsFree: false,\n caption: \"\",\n notice: null,\n start: this.startVoiceRequest,\n stop: this.stopVoice,\n press: (options) => this.voiceSession?.beginUtterance(options),\n release: () => this.voiceSession?.endUtterance(),\n confirm: () => this.voiceSession?.confirmUtterance(),\n discard: () => this.voiceSession?.discardUtterance(),\n onLevel: (listener) => {\n this.levelListeners.add(listener);\n return () => {\n this.levelListeners.delete(listener);\n };\n },\n },\n };\n }\n\n // ---- state -------------------------------------------------------------\n\n /** The current state. The same object until something changes. */\n getState = (): MiheState => this.state;\n\n /** Called after every change of state. Returns the unsubscribe. */\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n };\n\n /** Voice controls and state; the same as `getState().voice`. */\n get voice(): VoiceControls {\n return this.state.voice;\n }\n\n private setState(patch: Partial<MiheState>): void {\n this.state = { ...this.state, ...patch };\n // Copied, so a listener that unsubscribes while being called is safe.\n for (const listener of [...this.listeners]) listener();\n }\n\n private setVoice(patch: Partial<VoiceControls>): void {\n this.setState({ voice: { ...this.state.voice, ...patch } });\n }\n\n private setMessages(update: (messages: ChatMessage[]) => ChatMessage[]): void {\n this.setState({ messages: update(this.state.messages) });\n }\n\n // ---- lifecycle ---------------------------------------------------------\n\n /**\n * Replace options. Callbacks take effect at once; a new `url`, `publicKey`\n * or `debug` reconnects, since each belongs to the session.\n */\n update(options: Partial<MiheOptions>): void {\n const previous = this.options;\n this.options = { ...previous, ...options };\n const attachAccept = this.options.describeAttachment ? this.options.attachmentAccept ?? \"image/*\" : null;\n if (attachAccept !== this.state.attachAccept) this.setState({ attachAccept });\n const identityChanged =\n this.options.url !== previous.url ||\n this.options.publicKey !== previous.publicKey ||\n Boolean(this.options.debug) !== Boolean(previous.debug);\n if (identityChanged && this.client) {\n this.disconnect();\n this.connect();\n }\n }\n\n /** Open the session. Called by `createMihe`; does nothing if connected. */\n connect(): void {\n if (this.client) return;\n const client = new AssistantClient({\n url: this.options.url,\n publicKey: this.options.publicKey,\n getUserToken: () => this.options.getUserToken(),\n sdkVersion: SDK_VERSION,\n onEvent: this.handleEvent,\n debug: this.options.debug ?? false,\n });\n this.client = client;\n\n // Debounced, because a screen with twenty components fires twenty\n // registrations as it mounts and they should travel as one snapshot.\n this.unsubscribeStore = this.store.subscribe(() => {\n if (this.publishTimer !== null) clearTimeout(this.publishTimer);\n this.publishTimer = setTimeout(this.publish, SCREEN_DEBOUNCE_MS);\n });\n this.publish();\n\n void client.connect();\n }\n\n /**\n * Close the session, the voice session and any highlight. The\n * conversation and the declarations stay, for a later `connect()`.\n */\n disconnect(): void {\n if (this.publishTimer !== null) clearTimeout(this.publishTimer);\n this.publishTimer = null;\n this.unsubscribeStore?.();\n this.unsubscribeStore = null;\n this.client?.close();\n this.client = null;\n void this.voiceSession?.stop();\n this.voiceSession = null;\n this.highlighter.clear();\n }\n\n /** Disconnect and drop every listener. The instance is done after this. */\n destroy(): void {\n this.disconnect();\n this.listeners.clear();\n this.levelListeners.clear();\n }\n\n // ---- conversation ------------------------------------------------------\n\n send = (text: string): void => {\n const trimmed = text.trim();\n const ready = this.state.attachments.filter((a) => a.status === \"ready\");\n // A picture still being read would arrive without its words; the send button waits too.\n if (this.state.attachments.some((a) => a.status === \"reading\")) return;\n if (!trimmed && !ready.length) return;\n\n // What the assistant reads: the words, then each picture as the app described it.\n const wire = [\n trimmed || \"(See the attached picture.)\",\n ...ready.map((a) => `[Attached picture \"${a.name}\", as the app describes it: ${a.text}]`),\n ].join(\"\\n\\n\");\n\n // Opening on send is what makes a \"Help me add a product\" button work as\n // one call: the reply has to be visible somewhere. Harmless for host apps\n // rendering their own always-visible panel.\n const id = messageId(\"msg\");\n const shown: ChatMessage = { id, role: \"user\", text: trimmed, ...(ready.length ? { attachments: ready.map((a) => ({ name: a.name, ...(a.previewUrl ? { previewUrl: a.previewUrl } : {}) })) } : {}) };\n // Failed chips go with the send too; their previews aren't shown anywhere now.\n for (const a of this.state.attachments) if (a.status === \"failed\" && a.previewUrl) URL.revokeObjectURL(a.previewUrl);\n this.setState({\n isOpen: true,\n messages: [...this.state.messages, shown],\n attachments: [],\n lastError: null,\n });\n this.client?.sendMessage(wire, id);\n };\n\n /**\n * Attach a picture to the next message (amendment 102). Does nothing when\n * the app takes no attachments. The app describes it now, so the words are\n * ready by the time the person presses send.\n */\n attach = (file: File): void => {\n const describe = this.options.describeAttachment;\n if (!describe) return;\n const patch = (id: string, change: Partial<PendingAttachment>) =>\n this.setState({ attachments: this.state.attachments.map((a) => (a.id === id ? { ...a, ...change } : a)) });\n if (this.state.attachments.length >= MAX_ATTACHMENTS) {\n this.setState({ lastError: `Up to ${MAX_ATTACHMENTS} pictures at a time.` });\n return;\n }\n if (!acceptsFile(this.state.attachAccept ?? \"\", file)) {\n this.setState({ lastError: `\"${file.name}\" isn't a kind of file this can read.` });\n return;\n }\n const id = messageId(\"att\");\n const previewUrl = file.type.startsWith(\"image/\") && typeof URL.createObjectURL === \"function\" ? URL.createObjectURL(file) : undefined;\n this.setState({ attachments: [...this.state.attachments, { id, name: file.name || \"picture\", status: \"reading\", ...(previewUrl ? { previewUrl } : {}) }], lastError: null });\n let timer: ReturnType<typeof setTimeout> | undefined;\n Promise.race([\n Promise.resolve().then(() => describe(file)),\n new Promise<never>((_, reject) => { timer = setTimeout(() => reject(new Error(\"It took too long to read.\")), ATTACHMENT_TIMEOUT_MS); }),\n ])\n .then((text) => {\n const words = typeof text === \"string\" ? text.trim() : \"\";\n patch(id, words ? { status: \"ready\", text: words } : { status: \"failed\", error: \"Nothing could be read from it.\" });\n // On a call nobody presses Send: a picture goes as soon as it's read, so\n // what's said next can be about it (amendment 102). Once all are read.\n if (words && this.onCall() && !this.state.attachments.some((a) => a.status === \"reading\")) this.send(\"\");\n })\n .catch((cause: unknown) => patch(id, { status: \"failed\", error: cause instanceof Error && cause.message ? cause.message : \"It couldn't be read.\" }))\n .finally(() => clearTimeout(timer));\n };\n\n /** A voice call is open (any state but idle, connecting or error). */\n private onCall(): boolean {\n return ![\"idle\", \"connecting\", \"error\"].includes(this.state.voice.state);\n }\n\n /** Take a waiting picture off the next message. */\n removeAttachment = (id: string): void => {\n const gone = this.state.attachments.find((a) => a.id === id);\n if (!gone) return;\n if (gone.previewUrl) URL.revokeObjectURL(gone.previewUrl);\n this.setState({ attachments: this.state.attachments.filter((a) => a.id !== id) });\n };\n\n /**\n * Work out what a card says (amendment 99). The app's description when it\n * gives one in time, else the registry's hints. Until then the card is\n * pending, so nobody confirms a card whose words are about to change.\n */\n private describe(proposal: ProposalPayload): void {\n const set = (view: CardView | \"pending\") => {\n // A card already answered or withdrawn doesn't come back.\n if (view !== \"pending\" && !this.state.proposals.some((p) => p.proposalId === proposal.proposalId)) return;\n this.setState({ cardViews: { ...this.state.cardViews, [proposal.proposalId]: view } });\n };\n const describe = this.options.describeProposal;\n if (!describe) { set(hintedView(proposal)); return; }\n set(\"pending\");\n let settled = false;\n const finish = (d: ProposalDescription | null | undefined) => {\n if (settled) return; settled = true;\n set(d && Array.isArray(d.lines) ? appView(d) : hintedView(proposal));\n };\n const timer = setTimeout(() => finish(null), DESCRIBE_TIMEOUT_MS);\n Promise.resolve()\n .then(() => describe(proposal))\n .then((d) => { clearTimeout(timer); finish(d); })\n .catch((cause) => {\n clearTimeout(timer);\n console.warn(\"[mihe] describeProposal failed; the card uses the registry's hints.\", cause);\n finish(null);\n });\n }\n\n respondToProposal = (proposalId: string, confirmed: boolean): void => {\n this.client?.confirmAction(proposalId, confirmed);\n if (!confirmed) {\n this.setState({\n proposals: this.state.proposals.filter((p) => p.proposalId !== proposalId),\n });\n }\n };\n\n open = (): void => {\n if (!this.state.isOpen) this.setState({ isOpen: true });\n };\n\n close = (): void => {\n if (this.state.isOpen) this.setState({ isOpen: false });\n };\n\n toggle = (): void => {\n this.setState({ isOpen: !this.state.isOpen });\n };\n\n /**\n * A one-tap \"ask this\", for suggestion chips and help links:\n * `chip.onclick = mihe.prompt(\"How do I add a product?\")`.\n */\n prompt(text: string): () => void {\n return () => this.send(text);\n }\n\n // ---- declarations ------------------------------------------------------\n\n /**\n * Declare the screen the user is on. Call it again when what the screen\n * shows changes; call the returned function when the user leaves it.\n *\n * `visibleData` is what makes answers concrete \u2014 \"you have 7 low-stock\n * products\" rather than \"check your stock levels\" \u2014 so include the summary\n * values already on screen, and only those.\n */\n setScreen(screen: ScreenDeclaration): () => void {\n const serialized = JSON.stringify(screen);\n // A render function that declares on every render would otherwise send\n // the same screen over the socket each time.\n if (serialized !== this.lastScreen || this.store.getScreenContext()?.screenId !== screen.screenId) {\n this.lastScreen = serialized;\n this.store.setScreen(JSON.parse(serialized) as ScreenDeclaration);\n }\n return () => {\n this.store.clearScreen(screen.screenId);\n if (this.store.getScreenContext() === null) this.lastScreen = null;\n };\n }\n\n /**\n * Declare a component the assistant can talk about or point at. Marks\n * `element` so it can be found; pass `null` for one that is described but\n * cannot be highlighted.\n */\n registerComponent(\n element: Element | null,\n componentKey: string,\n meta: ComponentDeclaration,\n ): () => void {\n element?.setAttribute(COMPONENT_ATTRIBUTE, componentKey);\n this.store.registerComponent(componentKey, { ...meta });\n return () => {\n this.store.unregisterComponent(componentKey);\n if (element?.getAttribute(COMPONENT_ATTRIBUTE) === componentKey) {\n element.removeAttribute(COMPONENT_ATTRIBUTE);\n }\n };\n }\n\n /**\n * Supply the code for an action the assistant may run in the browser.\n *\n * The action itself \u2014 its risk tier, permissions, and whether it needs\n * confirmation \u2014 is configured in the dashboard, not here. Whatever the\n * handler returns becomes the tool result the model sees, so return\n * something descriptive. Throwing reports the failure honestly.\n */\n registerAction(name: string, handler: ActionHandler[\"handler\"]): () => void {\n const existing = this.actions.get(name);\n if (existing && this.store.getAction(name) === existing.declaration) {\n existing.latest = handler;\n return () => this.undoAction(name, existing);\n }\n const entry: Declared<ActionHandler[\"handler\"], ActionHandler> = {\n latest: handler,\n signature: name,\n declaration: { name, handler: (input) => entry.latest(input) },\n };\n this.actions.set(name, entry);\n this.store.registerAction(entry.declaration);\n return () => this.undoAction(name, entry);\n }\n\n private undoAction(name: string, entry: Declared<ActionHandler[\"handler\"], ActionHandler>) {\n if (this.actions.get(name) !== entry) return;\n this.actions.delete(name);\n if (this.store.getAction(name) === entry.declaration) this.store.unregisterAction(name);\n }\n\n /**\n * Let the assistant see which tab is open and switch to another. Pass the\n * tab that is really showing as `value`, and call this again when it\n * changes: a switch is not reported done until `value` matches.\n */\n registerTabs(tabs: AssistantTabs): () => void {\n return this.declare(\n this.tabs,\n tabs.id,\n tabs,\n tabsSignature(tabs),\n (latest) => tabsDeclaration(latest),\n (id) => this.store.getControl(id),\n (id, declaration) => this.store.registerControl(id, declaration),\n (id) => this.store.unregisterControl(id),\n );\n }\n\n /**\n * Let the assistant read a table. The rows are not sent with the screen;\n * the model reads them when a question needs them, and only the declared\n * columns ever leave the browser (amendment 67). Call it again when the\n * rows change.\n */\n registerTable<Row>(table: AssistantTable<Row>): () => void {\n return this.declare(\n this.tables,\n table.id,\n table as AssistantTable<unknown>,\n tableSignature(table),\n (latest) => tableDeclaration(latest),\n (id) => this.store.getTable(id),\n (id, declaration) => this.store.registerTable(id, declaration),\n (id) => this.store.unregisterTable(id),\n );\n }\n\n /**\n * Let the assistant explain a chart. Each series is summarised here, in\n * the browser; the points are read when an exact value is wanted. Call it\n * again when the data changes.\n */\n registerChart<Datum>(chart: AssistantChart<Datum>): () => void {\n return this.declare(\n this.charts,\n chart.id,\n chart as AssistantChart<unknown>,\n chartSignature(chart),\n (latest) => chartDeclaration(latest),\n (id) => this.store.getChart(id),\n (id, declaration) => this.store.registerChart(id, declaration),\n (id) => this.store.unregisterChart(id),\n );\n }\n\n /**\n * Add figures to the screen's `visibleData` under `key`: the stat cards\n * inside a tab, a summary panel. Dropped when the screen changes.\n */\n setValues(key: string, values: Record<string, unknown>): () => void {\n this.store.setValues(key, JSON.parse(JSON.stringify(values)) as Record<string, unknown>);\n return () => this.store.clearValues(key);\n }\n\n /**\n * One declaration kind's register-update-undo, shared so tabs, tables and\n * charts behave the same. The store's entry is re-registered only when the\n * signature changes, or when the store no longer holds it because the\n * screen changed underneath.\n */\n private declare<T, D>(\n entries: Map<string, Declared<T, unknown>>,\n id: string,\n value: T,\n signature: string,\n build: (latest: () => T) => D,\n current: (id: string) => D | undefined,\n register: (id: string, declaration: D) => void,\n unregister: (id: string) => void,\n ): () => void {\n let entry = entries.get(id) as Declared<T, D> | undefined;\n if (entry && current(id) === entry.declaration) {\n entry.latest = value;\n if (entry.signature !== signature) {\n const target = entry;\n entry.signature = signature;\n entry.declaration = build(() => target.latest);\n register(id, entry.declaration);\n }\n } else {\n const fresh = { latest: value, signature } as Declared<T, D>;\n fresh.declaration = build(() => fresh.latest);\n entries.set(id, fresh as Declared<T, unknown>);\n register(id, fresh.declaration);\n entry = fresh;\n }\n const mine = entry;\n return () => {\n if (entries.get(id) !== mine) return;\n entries.delete(id);\n if (current(id) === mine.declaration) unregister(id);\n };\n }\n\n /**\n * Make an element open and close the assistant, keyboard and screen\n * reader included: a `div` with only a click handler is invisible to both,\n * and that is the most common way a custom trigger ships broken.\n */\n bindTrigger(element: HTMLElement): () => void {\n const native = element instanceof HTMLButtonElement;\n const added: string[] = [\"aria-expanded\", \"aria-controls\", \"aria-haspopup\"];\n if (!native) {\n element.setAttribute(\"role\", \"button\");\n added.push(\"role\");\n if (!element.hasAttribute(\"tabindex\")) {\n element.setAttribute(\"tabindex\", \"0\");\n added.push(\"tabindex\");\n }\n }\n element.setAttribute(\"aria-controls\", ASSISTANT_PANEL_ID);\n element.setAttribute(\"aria-haspopup\", \"dialog\");\n\n const sync = () => element.setAttribute(\"aria-expanded\", String(this.state.isOpen));\n const onClick = () => this.toggle();\n // Space and Enter are what a native button responds to; a button\n // already turns them into a click.\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n this.toggle();\n }\n };\n\n sync();\n const unsubscribe = this.subscribe(sync);\n element.addEventListener(\"click\", onClick);\n if (!native) element.addEventListener(\"keydown\", onKeyDown);\n\n return () => {\n unsubscribe();\n element.removeEventListener(\"click\", onClick);\n element.removeEventListener(\"keydown\", onKeyDown);\n for (const attribute of added) element.removeAttribute(attribute);\n };\n }\n\n // ---- the runtime's events ---------------------------------------------\n\n private handleEvent = (event: ClientEvent): void => {\n switch (event.type) {\n case \"state\":\n this.setState({ connection: event.state });\n // Re-sent on every change of connection, so a reconnected session\n // learns the screen without waiting for the user to move.\n if (this.publishTimer !== null) clearTimeout(this.publishTimer);\n this.publishTimer = null;\n this.publish();\n break;\n\n case \"turn_start\":\n this.setState({\n busy: true,\n messages: [\n ...this.state.messages,\n { id: event.turnId, role: \"assistant\", text: \"\", streaming: true },\n ],\n });\n break;\n\n case \"delta\":\n this.setMessages((current) =>\n current.map((message) =>\n message.id === event.turnId\n ? { ...message, text: message.text + event.text }\n : message,\n ),\n );\n break;\n\n case \"complete\":\n this.setState({\n busy: false,\n messages: this.state.messages.map((message) =>\n message.id === event.turnId\n ? {\n ...message,\n text: event.message,\n streaming: false,\n sources: event.sources.length ? event.sources : undefined,\n }\n : message,\n ),\n });\n break;\n\n case \"command\": {\n const ctx = {\n store: this.store,\n highlighter: this.highlighter,\n navigate:\n this.options.navigate ??\n ((route: string) => {\n window.location.assign(route);\n }),\n screenPublished: this.awaitScreenPublished,\n viewPublished: this.awaitViewPublished,\n openModal: this.options.openModal,\n };\n const client = this.client;\n // Always acks, including on a thrown handler, because the runtime is\n // blocking a turn until it hears back.\n void executeCommand(event.command, ctx)\n .then((result) =>\n client?.ackCommand(event.commandId, result.ok, result.result, result.error),\n )\n .catch((cause: unknown) =>\n client?.ackCommand(\n event.commandId,\n false,\n undefined,\n cause instanceof Error ? cause.message : \"The command failed.\",\n ),\n );\n break;\n }\n\n case \"proposal\":\n this.setState({ proposals: [...this.state.proposals, event.proposal] });\n this.describe(event.proposal);\n break;\n\n case \"action_status\": {\n if (event.status === \"executing\") break;\n\n // Retire the card and say what became of it. Without this the card\n // simply vanishes on approval and the assistant stays silent \u2014 which\n // reads as nothing having happened, for an action that may have just\n // notified a customer. The model does learn the outcome, but not\n // until its next turn, so it cannot be what tells the user.\n const settled = this.state.proposals.find((p) => p.proposalId === event.proposalId);\n\n if (settled) {\n // Keyed by the proposal, so a status arriving twice \u2014 a reconnect\n // replaying it, a double tap \u2014 settles to one line rather than two.\n const statusId = `status_${event.proposalId}`;\n if (event.status !== \"rejected\" && !this.state.messages.some((m) => m.id === statusId)) {\n // The label comes from the registry, like everything else on the\n // card, so this line cannot be steered by model output.\n const outcome =\n event.status === \"executed\"\n ? `${settled.label} \u2014 done.`\n : `${settled.label} \u2014 ${event.error ?? \"that did not work.\"}`;\n this.setMessages((messages) => [\n ...messages,\n { id: statusId, role: \"assistant\", text: outcome },\n ]);\n }\n\n // Host apps cache \u2014 an action that changed data server-side leaves\n // the page showing what was true a moment ago.\n this.options.onActionComplete?.({\n actionName: settled.actionName,\n status: event.status,\n result: event.result,\n });\n }\n\n this.setState({\n proposals: this.state.proposals.filter((p) => p.proposalId !== event.proposalId),\n ...(event.error ? { lastError: event.error } : {}),\n });\n break;\n }\n\n case \"voice_available\":\n this.setVoice({ available: event.available });\n break;\n\n case \"assistant_name\":\n this.setState({ assistantName: event.name });\n break;\n\n case \"launcher_icon\":\n this.setState({ launcherIconUrl: event.url });\n break;\n\n case \"widget_settings\":\n this.setState({ widgetSettings: event.settings });\n break;\n\n case \"context_report\":\n console.info(formatContextReport(event.report));\n break;\n\n case \"voice_token\":\n // Only ever the answer to a session the user actually asked for. It\n // used to double as an availability probe, which meant minting a\n // single-use credential to answer a yes/no question.\n if (!this.voiceRequested) break;\n this.voiceRequested = false;\n void this.startVoice(event.token);\n break;\n\n case \"error\":\n // Voice refusals are not chat errors and must not be shown as one:\n // the chat socket is fine, and the honest reading is \"voice is not\n // available here\", which is a button that should disappear.\n if (event.code.startsWith(\"voice_\")) {\n this.voiceRequested = false;\n this.setState({ busy: false, voice: { ...this.state.voice, available: false } });\n break;\n }\n this.setState({ busy: false, lastError: event.message });\n break;\n }\n };\n\n // ---- screen publishing -------------------------------------------------\n\n private publish = (): void => {\n this.publishTimer = null;\n const screen = this.store.getScreenContext();\n\n // A null screen is published, not skipped. The user has navigated to a\n // page the host app never declared, and the runtime has to be told \u2014\n // otherwise it answers with the last screen it heard about. Nothing is\n // sent before the first screen ever registers, since there is no move to\n // report yet.\n if (!screen && this.publishedRoute === null) return;\n\n this.client?.updateScreen(screen);\n this.publishedRoute = screen?.route ?? null;\n this.publishedScreen = screen;\n // Published either way, so the runtime knows where the user is and scopes\n // actions to it. But a navigation is not acked against a screen whose\n // data is still loading: the model's next step would read its empty\n // counts as the answer.\n this.publishedSettled = !this.store.isScreenLoading();\n if (!screen || !this.publishedSettled) return;\n // Copied, because a waiter that matches removes itself from the set.\n for (const waiter of [...this.screenWaiters]) waiter(screen);\n };\n\n /**\n * A navigate command cannot be acked until the destination has published\n * its context, or the runtime's next step would still be reasoning about\n * the screen the user just left. See `CommandContext.screenPublished`.\n */\n private awaitScreenPublished = (route: string, options?: { fresh?: boolean }) =>\n new Promise<boolean>((resolve) => {\n // Pages publish their path, never the query, so arrival is matched on\n // the path.\n const path = route.split(\"?\")[0];\n if (!options?.fresh && this.publishedRoute === path && this.publishedSettled) {\n return resolve(true);\n }\n const done = (arrived: boolean) => {\n this.screenWaiters.delete(waiter);\n clearTimeout(timer);\n resolve(arrived);\n };\n const waiter = (published: ScreenContext) => {\n if (published.route === path) done(true);\n };\n // Bounded, so a route the app never publishes stalls one turn rather\n // than hanging until the runtime's own ack timeout fires.\n const timer = setTimeout(() => done(false), SCREEN_ARRIVAL_TIMEOUT_MS);\n this.screenWaiters.add(waiter);\n });\n\n /**\n * A set_view ack waits the same way, for the tab the app was asked to show\n * to be the one the published screen reports. An app whose `onChange` did\n * nothing is reported as a failure rather than a switch that did not\n * happen.\n */\n private awaitViewPublished = (controlKey: string, value: string) =>\n new Promise<boolean>((resolve) => {\n const showing = (screen: ScreenContext | null) =>\n screen?.controls.some(\n (control) => control.controlKey === controlKey && control.value === value,\n ) ?? false;\n if (this.publishedSettled && showing(this.publishedScreen)) return resolve(true);\n\n const done = (shown: boolean) => {\n this.screenWaiters.delete(waiter);\n clearTimeout(timer);\n resolve(shown);\n };\n const waiter = (published: ScreenContext) => {\n if (showing(published)) done(true);\n };\n const timer = setTimeout(() => done(false), SCREEN_ARRIVAL_TIMEOUT_MS);\n this.screenWaiters.add(waiter);\n });\n\n // ---- voice -------------------------------------------------------------\n\n private startVoiceRequest = (options?: { handsFree?: boolean; openPanel?: boolean }) => {\n // A new session is not the place to still be reading why the last one\n // stopped.\n this.setVoice({ notice: null });\n // A fresh permit every time: they are single use and expire in a minute.\n this.requestedHandsFree = Boolean(options?.handsFree);\n this.requestedOpenPanel = options?.openPanel ?? true;\n this.voiceRequested = true;\n this.client?.requestVoiceToken();\n };\n\n private stopVoice = () => {\n this.voiceRequested = false;\n void this.voiceSession?.stop();\n this.voiceSession = null;\n // A countdown for a session the user has already closed is worse than no\n // notice: it is a wrong one.\n this.setVoice({ handsFree: false, notice: null });\n };\n\n private async startVoice(token: string): Promise<void> {\n const wanted = this.requestedHandsFree;\n this.setVoice({ handsFree: wanted });\n\n const session = new VoiceSession({\n url: voiceUrl(this.options.url, token, wanted),\n handsFree: wanted,\n onEvent: this.handleVoiceEvent,\n onLevel: (source, level) => {\n for (const listener of this.levelListeners) listener(source, level);\n },\n });\n this.voiceSession = session;\n // Opening the panel so what is said has somewhere to appear, for the\n // same reason `send` does, unless the caller shows voice itself.\n if (this.requestedOpenPanel) this.open();\n await session.start();\n }\n\n private handleVoiceEvent = (event: VoiceEvent): void => {\n switch (event.type) {\n case \"state\":\n this.setVoice({\n state: event.state,\n ...(event.state === \"idle\" || event.state === \"error\" ? { caption: \"\" } : {}),\n });\n break;\n\n case \"transcript\":\n // The caption is for words still being spoken. Once a line is final\n // it joins the transcript, so the caption is cleared rather than\n // showing the same sentence twice.\n if (event.final) {\n this.setState({\n voice: { ...this.state.voice, caption: \"\" },\n messages: [\n ...this.state.messages,\n { id: messageId(\"voice\"), role: event.role, text: event.text },\n ],\n });\n } else {\n this.setVoice({ caption: event.text });\n }\n break;\n\n case \"ending_soon\":\n this.setVoice({ notice: endingSoonNotice(event.secondsRemaining) });\n break;\n\n case \"ended\":\n // The runtime's own words, which say the conversation survives. The\n // session tore itself down, so nothing else will clear the mode; left\n // set, the next push-to-talk session would open as hands-free.\n this.setVoice({ caption: \"\", notice: event.message, handsFree: false });\n break;\n\n case \"error\":\n this.setState({ lastError: event.message });\n break;\n }\n };\n}\n\n/** Whether a file matches an `accept` list (\"image/*,.pdf\"). */\nexport function acceptsFile(accept: string, file: { name: string; type: string }): boolean {\n const name = file.name.toLowerCase();\n return accept.split(\",\").map((x) => x.trim().toLowerCase()).filter(Boolean).some((rule) =>\n rule.startsWith(\".\") ? name.endsWith(rule) : rule.endsWith(\"/*\") ? file.type.toLowerCase().startsWith(rule.slice(0, -1)) : file.type.toLowerCase() === rule);\n}\n", "/**\n * Building DOM without a framework, and without ever parsing a string.\n *\n * Text goes in as text nodes, so a product name holding `<img onerror=\u2026>`\n * is shown as those characters and never runs: the card's firebreak depends\n * on it. Nothing in the widget sets `innerHTML`.\n */\n\ntype Child = Node | string | null | undefined | false;\n\nexport interface Props {\n class?: string;\n /** Set with `setAttribute`; `false` and `undefined` leave it off. */\n attrs?: Record<string, string | number | boolean | undefined>;\n on?: { [K in keyof HTMLElementEventMap]?: (event: HTMLElementEventMap[K]) => void };\n}\n\nexport function h<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n props: Props = {},\n ...children: Child[]\n): HTMLElementTagNameMap[K] {\n const element = document.createElement(tag);\n if (props.class) element.className = props.class;\n setAttrs(element, props.attrs ?? {});\n for (const [type, listener] of Object.entries(props.on ?? {})) {\n element.addEventListener(type, listener as EventListener);\n }\n append(element, children);\n return element;\n}\n\nconst SVG = \"http://www.w3.org/2000/svg\";\n\nexport function svg(tag: string, attrs: Record<string, string | number>, ...children: Node[]): SVGElement {\n const element = document.createElementNS(SVG, tag);\n for (const [name, value] of Object.entries(attrs)) element.setAttribute(name, String(value));\n for (const child of children) element.appendChild(child);\n return element;\n}\n\nexport function setAttrs(element: Element, attrs: Record<string, string | number | boolean | undefined>): void {\n for (const [name, value] of Object.entries(attrs)) {\n if (value === undefined || value === false) element.removeAttribute(name);\n else element.setAttribute(name, value === true ? \"\" : String(value));\n }\n}\n\nexport function append(element: Node, children: Child[]): void {\n for (const child of children) {\n if (child === null || child === undefined || child === false) continue;\n element.appendChild(typeof child === \"string\" ? document.createTextNode(child) : child);\n }\n}\n\n/** Replace an element's children. */\nexport function replace(element: Node, ...children: Child[]): void {\n while (element.firstChild) element.removeChild(element.firstChild);\n append(element, children);\n}\n", "/**\n * The Mihe mark, the widget's default launcher icon (amendment 58).\n *\n * An oracle in an open ring, with one pair of arcs for its voice. Drawn inline\n * rather than loaded, so the default launcher needs no request and no\n * permission from the host page's Content-Security-Policy. A copy of the\n * React SDK's `mark.tsx`, which holds the same two paths.\n */\n\nimport { svg } from \"./dom\";\n\nconst TRANSFORM = \"translate(0.000000,1760.000000) scale(0.100000,-0.100000)\";\n\n/** The figure and the ring. */\nconst BODY = \"M8165 16868 c-450 -31 -1057 -131 -1495 -248 -76 -20 -156 -42 -180 -50 -14 -5 -50 -15 -80 -24 -146 -41 -273 -82 -360 -117 -25 -9 -74 -27 -110 -39 -36 -12 -74 -25 -85 -30 -11 -4 -40 -15 -65 -25 -25 -10 -54 -21 -65 -26 -11 -4 -85 -35 -165 -67 -218 -90 -530 -238 -715 -340 -38 -21 -106 -58 -150 -82 -44 -24 -108 -61 -143 -82 -34 -21 -65 -38 -68 -38 -2 0 -17 -8 -32 -19 -15 -10 -52 -34 -82 -53 -1161 -733 -2106 -1735 -2771 -2935 -74 -134 -219 -425 -219 -440 0 -4 -10 -24 -23 -43 -13 -19 -34 -60 -46 -90 -13 -30 -35 -80 -48 -110 -49 -112 -56 -127 -91 -220 -16 -41 -32 -84 -37 -95 -45 -114 -195 -563 -238 -715 -26 -88 -58 -212 -67 -255 -5 -22 -16 -69 -25 -105 -19 -78 -66 -317 -89 -455 -140 -836 -144 -1701 -11 -2525 9 -52 20 -122 25 -155 9 -53 17 -97 51 -262 16 -78 80 -337 105 -423 74 -256 219 -700 264 -810 26 -63 55 -135 79 -192 6 -13 24 -54 40 -91 17 -38 31 -70 31 -72 0 -3 105 -221 203 -425 39 -79 80 -154 128 -233 16 -26 29 -50 29 -52 0 -3 37 -65 82 -138 45 -72 88 -143 95 -157 64 -113 285 -440 339 -500 12 -14 43 -56 70 -95 42 -63 164 -220 221 -285 12 -14 36 -43 55 -66 250 -310 644 -706 1038 -1045 68 -59 396 -310 514 -395 88 -63 522 -341 596 -382 19 -10 80 -45 135 -77 120 -69 571 -298 670 -339 39 -16 107 -47 153 -68 46 -21 87 -38 93 -38 5 0 17 -4 27 -10 25 -13 78 -35 157 -63 39 -14 120 -43 180 -65 222 -82 417 -145 565 -183 41 -10 91 -23 110 -29 134 -37 514 -119 645 -139 836 -130 1685 -150 2385 -57 63 8 151 20 195 26 44 6 100 15 125 20 25 5 88 17 140 25 286 49 729 160 1015 256 198 67 349 119 370 129 11 6 72 30 135 54 63 24 142 56 175 71 103 46 118 52 183 79 53 22 224 108 572 287 750 385 1586 1059 2290 1844 219 244 499 612 666 873 32 52 91 144 131 205 91 141 268 456 366 652 87 172 167 341 167 350 0 4 6 21 14 38 56 124 212 529 241 627 9 30 21 64 26 75 4 11 16 45 25 75 9 30 25 82 36 115 54 166 146 535 178 710 82 461 106 640 137 1030 20 252 23 814 5 1015 -6 74 -17 191 -23 260 -12 141 -34 328 -49 410 -5 30 -16 100 -25 155 -9 55 -26 145 -37 200 -11 55 -24 118 -28 140 -4 22 -16 72 -25 110 -9 39 -20 86 -24 105 -203 889 -617 1816 -1181 2640 -141 205 -236 333 -350 470 -52 63 -113 138 -135 165 -203 255 -745 786 -1035 1016 -58 45 -138 109 -179 142 -228 181 -749 524 -1006 662 -49 26 -115 62 -145 78 -71 38 -346 176 -430 215 -138 64 -201 92 -206 92 -3 0 -35 14 -71 30 -35 17 -70 30 -77 30 -6 0 -16 4 -21 8 -9 8 -216 87 -387 148 -38 13 -98 35 -135 49 -38 13 -102 34 -143 45 -41 12 -97 28 -125 36 -180 53 -464 125 -610 154 -165 33 -214 43 -240 47 -16 2 -66 10 -110 18 -328 56 -573 84 -755 88 l-125 2 0 -515 0 -515 190 -21 c105 -12 242 -29 305 -39 63 -10 138 -21 165 -26 123 -18 192 -30 225 -39 19 -5 62 -14 95 -20 233 -44 615 -154 905 -260 47 -17 105 -38 130 -47 25 -8 59 -21 75 -28 17 -7 93 -39 170 -71 418 -173 867 -425 1345 -757 747 -518 1525 -1379 1970 -2182 18 -33 41 -73 50 -90 64 -116 84 -152 123 -231 25 -49 57 -118 72 -154 15 -36 36 -85 47 -110 42 -93 56 -127 78 -180 12 -30 26 -64 31 -75 61 -146 144 -376 194 -540 15 -49 31 -101 36 -115 9 -26 46 -175 74 -295 8 -38 20 -86 25 -105 15 -58 45 -214 80 -425 24 -141 62 -499 79 -745 34 -482 -17 -1111 -134 -1655 -14 -66 -30 -140 -35 -165 -8 -38 -22 -93 -65 -260 -28 -107 -86 -292 -127 -405 -59 -164 -118 -318 -128 -337 -6 -10 -10 -22 -10 -27 0 -6 -18 -49 -40 -96 -22 -47 -40 -88 -40 -90 0 -27 -247 -518 -314 -624 -14 -22 -26 -43 -26 -45 0 -37 -381 -596 -565 -829 -225 -283 -622 -706 -875 -929 -155 -137 -228 -198 -237 -198 -9 0 -127 185 -183 285 -12 22 -28 47 -35 55 -7 8 -32 49 -55 90 -24 41 -76 125 -115 185 -40 61 -123 195 -186 298 -63 103 -126 207 -141 230 -15 23 -31 49 -35 57 -3 8 -16 29 -27 45 -12 17 -26 39 -33 50 -7 11 -33 54 -59 95 -67 106 -620 996 -1072 1725 -58 94 -129 206 -158 251 -29 45 -65 103 -80 130 -15 27 -55 92 -89 144 -88 134 -280 445 -400 645 -56 94 -163 269 -239 389 -75 121 -135 224 -134 228 2 5 35 54 73 109 403 577 447 641 713 1032 134 197 262 399 262 413 0 8 -36 85 -79 172 -44 86 -142 285 -219 442 -77 157 -244 497 -372 755 -128 259 -297 601 -375 760 -78 160 -220 448 -315 640 -95 193 -264 535 -376 760 -400 810 -543 1101 -566 1155 -14 30 -33 74 -43 98 -17 39 -55 61 -55 31 0 -6 -35 -83 -79 -172 -43 -89 -106 -218 -139 -287 -68 -142 -323 -661 -607 -1235 -106 -214 -271 -550 -368 -745 -225 -457 -457 -927 -747 -1515 -363 -735 -420 -852 -420 -862 0 -10 166 -267 241 -373 21 -30 95 -138 164 -240 68 -102 160 -237 204 -300 80 -115 147 -212 296 -430 43 -63 93 -135 112 -159 42 -56 42 -57 -11 -134 -24 -35 -58 -90 -76 -122 -29 -52 -66 -114 -107 -180 -7 -11 -20 -33 -29 -50 -9 -16 -45 -73 -79 -125 -34 -52 -74 -117 -89 -145 -16 -27 -34 -57 -42 -66 -8 -8 -14 -19 -14 -23 0 -4 -10 -22 -23 -39 -12 -18 -28 -41 -34 -52 -7 -11 -33 -54 -59 -95 -26 -41 -58 -95 -72 -120 -13 -25 -35 -58 -47 -75 -21 -29 -238 -378 -619 -995 -96 -157 -210 -339 -252 -405 -104 -164 -269 -429 -343 -550 -33 -55 -92 -149 -131 -210 -38 -60 -78 -126 -90 -145 -11 -19 -25 -42 -30 -50 -5 -8 -44 -71 -86 -140 -42 -69 -104 -168 -138 -220 -34 -53 -66 -104 -73 -115 -6 -11 -76 -123 -155 -249 -79 -126 -145 -234 -147 -240 -2 -6 -21 -36 -41 -66 -76 -112 -170 -260 -170 -267 0 -5 -14 -24 -30 -43 -29 -35 -29 -35 -82 16 -29 28 -91 84 -138 124 -567 490 -1171 1224 -1508 1832 -47 85 -232 451 -274 543 -20 44 -53 115 -72 158 -20 42 -36 80 -36 83 0 3 -6 20 -14 37 -20 47 -40 99 -85 222 -79 217 -90 252 -140 420 -49 167 -85 311 -141 558 -120 534 -163 1390 -99 1962 41 369 37 343 84 595 48 260 75 373 159 683 39 143 224 654 286 792 110 242 174 371 314 630 563 1039 1532 2008 2644 2642 68 38 455 233 532 267 131 58 397 168 465 192 108 39 384 130 445 148 30 8 87 25 125 36 316 96 789 185 1190 225 94 9 176 20 183 24 12 5 16 287 12 1022 0 13 -27 15 -135 7z m660 -4508 c4 -16 11 -32 16 -35 5 -4 9 -12 9 -20 0 -8 19 -53 41 -102 71 -151 106 -226 129 -278 12 -27 39 -86 60 -130 21 -44 52 -111 70 -150 17 -38 44 -97 60 -130 15 -33 35 -76 43 -95 8 -19 31 -69 50 -110 62 -134 89 -194 107 -240 10 -25 46 -103 80 -175 90 -190 106 -224 133 -285 49 -108 70 -156 97 -215 15 -33 37 -82 50 -110 12 -27 40 -87 61 -133 40 -83 40 -83 6 -140 -19 -31 -50 -86 -69 -122 -65 -120 -91 -165 -140 -242 -26 -43 -48 -80 -48 -83 0 -2 -17 -33 -38 -67 -21 -35 -41 -70 -45 -78 -4 -8 -11 -22 -17 -30 -34 -54 -110 -181 -110 -185 0 -3 -11 -23 -25 -45 -14 -22 -46 -80 -72 -128 -25 -48 -54 -98 -63 -112 -41 -58 -150 -263 -150 -280 0 -10 15 -47 33 -82 62 -117 120 -223 128 -234 5 -6 48 -87 95 -180 47 -93 90 -173 94 -179 4 -5 27 -46 50 -90 23 -44 67 -127 98 -185 30 -58 59 -110 63 -116 11 -14 72 -130 111 -209 37 -77 56 -114 103 -200 142 -264 186 -348 323 -615 63 -124 128 -250 145 -280 33 -59 82 -154 250 -480 58 -113 129 -250 157 -305 96 -182 151 -288 268 -515 63 -124 135 -263 160 -310 24 -47 118 -229 207 -405 170 -333 225 -440 277 -534 17 -31 112 -216 211 -411 99 -195 188 -372 199 -393 11 -22 18 -40 16 -42 -10 -11 -431 -215 -443 -215 -2 0 -36 -15 -77 -34 -40 -18 -109 -48 -153 -66 -44 -18 -93 -38 -110 -45 -16 -7 -75 -29 -130 -50 -55 -20 -116 -43 -135 -50 -39 -14 -282 -90 -345 -107 -22 -6 -60 -16 -85 -24 -61 -17 -216 -55 -265 -64 -22 -4 -85 -17 -140 -28 -165 -33 -277 -52 -513 -87 -183 -27 -182 -27 -182 78 0 34 -4 98 -10 142 -5 44 -14 141 -20 215 -24 285 -30 360 -45 530 -9 96 -20 229 -25 295 -5 66 -17 206 -25 310 -9 105 -20 249 -25 320 -5 72 -12 148 -15 170 -3 22 -10 105 -15 185 -5 80 -17 228 -25 330 -9 102 -20 237 -24 300 -5 63 -19 225 -30 360 -11 135 -27 335 -36 445 -30 392 -37 461 -50 510 -8 28 -26 97 -41 155 -14 58 -37 146 -50 195 -41 154 -55 209 -64 245 -9 33 -23 89 -55 205 -7 28 -25 100 -40 160 -24 98 -49 196 -61 233 -8 27 -28 5 -40 -44 -6 -27 -24 -94 -38 -149 -15 -55 -43 -161 -62 -235 -20 -74 -49 -187 -65 -250 -17 -63 -39 -151 -50 -195 -23 -92 -39 -157 -69 -270 -30 -113 -36 -170 -75 -720 -8 -110 -21 -276 -30 -370 -8 -93 -19 -226 -25 -295 -5 -69 -12 -143 -15 -165 -3 -22 -10 -105 -16 -185 -24 -330 -41 -552 -59 -745 -22 -237 -31 -345 -40 -460 -5 -66 -16 -201 -25 -300 -8 -99 -19 -234 -25 -300 -15 -193 -40 -482 -50 -590 -5 -55 -9 -106 -10 -112 0 -18 -71 -17 -184 2 -50 9 -122 20 -161 26 -108 16 -241 39 -345 61 -52 11 -113 24 -135 28 -52 11 -235 56 -300 75 -27 7 -75 20 -106 29 -62 17 -397 127 -444 146 -16 7 -39 16 -50 21 -11 5 -56 22 -100 39 -214 82 -663 292 -724 339 -23 18 -23 18 62 182 46 90 130 252 185 359 55 107 121 236 147 285 26 50 100 194 165 320 64 127 146 286 182 355 36 69 100 193 143 275 43 83 106 204 140 270 34 66 109 210 165 320 108 211 202 391 280 540 26 50 70 133 97 185 28 52 112 217 188 365 76 149 141 275 145 280 4 6 54 100 110 210 57 110 123 238 148 285 25 47 63 119 85 160 22 41 71 133 110 205 39 71 107 202 152 290 45 88 91 177 103 197 12 21 37 66 55 100 19 35 51 95 72 133 238 435 243 447 218 497 -34 66 -100 186 -137 247 -19 31 -43 72 -53 91 -65 116 -87 155 -142 248 -34 57 -61 106 -61 108 0 2 -10 21 -23 42 -13 20 -44 75 -69 122 -25 47 -73 132 -107 188 -33 57 -61 106 -61 108 0 2 -29 52 -65 110 -68 111 -74 132 -51 180 26 52 103 218 126 269 13 28 41 88 62 135 22 47 54 114 70 150 48 104 78 169 124 267 24 51 44 98 44 103 0 6 4 18 10 28 13 22 90 192 90 198 0 2 23 52 51 112 54 112 81 171 205 440 19 41 34 76 34 78 0 3 15 37 34 77 18 40 43 95 56 122 12 28 43 93 67 145 25 52 49 105 54 117 12 32 36 28 44 -7z\";\n\n/** The two arcs, the only part of the mark in colour. */\nconst VOICE = \"M4399 12732 c-37 -42 -101 -117 -143 -166 -123 -145 -496 -679 -496 -712 0 -2 -18 -34 -41 -71 -100 -164 -271 -530 -369 -793 -17 -47 -36 -96 -42 -110 -35 -86 -134 -426 -172 -595 -50 -218 -49 -218 -81 -445 -85 -598 -74 -1269 30 -1820 8 -47 20 -107 24 -135 11 -60 68 -292 87 -355 8 -25 21 -67 29 -95 23 -79 72 -227 85 -260 7 -16 16 -39 20 -50 34 -90 73 -186 108 -265 11 -25 30 -70 43 -100 45 -106 197 -400 256 -493 14 -22 36 -60 50 -85 97 -175 409 -612 437 -612 6 0 85 66 176 147 91 80 199 176 240 212 41 36 100 89 130 117 30 29 73 67 95 86 123 107 120 82 23 209 -209 275 -459 726 -589 1064 -112 292 -165 474 -219 750 -19 96 -26 136 -50 287 -70 444 -45 1047 65 1543 9 39 20 88 24 110 149 680 553 1448 1043 1980 15 17 28 33 28 36 0 11 -703 699 -714 699 -6 0 -41 -35 -77 -78z M12835 12532 c-154 -152 -313 -307 -354 -344 -84 -76 -85 -63 16 -174 227 -250 460 -601 637 -959 70 -144 93 -192 130 -280 10 -22 21 -49 26 -60 5 -11 16 -42 25 -70 10 -27 21 -59 26 -70 23 -55 77 -234 112 -370 45 -175 56 -227 78 -345 24 -127 27 -151 58 -425 26 -231 28 -544 6 -780 -17 -180 -30 -267 -85 -570 -9 -47 -39 -165 -89 -350 -23 -83 -54 -178 -76 -230 -7 -16 -19 -48 -28 -70 -144 -374 -335 -726 -576 -1060 -56 -77 -100 -145 -99 -151 2 -5 68 -68 148 -140 80 -71 172 -154 205 -185 138 -128 375 -329 388 -329 23 0 437 582 437 614 0 3 16 30 35 60 62 98 305 576 305 601 0 3 14 35 31 72 54 117 135 338 174 478 124 434 155 590 214 1065 67 536 5 1382 -141 1930 -11 41 -24 89 -28 105 -24 91 -128 414 -175 543 -34 95 -127 306 -196 447 -145 295 -273 511 -475 795 -154 217 -416 530 -443 530 -3 0 -132 -125 -286 -278z\";\n\nexport function miheMark(size: number, body: string, voice: string): SVGElement {\n const mark = svg(\n \"svg\",\n { viewBox: \"0 0 1760 1760\", width: size, height: size, \"aria-hidden\": \"true\", focusable: \"false\" },\n svg(\"path\", { fill: body, \"fill-rule\": \"evenodd\", transform: TRANSFORM, d: BODY }),\n svg(\"path\", { fill: voice, \"fill-rule\": \"evenodd\", transform: TRANSFORM, d: VOICE }),\n );\n mark.style.display = \"block\";\n return mark;\n}\n", "/**\n * The widget's stylesheet, inside its shadow root.\n *\n * The shadow root is what keeps the host page out: a Tailwind reset or a\n * site's `button { \u2026 }` cannot select anything in here, and nothing here can\n * select the page. Inherited properties still cross the boundary, which is\n * why `:host` resets them all.\n *\n * Theming is through custom properties set on the element, the one styling\n * hook a shadow root lets through by design:\n *\n * ```css\n * mihe-assistant { --mihe-accent: #0a7d5a; --mihe-radius: 10px; }\n * ```\n *\n * The values match the React widget's.\n */\n\nexport const LAUNCHER_BG = \"#221f1b\";\nexport const LAUNCHER_INK = \"#eae7e4\";\nexport const LAUNCHER_VOICE = \"#5ac8cd\";\n\nexport const STYLES = `\n:host {\n all: initial;\n --mihe-accent: #4f7cff;\n --mihe-radius: 14px;\n --mihe-font: system-ui, -apple-system, 'Segoe UI', sans-serif;\n --mihe-z: 2147482000;\n}\n:host([layout=\"inline\"]) { display: block; width: 100%; height: 100%; }\n*, *::before, *::after { box-sizing: border-box; }\n[hidden] { display: none !important; }\nbutton { font: inherit; color: inherit; margin: 0; }\n\n.panel {\n display: flex; flex-direction: column; width: 100%; height: 100%;\n background: #fff; color: #0f172a;\n border-radius: var(--mihe-radius); border: 1px solid #e2e8f0;\n box-shadow: 0 20px 50px rgba(15,23,42,0.22);\n font: 14px/1.5 var(--mihe-font); overflow: hidden;\n}\n.floating {\n position: fixed; bottom: 20px;\n width: min(380px, calc(100vw - 40px)); height: min(560px, calc(100vh - 40px));\n z-index: var(--mihe-z);\n}\n.floating.right { right: 20px; }\n.floating.left { left: 20px; }\n.header {\n display: flex; align-items: center; justify-content: space-between;\n padding: 12px 14px; border-bottom: 1px solid #e2e8f0; font-weight: 600;\n}\n.header-title { display: flex; align-items: center; gap: 8px; }\n.status-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; background: #f59e0b; }\n.status-dot.open { background: #22c55e; }\n.icon-button { border: none; background: transparent; cursor: pointer; font-size: 14px; color: #64748b; }\n.transcript { flex: 1; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 10px; }\n.empty { color: #64748b; margin: 0; }\n.bubble-user, .bubble-assistant { max-width: 85%; padding: 8px 12px; white-space: pre-wrap; overflow-wrap: anywhere; }\n.bubble-user { align-self: flex-end; background: var(--mihe-accent); color: #fff; border-radius: 12px 12px 2px 12px; }\n.bubble-assistant { align-self: flex-start; background: #f1f5f9; border-radius: 12px 12px 12px 2px; }\n.md { white-space: normal; }\n.md p, .md ul, .md ol { margin: 0; }\n.md > * + * { margin-top: 6px; }\n.md ul, .md ol { padding-left: 18px; }\n.md ul { list-style: disc; }\n.md ol { list-style: decimal; }\n.md code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.92em; background: rgba(15,23,42,0.06); padding: 0 3px; border-radius: 3px; }\n.md a { color: inherit; }\n.thinking { color: #64748b; font-style: italic; }\n.sources { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; padding-top: 8px; border-top: 1px solid #e2e8f0; }\n.source { font-size: 11px; color: #475569; background: #f1f5f9; padding: 2px 8px; border-radius: 999px; }\n.source a { color: inherit; text-decoration: none; }\n.error { background: #fef2f2; color: #991b1b; padding: 8px 10px; border-radius: 8px; font-size: 13px; }\n.notice { color: #64748b; font-size: 13px; text-align: center; }\n.voice-notice { background: #f1f5f9; color: #475569; padding: 8px 10px; border-radius: 8px; font-size: 13px; }\n.composer { display: flex; gap: 8px; padding: 12px; border-top: 1px solid #e2e8f0; }\n.panel-body { display: flex; flex-direction: column; flex: 1; min-height: 0; }\n.panel-body[hidden], .call[hidden], .talk[hidden] { display: none; }\n.talk { margin-left: auto; margin-right: 4px; min-height: 32px; padding: 0 14px; border-radius: 999px; border: 1px solid #cbd5e1; background: #fff; cursor: pointer; font: 600 13px system-ui, sans-serif; }\n.call { flex: 1; display: flex; flex-direction: column; justify-content: flex-end; gap: 8px; padding: 12px; border-top: 1px solid #e2e8f0; }\n.call-cards { display: flex; flex-direction: column; gap: 8px; }\n.call-bar { display: flex; align-items: center; gap: 12px; padding: 8px 8px 8px 12px; border-radius: 999px; background: #fff; border: 1px solid rgba(15,23,42,0.1); box-shadow: 0 6px 18px rgba(15,23,42,0.16); min-width: 0; }\n.call-mark { position: relative; display: grid; place-items: center; width: 36px; height: 36px; flex: none; }\n.call-ring { position: absolute; inset: 0; border-radius: 50%; background: rgba(0,114,119,0.14); transition: transform 60ms linear; }\n.call-line { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.call-hold { flex: none; width: auto; min-height: 36px; padding: 0 14px; border-radius: 999px; font: 600 13px system-ui, sans-serif; user-select: none; touch-action: none; }\n.call-hold[hidden] { display: none; }\n.call-hold.recording { background: #007277; border-color: #007277; color: #fff; }\n.call-end { flex: none; min-height: 36px; padding: 0 16px; border-radius: 999px; border: none; background: #b91c1c; color: #fff; cursor: pointer; font: 600 13px system-ui, sans-serif; }\n.clip { flex: none; width: 38px; display: grid; place-items: center; border-radius: 8px; border: 1px solid #cbd5e1; background: #fff; cursor: pointer; color: #475569; }\n.clip[hidden], .chips[hidden] { display: none; }\n.chips { list-style: none; margin: 0; padding: 8px 12px 0; display: flex; flex-wrap: wrap; gap: 6px; border-top: 1px solid #e2e8f0; }\n.chip { display: flex; align-items: center; gap: 6px; padding: 4px 4px 4px 6px; border-radius: 8px; background: #f1f5f9; max-width: 100%; font-size: 12px; }\n.chip.failed { background: #fef2f2; color: #991b1b; }\n.chip img { width: 28px; height: 28px; border-radius: 4px; object-fit: cover; flex: none; }\n.chip-text { display: flex; flex-direction: column; min-width: 0; }\n.chip-name { max-width: 140px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }\n.chip-status { opacity: 0.75; }\n.chip-remove { border: none; background: transparent; cursor: pointer; width: 28px; height: 28px; border-radius: 6px; color: inherit; font-size: 12px; }\n.sent-pictures { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 6px; }\n.sent-pictures img { width: 64px; height: 64px; border-radius: 6px; object-fit: cover; display: block; }\n.sent-name { font-size: 12px; opacity: 0.85; }\n.input {\n flex: 1; min-width: 0; padding: 9px 12px; border-radius: 8px; border: 1px solid #cbd5e1;\n font: 14px var(--mihe-font); color: #0f172a; background: #fff; outline: none;\n}\n.send {\n flex: none; padding: 9px 16px; border-radius: 8px; border: none; background: var(--mihe-accent);\n color: #fff; cursor: pointer; font: 600 13px var(--mihe-font);\n}\n.send:disabled { opacity: 0.6; cursor: default; }\n.mic {\n width: 38px; border-radius: 8px; border: 1px solid #cbd5e1; background: #fff; cursor: pointer;\n font-size: 16px; user-select: none; -webkit-user-select: none; touch-action: none;\n}\n.mic.live { border-color: var(--mihe-accent); }\n.mic.recording { border-color: #dc2626; background: #fef2f2; }\n.hands-free {\n width: 30px; border-radius: 8px; border: 1px solid #cbd5e1; background: #fff; color: #475569;\n cursor: pointer; font-size: 15px; user-select: none; -webkit-user-select: none;\n}\n.pulse {\n display: block; width: 12px; height: 12px; margin: 0 auto; border-radius: 50%;\n background: var(--mihe-accent); animation: mihe-pulse 2s ease-in-out infinite;\n}\n.pulse.fast { animation-duration: 0.8s; }\n@keyframes mihe-pulse { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(0.6); opacity: 0.45; } }\n\n.card {\n border: 1px solid #e2e8f0; border-radius: 10px; padding: 12px; background: #fff; color: #0f172a;\n box-shadow: 0 2px 8px rgba(15,23,42,0.06); font: 14px/1.5 var(--mihe-font);\n}\n.card-header { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 6px; }\n.risk { font-size: 11px; font-weight: 600; padding: 2px 8px; border-radius: 999px; white-space: nowrap; background: #fef3c7; color: #92400e; }\n.risk-high { background: #fee2e2; color: #991b1b; }\n.card-description { margin: 0 0 10px; color: #475569; font-size: 13px; }\n.card-input { margin: 0 0 12px; display: flex; flex-direction: column; gap: 8px; max-height: 40vh; overflow-y: auto; }\n.card-row { display: flex; flex-wrap: wrap; gap: 8px; font-size: 13px; }\n.card-key { color: #64748b; min-width: 90px; margin: 0; }\n.card-value { margin: 0; font-weight: 500; overflow-wrap: anywhere; min-width: 0; flex: 1 1 160px; }\n.input-list { margin: 0; padding-left: 20px; display: grid; gap: 8px; }\n.input-text { white-space: pre-wrap; }\n.card-actions { display: flex; gap: 8px; justify-content: flex-end; }\n.card-summary { margin: 0 0 10px; font-size: 14px; font-weight: 600; color: #0f172a; }\n.card-note { margin: 0 0 12px; font-size: 12px; color: #64748b; }\n.card-more { margin: 0 0 12px; font-size: 12px; color: #64748b; }\n.card-more summary { cursor: pointer; }\n.card-more .card-description { margin: 6px 0 0; }\n.card-value-wide { flex-basis: 100%; }\n.card-images { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 4px; }\n.card-images img { width: 44px; height: 44px; object-fit: cover; border-radius: 6px; background: #f1f5f9; }\n.card-before { color: #94a3b8; font-weight: 400; }\n.card-arrow { color: #94a3b8; }\n.button-primary:disabled { opacity: 0.5; cursor: default; }\n.button-danger { background: #dc2626; }\n.card-media-row { display: flex; gap: 10px; align-items: flex-start; font-size: 13px; }\n.card-media-row .card-images { flex: none; max-width: 140px; margin-bottom: 0; }\n.card-media-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }\n.card-media-label { margin: 0; font-weight: 600; color: #0f172a; }\n.card-media-value { margin: 0; overflow-wrap: anywhere; }\n.card-media-strong { font-weight: 600; }\n.button-secondary {\n padding: 7px 14px; border-radius: 8px; border: 1px solid #cbd5e1; background: #fff;\n cursor: pointer; font: 500 13px var(--mihe-font);\n}\n.button-primary {\n padding: 7px 14px; border-radius: 8px; border: none; background: var(--mihe-accent); color: #fff;\n cursor: pointer; font: 600 13px var(--mihe-font);\n}\n\n.launcher {\n position: fixed; right: 20px; bottom: 20px; width: 52px; height: 52px; border-radius: 50%;\n border: none; padding: 0; display: grid; place-items: center; overflow: hidden;\n background: ${LAUNCHER_BG}; color: ${LAUNCHER_INK}; cursor: pointer;\n box-shadow: 0 8px 24px rgba(15,23,42,0.25); z-index: var(--mihe-z);\n}\n.launcher-image { width: 100%; height: 100%; object-fit: cover; display: block; }\n\n.orb {\n position: fixed; width: 56px; height: 56px; padding: 0; border: none; border-radius: 50%;\n background: ${LAUNCHER_BG}; color: ${LAUNCHER_INK}; box-shadow: 0 8px 24px rgba(15,23,42,0.25);\n z-index: var(--mihe-z); cursor: pointer; transition: left 180ms ease, top 180ms ease;\n touch-action: none; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;\n -webkit-tap-highlight-color: transparent;\n}\n.orb.dragging { cursor: grabbing; transition: none; }\n.orb:disabled { cursor: default; }\n.face { position: absolute; inset: 0; display: grid; place-items: center; border-radius: 50%; overflow: hidden; pointer-events: none; }\n.ring {\n position: absolute; inset: -6px; border-radius: 50%; border: 2px solid ${LAUNCHER_VOICE};\n pointer-events: none; transform-origin: center; will-change: transform;\n}\n.ring.idle { border-color: transparent; }\n.ring.recording { border-color: #e5484d; border-width: 3px; }\n.ring.thinking, .ring.connecting { border-style: dashed; }\n.ring.listening { animation: mihe-orb-breathe 2.4s ease-in-out infinite; }\n.ring.thinking { animation: mihe-orb-spin 1.2s linear infinite; }\n.ring.connecting { animation: mihe-orb-spin 2.4s linear infinite; }\n@keyframes mihe-orb-breathe { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } }\n@keyframes mihe-orb-spin { to { rotate: 360deg; } }\n.stop {\n position: fixed; width: 44px; height: 44px; border-radius: 50%; border: 1px solid rgba(15,23,42,0.12);\n background: #fff; box-shadow: 0 4px 14px rgba(15,23,42,0.18); display: grid; place-items: center;\n cursor: pointer; z-index: var(--mihe-z);\n}\n.stop-square { width: 12px; height: 12px; border-radius: 2px; background: #0f172a; }\n.popover {\n position: fixed; max-width: min(340px, calc(100vw - 40px)); z-index: calc(var(--mihe-z) + 1);\n display: flex; flex-direction: column; gap: 8px; font: 14px/1.45 var(--mihe-font);\n}\n.popover.cards { width: 320px; }\n.menu-item {\n display: inline-flex; align-items: center; gap: 10px; min-height: 44px; padding: 0 16px 0 6px;\n border-radius: 999px; border: 1px solid rgba(15,23,42,0.1); background: #fff; color: #0f172a;\n box-shadow: 0 6px 18px rgba(15,23,42,0.16); cursor: pointer; font: 500 14px var(--mihe-font);\n}\n.menu-icon {\n display: grid; place-items: center; width: 34px; height: 34px; border-radius: 50%;\n background: ${LAUNCHER_BG}; color: ${LAUNCHER_VOICE};\n}\n.orb-bubble {\n padding: 8px 12px; border-radius: 12px; background: #fff; color: #0f172a;\n border: 1px solid rgba(15,23,42,0.1); box-shadow: 0 6px 18px rgba(15,23,42,0.16);\n}\n.orb-bubble.error { background: #fef2f2; color: #991b1b; border-color: #fecaca; }\n\n@media (prefers-reduced-motion: reduce) {\n .pulse, .ring { animation: none !important; }\n .ring { transform: none !important; }\n .orb { transition: none; }\n}\n`;\n", "/**\n * What the launcher and the orb show: the host's own icon, the customer's\n * uploaded icon, or the Mihe mark on the brand's warm ink.\n */\n\nimport { h } from \"./dom\";\nimport { miheMark } from \"./mark\";\nimport { LAUNCHER_INK, LAUNCHER_VOICE } from \"./styles\";\n\n/**\n * The uploaded image fills the circle, so what the dashboard's preview shows\n * is what a user sees. If it cannot load (a Content-Security-Policy that does\n * not allow the storage's domain, say), it falls back to the mark instead of\n * an empty circle: `onFail` is told which URL failed, and the next draw skips\n * it.\n */\nexport function launcherFace(\n override: Node | undefined,\n url: string | null,\n failed: string | null,\n onFail: (url: string) => void,\n): Node {\n if (override) return override.cloneNode(true);\n if (url && url !== failed) {\n return h(\"img\", {\n class: \"launcher-image\",\n attrs: { src: url, alt: \"\", draggable: \"false\" },\n on: { error: () => onFail(url) },\n });\n }\n return miheMark(34, LAUNCHER_INK, LAUNCHER_VOICE);\n}\n", "/**\n * The approval surface for a proposed action, shared by the panel and the orb.\n *\n * What it says comes from the card's view (amendment 99): the app's own\n * description of the input that will run, or the registry's display hints,\n * or, with neither, every field of the input by its key. Never the\n * assistant's prose, so text smuggled into a product name or a document\n * cannot change what a user is shown they are about to approve. Everything is\n * added as text, so markup in it is shown rather than run.\n *\n * High-risk actions require this tap \u2014 the runtime refuses a spoken or typed\n * \"yes\" for them \u2014 so this is a security boundary, not decoration.\n */\n\nimport { hintedView, type CardLine, type CardView } from \"../card-view\";\nimport type { ProposalPayload } from \"../client\";\nimport { h } from \"./dom\";\n\nexport function confirmationCard(\n proposal: ProposalPayload,\n onRespond: (proposalId: string, confirmed: boolean) => void,\n view: CardView | \"pending\" = hintedView(proposal),\n): HTMLElement {\n const high = proposal.risk === \"high\";\n const pending = view === \"pending\";\n const v = pending ? null : view;\n const description = v && v.source !== \"plain\"\n ? h(\"details\", { class: \"card-more\" }, h(\"summary\", {}, \"More about this\"), h(\"p\", { class: \"card-description\" }, proposal.description))\n : h(\"p\", { class: \"card-description\" }, proposal.description);\n\n return h(\n \"div\",\n { class: \"card\", attrs: { role: \"group\", \"aria-label\": `Confirm: ${proposal.label}`, \"aria-busy\": pending ? \"true\" : undefined } },\n h(\n \"div\",\n { class: \"card-header\" },\n h(\"strong\", {}, proposal.label),\n h(\"span\", { class: high ? \"risk risk-high\" : \"risk\" }, high ? \"Needs care\" : \"Confirm\"),\n ),\n pending && h(\"p\", { class: \"card-description\" }, \"Checking the details\u2026\"),\n v?.summary && h(\"p\", { class: \"card-summary\" }, v.summary),\n v && v.source === \"plain\" && description,\n v && v.lines.length > 0 && h(\"dl\", { class: \"card-input\" }, ...v.lines.map(cardLine)),\n v?.note && h(\"p\", { class: \"card-note\" }, v.note),\n v && v.source !== \"plain\" && description,\n h(\n \"div\",\n { class: \"card-actions\" },\n h(\n \"button\",\n { class: \"button-secondary\", attrs: { type: \"button\" }, on: { click: () => onRespond(proposal.proposalId, false) } },\n \"Cancel\",\n ),\n h(\n \"button\",\n { class: high ? \"button-primary button-danger\" : \"button-primary\", attrs: { type: \"button\", disabled: pending ? \"\" : undefined }, on: { click: () => { if (!pending) onRespond(proposal.proposalId, true); } } },\n v?.confirmLabel ?? \"Confirm\",\n ),\n ),\n );\n}\n\n/** One line: its label, then pictures, what it was and what it becomes, and any list (each entry checkable on its own). */\nfunction cardLine(line: CardLine): HTMLElement {\n // With pictures: the pictures on the left, what they are beside them, so rows line up whatever the names.\n if (line.images?.length) {\n return h(\n \"div\",\n { class: \"card-media-row\" },\n h(\"span\", { class: \"card-images\" }, ...line.images.map((im) => h(\"img\", { attrs: { src: im.src, alt: im.alt ?? \"\", loading: \"lazy\", referrerpolicy: \"no-referrer\" } }))),\n h(\"span\", { class: \"card-media-text\" }, line.label && h(\"dt\", { class: \"card-media-label\" }, line.label), h(\"dd\", { class: line.label ? \"card-media-value\" : \"card-media-value card-media-strong\" }, ...lineValue(line))),\n );\n }\n const value = h(\n \"dd\",\n { class: line.label ? \"card-value\" : \"card-value card-value-wide\" },\n ...lineValue(line),\n );\n return h(\"div\", { class: \"card-row\" }, line.label && h(\"dt\", { class: \"card-key\" }, line.label), value);\n}\n\n/** What a line says: what it was, what it becomes, and any list. */\nfunction lineValue(line: CardLine) {\n return [\n line.before !== undefined && h(\"s\", { class: \"card-before\" }, line.before),\n line.before !== undefined && h(\"span\", { class: \"card-arrow\", attrs: { \"aria-label\": \"becomes\" } }, \" \u2192 \"),\n line.text !== undefined && h(\"span\", { class: \"input-text\" }, line.text),\n !!line.items?.length && h(\"ol\", { class: \"input-list\" }, ...line.items.map((item) => h(\"li\", {}, h(\"span\", { class: \"input-text\" }, item)))),\n ];\n}\n", "/** The orb menu's icons, the same strokes as the React SDK's. */\n\nimport { svg } from \"./dom\";\n\nfunction icon(...children: SVGElement[]): SVGElement {\n return svg(\n \"svg\",\n {\n viewBox: \"0 0 20 20\",\n width: 18,\n height: 18,\n fill: \"none\",\n stroke: \"currentColor\",\n \"stroke-width\": 1.7,\n \"stroke-linecap\": \"round\",\n \"stroke-linejoin\": \"round\",\n \"aria-hidden\": \"true\",\n },\n ...children,\n );\n}\n\nexport const chatIcon = () =>\n icon(svg(\"path\", { d: \"M4 4.5h12a1.5 1.5 0 0 1 1.5 1.5v7a1.5 1.5 0 0 1-1.5 1.5H9l-4 3v-3H4A1.5 1.5 0 0 1 2.5 13V6A1.5 1.5 0 0 1 4 4.5Z\" }));\n\nexport const micIcon = () =>\n icon(\n svg(\"rect\", { x: 7, y: 2.5, width: 6, height: 10, rx: 3 }),\n svg(\"path\", { d: \"M4.5 9.5a5.5 5.5 0 0 0 11 0M10 15v2.5\" }),\n );\n\nexport const waveIcon = () => icon(svg(\"path\", { d: \"M3 10v0M6.5 7v6M10 4v12M13.5 7v6M17 10v0\" }));\n\n/** The chat box's attach button. */\nexport const clipIcon = () => icon(svg(\"path\", { d: \"M15.5 9.5l-5.6 5.6a3.5 3.5 0 0 1-5-5L10.6 4.4a2.3 2.3 0 0 1 3.3 3.3L8.3 13.3a1.1 1.1 0 0 1-1.6-1.6L11.8 6.6\" }));\n", "/**\n * The orb: the widget as a circle, and voice shown in the circle itself\n * (amendment 61). The React SDK's `orb.tsx` in plain DOM; its comments carry\n * the reasoning, and the behaviour is the same:\n *\n * | Mode | Tap | Hold | Drag past 8px |\n * |--------------|--------------------------|--------------------------|--------------------------|\n * | Idle | menu, or chat | nothing | moves it, if draggable |\n * | Menu open | closes it | nothing | nothing |\n * | Hands-free | ends the session | nothing | moves it, if draggable |\n * | Push-to-talk | nothing | records, release sends | discards, then moves it |\n *\n * Two things the orb still shows with the panel closed: the confirmation\n * card, from the proposal payload, and refusals and errors, in a bubble.\n *\n * The orb button is built once and updated, never replaced, because a press\n * in progress holds pointer capture on it.\n */\n\nimport type { VoiceMode, WidgetCorner } from \"@mihe-dev/protocol\";\n\nimport type { Mihe, MiheState } from \"../mihe\";\nimport { confirmationCard } from \"./card\";\nimport { h, replace, setAttrs } from \"./dom\";\nimport { chatIcon, micIcon, waveIcon } from \"./icons\";\nimport { launcherFace } from \"./launcher\";\n\nexport const ORB_SIZE = 56;\nexport const MARGIN = 20;\nconst DRAG_THRESHOLD = 8;\n/** How long a press is held on the device before it counts as speech. */\nconst HOLD_WINDOW_MS = 150;\n\nconst POSITION_KEY = \"mihe:orb-position\";\nconst HINT_KEY = \"mihe:hold-hint-done\";\n\n/** Where a dragged orb rests: a side, and how far down the viewport. */\ninterface Rest {\n side: \"left\" | \"right\";\n /** Top of the orb as a fraction of the viewport's height, so it survives a resize. */\n y: number;\n}\n\nexport interface OrbOptions {\n heading: string;\n draggable: boolean;\n corner: WidgetCorner;\n voiceModes: VoiceMode[];\n /** Drawn in the orb in place of the uploaded icon or the mark. Cloned. */\n launcherIcon?: Node;\n /** Distance from the viewport's bottom and side edges, in pixels. */\n offset: { bottom: number; side: number };\n}\n\ntype MenuKey = \"chat\" | VoiceMode;\n\ninterface Press {\n x: number;\n y: number;\n offsetX: number;\n offsetY: number;\n dragging: boolean;\n recording: \"held\" | \"live\" | null;\n timer: ReturnType<typeof setTimeout> | null;\n}\n\nfunction readRest(): Rest | null {\n try {\n const raw = window.localStorage.getItem(POSITION_KEY);\n if (!raw) return null;\n const parsed = JSON.parse(raw) as Rest;\n if ((parsed.side === \"left\" || parsed.side === \"right\") && typeof parsed.y === \"number\") return parsed;\n } catch {\n // Private windows and blocked storage: fall back to the corner.\n }\n return null;\n}\n\nfunction writeRest(rest: Rest): void {\n try {\n window.localStorage.setItem(POSITION_KEY, JSON.stringify(rest));\n } catch {\n // Remembering is a convenience; forgetting is fine.\n }\n}\n\nfunction hintDone(): boolean {\n try {\n return window.localStorage.getItem(HINT_KEY) === \"1\";\n } catch {\n return false;\n }\n}\n\nfunction markHintDone(): void {\n try {\n window.localStorage.setItem(HINT_KEY, \"1\");\n } catch {\n // Shown again next time, which is harmless.\n }\n}\n\nexport class Orb {\n /** Everything the orb draws, as siblings in the shadow root. */\n readonly root: HTMLElement;\n private readonly button: HTMLButtonElement;\n private readonly ring: HTMLElement;\n private readonly face: HTMLElement;\n private readonly stop: HTMLButtonElement;\n private readonly menu: HTMLElement;\n private readonly cards: HTMLElement;\n private readonly bubble: HTMLElement;\n\n private options!: OrbOptions;\n private state!: MiheState;\n private menuOpen = false;\n private rest: Rest | null = null;\n private drag: { left: number; top: number } | null = null;\n private showHint = !hintDone();\n private press: Press | null = null;\n private previousVoiceState: string = \"idle\";\n private faceKey: unknown = undefined;\n private drawnProposals: unknown = undefined;\n private drawnViews: unknown = undefined;\n private failedIcon: string | null = null;\n private ringFrame = 0;\n private stopLevel: (() => void) | null = null;\n private readonly reducedMotion =\n typeof window !== \"undefined\" && window.matchMedia?.(\"(prefers-reduced-motion: reduce)\").matches === true;\n private readonly cleanups: (() => void)[] = [];\n\n constructor(private readonly mihe: Mihe) {\n this.ring = h(\"span\", { class: \"ring idle\", attrs: { \"aria-hidden\": \"true\" } });\n this.face = h(\"span\", { class: \"face\" });\n this.button = h(\n \"button\",\n {\n class: \"orb\",\n attrs: { type: \"button\" },\n on: {\n pointerdown: (event) => this.onPointerDown(event),\n pointermove: (event) => this.onPointerMove(event),\n pointerup: () => this.finish(false),\n pointercancel: () => this.finish(true),\n keydown: (event) => this.onKeyDown(event),\n keyup: (event) => this.onKeyUp(event),\n contextmenu: (event) => event.preventDefault(),\n },\n },\n this.ring,\n this.face,\n );\n // Always a visible way out that is not a gesture.\n this.stop = h(\n \"button\",\n {\n class: \"stop\",\n attrs: { type: \"button\", \"aria-label\": \"End voice\" },\n on: { click: () => this.mihe.voice.stop() },\n },\n h(\"span\", { class: \"stop-square\", attrs: { \"aria-hidden\": \"true\" } }),\n );\n this.menu = h(\"div\", { class: \"popover\", attrs: { role: \"menu\" }, on: { keydown: (event) => this.onMenuKeyDown(event) } });\n this.cards = h(\"div\", { class: \"popover cards\" });\n this.bubble = h(\"div\", { class: \"popover orb-bubble\", attrs: { role: \"status\", \"aria-live\": \"polite\" } });\n this.root = h(\"div\", {}, this.menu, this.cards, this.bubble, this.button, this.stop);\n\n const onResize = () => this.render();\n window.addEventListener(\"resize\", onResize);\n this.cleanups.push(() => window.removeEventListener(\"resize\", onResize));\n\n const onOutside = (event: PointerEvent) => {\n if (!this.menuOpen) return;\n const path = event.composedPath();\n if (!path.includes(this.menu) && !path.includes(this.button)) this.setMenu(false);\n };\n document.addEventListener(\"pointerdown\", onOutside);\n this.cleanups.push(() => document.removeEventListener(\"pointerdown\", onOutside));\n }\n\n update(state: MiheState, options: OrbOptions): void {\n if (this.options?.draggable !== options.draggable) {\n this.rest = options.draggable ? readRest() : null;\n }\n // \"Hold to talk\" stays until the first recording that reached the model.\n if (this.previousVoiceState === \"recording\" && state.voice.state === \"thinking\") {\n markHintDone();\n this.showHint = false;\n }\n this.previousVoiceState = state.voice.state;\n this.state = state;\n this.options = options;\n this.render();\n }\n\n destroy(): void {\n this.stopRing();\n if (this.press?.timer) clearTimeout(this.press.timer);\n for (const cleanup of this.cleanups) cleanup();\n }\n\n // ---- what the orb is doing --------------------------------------------\n\n private get voiceLive() {\n const state = this.state.voice.state;\n return state !== \"idle\" && state !== \"error\";\n }\n\n private get handsFree() {\n return this.voiceLive && this.state.voice.handsFree;\n }\n\n private get holdToTalk() {\n return this.voiceLive && !this.state.voice.handsFree;\n }\n\n private menuItems(): { key: MenuKey; label: string }[] {\n const allowed = this.state.voice.available ? this.options.voiceModes : [];\n return [\n { key: \"chat\", label: \"Chat\" },\n ...(allowed.includes(\"hands-free\") ? [{ key: \"hands-free\" as const, label: \"Talk\" }] : []),\n ...(allowed.includes(\"hold\") ? [{ key: \"hold\" as const, label: \"Hold to talk\" }] : []),\n ];\n }\n\n // ---- position ----------------------------------------------------------\n\n private viewport() {\n return { width: window.innerWidth, height: window.innerHeight };\n }\n\n private clampTop(top: number): number {\n const { height } = this.viewport();\n const lowest = height - this.options.offset.bottom - ORB_SIZE;\n return Math.min(Math.max(MARGIN, top), Math.max(MARGIN, lowest));\n }\n\n /** The side the orb sits on, which is also where the panel opens. */\n side(): \"left\" | \"right\" {\n const { width } = this.viewport();\n if (this.drag) return this.drag.left + ORB_SIZE / 2 < width / 2 ? \"left\" : \"right\";\n return this.rest?.side ?? (this.options?.corner === \"bottom-left\" ? \"left\" : \"right\");\n }\n\n private position() {\n const { width, height } = this.viewport();\n const side = this.side();\n const edge = this.options.offset.side;\n const left = this.drag ? this.drag.left : side === \"left\" ? edge : width - edge - ORB_SIZE;\n const top = this.drag\n ? this.drag.top\n : this.clampTop(this.rest ? this.rest.y * height : height - this.options.offset.bottom - ORB_SIZE);\n return { side, left, top, above: top + ORB_SIZE / 2 > height / 2 };\n }\n\n // ---- drawing -----------------------------------------------------------\n\n private render(): void {\n if (!this.state) return;\n const { voice, proposals, lastError, connection } = this.state;\n const { heading } = this.options;\n const { side, left, top, above } = this.position();\n const items = this.menuItems();\n const voiceLive = this.voiceLive;\n\n // The orb.\n const label = this.handsFree\n ? `Stop talking to ${heading}`\n : this.holdToTalk\n ? voice.state === \"recording\"\n ? \"Release to send\"\n : \"Hold to talk\"\n : items.length === 1\n ? `Open ${heading}`\n : `${heading}: choose chat or voice`;\n const menuish = items.length > 1 && !voiceLive;\n setAttrs(this.button, {\n \"aria-label\": label,\n \"aria-haspopup\": menuish ? \"menu\" : undefined,\n \"aria-expanded\": menuish ? String(this.menuOpen) : undefined,\n \"aria-pressed\": this.holdToTalk ? String(voice.state === \"recording\") : undefined,\n \"aria-busy\": String(voice.state === \"connecting\"),\n });\n this.button.disabled = connection !== \"open\" && !voiceLive;\n this.button.style.left = `${left}px`;\n this.button.style.top = `${top}px`;\n this.button.classList.toggle(\"dragging\", this.drag !== null);\n\n const ringState = !voiceLive\n ? \"idle\"\n : ([\"connecting\", \"thinking\", \"recording\", \"speaking\"] as const).find((s) => s === voice.state) ?? \"listening\";\n this.ring.className = `ring ${ringState}`;\n this.syncRing();\n\n const faceKey = [this.options.launcherIcon, this.state.launcherIconUrl, this.failedIcon];\n if (!sameList(faceKey, this.faceKey)) {\n this.faceKey = faceKey;\n replace(\n this.face,\n launcherFace(this.options.launcherIcon, this.state.launcherIconUrl, this.failedIcon, (url) => {\n this.failedIcon = url;\n this.render();\n }),\n );\n }\n\n this.stop.hidden = !voiceLive;\n this.stop.style.left = `${left + (side === \"right\" ? -52 : ORB_SIZE + 8)}px`;\n this.stop.style.top = `${top + (ORB_SIZE - 44) / 2}px`;\n\n // Floating things open beside the orb, towards the page and away from\n // the nearer edge, so nothing is drawn off-screen wherever it was dragged.\n const { height } = this.viewport();\n for (const popover of [this.menu, this.cards, this.bubble]) {\n popover.style.left = side === \"left\" ? `${this.options.offset.side}px` : \"\";\n popover.style.right = side === \"right\" ? `${this.options.offset.side}px` : \"\";\n popover.style.bottom = above ? `${height - top + 12}px` : \"\";\n popover.style.top = above ? \"\" : `${top + ORB_SIZE + 12}px`;\n }\n\n // The menu.\n this.menu.hidden = !this.menuOpen;\n this.menu.setAttribute(\"aria-label\", heading);\n this.menu.style.flexDirection = above ? \"column-reverse\" : \"column\";\n this.menu.style.alignItems = side === \"right\" ? \"flex-end\" : \"flex-start\";\n if (this.menuOpen && this.menu.childElementCount !== items.length) {\n replace(\n this.menu,\n ...items.map((item) =>\n h(\n \"button\",\n { class: \"menu-item\", attrs: { type: \"button\", role: \"menuitem\" }, on: { click: () => this.choose(item.key) } },\n h(\n \"span\",\n { class: \"menu-icon\", attrs: { \"aria-hidden\": \"true\" } },\n item.key === \"chat\" ? chatIcon() : item.key === \"hold\" ? micIcon() : waveIcon(),\n ),\n item.label,\n ),\n ),\n );\n }\n\n // The card, out of the orb. The session waits for this tap, and it is\n // the same card the panel draws, from the same payload.\n this.cards.hidden = this.menuOpen || proposals.length === 0;\n // A card is redrawn when the list changes, and when its view arrives (amendment 99).\n if (proposals !== this.drawnProposals || this.state.cardViews !== this.drawnViews) {\n this.drawnProposals = proposals;\n this.drawnViews = this.state.cardViews;\n replace(this.cards, ...proposals.map((p) => confirmationCard(p, this.mihe.respondToProposal, this.state.cardViews[p.proposalId])));\n }\n\n const bubble: { text: string; tone: \"quiet\" | \"error\" } | null =\n voice.state === \"connecting\"\n ? { text: \"Setting up the microphone\u2026\", tone: \"quiet\" }\n : lastError && (voiceLive || voice.state === \"error\")\n ? { text: lastError, tone: \"error\" }\n : voice.notice\n ? { text: voice.notice, tone: \"quiet\" }\n : this.holdToTalk && this.showHint && voice.state === \"listening\"\n ? { text: \"Hold to talk\", tone: \"quiet\" }\n : null;\n this.bubble.hidden = this.menuOpen || proposals.length > 0 || bubble === null;\n this.bubble.classList.toggle(\"error\", bubble?.tone === \"error\");\n if (this.bubble.textContent !== (bubble?.text ?? \"\")) this.bubble.textContent = bubble?.text ?? \"\";\n }\n\n /**\n * The ring follows the voice level without a redraw: a level arrives tens\n * of times a second, and each one only moves one element.\n */\n private syncRing(): void {\n const wanted = this.voiceLive && !this.reducedMotion;\n if (wanted === (this.stopLevel !== null)) return;\n if (!wanted) {\n this.stopRing();\n return;\n }\n let target = 0;\n let current = 0;\n this.stopLevel = this.mihe.voice.onLevel((source, level) => {\n const voice = this.mihe.getState().voice;\n // Only the side whose turn it is moves the ring.\n if (source === \"user\" && (voice.state === \"recording\" || voice.handsFree)) target = level;\n if (source === \"assistant\") target = level;\n });\n const tick = () => {\n // Quick to rise, slow to fall, which reads as breathing, not flicker.\n current += (target - current) * (target > current ? 0.45 : 0.12);\n target *= 0.92;\n this.ring.style.transform = `scale(${1 + current * 0.38})`;\n this.ringFrame = requestAnimationFrame(tick);\n };\n this.ringFrame = requestAnimationFrame(tick);\n }\n\n private stopRing(): void {\n this.stopLevel?.();\n this.stopLevel = null;\n cancelAnimationFrame(this.ringFrame);\n this.ring.style.transform = \"scale(1)\";\n }\n\n // ---- the menu ----------------------------------------------------------\n\n private setMenu(open: boolean): void {\n this.menuOpen = open;\n if (!open) replace(this.menu);\n this.render();\n // Focus moves into the menu so the arrow keys and Esc work at once.\n if (open) this.menu.querySelector(\"button\")?.focus();\n }\n\n private choose(key: MenuKey): void {\n this.setMenu(false);\n if (key === \"chat\") {\n this.mihe.open();\n } else {\n // Entering a voice mode asks for the microphone now, so the browser's\n // permission prompt does not swallow the first hold.\n this.mihe.voice.start({ handsFree: key === \"hands-free\", openPanel: false });\n }\n }\n\n private onMenuKeyDown(event: KeyboardEvent): void {\n const buttons = Array.from(this.menu.querySelectorAll(\"button\"));\n const active = (this.root.getRootNode() as Document | ShadowRoot).activeElement;\n const index = buttons.indexOf(active as HTMLButtonElement);\n if (event.key === \"Escape\") {\n event.preventDefault();\n this.setMenu(false);\n this.button.focus();\n } else if (event.key === \"ArrowDown\" || event.key === \"ArrowRight\") {\n event.preventDefault();\n buttons[(index + 1) % buttons.length]?.focus();\n } else if (event.key === \"ArrowUp\" || event.key === \"ArrowLeft\") {\n event.preventDefault();\n buttons[(index - 1 + buttons.length) % buttons.length]?.focus();\n }\n }\n\n // ---- the orb itself ----------------------------------------------------\n\n private tap(): void {\n if (this.menuOpen) {\n this.setMenu(false);\n return;\n }\n if (this.handsFree) {\n // Stops rather than pauses: a hands-free session is billed for every\n // second it is open.\n this.mihe.voice.stop();\n return;\n }\n if (this.holdToTalk) return; // a tap is too short to be speech\n if (this.menuItems().length === 1) this.mihe.open();\n else this.setMenu(true);\n }\n\n private onPointerDown(event: PointerEvent): void {\n if (event.button !== 0) return;\n this.button.setPointerCapture?.(event.pointerId);\n const { left, top } = this.position();\n const press: Press = {\n x: event.clientX,\n y: event.clientY,\n offsetX: event.clientX - left,\n offsetY: event.clientY - top,\n dragging: false,\n recording: null,\n timer: null,\n };\n this.press = press;\n\n const voice = this.mihe.getState().voice;\n if (this.holdToTalk && (voice.state === \"listening\" || voice.state === \"speaking\")) {\n if (this.options.draggable) {\n // Held on the device until it is clear this is speech, not a drag.\n voice.press({ hold: true });\n press.recording = \"held\";\n press.timer = setTimeout(() => {\n if (this.press === press && !press.dragging) {\n this.mihe.voice.confirm();\n press.recording = \"live\";\n }\n }, HOLD_WINDOW_MS);\n } else {\n voice.press();\n press.recording = \"live\";\n }\n }\n }\n\n private onPointerMove(event: PointerEvent): void {\n const press = this.press;\n if (!press || !this.options.draggable || this.menuOpen) return;\n // Once a recording is live, the finger is holding a microphone, not the orb.\n if (press.recording === \"live\") return;\n\n const moved = Math.hypot(event.clientX - press.x, event.clientY - press.y);\n if (!press.dragging && moved > DRAG_THRESHOLD) {\n press.dragging = true;\n if (press.recording === \"held\") {\n if (press.timer) clearTimeout(press.timer);\n this.mihe.voice.discard();\n press.recording = null;\n }\n }\n if (press.dragging) {\n const { width, height } = this.viewport();\n this.drag = {\n left: Math.min(Math.max(0, event.clientX - press.offsetX), width - ORB_SIZE),\n top: Math.min(Math.max(0, event.clientY - press.offsetY), height - ORB_SIZE),\n };\n this.render();\n }\n }\n\n private finish(cancelled: boolean): void {\n const press = this.press;\n this.press = null;\n if (!press) return;\n if (press.timer) clearTimeout(press.timer);\n\n if (press.dragging && this.drag) {\n // Snap to the nearer side, and keep the height.\n const { width, height } = this.viewport();\n this.rest = {\n side: this.drag.left + ORB_SIZE / 2 < width / 2 ? \"left\" : \"right\",\n y: this.clampTop(this.drag.top) / height,\n };\n writeRest(this.rest);\n this.drag = null;\n this.render();\n return;\n }\n this.drag = null;\n\n if (press.recording) {\n // Released: a held press that never moved is speech too.\n this.mihe.voice.release();\n return;\n }\n if (!cancelled) this.tap();\n }\n\n private onKeyDown(event: KeyboardEvent): void {\n if (event.repeat) return;\n const voice = this.mihe.getState().voice;\n if (this.holdToTalk && event.key === \" \") {\n event.preventDefault();\n if (voice.state === \"listening\" || voice.state === \"speaking\") voice.press();\n return;\n }\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n this.tap();\n }\n if (event.key === \"Escape\" && this.voiceLive) {\n event.preventDefault();\n voice.stop();\n }\n }\n\n private onKeyUp(event: KeyboardEvent): void {\n if (this.holdToTalk && event.key === \" \") {\n event.preventDefault();\n this.mihe.voice.release();\n }\n }\n}\n\nfunction sameList(a: unknown, b: unknown): boolean {\n return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((v, i) => v === b[i]);\n}\n", "/**\n * The little Markdown a model writes in a chat reply, read into a tree that\n * each widget draws with its own elements.\n *\n * Models write `**bold**`, lists and the odd `code` whatever the prompt says,\n * and a reply full of asterisks reads as broken. This reads that subset and\n * nothing more: paragraphs, bullet and numbered lists, headings (drawn as bold\n * lines), bold, italic, inline code and links. Never HTML: the tree holds only\n * strings, every renderer puts them in as text, and a link keeps only an\n * http(s) or mailto address. A reply is still model prose, so it gets no more\n * power than plain text had.\n *\n * Safe on a reply that is still streaming: an unclosed `**` stays as written\n * until its pair arrives.\n */\n\nexport type Inline =\n | { kind: \"text\"; text: string; bold?: boolean; italic?: boolean }\n | { kind: \"code\"; text: string }\n | { kind: \"link\"; text: string; href: string };\n\nexport type Block =\n | { kind: \"paragraph\"; lines: Inline[][] }\n | { kind: \"heading\"; inline: Inline[] }\n | { kind: \"list\"; ordered: boolean; start: number; items: Inline[][] };\n\nconst BULLET = /^\\s*[-*\u2022]\\s+(.*)$/;\nconst NUMBERED = /^\\s*(\\d{1,3})[.)]\\s+(.*)$/;\nconst HEADING = /^\\s{0,3}#{1,6}\\s+(.*?)\\s*#*\\s*$/;\n\nexport function parseMarkdown(text: string): Block[] {\n const blocks: Block[] = [];\n let paragraph: string[] = [];\n let list: { ordered: boolean; start: number; items: string[] } | null = null;\n\n const flush = () => {\n if (paragraph.length) blocks.push({ kind: \"paragraph\", lines: paragraph.map(parseInline) });\n if (list) blocks.push({ kind: \"list\", ordered: list.ordered, start: list.start, items: list.items.map(parseInline) });\n paragraph = [];\n list = null;\n };\n\n for (const raw of text.replace(/\\r\\n?/g, \"\\n\").split(\"\\n\")) {\n const line = raw.trimEnd();\n if (line.trim() === \"\") { flush(); continue; }\n const heading = HEADING.exec(line);\n if (heading) { flush(); blocks.push({ kind: \"heading\", inline: parseInline(heading[1] ?? \"\") }); continue; }\n const bullet = BULLET.exec(line);\n const numbered = bullet ? null : NUMBERED.exec(line);\n if (bullet || numbered) {\n const ordered = !!numbered;\n if (paragraph.length || (list && list.ordered !== ordered)) flush();\n if (!list) list = { ordered, start: numbered ? Number(numbered[1]) : 1, items: [] };\n list.items.push((bullet ? bullet[1] : numbered![2]) ?? \"\");\n continue;\n }\n // A line indented under a list item carries on that item.\n if (list && /^\\s+/.test(raw)) { list.items[list.items.length - 1] += ` ${line.trim()}`; continue; }\n if (list) flush();\n paragraph.push(line);\n }\n flush();\n return blocks;\n}\n\n/** Only addresses that open a page or a mail: never `javascript:` or `data:`. */\nexport function safeHref(href: string): string | null {\n const trimmed = href.trim();\n return /^(https?:\\/\\/|mailto:)/i.test(trimmed) ? trimmed : null;\n}\n\nconst INLINE = /`([^`\\n]+)`|\\[([^\\]\\n]+)\\]\\(([^)\\s]+)\\)|\\*\\*(?=\\S)([^*\\n]*?\\S)\\*\\*|__(?=\\S)([^_\\n]*?\\S)__|(?<![\\w*])\\*(?=[^\\s*])([^*\\n]*?[^\\s*])\\*(?![\\w*])|(?<![\\w_])_(?=[^\\s_])([^_\\n]*?[^\\s_])_(?![\\w_])/g;\n\nexport function parseInline(text: string): Inline[] {\n const out: Inline[] = [];\n const push = (part: Inline) => {\n const last = out[out.length - 1];\n if (part.kind === \"text\" && last?.kind === \"text\" && !last.bold === !part.bold && !last.italic === !part.italic) last.text += part.text;\n else out.push(part);\n };\n let at = 0;\n for (const m of text.matchAll(INLINE)) {\n const index = m.index ?? 0;\n if (index > at) push({ kind: \"text\", text: text.slice(at, index) });\n const [whole, code, linkText, href, bold1, bold2, italic1, italic2] = m;\n if (code !== undefined) push({ kind: \"code\", text: code });\n else if (linkText !== undefined) {\n const safe = safeHref(href ?? \"\");\n push(safe ? { kind: \"link\", text: linkText, href: safe } : { kind: \"text\", text: linkText });\n } else if (bold1 !== undefined || bold2 !== undefined) {\n // Italic inside bold is common enough (`**an _important_ note**`).\n for (const part of parseInline(bold1 ?? bold2 ?? \"\")) push(part.kind === \"text\" ? { ...part, bold: true } : part);\n } else if (italic1 !== undefined || italic2 !== undefined) push({ kind: \"text\", text: italic1 ?? italic2 ?? \"\", italic: true });\n else push({ kind: \"text\", text: whole });\n at = index + whole.length;\n }\n if (at < text.length) push({ kind: \"text\", text: text.slice(at) });\n return out;\n}\n", "/**\n * Draws a reply's Markdown tree (`../markdown.ts`) as DOM, through `h`, so\n * every piece of text goes in as a text node.\n */\nimport { parseMarkdown, type Inline } from \"../markdown\";\nimport { h } from \"./dom\";\n\nfunction inline(parts: Inline[]): Node[] {\n return parts.map((part) => {\n if (part.kind === \"code\") return h(\"code\", {}, part.text);\n if (part.kind === \"link\") return h(\"a\", { attrs: { href: part.href, target: \"_blank\", rel: \"noreferrer\" } }, part.text);\n let node: Node = document.createTextNode(part.text);\n if (part.italic) node = h(\"em\", {}, node);\n if (part.bold) node = h(\"strong\", {}, node);\n return node;\n });\n}\n\nexport function renderMarkdown(text: string): HTMLElement {\n return h(\n \"div\",\n { class: \"md\" },\n ...parseMarkdown(text).map((block) => {\n if (block.kind === \"heading\") return h(\"p\", {}, h(\"strong\", {}, ...inline(block.inline)));\n if (block.kind === \"list\") {\n const items = block.items.map((item) => h(\"li\", {}, ...inline(item)));\n return block.ordered ? h(\"ol\", { attrs: { start: block.start === 1 ? undefined : block.start } }, ...items) : h(\"ul\", {}, ...items);\n }\n return h(\"p\", {}, ...block.lines.flatMap((line, i) => (i ? [h(\"br\"), ...inline(line)] : inline(line))));\n }),\n );\n}\n", "/**\n * The conversation: transcript, confirmation cards, composer; and in the\n * classic form a Talk button in the header, which shows the call in place of\n * the conversation while it lasts (amendment 105). The chat box has no\n * microphone.\n *\n * Built once and updated. The composer and the microphone buttons in\n * particular are never rebuilt: a rebuilt input loses what the user was\n * typing, and a push-to-talk button replaced mid-press loses its pointer\n * capture, so the release lands elsewhere and the microphone stays open.\n */\n\nimport type { VoiceMode } from \"@mihe-dev/protocol\";\n\nimport { ASSISTANT_PANEL_ID, type Mihe, type MiheState } from \"../mihe\";\nimport { confirmationCard } from \"./card\";\nimport { h, replace } from \"./dom\";\nimport { renderMarkdown } from \"./markdown\";\nimport { clipIcon } from \"./icons\";\nimport { miheMark } from \"./mark\";\n\nexport const DEFAULT_VOICE_MODES: VoiceMode[] = [\"hands-free\", \"hold\"];\n\nexport interface PanelOptions {\n /** Header title; the assistant's name from the app's settings otherwise. */\n title?: string;\n placeholder?: string;\n showClose: boolean;\n /** A Talk button in the header, and the call in place of the conversation while it lasts. Off when the orb owns voice. */\n showVoice: boolean;\n}\n\nexport class Panel {\n readonly root: HTMLElement;\n private readonly heading: Text;\n private readonly dot: HTMLElement;\n private readonly closeButton: HTMLButtonElement;\n private readonly transcript: HTMLElement;\n private readonly input: HTMLInputElement;\n private readonly sendButton: HTMLButtonElement;\n /** The paperclip and its file input, shown only when the app describes attachments (amendment 102). */\n private readonly clip: HTMLButtonElement;\n private readonly fileInput: HTMLInputElement;\n private readonly chips: HTMLElement;\n private drawnAttachments: unknown = null;\n private readonly talk: HTMLButtonElement;\n private readonly call: CallView;\n private readonly body: HTMLElement;\n /** What the transcript was last drawn from, so it is redrawn only on change. */\n private drawn: unknown[] = [];\n\n constructor(\n private readonly mihe: Mihe,\n private options: PanelOptions,\n ) {\n this.heading = document.createTextNode(\"\");\n this.dot = h(\"span\", { class: \"status-dot\", attrs: { \"aria-hidden\": \"true\" } });\n this.closeButton = h(\n \"button\",\n {\n class: \"icon-button\",\n attrs: { type: \"button\", \"aria-label\": \"Close assistant\" },\n on: { click: () => this.mihe.close() },\n },\n \"\u2715\",\n );\n this.transcript = h(\"div\", { class: \"transcript\" });\n this.input = h(\"input\", {\n class: \"input\",\n attrs: { \"aria-label\": \"Message\" },\n on: {\n input: () => this.syncSend(this.mihe.getState()),\n // A pasted screenshot attaches; pasted words paste as usual.\n paste: (event) => {\n if (!this.mihe.getState().attachAccept) return;\n const files = Array.from(event.clipboardData?.files ?? []);\n if (!files.length) return;\n event.preventDefault();\n for (const file of files) this.mihe.attach(file);\n },\n },\n });\n this.fileInput = h(\"input\", {\n attrs: { type: \"file\", multiple: true, hidden: true },\n on: {\n change: () => {\n for (const file of Array.from(this.fileInput.files ?? [])) this.mihe.attach(file);\n this.fileInput.value = \"\";\n },\n },\n });\n this.clip = h(\n \"button\",\n { class: \"clip\", attrs: { type: \"button\", \"aria-label\": \"Attach a picture\", title: \"Attach a picture\" }, on: { click: () => this.fileInput.click() } },\n clipIcon(),\n );\n this.chips = h(\"ul\", { class: \"chips\", attrs: { \"aria-label\": \"Attached pictures\" } });\n this.sendButton = h(\"button\", { class: \"send\", attrs: { type: \"submit\" } }, \"Send\");\n this.talk = h(\"button\", { class: \"talk\", attrs: { type: \"button\", title: \"Talk instead of typing\" }, on: { click: () => {\n const modes = this.mihe.getState().widgetSettings?.voiceModes ?? DEFAULT_VOICE_MODES;\n this.mihe.voice.start(modes.includes(\"hands-free\") ? { handsFree: true } : undefined);\n } } }, \"Talk\");\n this.call = new CallView(mihe);\n\n const composer = h(\n \"form\",\n {\n class: \"composer\",\n on: {\n submit: (event) => {\n event.preventDefault();\n this.mihe.send(this.input.value);\n this.input.value = \"\";\n this.syncSend(this.mihe.getState());\n },\n },\n },\n this.clip,\n this.input,\n this.sendButton,\n // Last, so the message box stays the composer's first input.\n this.fileInput,\n );\n\n this.root = h(\n \"section\",\n { class: \"panel\", attrs: { id: ASSISTANT_PANEL_ID } },\n h(\"header\", { class: \"header\" }, h(\"div\", { class: \"header-title\" }, this.dot, this.heading), this.talk, this.closeButton),\n (this.body = h(\"div\", { class: \"panel-body\" }, this.transcript, this.chips, composer)),\n this.call.root,\n );\n }\n\n setOptions(options: PanelOptions): void {\n this.options = options;\n }\n\n update(state: MiheState): void {\n const heading = this.options.title ?? state.assistantName ?? \"Assistant\";\n this.heading.data = heading;\n this.root.setAttribute(\"aria-label\", heading);\n this.dot.classList.toggle(\"open\", state.connection === \"open\");\n this.closeButton.hidden = !this.options.showClose;\n this.input.placeholder = this.options.placeholder ?? \"Ask me anything about this app\u2026\";\n this.input.disabled = state.connection !== \"open\";\n this.clip.hidden = !state.attachAccept;\n this.clip.disabled = state.connection !== \"open\";\n if (state.attachAccept) this.fileInput.accept = state.attachAccept;\n if (state.attachments !== this.drawnAttachments) { this.drawnAttachments = state.attachments; this.drawChips(state); }\n this.syncSend(state);\n\n // The modes the app allows. Offering one it does not would be a button\n // the runtime refuses.\n const modes = state.widgetSettings?.voiceModes ?? DEFAULT_VOICE_MODES;\n const canTalk = this.options.showVoice && state.voice.available && modes.length > 0;\n const onCall = this.options.showVoice && state.voice.state !== \"idle\" && state.voice.state !== \"error\";\n this.talk.hidden = !canTalk || onCall;\n this.talk.disabled = state.connection !== \"open\";\n this.body.hidden = onCall;\n this.call.update(state, onCall);\n\n const inputs = [\n state.messages,\n state.proposals,\n state.cardViews,\n state.voice.state === \"connecting\",\n state.voice.notice,\n state.lastError,\n state.connection,\n ];\n if (inputs.every((value, index) => value === this.drawn[index])) return;\n this.drawn = inputs;\n this.drawTranscript(state);\n }\n\n private syncSend(state: MiheState): void {\n const reading = state.attachments.some((a) => a.status === \"reading\");\n const ready = state.attachments.some((a) => a.status === \"ready\");\n this.sendButton.disabled = state.busy || state.connection !== \"open\" || reading || (!this.input.value.trim() && !ready);\n }\n\n private drawChips(state: MiheState): void {\n this.chips.hidden = state.attachments.length === 0;\n replace(\n this.chips,\n ...state.attachments.map((a) =>\n h(\n \"li\",\n { class: a.status === \"failed\" ? \"chip failed\" : \"chip\" },\n a.previewUrl && h(\"img\", { attrs: { src: a.previewUrl, alt: \"\" } }),\n h(\n \"span\",\n { class: \"chip-text\" },\n h(\"span\", { class: \"chip-name\" }, a.name),\n h(\"span\", { class: \"chip-status\", attrs: a.status === \"failed\" ? { role: \"alert\" } : {} }, a.status === \"reading\" ? \"Reading\u2026\" : a.status === \"ready\" ? \"Ready\" : a.error ?? \"Couldn't read it\"),\n ),\n h(\"button\", { class: \"chip-remove\", attrs: { type: \"button\", \"aria-label\": `Remove ${a.name}` }, on: { click: () => this.mihe.removeAttachment(a.id) } }, \"\u2715\"),\n ),\n ),\n );\n }\n\n private drawTranscript(state: MiheState): void {\n replace(\n this.transcript,\n state.messages.length === 0 &&\n h(\n \"p\",\n { class: \"empty\" },\n \"Ask where something is, what a screen does, or for help getting something done.\",\n ),\n ...state.messages.map((message) =>\n h(\n \"div\",\n { class: message.role === \"user\" ? \"bubble-user\" : \"bubble-assistant\" },\n message.attachments && message.attachments.length > 0 &&\n h(\n \"div\",\n { class: \"sent-pictures\" },\n ...message.attachments.map((a) =>\n a.previewUrl ? h(\"img\", { attrs: { src: a.previewUrl, alt: a.name, title: a.name } }) : h(\"span\", { class: \"sent-name\" }, `\uD83D\uDCCE ${a.name}`),\n ),\n ),\n message.role === \"user\" ? message.text : renderMarkdown(message.text),\n message.streaming && message.text === \"\" && h(\"span\", { class: \"thinking\" }, \"thinking\u2026\"),\n message.sources && message.sources.length > 0 &&\n // Titles only. The snippet is what the model read, not something\n // a reader needs.\n h(\n \"div\",\n { class: \"sources\" },\n ...message.sources.map((source) =>\n h(\n \"span\",\n { class: \"source\" },\n source.url && /^https?:\\/\\//i.test(source.url)\n ? h(\"a\", { attrs: { href: source.url, target: \"_blank\", rel: \"noreferrer noopener\" } }, source.title)\n : source.title,\n ),\n ),\n ),\n ),\n ),\n ...state.proposals.map((proposal) => confirmationCard(proposal, this.mihe.respondToProposal, state.cardViews[proposal.proposalId])),\n // Setting up a voice session takes a moment, and the microphone stays\n // shut until it finishes (amendment 32). Without a word here the user\n // talks into a session that is not listening yet.\n state.voice.state === \"connecting\" && h(\"div\", { class: \"voice-notice\" }, \"Setting up the microphone\u2026\"),\n state.voice.notice && h(\"div\", { class: \"voice-notice\" }, state.voice.notice),\n state.lastError && h(\"div\", { class: \"error\" }, state.lastError),\n state.connection !== \"open\" &&\n h(\"div\", { class: \"notice\" }, state.connection === \"closed\" ? \"Disconnected.\" : \"Connecting\u2026\"),\n );\n this.transcript.scrollTo?.({ top: this.transcript.scrollHeight, behavior: \"smooth\" });\n }\n}\n\n/**\n * The microphone, in whichever of its two modes is running.\n *\n * Idle, it offers both: hold to talk, or hands-free. Once a session is live\n * only the running one is shown, with a way out.\n */\n/**\n * The call in place of the conversation (amendment 105): the mark moving with\n * whoever speaks, one line of what's said (held until the next begins), any\n * card that needs a tap, hold to talk when the call isn't hands-free, and End.\n * A copy of the React SDK's `call.tsx`.\n */\nclass CallView {\n readonly root: HTMLElement;\n private readonly cards: HTMLElement;\n private readonly ring: HTMLElement;\n private readonly line: HTMLElement;\n private readonly hold: HTMLButtonElement;\n private held = \"\";\n private drawnCards: unknown = null;\n private stopLevel: (() => void) | null = null;\n\n constructor(private readonly mihe: Mihe) {\n this.cards = h(\"div\", { class: \"call-cards\" });\n this.ring = h(\"span\", { class: \"call-ring\" });\n this.line = h(\"span\", { class: \"call-line\", attrs: { \"aria-live\": \"polite\" } });\n this.hold = pushToTalk(mihe);\n this.hold.classList.add(\"call-hold\");\n const end = h(\"button\", { class: \"call-end\", attrs: { type: \"button\" }, on: { click: () => this.mihe.voice.stop() } }, \"End\");\n const mark = h(\"span\", { class: \"call-mark\", attrs: { \"aria-hidden\": \"true\" } }, this.ring, miheMark(26, \"#0f172a\", \"#007277\"));\n this.root = h(\"div\", { class: \"call\", attrs: { role: \"region\", \"aria-label\": \"Voice call\" } }, this.cards, h(\"div\", { class: \"call-bar\" }, mark, this.line, this.hold, end));\n this.root.hidden = true;\n }\n\n update(state: MiheState, on: boolean): void {\n this.root.hidden = !on;\n const v = state.voice;\n if (!on) { this.held = \"\"; this.stopLevel?.(); this.stopLevel = null; return; }\n if (!this.stopLevel && !matchMedia?.(\"(prefers-reduced-motion: reduce)\").matches) {\n this.stopLevel = this.mihe.voice.onLevel((source, level) => {\n const now = this.mihe.getState().voice;\n if (source === \"assistant\" || now.handsFree || now.state === \"recording\") this.ring.style.transform = `scale(${1 + level * 0.38})`;\n });\n }\n if (v.caption) this.held = v.caption;\n const fallback = v.state === \"connecting\" ? \"Setting up the microphone\u2026\" : v.state === \"thinking\" ? \"Thinking\u2026\" : v.state === \"recording\" ? \"Listening\u2026\" : v.handsFree ? (v.state === \"speaking\" ? \"\" : \"Listening, just talk\") : \"Hold to talk\";\n const text = (v.caption || this.held || (state.lastError && v.state !== \"connecting\" ? state.lastError : \"\") || fallback).replace(/\\s+/g, \" \").trim();\n this.line.textContent = text.length <= 90 ? text : `\u2026${text.slice(text.length - 89).replace(/^\\S*\\s/, \"\")}`;\n this.line.title = text;\n this.hold.hidden = v.handsFree || v.state === \"connecting\";\n this.hold.textContent = v.state === \"recording\" ? \"Release to send\" : \"Hold to talk\";\n if (state.proposals !== this.drawnCards) {\n this.drawnCards = state.proposals;\n replace(this.cards, ...state.proposals.map((p) => confirmationCard(p, this.mihe.respondToProposal, state.cardViews[p.proposalId])));\n }\n }\n}\n\n/**\n * The push-to-talk button: hold to speak, release to send, the same gesture\n * on a mouse, a touchscreen and a keyboard.\n *\n * - **Pointer down, not click,** because the press begins the utterance and\n * the release ends it. It is also the gesture iOS Safari needs before any\n * audio, which is why the first press opens the session.\n * - **Pointer capture,** so a finger that slides off still ends the\n * utterance on release.\n * - **Space and Enter, held,** with key repeat ignored.\n *\n * State is read at the moment of each event, never captured.\n */\nfunction pushToTalk(mihe: Mihe): HTMLButtonElement {\n const phase = () => {\n const state = mihe.getState().voice.state;\n const connecting = state === \"connecting\";\n return { connecting, live: state !== \"idle\" && state !== \"error\" && !connecting };\n };\n const begin = (button: HTMLElement, pointerId?: number) => {\n const { connecting, live } = phase();\n if (connecting) return;\n if (!live) {\n mihe.voice.start();\n return;\n }\n if (pointerId !== undefined) button.setPointerCapture?.(pointerId);\n mihe.voice.press();\n };\n const end = () => {\n if (phase().live) mihe.voice.release();\n };\n\n const button: HTMLButtonElement = h(\n \"button\",\n {\n class: \"mic\",\n attrs: { type: \"button\" },\n on: {\n pointerdown: (event) => begin(button, event.pointerId),\n pointerup: end,\n pointercancel: end,\n keydown: (event) => {\n if (event.repeat) return;\n if (event.key === \" \" || event.key === \"Enter\") {\n event.preventDefault();\n begin(button);\n }\n },\n keyup: (event) => {\n if (event.key === \" \" || event.key === \"Enter\") {\n event.preventDefault();\n end();\n }\n },\n },\n },\n \"\\u{1F399}\",\n );\n return button;\n}\n", "/**\n * `<mihe-assistant>`: the widget as a web component, for any page and any\n * framework (doc 08). The orb by default, or the classic launcher and panel,\n * or just the panel inside a container of the host's.\n *\n * ```ts\n * import { createMihe, mountWidget } from \"@mihe-dev/js\";\n * const mihe = createMihe({ \u2026 });\n * mountWidget(mihe); // floating\n * mountWidget(mihe, { layout: \"inline\", container: sidebar }); // in a sidebar\n * ```\n *\n * Or in HTML, connected by script:\n *\n * ```html\n * <mihe-assistant form=\"classic\" corner=\"bottom-left\"></mihe-assistant>\n * <script type=\"module\">\n * defineMiheAssistant();\n * document.querySelector(\"mihe-assistant\").mihe = createMihe({ \u2026 });\n * </script>\n * ```\n *\n * Everything renders in a shadow root, so the host page's CSS cannot reach\n * it. One consequence: the panel's id is inside the shadow root, so a host\n * trigger's `aria-controls` cannot point at it.\n *\n * The class is defined when `defineMiheAssistant` first runs, never on\n * import: importing must work where `HTMLElement` does not exist, such as a\n * server render, and must not register anything a page did not ask for.\n */\n\nimport type { VoiceMode, WidgetCorner, WidgetForm, WidgetSettings } from \"@mihe-dev/protocol\";\n\nimport type { Mihe } from \"../mihe\";\nimport { h, replace, setAttrs } from \"./dom\";\nimport { launcherFace } from \"./launcher\";\nimport { Orb } from \"./orb\";\nimport { DEFAULT_VOICE_MODES, Panel } from \"./panel\";\nimport { STYLES } from \"./styles\";\n\nexport const ELEMENT_NAME = \"mihe-assistant\";\n\nexport interface WidgetOptions {\n /**\n * `\"floating\"`, the default: the orb or launcher, and a panel over the\n * page. `\"inline\"`: the panel alone, filling the element, for a sidebar or\n * a drawer the host app owns.\n */\n layout?: \"floating\" | \"inline\";\n /** Header title, and the launcher's accessible name. The app's assistant name otherwise. */\n title?: string;\n placeholder?: string;\n /** Show the orb or launcher. Off when the host app has its own trigger. */\n showLauncher?: boolean;\n /** Open on first mount. */\n defaultOpen?: boolean;\n /** Inline only: a close button in the header. */\n showClose?: boolean;\n /** Drawn in the orb or launcher in place of the uploaded icon. Cloned. */\n launcherIcon?: Node;\n /**\n * How far the orb, launcher and floating panel sit from the bottom and side\n * edges, in pixels; 20 by default. For a page with its own floating widget\n * in the same corner, such as a support chat: lift Mihe above it rather\n * than stacking the two.\n */\n offset?: { bottom?: number; side?: number };\n /*\n * The four below are settings in the dashboard (amendment 61), delivered\n * with the handshake. One passed here overrides the setting, except that\n * voice modes only narrow it: the runtime refuses a mode the app has not\n * allowed.\n */\n form?: WidgetForm;\n draggable?: boolean;\n corner?: WidgetCorner;\n voiceModes?: VoiceMode[];\n}\n\n/** `<mihe-assistant>`, once defined. */\nexport interface MiheAssistantElement extends HTMLElement {\n /** The assistant it draws. Setting it connects the element. */\n mihe: Mihe | null;\n /** Merged over the element's attributes. */\n options: WidgetOptions;\n}\n\n/** A setting passed in code, the app's setting otherwise, the SDK's default last. */\nfunction resolve(options: WidgetOptions, settings: WidgetSettings | null) {\n const allowed = settings?.voiceModes ?? DEFAULT_VOICE_MODES;\n return {\n form: options.form ?? settings?.form ?? \"orb\",\n draggable: options.draggable ?? settings?.draggable ?? false,\n corner: options.corner ?? settings?.corner ?? \"bottom-right\",\n voiceModes: options.voiceModes ? options.voiceModes.filter((mode) => allowed.includes(mode)) : allowed,\n };\n}\n\n/** Options written as attributes, for a widget placed in HTML. */\nfunction fromAttributes(element: HTMLElement): WidgetOptions {\n const read = (name: string) => element.getAttribute(name) ?? undefined;\n const flag = (name: string) => (element.hasAttribute(name) ? read(name) !== \"false\" : undefined);\n const modes = read(\"voice-modes\");\n return {\n layout: read(\"layout\") as WidgetOptions[\"layout\"],\n // Not `title`, which every element already has as its tooltip.\n title: read(\"heading\"),\n placeholder: read(\"placeholder\"),\n showLauncher: flag(\"show-launcher\"),\n defaultOpen: flag(\"default-open\"),\n showClose: flag(\"show-close\"),\n form: read(\"form\") as WidgetForm | undefined,\n draggable: flag(\"draggable\"),\n corner: read(\"corner\") as WidgetCorner | undefined,\n offset:\n read(\"offset-bottom\") === undefined && read(\"offset-side\") === undefined\n ? undefined\n : {\n bottom: read(\"offset-bottom\") === undefined ? undefined : Number(read(\"offset-bottom\")),\n side: read(\"offset-side\") === undefined ? undefined : Number(read(\"offset-side\")),\n },\n voiceModes: modes === undefined ? undefined : (modes.split(/[\\s,]+/).filter(Boolean) as VoiceMode[]),\n };\n}\n\nfunction withoutUndefined(options: WidgetOptions): WidgetOptions {\n return Object.fromEntries(Object.entries(options).filter(([, value]) => value !== undefined));\n}\n\n/** Register `<mihe-assistant>`. Safe to call more than once. */\nexport function defineMiheAssistant(): void {\n if (typeof customElements === \"undefined\" || customElements.get(ELEMENT_NAME)) return;\n\n class Element extends HTMLElement implements MiheAssistantElement {\n static observedAttributes = [\n \"layout\",\n \"heading\",\n \"placeholder\",\n \"show-launcher\",\n \"default-open\",\n \"show-close\",\n \"form\",\n \"draggable\",\n \"corner\",\n \"voice-modes\",\n \"offset-bottom\",\n \"offset-side\",\n ];\n\n private assistant: Mihe | null = null;\n private ownOptions: WidgetOptions = {};\n private unsubscribe: (() => void) | null = null;\n private shadow: ShadowRoot | null = null;\n private panel: Panel | null = null;\n private orb: Orb | null = null;\n private launcher: HTMLButtonElement | null = null;\n private launcherKey: unknown[] = [];\n private failedIcon: string | null = null;\n private openedByDefault = false;\n\n get mihe(): Mihe | null {\n return this.assistant;\n }\n\n set mihe(value: Mihe | null) {\n if (value === this.assistant) return;\n this.disconnectAssistant();\n this.assistant = value;\n if (this.isConnected) this.connectAssistant();\n }\n\n get options(): WidgetOptions {\n return this.ownOptions;\n }\n\n set options(value: WidgetOptions) {\n this.ownOptions = { ...value };\n this.render();\n }\n\n connectedCallback(): void {\n if (!this.shadow) {\n this.shadow = this.attachShadow({ mode: \"open\" });\n this.shadow.appendChild(h(\"style\", {}, STYLES));\n }\n this.connectAssistant();\n }\n\n disconnectedCallback(): void {\n this.disconnectAssistant();\n }\n\n attributeChangedCallback(): void {\n this.render();\n }\n\n private connectAssistant(): void {\n if (!this.assistant || this.unsubscribe || !this.shadow) return;\n const mihe = this.assistant;\n this.panel = new Panel(mihe, { showClose: true, showVoice: true });\n this.shadow.appendChild(this.panel.root);\n this.unsubscribe = mihe.subscribe(() => this.render());\n // Honoured once. After that the assistant owns open and closed, so a\n // host trigger and this option cannot fight over it.\n if (this.resolved().defaultOpen && !this.openedByDefault) {\n this.openedByDefault = true;\n mihe.open();\n }\n this.render();\n }\n\n private disconnectAssistant(): void {\n this.unsubscribe?.();\n this.unsubscribe = null;\n this.orb?.destroy();\n for (const part of [this.panel?.root, this.orb?.root, this.launcher]) part?.remove();\n this.panel = null;\n this.orb = null;\n this.launcher = null;\n this.launcherKey = [];\n }\n\n private resolved(): WidgetOptions {\n return { ...withoutUndefined(fromAttributes(this)), ...withoutUndefined(this.ownOptions) };\n }\n\n private render(): void {\n const mihe = this.assistant;\n if (!mihe || !this.shadow || !this.panel) return;\n const state = mihe.getState();\n const options = this.resolved();\n const widget = resolve(options, state.widgetSettings);\n const heading = options.title ?? state.assistantName ?? \"Assistant\";\n const offset = { bottom: options.offset?.bottom ?? 20, side: options.offset?.side ?? 20 };\n const showLauncher = options.showLauncher ?? true;\n const panel = this.panel;\n\n if (options.layout === \"inline\") {\n // For `:host([layout=\"inline\"])`. Set only when missing, because\n // setting an observed attribute calls back into this render.\n if (this.getAttribute(\"layout\") !== \"inline\") this.setAttribute(\"layout\", \"inline\");\n panel.setOptions({\n title: options.title,\n placeholder: options.placeholder,\n showClose: options.showClose ?? false,\n showVoice: true,\n });\n panel.root.classList.remove(\"floating\", \"left\", \"right\");\n panel.root.removeAttribute(\"style\");\n panel.root.hidden = false;\n panel.update(state);\n this.hideOrb();\n if (this.launcher) this.launcher.hidden = true;\n return;\n }\n\n if (widget.form === \"orb\") {\n if (this.launcher) this.launcher.hidden = true;\n if (!this.orb) {\n this.orb = new Orb(mihe);\n this.shadow.appendChild(this.orb.root);\n }\n this.orb.update(state, {\n heading,\n draggable: widget.draggable,\n corner: widget.corner,\n voiceModes: widget.voiceModes,\n launcherIcon: options.launcherIcon,\n offset,\n });\n // Chat open: the panel takes the orb's place, on the orb's side.\n // Voice is the orb's, so the panel has no microphone.\n this.orb.root.hidden = state.isOpen || !showLauncher;\n this.showPanel(state.isOpen, this.orb.side(), offset, {\n title: options.title,\n placeholder: options.placeholder,\n showClose: true,\n showVoice: false,\n });\n return;\n }\n\n this.hideOrb();\n this.showPanel(state.isOpen, \"right\", offset, {\n title: options.title,\n placeholder: options.placeholder,\n showClose: true,\n showVoice: true,\n });\n const launcher = this.drawLauncher(heading, options.launcherIcon, state.launcherIconUrl);\n launcher.hidden = state.isOpen || !showLauncher;\n launcher.style.bottom = `${offset.bottom}px`;\n launcher.style.right = `${offset.side}px`;\n }\n\n private showPanel(\n open: boolean,\n side: \"left\" | \"right\",\n offset: { bottom: number; side: number },\n options: ConstructorParameters<typeof Panel>[1],\n ) {\n const panel = this.panel!;\n panel.root.hidden = !open;\n panel.root.classList.add(\"floating\");\n panel.root.style.bottom = `${offset.bottom}px`;\n panel.root.style.left = side === \"left\" ? `${offset.side}px` : \"\";\n panel.root.style.right = side === \"right\" ? `${offset.side}px` : \"\";\n // The panel keeps to the viewport however far it is lifted.\n panel.root.style.height = `min(560px, calc(100vh - ${offset.bottom + 20}px))`;\n panel.setOptions(options);\n panel.update(this.assistant!.getState());\n }\n\n private hideOrb(): void {\n if (this.orb) this.orb.root.hidden = true;\n }\n\n private drawLauncher(heading: string, icon: Node | undefined, url: string | null): HTMLButtonElement {\n if (!this.launcher) {\n this.launcher = h(\"button\", {\n class: \"launcher\",\n attrs: { type: \"button\", \"aria-controls\": \"mihe-panel\", \"aria-expanded\": \"false\" },\n on: { click: () => this.assistant?.open() },\n });\n this.shadow!.appendChild(this.launcher);\n }\n setAttrs(this.launcher, { \"aria-label\": `Open ${heading}` });\n const key = [icon, url, this.failedIcon];\n if (key.some((value, index) => value !== this.launcherKey[index])) {\n this.launcherKey = key;\n replace(\n this.launcher,\n launcherFace(icon, url, this.failedIcon, (failed) => {\n this.failedIcon = failed;\n this.render();\n }),\n );\n }\n return this.launcher;\n }\n }\n\n customElements.define(ELEMENT_NAME, Element);\n}\n\nexport interface MountedWidget {\n element: MiheAssistantElement;\n /** Remove the widget. The assistant stays connected. */\n unmount: () => void;\n}\n\n/**\n * Put the widget on the page: floating over it by default, or inline in\n * `container` with `layout: \"inline\"`.\n */\nexport function mountWidget(\n mihe: Mihe,\n options: WidgetOptions & { container?: Element } = {},\n): MountedWidget {\n defineMiheAssistant();\n const { container, ...widget } = options;\n const element = document.createElement(ELEMENT_NAME) as MiheAssistantElement;\n element.options = widget;\n element.mihe = mihe;\n (container ?? document.body).appendChild(element);\n return { element, unmount: () => element.remove() };\n}\n"],
5
- "mappings": ";wKAQO,IAAMA,EAAmB,EAWnBC,EAAoB,KCgIjC,IAAMC,GAAyB,IAElBC,EAAN,KAAsB,CAY3B,YAA6BC,EAAwB,CAAxBC,EAAA,eAAAD,GAX7BC,EAAA,KAAQ,SAA+B,MACvCA,EAAA,KAAQ,QAAyB,QACjCA,EAAA,KAAQ,UAAU,GAClBA,EAAA,KAAQ,iBAAuD,MAC/DA,EAAA,KAAQ,iBAAwD,MAEhEA,EAAA,KAAQ,UAAU,IAClBA,EAAA,KAAQ,iBAAgC,MAExCA,EAAA,KAAQ,aAAmC,KAEW,CAEtD,IAAI,iBAAmC,CACrC,OAAO,KAAK,KACd,CAEA,MAAM,SAAyB,CACzB,KAAK,QAAU,cAAgB,KAAK,QAAU,SAClD,KAAK,QAAU,GACf,MAAM,KAAK,KAAK,EAClB,CAEA,OAAc,CACZ,KAAK,QAAU,GACf,KAAK,YAAY,EACjB,KAAK,QAAQ,MAAM,EACnB,KAAK,OAAS,KACd,KAAK,SAAS,QAAQ,CACxB,CAGA,YAAYC,EAAcC,EAA2B,CACnD,KAAK,KAAK,CAAE,KAAM,UAAW,KAAM,CAAE,KAAAD,EAAM,YAAAC,CAAY,CAAE,CAAC,CAC5D,CAgBA,aAAaC,EAAoC,CAC/C,KAAK,WAAaA,EAClB,KAAK,KAAK,CAAE,KAAM,gBAAiB,KAAM,CAAE,OAAAA,CAAO,CAAE,CAAC,CACvD,CAMA,WACEC,EACAC,EACAC,EACAC,EACM,CACN,KAAK,KAAK,CACR,KAAM,iBACN,KAAM,CACJ,UAAAH,EACA,GAAAC,EACA,OAASC,GAAU,KACnB,MAAOC,GAAS,IAClB,CACF,CAAC,CACH,CAEA,cACEC,EACAC,EACAC,EACM,CACN,KAAK,KAAK,CACR,KAAM,iBACN,KAAM,CACJ,WAAAF,EACA,UAAAC,EACA,YAAcC,GAAe,IAC/B,CACF,CAAC,CACH,CAEA,WAAWC,EAAsB,CAC/B,KAAK,KAAK,CAAE,KAAM,SAAU,KAAM,CAAE,OAAAA,CAAO,CAAE,CAAC,CAChD,CAeA,mBAA0B,CACxB,KAAK,KAAK,CAAE,KAAM,qBAAsB,CAAC,CAC3C,CAIA,MAAc,MAAsB,CAClC,KAAK,SAAS,KAAK,UAAY,EAAI,aAAe,cAAc,EAEhE,IAAIC,EACJ,GAAI,CACFA,EAAQ,MAAM,KAAK,QAAQ,aAAa,CAC1C,MAAgB,CACd,GAAI,KAAK,QAAS,OAGlB,KAAK,KAAK,CACR,KAAM,QACN,KAAM,yBACN,QAAS,8CACT,YAAa,EACf,CAAC,EACD,KAAK,kBAAkB,EACvB,MACF,CAMA,GAAI,KAAK,QAAS,OAMlB,IAAMC,GAHJ,KAAK,QAAQ,gBACXC,GAAgB,IAAI,UAAUA,CAAG,IAEd,KAAK,QAAQ,GAAG,EACvC,KAAK,OAASD,EAEdA,EAAO,OAAS,IAAM,CACpB,IAAME,EAAsB,CAC1B,KAAM,OACN,KAAM,CACJ,UAAW,KAAK,QAAQ,UACxB,UAAWH,EACX,WAAY,KAAK,QAAQ,WACzB,gBAAiB,EACjB,eAAgB,KAAK,eACrB,GAAI,KAAK,QAAQ,MAAQ,CAAE,MAAO,EAAK,EAAI,CAAC,CAC9C,CACF,EACAC,EAAO,KAAK,KAAK,UAAUE,CAAI,CAAC,CAClC,EAEAF,EAAO,UAAaG,GAAU,CAC5B,GAAI,OAAOA,EAAM,MAAS,SAAU,OACpC,IAAIC,EACJ,GAAI,CACFA,EAAU,KAAK,MAAMD,EAAM,IAAI,CACjC,MAAQ,CACN,MACF,CACA,KAAK,OAAOC,CAAO,CACrB,EAEAJ,EAAO,QAAU,IAAM,CAErB,GADA,KAAK,YAAY,EACb,KAAK,QAAS,CAChB,KAAK,SAAS,QAAQ,EACtB,MACF,CACA,KAAK,kBAAkB,CACzB,EAEAA,EAAO,QAAU,IAAM,CAEvB,CACF,CAEQ,OAAOI,EAA8B,CAC3C,OAAQA,EAAQ,KAAM,CACpB,IAAK,UAAW,CACd,KAAK,QAAU,EACf,KAAK,eAAiBA,EAAQ,KAAK,eACnC,KAAK,KAAK,CACR,KAAM,kBACN,UAAWA,EAAQ,KAAK,gBAAkB,EAC5C,CAAC,EAIGA,EAAQ,KAAK,eACf,KAAK,KAAK,CAAE,KAAM,iBAAkB,KAAMA,EAAQ,KAAK,aAAc,CAAC,EAExE,KAAK,KAAK,CACR,KAAM,gBACN,IAAKA,EAAQ,KAAK,iBAAmB,IACvC,CAAC,EACD,KAAK,KAAK,CAAE,KAAM,kBAAmB,SAAUA,EAAQ,KAAK,QAAU,IAAK,CAAC,EAC5E,KAAK,SAAS,MAAM,EACpB,KAAK,eAAeA,EAAQ,KAAK,mBAAmB,EAEhD,KAAK,YAAY,KAAK,aAAa,KAAK,UAAU,EACtD,KACF,CAEA,IAAK,aAAc,CACjB,KAAK,KAAK,CACR,KAAM,QACN,KAAMA,EAAQ,KAAK,KACnB,QAASA,EAAQ,KAAK,QAEtB,YAAa,EACf,CAAC,EACD,KAAK,QAAU,GACf,KACF,CAEA,IAAK,eAAgB,CAEd,KAAK,QACP,aAAa,EACb,KAAML,GACL,KAAK,KAAK,CACR,KAAM,OACN,KAAM,CACJ,UAAW,KAAK,QAAQ,UACxB,UAAWA,EACX,WAAY,KAAK,QAAQ,WACzB,gBAAiB,EACjB,eAAgB,KAAK,eACrB,GAAI,KAAK,QAAQ,MAAQ,CAAE,MAAO,EAAK,EAAI,CAAC,CAC9C,CACF,CAAC,CACH,EACC,MAAM,IAAM,KAAK,QAAQ,MAAM,CAAC,EACnC,KACF,CAEA,IAAK,aACH,KAAK,KAAK,CAAE,KAAM,aAAc,OAAQK,EAAQ,KAAK,MAAO,CAAC,EAC7D,MAEF,IAAK,QACH,KAAK,KAAK,CACR,KAAM,QACN,OAAQA,EAAQ,KAAK,OACrB,KAAMA,EAAQ,KAAK,IACrB,CAAC,EACD,MAEF,IAAK,WACH,KAAK,KAAK,CACR,KAAM,WACN,OAAQA,EAAQ,KAAK,OACrB,QAASA,EAAQ,KAAK,QACtB,QAASA,EAAQ,KAAK,SAAW,CAAC,CACpC,CAAC,EACD,MAEF,IAAK,UACH,KAAK,KAAK,CACR,KAAM,UACN,UAAWA,EAAQ,KAAK,UACxB,QAASA,EAAQ,KAAK,OACxB,CAAC,EACD,MAEF,IAAK,kBACH,KAAK,KAAK,CACR,KAAM,WACN,SAAUA,EAAQ,IACpB,CAAC,EACD,MAEF,IAAK,gBACH,KAAK,KAAK,CACR,KAAM,gBACN,WAAYA,EAAQ,KAAK,WACzB,OAAQA,EAAQ,KAAK,OACrB,OAAQA,EAAQ,KAAK,QAAU,OAC/B,MAAOA,EAAQ,KAAK,OAAS,MAC/B,CAAC,EACD,MAEF,IAAK,QACH,KAAK,KAAK,CACR,KAAM,QACN,KAAMA,EAAQ,KAAK,KACnB,QAASA,EAAQ,KAAK,QACtB,YAAaA,EAAQ,KAAK,WAC5B,CAAC,EACD,MAEF,IAAK,cACH,KAAK,KAAK,CACR,KAAM,cACN,MAAOA,EAAQ,KAAK,MACpB,UAAWA,EAAQ,KAAK,SAC1B,CAAC,EACD,MAEF,IAAK,iBACH,KAAK,KAAK,CAAE,KAAM,iBAAkB,OAAQA,EAAQ,IAAK,CAAC,EAC1D,MAEF,IAAK,OACH,KACJ,CACF,CAEQ,KAAKA,EAA8B,CACzC,GAAI,OAAK,QAAU,QAAUA,EAAQ,OAAS,QAC9C,GAAI,CACF,KAAK,QAAQ,KAAK,KAAK,UAAUA,CAAO,CAAC,CAC3C,MAAQ,CAER,CACF,CAEQ,mBAA0B,CAChC,KAAK,SAAS,cAAc,EAC5B,IAAMC,EACJ,KAAK,QAAQ,iBAAiB,KAAK,OAAO,GAAKC,GAAQ,KAAK,OAAO,EACrE,KAAK,SAAW,EAChB,KAAK,eAAiB,WAAW,IAAG,CAAQ,KAAK,KAAK,GAAGD,CAAK,CAChE,CAEQ,eAAeE,EAA0B,CAC/C,KAAK,eAAe,EACpB,KAAK,eAAiB,YAAY,IAAM,CACtC,KAAK,KAAK,CAAE,KAAM,MAAO,CAAC,CAC5B,EAAGA,CAAU,CACf,CAEQ,gBAAuB,CACzB,KAAK,iBAAmB,OAC1B,cAAc,KAAK,cAAc,EACjC,KAAK,eAAiB,KAE1B,CAEQ,aAAoB,CAC1B,KAAK,eAAe,EAChB,KAAK,iBAAmB,OAC1B,aAAa,KAAK,cAAc,EAChC,KAAK,eAAiB,KAE1B,CAEQ,SAASC,EAA8B,CACzC,KAAK,QAAUA,IACnB,KAAK,MAAQA,EACb,KAAK,KAAK,CAAE,KAAM,QAAS,MAAAA,CAAM,CAAC,EACpC,CAEQ,KAAKL,EAA0B,CACrC,KAAK,QAAQ,QAAQA,CAAK,CAC5B,CACF,EAQA,SAASG,GAAQG,EAAyB,CACxC,IAAMC,EAAO,KAAK,IAAI,IAAO,GAAKD,EAASzB,EAAsB,EACjE,OAAO0B,EAAO,EAAI,KAAK,OAAO,GAAKA,EAAO,EAC5C,CC3fO,SAASC,EAAYC,EAAwB,CAClD,GAAIA,GAAU,KAA6B,MAAO,SAClD,GAAI,OAAOA,GAAU,SAAU,OAAOA,EACtC,GAAI,OAAOA,GAAU,UAAY,OAAOA,GAAU,UAChD,OAAO,OAAOA,CAAK,EAErB,GAAI,MAAM,QAAQA,CAAK,EACrB,OAAOA,EAAM,IAAID,CAAW,EAAE,KAAK,IAAI,GAAK,SAG9C,GAAI,OAAOC,GAAU,SAAU,CAC7B,IAAMC,EAAU,OAAO,QAAQD,CAAgC,EAIzDE,EAAWD,EAAQ,KAAK,CAAC,CAACE,CAAG,IAAM,0BAA0B,KAAKA,CAAG,CAAC,EAGtEC,EAAOH,EAAQ,KAAK,CAAC,CAACE,EAAKE,CAAM,IACrC,4CAA4C,KAAKF,CAAG,GACpD,OAAOE,GAAW,QACpB,EACA,GAAIH,GAAYE,EAAM,CACpB,IAAME,EAAUL,EACb,OAAO,CAAC,CAACE,CAAG,IAAMA,IAAQD,EAAS,CAAC,GAAKC,IAAQC,EAAK,CAAC,CAAC,EACxD,IAAI,CAAC,CAACD,EAAKE,CAAM,IAAM,GAAGE,EAAYJ,CAAG,CAAC,KAAKJ,EAAYM,CAAM,CAAC,EAAE,EACvE,MAAO,CACL,GAAGN,EAAYG,EAAS,CAAC,CAAC,CAAC,SAAMH,EAAYK,EAAK,CAAC,CAAC,CAAC,GACrD,GAAGE,CACL,EAAE,KAAK,IAAI,CACb,CAEA,OAAOL,EACJ,IAAI,CAAC,CAACE,EAAKE,CAAM,IAAM,GAAGE,EAAYJ,CAAG,CAAC,KAAKJ,EAAYM,CAAM,CAAC,EAAE,EACpE,KAAK,IAAI,CACd,CAEA,OAAO,OAAOL,CAAK,CACrB,CAGO,SAASO,EAAYJ,EAAqB,CAC/C,IAAMK,EAASL,EACZ,QAAQ,SAAU,GAAG,EACrB,QAAQ,qBAAsB,OAAO,EACrC,KAAK,EACR,OAAOK,EAAO,OAAO,CAAC,EAAE,YAAY,EAAIA,EAAO,MAAM,CAAC,EAAE,YAAY,CACtE,CCJA,IAAMC,EAAYC,GAAwBA,GAAK,OAAOA,GAAM,SAAYA,EAAe,CAAC,EAClFC,GAAUC,GAAcA,EAAE,eAAe,IAAM,GAC/CC,GAAQ,CAACC,EAAaF,IAAe,OAAOA,EAAE,OAAU,UAAYA,EAAE,MAAM,KAAK,EAAIA,EAAE,MAAQG,EAAYD,CAAG,EAG7G,SAASE,EAAYC,EAAgBC,EAAyB,CACnE,IAAMN,EAAIH,EAASS,CAAM,EACnBC,EAAMP,EAAE,eAAe,EAC7B,GAAIO,IAAQ,SAAW,OAAOF,GAAU,UAAW,OAAOA,EAAQ,MAAQ,KAC1E,GAAIE,IAAQ,SAAW,OAAOF,GAAU,SAAU,OAAOA,EAAM,eAAe,OAAW,CAAE,sBAAuB,EAAG,sBAAuB,CAAE,CAAC,EAC/I,GAAIE,IAAQ,QAAU,OAAOF,GAAU,UAAY,CAAC,OAAO,MAAM,KAAK,MAAMA,CAAK,CAAC,EAAG,OAAO,IAAI,KAAKA,CAAK,EAAE,mBAAmB,OAAW,CAAE,IAAK,UAAW,MAAO,QAAS,KAAM,SAAU,CAAC,EAC7L,GAAI,MAAM,QAAQA,CAAK,EAAG,OAAOA,EAAM,IAAKP,GAAMM,EAAYN,EAAGE,EAAE,KAAK,CAAC,EAAE,KAAK,IAAI,GAAK,SACzF,GAAIK,GAAS,OAAOA,GAAU,SAAU,CACtC,IAAMG,EAAQR,EAAE,YAAc,CAAC,EAEzBS,EADO,CAAC,GAAG,OAAO,KAAKD,CAAK,EAAE,OAAQE,GAAMA,KAAML,CAAgB,EAAG,GAAG,OAAO,KAAKA,CAAe,EAAE,OAAQK,GAAM,EAAEA,KAAKF,EAAM,CAAC,EAClH,OAAQE,GAAM,CAACX,GAAOF,EAASW,EAAME,CAAC,CAAC,CAAC,CAAC,EAE9D,OAAI,OAAO,KAAKF,CAAK,EAAE,OAAeC,EAAQ,IAAKC,GAAOA,IAAMD,EAAQ,CAAC,GAAK,OAAQJ,EAAcK,CAAC,GAAM,SAAW,OAAQL,EAAcK,CAAC,CAAC,EAAI,GAAGT,GAAMS,EAAGb,EAASW,EAAME,CAAC,CAAC,CAAC,CAAC,IAAIN,EAAaC,EAAcK,CAAC,EAAGF,EAAME,CAAC,CAAC,CAAC,EAAG,EAAE,KAAK,QAAK,EACrOC,EAAYN,CAAK,CAC1B,CACA,OAAOM,EAAYN,CAAK,CAC1B,CAGO,SAASO,GAAYC,EAAmBC,EAAgCR,EAA+B,CAC5G,GAAI,OAAOO,GAAa,UAAY,CAACA,EAAS,KAAK,EAAG,OAAO,KAC7D,IAAIE,EAAU,GACRC,EAAMH,EAAS,QAAQ,uBAAwB,CAACI,EAAGf,IAAgB,CACvE,IAAMJ,EAAIgB,EAAMZ,CAAG,EACnB,OAAuBJ,GAAM,MAAQA,IAAM,IAAMiB,EAAU,GAAa,IACjE,MAAM,QAAQjB,CAAC,EAAI,OAAOA,EAAE,MAAM,EAAIM,EAAYN,EAAGQ,EAAO,aAAaJ,CAAG,CAAC,CACtF,CAAC,EACD,OAAOa,EAAU,KAAOC,CAC1B,CAGO,SAASE,EAAWC,EAAqC,CAC9D,IAAMb,EAAST,EAASsB,EAAS,WAAW,EACtCX,EAAQF,EAAO,YAAc,CAAC,EAC9BQ,EAAQK,EAAS,OAAS,CAAC,EAC3BC,EAAS,CAAC,CAACd,EAAO,gBAAgB,GAAK,OAAO,OAAOE,CAAK,EAAE,KAAMa,GAAM,CAAE,IAAMrB,EAAIH,EAASwB,CAAC,EAAG,OAAO,OAAOrB,EAAE,OAAU,UAAYD,GAAOC,CAAC,GAAKA,EAAE,eAAe,IAAM,MAAW,CAAC,EAEvLsB,EADO,CAAC,GAAG,OAAO,KAAKd,CAAK,EAAE,OAAQE,GAAMA,KAAKI,CAAK,EAAG,GAAG,OAAO,KAAKA,CAAK,EAAE,OAAQJ,GAAM,EAAEA,KAAKF,EAAM,CAAC,EAClF,OAAQE,GAAM,CAACX,GAAOF,EAASW,EAAME,CAAC,CAAC,CAAC,CAAC,EAAE,IAAKA,GAAM,CACnF,IAAMZ,EAAIgB,EAAMJ,CAAC,EACXa,EAAQH,EAASnB,GAAMS,EAAGb,EAASW,EAAME,CAAC,CAAC,CAAC,EAAIP,EAAYO,CAAC,EACnE,OAAO,MAAM,QAAQZ,CAAC,GAAKA,EAAE,OAAS,EAAI,CAAE,MAAAyB,EAAO,MAAOzB,EAAE,IAAK0B,GAAMpB,EAAYoB,EAAG3B,EAASW,EAAME,CAAC,CAAC,EAAE,KAAK,CAAC,CAAE,EAAI,CAAE,MAAAa,EAAO,KAAMnB,EAAYN,EAAGU,EAAME,CAAC,CAAC,CAAE,CAC/J,CAAC,EACKe,EAAUb,GAAYN,EAAO,gBAAgB,EAAGQ,EAAOR,CAAM,GAAK,OACxE,MAAO,CAAE,OAAQc,EAAS,QAAU,QAAS,GAAIK,EAAU,CAAE,QAAAA,CAAQ,EAAI,CAAC,EAAI,MAAAH,CAAM,CACtF,CAEA,IAAMI,GAAW,iCAGV,SAASC,GAAQC,EAAkC,CACxD,IAAMC,EAAO/B,GAAgB,OAAOA,GAAM,SAAWA,EAAuBA,GAAM,KAAO,OAAY,OAAOA,CAAC,EAC7G,MAAO,CACL,OAAQ,MACR,GAAI+B,EAAID,EAAE,OAAO,EAAI,CAAE,QAASC,EAAID,EAAE,OAAO,CAAE,EAAI,CAAC,EACpD,GAAIC,EAAID,EAAE,IAAI,EAAI,CAAE,KAAMC,EAAID,EAAE,IAAI,CAAE,EAAI,CAAC,EAC3C,GAAIC,EAAID,EAAE,YAAY,EAAI,CAAE,aAAcC,EAAID,EAAE,YAAY,CAAE,EAAI,CAAC,EACnE,OAAQ,MAAM,QAAQA,EAAE,KAAK,EAAIA,EAAE,MAAQ,CAAC,GAAG,IAAKE,IAAO,CACzD,GAAID,EAAIC,EAAE,KAAK,EAAI,CAAE,MAAOD,EAAIC,EAAE,KAAK,CAAE,EAAI,CAAC,EAC9C,GAAID,EAAIC,EAAE,IAAI,IAAM,OAAY,CAAE,KAAMD,EAAIC,EAAE,IAAI,CAAE,EAAI,CAAC,EACzD,GAAID,EAAIC,EAAE,MAAM,IAAM,OAAY,CAAE,OAAQD,EAAIC,EAAE,MAAM,CAAE,EAAI,CAAC,EAC/D,GAAI,MAAM,QAAQA,EAAE,KAAK,EAAI,CAAE,MAAOA,EAAE,MAAM,IAAKN,GAAM,OAAOA,CAAC,CAAC,CAAE,EAAI,CAAC,EACzE,GAAI,MAAM,QAAQM,EAAE,MAAM,EAAI,CAAE,OAAQA,EAAE,OAAO,OAAQC,GAAOA,GAAM,OAAOA,EAAG,KAAQ,UAAYL,GAAS,KAAKK,EAAG,GAAG,CAAC,EAAE,MAAM,EAAG,EAAE,EAAE,IAAKA,IAAQ,CAAE,IAAKA,EAAG,IAAK,GAAIF,EAAIE,EAAG,GAAG,EAAI,CAAE,IAAKF,EAAIE,EAAG,GAAG,CAAE,EAAI,CAAC,CAAG,EAAE,CAAE,EAAI,CAAC,CAC5N,EAAE,CACJ,CACF,CAGO,IAAMC,GAAsB,IC3H5B,IAAMC,EAAsB,sBAE7BC,GAAa,iBAYnB,SAASC,GAAUC,EAAsC,CACvD,OAAKA,EACD,EAAAA,EAAQ,QAAQ,iDAAiD,EADhD,EAGvB,CAEO,SAASC,EAAcC,EAA0C,CACtE,OAAO,SAAS,cACd,IAAIC,CAAmB,KAAK,IAAI,OAAOD,CAAY,CAAC,IACtD,CACF,CAEO,IAAME,EAAN,KAAsB,CAAtB,cACLC,EAAA,KAAQ,UAA8B,MACtCA,EAAA,KAAQ,UAA8B,MACtCA,EAAA,KAAQ,SAA6B,MACrCA,EAAA,KAAQ,WAAW,IACnBA,EAAA,KAAQ,WAAoC,MAC5CA,EAAA,KAAQ,QAAQ,GAoGhBA,EAAA,KAAQ,WAAW,IAAY,CAC7B,GAAI,CAAC,KAAK,SAAW,CAAC,KAAK,OAAQ,OAGnC,GAAI,CAAC,KAAK,OAAO,YAAa,CAC5B,KAAK,MAAM,EACX,MACF,CAEA,IAAMC,EAAO,KAAK,OAAO,sBAAsB,EAC/C,OAAO,OAAO,KAAK,QAAQ,MAAO,CAChC,IAAK,GAAGA,EAAK,IAAM,CAAO,KAC1B,KAAM,GAAGA,EAAK,KAAO,CAAO,KAC5B,MAAO,GAAGA,EAAK,MAAQ,EAAW,KAClC,OAAQ,GAAGA,EAAK,OAAS,EAAW,IACtC,CAAC,CACH,GAGAD,EAAA,KAAQ,aAAa,IAAY,CAC3B,KAAK,QACT,KAAK,MAAQ,sBAAsB,IAAM,CACvC,KAAK,MAAQ,EACb,KAAK,SAAS,CAChB,CAAC,EACH,GAGAA,EAAA,KAAQ,YAAaE,GAA+B,CAC9CA,EAAM,MAAQ,UAAU,KAAK,MAAM,CACzC,GA4BAF,EAAA,KAAQ,gBAAiBE,GAAuB,CAC9C,IAAMC,EAAOD,EAAM,OACJC,aAAgB,MAAQ,KAAK,QAAQ,SAASA,CAAI,GACnD,CAACT,GAAU,KAAK,MAAM,GACpC,KAAK,MAAM,CACb,GAhKA,UAAUG,EAAsBO,EAA2B,CACzD,IAAMC,EAAST,EAAcC,CAAY,EACzC,OAAKQ,GAEL,KAAK,OAASA,EACd,KAAK,cAAc,EACnB,KAAK,WAAWD,CAAO,EACvB,KAAK,SAAS,EACd,KAAK,cAAc,EAEnBC,EAAO,eAAe,CAAE,SAAU,SAAU,MAAO,QAAS,CAAC,EACtD,IATa,EAUtB,CAGA,MAAMR,EAAsBO,EAA2B,CACrD,IAAMC,EAAST,EAAcC,CAAY,EACzC,OAAKQ,GAEL,KAAK,UAAUR,EAAcO,CAAO,GAIlCC,EAAO,QAAQ,6CAA6C,EACxDA,EACAA,EAAO,cACL,6CACF,IACK,MAAM,EACV,IAZa,EAatB,CAEA,SAASR,EAA+B,CACtC,IAAMQ,EAAST,EAAcC,CAAY,EACzC,OAAKQ,GACLA,EAAO,eAAe,CAAE,SAAU,SAAU,MAAO,QAAS,CAAC,EACtD,IAFa,EAGtB,CAEA,OAAc,CACZ,KAAK,aAAa,EAClB,KAAK,SAAS,OAAO,EACrB,KAAK,QAAU,KACf,KAAK,QAAU,KACf,KAAK,OAAS,IAChB,CAEQ,eAAsB,CAC5B,GAAI,KAAK,QAAS,OAElB,IAAMC,EAAU,SAAS,cAAc,KAAK,EAC5CA,EAAQ,GAAKC,GAGb,OAAO,OAAOD,EAAQ,MAAO,CAC3B,SAAU,QACV,OAAQ,aACR,cAAe,OACf,OAAQ,oBACR,aAAc,MACd,UAAW,sCACX,WAAY,sEACd,CAAwC,EAExC,IAAME,EAAU,SAAS,cAAc,KAAK,EAC5C,OAAO,OAAOA,EAAQ,MAAO,CAC3B,SAAU,WACV,KAAM,IACN,IAAK,OACL,UAAW,MACX,SAAU,QACV,QAAS,WACT,aAAc,MACd,WAAY,UACZ,MAAO,UACP,KAAM,oDACN,QAAS,MACX,CAAwC,EAExCF,EAAQ,YAAYE,CAAO,EAC3B,SAAS,KAAK,YAAYF,CAAO,EAEjC,KAAK,QAAUA,EACf,KAAK,QAAUE,CACjB,CAEQ,WAAWJ,EAAwB,CACpC,KAAK,UACNA,GACF,KAAK,QAAQ,YAAcA,EAC3B,KAAK,QAAQ,MAAM,QAAU,UAE7B,KAAK,QAAQ,YAAc,GAC3B,KAAK,QAAQ,MAAM,QAAU,QAEjC,CA+EQ,eAAsB,CACxB,KAAK,WACT,KAAK,SAAW,GAChB,OAAO,iBAAiB,SAAU,KAAK,SAAU,CAAE,QAAS,GAAM,QAAS,EAAK,CAAC,EACjF,OAAO,iBAAiB,SAAU,KAAK,SAAU,CAAE,QAAS,EAAK,CAAC,EAClE,OAAO,iBAAiB,UAAW,KAAK,SAAS,EAEjD,OAAO,iBAAiB,cAAe,KAAK,cAAe,CAAE,QAAS,EAAK,CAAC,EAE5E,KAAK,SAAW,IAAI,iBAAiB,KAAK,UAAU,EACpD,KAAK,SAAS,QAAQ,SAAS,KAAM,CAAE,UAAW,GAAM,QAAS,EAAK,CAAC,EACzE,CAEQ,cAAqB,CACtB,KAAK,WACV,KAAK,SAAW,GAChB,OAAO,oBAAoB,SAAU,KAAK,SAAU,CAAE,QAAS,EAAK,CAAC,EACrE,OAAO,oBAAoB,SAAU,KAAK,QAAQ,EAClD,OAAO,oBAAoB,UAAW,KAAK,SAAS,EACpD,OAAO,oBAAoB,cAAe,KAAK,cAAe,CAAE,QAAS,EAAK,CAAC,EAE/E,KAAK,UAAU,WAAW,EAC1B,KAAK,SAAW,KACZ,KAAK,QACP,qBAAqB,KAAK,KAAK,EAC/B,KAAK,MAAQ,GAEjB,CACF,ECtOO,IAAMK,GAAiB,IAQjBC,GAAsB,IA8C5B,SAASC,GACdC,EACAC,EACe,CACf,OAAW,CAACC,EAAKC,CAAK,IAAK,OAAO,QAAQF,CAAM,EAAG,CACjD,IAAMG,EAAQJ,EAAS,KAAMK,GAAMA,EAAE,MAAQH,CAAG,EAChD,GAAI,CAACE,EAAO,MAAO,IAAIF,CAAG,oDAC1B,GAAI,OAAOC,GAAU,SAAU,MAAO,kBAAkBD,CAAG,sBAC3D,GAAIE,EAAM,OAAS,QAAU,CAACA,EAAM,OAAO,SAASD,CAAK,EACvD,MAAO,IAAIA,CAAK,oBAAoBD,CAAG,cAEzC,GAAIE,EAAM,OAAS,QAAU,CAACE,GAAUH,CAAK,EAC3C,MAAO,IAAIA,CAAK,sCAElB,GAAIC,EAAM,OAAS,SAAWD,EAAM,OAASI,IAAkB,wBAAwB,KAAKJ,CAAK,GAC/F,MAAO,mBAAmBD,CAAG,+CAEjC,CACA,OAAO,IACT,CAGA,IAAMK,GAAiB,IAEvB,SAASD,GAAUH,EAAwB,CACzC,GAAI,CAAC,sBAAsB,KAAKA,CAAK,EAAG,MAAO,GAC/C,IAAMK,EAAS,IAAI,KAAK,GAAGL,CAAK,YAAY,EAC5C,MAAO,CAAC,OAAO,MAAMK,EAAO,QAAQ,CAAC,GAAKA,EAAO,YAAY,EAAE,MAAM,EAAG,EAAE,IAAML,CAClF,CAEA,SAASM,IAA6B,CACpC,OAAO,OAAO,OAAW,IAAc,KAAO,OAAO,SAAS,QAChE,CAEA,SAASC,IAA4B,CACnC,OAAO,OAAO,OAAW,IACrB,KACA,OAAO,SAAS,SAAW,OAAO,SAAS,MACjD,CAEA,eAAsBC,GACpBC,EACAC,EACwB,CACxB,OAAQD,EAAQ,KAAM,CACpB,IAAK,WAAY,CACf,GAAM,CAAE,MAAAE,CAAM,EAAIF,EAAQ,KAOpBG,EAAQD,EAAM,SAAS,GAAG,GAAKJ,GAAW,IAAMI,EACtD,OAAAD,EAAI,SAASC,CAAK,EACD,MAAMD,EAAI,kBAAkBC,EAAO,CAAE,MAAAC,CAAM,CAAC,GAAM,GAE/D,CAAE,GAAI,GAAM,OAAQ,CAAE,MAAAD,CAAM,CAAE,EAC9B,CACE,GAAI,GACJ,OAAQ,CACN,MAAAA,EACA,KAAM,mJACR,CACF,CACN,CAEA,IAAK,YAAa,CAChB,GAAM,CAAE,aAAAE,EAAc,QAAAC,CAAQ,EAAIL,EAAQ,KAC1C,OAAOC,EAAI,YAAY,UAAUG,EAAcC,GAAW,MAAS,EAC/D,CAAE,GAAI,EAAK,EACX,CAAE,GAAI,GAAO,MAAOC,GAAUF,CAAY,CAAE,CAClD,CAEA,IAAK,QAAS,CACZ,GAAM,CAAE,aAAAA,EAAc,QAAAC,CAAQ,EAAIL,EAAQ,KAC1C,OAAOC,EAAI,YAAY,MAAMG,EAAcC,GAAW,MAAS,EAC3D,CAAE,GAAI,EAAK,EACX,CAAE,GAAI,GAAO,MAAOC,GAAUF,CAAY,CAAE,CAClD,CAEA,IAAK,YAAa,CAChB,GAAM,CAAE,aAAAA,CAAa,EAAIJ,EAAQ,KACjC,OAAOC,EAAI,YAAY,SAASG,CAAY,EACxC,CAAE,GAAI,EAAK,EACX,CAAE,GAAI,GAAO,MAAOE,GAAUF,CAAY,CAAE,CAClD,CAEA,IAAK,aAAc,CACjB,GAAM,CAAE,SAAAG,CAAS,EAAIP,EAAQ,KAC7B,OAAKC,EAAI,UAcLA,EAAI,UAAUM,CAAQ,IAAM,GACvB,CACL,GAAI,GACJ,MAAO,iBAAiBA,CAAQ,6DAClC,EAEK,CAAE,GAAI,EAAK,EAnBT,CACL,GAAI,GACJ,MACE,0EACJ,CAgBJ,CAEA,IAAK,sBAAuB,CAC1B,GAAM,CAAE,WAAAC,EAAY,MAAAC,CAAM,EAAIT,EAAQ,KAChCU,EAAST,EAAI,MAAM,UAAUO,CAAU,EAC7C,GAAI,CAACE,EAGH,MAAO,CACL,GAAI,GACJ,MAAO,wCAAwCF,CAAU,IAC3D,EAOF,IAAMG,EAAU,KAAK,IAAI,EACnBC,EAAO,WAAW,IAAM,CAC5B,QAAQ,KACN,eAAeJ,CAAU,yBAAyBvB,GAAiB,GAAI,wBAChDC,GAAsB,GAAI,uNAGnD,CACF,EAAGD,EAAc,EACX4B,EAAO,IAAM,CACb,KAAK,IAAI,EAAIF,EAAUzB,IAEzB,QAAQ,KACN,eAAesB,CAAU,4BAA4B,KAAK,OAAO,KAAK,IAAI,EAAIG,GAAW,GAAI,CAAC,uCACxDzB,GAAsB,GAAI,2NAElE,CAEJ,EAEA,GAAI,CACF,IAAM4B,EAAS,MAAMJ,EAAO,QACzBD,GAAS,CAAC,CACb,EACA,OAAAI,EAAK,EACE,CAAE,GAAI,GAAM,OAAQC,GAAU,CAAE,KAAM,EAAK,CAAE,CACtD,OAASC,EAAO,CACd,OAAAF,EAAK,EAGE,CAAE,GAAI,GAAO,MAAOG,EAAcD,CAAK,CAAE,CAClD,QAAE,CACA,aAAaH,CAAI,CACnB,CACF,CAEA,IAAK,WAAY,CACf,GAAM,CAAE,WAAAK,EAAY,MAAA1B,CAAM,EAAIS,EAAQ,KAChCkB,EAAUjB,EAAI,MAAM,WAAWgB,CAAU,EAC/C,GAAI,CAACC,EACH,MAAO,CAAE,GAAI,GAAO,MAAO,IAAID,CAAU,iCAAkC,EAI7E,GAAI,CAACC,EAAQ,QAAQ,KAAMC,GAAWA,EAAO,QAAU5B,CAAK,EAC1D,MAAO,CAAE,GAAI,GAAO,MAAO,IAAIA,CAAK,oBAAoB0B,CAAU,cAAe,EAGnF,GAAI,CACF,MAAMC,EAAQ,SAAS3B,CAAK,CAC9B,OAASwB,EAAO,CACd,MAAO,CAAE,GAAI,GAAO,MAAOC,EAAcD,CAAK,CAAE,CAClD,CAGA,OADe,MAAMd,EAAI,gBAAgBgB,EAAY1B,CAAK,GAAM,GAE5D,CAAE,GAAI,GAAM,OAAQ,CAAE,QAASA,CAAM,CAAE,EACvC,CACE,GAAI,GACJ,MAAO,8BAA8BA,CAAK,WAAW0B,CAAU,8BACjE,CACN,CAEA,IAAK,aAAc,CACjB,GAAM,CAAE,SAAAG,EAAU,OAAAC,EAAQ,MAAAC,CAAM,EAAItB,EAAQ,KACtCuB,EAAQtB,EAAI,MAAM,SAASmB,CAAQ,EACzC,GAAI,CAACG,EACH,MAAO,CAAE,GAAI,GAAO,MAAO,IAAIH,CAAQ,iCAAkC,EAE3E,GAAI,CAEF,MAAO,CACL,GAAI,GACJ,OAAQ,CACN,KAJSG,EAAM,KAAKF,EAAQC,CAAK,EAKjC,OAAAD,EACA,OAAQE,EAAM,WACd,GAAIA,EAAM,QAAU,OAAY,CAAC,EAAI,CAAE,MAAOA,EAAM,KAAM,CAC5D,CACF,CACF,OAASR,EAAO,CACd,MAAO,CAAE,GAAI,GAAO,MAAOC,EAAcD,CAAK,CAAE,CAClD,CACF,CAEA,IAAK,cAAe,CAClB,GAAM,CAAE,SAAAK,EAAU,OAAA/B,EAAQ,MAAAiC,EAAO,KAAAE,CAAK,EAAIxB,EAAQ,KAC5CuB,EAAQtB,EAAI,MAAM,SAASmB,CAAQ,EACzC,GAAI,CAACG,EACH,MAAO,CAAE,GAAI,GAAO,MAAO,IAAIH,CAAQ,iCAAkC,EAE3E,GAAI,CAACG,EAAM,MACT,MAAO,CAAE,GAAI,GAAO,MAAO,IAAIH,CAAQ,uBAAwB,EAIjE,IAAMK,EAAUtC,GAAiBoC,EAAM,MAAM,OAAQlC,CAAM,EAC3D,GAAIoC,EAAS,MAAO,CAAE,GAAI,GAAO,MAAOA,CAAQ,EAEhD,IAAIC,EACJ,GAAI,CACFA,EAAQ,MAAMH,EAAM,MAAM,IAAIlC,CAAM,CACtC,OAAS0B,EAAO,CACd,MAAO,CAAE,GAAI,GAAO,MAAOC,EAAcD,CAAK,CAAE,CAClD,CAEA,IAAIY,EAAQ,GACZ,GAAIH,GAAQD,EAAM,MAAM,KACtB,GAAI,CACF,MAAMA,EAAM,MAAM,KAAKlC,CAAM,EAG7BsC,EAAS,MAAM1B,EAAI,kBAAkBJ,GAAY,GAAK,GAAI,CAAE,MAAO,EAAK,CAAC,GAAM,EACjF,OAASkB,EAAO,CACd,MAAO,CAAE,GAAI,GAAO,MAAOC,EAAcD,CAAK,CAAE,CAClD,CAGF,MAAO,CACL,GAAI,GACJ,OAAQ,CACN,KAAMW,EAAM,KAAK,MAAM,EAAGJ,CAAK,EAC/B,GAAII,EAAM,QAAU,OAAY,CAAC,EAAI,CAAE,MAAOA,EAAM,KAAM,EAC1D,MAAAC,CACF,CACF,CACF,CAEA,IAAK,aAAc,CACjB,GAAM,CAAE,SAAAC,EAAU,OAAAC,EAAQ,MAAAP,CAAM,EAAItB,EAAQ,KACtC8B,EAAQ7B,EAAI,MAAM,SAAS2B,CAAQ,EACzC,GAAI,CAACE,EACH,MAAO,CAAE,GAAI,GAAO,MAAO,IAAIF,CAAQ,iCAAkC,EAE3E,GAAI,CACF,MAAO,CAAE,GAAI,GAAM,OAAQ,CAAE,OAAQE,EAAM,KAAKD,GAAU,OAAWP,CAAK,CAAE,CAAE,CAChF,OAASP,EAAO,CACd,MAAO,CAAE,GAAI,GAAO,MAAOC,EAAcD,CAAK,CAAE,CAClD,CACF,CAEA,IAAK,kBACH,OAAAd,EAAI,YAAY,MAAM,EACf,CAAE,GAAI,EAAK,EAGpB,QAIE,MAAO,CACL,GAAI,GACJ,MAAO,2CAHOD,EAG4C,IAAI,kCAChE,CAEJ,CACF,CAEA,SAASM,GAAUF,EAA8B,CAE/C,MAD4B,EAAQ2B,EAAc3B,CAAY,EAE1D,IAAIA,CAAY,oCAChB,IAAIA,CAAY,iCACtB,CAEA,SAASY,EAAcD,EAAwB,CAC7C,OAAIA,aAAiB,MAAcA,EAAM,QACrC,OAAOA,GAAU,SAAiBA,EAC/B,oBACT,CC/VO,SAASiB,GAAoBC,EAA+B,CAEjE,IAAMC,EAAQ,CAAC,yBADDD,EAAO,SAAW,KAAKA,EAAO,QAAQ,KAAO,sBACd,kBAAkB,EAC/DC,EAAM,KAAK,YAAYC,GAASF,EAAO,IAAI,CAAC,EAAE,EAC1CA,EAAO,OAAOC,EAAM,KAAK,YAAYC,GAASF,EAAO,KAAK,CAAC,EAAE,EAEjE,IAAMG,GAAWH,EAAO,OAASA,EAAO,MAAM,MAAM,MAAM,EAAG,CAAO,EACpE,OAAIG,EAAQ,OAAS,GACnBF,EAAM,KACJ,oBAAoBE,EAAQ,IAAKC,GAAS,GAAGA,EAAK,IAAI,IAAIA,EAAK,MAAM,EAAE,EAAE,KAAK,IAAI,CAAC,EACrF,EAEKH,EAAM,KAAK;AAAA,CAAI,CACxB,CAEA,SAASC,GAASG,EAA2B,CAE3C,MACE,IAFYA,EAAK,kBAAoBA,EAAK,WAAaA,EAAK,cAEnD,eAAe,IAAI,CAAC,+BAA0BA,EAAK,iBAAiB,KAC1EA,EAAK,MAAM,MAAM,UAAUA,EAAK,UAAU,iBAAiBA,EAAK,YAAY,EAEnF,CChCO,SAASC,GAAUC,EAAsBC,EAA6C,CAC3F,GAAM,CAACC,CAAK,EAAIF,EAChB,GAAI,CAACE,EAAO,OACZ,IAAMC,EAAOH,EAAOA,EAAO,OAAS,CAAC,EAEjCI,EAAMF,EACNG,EAAMH,EACNI,EAAQ,EACRC,EACAC,EAEJ,OAAAR,EAAO,QAAQ,CAACS,EAAOC,IAAM,CAI3B,GAHAJ,GAASG,EAAM,EACXA,EAAM,EAAIL,EAAI,IAAGA,EAAMK,GACvBA,EAAM,EAAIJ,EAAI,IAAGA,EAAMI,GACvB,CAACR,GAAWS,IAAM,EAAG,OACzB,IAAMC,EAAWX,EAAOU,EAAI,CAAC,EACvBE,EAASH,EAAM,EAAIE,EAAS,EAC9BC,EAAS,IAAM,CAACL,GAAQK,EAASL,EAAK,UACxCA,EAAO,CAAE,KAAMI,EAAS,EAAG,GAAIF,EAAM,EAAG,OAAAG,CAAO,GAE7CA,EAAS,IAAM,CAACJ,GAAQI,EAASJ,EAAK,UACxCA,EAAO,CAAE,KAAMG,EAAS,EAAG,GAAIF,EAAM,EAAG,OAAAG,CAAO,EAEnD,CAAC,EAEM,CACL,OAAQZ,EAAO,OACf,MAAAE,EACA,KAAAC,EACA,IAAAC,EACA,IAAAC,EACA,MAAO,KAAK,MAAMC,EAAQ,GAAG,EAAI,IACjC,GAAIC,EAAO,CAAE,YAAaA,CAAK,EAAI,CAAC,EACpC,GAAIC,EAAO,CAAE,YAAaA,CAAK,EAAI,CAAC,CACtC,CACF,CAMO,SAASK,GAAQb,EAAac,EAAoB,CACvD,GAAId,EAAO,QAAUc,EAAO,OAAOd,EACnC,GAAIc,GAAS,EAAG,OAAOd,EAAO,MAAM,EAAG,CAAC,EACxC,IAAMe,GAAQf,EAAO,OAAS,IAAMc,EAAQ,GAC5C,OAAO,MAAM,KAAK,CAAE,OAAQA,CAAM,EAAG,CAACE,EAAGN,IAAMV,EAAO,KAAK,MAAMU,EAAIK,CAAI,CAAC,CAAE,CAC9E,CC4EA,IAAME,GAAiB,IAEhB,SAASC,GAAOC,EAAkD,CACvE,GAAIA,GAAU,KAA6B,OAAO,KAClD,GAAI,OAAOA,GAAU,UAAY,OAAOA,GAAU,UAAW,OAAOA,EACpE,IAAMC,EACJD,aAAiB,KACbA,EAAM,YAAY,EAClB,OAAOA,GAAU,SACfA,EACA,KAAK,UAAUA,CAAK,EAC5B,OAAOC,EAAK,OAASH,GAAiB,GAAGG,EAAK,MAAM,EAAGH,EAAc,CAAC,SAAMG,CAC9E,CAGO,SAASC,GAAgBC,EAAqD,CACnF,IAAMC,EAAOD,EAAO,EACpB,MAAO,CACL,KAAM,OACN,MAAOC,EAAK,MACZ,QAASA,EAAK,KAAK,IAAI,CAAC,CAAE,MAAAJ,EAAO,MAAAK,CAAM,KAAO,CAAE,MAAAL,EAAO,MAAAK,CAAM,EAAE,EAC/D,MAAOD,EAAK,MACZ,SAAWJ,GAAUG,EAAO,EAAE,SAASH,CAAK,CAC9C,CACF,CAMO,SAASM,GAAcF,EAA6B,CACzD,OAAO,KAAK,UAAU,CACpB,MAAOA,EAAK,MACZ,KAAMA,EAAK,KAAK,IAAI,CAAC,CAAE,MAAAJ,EAAO,MAAAK,CAAM,KAAO,CAAE,MAAAL,EAAO,MAAAK,CAAM,EAAE,EAC5D,MAAOD,EAAK,KACd,CAAC,CACH,CAGA,SAASG,GAAYC,EAA8D,CACjF,OAAO,OAAO,QAAQA,EAAM,MAAM,EAAE,IAAI,CAAC,CAACC,EAAKC,CAAK,KAAO,CACzD,IAAAD,EACA,KAAMC,EAAM,KACZ,OAAQA,EAAM,OAAS,OAAS,CAAC,GAAGA,EAAM,MAAM,EAAI,CAAC,EACrD,YAAaA,EAAM,aAAe,EACpC,EAAE,CACJ,CAOO,SAASC,GAAoBC,EAAoC,CACtE,OAAO,KAAK,UAAU,CACpB,MAAOA,EAAM,MACb,QAASA,EAAM,QAAQ,IAAI,CAAC,CAAE,IAAAH,EAAK,MAAAJ,CAAM,KAAO,CAAE,IAAAI,EAAK,MAAAJ,CAAM,EAAE,EAC/D,UAAWO,EAAM,UACjB,MAAOA,EAAM,OAAO,SACpB,MAAOA,EAAM,MACb,OAAQA,EAAM,KAAK,OACnB,MAAOA,EAAM,MACT,CACE,OAAQL,GAAYK,EAAM,KAAqC,EAC/D,QAASA,EAAM,MAAM,OAAS,MAChC,EACA,MACN,CAAC,CACH,CAGO,SAASC,GAAsBV,EAAqD,CACzF,IAAMS,EAAQT,EAAO,EAIfW,EAAUC,GAAa,CAC3B,GAAM,CAAE,MAAAC,EAAO,QAAAC,EAAS,MAAAC,CAAM,EAAIf,EAAO,EACnCgB,EAAwD,CAAC,EAC/D,QAAWC,KAAUH,EAAS,CAC5B,IAAMI,EAAMD,EAAO,MAAQA,EAAO,MAAML,CAAG,EAAKA,EAAgCK,EAAO,GAAG,EAC1FD,EAAIC,EAAO,KAAK,EAAIrB,GAAOsB,CAAG,CAChC,CACA,MAAO,CACL,GAAIL,EAAMD,CAAG,EACb,GAAIG,EAAQ,CAAE,KAAMA,EAAM,OAAOH,CAAG,CAAE,EAAI,CAAC,EAC3C,GAAGI,CACL,CACF,EAEA,MAAO,CACL,MAAOP,EAAM,MACb,QAASA,EAAM,QAAQ,IAAI,CAAC,CAAE,IAAAH,EAAK,MAAAJ,CAAM,KAAO,CAAE,IAAAI,EAAK,MAAAJ,CAAM,EAAE,EAC/D,UAAWO,EAAM,UACjB,MAAOA,EAAM,OAAO,SACpB,MAAOA,EAAM,MACb,WAAYA,EAAM,KAAK,OACvB,KAAM,CAACU,EAAQC,IAAUpB,EAAO,EAAE,KAAK,MAAMmB,EAAQA,EAASC,CAAK,EAAE,IAAIT,CAAM,EAC/E,GAAIF,EAAM,MACN,CACE,MAAO,CACL,OAAQL,GAAYK,EAAM,KAAqC,EAC/D,IAAK,MAAOY,GAAmC,CAC7C,IAAMhB,EAAQL,EAAO,EAAE,MACvB,GAAI,CAACK,EAAO,MAAM,IAAI,MAAM,uCAAuC,EACnE,IAAMiB,EAAQ,MAAMjB,EAAM,IAAIgB,CAAM,EACpC,MAAO,CACL,KAAMC,EAAM,KAAK,IAAIX,CAAM,EAC3B,GAAIW,EAAM,QAAU,OAAY,CAAC,EAAI,CAAE,MAAOA,EAAM,KAAM,CAC5D,CACF,EACA,GAAIb,EAAM,MAAM,KACZ,CAAE,KAAOY,GAAmCrB,EAAO,EAAE,OAAO,OAAOqB,CAAM,CAAE,EAC3E,CAAC,CACP,CACF,EACA,CAAC,CACP,CACF,CAGA,SAASE,GAAmBC,EAA8BlB,EAA2B,CACnF,IAAMmB,EAASD,EAAM,EAAE,OACvB,OAAOA,EAAM,KAAK,QAASE,GAAU,CACnC,IAAMR,EAAOQ,EAAkCpB,CAAG,EAC5CqB,EAAI,OAAOT,GAAQ,SAAWA,EAAMA,GAAO,KAAO,IAAM,OAAOA,CAAG,EACxE,GAAI,CAAC,OAAO,SAASS,CAAC,EAAG,MAAO,CAAC,EACjC,IAAMC,EAAQF,EAAkCF,EAAM,EAAE,GAAG,EAC3D,MAAO,CAAC,CAAE,EAAGC,EAASA,EAAOG,CAAI,EAAI,OAAOA,CAAI,EAAG,EAAAD,CAAE,CAAC,CACxD,CAAC,CACH,CAMO,SAASE,EAAwB7B,EAAuD,CAC7F,IAAMwB,EAAQxB,EAAO,EACf8B,EAAUN,EAAM,EAAE,OAAS,WACjC,MAAO,CACL,MAAOA,EAAM,MACb,OAAQA,EAAM,EAAE,MAChB,MAAOM,EAAU,UAAY,WAC7B,OAAQN,EAAM,OAAO,IAAKO,GAAM,CAC9B,IAAMC,EAAUC,GAAUV,GAAYC,EAAOO,EAAE,GAAG,EAAGD,CAAO,EAC5D,MAAO,CACL,KAAMC,EAAE,KACR,GAAIA,EAAE,KAAO,CAAE,KAAMA,EAAE,IAAK,EAAI,CAAC,EACjC,GAAIC,EAAU,CAAE,QAAAA,CAAQ,EAAI,CAAC,CAC/B,CACF,CAAC,EACD,KAAM,CAACE,EAAMd,IAAU,CACrB,IAAMe,EAAUnC,EAAO,EACvB,OAAOmC,EAAQ,OACZ,OAAQJ,GAAMG,IAAS,QAAaH,EAAE,OAASG,CAAI,EACnD,IAAKH,IAAO,CAAE,KAAMA,EAAE,KAAM,OAAQK,GAAKb,GAAYY,EAASJ,EAAE,GAAG,EAAGX,CAAK,CAAE,EAAE,CACpF,CACF,CACF,CAGO,SAASiB,GAAsBb,EAAsC,CAC1E,GAAM,CAAE,KAAMc,EAAO,GAAGC,CAAS,EAAIV,EAAiB,IAAML,CAAK,EACjE,OAAO,KAAK,UAAUe,CAAQ,CAChC,CClKO,IAAMC,EAAN,KAAqB,CAArB,cACLC,EAAA,KAAQ,SAAmC,MAE3CA,EAAA,KAAQ,aAAa,IAAI,KACzBA,EAAA,KAAQ,WAAW,IAAI,KACvBA,EAAA,KAAQ,SAAS,IAAI,KACrBA,EAAA,KAAQ,SAAS,IAAI,KAErBA,EAAA,KAAQ,SAAS,IAAI,KACrBA,EAAA,KAAQ,UAAU,IAAI,KACtBA,EAAA,KAAQ,YAAY,IAAI,KAGxBA,EAAA,KAAQ,WAAiC,MACzCA,EAAA,KAAQ,gBAAgB,IAExBA,EAAA,iBAAaC,IACX,KAAK,UAAU,IAAIA,CAAQ,EACpB,IAAM,CACX,KAAK,UAAU,OAAOA,CAAQ,CAChC,IASFD,EAAA,wBAAmB,IACZ,KAAK,eAEV,KAAK,SAAW,KAAK,OACjB,CACE,SAAU,KAAK,OAAO,SACtB,MAAO,KAAK,OAAO,MACnB,MAAO,KAAK,OAAO,MACnB,YAAa,KAAK,OAAO,aAAe,GACxC,SAAU,KAAK,OAAO,UAAY,CAAC,EACnC,YAAc,KAAK,OAAO,QACtB,CAAC,EACD,CACE,GAAI,KAAK,OAAO,aAAe,CAAC,EAChC,GAAG,OAAO,YAAY,KAAK,MAAM,CACnC,EACJ,WAAY,CAAC,GAAG,KAAK,WAAW,QAAQ,CAAC,EAAE,IAAI,CAAC,CAACE,EAAcC,CAAI,KAAO,CACxE,aAAAD,EACA,KAAMC,EAAK,KACX,MAAOA,EAAK,MACZ,YAAaA,EAAK,aAAe,GACjC,QAASA,EAAK,SAAW,CAAC,CAC5B,EAAE,EACF,SAAU,CAAC,GAAG,KAAK,SAAS,QAAQ,CAAC,EAAE,IAAI,CAAC,CAACC,EAAYC,CAAO,KAAO,CACrE,WAAAD,EACA,KAAMC,EAAQ,KACd,MAAOA,EAAQ,MACf,QAASA,EAAQ,QAAQ,IAAI,CAAC,CAAE,MAAAC,EAAO,MAAAC,CAAM,KAAO,CAAE,MAAAD,EAAO,MAAAC,CAAM,EAAE,EACrE,GAAIF,EAAQ,QAAU,OAAY,CAAC,EAAI,CAAE,MAAOA,EAAQ,KAAM,CAChE,EAAE,EACF,OAAQ,CAAC,GAAG,KAAK,OAAO,QAAQ,CAAC,EAAE,IAAI,CAAC,CAACG,EAAUC,CAAK,KAAO,CAC7D,SAAAD,EACA,MAAOC,EAAM,MACb,QAASA,EAAM,QAAQ,IAAI,CAAC,CAAE,IAAAC,EAAK,MAAAH,CAAM,KAAO,CAAE,IAAAG,EAAK,MAAAH,CAAM,EAAE,EAC/D,WAAYE,EAAM,WAClB,GAAIA,EAAM,UAAY,CAAE,MAAOA,EAAM,SAAU,EAAI,CAAC,EACpD,GAAIA,EAAM,MAAQ,CAAE,MAAOA,EAAM,KAAM,EAAI,CAAC,EAC5C,GAAIA,EAAM,QAAU,OAAY,CAAC,EAAI,CAAE,UAAWA,EAAM,KAAM,EAC9D,GAAIA,EAAM,MACN,CACE,MAAO,CACL,OAAQA,EAAM,MAAM,OAAO,IAAI,CAAC,CAAE,IAAAC,EAAK,KAAAC,EAAM,OAAAC,EAAQ,YAAAC,CAAY,KAAO,CACtE,IAAAH,EACA,KAAAC,EACA,OAAAC,EACA,YAAAC,CACF,EAAE,EACF,QAASJ,EAAM,MAAM,OAAS,MAChC,CACF,EACA,CAAC,CACP,EAAE,EACF,OAAQ,CAAC,GAAG,KAAK,OAAO,QAAQ,CAAC,EAAE,IAAI,CAAC,CAACK,EAAUC,CAAK,KAAO,CAC7D,SAAAD,EACA,MAAOC,EAAM,MACb,OAAQA,EAAM,OACd,MAAOA,EAAM,MACb,OAAQA,EAAM,MAChB,EAAE,CACJ,EACA,KAEJ,KAAK,cAAgB,GACd,KAAK,UA9DoB,KAAK,UAiEvC,UAAUC,EAAiC,CACzC,IAAMC,EAAW,KAAK,OACtB,KAAK,OAASD,EAKVC,GAAYA,EAAS,WAAaD,EAAO,WAC3C,KAAK,WAAW,MAAM,EACtB,KAAK,SAAS,MAAM,EACpB,KAAK,OAAO,MAAM,EAClB,KAAK,OAAO,MAAM,EAClB,KAAK,OAAO,MAAM,GAEpB,KAAK,WAAW,CAClB,CAEA,YAAYE,EAAwB,CAG9B,KAAK,QAAQ,WAAaA,IAC9B,KAAK,OAAS,KACd,KAAK,WAAW,MAAM,EACtB,KAAK,SAAS,MAAM,EACpB,KAAK,OAAO,MAAM,EAClB,KAAK,OAAO,MAAM,EAClB,KAAK,OAAO,MAAM,EAClB,KAAK,WAAW,EAClB,CAEA,kBAAkBhB,EAAsBC,EAAkC,CACxE,KAAK,WAAW,IAAID,EAAcC,CAAI,EACtC,KAAK,WAAW,CAClB,CAEA,oBAAoBD,EAA4B,CAC1C,KAAK,WAAW,OAAOA,CAAY,GAAG,KAAK,WAAW,CAC5D,CAEA,gBAAgBE,EAAoBC,EAAuC,CACzE,KAAK,SAAS,IAAID,EAAYC,CAAO,EACrC,KAAK,WAAW,CAClB,CAEA,kBAAkBD,EAA0B,CACtC,KAAK,SAAS,OAAOA,CAAU,GAAG,KAAK,WAAW,CACxD,CAEA,WAAWA,EAAwD,CACjE,OAAO,KAAK,SAAS,IAAIA,CAAU,CACrC,CAEA,cAAcI,EAAkBC,EAA+B,CAC7D,KAAK,OAAO,IAAID,EAAUC,CAAK,EAC/B,KAAK,WAAW,CAClB,CAEA,gBAAgBD,EAAwB,CAClC,KAAK,OAAO,OAAOA,CAAQ,GAAG,KAAK,WAAW,CACpD,CAEA,SAASA,EAAgD,CACvD,OAAO,KAAK,OAAO,IAAIA,CAAQ,CACjC,CAEA,UAAUE,EAAaE,EAAuC,CAC5D,KAAK,OAAO,IAAIF,EAAKE,CAAM,EAC3B,KAAK,WAAW,CAClB,CAEA,YAAYF,EAAmB,CACzB,KAAK,OAAO,OAAOA,CAAG,GAAG,KAAK,WAAW,CAC/C,CAEA,cAAcI,EAAkBC,EAA+B,CAC7D,KAAK,OAAO,IAAID,EAAUC,CAAK,EAC/B,KAAK,WAAW,CAClB,CAEA,gBAAgBD,EAAwB,CAClC,KAAK,OAAO,OAAOA,CAAQ,GAAG,KAAK,WAAW,CACpD,CAEA,SAASA,EAAgD,CACvD,OAAO,KAAK,OAAO,IAAIA,CAAQ,CACjC,CAEA,eAAeK,EAA6B,CACtC,KAAK,QAAQ,IAAIA,EAAO,IAAI,GAC9B,QAAQ,KACN,mDAAmDA,EAAO,IAAI,8DAEhE,EAEF,KAAK,QAAQ,IAAIA,EAAO,KAAMA,CAAM,CAEtC,CAEA,iBAAiBC,EAAoB,CACnC,KAAK,QAAQ,OAAOA,CAAI,CAC1B,CAEA,UAAUA,EAAyC,CACjD,OAAO,KAAK,QAAQ,IAAIA,CAAI,CAC9B,CAGA,iBAA2B,CACzB,OAAO,KAAK,QAAQ,UAAY,EAClC,CAEA,aAAalB,EAA+B,CAC1C,OAAO,KAAK,WAAW,IAAIA,CAAY,CACzC,CAEQ,YAAmB,CACzB,KAAK,cAAgB,GACrB,QAAWD,KAAY,KAAK,UAAWA,EAAS,CAClD,CACF,ECjUA,IAAMoB,GAAW,GAUXC,GAAmB,IAGnBC,GAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8CjB,SAASC,GAAQC,EAAoC,CAC1D,IAAMC,EAAS,IAAI,YAAYD,EAAQ,OAAS,CAAC,EAC3CE,EAAO,IAAI,SAASD,CAAM,EAChC,QAAS,EAAI,EAAG,EAAID,EAAQ,OAAQ,GAAK,EAAG,CAG1C,IAAMG,EAAU,KAAK,IAAI,GAAI,KAAK,IAAI,EAAGH,EAAQ,CAAC,GAAK,CAAC,CAAC,EACzDE,EAAK,SAAS,EAAI,EAAGC,EAAU,MAAQ,EAAI,CAC7C,CACA,OAAOF,CACT,CAGO,SAASG,GAAUH,EAAmC,CAC3D,IAAMC,EAAO,IAAI,SAASD,CAAM,EAC1BD,EAAU,IAAI,aAAaC,EAAO,WAAa,CAAC,EACtD,QAAS,EAAI,EAAG,EAAID,EAAQ,OAAQ,GAAK,EACvCA,EAAQ,CAAC,EAAIE,EAAK,SAAS,EAAI,EAAG,EAAI,EAAI,MAE5C,OAAOF,CACT,CAUO,SAASK,EAASC,EAA0B,CACjD,IAAMN,EAAUI,GAAUE,CAAG,EAC7B,GAAIN,EAAQ,SAAW,EAAG,MAAO,GACjC,IAAIO,EAAM,EACV,QAAWC,KAAUR,EAASO,GAAOC,EAASA,EAC9C,IAAMC,EAAM,KAAK,KAAKF,EAAMP,EAAQ,MAAM,EAC1C,OAAO,KAAK,IAAI,EAAG,KAAK,KAAKS,CAAG,EAAI,GAAG,CACzC,CAWO,SAASC,GACdV,EACAW,EACAC,EACc,CACd,GAAID,IAAaC,EAAQ,OAAOZ,EAEhC,IAAMa,EAAQF,EAAWC,EACnBE,EAAS,KAAK,MAAMd,EAAQ,OAASa,CAAK,EAC1CE,EAAM,IAAI,aAAaD,CAAM,EAEnC,QAASE,EAAI,EAAGA,EAAIF,EAAQE,GAAK,EAAG,CAClC,IAAMC,EAAWD,EAAIH,EACfK,EAAQ,KAAK,MAAMD,CAAQ,EAC3BE,EAAO,KAAK,IAAID,EAAQ,EAAGlB,EAAQ,OAAS,CAAC,EAC7CoB,EAAWH,EAAWC,EAC5BH,EAAIC,CAAC,GAAKhB,EAAQkB,CAAK,GAAK,IAAM,EAAIE,IAAapB,EAAQmB,CAAI,GAAK,GAAKC,CAC3E,CACA,OAAOL,CACT,CAWO,IAAMM,EAAN,KAAwB,CAO7B,YAA6BC,EAAqC,CAArCC,EAAA,eAAAD,GAN7BC,EAAA,KAAQ,UAA+B,MACvCA,EAAA,KAAQ,SAA6B,MACrCA,EAAA,KAAQ,OAAsD,MAC9DA,EAAA,KAAQ,SAA4C,MACpDA,EAAA,KAAQ,OAAO,GAEoD,CAGnE,IAAI,WAAqB,CACvB,OAAO,KAAK,IACd,CAMA,MAAM,OAAuB,CAC3B,GAAI,KAAK,QAAS,CAChB,MAAM,KAAK,QAAQ,OAAO,EAC1B,MACF,CAEA,KAAK,OAAS,MAAM,UAAU,aAAa,aAAa,CACtD,MAAO,CACL,aAAc,EAId,iBAAkB,GAClB,iBAAkB,GAClB,gBAAiB,EACnB,CACF,CAAC,EAED,IAAMC,EAAU,IAAI,aAAa,CAAE,WAAY,IAAkB,CAAC,EAClE,KAAK,QAAUA,EACf,KAAK,OAASA,EAAQ,wBAAwB,KAAK,MAAM,EAEzD,IAAMC,EAAWzB,GAA0B,CACpC,KAAK,MACV,KAAK,QACHD,GAAQW,GAASV,EAASwB,EAAQ,WAAY,IAAiB,CAAC,CAClE,CACF,EAEA,GAAI,CACF,IAAME,EAAM,IAAI,gBACd,IAAI,KAAK,CAAC5B,EAAe,EAAG,CAAE,KAAM,wBAAyB,CAAC,CAChE,EACA,MAAM0B,EAAQ,aAAa,UAAUE,CAAG,EACxC,IAAI,gBAAgBA,CAAG,EAEvB,IAAMC,EAAU,IAAI,iBAAiBH,EAAS,eAAgB,CAC5D,iBAAkB,CAGhB,aAAc,KAAK,MAAOA,EAAQ,WAAa5B,GAAY,GAAI,CACjE,CACF,CAAC,EACD+B,EAAQ,KAAK,UAAaC,GACxBH,EAAQG,EAAM,IAAoB,EACpC,KAAK,OAAO,QAAQD,CAAO,EAI3B,IAAME,EAAOL,EAAQ,WAAW,EAChCK,EAAK,KAAK,MAAQ,EAClBF,EAAQ,QAAQE,CAAI,EAAE,QAAQL,EAAQ,WAAW,EACjD,KAAK,KAAOG,CACd,MAAQ,CAIN,IAAMG,EAAS,KAAK,IAClB,IACA,GAAK,KAAK,MAAM,KAAK,KAAMN,EAAQ,WAAa5B,GAAY,GAAI,CAAC,CACnE,EACMmC,EAAYP,EAAQ,sBAAsBM,EAAQ,EAAG,CAAC,EAC5DC,EAAU,eAAkBH,GAC1BH,EAAQ,IAAI,aAAaG,EAAM,YAAY,eAAe,CAAC,CAAC,CAAC,EAC/D,KAAK,OAAO,QAAQG,CAAS,EAC7BA,EAAU,QAAQP,EAAQ,WAAW,EACrC,KAAK,KAAOO,CACd,CAEA,MAAMP,EAAQ,OAAO,CACvB,CAGA,QAAe,CACb,KAAK,KAAO,EACd,CAGA,MAAa,CACX,KAAK,KAAO,EACd,CAGA,MAAM,MAAsB,CAC1B,KAAK,KAAO,GACZ,KAAK,MAAM,WAAW,EACtB,KAAK,QAAQ,WAAW,EACxB,KAAK,QAAQ,UAAU,EAAE,QAASQ,GAAUA,EAAM,KAAK,CAAC,EACxD,MAAM,KAAK,SAAS,MAAM,EAAE,MAAM,IAAG,EAAY,EACjD,KAAK,KAAO,KACZ,KAAK,OAAS,KACd,KAAK,OAAS,KACd,KAAK,QAAU,IACjB,CACF,EAWaC,EAAN,KAAmB,CAMxB,YAA6BC,EAAmC,CAAnCX,EAAA,eAAAW,GAE7BX,EAAA,KAAQ,cAAc,IAAI,KAC1BA,EAAA,KAAQ,UAA+B,MACvCA,EAAA,KAAQ,OAAwB,MAChCA,EAAA,KAAQ,UAAU,IAAI,KAEtBA,EAAA,KAAQ,YAAY,GAEpBA,EAAA,KAAQ,YAAY,EAT6C,CAYjE,MAAM,OAAuB,CAC3B,GAAI,KAAK,QAAS,CAChB,MAAM,KAAK,QAAQ,OAAO,EAC1B,MACF,CACA,IAAMC,EAAU,IAAI,aAAa,CAAE,WAAY,IAAkB,CAAC,EAClE,KAAK,KAAOA,EAAQ,WAAW,EAC/B,KAAK,KAAK,QAAQA,EAAQ,WAAW,EACrC,KAAK,QAAUA,EACf,MAAMA,EAAQ,OAAO,CACvB,CAGA,QAAQlB,EAAwB,CAC9B,IAAMkB,EAAU,KAAK,QACrB,GAAI,CAACA,GAAW,CAAC,KAAK,KAAM,OAE5B,IAAMxB,EAAUI,GAAUE,CAAG,EAC7B,GAAIN,EAAQ,SAAW,EAAG,OAK1B,IAAMC,EAASuB,EAAQ,aAAa,EAAGxB,EAAQ,OAAQ,IAAiB,EACxEC,EAAO,eAAe,CAAC,EAAE,IAAID,CAAO,EAEpC,IAAMmC,EAASX,EAAQ,mBAAmB,EAC1CW,EAAO,OAASlC,EAChBkC,EAAO,QAAQ,KAAK,IAAI,EAExB,IAAMC,EAAMZ,EAAQ,YAYpB,GAXI,KAAK,UAAYY,IAKnB,KAAK,UAAYA,EAAMvC,GAAmB,IAC1C,KAAK,UAAY,KAAK,WAGxBsC,EAAO,MAAM,KAAK,SAAS,EAEvB,KAAK,QAAS,CAChB,IAAME,EAAQhC,EAASC,CAAG,EACpBgC,EAAS,KAAK,QACdC,EAAQ,WACZ,IAAM,CACJ,KAAK,YAAY,OAAOA,CAAK,EAC7BD,EAAOD,CAAK,CACd,EACA,KAAK,IAAI,GAAI,KAAK,UAAYD,GAAO,GAAI,CAC3C,EACA,KAAK,YAAY,IAAIG,CAAK,CAC5B,CAEA,KAAK,WAAatC,EAAO,SAEzB,KAAK,QAAQ,IAAIkC,CAAM,EACvBA,EAAO,QAAU,IAAM,KAAK,QAAQ,OAAOA,CAAM,CACnD,CAYA,SAAkB,CAChB,GAAI,CAAC,KAAK,SAAW,KAAK,YAAc,EAAG,MAAO,GAClD,IAAMK,EAAQ,KAAK,QAAQ,YAAc,KAAK,UAC9C,OAAO,KAAK,IAAI,EAAG,KAAK,MAAMA,EAAQ,GAAI,CAAC,CAC7C,CAGA,IAAI,UAAoB,CACtB,OAAO,KAAK,QAAQ,KAAO,CAC7B,CAGA,OAAc,CACZ,QAAWL,KAAU,KAAK,QACxB,GAAI,CACFA,EAAO,KAAK,CACd,MAAQ,CAER,CAEF,KAAK,QAAQ,MAAM,EACnB,KAAK,UAAY,EACjB,KAAK,UAAY,EAIjB,QAAWI,KAAS,KAAK,YAAa,aAAaA,CAAK,EACxD,KAAK,YAAY,MAAM,EACvB,KAAK,UAAU,CAAC,CAClB,CAEA,MAAM,MAAsB,CAC1B,KAAK,MAAM,EACX,KAAK,MAAM,WAAW,EACtB,MAAM,KAAK,SAAS,MAAM,EAAE,MAAM,IAAG,EAAY,EACjD,KAAK,KAAO,KACZ,KAAK,QAAU,IACjB,CACF,EC5TO,IAAME,EAAN,KAAmB,CAqCxB,YAA6BC,EAA8B,CAA9BC,EAAA,eAAAD,GApC7BC,EAAA,KAAQ,SAAiC,MACzCA,EAAA,KAAQ,QAAoB,QAC5BA,EAAA,KAAQ,OACRA,EAAA,KAAQ,WAURA,EAAA,KAAQ,OAA6B,MACrCA,EAAA,KAAQ,UAAU,IASlBA,EAAA,KAAQ,cAAc,GAEtBA,EAAA,KAAQ,gBAAgB,IAExBA,EAAA,KAAQ,gBAAuD,MAO/DA,EAAA,KAAQ,iBAAiB,KAGvB,KAAK,QAAU,IAAIC,EAAcC,GAAUH,EAAQ,UAAU,YAAaG,CAAK,CAAC,EAChF,KAAK,IAAM,IAAIC,EAAmBC,GAAQ,KAAK,UAAUA,CAAG,CAAC,CAC/D,CAEA,IAAI,cAA2B,CAC7B,OAAO,KAAK,KACd,CAUA,MAAM,OAAuB,CAC3B,GAAI,KAAK,OAAQ,OACjB,KAAK,QAAU,GACf,KAAK,SAAS,YAAY,EAE1B,GAAI,CAIF,MAAM,KAAK,IAAI,MAAM,EACrB,MAAM,KAAK,QAAQ,MAAM,CAC3B,OAASC,EAAO,CACd,KAAK,KAAK,CACR,KAAM,QACN,KAAM,yBACN,QACEA,aAAiB,OAASA,EAAM,OAAS,kBACrC,yDACA,8BAEN,YAAa,GACb,eAAgB,EAClB,CAAC,EACD,KAAK,SAAS,OAAO,EACrB,MACF,CAMA,IAAMC,GAHJ,KAAK,QAAQ,gBACXC,GAAgB,IAAI,UAAUA,CAAG,IAEd,KAAK,QAAQ,GAAG,EACvCD,EAAO,WAAa,cACpB,KAAK,OAASA,EAEdA,EAAO,UAAaE,GAAU,CACxB,OAAOA,EAAM,MAAS,SACxB,KAAK,cAAcA,EAAM,IAAI,EACpBA,EAAM,gBAAgB,cAC/B,KAAK,QAAQ,QAAQA,EAAM,IAAI,EAK3B,KAAK,QAAU,YAAY,KAAK,SAAS,UAAU,EAE3D,EAEAF,EAAO,QAAU,IAAM,CAKhB,KAAK,KAAK,CACjB,EAEAA,EAAO,QAAU,IAAM,CAEvB,CACF,CASA,eAAeP,EAAoC,CACjD,GAAI,MAAK,QAAQ,WACb,OAAK,QAAU,aAAe,KAAK,QAAU,YAIjD,IAFA,KAAK,YAAc,EAEfA,GAAS,KAAM,CACjB,KAAK,KAAO,CAAC,EACb,KAAK,IAAI,OAAO,EAChB,KAAK,SAAS,WAAW,EACzB,MACF,CAEA,KAAK,kBAAkB,EACvB,KAAK,IAAI,OAAO,EAChB,KAAK,SAAS,WAAW,EAC3B,CAGA,kBAAyB,CACvB,IAAMU,EAAO,KAAK,KAClB,GAAKA,EACL,MAAK,KAAO,KACZ,KAAK,kBAAkB,EACvB,QAAWL,KAAOK,EAAM,KAAK,UAAUL,CAAG,EAC5C,CAGA,kBAAyB,CAClB,KAAK,OACV,KAAK,KAAO,KACZ,KAAK,IAAI,KAAK,EACd,KAAK,YAAc,EACnB,KAAK,QAAQ,UAAU,OAAQ,CAAC,EAChC,KAAK,SAAS,KAAK,QAAQ,SAAW,WAAa,WAAW,EAChE,CAMQ,mBAA0B,CAI5B,KAAK,QAAQ,WACf,KAAK,YAAY,CACf,KAAM,WACN,KAAM,CAAE,GAAI,KAAK,QAAQ,QAAQ,CAAE,CACrC,CAAC,EACD,KAAK,QAAQ,MAAM,EAEvB,CAGA,cAAqB,CACnB,GAAI,MAAK,QAAQ,WACb,KAAK,QAAU,YAWnB,IATA,KAAK,iBAAiB,EACtB,KAAK,IAAI,KAAK,EACd,KAAK,QAAQ,UAAU,OAAQ,CAAC,EAO5B,KAAK,YAAc,KAAK,eAAgB,CAC1C,KAAK,KAAK,CACR,KAAM,QACN,KAAM,sBACN,QAAS,mCACT,YAAa,GACb,eAAgB,EAClB,CAAC,EACD,KAAK,SAAS,WAAW,EACzB,MACF,CAEA,KAAK,YAAY,CAAE,KAAM,QAAS,CAAC,EACnC,KAAK,SAAS,UAAU,EAC1B,CAEA,MAAM,MAAsB,CACtB,KAAK,UACT,KAAK,QAAU,GAEX,KAAK,gBAAkB,OACzB,cAAc,KAAK,aAAa,EAChC,KAAK,cAAgB,MAGvB,KAAK,YAAY,CAAE,KAAM,KAAM,CAAC,EAChC,KAAK,QAAQ,MAAM,EACnB,KAAK,OAAS,KAEd,MAAM,QAAQ,IAAI,CAAC,KAAK,IAAI,KAAK,EAAG,KAAK,QAAQ,KAAK,CAAC,CAAC,EACxD,KAAK,SAAS,MAAM,EACtB,CAIQ,cAAcM,EAAmB,CACvC,IAAIC,EACJ,GAAI,CACFA,EAAU,KAAK,MAAMD,CAAG,CAC1B,MAAQ,CACN,MACF,CAEA,OAAQC,EAAQ,KAAM,CACpB,IAAK,QAGH,KAAK,eAAiBA,EAAQ,KAAK,gBAAkB,KAAK,eAmBtD,KAAK,QAAQ,WAAW,KAAK,IAAI,OAAO,EAE5C,KAAK,SAAS,WAAW,EACzB,MAEF,IAAK,aAAc,CAKjB,IAAIC,EAAOD,EAAQ,KAAK,KACpBA,EAAQ,KAAK,OAAS,cACpBA,EAAQ,KAAK,MACf,KAAK,cAAgB,IAErB,KAAK,eAAiBC,EACtBA,EAAO,KAAK,gBAGhB,KAAK,KAAK,CACR,KAAM,aACN,KAAMD,EAAQ,KAAK,KACnB,KAAAC,EACA,MAAOD,EAAQ,KAAK,KACtB,CAAC,EACD,KACF,CAEA,IAAK,SAYDA,EAAQ,KAAK,QAAU,aACvB,KAAK,QAAQ,UAEb,KAAK,QAAQ,MAAM,EAIjBA,EAAQ,KAAK,QAAU,cAAa,KAAK,cAAgB,IAKzD,KAAK,QAAU,aACjB,KAAK,SACHA,EAAQ,KAAK,QAAU,eACnB,aACAA,EAAQ,KAAK,KACnB,EAEF,MAEF,IAAK,eAIC,KAAK,QAAU,YAAc,KAAK,QAAU,aAC9C,KAAK,SAAS,WAAW,EAE3B,MAEF,IAAK,cACH,KAAK,KAAK,CACR,KAAM,cACN,iBAAkBA,EAAQ,KAAK,gBACjC,CAAC,EACD,MAEF,IAAK,QACH,KAAK,KAAK,CACR,KAAM,QACN,OAAQA,EAAQ,KAAK,OACrB,QAASA,EAAQ,KAAK,OACxB,CAAC,EAII,KAAK,KAAK,EACf,MAEF,IAAK,QACH,KAAK,KAAK,CACR,KAAM,QACN,KAAMA,EAAQ,KAAK,KACnB,QAASA,EAAQ,KAAK,QACtB,YAAaA,EAAQ,KAAK,YAC1B,eAAgBA,EAAQ,KAAK,cAC/B,CAAC,EAIIA,EAAQ,KAAK,cAChB,KAAK,SAAS,OAAO,EAChB,KAAK,KAAK,GAEjB,KACJ,CACF,CAEQ,UAAUP,EAAwB,CAGxC,GAAI,GAAC,KAAK,QAAQ,WAAa,KAAK,QAAU,aAC9C,IAAI,KAAK,KAAM,CACb,KAAK,KAAK,KAAKA,CAAG,EAClB,KAAK,QAAQ,UAAU,OAAQS,EAAST,CAAG,CAAC,EAC5C,MACF,CACA,KAAK,QAAQ,UAAU,OAAQS,EAAST,CAAG,CAAC,EAE5C,KAAK,aAAgBA,EAAI,WAAa,KAAS,EAAI,MACnD,GAAI,CACF,KAAK,QAAQ,KAAKA,CAAG,CACvB,MAAQ,CAER,EACF,CAEQ,YAAYO,EAAmC,CACrD,GAAI,CACF,KAAK,QAAQ,KAAK,KAAK,UAAUA,CAAO,CAAC,CAC3C,MAAQ,CAER,CACF,CAEQ,SAASG,EAAyB,CACpC,KAAK,QAAUA,IACnB,KAAK,MAAQA,EACb,KAAK,4BAA4B,EACjC,KAAK,QAAQ,QAAQ,CAAE,KAAM,QAAS,MAAAA,CAAM,CAAC,EAC/C,CAiBQ,6BAAoC,CAI1C,GAAI,EAFF,EAAQ,KAAK,QAAQ,WAAc,KAAK,QAAU,YAEjC,CACb,KAAK,gBAAkB,OACzB,cAAc,KAAK,aAAa,EAChC,KAAK,cAAgB,MAEvB,MACF,CAEI,KAAK,gBAAkB,OAE3B,KAAK,cAAgB,YAAY,IAAM,CAChC,KAAK,QAAQ,UAClB,KAAK,YAAY,CACf,KAAM,WACN,KAAM,CAAE,GAAI,KAAK,QAAQ,QAAQ,CAAE,CACrC,CAAC,CACH,EAAG,GAAG,EACR,CAEQ,KAAKN,EAAyB,CACpC,KAAK,QAAQ,QAAQA,CAAK,CAC5B,CACF,EASO,SAASO,GACdC,EACAC,EACAC,EAAY,GACJ,CACR,IAAMC,EAAOH,EAAQ,QAAQ,eAAgB,WAAW,EAClDI,EAAYD,EAAK,SAAS,GAAG,EAAI,IAAM,IACvCE,EAAOH,EAAY,kBAAoB,GAC7C,MAAO,GAAGC,CAAI,GAAGC,CAAS,SAAS,mBAAmBH,CAAK,CAAC,GAAGI,CAAI,EACrE,CCvfA,IAAMC,GAAqB,GAOrBC,GAA4B,KAErBC,GAAc,QAMdC,EAAqB,aA8CrBC,GAAkB,EAElBC,GAAwB,IAuLrC,SAASC,GAAiBC,EAAkC,CAE1D,MAAO,SADS,KAAK,IAAI,EAAG,KAAK,MAAMA,EAAmB,CAAC,EAAI,CAAC,CACzC,8DACzB,CAEA,SAASC,GAAUC,EAAwB,CACzC,MAAO,GAAGA,CAAM,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,EAAG,CAAC,CAAC,EAC1E,CAgBO,SAASC,GAAWC,EAA4B,CACrD,IAAMC,EAAO,IAAIC,EAAKF,CAAO,EAC7B,OAAAC,EAAK,QAAQ,EACNA,CACT,CAEO,IAAMC,EAAN,KAAW,CA2ChB,YAAYF,EAAsB,CAzClCG,EAAA,KAAS,QAAQ,IAAIC,GACrBD,EAAA,KAAiB,cAAc,IAAIE,GACnCF,EAAA,KAAQ,WACRA,EAAA,KAAQ,SACRA,EAAA,KAAiB,YAAY,IAAI,KAEjCA,EAAA,KAAQ,SAAiC,MACzCA,EAAA,KAAQ,eAAoC,MAQ5CA,EAAA,KAAQ,iBAAiB,IAEzBA,EAAA,KAAQ,qBAAqB,IAE7BA,EAAA,KAAQ,qBAAqB,IAC7BA,EAAA,KAAiB,iBAAiB,IAAI,KAKtCA,EAAA,KAAQ,iBAAgC,MAExCA,EAAA,KAAQ,kBAAwC,MAEhDA,EAAA,KAAQ,mBAAmB,IAE3BA,EAAA,KAAiB,gBAAgB,IAAI,KACrCA,EAAA,KAAQ,eAAqD,MAC7DA,EAAA,KAAQ,mBAAwC,MAEhDA,EAAA,KAAiB,OAAO,IAAI,KAC5BA,EAAA,KAAiB,SAAS,IAAI,KAC9BA,EAAA,KAAiB,SAAS,IAAI,KAC9BA,EAAA,KAAiB,UAAU,IAAI,KAC/BA,EAAA,KAAQ,aAA4B,MA0CpCA,EAAA,gBAAW,IAAiB,KAAK,OAGjCA,EAAA,iBAAaG,IACX,KAAK,UAAU,IAAIA,CAAQ,EACpB,IAAM,CACX,KAAK,UAAU,OAAOA,CAAQ,CAChC,IA4FFH,EAAA,YAAQI,GAAuB,CAC7B,IAAMC,EAAUD,EAAK,KAAK,EACpBE,EAAQ,KAAK,MAAM,YAAY,OAAQC,GAAMA,EAAE,SAAW,OAAO,EAGvE,GADI,KAAK,MAAM,YAAY,KAAMA,GAAMA,EAAE,SAAW,SAAS,GACzD,CAACF,GAAW,CAACC,EAAM,OAAQ,OAG/B,IAAME,EAAO,CACXH,GAAW,8BACX,GAAGC,EAAM,IAAKC,GAAM,sBAAsBA,EAAE,IAAI,+BAA+BA,EAAE,IAAI,GAAG,CAC1F,EAAE,KAAK;AAAA;AAAA,CAAM,EAKPE,EAAKf,GAAU,KAAK,EACpBgB,EAAqB,CAAE,GAAAD,EAAI,KAAM,OAAQ,KAAMJ,EAAS,GAAIC,EAAM,OAAS,CAAE,YAAaA,EAAM,IAAKC,IAAO,CAAE,KAAMA,EAAE,KAAM,GAAIA,EAAE,WAAa,CAAE,WAAYA,EAAE,UAAW,EAAI,CAAC,CAAG,EAAE,CAAE,EAAI,CAAC,CAAG,EAEpM,QAAWA,KAAK,KAAK,MAAM,YAAiBA,EAAE,SAAW,UAAYA,EAAE,YAAY,IAAI,gBAAgBA,EAAE,UAAU,EACnH,KAAK,SAAS,CACZ,OAAQ,GACR,SAAU,CAAC,GAAG,KAAK,MAAM,SAAUG,CAAK,EACxC,YAAa,CAAC,EACd,UAAW,IACb,CAAC,EACD,KAAK,QAAQ,YAAYF,EAAMC,CAAE,CACnC,GAOAT,EAAA,cAAUW,GAAqB,CAC7B,IAAMC,EAAW,KAAK,QAAQ,mBAC9B,GAAI,CAACA,EAAU,OACf,IAAMC,EAAQ,CAACJ,EAAYK,IACzB,KAAK,SAAS,CAAE,YAAa,KAAK,MAAM,YAAY,IAAKP,GAAOA,EAAE,KAAOE,EAAK,CAAE,GAAGF,EAAG,GAAGO,CAAO,EAAIP,CAAE,CAAE,CAAC,EAC3G,GAAI,KAAK,MAAM,YAAY,QAAUjB,GAAiB,CACpD,KAAK,SAAS,CAAE,UAAW,SAASA,EAAe,sBAAuB,CAAC,EAC3E,MACF,CACA,GAAI,CAACyB,GAAY,KAAK,MAAM,cAAgB,GAAIJ,CAAI,EAAG,CACrD,KAAK,SAAS,CAAE,UAAW,IAAIA,EAAK,IAAI,uCAAwC,CAAC,EACjF,MACF,CACA,IAAMF,EAAKf,GAAU,KAAK,EACpBsB,EAAaL,EAAK,KAAK,WAAW,QAAQ,GAAK,OAAO,IAAI,iBAAoB,WAAa,IAAI,gBAAgBA,CAAI,EAAI,OAC7H,KAAK,SAAS,CAAE,YAAa,CAAC,GAAG,KAAK,MAAM,YAAa,CAAE,GAAAF,EAAI,KAAME,EAAK,MAAQ,UAAW,OAAQ,UAAW,GAAIK,EAAa,CAAE,WAAAA,CAAW,EAAI,CAAC,CAAG,CAAC,EAAG,UAAW,IAAK,CAAC,EAC3K,IAAIC,EACJ,QAAQ,KAAK,CACX,QAAQ,QAAQ,EAAE,KAAK,IAAML,EAASD,CAAI,CAAC,EAC3C,IAAI,QAAe,CAACO,EAAGC,IAAW,CAAEF,EAAQ,WAAW,IAAME,EAAO,IAAI,MAAM,2BAA2B,CAAC,EAAG5B,EAAqB,CAAG,CAAC,CACxI,CAAC,EACE,KAAMa,GAAS,CACd,IAAMgB,EAAQ,OAAOhB,GAAS,SAAWA,EAAK,KAAK,EAAI,GACvDS,EAAMJ,EAAIW,EAAQ,CAAE,OAAQ,QAAS,KAAMA,CAAM,EAAI,CAAE,OAAQ,SAAU,MAAO,gCAAiC,CAAC,EAG9GA,GAAS,KAAK,OAAO,GAAK,CAAC,KAAK,MAAM,YAAY,KAAMb,GAAMA,EAAE,SAAW,SAAS,GAAG,KAAK,KAAK,EAAE,CACzG,CAAC,EACA,MAAOc,GAAmBR,EAAMJ,EAAI,CAAE,OAAQ,SAAU,MAAOY,aAAiB,OAASA,EAAM,QAAUA,EAAM,QAAU,sBAAuB,CAAC,CAAC,EAClJ,QAAQ,IAAM,aAAaJ,CAAK,CAAC,CACtC,GAQAjB,EAAA,wBAAoBS,GAAqB,CACvC,IAAMa,EAAO,KAAK,MAAM,YAAY,KAAMf,GAAMA,EAAE,KAAOE,CAAE,EACtDa,IACDA,EAAK,YAAY,IAAI,gBAAgBA,EAAK,UAAU,EACxD,KAAK,SAAS,CAAE,YAAa,KAAK,MAAM,YAAY,OAAQf,GAAMA,EAAE,KAAOE,CAAE,CAAE,CAAC,EAClF,GAgCAT,EAAA,yBAAoB,CAACuB,EAAoBC,IAA6B,CACpE,KAAK,QAAQ,cAAcD,EAAYC,CAAS,EAC3CA,GACH,KAAK,SAAS,CACZ,UAAW,KAAK,MAAM,UAAU,OAAQC,GAAMA,EAAE,aAAeF,CAAU,CAC3E,CAAC,CAEL,GAEAvB,EAAA,YAAO,IAAY,CACZ,KAAK,MAAM,QAAQ,KAAK,SAAS,CAAE,OAAQ,EAAK,CAAC,CACxD,GAEAA,EAAA,aAAQ,IAAY,CACd,KAAK,MAAM,QAAQ,KAAK,SAAS,CAAE,OAAQ,EAAM,CAAC,CACxD,GAEAA,EAAA,cAAS,IAAY,CACnB,KAAK,SAAS,CAAE,OAAQ,CAAC,KAAK,MAAM,MAAO,CAAC,CAC9C,GAyOAA,EAAA,KAAQ,cAAe0B,GAA6B,CAClD,OAAQA,EAAM,KAAM,CAClB,IAAK,QACH,KAAK,SAAS,CAAE,WAAYA,EAAM,KAAM,CAAC,EAGrC,KAAK,eAAiB,MAAM,aAAa,KAAK,YAAY,EAC9D,KAAK,aAAe,KACpB,KAAK,QAAQ,EACb,MAEF,IAAK,aACH,KAAK,SAAS,CACZ,KAAM,GACN,SAAU,CACR,GAAG,KAAK,MAAM,SACd,CAAE,GAAIA,EAAM,OAAQ,KAAM,YAAa,KAAM,GAAI,UAAW,EAAK,CACnE,CACF,CAAC,EACD,MAEF,IAAK,QACH,KAAK,YAAaC,GAChBA,EAAQ,IAAKC,GACXA,EAAQ,KAAOF,EAAM,OACjB,CAAE,GAAGE,EAAS,KAAMA,EAAQ,KAAOF,EAAM,IAAK,EAC9CE,CACN,CACF,EACA,MAEF,IAAK,WACH,KAAK,SAAS,CACZ,KAAM,GACN,SAAU,KAAK,MAAM,SAAS,IAAKA,GACjCA,EAAQ,KAAOF,EAAM,OACjB,CACE,GAAGE,EACH,KAAMF,EAAM,QACZ,UAAW,GACX,QAASA,EAAM,QAAQ,OAASA,EAAM,QAAU,MAClD,EACAE,CACN,CACF,CAAC,EACD,MAEF,IAAK,UAAW,CACd,IAAMC,EAAM,CACV,MAAO,KAAK,MACZ,YAAa,KAAK,YAClB,SACE,KAAK,QAAQ,WACXC,GAAkB,CAClB,OAAO,SAAS,OAAOA,CAAK,CAC9B,GACF,gBAAiB,KAAK,qBACtB,cAAe,KAAK,mBACpB,UAAW,KAAK,QAAQ,SAC1B,EACMC,EAAS,KAAK,OAGfC,GAAeN,EAAM,QAASG,CAAG,EACnC,KAAMI,GACLF,GAAQ,WAAWL,EAAM,UAAWO,EAAO,GAAIA,EAAO,OAAQA,EAAO,KAAK,CAC5E,EACC,MAAOZ,GACNU,GAAQ,WACNL,EAAM,UACN,GACA,OACAL,aAAiB,MAAQA,EAAM,QAAU,qBAC3C,CACF,EACF,KACF,CAEA,IAAK,WACH,KAAK,SAAS,CAAE,UAAW,CAAC,GAAG,KAAK,MAAM,UAAWK,EAAM,QAAQ,CAAE,CAAC,EACtE,KAAK,SAASA,EAAM,QAAQ,EAC5B,MAEF,IAAK,gBAAiB,CACpB,GAAIA,EAAM,SAAW,YAAa,MAOlC,IAAMQ,EAAU,KAAK,MAAM,UAAU,KAAMT,GAAMA,EAAE,aAAeC,EAAM,UAAU,EAElF,GAAIQ,EAAS,CAGX,IAAMC,EAAW,UAAUT,EAAM,UAAU,GAC3C,GAAIA,EAAM,SAAW,YAAc,CAAC,KAAK,MAAM,SAAS,KAAMU,GAAMA,EAAE,KAAOD,CAAQ,EAAG,CAGtF,IAAME,EACJX,EAAM,SAAW,WACb,GAAGQ,EAAQ,KAAK,gBAChB,GAAGA,EAAQ,KAAK,WAAMR,EAAM,OAAS,oBAAoB,GAC/D,KAAK,YAAaY,GAAa,CAC7B,GAAGA,EACH,CAAE,GAAIH,EAAU,KAAM,YAAa,KAAME,CAAQ,CACnD,CAAC,CACH,CAIA,KAAK,QAAQ,mBAAmB,CAC9B,WAAYH,EAAQ,WACpB,OAAQR,EAAM,OACd,OAAQA,EAAM,MAChB,CAAC,CACH,CAEA,KAAK,SAAS,CACZ,UAAW,KAAK,MAAM,UAAU,OAAQD,GAAMA,EAAE,aAAeC,EAAM,UAAU,EAC/E,GAAIA,EAAM,MAAQ,CAAE,UAAWA,EAAM,KAAM,EAAI,CAAC,CAClD,CAAC,EACD,KACF,CAEA,IAAK,kBACH,KAAK,SAAS,CAAE,UAAWA,EAAM,SAAU,CAAC,EAC5C,MAEF,IAAK,iBACH,KAAK,SAAS,CAAE,cAAeA,EAAM,IAAK,CAAC,EAC3C,MAEF,IAAK,gBACH,KAAK,SAAS,CAAE,gBAAiBA,EAAM,GAAI,CAAC,EAC5C,MAEF,IAAK,kBACH,KAAK,SAAS,CAAE,eAAgBA,EAAM,QAAS,CAAC,EAChD,MAEF,IAAK,iBACH,QAAQ,KAAKa,GAAoBb,EAAM,MAAM,CAAC,EAC9C,MAEF,IAAK,cAIH,GAAI,CAAC,KAAK,eAAgB,MAC1B,KAAK,eAAiB,GACjB,KAAK,WAAWA,EAAM,KAAK,EAChC,MAEF,IAAK,QAIH,GAAIA,EAAM,KAAK,WAAW,QAAQ,EAAG,CACnC,KAAK,eAAiB,GACtB,KAAK,SAAS,CAAE,KAAM,GAAO,MAAO,CAAE,GAAG,KAAK,MAAM,MAAO,UAAW,EAAM,CAAE,CAAC,EAC/E,KACF,CACA,KAAK,SAAS,CAAE,KAAM,GAAO,UAAWA,EAAM,OAAQ,CAAC,EACvD,KACJ,CACF,GAIA1B,EAAA,KAAQ,UAAU,IAAY,CAC5B,KAAK,aAAe,KACpB,IAAMwC,EAAS,KAAK,MAAM,iBAAiB,EAO3C,GAAI,GAACA,GAAU,KAAK,iBAAmB,QAEvC,KAAK,QAAQ,aAAaA,CAAM,EAChC,KAAK,eAAiBA,GAAQ,OAAS,KACvC,KAAK,gBAAkBA,EAKvB,KAAK,iBAAmB,CAAC,KAAK,MAAM,gBAAgB,EAChD,GAACA,GAAU,CAAC,KAAK,mBAErB,QAAWC,IAAU,CAAC,GAAG,KAAK,aAAa,EAAGA,EAAOD,CAAM,CAC7D,GAOAxC,EAAA,KAAQ,uBAAuB,CAAC8B,EAAejC,IAC7C,IAAI,QAAkB6C,GAAY,CAGhC,IAAMC,EAAOb,EAAM,MAAM,GAAG,EAAE,CAAC,EAC/B,GAAI,CAACjC,GAAS,OAAS,KAAK,iBAAmB8C,GAAQ,KAAK,iBAC1D,OAAOD,EAAQ,EAAI,EAErB,IAAME,EAAQC,GAAqB,CACjC,KAAK,cAAc,OAAOJ,CAAM,EAChC,aAAaxB,CAAK,EAClByB,EAAQG,CAAO,CACjB,EACMJ,EAAUK,GAA6B,CACvCA,EAAU,QAAUH,GAAMC,EAAK,EAAI,CACzC,EAGM3B,EAAQ,WAAW,IAAM2B,EAAK,EAAK,EAAGzD,EAAyB,EACrE,KAAK,cAAc,IAAIsD,CAAM,CAC/B,CAAC,GAQHzC,EAAA,KAAQ,qBAAqB,CAAC+C,EAAoBC,IAChD,IAAI,QAAkBN,GAAY,CAChC,IAAMO,EAAWT,GACfA,GAAQ,SAAS,KACdU,GAAYA,EAAQ,aAAeH,GAAcG,EAAQ,QAAUF,CACtE,GAAK,GACP,GAAI,KAAK,kBAAoBC,EAAQ,KAAK,eAAe,EAAG,OAAOP,EAAQ,EAAI,EAE/E,IAAME,EAAQlC,GAAmB,CAC/B,KAAK,cAAc,OAAO+B,CAAM,EAChC,aAAaxB,CAAK,EAClByB,EAAQhC,CAAK,CACf,EACM+B,EAAUK,GAA6B,CACvCG,EAAQH,CAAS,GAAGF,EAAK,EAAI,CACnC,EACM3B,EAAQ,WAAW,IAAM2B,EAAK,EAAK,EAAGzD,EAAyB,EACrE,KAAK,cAAc,IAAIsD,CAAM,CAC/B,CAAC,GAIHzC,EAAA,KAAQ,oBAAqBH,GAA2D,CAGtF,KAAK,SAAS,CAAE,OAAQ,IAAK,CAAC,EAE9B,KAAK,mBAAqB,EAAQA,GAAS,UAC3C,KAAK,mBAAqBA,GAAS,WAAa,GAChD,KAAK,eAAiB,GACtB,KAAK,QAAQ,kBAAkB,CACjC,GAEAG,EAAA,KAAQ,YAAY,IAAM,CACxB,KAAK,eAAiB,GACjB,KAAK,cAAc,KAAK,EAC7B,KAAK,aAAe,KAGpB,KAAK,SAAS,CAAE,UAAW,GAAO,OAAQ,IAAK,CAAC,CAClD,GAqBAA,EAAA,KAAQ,mBAAoB0B,GAA4B,CACtD,OAAQA,EAAM,KAAM,CAClB,IAAK,QACH,KAAK,SAAS,CACZ,MAAOA,EAAM,MACb,GAAIA,EAAM,QAAU,QAAUA,EAAM,QAAU,QAAU,CAAE,QAAS,EAAG,EAAI,CAAC,CAC7E,CAAC,EACD,MAEF,IAAK,aAICA,EAAM,MACR,KAAK,SAAS,CACZ,MAAO,CAAE,GAAG,KAAK,MAAM,MAAO,QAAS,EAAG,EAC1C,SAAU,CACR,GAAG,KAAK,MAAM,SACd,CAAE,GAAIhC,GAAU,OAAO,EAAG,KAAMgC,EAAM,KAAM,KAAMA,EAAM,IAAK,CAC/D,CACF,CAAC,EAED,KAAK,SAAS,CAAE,QAASA,EAAM,IAAK,CAAC,EAEvC,MAEF,IAAK,cACH,KAAK,SAAS,CAAE,OAAQlC,GAAiBkC,EAAM,gBAAgB,CAAE,CAAC,EAClE,MAEF,IAAK,QAIH,KAAK,SAAS,CAAE,QAAS,GAAI,OAAQA,EAAM,QAAS,UAAW,EAAM,CAAC,EACtE,MAEF,IAAK,QACH,KAAK,SAAS,CAAE,UAAWA,EAAM,OAAQ,CAAC,EAC1C,KACJ,CACF,GA7zBE,KAAK,QAAU,CAAE,GAAG7B,CAAQ,EAC5B,KAAK,MAAQ,CACX,WAAY,OACZ,SAAU,CAAC,EACX,UAAW,CAAC,EACZ,UAAW,CAAC,EACZ,KAAM,GACN,UAAW,KACX,OAAQ,GACR,cAAe,KACf,gBAAiB,KACjB,eAAgB,KAChB,aAAcA,EAAQ,mBAAqBA,EAAQ,kBAAoB,UAAY,KACnF,YAAa,CAAC,EACd,MAAO,CACL,UAAW,GACX,MAAO,OACP,UAAW,GACX,QAAS,GACT,OAAQ,KACR,MAAO,KAAK,kBACZ,KAAM,KAAK,UACX,MAAQA,GAAY,KAAK,cAAc,eAAeA,CAAO,EAC7D,QAAS,IAAM,KAAK,cAAc,aAAa,EAC/C,QAAS,IAAM,KAAK,cAAc,iBAAiB,EACnD,QAAS,IAAM,KAAK,cAAc,iBAAiB,EACnD,QAAUM,IACR,KAAK,eAAe,IAAIA,CAAQ,EACzB,IAAM,CACX,KAAK,eAAe,OAAOA,CAAQ,CACrC,EAEJ,CACF,CACF,CAgBA,IAAI,OAAuB,CACzB,OAAO,KAAK,MAAM,KACpB,CAEQ,SAASU,EAAiC,CAChD,KAAK,MAAQ,CAAE,GAAG,KAAK,MAAO,GAAGA,CAAM,EAEvC,QAAWV,IAAY,CAAC,GAAG,KAAK,SAAS,EAAGA,EAAS,CACvD,CAEQ,SAASU,EAAqC,CACpD,KAAK,SAAS,CAAE,MAAO,CAAE,GAAG,KAAK,MAAM,MAAO,GAAGA,CAAM,CAAE,CAAC,CAC5D,CAEQ,YAAYsC,EAA0D,CAC5E,KAAK,SAAS,CAAE,SAAUA,EAAO,KAAK,MAAM,QAAQ,CAAE,CAAC,CACzD,CAQA,OAAOtD,EAAqC,CAC1C,IAAMuD,EAAW,KAAK,QACtB,KAAK,QAAU,CAAE,GAAGA,EAAU,GAAGvD,CAAQ,EACzC,IAAMwD,EAAe,KAAK,QAAQ,mBAAqB,KAAK,QAAQ,kBAAoB,UAAY,KAChGA,IAAiB,KAAK,MAAM,cAAc,KAAK,SAAS,CAAE,aAAAA,CAAa,CAAC,GAE1E,KAAK,QAAQ,MAAQD,EAAS,KAC9B,KAAK,QAAQ,YAAcA,EAAS,WACpC,EAAQ,KAAK,QAAQ,OAAW,EAAQA,EAAS,QAC5B,KAAK,SAC1B,KAAK,WAAW,EAChB,KAAK,QAAQ,EAEjB,CAGA,SAAgB,CACd,GAAI,KAAK,OAAQ,OACjB,IAAMrB,EAAS,IAAIuB,EAAgB,CACjC,IAAK,KAAK,QAAQ,IAClB,UAAW,KAAK,QAAQ,UACxB,aAAc,IAAM,KAAK,QAAQ,aAAa,EAC9C,WAAYlE,GACZ,QAAS,KAAK,YACd,MAAO,KAAK,QAAQ,OAAS,EAC/B,CAAC,EACD,KAAK,OAAS2C,EAId,KAAK,iBAAmB,KAAK,MAAM,UAAU,IAAM,CAC7C,KAAK,eAAiB,MAAM,aAAa,KAAK,YAAY,EAC9D,KAAK,aAAe,WAAW,KAAK,QAAS7C,EAAkB,CACjE,CAAC,EACD,KAAK,QAAQ,EAER6C,EAAO,QAAQ,CACtB,CAMA,YAAmB,CACb,KAAK,eAAiB,MAAM,aAAa,KAAK,YAAY,EAC9D,KAAK,aAAe,KACpB,KAAK,mBAAmB,EACxB,KAAK,iBAAmB,KACxB,KAAK,QAAQ,MAAM,EACnB,KAAK,OAAS,KACT,KAAK,cAAc,KAAK,EAC7B,KAAK,aAAe,KACpB,KAAK,YAAY,MAAM,CACzB,CAGA,SAAgB,CACd,KAAK,WAAW,EAChB,KAAK,UAAU,MAAM,EACrB,KAAK,eAAe,MAAM,CAC5B,CAuEQ,QAAkB,CACxB,MAAO,CAAC,CAAC,OAAQ,aAAc,OAAO,EAAE,SAAS,KAAK,MAAM,MAAM,KAAK,CACzE,CAeQ,SAASwB,EAAiC,CAChD,IAAMC,EAAOC,GAA+B,CAEtCA,IAAS,WAAa,CAAC,KAAK,MAAM,UAAU,KAAMhC,GAAMA,EAAE,aAAe8B,EAAS,UAAU,GAChG,KAAK,SAAS,CAAE,UAAW,CAAE,GAAG,KAAK,MAAM,UAAW,CAACA,EAAS,UAAU,EAAGE,CAAK,CAAE,CAAC,CACvF,EACM7C,EAAW,KAAK,QAAQ,iBAC9B,GAAI,CAACA,EAAU,CAAE4C,EAAIE,EAAWH,CAAQ,CAAC,EAAG,MAAQ,CACpDC,EAAI,SAAS,EACb,IAAItB,EAAU,GACRyB,EAAUC,GAA8C,CACxD1B,IAAiBA,EAAU,GAC/BsB,EAAII,GAAK,MAAM,QAAQA,EAAE,KAAK,EAAIC,GAAQD,CAAC,EAAIF,EAAWH,CAAQ,CAAC,EACrE,EACMtC,EAAQ,WAAW,IAAM0C,EAAO,IAAI,EAAGG,EAAmB,EAChE,QAAQ,QAAQ,EACb,KAAK,IAAMlD,EAAS2C,CAAQ,CAAC,EAC7B,KAAMK,GAAM,CAAE,aAAa3C,CAAK,EAAG0C,EAAOC,CAAC,CAAG,CAAC,EAC/C,MAAOvC,GAAU,CAChB,aAAaJ,CAAK,EAClB,QAAQ,KAAK,sEAAuEI,CAAK,EACzFsC,EAAO,IAAI,CACb,CAAC,CACL,CA2BA,OAAOvD,EAA0B,CAC/B,MAAO,IAAM,KAAK,KAAKA,CAAI,CAC7B,CAYA,UAAUoC,EAAuC,CAC/C,IAAMuB,EAAa,KAAK,UAAUvB,CAAM,EAGxC,OAAIuB,IAAe,KAAK,YAAc,KAAK,MAAM,iBAAiB,GAAG,WAAavB,EAAO,YACvF,KAAK,WAAauB,EAClB,KAAK,MAAM,UAAU,KAAK,MAAMA,CAAU,CAAsB,GAE3D,IAAM,CACX,KAAK,MAAM,YAAYvB,EAAO,QAAQ,EAClC,KAAK,MAAM,iBAAiB,IAAM,OAAM,KAAK,WAAa,KAChE,CACF,CAOA,kBACEwB,EACAC,EACAC,EACY,CACZ,OAAAF,GAAS,aAAaG,EAAqBF,CAAY,EACvD,KAAK,MAAM,kBAAkBA,EAAc,CAAE,GAAGC,CAAK,CAAC,EAC/C,IAAM,CACX,KAAK,MAAM,oBAAoBD,CAAY,EACvCD,GAAS,aAAaG,CAAmB,IAAMF,GACjDD,EAAQ,gBAAgBG,CAAmB,CAE/C,CACF,CAUA,eAAeC,EAAcC,EAA+C,CAC1E,IAAMC,EAAW,KAAK,QAAQ,IAAIF,CAAI,EACtC,GAAIE,GAAY,KAAK,MAAM,UAAUF,CAAI,IAAME,EAAS,YACtD,OAAAA,EAAS,OAASD,EACX,IAAM,KAAK,WAAWD,EAAME,CAAQ,EAE7C,IAAMC,EAA2D,CAC/D,OAAQF,EACR,UAAWD,EACX,YAAa,CAAE,KAAAA,EAAM,QAAUI,GAAUD,EAAM,OAAOC,CAAK,CAAE,CAC/D,EACA,YAAK,QAAQ,IAAIJ,EAAMG,CAAK,EAC5B,KAAK,MAAM,eAAeA,EAAM,WAAW,EACpC,IAAM,KAAK,WAAWH,EAAMG,CAAK,CAC1C,CAEQ,WAAWH,EAAcG,EAA0D,CACrF,KAAK,QAAQ,IAAIH,CAAI,IAAMG,IAC/B,KAAK,QAAQ,OAAOH,CAAI,EACpB,KAAK,MAAM,UAAUA,CAAI,IAAMG,EAAM,aAAa,KAAK,MAAM,iBAAiBH,CAAI,EACxF,CAOA,aAAaK,EAAiC,CAC5C,OAAO,KAAK,QACV,KAAK,KACLA,EAAK,GACLA,EACAC,GAAcD,CAAI,EACjBE,GAAWC,GAAgBD,CAAM,EACjClE,GAAO,KAAK,MAAM,WAAWA,CAAE,EAChC,CAACA,EAAIoE,IAAgB,KAAK,MAAM,gBAAgBpE,EAAIoE,CAAW,EAC9DpE,GAAO,KAAK,MAAM,kBAAkBA,CAAE,CACzC,CACF,CAQA,cAAmBqE,EAAwC,CACzD,OAAO,KAAK,QACV,KAAK,OACLA,EAAM,GACNA,EACAC,GAAeD,CAAK,EACnBH,GAAWK,GAAiBL,CAAM,EAClClE,GAAO,KAAK,MAAM,SAASA,CAAE,EAC9B,CAACA,EAAIoE,IAAgB,KAAK,MAAM,cAAcpE,EAAIoE,CAAW,EAC5DpE,GAAO,KAAK,MAAM,gBAAgBA,CAAE,CACvC,CACF,CAOA,cAAqBwE,EAA0C,CAC7D,OAAO,KAAK,QACV,KAAK,OACLA,EAAM,GACNA,EACAC,GAAeD,CAAK,EACnBN,GAAWQ,EAAiBR,CAAM,EAClClE,GAAO,KAAK,MAAM,SAASA,CAAE,EAC9B,CAACA,EAAIoE,IAAgB,KAAK,MAAM,cAAcpE,EAAIoE,CAAW,EAC5DpE,GAAO,KAAK,MAAM,gBAAgBA,CAAE,CACvC,CACF,CAMA,UAAU2E,EAAaC,EAA6C,CAClE,YAAK,MAAM,UAAUD,EAAK,KAAK,MAAM,KAAK,UAAUC,CAAM,CAAC,CAA4B,EAChF,IAAM,KAAK,MAAM,YAAYD,CAAG,CACzC,CAQQ,QACNE,EACA7E,EACAuC,EACAuC,EACAC,EACA7D,EACA8D,EACAC,EACY,CACZ,IAAInB,EAAQe,EAAQ,IAAI7E,CAAE,EAC1B,GAAI8D,GAAS5C,EAAQlB,CAAE,IAAM8D,EAAM,aAEjC,GADAA,EAAM,OAASvB,EACXuB,EAAM,YAAcgB,EAAW,CACjC,IAAMI,EAASpB,EACfA,EAAM,UAAYgB,EAClBhB,EAAM,YAAciB,EAAM,IAAMG,EAAO,MAAM,EAC7CF,EAAShF,EAAI8D,EAAM,WAAW,CAChC,MACK,CACL,IAAMqB,EAAQ,CAAE,OAAQ5C,EAAO,UAAAuC,CAAU,EACzCK,EAAM,YAAcJ,EAAM,IAAMI,EAAM,MAAM,EAC5CN,EAAQ,IAAI7E,EAAImF,CAA6B,EAC7CH,EAAShF,EAAImF,EAAM,WAAW,EAC9BrB,EAAQqB,CACV,CACA,IAAMC,EAAOtB,EACb,MAAO,IAAM,CACPe,EAAQ,IAAI7E,CAAE,IAAMoF,IACxBP,EAAQ,OAAO7E,CAAE,EACbkB,EAAQlB,CAAE,IAAMoF,EAAK,aAAaH,EAAWjF,CAAE,EACrD,CACF,CAOA,YAAYuD,EAAkC,CAC5C,IAAM8B,EAAS9B,aAAmB,kBAC5B+B,EAAkB,CAAC,gBAAiB,gBAAiB,eAAe,EACrED,IACH9B,EAAQ,aAAa,OAAQ,QAAQ,EACrC+B,EAAM,KAAK,MAAM,EACZ/B,EAAQ,aAAa,UAAU,IAClCA,EAAQ,aAAa,WAAY,GAAG,EACpC+B,EAAM,KAAK,UAAU,IAGzB/B,EAAQ,aAAa,gBAAiB3E,CAAkB,EACxD2E,EAAQ,aAAa,gBAAiB,QAAQ,EAE9C,IAAMgC,EAAO,IAAMhC,EAAQ,aAAa,gBAAiB,OAAO,KAAK,MAAM,MAAM,CAAC,EAC5EiC,EAAU,IAAM,KAAK,OAAO,EAG5BC,EAAaxE,GAAyB,EACtCA,EAAM,MAAQ,SAAWA,EAAM,MAAQ,OACzCA,EAAM,eAAe,EACrB,KAAK,OAAO,EAEhB,EAEAsE,EAAK,EACL,IAAMG,EAAc,KAAK,UAAUH,CAAI,EACvC,OAAAhC,EAAQ,iBAAiB,QAASiC,CAAO,EACpCH,GAAQ9B,EAAQ,iBAAiB,UAAWkC,CAAS,EAEnD,IAAM,CACXC,EAAY,EACZnC,EAAQ,oBAAoB,QAASiC,CAAO,EAC5CjC,EAAQ,oBAAoB,UAAWkC,CAAS,EAChD,QAAWE,KAAaL,EAAO/B,EAAQ,gBAAgBoC,CAAS,CAClE,CACF,CAkRA,MAAc,WAAWC,EAA8B,CACrD,IAAMC,EAAS,KAAK,mBACpB,KAAK,SAAS,CAAE,UAAWA,CAAO,CAAC,EAEnC,IAAMC,EAAU,IAAIC,EAAa,CAC/B,IAAKC,GAAS,KAAK,QAAQ,IAAKJ,EAAOC,CAAM,EAC7C,UAAWA,EACX,QAAS,KAAK,iBACd,QAAS,CAACI,EAAQC,IAAU,CAC1B,QAAWxG,KAAY,KAAK,eAAgBA,EAASuG,EAAQC,CAAK,CACpE,CACF,CAAC,EACD,KAAK,aAAeJ,EAGhB,KAAK,oBAAoB,KAAK,KAAK,EACvC,MAAMA,EAAQ,MAAM,CACtB,CA4CF,EAGO,SAASxF,GAAY6F,EAAgBjG,EAA+C,CACzF,IAAMyD,EAAOzD,EAAK,KAAK,YAAY,EACnC,OAAOiG,EAAO,MAAM,GAAG,EAAE,IAAKC,GAAMA,EAAE,KAAK,EAAE,YAAY,CAAC,EAAE,OAAO,OAAO,EAAE,KAAMC,GAChFA,EAAK,WAAW,GAAG,EAAI1C,EAAK,SAAS0C,CAAI,EAAIA,EAAK,SAAS,IAAI,EAAInG,EAAK,KAAK,YAAY,EAAE,WAAWmG,EAAK,MAAM,EAAG,EAAE,CAAC,EAAInG,EAAK,KAAK,YAAY,IAAMmG,CAAI,CAC/J,CCxqCO,SAASC,EACdC,EACAC,EAAe,CAAC,KACbC,EACuB,CAC1B,IAAMC,EAAU,SAAS,cAAcH,CAAG,EACtCC,EAAM,QAAOE,EAAQ,UAAYF,EAAM,OAC3CG,EAASD,EAASF,EAAM,OAAS,CAAC,CAAC,EACnC,OAAW,CAACI,EAAMC,CAAQ,IAAK,OAAO,QAAQL,EAAM,IAAM,CAAC,CAAC,EAC1DE,EAAQ,iBAAiBE,EAAMC,CAAyB,EAE1D,OAAAC,GAAOJ,EAASD,CAAQ,EACjBC,CACT,CAEA,IAAMK,GAAM,6BAEL,SAASC,EAAIT,EAAaU,KAA2CR,EAA8B,CACxG,IAAMC,EAAU,SAAS,gBAAgBK,GAAKR,CAAG,EACjD,OAAW,CAACW,EAAMC,CAAK,IAAK,OAAO,QAAQF,CAAK,EAAGP,EAAQ,aAAaQ,EAAM,OAAOC,CAAK,CAAC,EAC3F,QAAWC,KAASX,EAAUC,EAAQ,YAAYU,CAAK,EACvD,OAAOV,CACT,CAEO,SAASC,EAASD,EAAkBO,EAAoE,CAC7G,OAAW,CAACC,EAAMC,CAAK,IAAK,OAAO,QAAQF,CAAK,EAC1CE,IAAU,QAAaA,IAAU,GAAOT,EAAQ,gBAAgBQ,CAAI,EACnER,EAAQ,aAAaQ,EAAMC,IAAU,GAAO,GAAK,OAAOA,CAAK,CAAC,CAEvE,CAEO,SAASL,GAAOJ,EAAeD,EAAyB,CAC7D,QAAWW,KAASX,EACdW,GAAU,MAA+BA,IAAU,IACvDV,EAAQ,YAAY,OAAOU,GAAU,SAAW,SAAS,eAAeA,CAAK,EAAIA,CAAK,CAE1F,CAGO,SAASC,EAAQX,KAAkBD,EAAyB,CACjE,KAAOC,EAAQ,YAAYA,EAAQ,YAAYA,EAAQ,UAAU,EACjEI,GAAOJ,EAASD,CAAQ,CAC1B,CChDA,IAAMa,GAAY,4DAGZC,GAAO,o2RAGPC,GAAQ,2iDAEP,SAASC,EAASC,EAAcC,EAAcC,EAA2B,CAC9E,IAAMC,EAAOC,EACX,MACA,CAAE,QAAS,gBAAiB,MAAOJ,EAAM,OAAQA,EAAM,cAAe,OAAQ,UAAW,OAAQ,EACjGI,EAAI,OAAQ,CAAE,KAAMH,EAAM,YAAa,UAAW,UAAWL,GAAW,EAAGC,EAAK,CAAC,EACjFO,EAAI,OAAQ,CAAE,KAAMF,EAAO,YAAa,UAAW,UAAWN,GAAW,EAAGE,EAAM,CAAC,CACrF,EACA,OAAAK,EAAK,MAAM,QAAU,QACdA,CACT,CCVO,IAAME,GAAc,UACdC,EAAe,UACfC,EAAiB,UAEjBC,GAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gBAyJNH,EAAW,YAAYC,CAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gBAOnCD,EAAW,YAAYC,CAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2EASwBC,CAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gBA6BzEF,EAAW,YAAYE,CAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;EC5M9C,SAASE,EACdC,EACAC,EACAC,EACAC,EACM,CACN,OAAIH,EAAiBA,EAAS,UAAU,EAAI,EACxCC,GAAOA,IAAQC,EACVE,EAAE,MAAO,CACd,MAAO,iBACP,MAAO,CAAE,IAAKH,EAAK,IAAK,GAAI,UAAW,OAAQ,EAC/C,GAAI,CAAE,MAAO,IAAME,EAAOF,CAAG,CAAE,CACjC,CAAC,EAEII,EAAS,GAAIC,EAAcC,CAAc,CAClD,CCbO,SAASC,EACdC,EACAC,EACAC,EAA6BC,EAAWH,CAAQ,EACnC,CACb,IAAMI,EAAOJ,EAAS,OAAS,OACzBK,EAAUH,IAAS,UACnBI,EAAID,EAAU,KAAOH,EACrBK,EAAcD,GAAKA,EAAE,SAAW,QAClCE,EAAE,UAAW,CAAE,MAAO,WAAY,EAAGA,EAAE,UAAW,CAAC,EAAG,iBAAiB,EAAGA,EAAE,IAAK,CAAE,MAAO,kBAAmB,EAAGR,EAAS,WAAW,CAAC,EACrIQ,EAAE,IAAK,CAAE,MAAO,kBAAmB,EAAGR,EAAS,WAAW,EAE9D,OAAOQ,EACL,MACA,CAAE,MAAO,OAAQ,MAAO,CAAE,KAAM,QAAS,aAAc,YAAYR,EAAS,KAAK,GAAI,YAAaK,EAAU,OAAS,MAAU,CAAE,EACjIG,EACE,MACA,CAAE,MAAO,aAAc,EACvBA,EAAE,SAAU,CAAC,EAAGR,EAAS,KAAK,EAC9BQ,EAAE,OAAQ,CAAE,MAAOJ,EAAO,iBAAmB,MAAO,EAAGA,EAAO,aAAe,SAAS,CACxF,EACAC,GAAWG,EAAE,IAAK,CAAE,MAAO,kBAAmB,EAAG,4BAAuB,EACxEF,GAAG,SAAWE,EAAE,IAAK,CAAE,MAAO,cAAe,EAAGF,EAAE,OAAO,EACzDA,GAAKA,EAAE,SAAW,SAAWC,EAC7BD,GAAKA,EAAE,MAAM,OAAS,GAAKE,EAAE,KAAM,CAAE,MAAO,YAAa,EAAG,GAAGF,EAAE,MAAM,IAAIG,EAAQ,CAAC,EACpFH,GAAG,MAAQE,EAAE,IAAK,CAAE,MAAO,WAAY,EAAGF,EAAE,IAAI,EAChDA,GAAKA,EAAE,SAAW,SAAWC,EAC7BC,EACE,MACA,CAAE,MAAO,cAAe,EACxBA,EACE,SACA,CAAE,MAAO,mBAAoB,MAAO,CAAE,KAAM,QAAS,EAAG,GAAI,CAAE,MAAO,IAAMP,EAAUD,EAAS,WAAY,EAAK,CAAE,CAAE,EACnH,QACF,EACAQ,EACE,SACA,CAAE,MAAOJ,EAAO,+BAAiC,iBAAkB,MAAO,CAAE,KAAM,SAAU,SAAUC,EAAU,GAAK,MAAU,EAAG,GAAI,CAAE,MAAO,IAAM,CAAOA,GAASJ,EAAUD,EAAS,WAAY,EAAI,CAAG,CAAE,CAAE,EAC/MM,GAAG,cAAgB,SACrB,CACF,CACF,CACF,CAGA,SAASG,GAASC,EAA6B,CAE7C,GAAIA,EAAK,QAAQ,OACf,OAAOF,EACL,MACA,CAAE,MAAO,gBAAiB,EAC1BA,EAAE,OAAQ,CAAE,MAAO,aAAc,EAAG,GAAGE,EAAK,OAAO,IAAKC,GAAOH,EAAE,MAAO,CAAE,MAAO,CAAE,IAAKG,EAAG,IAAK,IAAKA,EAAG,KAAO,GAAI,QAAS,OAAQ,eAAgB,aAAc,CAAE,CAAC,CAAC,CAAC,EACvKH,EAAE,OAAQ,CAAE,MAAO,iBAAkB,EAAGE,EAAK,OAASF,EAAE,KAAM,CAAE,MAAO,kBAAmB,EAAGE,EAAK,KAAK,EAAGF,EAAE,KAAM,CAAE,MAAOE,EAAK,MAAQ,mBAAqB,oCAAqC,EAAG,GAAGE,GAAUF,CAAI,CAAC,CAAC,CAC1N,EAEF,IAAMG,EAAQL,EACZ,KACA,CAAE,MAAOE,EAAK,MAAQ,aAAe,4BAA6B,EAClE,GAAGE,GAAUF,CAAI,CACnB,EACA,OAAOF,EAAE,MAAO,CAAE,MAAO,UAAW,EAAGE,EAAK,OAASF,EAAE,KAAM,CAAE,MAAO,UAAW,EAAGE,EAAK,KAAK,EAAGG,CAAK,CACxG,CAGA,SAASD,GAAUF,EAAgB,CACjC,MAAO,CACLA,EAAK,SAAW,QAAaF,EAAE,IAAK,CAAE,MAAO,aAAc,EAAGE,EAAK,MAAM,EACzEA,EAAK,SAAW,QAAaF,EAAE,OAAQ,CAAE,MAAO,aAAc,MAAO,CAAE,aAAc,SAAU,CAAE,EAAG,UAAK,EACzGE,EAAK,OAAS,QAAaF,EAAE,OAAQ,CAAE,MAAO,YAAa,EAAGE,EAAK,IAAI,EACvE,CAAC,CAACA,EAAK,OAAO,QAAUF,EAAE,KAAM,CAAE,MAAO,YAAa,EAAG,GAAGE,EAAK,MAAM,IAAKI,GAASN,EAAE,KAAM,CAAC,EAAGA,EAAE,OAAQ,CAAE,MAAO,YAAa,EAAGM,CAAI,CAAC,CAAC,CAAC,CAC7I,CACF,CCrFA,SAASC,KAAQC,EAAoC,CACnD,OAAOC,EACL,MACA,CACE,QAAS,YACT,MAAO,GACP,OAAQ,GACR,KAAM,OACN,OAAQ,eACR,eAAgB,IAChB,iBAAkB,QAClB,kBAAmB,QACnB,cAAe,MACjB,EACA,GAAGD,CACL,CACF,CAEO,IAAME,GAAW,IACtBH,EAAKE,EAAI,OAAQ,CAAE,EAAG,iHAAkH,CAAC,CAAC,EAE/HE,GAAU,IACrBJ,EACEE,EAAI,OAAQ,CAAE,EAAG,EAAG,EAAG,IAAK,MAAO,EAAG,OAAQ,GAAI,GAAI,CAAE,CAAC,EACzDA,EAAI,OAAQ,CAAE,EAAG,uCAAwC,CAAC,CAC5D,EAEWG,GAAW,IAAML,EAAKE,EAAI,OAAQ,CAAE,EAAG,0CAA2C,CAAC,CAAC,EAGpFI,GAAW,IAAMN,EAAKE,EAAI,OAAQ,CAAE,EAAG,6GAA8G,CAAC,CAAC,ECP7J,IAAMK,EAAW,GACXC,GAAS,GAChBC,GAAiB,EAEjBC,GAAiB,IAEjBC,GAAe,oBACfC,GAAW,sBAgCjB,SAASC,IAAwB,CAC/B,GAAI,CACF,IAAMC,EAAM,OAAO,aAAa,QAAQH,EAAY,EACpD,GAAI,CAACG,EAAK,OAAO,KACjB,IAAMC,EAAS,KAAK,MAAMD,CAAG,EAC7B,IAAKC,EAAO,OAAS,QAAUA,EAAO,OAAS,UAAY,OAAOA,EAAO,GAAM,SAAU,OAAOA,CAClG,MAAQ,CAER,CACA,OAAO,IACT,CAEA,SAASC,GAAUC,EAAkB,CACnC,GAAI,CACF,OAAO,aAAa,QAAQN,GAAc,KAAK,UAAUM,CAAI,CAAC,CAChE,MAAQ,CAER,CACF,CAEA,SAASC,IAAoB,CAC3B,GAAI,CACF,OAAO,OAAO,aAAa,QAAQN,EAAQ,IAAM,GACnD,MAAQ,CACN,MAAO,EACT,CACF,CAEA,SAASO,IAAqB,CAC5B,GAAI,CACF,OAAO,aAAa,QAAQP,GAAU,GAAG,CAC3C,MAAQ,CAER,CACF,CAEO,IAAMQ,EAAN,KAAU,CA6Bf,YAA6BC,EAAY,CAAZC,EAAA,YAAAD,GA3B7BC,EAAA,KAAS,QACTA,EAAA,KAAiB,UACjBA,EAAA,KAAiB,QACjBA,EAAA,KAAiB,QACjBA,EAAA,KAAiB,QACjBA,EAAA,KAAiB,QACjBA,EAAA,KAAiB,SACjBA,EAAA,KAAiB,UAEjBA,EAAA,KAAQ,WACRA,EAAA,KAAQ,SACRA,EAAA,KAAQ,WAAW,IACnBA,EAAA,KAAQ,OAAoB,MAC5BA,EAAA,KAAQ,OAA6C,MACrDA,EAAA,KAAQ,WAAW,CAACJ,GAAS,GAC7BI,EAAA,KAAQ,QAAsB,MAC9BA,EAAA,KAAQ,qBAA6B,QACrCA,EAAA,KAAQ,WACRA,EAAA,KAAQ,kBACRA,EAAA,KAAQ,cACRA,EAAA,KAAQ,aAA4B,MACpCA,EAAA,KAAQ,YAAY,GACpBA,EAAA,KAAQ,YAAiC,MACzCA,EAAA,KAAiB,gBACf,OAAO,OAAW,KAAe,OAAO,aAAa,kCAAkC,EAAE,UAAY,IACvGA,EAAA,KAAiB,WAA2B,CAAC,GAG3C,KAAK,KAAOC,EAAE,OAAQ,CAAE,MAAO,YAAa,MAAO,CAAE,cAAe,MAAO,CAAE,CAAC,EAC9E,KAAK,KAAOA,EAAE,OAAQ,CAAE,MAAO,MAAO,CAAC,EACvC,KAAK,OAASA,EACZ,SACA,CACE,MAAO,MACP,MAAO,CAAE,KAAM,QAAS,EACxB,GAAI,CACF,YAAcC,GAAU,KAAK,cAAcA,CAAK,EAChD,YAAcA,GAAU,KAAK,cAAcA,CAAK,EAChD,UAAW,IAAM,KAAK,OAAO,EAAK,EAClC,cAAe,IAAM,KAAK,OAAO,EAAI,EACrC,QAAUA,GAAU,KAAK,UAAUA,CAAK,EACxC,MAAQA,GAAU,KAAK,QAAQA,CAAK,EACpC,YAAcA,GAAUA,EAAM,eAAe,CAC/C,CACF,EACA,KAAK,KACL,KAAK,IACP,EAEA,KAAK,KAAOD,EACV,SACA,CACE,MAAO,OACP,MAAO,CAAE,KAAM,SAAU,aAAc,WAAY,EACnD,GAAI,CAAE,MAAO,IAAM,KAAK,KAAK,MAAM,KAAK,CAAE,CAC5C,EACAA,EAAE,OAAQ,CAAE,MAAO,cAAe,MAAO,CAAE,cAAe,MAAO,CAAE,CAAC,CACtE,EACA,KAAK,KAAOA,EAAE,MAAO,CAAE,MAAO,UAAW,MAAO,CAAE,KAAM,MAAO,EAAG,GAAI,CAAE,QAAUC,GAAU,KAAK,cAAcA,CAAK,CAAE,CAAE,CAAC,EACzH,KAAK,MAAQD,EAAE,MAAO,CAAE,MAAO,eAAgB,CAAC,EAChD,KAAK,OAASA,EAAE,MAAO,CAAE,MAAO,qBAAsB,MAAO,CAAE,KAAM,SAAU,YAAa,QAAS,CAAE,CAAC,EACxG,KAAK,KAAOA,EAAE,MAAO,CAAC,EAAG,KAAK,KAAM,KAAK,MAAO,KAAK,OAAQ,KAAK,OAAQ,KAAK,IAAI,EAEnF,IAAME,EAAW,IAAM,KAAK,OAAO,EACnC,OAAO,iBAAiB,SAAUA,CAAQ,EAC1C,KAAK,SAAS,KAAK,IAAM,OAAO,oBAAoB,SAAUA,CAAQ,CAAC,EAEvE,IAAMC,EAAaF,GAAwB,CACzC,GAAI,CAAC,KAAK,SAAU,OACpB,IAAMG,EAAOH,EAAM,aAAa,EAC5B,CAACG,EAAK,SAAS,KAAK,IAAI,GAAK,CAACA,EAAK,SAAS,KAAK,MAAM,GAAG,KAAK,QAAQ,EAAK,CAClF,EACA,SAAS,iBAAiB,cAAeD,CAAS,EAClD,KAAK,SAAS,KAAK,IAAM,SAAS,oBAAoB,cAAeA,CAAS,CAAC,CACjF,CAEA,OAAOE,EAAkBC,EAA2B,CAC9C,KAAK,SAAS,YAAcA,EAAQ,YACtC,KAAK,KAAOA,EAAQ,UAAYhB,GAAS,EAAI,MAG3C,KAAK,qBAAuB,aAAee,EAAM,MAAM,QAAU,aACnET,GAAa,EACb,KAAK,SAAW,IAElB,KAAK,mBAAqBS,EAAM,MAAM,MACtC,KAAK,MAAQA,EACb,KAAK,QAAUC,EACf,KAAK,OAAO,CACd,CAEA,SAAgB,CACd,KAAK,SAAS,EACV,KAAK,OAAO,OAAO,aAAa,KAAK,MAAM,KAAK,EACpD,QAAWC,KAAW,KAAK,SAAUA,EAAQ,CAC/C,CAIA,IAAY,WAAY,CACtB,IAAMF,EAAQ,KAAK,MAAM,MAAM,MAC/B,OAAOA,IAAU,QAAUA,IAAU,OACvC,CAEA,IAAY,WAAY,CACtB,OAAO,KAAK,WAAa,KAAK,MAAM,MAAM,SAC5C,CAEA,IAAY,YAAa,CACvB,OAAO,KAAK,WAAa,CAAC,KAAK,MAAM,MAAM,SAC7C,CAEQ,WAA+C,CACrD,IAAMG,EAAU,KAAK,MAAM,MAAM,UAAY,KAAK,QAAQ,WAAa,CAAC,EACxE,MAAO,CACL,CAAE,IAAK,OAAQ,MAAO,MAAO,EAC7B,GAAIA,EAAQ,SAAS,YAAY,EAAI,CAAC,CAAE,IAAK,aAAuB,MAAO,MAAO,CAAC,EAAI,CAAC,EACxF,GAAIA,EAAQ,SAAS,MAAM,EAAI,CAAC,CAAE,IAAK,OAAiB,MAAO,cAAe,CAAC,EAAI,CAAC,CACtF,CACF,CAIQ,UAAW,CACjB,MAAO,CAAE,MAAO,OAAO,WAAY,OAAQ,OAAO,WAAY,CAChE,CAEQ,SAASC,EAAqB,CACpC,GAAM,CAAE,OAAAC,CAAO,EAAI,KAAK,SAAS,EAC3BC,EAASD,EAAS,KAAK,QAAQ,OAAO,OAAS1B,EACrD,OAAO,KAAK,IAAI,KAAK,IAAIC,GAAQwB,CAAG,EAAG,KAAK,IAAIxB,GAAQ0B,CAAM,CAAC,CACjE,CAGA,MAAyB,CACvB,GAAM,CAAE,MAAAC,CAAM,EAAI,KAAK,SAAS,EAChC,OAAI,KAAK,KAAa,KAAK,KAAK,KAAO5B,EAAW,EAAI4B,EAAQ,EAAI,OAAS,QACpE,KAAK,MAAM,OAAS,KAAK,SAAS,SAAW,cAAgB,OAAS,QAC/E,CAEQ,UAAW,CACjB,GAAM,CAAE,MAAAA,EAAO,OAAAF,CAAO,EAAI,KAAK,SAAS,EAClCG,EAAO,KAAK,KAAK,EACjBC,EAAO,KAAK,QAAQ,OAAO,KAC3BC,EAAO,KAAK,KAAO,KAAK,KAAK,KAAOF,IAAS,OAASC,EAAOF,EAAQE,EAAO9B,EAC5EyB,EAAM,KAAK,KACb,KAAK,KAAK,IACV,KAAK,SAAS,KAAK,KAAO,KAAK,KAAK,EAAIC,EAASA,EAAS,KAAK,QAAQ,OAAO,OAAS1B,CAAQ,EACnG,MAAO,CAAE,KAAA6B,EAAM,KAAAE,EAAM,IAAAN,EAAK,MAAOA,EAAMzB,EAAW,EAAI0B,EAAS,CAAE,CACnE,CAIQ,QAAe,CACrB,GAAI,CAAC,KAAK,MAAO,OACjB,GAAM,CAAE,MAAAM,EAAO,UAAAC,EAAW,UAAAC,EAAW,WAAAC,CAAW,EAAI,KAAK,MACnD,CAAE,QAAAC,CAAQ,EAAI,KAAK,QACnB,CAAE,KAAAP,EAAM,KAAAE,EAAM,IAAAN,EAAK,MAAAY,CAAM,EAAI,KAAK,SAAS,EAC3CC,EAAQ,KAAK,UAAU,EACvBC,EAAY,KAAK,UAGjBC,EAAQ,KAAK,UACf,mBAAmBJ,CAAO,GAC1B,KAAK,WACHJ,EAAM,QAAU,YACd,kBACA,eACFM,EAAM,SAAW,EACf,QAAQF,CAAO,GACf,GAAGA,CAAO,yBACZK,EAAUH,EAAM,OAAS,GAAK,CAACC,EACrCG,EAAS,KAAK,OAAQ,CACpB,aAAcF,EACd,gBAAiBC,EAAU,OAAS,OACpC,gBAAiBA,EAAU,OAAO,KAAK,QAAQ,EAAI,OACnD,eAAgB,KAAK,WAAa,OAAOT,EAAM,QAAU,WAAW,EAAI,OACxE,YAAa,OAAOA,EAAM,QAAU,YAAY,CAClD,CAAC,EACD,KAAK,OAAO,SAAWG,IAAe,QAAU,CAACI,EACjD,KAAK,OAAO,MAAM,KAAO,GAAGR,CAAI,KAChC,KAAK,OAAO,MAAM,IAAM,GAAGN,CAAG,KAC9B,KAAK,OAAO,UAAU,OAAO,WAAY,KAAK,OAAS,IAAI,EAE3D,IAAMkB,EAAaJ,EAEd,CAAC,aAAc,WAAY,YAAa,UAAU,EAAY,KAAMK,GAAMA,IAAMZ,EAAM,KAAK,GAAK,YADjG,OAEJ,KAAK,KAAK,UAAY,QAAQW,CAAS,GACvC,KAAK,SAAS,EAEd,IAAME,GAAU,CAAC,KAAK,QAAQ,aAAc,KAAK,MAAM,gBAAiB,KAAK,UAAU,EAClFC,GAASD,GAAS,KAAK,OAAO,IACjC,KAAK,QAAUA,GACfE,EACE,KAAK,KACLC,EAAa,KAAK,QAAQ,aAAc,KAAK,MAAM,gBAAiB,KAAK,WAAaC,GAAQ,CAC5F,KAAK,WAAaA,EAClB,KAAK,OAAO,CACd,CAAC,CACH,GAGF,KAAK,KAAK,OAAS,CAACV,EACpB,KAAK,KAAK,MAAM,KAAO,GAAGR,GAAQF,IAAS,QAAU,IAAM7B,EAAW,EAAE,KACxE,KAAK,KAAK,MAAM,IAAM,GAAGyB,GAAOzB,EAAW,IAAM,CAAC,KAIlD,GAAM,CAAE,OAAA0B,EAAO,EAAI,KAAK,SAAS,EACjC,QAAWwB,IAAW,CAAC,KAAK,KAAM,KAAK,MAAO,KAAK,MAAM,EACvDA,EAAQ,MAAM,KAAOrB,IAAS,OAAS,GAAG,KAAK,QAAQ,OAAO,IAAI,KAAO,GACzEqB,EAAQ,MAAM,MAAQrB,IAAS,QAAU,GAAG,KAAK,QAAQ,OAAO,IAAI,KAAO,GAC3EqB,EAAQ,MAAM,OAASb,EAAQ,GAAGX,GAASD,EAAM,EAAE,KAAO,GAC1DyB,EAAQ,MAAM,IAAMb,EAAQ,GAAK,GAAGZ,EAAMzB,EAAW,EAAE,KAIzD,KAAK,KAAK,OAAS,CAAC,KAAK,SACzB,KAAK,KAAK,aAAa,aAAcoC,CAAO,EAC5C,KAAK,KAAK,MAAM,cAAgBC,EAAQ,iBAAmB,SAC3D,KAAK,KAAK,MAAM,WAAaR,IAAS,QAAU,WAAa,aACzD,KAAK,UAAY,KAAK,KAAK,oBAAsBS,EAAM,QACzDS,EACE,KAAK,KACL,GAAGT,EAAM,IAAKa,GACZnC,EACE,SACA,CAAE,MAAO,YAAa,MAAO,CAAE,KAAM,SAAU,KAAM,UAAW,EAAG,GAAI,CAAE,MAAO,IAAM,KAAK,OAAOmC,EAAK,GAAG,CAAE,CAAE,EAC9GnC,EACE,OACA,CAAE,MAAO,YAAa,MAAO,CAAE,cAAe,MAAO,CAAE,EACvDmC,EAAK,MAAQ,OAASC,GAAS,EAAID,EAAK,MAAQ,OAASE,GAAQ,EAAIC,GAAS,CAChF,EACAH,EAAK,KACP,CACF,CACF,EAKF,KAAK,MAAM,OAAS,KAAK,UAAYlB,EAAU,SAAW,GAEtDA,IAAc,KAAK,gBAAkB,KAAK,MAAM,YAAc,KAAK,cACrE,KAAK,eAAiBA,EACtB,KAAK,WAAa,KAAK,MAAM,UAC7Bc,EAAQ,KAAK,MAAO,GAAGd,EAAU,IAAKsB,GAAMC,EAAiBD,EAAG,KAAK,KAAK,kBAAmB,KAAK,MAAM,UAAUA,EAAE,UAAU,CAAC,CAAC,CAAC,GAGnI,IAAME,EACJzB,EAAM,QAAU,aACZ,CAAE,KAAM,kCAA8B,KAAM,OAAQ,EACpDE,IAAcK,GAAaP,EAAM,QAAU,SACzC,CAAE,KAAME,EAAW,KAAM,OAAQ,EACjCF,EAAM,OACJ,CAAE,KAAMA,EAAM,OAAQ,KAAM,OAAQ,EACpC,KAAK,YAAc,KAAK,UAAYA,EAAM,QAAU,YAClD,CAAE,KAAM,eAAgB,KAAM,OAAQ,EACtC,KACZ,KAAK,OAAO,OAAS,KAAK,UAAYC,EAAU,OAAS,GAAKwB,IAAW,KACzE,KAAK,OAAO,UAAU,OAAO,QAASA,GAAQ,OAAS,OAAO,EAC1D,KAAK,OAAO,eAAiBA,GAAQ,MAAQ,MAAK,KAAK,OAAO,YAAcA,GAAQ,MAAQ,GAClG,CAMQ,UAAiB,CACvB,IAAMC,EAAS,KAAK,WAAa,CAAC,KAAK,cACvC,GAAIA,KAAY,KAAK,YAAc,MAAO,OAC1C,GAAI,CAACA,EAAQ,CACX,KAAK,SAAS,EACd,MACF,CACA,IAAIC,EAAS,EACTC,EAAU,EACd,KAAK,UAAY,KAAK,KAAK,MAAM,QAAQ,CAACC,EAAQC,IAAU,CAC1D,IAAM9B,EAAQ,KAAK,KAAK,SAAS,EAAE,MAE/B6B,IAAW,SAAW7B,EAAM,QAAU,aAAeA,EAAM,aAAY2B,EAASG,GAChFD,IAAW,cAAaF,EAASG,EACvC,CAAC,EACD,IAAMC,EAAO,IAAM,CAEjBH,IAAYD,EAASC,IAAYD,EAASC,EAAU,IAAO,KAC3DD,GAAU,IACV,KAAK,KAAK,MAAM,UAAY,SAAS,EAAIC,EAAU,GAAI,IACvD,KAAK,UAAY,sBAAsBG,CAAI,CAC7C,EACA,KAAK,UAAY,sBAAsBA,CAAI,CAC7C,CAEQ,UAAiB,CACvB,KAAK,YAAY,EACjB,KAAK,UAAY,KACjB,qBAAqB,KAAK,SAAS,EACnC,KAAK,KAAK,MAAM,UAAY,UAC9B,CAIQ,QAAQC,EAAqB,CACnC,KAAK,SAAWA,EACXA,GAAMjB,EAAQ,KAAK,IAAI,EAC5B,KAAK,OAAO,EAERiB,GAAM,KAAK,KAAK,cAAc,QAAQ,GAAG,MAAM,CACrD,CAEQ,OAAOC,EAAoB,CACjC,KAAK,QAAQ,EAAK,EACdA,IAAQ,OACV,KAAK,KAAK,KAAK,EAIf,KAAK,KAAK,MAAM,MAAM,CAAE,UAAWA,IAAQ,aAAc,UAAW,EAAM,CAAC,CAE/E,CAEQ,cAAchD,EAA4B,CAChD,IAAMiD,EAAU,MAAM,KAAK,KAAK,KAAK,iBAAiB,QAAQ,CAAC,EACzDC,EAAU,KAAK,KAAK,YAAY,EAA4B,cAC5DC,EAAQF,EAAQ,QAAQC,CAA2B,EACrDlD,EAAM,MAAQ,UAChBA,EAAM,eAAe,EACrB,KAAK,QAAQ,EAAK,EAClB,KAAK,OAAO,MAAM,GACTA,EAAM,MAAQ,aAAeA,EAAM,MAAQ,cACpDA,EAAM,eAAe,EACrBiD,GAASE,EAAQ,GAAKF,EAAQ,MAAM,GAAG,MAAM,IACpCjD,EAAM,MAAQ,WAAaA,EAAM,MAAQ,eAClDA,EAAM,eAAe,EACrBiD,GAASE,EAAQ,EAAIF,EAAQ,QAAUA,EAAQ,MAAM,GAAG,MAAM,EAElE,CAIQ,KAAY,CAClB,GAAI,KAAK,SAAU,CACjB,KAAK,QAAQ,EAAK,EAClB,MACF,CACA,GAAI,KAAK,UAAW,CAGlB,KAAK,KAAK,MAAM,KAAK,EACrB,MACF,CACI,KAAK,aACL,KAAK,UAAU,EAAE,SAAW,EAAG,KAAK,KAAK,KAAK,EAC7C,KAAK,QAAQ,EAAI,EACxB,CAEQ,cAAcjD,EAA2B,CAC/C,GAAIA,EAAM,SAAW,EAAG,OACxB,KAAK,OAAO,oBAAoBA,EAAM,SAAS,EAC/C,GAAM,CAAE,KAAAc,EAAM,IAAAN,CAAI,EAAI,KAAK,SAAS,EAC9B4C,EAAe,CACnB,EAAGpD,EAAM,QACT,EAAGA,EAAM,QACT,QAASA,EAAM,QAAUc,EACzB,QAASd,EAAM,QAAUQ,EACzB,SAAU,GACV,UAAW,KACX,MAAO,IACT,EACA,KAAK,MAAQ4C,EAEb,IAAMrC,EAAQ,KAAK,KAAK,SAAS,EAAE,MAC/B,KAAK,aAAeA,EAAM,QAAU,aAAeA,EAAM,QAAU,cACjE,KAAK,QAAQ,WAEfA,EAAM,MAAM,CAAE,KAAM,EAAK,CAAC,EAC1BqC,EAAM,UAAY,OAClBA,EAAM,MAAQ,WAAW,IAAM,CACzB,KAAK,QAAUA,GAAS,CAACA,EAAM,WACjC,KAAK,KAAK,MAAM,QAAQ,EACxBA,EAAM,UAAY,OAEtB,EAAGlE,EAAc,IAEjB6B,EAAM,MAAM,EACZqC,EAAM,UAAY,QAGxB,CAEQ,cAAcpD,EAA2B,CAC/C,IAAMoD,EAAQ,KAAK,MAGnB,GAFI,CAACA,GAAS,CAAC,KAAK,QAAQ,WAAa,KAAK,UAE1CA,EAAM,YAAc,OAAQ,OAEhC,IAAMC,EAAQ,KAAK,MAAMrD,EAAM,QAAUoD,EAAM,EAAGpD,EAAM,QAAUoD,EAAM,CAAC,EASzE,GARI,CAACA,EAAM,UAAYC,EAAQpE,KAC7BmE,EAAM,SAAW,GACbA,EAAM,YAAc,SAClBA,EAAM,OAAO,aAAaA,EAAM,KAAK,EACzC,KAAK,KAAK,MAAM,QAAQ,EACxBA,EAAM,UAAY,OAGlBA,EAAM,SAAU,CAClB,GAAM,CAAE,MAAAzC,EAAO,OAAAF,CAAO,EAAI,KAAK,SAAS,EACxC,KAAK,KAAO,CACV,KAAM,KAAK,IAAI,KAAK,IAAI,EAAGT,EAAM,QAAUoD,EAAM,OAAO,EAAGzC,EAAQ5B,CAAQ,EAC3E,IAAK,KAAK,IAAI,KAAK,IAAI,EAAGiB,EAAM,QAAUoD,EAAM,OAAO,EAAG3C,EAAS1B,CAAQ,CAC7E,EACA,KAAK,OAAO,CACd,CACF,CAEQ,OAAOuE,EAA0B,CACvC,IAAMF,EAAQ,KAAK,MAEnB,GADA,KAAK,MAAQ,KACT,EAACA,EAGL,IAFIA,EAAM,OAAO,aAAaA,EAAM,KAAK,EAErCA,EAAM,UAAY,KAAK,KAAM,CAE/B,GAAM,CAAE,MAAAzC,EAAO,OAAAF,CAAO,EAAI,KAAK,SAAS,EACxC,KAAK,KAAO,CACV,KAAM,KAAK,KAAK,KAAO1B,EAAW,EAAI4B,EAAQ,EAAI,OAAS,QAC3D,EAAG,KAAK,SAAS,KAAK,KAAK,GAAG,EAAIF,CACpC,EACAjB,GAAU,KAAK,IAAI,EACnB,KAAK,KAAO,KACZ,KAAK,OAAO,EACZ,MACF,CAGA,GAFA,KAAK,KAAO,KAER4D,EAAM,UAAW,CAEnB,KAAK,KAAK,MAAM,QAAQ,EACxB,MACF,CACKE,GAAW,KAAK,IAAI,EAC3B,CAEQ,UAAUtD,EAA4B,CAC5C,GAAIA,EAAM,OAAQ,OAClB,IAAMe,EAAQ,KAAK,KAAK,SAAS,EAAE,MACnC,GAAI,KAAK,YAAcf,EAAM,MAAQ,IAAK,CACxCA,EAAM,eAAe,GACjBe,EAAM,QAAU,aAAeA,EAAM,QAAU,aAAYA,EAAM,MAAM,EAC3E,MACF,EACIf,EAAM,MAAQ,SAAWA,EAAM,MAAQ,OACzCA,EAAM,eAAe,EACrB,KAAK,IAAI,GAEPA,EAAM,MAAQ,UAAY,KAAK,YACjCA,EAAM,eAAe,EACrBe,EAAM,KAAK,EAEf,CAEQ,QAAQf,EAA4B,CACtC,KAAK,YAAcA,EAAM,MAAQ,MACnCA,EAAM,eAAe,EACrB,KAAK,KAAK,MAAM,QAAQ,EAE5B,CACF,EAEA,SAAS6B,GAAS0B,EAAYC,EAAqB,CACjD,OAAO,MAAM,QAAQD,CAAC,GAAK,MAAM,QAAQC,CAAC,GAAKD,EAAE,SAAWC,EAAE,QAAUD,EAAE,MAAM,CAACE,EAAG,IAAMA,IAAMD,EAAE,CAAC,CAAC,CACtG,CCriBA,IAAME,GAAS,oBACTC,GAAW,4BACXC,GAAU,kCAET,SAASC,GAAcC,EAAuB,CACnD,IAAMC,EAAkB,CAAC,EACrBC,EAAsB,CAAC,EACvBC,EAAoE,KAElEC,EAAQ,IAAM,CACdF,EAAU,QAAQD,EAAO,KAAK,CAAE,KAAM,YAAa,MAAOC,EAAU,IAAIG,CAAW,CAAE,CAAC,EACtFF,GAAMF,EAAO,KAAK,CAAE,KAAM,OAAQ,QAASE,EAAK,QAAS,MAAOA,EAAK,MAAO,MAAOA,EAAK,MAAM,IAAIE,CAAW,CAAE,CAAC,EACpHH,EAAY,CAAC,EACbC,EAAO,IACT,EAEA,QAAWG,KAAON,EAAK,QAAQ,SAAU;AAAA,CAAI,EAAE,MAAM;AAAA,CAAI,EAAG,CAC1D,IAAMO,EAAOD,EAAI,QAAQ,EACzB,GAAIC,EAAK,KAAK,IAAM,GAAI,CAAEH,EAAM,EAAG,QAAU,CAC7C,IAAMI,EAAUV,GAAQ,KAAKS,CAAI,EACjC,GAAIC,EAAS,CAAEJ,EAAM,EAAGH,EAAO,KAAK,CAAE,KAAM,UAAW,OAAQI,EAAYG,EAAQ,CAAC,GAAK,EAAE,CAAE,CAAC,EAAG,QAAU,CAC3G,IAAMC,EAASb,GAAO,KAAKW,CAAI,EACzBG,EAAWD,EAAS,KAAOZ,GAAS,KAAKU,CAAI,EACnD,GAAIE,GAAUC,EAAU,CACtB,IAAMC,EAAU,CAAC,CAACD,GACdR,EAAU,QAAWC,GAAQA,EAAK,UAAYQ,IAAUP,EAAM,EAC7DD,IAAMA,EAAO,CAAE,QAAAQ,EAAS,MAAOD,EAAW,OAAOA,EAAS,CAAC,CAAC,EAAI,EAAG,MAAO,CAAC,CAAE,GAClFP,EAAK,MAAM,MAAMM,EAASA,EAAO,CAAC,EAAIC,EAAU,CAAC,IAAM,EAAE,EACzD,QACF,CAEA,GAAIP,GAAQ,OAAO,KAAKG,CAAG,EAAG,CAAEH,EAAK,MAAMA,EAAK,MAAM,OAAS,CAAC,GAAK,IAAII,EAAK,KAAK,CAAC,GAAI,QAAU,CAC9FJ,GAAMC,EAAM,EAChBF,EAAU,KAAKK,CAAI,CACrB,CACA,OAAAH,EAAM,EACCH,CACT,CAGO,SAASW,GAASC,EAA6B,CACpD,IAAMC,EAAUD,EAAK,KAAK,EAC1B,MAAO,0BAA0B,KAAKC,CAAO,EAAIA,EAAU,IAC7D,CAEA,IAAMC,GAAS,+LAER,SAASV,EAAYL,EAAwB,CAClD,IAAMgB,EAAgB,CAAC,EACjBC,EAAQC,GAAiB,CAC7B,IAAMC,EAAOH,EAAIA,EAAI,OAAS,CAAC,EAC3BE,EAAK,OAAS,QAAUC,GAAM,OAAS,QAAU,CAACA,EAAK,MAAS,CAACD,EAAK,MAAQ,CAACC,EAAK,QAAW,CAACD,EAAK,OAAQC,EAAK,MAAQD,EAAK,KAC9HF,EAAI,KAAKE,CAAI,CACpB,EACIE,EAAK,EACT,QAAWC,KAAKrB,EAAK,SAASe,EAAM,EAAG,CACrC,IAAMO,EAAQD,EAAE,OAAS,EACrBC,EAAQF,GAAIH,EAAK,CAAE,KAAM,OAAQ,KAAMjB,EAAK,MAAMoB,EAAIE,CAAK,CAAE,CAAC,EAClE,GAAM,CAACC,EAAOC,EAAMC,EAAUZ,EAAMa,EAAOC,EAAOC,EAASC,CAAO,EAAIR,EACtE,GAAIG,IAAS,OAAWP,EAAK,CAAE,KAAM,OAAQ,KAAMO,CAAK,CAAC,UAChDC,IAAa,OAAW,CAC/B,IAAMK,EAAOlB,GAASC,GAAQ,EAAE,EAChCI,EAAKa,EAAO,CAAE,KAAM,OAAQ,KAAML,EAAU,KAAMK,CAAK,EAAI,CAAE,KAAM,OAAQ,KAAML,CAAS,CAAC,CAC7F,SAAWC,IAAU,QAAaC,IAAU,OAE1C,QAAWT,KAAQb,EAAYqB,GAASC,GAAS,EAAE,EAAGV,EAAKC,EAAK,OAAS,OAAS,CAAE,GAAGA,EAAM,KAAM,EAAK,EAAIA,CAAI,OACvDD,EAAhDW,IAAY,QAAaC,IAAY,OAAgB,CAAE,KAAM,OAAQ,KAAMD,GAAWC,GAAW,GAAI,OAAQ,EAAK,EACnH,CAAE,KAAM,OAAQ,KAAMN,CAAM,CADwF,EAE9HH,EAAKE,EAAQC,EAAM,MACrB,CACA,OAAIH,EAAKpB,EAAK,QAAQiB,EAAK,CAAE,KAAM,OAAQ,KAAMjB,EAAK,MAAMoB,CAAE,CAAE,CAAC,EAC1DJ,CACT,CC3FA,SAASe,EAAOC,EAAyB,CACvC,OAAOA,EAAM,IAAKC,GAAS,CACzB,GAAIA,EAAK,OAAS,OAAQ,OAAOC,EAAE,OAAQ,CAAC,EAAGD,EAAK,IAAI,EACxD,GAAIA,EAAK,OAAS,OAAQ,OAAOC,EAAE,IAAK,CAAE,MAAO,CAAE,KAAMD,EAAK,KAAM,OAAQ,SAAU,IAAK,YAAa,CAAE,EAAGA,EAAK,IAAI,EACtH,IAAIE,EAAa,SAAS,eAAeF,EAAK,IAAI,EAClD,OAAIA,EAAK,SAAQE,EAAOD,EAAE,KAAM,CAAC,EAAGC,CAAI,GACpCF,EAAK,OAAME,EAAOD,EAAE,SAAU,CAAC,EAAGC,CAAI,GACnCA,CACT,CAAC,CACH,CAEO,SAASC,GAAeC,EAA2B,CACxD,OAAOH,EACL,MACA,CAAE,MAAO,IAAK,EACd,GAAGI,GAAcD,CAAI,EAAE,IAAKE,GAAU,CACpC,GAAIA,EAAM,OAAS,UAAW,OAAOL,EAAE,IAAK,CAAC,EAAGA,EAAE,SAAU,CAAC,EAAG,GAAGH,EAAOQ,EAAM,MAAM,CAAC,CAAC,EACxF,GAAIA,EAAM,OAAS,OAAQ,CACzB,IAAMC,EAAQD,EAAM,MAAM,IAAKE,GAASP,EAAE,KAAM,CAAC,EAAG,GAAGH,EAAOU,CAAI,CAAC,CAAC,EACpE,OAAOF,EAAM,QAAUL,EAAE,KAAM,CAAE,MAAO,CAAE,MAAOK,EAAM,QAAU,EAAI,OAAYA,EAAM,KAAM,CAAE,EAAG,GAAGC,CAAK,EAAIN,EAAE,KAAM,CAAC,EAAG,GAAGM,CAAK,CACpI,CACA,OAAON,EAAE,IAAK,CAAC,EAAG,GAAGK,EAAM,MAAM,QAAQ,CAACG,EAAM,IAAO,EAAI,CAACR,EAAE,IAAI,EAAG,GAAGH,EAAOW,CAAI,CAAC,EAAIX,EAAOW,CAAI,CAAE,CAAC,CACxG,CAAC,CACH,CACF,CCVO,IAAMC,EAAmC,CAAC,aAAc,MAAM,EAWxDC,EAAN,KAAY,CAmBjB,YACmBC,EACTC,EACR,CAFiBC,EAAA,YAAAF,GACTE,EAAA,eAAAD,GApBVC,EAAA,KAAS,QACTA,EAAA,KAAiB,WACjBA,EAAA,KAAiB,OACjBA,EAAA,KAAiB,eACjBA,EAAA,KAAiB,cACjBA,EAAA,KAAiB,SACjBA,EAAA,KAAiB,cAEjBA,EAAA,KAAiB,QACjBA,EAAA,KAAiB,aACjBA,EAAA,KAAiB,SACjBA,EAAA,KAAQ,mBAA4B,MACpCA,EAAA,KAAiB,QACjBA,EAAA,KAAiB,QACjBA,EAAA,KAAiB,QAEjBA,EAAA,KAAQ,QAAmB,CAAC,GAM1B,KAAK,QAAU,SAAS,eAAe,EAAE,EACzC,KAAK,IAAMC,EAAE,OAAQ,CAAE,MAAO,aAAc,MAAO,CAAE,cAAe,MAAO,CAAE,CAAC,EAC9E,KAAK,YAAcA,EACjB,SACA,CACE,MAAO,cACP,MAAO,CAAE,KAAM,SAAU,aAAc,iBAAkB,EACzD,GAAI,CAAE,MAAO,IAAM,KAAK,KAAK,MAAM,CAAE,CACvC,EACA,QACF,EACA,KAAK,WAAaA,EAAE,MAAO,CAAE,MAAO,YAAa,CAAC,EAClD,KAAK,MAAQA,EAAE,QAAS,CACtB,MAAO,QACP,MAAO,CAAE,aAAc,SAAU,EACjC,GAAI,CACF,MAAO,IAAM,KAAK,SAAS,KAAK,KAAK,SAAS,CAAC,EAE/C,MAAQC,GAAU,CAChB,GAAI,CAAC,KAAK,KAAK,SAAS,EAAE,aAAc,OACxC,IAAMC,EAAQ,MAAM,KAAKD,EAAM,eAAe,OAAS,CAAC,CAAC,EACzD,GAAKC,EAAM,OACX,CAAAD,EAAM,eAAe,EACrB,QAAWE,KAAQD,EAAO,KAAK,KAAK,OAAOC,CAAI,EACjD,CACF,CACF,CAAC,EACD,KAAK,UAAYH,EAAE,QAAS,CAC1B,MAAO,CAAE,KAAM,OAAQ,SAAU,GAAM,OAAQ,EAAK,EACpD,GAAI,CACF,OAAQ,IAAM,CACZ,QAAWG,KAAQ,MAAM,KAAK,KAAK,UAAU,OAAS,CAAC,CAAC,EAAG,KAAK,KAAK,OAAOA,CAAI,EAChF,KAAK,UAAU,MAAQ,EACzB,CACF,CACF,CAAC,EACD,KAAK,KAAOH,EACV,SACA,CAAE,MAAO,OAAQ,MAAO,CAAE,KAAM,SAAU,aAAc,mBAAoB,MAAO,kBAAmB,EAAG,GAAI,CAAE,MAAO,IAAM,KAAK,UAAU,MAAM,CAAE,CAAE,EACrJI,GAAS,CACX,EACA,KAAK,MAAQJ,EAAE,KAAM,CAAE,MAAO,QAAS,MAAO,CAAE,aAAc,mBAAoB,CAAE,CAAC,EACrF,KAAK,WAAaA,EAAE,SAAU,CAAE,MAAO,OAAQ,MAAO,CAAE,KAAM,QAAS,CAAE,EAAG,MAAM,EAClF,KAAK,KAAOA,EAAE,SAAU,CAAE,MAAO,OAAQ,MAAO,CAAE,KAAM,SAAU,MAAO,wBAAyB,EAAG,GAAI,CAAE,MAAO,IAAM,CACtH,IAAMK,EAAQ,KAAK,KAAK,SAAS,EAAE,gBAAgB,YAAcV,EACjE,KAAK,KAAK,MAAM,MAAMU,EAAM,SAAS,YAAY,EAAI,CAAE,UAAW,EAAK,EAAI,MAAS,CACtF,CAAE,CAAE,EAAG,MAAM,EACb,KAAK,KAAO,IAAIC,GAAST,CAAI,EAE7B,IAAMU,EAAWP,EACf,OACA,CACE,MAAO,WACP,GAAI,CACF,OAASC,GAAU,CACjBA,EAAM,eAAe,EACrB,KAAK,KAAK,KAAK,KAAK,MAAM,KAAK,EAC/B,KAAK,MAAM,MAAQ,GACnB,KAAK,SAAS,KAAK,KAAK,SAAS,CAAC,CACpC,CACF,CACF,EACA,KAAK,KACL,KAAK,MACL,KAAK,WAEL,KAAK,SACP,EAEA,KAAK,KAAOD,EACV,UACA,CAAE,MAAO,QAAS,MAAO,CAAE,GAAIQ,CAAmB,CAAE,EACpDR,EAAE,SAAU,CAAE,MAAO,QAAS,EAAGA,EAAE,MAAO,CAAE,MAAO,cAAe,EAAG,KAAK,IAAK,KAAK,OAAO,EAAG,KAAK,KAAM,KAAK,WAAW,EACxH,KAAK,KAAOA,EAAE,MAAO,CAAE,MAAO,YAAa,EAAG,KAAK,WAAY,KAAK,MAAOO,CAAQ,EACpF,KAAK,KAAK,IACZ,CACF,CAEA,WAAWT,EAA6B,CACtC,KAAK,QAAUA,CACjB,CAEA,OAAOW,EAAwB,CAC7B,IAAMC,EAAU,KAAK,QAAQ,OAASD,EAAM,eAAiB,YAC7D,KAAK,QAAQ,KAAOC,EACpB,KAAK,KAAK,aAAa,aAAcA,CAAO,EAC5C,KAAK,IAAI,UAAU,OAAO,OAAQD,EAAM,aAAe,MAAM,EAC7D,KAAK,YAAY,OAAS,CAAC,KAAK,QAAQ,UACxC,KAAK,MAAM,YAAc,KAAK,QAAQ,aAAe,uCACrD,KAAK,MAAM,SAAWA,EAAM,aAAe,OAC3C,KAAK,KAAK,OAAS,CAACA,EAAM,aAC1B,KAAK,KAAK,SAAWA,EAAM,aAAe,OACtCA,EAAM,eAAc,KAAK,UAAU,OAASA,EAAM,cAClDA,EAAM,cAAgB,KAAK,mBAAoB,KAAK,iBAAmBA,EAAM,YAAa,KAAK,UAAUA,CAAK,GAClH,KAAK,SAASA,CAAK,EAInB,IAAMJ,EAAQI,EAAM,gBAAgB,YAAcd,EAC5CgB,EAAU,KAAK,QAAQ,WAAaF,EAAM,MAAM,WAAaJ,EAAM,OAAS,EAC5EO,EAAS,KAAK,QAAQ,WAAaH,EAAM,MAAM,QAAU,QAAUA,EAAM,MAAM,QAAU,QAC/F,KAAK,KAAK,OAAS,CAACE,GAAWC,EAC/B,KAAK,KAAK,SAAWH,EAAM,aAAe,OAC1C,KAAK,KAAK,OAASG,EACnB,KAAK,KAAK,OAAOH,EAAOG,CAAM,EAE9B,IAAMC,EAAS,CACbJ,EAAM,SACNA,EAAM,UACNA,EAAM,UACNA,EAAM,MAAM,QAAU,aACtBA,EAAM,MAAM,OACZA,EAAM,UACNA,EAAM,UACR,EACII,EAAO,MAAM,CAACC,EAAOC,IAAUD,IAAU,KAAK,MAAMC,CAAK,CAAC,IAC9D,KAAK,MAAQF,EACb,KAAK,eAAeJ,CAAK,EAC3B,CAEQ,SAASA,EAAwB,CACvC,IAAMO,EAAUP,EAAM,YAAY,KAAMQ,GAAMA,EAAE,SAAW,SAAS,EAC9DC,EAAQT,EAAM,YAAY,KAAMQ,GAAMA,EAAE,SAAW,OAAO,EAChE,KAAK,WAAW,SAAWR,EAAM,MAAQA,EAAM,aAAe,QAAUO,GAAY,CAAC,KAAK,MAAM,MAAM,KAAK,GAAK,CAACE,CACnH,CAEQ,UAAUT,EAAwB,CACxC,KAAK,MAAM,OAASA,EAAM,YAAY,SAAW,EACjDU,EACE,KAAK,MACL,GAAGV,EAAM,YAAY,IAAKQ,GACxBjB,EACE,KACA,CAAE,MAAOiB,EAAE,SAAW,SAAW,cAAgB,MAAO,EACxDA,EAAE,YAAcjB,EAAE,MAAO,CAAE,MAAO,CAAE,IAAKiB,EAAE,WAAY,IAAK,EAAG,CAAE,CAAC,EAClEjB,EACE,OACA,CAAE,MAAO,WAAY,EACrBA,EAAE,OAAQ,CAAE,MAAO,WAAY,EAAGiB,EAAE,IAAI,EACxCjB,EAAE,OAAQ,CAAE,MAAO,cAAe,MAAOiB,EAAE,SAAW,SAAW,CAAE,KAAM,OAAQ,EAAI,CAAC,CAAE,EAAGA,EAAE,SAAW,UAAY,gBAAaA,EAAE,SAAW,QAAU,QAAUA,EAAE,OAAS,kBAAkB,CACjM,EACAjB,EAAE,SAAU,CAAE,MAAO,cAAe,MAAO,CAAE,KAAM,SAAU,aAAc,UAAUiB,EAAE,IAAI,EAAG,EAAG,GAAI,CAAE,MAAO,IAAM,KAAK,KAAK,iBAAiBA,EAAE,EAAE,CAAE,CAAE,EAAG,QAAG,CAC/J,CACF,CACF,CACF,CAEQ,eAAeR,EAAwB,CAC7CU,EACE,KAAK,WACLV,EAAM,SAAS,SAAW,GACxBT,EACE,IACA,CAAE,MAAO,OAAQ,EACjB,iFACF,EACF,GAAGS,EAAM,SAAS,IAAKW,GACrBpB,EACE,MACA,CAAE,MAAOoB,EAAQ,OAAS,OAAS,cAAgB,kBAAmB,EACtEA,EAAQ,aAAeA,EAAQ,YAAY,OAAS,GAClDpB,EACE,MACA,CAAE,MAAO,eAAgB,EACzB,GAAGoB,EAAQ,YAAY,IAAKH,GAC1BA,EAAE,WAAajB,EAAE,MAAO,CAAE,MAAO,CAAE,IAAKiB,EAAE,WAAY,IAAKA,EAAE,KAAM,MAAOA,EAAE,IAAK,CAAE,CAAC,EAAIjB,EAAE,OAAQ,CAAE,MAAO,WAAY,EAAG,aAAMiB,EAAE,IAAI,EAAE,CAC1I,CACF,EACFG,EAAQ,OAAS,OAASA,EAAQ,KAAOC,GAAeD,EAAQ,IAAI,EACpEA,EAAQ,WAAaA,EAAQ,OAAS,IAAMpB,EAAE,OAAQ,CAAE,MAAO,UAAW,EAAG,gBAAW,EACxFoB,EAAQ,SAAWA,EAAQ,QAAQ,OAAS,GAG1CpB,EACE,MACA,CAAE,MAAO,SAAU,EACnB,GAAGoB,EAAQ,QAAQ,IAAKE,GACtBtB,EACE,OACA,CAAE,MAAO,QAAS,EAClBsB,EAAO,KAAO,gBAAgB,KAAKA,EAAO,GAAG,EACzCtB,EAAE,IAAK,CAAE,MAAO,CAAE,KAAMsB,EAAO,IAAK,OAAQ,SAAU,IAAK,qBAAsB,CAAE,EAAGA,EAAO,KAAK,EAClGA,EAAO,KACb,CACF,CACF,CACJ,CACF,EACA,GAAGb,EAAM,UAAU,IAAKc,GAAaC,EAAiBD,EAAU,KAAK,KAAK,kBAAmBd,EAAM,UAAUc,EAAS,UAAU,CAAC,CAAC,EAIlId,EAAM,MAAM,QAAU,cAAgBT,EAAE,MAAO,CAAE,MAAO,cAAe,EAAG,iCAA4B,EACtGS,EAAM,MAAM,QAAUT,EAAE,MAAO,CAAE,MAAO,cAAe,EAAGS,EAAM,MAAM,MAAM,EAC5EA,EAAM,WAAaT,EAAE,MAAO,CAAE,MAAO,OAAQ,EAAGS,EAAM,SAAS,EAC/DA,EAAM,aAAe,QACnBT,EAAE,MAAO,CAAE,MAAO,QAAS,EAAGS,EAAM,aAAe,SAAW,gBAAkB,kBAAa,CACjG,EACA,KAAK,WAAW,WAAW,CAAE,IAAK,KAAK,WAAW,aAAc,SAAU,QAAS,CAAC,CACtF,CACF,EAcMH,GAAN,KAAe,CAUb,YAA6BT,EAAY,CAAZE,EAAA,YAAAF,GAT7BE,EAAA,KAAS,QACTA,EAAA,KAAiB,SACjBA,EAAA,KAAiB,QACjBA,EAAA,KAAiB,QACjBA,EAAA,KAAiB,QACjBA,EAAA,KAAQ,OAAO,IACfA,EAAA,KAAQ,aAAsB,MAC9BA,EAAA,KAAQ,YAAiC,MAGvC,KAAK,MAAQC,EAAE,MAAO,CAAE,MAAO,YAAa,CAAC,EAC7C,KAAK,KAAOA,EAAE,OAAQ,CAAE,MAAO,WAAY,CAAC,EAC5C,KAAK,KAAOA,EAAE,OAAQ,CAAE,MAAO,YAAa,MAAO,CAAE,YAAa,QAAS,CAAE,CAAC,EAC9E,KAAK,KAAOyB,GAAW5B,CAAI,EAC3B,KAAK,KAAK,UAAU,IAAI,WAAW,EACnC,IAAM6B,EAAM1B,EAAE,SAAU,CAAE,MAAO,WAAY,MAAO,CAAE,KAAM,QAAS,EAAG,GAAI,CAAE,MAAO,IAAM,KAAK,KAAK,MAAM,KAAK,CAAE,CAAE,EAAG,KAAK,EACtH2B,EAAO3B,EAAE,OAAQ,CAAE,MAAO,YAAa,MAAO,CAAE,cAAe,MAAO,CAAE,EAAG,KAAK,KAAM4B,EAAS,GAAI,UAAW,SAAS,CAAC,EAC9H,KAAK,KAAO5B,EAAE,MAAO,CAAE,MAAO,OAAQ,MAAO,CAAE,KAAM,SAAU,aAAc,YAAa,CAAE,EAAG,KAAK,MAAOA,EAAE,MAAO,CAAE,MAAO,UAAW,EAAG2B,EAAM,KAAK,KAAM,KAAK,KAAMD,CAAG,CAAC,EAC3K,KAAK,KAAK,OAAS,EACrB,CAEA,OAAOjB,EAAkBoB,EAAmB,CAC1C,KAAK,KAAK,OAAS,CAACA,EACpB,IAAMC,EAAIrB,EAAM,MAChB,GAAI,CAACoB,EAAI,CAAE,KAAK,KAAO,GAAI,KAAK,YAAY,EAAG,KAAK,UAAY,KAAM,MAAQ,CAC1E,CAAC,KAAK,WAAa,CAAC,aAAa,kCAAkC,EAAE,UACvE,KAAK,UAAY,KAAK,KAAK,MAAM,QAAQ,CAACP,EAAQS,IAAU,CAC1D,IAAMC,EAAM,KAAK,KAAK,SAAS,EAAE,OAC7BV,IAAW,aAAeU,EAAI,WAAaA,EAAI,QAAU,eAAa,KAAK,KAAK,MAAM,UAAY,SAAS,EAAID,EAAQ,GAAI,IACjI,CAAC,GAECD,EAAE,UAAS,KAAK,KAAOA,EAAE,SAC7B,IAAMG,EAAWH,EAAE,QAAU,aAAe,kCAA+BA,EAAE,QAAU,WAAa,iBAAcA,EAAE,QAAU,YAAc,kBAAeA,EAAE,UAAaA,EAAE,QAAU,WAAa,GAAK,uBAA0B,eAC5NI,GAAQJ,EAAE,SAAW,KAAK,OAASrB,EAAM,WAAaqB,EAAE,QAAU,aAAerB,EAAM,UAAY,KAAOwB,GAAU,QAAQ,OAAQ,GAAG,EAAE,KAAK,EACpJ,KAAK,KAAK,YAAcC,EAAK,QAAU,GAAKA,EAAO,SAAIA,EAAK,MAAMA,EAAK,OAAS,EAAE,EAAE,QAAQ,SAAU,EAAE,CAAC,GACzG,KAAK,KAAK,MAAQA,EAClB,KAAK,KAAK,OAASJ,EAAE,WAAaA,EAAE,QAAU,aAC9C,KAAK,KAAK,YAAcA,EAAE,QAAU,YAAc,kBAAoB,eAClErB,EAAM,YAAc,KAAK,aAC3B,KAAK,WAAaA,EAAM,UACxBU,EAAQ,KAAK,MAAO,GAAGV,EAAM,UAAU,IAAK0B,GAAMX,EAAiBW,EAAG,KAAK,KAAK,kBAAmB1B,EAAM,UAAU0B,EAAE,UAAU,CAAC,CAAC,CAAC,EAEtI,CACF,EAeA,SAASV,GAAW5B,EAA+B,CACjD,IAAMuC,EAAQ,IAAM,CAClB,IAAM3B,EAAQZ,EAAK,SAAS,EAAE,MAAM,MAC9BwC,EAAa5B,IAAU,aAC7B,MAAO,CAAE,WAAA4B,EAAY,KAAM5B,IAAU,QAAUA,IAAU,SAAW,CAAC4B,CAAW,CAClF,EACMC,EAAQ,CAACC,EAAqBC,IAAuB,CACzD,GAAM,CAAE,WAAAH,EAAY,KAAAI,CAAK,EAAIL,EAAM,EACnC,GAAI,CAAAC,EACJ,IAAI,CAACI,EAAM,CACT5C,EAAK,MAAM,MAAM,EACjB,MACF,CACI2C,IAAc,QAAWD,EAAO,oBAAoBC,CAAS,EACjE3C,EAAK,MAAM,MAAM,EACnB,EACM6B,EAAM,IAAM,CACZU,EAAM,EAAE,MAAMvC,EAAK,MAAM,QAAQ,CACvC,EAEM0C,EAA4BvC,EAChC,SACA,CACE,MAAO,MACP,MAAO,CAAE,KAAM,QAAS,EACxB,GAAI,CACF,YAAcC,GAAUqC,EAAMC,EAAQtC,EAAM,SAAS,EACrD,UAAWyB,EACX,cAAeA,EACf,QAAUzB,GAAU,CACdA,EAAM,SACNA,EAAM,MAAQ,KAAOA,EAAM,MAAQ,WACrCA,EAAM,eAAe,EACrBqC,EAAMC,CAAM,EAEhB,EACA,MAAQtC,GAAU,EACZA,EAAM,MAAQ,KAAOA,EAAM,MAAQ,WACrCA,EAAM,eAAe,EACrByB,EAAI,EAER,CACF,CACF,EACA,WACF,EACA,OAAOa,CACT,CC/UO,IAAMG,GAAe,iBAgD5B,SAASC,GAAQC,EAAwBC,EAAiC,CACxE,IAAMC,EAAUD,GAAU,YAAcE,EACxC,MAAO,CACL,KAAMH,EAAQ,MAAQC,GAAU,MAAQ,MACxC,UAAWD,EAAQ,WAAaC,GAAU,WAAa,GACvD,OAAQD,EAAQ,QAAUC,GAAU,QAAU,eAC9C,WAAYD,EAAQ,WAAaA,EAAQ,WAAW,OAAQI,GAASF,EAAQ,SAASE,CAAI,CAAC,EAAIF,CACjG,CACF,CAGA,SAASG,GAAeC,EAAqC,CAC3D,IAAMC,EAAQC,GAAiBF,EAAQ,aAAaE,CAAI,GAAK,OACvDC,EAAQD,GAAkBF,EAAQ,aAAaE,CAAI,EAAID,EAAKC,CAAI,IAAM,QAAU,OAChFE,EAAQH,EAAK,aAAa,EAChC,MAAO,CACL,OAAQA,EAAK,QAAQ,EAErB,MAAOA,EAAK,SAAS,EACrB,YAAaA,EAAK,aAAa,EAC/B,aAAcE,EAAK,eAAe,EAClC,YAAaA,EAAK,cAAc,EAChC,UAAWA,EAAK,YAAY,EAC5B,KAAMF,EAAK,MAAM,EACjB,UAAWE,EAAK,WAAW,EAC3B,OAAQF,EAAK,QAAQ,EACrB,OACEA,EAAK,eAAe,IAAM,QAAaA,EAAK,aAAa,IAAM,OAC3D,OACA,CACE,OAAQA,EAAK,eAAe,IAAM,OAAY,OAAY,OAAOA,EAAK,eAAe,CAAC,EACtF,KAAMA,EAAK,aAAa,IAAM,OAAY,OAAY,OAAOA,EAAK,aAAa,CAAC,CAClF,EACN,WAAYG,IAAU,OAAY,OAAaA,EAAM,MAAM,QAAQ,EAAE,OAAO,OAAO,CACrF,CACF,CAEA,SAASC,GAAiBX,EAAuC,CAC/D,OAAO,OAAO,YAAY,OAAO,QAAQA,CAAO,EAAE,OAAO,CAAC,CAAC,CAAEY,CAAK,IAAMA,IAAU,MAAS,CAAC,CAC9F,CAGO,SAASC,IAA4B,CAC1C,GAAI,OAAO,eAAmB,KAAe,eAAe,IAAIf,EAAY,EAAG,OAE/E,MAAMgB,UAAgB,WAA4C,CAAlE,kCAgBEC,EAAA,KAAQ,YAAyB,MACjCA,EAAA,KAAQ,aAA4B,CAAC,GACrCA,EAAA,KAAQ,cAAmC,MAC3CA,EAAA,KAAQ,SAA4B,MACpCA,EAAA,KAAQ,QAAsB,MAC9BA,EAAA,KAAQ,MAAkB,MAC1BA,EAAA,KAAQ,WAAqC,MAC7CA,EAAA,KAAQ,cAAyB,CAAC,GAClCA,EAAA,KAAQ,aAA4B,MACpCA,EAAA,KAAQ,kBAAkB,IAE1B,IAAI,MAAoB,CACtB,OAAO,KAAK,SACd,CAEA,IAAI,KAAKH,EAAoB,CACvBA,IAAU,KAAK,YACnB,KAAK,oBAAoB,EACzB,KAAK,UAAYA,EACb,KAAK,aAAa,KAAK,iBAAiB,EAC9C,CAEA,IAAI,SAAyB,CAC3B,OAAO,KAAK,UACd,CAEA,IAAI,QAAQA,EAAsB,CAChC,KAAK,WAAa,CAAE,GAAGA,CAAM,EAC7B,KAAK,OAAO,CACd,CAEA,mBAA0B,CACnB,KAAK,SACR,KAAK,OAAS,KAAK,aAAa,CAAE,KAAM,MAAO,CAAC,EAChD,KAAK,OAAO,YAAYI,EAAE,QAAS,CAAC,EAAGC,EAAM,CAAC,GAEhD,KAAK,iBAAiB,CACxB,CAEA,sBAA6B,CAC3B,KAAK,oBAAoB,CAC3B,CAEA,0BAAiC,CAC/B,KAAK,OAAO,CACd,CAEQ,kBAAyB,CAC/B,GAAI,CAAC,KAAK,WAAa,KAAK,aAAe,CAAC,KAAK,OAAQ,OACzD,IAAMC,EAAO,KAAK,UAClB,KAAK,MAAQ,IAAIC,EAAMD,EAAM,CAAE,UAAW,GAAM,UAAW,EAAK,CAAC,EACjE,KAAK,OAAO,YAAY,KAAK,MAAM,IAAI,EACvC,KAAK,YAAcA,EAAK,UAAU,IAAM,KAAK,OAAO,CAAC,EAGjD,KAAK,SAAS,EAAE,aAAe,CAAC,KAAK,kBACvC,KAAK,gBAAkB,GACvBA,EAAK,KAAK,GAEZ,KAAK,OAAO,CACd,CAEQ,qBAA4B,CAClC,KAAK,cAAc,EACnB,KAAK,YAAc,KACnB,KAAK,KAAK,QAAQ,EAClB,QAAWE,IAAQ,CAAC,KAAK,OAAO,KAAM,KAAK,KAAK,KAAM,KAAK,QAAQ,EAAGA,GAAM,OAAO,EACnF,KAAK,MAAQ,KACb,KAAK,IAAM,KACX,KAAK,SAAW,KAChB,KAAK,YAAc,CAAC,CACtB,CAEQ,UAA0B,CAChC,MAAO,CAAE,GAAGT,GAAiBN,GAAe,IAAI,CAAC,EAAG,GAAGM,GAAiB,KAAK,UAAU,CAAE,CAC3F,CAEQ,QAAe,CACrB,IAAMO,EAAO,KAAK,UAClB,GAAI,CAACA,GAAQ,CAAC,KAAK,QAAU,CAAC,KAAK,MAAO,OAC1C,IAAMG,EAAQH,EAAK,SAAS,EACtBlB,EAAU,KAAK,SAAS,EACxBsB,EAASvB,GAAQC,EAASqB,EAAM,cAAc,EAC9CE,EAAUvB,EAAQ,OAASqB,EAAM,eAAiB,YAClDG,EAAS,CAAE,OAAQxB,EAAQ,QAAQ,QAAU,GAAI,KAAMA,EAAQ,QAAQ,MAAQ,EAAG,EAClFyB,EAAezB,EAAQ,cAAgB,GACvC0B,EAAQ,KAAK,MAEnB,GAAI1B,EAAQ,SAAW,SAAU,CAG3B,KAAK,aAAa,QAAQ,IAAM,UAAU,KAAK,aAAa,SAAU,QAAQ,EAClF0B,EAAM,WAAW,CACf,MAAO1B,EAAQ,MACf,YAAaA,EAAQ,YACrB,UAAWA,EAAQ,WAAa,GAChC,UAAW,EACb,CAAC,EACD0B,EAAM,KAAK,UAAU,OAAO,WAAY,OAAQ,OAAO,EACvDA,EAAM,KAAK,gBAAgB,OAAO,EAClCA,EAAM,KAAK,OAAS,GACpBA,EAAM,OAAOL,CAAK,EAClB,KAAK,QAAQ,EACT,KAAK,WAAU,KAAK,SAAS,OAAS,IAC1C,MACF,CAEA,GAAIC,EAAO,OAAS,MAAO,CACrB,KAAK,WAAU,KAAK,SAAS,OAAS,IACrC,KAAK,MACR,KAAK,IAAM,IAAIK,EAAIT,CAAI,EACvB,KAAK,OAAO,YAAY,KAAK,IAAI,IAAI,GAEvC,KAAK,IAAI,OAAOG,EAAO,CACrB,QAAAE,EACA,UAAWD,EAAO,UAClB,OAAQA,EAAO,OACf,WAAYA,EAAO,WACnB,aAActB,EAAQ,aACtB,OAAAwB,CACF,CAAC,EAGD,KAAK,IAAI,KAAK,OAASH,EAAM,QAAU,CAACI,EACxC,KAAK,UAAUJ,EAAM,OAAQ,KAAK,IAAI,KAAK,EAAGG,EAAQ,CACpD,MAAOxB,EAAQ,MACf,YAAaA,EAAQ,YACrB,UAAW,GACX,UAAW,EACb,CAAC,EACD,MACF,CAEA,KAAK,QAAQ,EACb,KAAK,UAAUqB,EAAM,OAAQ,QAASG,EAAQ,CAC5C,MAAOxB,EAAQ,MACf,YAAaA,EAAQ,YACrB,UAAW,GACX,UAAW,EACb,CAAC,EACD,IAAM4B,EAAW,KAAK,aAAaL,EAASvB,EAAQ,aAAcqB,EAAM,eAAe,EACvFO,EAAS,OAASP,EAAM,QAAU,CAACI,EACnCG,EAAS,MAAM,OAAS,GAAGJ,EAAO,MAAM,KACxCI,EAAS,MAAM,MAAQ,GAAGJ,EAAO,IAAI,IACvC,CAEQ,UACNK,EACAC,EACAN,EACAxB,EACA,CACA,IAAM0B,EAAQ,KAAK,MACnBA,EAAM,KAAK,OAAS,CAACG,EACrBH,EAAM,KAAK,UAAU,IAAI,UAAU,EACnCA,EAAM,KAAK,MAAM,OAAS,GAAGF,EAAO,MAAM,KAC1CE,EAAM,KAAK,MAAM,KAAOI,IAAS,OAAS,GAAGN,EAAO,IAAI,KAAO,GAC/DE,EAAM,KAAK,MAAM,MAAQI,IAAS,QAAU,GAAGN,EAAO,IAAI,KAAO,GAEjEE,EAAM,KAAK,MAAM,OAAS,2BAA2BF,EAAO,OAAS,EAAE,OACvEE,EAAM,WAAW1B,CAAO,EACxB0B,EAAM,OAAO,KAAK,UAAW,SAAS,CAAC,CACzC,CAEQ,SAAgB,CAClB,KAAK,MAAK,KAAK,IAAI,KAAK,OAAS,GACvC,CAEQ,aAAaH,EAAiBQ,EAAwBC,EAAuC,CAC9F,KAAK,WACR,KAAK,SAAWhB,EAAE,SAAU,CAC1B,MAAO,WACP,MAAO,CAAE,KAAM,SAAU,gBAAiB,aAAc,gBAAiB,OAAQ,EACjF,GAAI,CAAE,MAAO,IAAM,KAAK,WAAW,KAAK,CAAE,CAC5C,CAAC,EACD,KAAK,OAAQ,YAAY,KAAK,QAAQ,GAExCiB,EAAS,KAAK,SAAU,CAAE,aAAc,QAAQV,CAAO,EAAG,CAAC,EAC3D,IAAMW,EAAM,CAACH,EAAMC,EAAK,KAAK,UAAU,EACvC,OAAIE,EAAI,KAAK,CAACtB,EAAOuB,IAAUvB,IAAU,KAAK,YAAYuB,CAAK,CAAC,IAC9D,KAAK,YAAcD,EACnBE,EACE,KAAK,SACLC,EAAaN,EAAMC,EAAK,KAAK,WAAaM,GAAW,CACnD,KAAK,WAAaA,EAClB,KAAK,OAAO,CACd,CAAC,CACH,GAEK,KAAK,QACd,CACF,CA9MEvB,EADID,EACG,qBAAqB,CAC1B,SACA,UACA,cACA,gBACA,eACA,aACA,OACA,YACA,SACA,cACA,gBACA,aACF,GAmMF,eAAe,OAAOhB,GAAcgB,CAAO,CAC7C,CAYO,SAASyB,GACdrB,EACAlB,EAAmD,CAAC,EACrC,CACfa,GAAoB,EACpB,GAAM,CAAE,UAAA2B,EAAW,GAAGlB,CAAO,EAAItB,EAC3BM,EAAU,SAAS,cAAcR,EAAY,EACnD,OAAAQ,EAAQ,QAAUgB,EAClBhB,EAAQ,KAAOY,GACdsB,GAAa,SAAS,MAAM,YAAYlC,CAAO,EACzC,CAAE,QAAAA,EAAS,QAAS,IAAMA,EAAQ,OAAO,CAAE,CACpD",
4
+ "sourcesContent": ["/**\n * Wire protocol version, sent by both sides in the handshake.\n *\n * Must match `protocol::PROTOCOL_VERSION` in the runtime. Additive changes\n * (new optional fields, new message variants) do not bump this \u2014 receivers\n * ignore unknown fields and reject unknown message types without closing the\n * socket. Breaking changes bump it.\n */\nexport const PROTOCOL_VERSION = 1;\n\n/**\n * Sample rate of every audio frame on the voice socket, in both directions.\n *\n * Must match `protocol::VOICE_SAMPLE_RATE` in the runtime, which\n * scripts/sync-protocol.sh checks. It is the provider's native rate, so\n * agreeing on it here is what keeps a resampler out of a latency-critical\n * path \u2014 and disagreeing does not error, it just plays the assistant at the\n * wrong pitch, which is why the check exists.\n */\nexport const VOICE_SAMPLE_RATE = 24000;\n", "/**\n * The WebSocket client (doc 02).\n *\n * One persistent socket per session carries chat, commands, and\n * confirmations. Two properties drive the design:\n *\n * - A frontend action is a tool call that round-trips through the browser, so\n * the runtime pushes a command and blocks until this client acks it. Every\n * command MUST be answered, including on failure \u2014 an unanswered command\n * stalls the assistant's turn until the runtime's timeout fires.\n * - Confirming an action needs server-to-client push, which is why this is a\n * socket rather than a request per message.\n *\n * The client owns transport only. It never decides what a command means; the\n * provider does that and hands back a result.\n */\n\nimport type { ContextReport } from \"./debug\";\nimport {\n PROTOCOL_VERSION,\n type AssistantCommand,\n type ClientMessage,\n type KnowledgeSourceRef,\n type ProposalStatus,\n type ScreenContext,\n type ServerMessage,\n type WidgetSettings,\n} from \"@mihe-dev/protocol\";\n\nexport type ConnectionState =\n | \"idle\"\n | \"connecting\"\n | \"open\"\n | \"reconnecting\"\n | \"closed\";\n\n/** What the provider needs to react to. */\nexport type ClientEvent =\n | { type: \"state\"; state: ConnectionState }\n /**\n * Answered on every handshake, so it cannot drift out of step with the\n * connection the way a one-shot probe could.\n */\n | { type: \"voice_available\"; available: boolean }\n /**\n * What the assistant calls itself, from the app's settings.\n *\n * Re-sent on every handshake for the same reason `voice_available` is: a\n * rename takes effect on the next session rather than on the next deploy of\n * the host app.\n */\n | { type: \"assistant_name\"; name: string }\n /**\n * The customer's launcher icon from the app's settings, or `null` for the\n * Mihe mark. Emitted on every handshake, absent or not: unlike the name,\n * removing an icon has to take effect, and an absent field is how the\n * runtime says \"the default\".\n */\n | { type: \"launcher_icon\"; url: string | null }\n /**\n * How the widget presents itself, from the app's settings (amendment 61).\n * `null` from a runtime too old to send any, which leaves the SDK's own\n * defaults and the host's props in charge.\n */\n | { type: \"widget_settings\"; settings: WidgetSettings | null }\n | { type: \"turn_start\"; turnId: string }\n | { type: \"delta\"; turnId: string; text: string }\n | {\n type: \"complete\";\n turnId: string;\n message: string;\n /** Documentation the answer drew on. Empty when none was used. */\n sources: KnowledgeSourceRef[];\n }\n | { type: \"command\"; commandId: string; command: AssistantCommand }\n | { type: \"proposal\"; proposal: ProposalPayload }\n | {\n type: \"action_status\";\n proposalId: string;\n status: ProposalStatus;\n result?: unknown;\n error?: string;\n }\n | { type: \"error\"; code: string; message: string; recoverable: boolean }\n /**\n * A single-use permit for the voice socket, in reply to\n * `requestVoiceToken`. Short-lived by design \u2014 see doc 03.\n */\n | { type: \"voice_token\"; token: string; expiresAt: string }\n /** What one answer on this screen re-sends, when `debug` is on. */\n | { type: \"context_report\"; report: ContextReport };\n\n/**\n * A confirmation card's contents.\n *\n * Every human-readable field here comes from the runtime's registry, never\n * from model output \u2014 that is the prompt-injection firebreak, and it is why\n * the widget renders exclusively from this payload and never from the\n * assistant's prose.\n */\nexport interface ProposalPayload {\n proposalId: string;\n actionName: string;\n label: string;\n description: string;\n risk: \"low\" | \"medium\" | \"high\";\n input: Record<string, unknown>;\n inputSchema: unknown;\n expiresAt: string;\n uiConfirmationRequired: boolean;\n}\n\nexport interface ClientOptions {\n /** Runtime WebSocket URL, e.g. `wss://runtime.example.com/v1/ws`. */\n url: string;\n publicKey: string;\n /**\n * Fetches a user token from the host app's own backend, which mints it\n * signed with the app secret.\n *\n * Called on every connect and reconnect rather than once, so a token that\n * expired while the tab was backgrounded is replaced rather than replayed.\n */\n getUserToken: () => Promise<string>;\n sdkVersion: string;\n onEvent: (event: ClientEvent) => void;\n /**\n * Ask the runtime what each screen costs, reported after every screen\n * update (amendment 67). For building the integration, not for production.\n */\n debug?: boolean;\n /** Injectable for tests. */\n socketFactory?: (url: string) => WebSocketLike;\n /** Injectable for tests; defaults to exponential backoff with jitter. */\n reconnectDelay?: (attempt: number) => number;\n}\n\n/** The slice of `WebSocket` this client uses, so tests can supply a fake. */\nexport interface WebSocketLike {\n send(data: string): void;\n close(): void;\n onopen: ((event: unknown) => void) | null;\n onclose: ((event: unknown) => void) | null;\n onerror: ((event: unknown) => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n}\n\nconst MAX_RECONNECT_DELAY_MS = 30_000;\n\nexport class AssistantClient {\n private socket: WebSocketLike | null = null;\n private state: ConnectionState = \"idle\";\n private attempt = 0;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n /** Set when the caller closed deliberately, so we do not reconnect. */\n private stopped = false;\n private conversationId: string | null = null;\n /** Re-sent after every reconnect, so the runtime is never blind to context. */\n private lastScreen: ScreenContext | null = null;\n\n constructor(private readonly options: ClientOptions) {}\n\n get connectionState(): ConnectionState {\n return this.state;\n }\n\n async connect(): Promise<void> {\n if (this.state === \"connecting\" || this.state === \"open\") return;\n this.stopped = false;\n await this.open();\n }\n\n close(): void {\n this.stopped = true;\n this.clearTimers();\n this.socket?.close();\n this.socket = null;\n this.setState(\"closed\");\n }\n\n /** Send a user message. Silently dropped when offline \u2014 the widget shows the state. */\n sendMessage(text: string, clientMsgId: string): void {\n this.send({ type: \"message\", data: { text, clientMsgId } });\n }\n\n /**\n * Publish the current screen. Idempotent: a full snapshot that replaces\n * whatever the runtime held.\n */\n /**\n * Tell the runtime where the user is, or that they are somewhere it does not\n * know about.\n *\n * `null` matters as much as a screen. The host app registers the screens it\n * chooses to; every other page is one the runtime must be told it cannot\n * see. Left unsaid, the runtime keeps the last screen it heard about, and\n * the assistant names a page the user left \u2014 which is worse than admitting\n * it does not know, and quietly keeps that screen's scoped actions callable.\n */\n updateScreen(screen: ScreenContext | null): void {\n this.lastScreen = screen;\n this.send({ type: \"screen_update\", data: { screen } });\n }\n\n /**\n * Answer a command. Must be called for every command received, including\n * failures \u2014 the runtime is blocking a turn on this.\n */\n ackCommand(\n commandId: string,\n ok: boolean,\n result?: unknown,\n error?: string,\n ): void {\n this.send({\n type: \"command_result\",\n data: {\n commandId,\n ok,\n result: (result ?? null) as never,\n error: error ?? null,\n },\n });\n }\n\n confirmAction(\n proposalId: string,\n confirmed: boolean,\n editedInput?: Record<string, unknown>,\n ): void {\n this.send({\n type: \"action_confirm\",\n data: {\n proposalId,\n confirmed,\n editedInput: (editedInput ?? null) as never,\n },\n });\n }\n\n cancelTurn(turnId: string): void {\n this.send({ type: \"cancel\", data: { turnId } });\n }\n\n /**\n * Ask for a permit to open the voice socket.\n *\n * Requested on the chat socket rather than fetched over HTTP because this\n * session is what the permit is scoped to: the runtime hands back a token\n * bound to the very session actor holding this connection, so the voice\n * socket joins the conversation already in progress instead of starting a\n * second one beside it.\n *\n * Answered by a `voice_token` event, or by an `error` when voice is not\n * available for this app \u2014 which is the signal to hide the microphone\n * rather than offer one that fails on the first press.\n */\n requestVoiceToken(): void {\n this.send({ type: \"voice_token_request\" });\n }\n\n // ------------------------------------------------------------- internals\n\n private async open(): Promise<void> {\n this.setState(this.attempt === 0 ? \"connecting\" : \"reconnecting\");\n\n let token: string;\n try {\n token = await this.options.getUserToken();\n } catch (cause) {\n if (this.stopped) return;\n // The host app's token endpoint is down. Retrying is right \u2014 this is\n // usually transient \u2014 but the user should be told why it is quiet.\n this.emit({\n type: \"error\",\n code: \"user_token_unavailable\",\n message: \"Could not get a session token from the app.\",\n recoverable: true,\n });\n this.scheduleReconnect();\n return;\n }\n\n // Closed while the token was on its way. Opening anyway left a socket\n // nothing would ever close: a second live session, which React's\n // StrictMode made on every development mount by connecting, closing and\n // connecting again.\n if (this.stopped) return;\n\n const factory =\n this.options.socketFactory ??\n ((url: string) => new WebSocket(url) as unknown as WebSocketLike);\n\n const socket = factory(this.options.url);\n this.socket = socket;\n\n socket.onopen = () => {\n const auth: ClientMessage = {\n type: \"auth\",\n data: {\n publicKey: this.options.publicKey,\n userToken: token,\n sdkVersion: this.options.sdkVersion,\n protocolVersion: PROTOCOL_VERSION,\n conversationId: this.conversationId,\n ...(this.options.debug ? { debug: true } : {}),\n },\n };\n socket.send(JSON.stringify(auth));\n };\n\n socket.onmessage = (event) => {\n if (typeof event.data !== \"string\") return;\n let message: ServerMessage;\n try {\n message = JSON.parse(event.data) as ServerMessage;\n } catch {\n return;\n }\n this.handle(message);\n };\n\n socket.onclose = () => {\n this.clearTimers();\n if (this.stopped) {\n this.setState(\"closed\");\n return;\n }\n this.scheduleReconnect();\n };\n\n socket.onerror = () => {\n // `onclose` always follows, and that is where reconnect is handled.\n };\n }\n\n private handle(message: ServerMessage): void {\n switch (message.type) {\n case \"auth_ok\": {\n this.attempt = 0;\n this.conversationId = message.data.conversationId;\n this.emit({\n type: \"voice_available\",\n available: message.data.voiceAvailable ?? false,\n });\n // Only when the runtime actually sent one. An older runtime omits the\n // field, and overwriting a good name with \"\" would rename the panel to\n // nothing \u2014 worse than leaving the SDK's own default in place.\n if (message.data.assistantName) {\n this.emit({ type: \"assistant_name\", name: message.data.assistantName });\n }\n this.emit({\n type: \"launcher_icon\",\n url: message.data.launcherIconUrl ?? null,\n });\n this.emit({ type: \"widget_settings\", settings: message.data.widget ?? null });\n this.setState(\"open\");\n this.startHeartbeat(message.data.heartbeatIntervalMs);\n // The runtime replays nothing, so re-publish context immediately.\n if (this.lastScreen) this.updateScreen(this.lastScreen);\n break;\n }\n\n case \"auth_error\": {\n this.emit({\n type: \"error\",\n code: message.data.code,\n message: message.data.message,\n // A bad key or an unsupported protocol version will not fix itself.\n recoverable: false,\n });\n this.stopped = true;\n break;\n }\n\n case \"auth_expired\": {\n // Re-authenticate in place rather than tearing down the socket.\n void this.options\n .getUserToken()\n .then((token) =>\n this.send({\n type: \"auth\",\n data: {\n publicKey: this.options.publicKey,\n userToken: token,\n sdkVersion: this.options.sdkVersion,\n protocolVersion: PROTOCOL_VERSION,\n conversationId: this.conversationId,\n ...(this.options.debug ? { debug: true } : {}),\n },\n }),\n )\n .catch(() => this.socket?.close());\n break;\n }\n\n case \"turn_start\":\n this.emit({ type: \"turn_start\", turnId: message.data.turnId });\n break;\n\n case \"delta\":\n this.emit({\n type: \"delta\",\n turnId: message.data.turnId,\n text: message.data.text,\n });\n break;\n\n case \"complete\":\n this.emit({\n type: \"complete\",\n turnId: message.data.turnId,\n message: message.data.message,\n sources: message.data.sources ?? [],\n });\n break;\n\n case \"command\":\n this.emit({\n type: \"command\",\n commandId: message.data.commandId,\n command: message.data.command,\n });\n break;\n\n case \"action_proposal\":\n this.emit({\n type: \"proposal\",\n proposal: message.data as unknown as ProposalPayload,\n });\n break;\n\n case \"action_status\":\n this.emit({\n type: \"action_status\",\n proposalId: message.data.proposalId,\n status: message.data.status,\n result: message.data.result ?? undefined,\n error: message.data.error ?? undefined,\n });\n break;\n\n case \"error\":\n this.emit({\n type: \"error\",\n code: message.data.code,\n message: message.data.message,\n recoverable: message.data.recoverable,\n });\n break;\n\n case \"voice_token\":\n this.emit({\n type: \"voice_token\",\n token: message.data.token,\n expiresAt: message.data.expiresAt,\n });\n break;\n\n case \"context_report\":\n this.emit({ type: \"context_report\", report: message.data });\n break;\n\n case \"pong\":\n break;\n }\n }\n\n private send(message: ClientMessage): void {\n if (this.state !== \"open\" && message.type !== \"auth\") return;\n try {\n this.socket?.send(JSON.stringify(message));\n } catch {\n // A send on a closing socket throws; `onclose` handles recovery.\n }\n }\n\n private scheduleReconnect(): void {\n this.setState(\"reconnecting\");\n const delay =\n this.options.reconnectDelay?.(this.attempt) ?? backoff(this.attempt);\n this.attempt += 1;\n this.reconnectTimer = setTimeout(() => void this.open(), delay);\n }\n\n private startHeartbeat(intervalMs: number): void {\n this.clearHeartbeat();\n this.heartbeatTimer = setInterval(() => {\n this.send({ type: \"ping\" });\n }, intervalMs);\n }\n\n private clearHeartbeat(): void {\n if (this.heartbeatTimer !== null) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n }\n\n private clearTimers(): void {\n this.clearHeartbeat();\n if (this.reconnectTimer !== null) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n }\n\n private setState(state: ConnectionState): void {\n if (this.state === state) return;\n this.state = state;\n this.emit({ type: \"state\", state });\n }\n\n private emit(event: ClientEvent): void {\n this.options.onEvent(event);\n }\n}\n\n/**\n * Exponential backoff with jitter, capped.\n *\n * Jitter matters more than it looks: without it, every tab of every customer\n * reconnects in lockstep after a runtime restart and stampedes it.\n */\nfunction backoff(attempt: number): number {\n const base = Math.min(1000 * 2 ** attempt, MAX_RECONNECT_DELAY_MS);\n return base / 2 + Math.random() * (base / 2);\n}\n", "/**\n * How a proposal's input reads on the approval card, for every card Mihe\n * draws: the web component's and the React SDK's.\n *\n * One copy, because the card is the last thing a user reads before approving\n * something consequential, and two copies would drift in exactly the place\n * where \"what they were shown\" has to mean one thing.\n */\n\n/**\n * Render one field of a proposal for a person to read.\n *\n * This is the last thing a user sees before approving something consequential,\n * so `JSON.stringify` is not good enough \u2014 it was showing\n * `[{\"productName\":\"Chocolate Protein Loaf\",\"quantity\":3}]` where the sentence\n * \"3 \u00D7 Chocolate Protein Loaf\" was wanted. Someone who cannot read the card\n * cannot meaningfully consent to what it describes.\n *\n * Values still come entirely from the schema-validated input, never from model\n * prose, so this changes how the payload is *shown* and not what it can say.\n */\nexport function formatValue(value: unknown): string {\n if (value === null || value === undefined) return \"\u2014\";\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n if (Array.isArray(value)) {\n return value.map(formatValue).join(\", \") || \"\u2014\";\n }\n\n if (typeof value === \"object\") {\n const entries = Object.entries(value as Record<string, unknown>);\n\n // A quantity paired with a name is common enough \u2014 order lines, stock\n // adjustments, recipe amounts \u2014 to be worth reading as one.\n const quantity = entries.find(([key]) => /^(quantity|qty|count)$/i.test(key));\n // An id is not a product name. Only compact a labelled quantity; all\n // remaining fields still have to be visible before the user consents.\n const name = entries.find(([key, nested]) =>\n /(?:name|title|label)$|^(?:product|item)$/i.test(key) &&\n typeof nested === \"string\",\n );\n if (quantity && name) {\n const details = entries\n .filter(([key]) => key !== quantity[0] && key !== name[0])\n .map(([key, nested]) => `${humanizeKey(key)}: ${formatValue(nested)}`);\n return [\n `${formatValue(quantity[1])} \u00D7 ${formatValue(name[1])}`,\n ...details,\n ].join(\"; \");\n }\n\n return entries\n .map(([key, nested]) => `${humanizeKey(key)}: ${formatValue(nested)}`)\n .join(\", \");\n }\n\n return String(value);\n}\n\n/** `productName` and `order_number` both become \"Product name\". */\nexport function humanizeKey(key: string): string {\n const spaced = key\n .replace(/[_-]+/g, \" \")\n .replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n .trim();\n return spaced.charAt(0).toUpperCase() + spaced.slice(1).toLowerCase();\n}\n", "/**\n * What a confirmation card says (amendment 99), worked out once per proposal\n * and drawn by every card Mihe has: the web component's and the React SDK's.\n *\n * Two sources, both trusted, neither the model:\n *\n * 1. **The app's own description.** `describeProposal` (an option) is handed\n * the proposal, exactly the input that will run, and returns the lines to\n * show: before and after, money in the store's currency, thumbnails. It is\n * the app's code over the app's data, so it can say what a generic card\n * can't. It must describe the input it was given, never anything else.\n * 2. **The registry's display hints**, for an app that doesn't describe a\n * card. They live in the action's input schema as annotations: a summary\n * template at the root (`x-mihe-summary`), and on each field a `title`,\n * `x-mihe-format` (`money`, `yesno`, `date`) and `x-mihe-hidden`. Fields\n * are shown in the schema's order. Hide only an opaque id that has a\n * readable name beside it which the app checks: everything else that will\n * run must be on the card.\n *\n * With neither, the card is what it always was: every field, by its key.\n */\n\nimport type { ProposalPayload } from \"./client\";\nimport { formatValue, humanizeKey } from \"./widget/format\";\n\n/** One line of a card. */\nexport interface CardLine {\n /** What the line is about: \"Price\", \"Amber Soy Candle\". */\n label?: string;\n /** The value, or what it becomes. */\n text?: string;\n /** What it was before, shown struck through ahead of `text`. */\n before?: string;\n /** A list, each entry on its own line so each can be checked. */\n items?: string[];\n /** Pictures of what the line is about, small. http(s), blob: and data:image only. */\n images?: { src: string; alt?: string }[];\n}\n\n/** What `describeProposal` returns. */\nexport interface ProposalDescription {\n /** One line saying what will happen: \"Change Linen Shirt's price\". */\n summary?: string;\n lines: CardLine[];\n /** A short note under the lines: \"All start as drafts.\" */\n note?: string;\n /** The confirm button's words: \"Make 3 products\". Defaults to \"Confirm\". */\n confirmLabel?: string;\n}\n\n/**\n * Describes a proposal for its card, from the app's own data. Return null (or\n * throw) to fall back to the registry's hints. Called once per proposal; the\n * card waits for it, with Confirm disabled, for up to four seconds.\n */\nexport type DescribeProposal = (proposal: ProposalPayload) => ProposalDescription | null | undefined | Promise<ProposalDescription | null | undefined>;\n\n/** A card ready to draw. */\nexport interface CardView extends ProposalDescription {\n /** Where it came from: the app, the registry's hints, or neither. */\n source: \"app\" | \"hints\" | \"plain\";\n}\n\ntype Schema = { title?: unknown; properties?: Record<string, Schema>; items?: Schema; \"x-mihe-summary\"?: unknown; \"x-mihe-format\"?: unknown; \"x-mihe-hidden\"?: unknown };\nconst asSchema = (v: unknown): Schema => (v && typeof v === \"object\" ? (v as Schema) : {});\nconst hidden = (s: Schema) => s[\"x-mihe-hidden\"] === true;\nconst title = (key: string, s: Schema) => (typeof s.title === \"string\" && s.title.trim() ? s.title : humanizeKey(key));\n\n/** One value, as its field's hints say to show it. */\nexport function formatField(value: unknown, schema: unknown): string {\n const s = asSchema(schema);\n const fmt = s[\"x-mihe-format\"];\n if (fmt === \"yesno\" && typeof value === \"boolean\") return value ? \"Yes\" : \"No\";\n if (fmt === \"money\" && typeof value === \"number\") return value.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });\n if (fmt === \"date\" && typeof value === \"string\" && !Number.isNaN(Date.parse(value))) return new Date(value).toLocaleDateString(undefined, { day: \"numeric\", month: \"short\", year: \"numeric\" });\n if (Array.isArray(value)) return value.map((v) => formatField(v, s.items)).join(\", \") || \"\u2014\";\n if (value && typeof value === \"object\") {\n const props = s.properties ?? {};\n const keys = [...Object.keys(props).filter((k) => k in (value as object)), ...Object.keys(value as object).filter((k) => !(k in props))];\n const visible = keys.filter((k) => !hidden(asSchema(props[k])));\n // With hints, an object reads as its fields in order, labelled by their titles (\"Amber Candle \u00B7 Price 18\").\n if (Object.keys(props).length) return visible.map((k) => (k === visible[0] && typeof (value as any)[k] === \"string\" ? String((value as any)[k]) : `${title(k, asSchema(props[k]))} ${formatField((value as any)[k], props[k])}`)).join(\" \u00B7 \");\n return formatValue(value);\n }\n return formatValue(value);\n}\n\n/** `x-mihe-summary`, filled from the input: \"Change {productName}\" \u2192 \"Change Linen Shirt\". Null if a field it names is missing. */\nexport function fillSummary(template: unknown, input: Record<string, unknown>, schema: Schema): string | null {\n if (typeof template !== \"string\" || !template.trim()) return null;\n let missing = false;\n const out = template.replace(/\\{([A-Za-z0-9_]+)\\}/g, (_, key: string) => {\n const v = input[key];\n if (v === undefined || v === null || v === \"\") { missing = true; return \"\"; }\n return Array.isArray(v) ? String(v.length) : formatField(v, schema.properties?.[key]);\n });\n return missing ? null : out;\n}\n\n/** The card from the registry's hints (or, with none, every field by its key). */\nexport function hintedView(proposal: ProposalPayload): CardView {\n const schema = asSchema(proposal.inputSchema);\n const props = schema.properties ?? {};\n const input = proposal.input ?? {};\n const hinted = !!schema[\"x-mihe-summary\"] || Object.values(props).some((p) => { const s = asSchema(p); return typeof s.title === \"string\" || hidden(s) || s[\"x-mihe-format\"] !== undefined; });\n const keys = [...Object.keys(props).filter((k) => k in input), ...Object.keys(input).filter((k) => !(k in props))];\n const lines: CardLine[] = keys.filter((k) => !hidden(asSchema(props[k]))).map((k) => {\n const v = input[k];\n const label = hinted ? title(k, asSchema(props[k])) : humanizeKey(k);\n return Array.isArray(v) && v.length > 0 ? { label, items: v.map((x) => formatField(x, asSchema(props[k]).items)) } : { label, text: formatField(v, props[k]) };\n });\n const summary = fillSummary(schema[\"x-mihe-summary\"], input, schema) ?? undefined;\n return { source: hinted ? \"hints\" : \"plain\", ...(summary ? { summary } : {}), lines };\n}\n\nconst SAFE_SRC = /^(https?:|blob:|data:image\\/)/i;\n\n/** The app's description, made safe to draw (only plain strings, only picture addresses that can't run anything). */\nexport function appView(d: ProposalDescription): CardView {\n const str = (v: unknown) => (typeof v === \"string\" ? v : v === undefined || v === null ? undefined : String(v));\n return {\n source: \"app\",\n ...(str(d.summary) ? { summary: str(d.summary) } : {}),\n ...(str(d.note) ? { note: str(d.note) } : {}),\n ...(str(d.confirmLabel) ? { confirmLabel: str(d.confirmLabel) } : {}),\n lines: (Array.isArray(d.lines) ? d.lines : []).map((l) => ({\n ...(str(l.label) ? { label: str(l.label) } : {}),\n ...(str(l.text) !== undefined ? { text: str(l.text) } : {}),\n ...(str(l.before) !== undefined ? { before: str(l.before) } : {}),\n ...(Array.isArray(l.items) ? { items: l.items.map((x) => String(x)) } : {}),\n ...(Array.isArray(l.images) ? { images: l.images.filter((im) => im && typeof im.src === \"string\" && SAFE_SRC.test(im.src)).slice(0, 12).map((im) => ({ src: im.src, ...(str(im.alt) ? { alt: str(im.alt) } : {}) })) } : {}),\n })),\n };\n}\n\n/** How long a card waits for the app's description before drawing from the hints. */\nexport const DESCRIBE_TIMEOUT_MS = 4000;\n", "/**\n * The highlight and focus engine (spec \u00A712).\n *\n * Draws an overlay over a registered element rather than mutating the\n * element's own styles. Two reasons: the host app's CSS is not ours to touch,\n * and an overlay cannot leave residue if the SDK unmounts mid-tutorial.\n *\n * Elements are found by the `data-mihe-component` attribute that\n * `useAssistantComponent` spreads onto them. Never by CSS selector or\n * position \u2014 the assistant points at things the app has declared, and at\n * nothing else.\n */\n\nexport const COMPONENT_ATTRIBUTE = \"data-mihe-component\";\n\nconst OVERLAY_ID = \"mihe-highlight\";\nconst PADDING = 6;\n\n/**\n * Whether clicking this element does something, rather than merely selecting\n * it. Buttons and links act; inputs and containers receive.\n *\n * Read from the element rather than from the registry's `ComponentType`\n * because the overlay is drawn over whatever the app spread the attribute\n * onto, which is not always the element the declaration describes \u2014 a label\n * carrying the props for the input beside it, for instance.\n */\nfunction activates(element: HTMLElement | null): boolean {\n if (!element) return false;\n if (element.closest(\"button, a[href], [role='button'], [role='link']\")) return true;\n return false;\n}\n\nexport function findComponent(componentKey: string): HTMLElement | null {\n return document.querySelector<HTMLElement>(\n `[${COMPONENT_ATTRIBUTE}=\"${CSS.escape(componentKey)}\"]`,\n );\n}\n\nexport class HighlightEngine {\n private overlay: HTMLElement | null = null;\n private caption: HTMLElement | null = null;\n private target: HTMLElement | null = null;\n private tracking = false;\n private observer: MutationObserver | null = null;\n private frame = 0;\n\n /** Draw attention to a component. Returns false if it is not on the page. */\n highlight(componentKey: string, message?: string): boolean {\n const target = findComponent(componentKey);\n if (!target) return false;\n\n this.target = target;\n this.ensureOverlay();\n this.setCaption(message);\n this.position();\n this.startTracking();\n\n target.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n return true;\n }\n\n /** Move keyboard focus into a component, highlighting it too. */\n focus(componentKey: string, message?: string): boolean {\n const target = findComponent(componentKey);\n if (!target) return false;\n\n this.highlight(componentKey, message);\n // A wrapper may be registered rather than the input itself; focus the\n // first focusable descendant when the element cannot take focus.\n const focusable =\n target.matches(\"input, textarea, select, button, [tabindex]\")\n ? target\n : target.querySelector<HTMLElement>(\n \"input, textarea, select, button, [tabindex]\",\n );\n focusable?.focus();\n return true;\n }\n\n scrollTo(componentKey: string): boolean {\n const target = findComponent(componentKey);\n if (!target) return false;\n target.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n return true;\n }\n\n clear(): void {\n this.stopTracking();\n this.overlay?.remove();\n this.overlay = null;\n this.caption = null;\n this.target = null;\n }\n\n private ensureOverlay(): void {\n if (this.overlay) return;\n\n const overlay = document.createElement(\"div\");\n overlay.id = OVERLAY_ID;\n // `pointer-events: none` is load-bearing: the highlight must never\n // intercept the click it is asking the user to make.\n Object.assign(overlay.style, {\n position: \"fixed\",\n zIndex: \"2147483000\",\n pointerEvents: \"none\",\n border: \"2px solid #4f7cff\",\n borderRadius: \"8px\",\n boxShadow: \"0 0 0 9999px rgba(15, 23, 42, 0.45)\",\n transition: \"top 120ms ease, left 120ms ease, width 120ms ease, height 120ms ease\",\n } satisfies Partial<CSSStyleDeclaration>);\n\n const caption = document.createElement(\"div\");\n Object.assign(caption.style, {\n position: \"absolute\",\n left: \"0\",\n top: \"100%\",\n marginTop: \"8px\",\n maxWidth: \"260px\",\n padding: \"8px 10px\",\n borderRadius: \"8px\",\n background: \"#0f172a\",\n color: \"#f8fafc\",\n font: \"500 13px/1.4 system-ui, -apple-system, sans-serif\",\n display: \"none\",\n } satisfies Partial<CSSStyleDeclaration>);\n\n overlay.appendChild(caption);\n document.body.appendChild(overlay);\n\n this.overlay = overlay;\n this.caption = caption;\n }\n\n private setCaption(message?: string): void {\n if (!this.caption) return;\n if (message) {\n this.caption.textContent = message;\n this.caption.style.display = \"block\";\n } else {\n this.caption.textContent = \"\";\n this.caption.style.display = \"none\";\n }\n }\n\n private position = (): void => {\n if (!this.overlay || !this.target) return;\n\n // The element may have unmounted since the highlight was drawn.\n if (!this.target.isConnected) {\n this.clear();\n return;\n }\n\n const rect = this.target.getBoundingClientRect();\n Object.assign(this.overlay.style, {\n top: `${rect.top - PADDING}px`,\n left: `${rect.left - PADDING}px`,\n width: `${rect.width + PADDING * 2}px`,\n height: `${rect.height + PADDING * 2}px`,\n });\n };\n\n /** Coalesced, because a MutationObserver can fire many times per render. */\n private reposition = (): void => {\n if (this.frame) return;\n this.frame = requestAnimationFrame(() => {\n this.frame = 0;\n this.position();\n });\n };\n\n /** Escape is the universal \"get this off my screen\". */\n private onKeyDown = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") this.clear();\n };\n\n /**\n * Any click outside the target dismisses.\n *\n * The overlay dims the whole page, so it reads as a modal \u2014 but it is\n * `pointer-events: none`, so clicks reach the app underneath. Without this\n * the user clicks away, the app responds, and the dim stays: it looks\n * dismissible and is not. Clicks on the target itself are left alone, since\n * that is the click the highlight is asking for and the app's own handler\n * usually removes the highlight's reason for existing anyway.\n */\n /**\n * A click ends the highlight, with one exception that has an exception.\n *\n * Clicking away dismisses it: the user has moved on and a ring around\n * something they are no longer looking at is clutter.\n *\n * Clicking *inside* the target does not, because the target is often an\n * input the user has just been asked to fill \u2014 losing the marker the moment\n * they click into the field would undo the pointing.\n *\n * But if the target is something clicking *activates* \u2014 a button, a link \u2014\n * then the click was the action, and the highlight has done its job. Without\n * this the ring survives the thing it pointed at: the assistant highlighted\n * \"Receive Stock\", the user clicked, the form opened, and the outline stayed\n * behind the dialog pointing at a button nobody needed any more.\n */\n private onPointerDown = (event: Event): void => {\n const node = event.target;\n const inside = node instanceof Node && this.target?.contains(node);\n if (inside && !activates(this.target)) return;\n this.clear();\n };\n\n /**\n * Keep the overlay glued to its target, and remove it when the target goes.\n *\n * Scroll and resize are listened to rather than polled; `capture: true` on\n * scroll catches scrolling inside containers, not just the window.\n *\n * The MutationObserver is what makes the overlay mortal. `position` already\n * clears when the target has left the DOM, but it only ran on scroll and\n * resize \u2014 so a highlighted element that unmounted while the page sat still\n * (a form closing after submit, say) left the overlay stranded over the page\n * with no way to dismiss it.\n */\n private startTracking(): void {\n if (this.tracking) return;\n this.tracking = true;\n window.addEventListener(\"scroll\", this.position, { passive: true, capture: true });\n window.addEventListener(\"resize\", this.position, { passive: true });\n window.addEventListener(\"keydown\", this.onKeyDown);\n // Capture, so the app's own handlers cannot stop it from reaching us.\n window.addEventListener(\"pointerdown\", this.onPointerDown, { capture: true });\n\n this.observer = new MutationObserver(this.reposition);\n this.observer.observe(document.body, { childList: true, subtree: true });\n }\n\n private stopTracking(): void {\n if (!this.tracking) return;\n this.tracking = false;\n window.removeEventListener(\"scroll\", this.position, { capture: true });\n window.removeEventListener(\"resize\", this.position);\n window.removeEventListener(\"keydown\", this.onKeyDown);\n window.removeEventListener(\"pointerdown\", this.onPointerDown, { capture: true });\n\n this.observer?.disconnect();\n this.observer = null;\n if (this.frame) {\n cancelAnimationFrame(this.frame);\n this.frame = 0;\n }\n }\n}\n", "/**\n * Executing the commands the runtime sends.\n *\n * Every command names a registered target \u2014 a route the runtime resolved from\n * its screen catalogue, or a component key the app declared. Nothing here\n * takes a CSS selector or a coordinate, so there is no path by which the model\n * drives the UI directly (spec \u00A75.2).\n *\n * Every branch returns a result, including failures, because the runtime is\n * blocking a turn on the ack and the model needs to learn the truth. Reporting\n * \"the element was not on screen\" is far better than reporting nothing.\n */\n\nimport type { AssistantCommand } from \"@mihe-dev/protocol\";\n\nimport type { HighlightEngine } from \"./highlight\";\nimport { findComponent } from \"./highlight\";\nimport type { AssistantStore, QueryParamDeclaration, TableRow } from \"./store\";\n\n/** When a frontend action is warned about as slow. */\nexport const ACTION_SLOW_MS = 5_000;\n\n/**\n * How long the runtime waits for a command's answer by default\n * (`COMMAND_ACK_TIMEOUT` in the runtime's session.rs); an action's\n * `timeoutSeconds` or the app's setting can make it longer (amendment 103).\n * Past it, the assistant is told the command failed.\n */\nexport const RUNTIME_ACK_WAIT_MS = 10_000;\n\nexport interface CommandResult {\n ok: boolean;\n result?: unknown;\n error?: string;\n}\n\nexport interface CommandContext {\n store: AssistantStore;\n highlighter: HighlightEngine;\n /**\n * How the host app changes route. Supplied by the customer, because only\n * they know whether that is Next.js's router, React Router, or something\n * else. Defaults to a full page load, which works everywhere and is\n * noticeably worse \u2014 the docs should push people to wire this up.\n */\n navigate: (route: string) => void;\n /**\n * Resolves once the screen at `route` has published its context to the\n * runtime, or `false` if it has not done so within the timeout.\n *\n * Navigating is the one command whose effects are not finished when the\n * function returns: the router swaps the tree, React mounts the destination,\n * its hooks register, and only then does the new context travel. The runtime\n * re-reads the screen after every tool call to decide what the next step may\n * do, so acking before that lands would tell it the user is still on the\n * screen they just left \u2014 and the action they navigated *for* would be\n * filtered out as off-screen.\n *\n * Optional so a hand-built context still works; the provider always supplies\n * it.\n */\n screenPublished?: (route: string, options?: { fresh?: boolean }) => Promise<boolean>;\n /**\n * Resolves once a published screen shows `controlKey` set to `value`, or\n * `false` if none does within the timeout. A switch is acked only then, for\n * `screenPublished`'s reason: the runtime's next step reads the screen.\n */\n viewPublished?: (controlKey: string, value: string) => Promise<boolean>;\n /** Opens a registered modal, when the host app has modals to open. */\n /** Return `false` when the key is not one this app can open. */\n openModal?: (modalKey: string) => void | boolean;\n}\n\n/** Why a set of search parameters is not what the table declared, or null. */\nexport function checkQueryParams(\n declared: QueryParamDeclaration[],\n params: Record<string, string>,\n): string | null {\n for (const [key, value] of Object.entries(params)) {\n const param = declared.find((p) => p.key === key);\n if (!param) return `\"${key}\" is not something this table can be searched by.`;\n if (typeof value !== \"string\") return `The value for \"${key}\" must be a string.`;\n if (param.kind === \"enum\" && !param.values.includes(value)) {\n return `\"${value}\" is not one of \"${key}\"'s values.`;\n }\n if (param.kind === \"date\" && !isIsoDate(value)) {\n return `\"${value}\" is not a date in YYYY-MM-DD form.`;\n }\n if (param.kind === \"text\" && (value.length > MAX_QUERY_TEXT || /[\\u0000-\\u001f\\u007f]/.test(value))) {\n return `The search for \"${key}\" is too long or contains control characters.`;\n }\n }\n return null;\n}\n\n/** Matches the runtime's cap, `QUERY_TEXT_MAX_CHARS`. */\nconst MAX_QUERY_TEXT = 100;\n\nfunction isIsoDate(value: string): boolean {\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) return false;\n const parsed = new Date(`${value}T00:00:00Z`);\n return !Number.isNaN(parsed.getTime()) && parsed.toISOString().slice(0, 10) === value;\n}\n\nfunction currentPath(): string | null {\n return typeof window === \"undefined\" ? null : window.location.pathname;\n}\n\nfunction currentUrl(): string | null {\n return typeof window === \"undefined\"\n ? null\n : window.location.pathname + window.location.search;\n}\n\nexport async function executeCommand(\n command: AssistantCommand,\n ctx: CommandContext,\n): Promise<CommandResult> {\n switch (command.type) {\n case \"navigate\": {\n const { route } = command.data;\n // A query means the page's state is about to change \u2014 usually the same\n // page with new filters. The screen already published is then the\n // *old* view, and acking on it would have the model read the chart it\n // was asked to replace. So wait for a fresh publish, unless the URL is\n // already exactly this, in which case nothing will change and nothing\n // will be published.\n const fresh = route.includes(\"?\") && currentUrl() !== route;\n ctx.navigate(route);\n const arrived = (await ctx.screenPublished?.(route, { fresh })) ?? true;\n return arrived\n ? { ok: true, result: { route } }\n : {\n ok: true,\n result: {\n route,\n note: \"Navigation started, but the destination has not reported its context yet. Anything scoped to that screen may not be available for another moment.\",\n },\n };\n }\n\n case \"highlight\": {\n const { componentKey, message } = command.data;\n return ctx.highlighter.highlight(componentKey, message ?? undefined)\n ? { ok: true }\n : { ok: false, error: notOnPage(componentKey) };\n }\n\n case \"focus\": {\n const { componentKey, message } = command.data;\n return ctx.highlighter.focus(componentKey, message ?? undefined)\n ? { ok: true }\n : { ok: false, error: notOnPage(componentKey) };\n }\n\n case \"scroll_to\": {\n const { componentKey } = command.data;\n return ctx.highlighter.scrollTo(componentKey)\n ? { ok: true }\n : { ok: false, error: notOnPage(componentKey) };\n }\n\n case \"open_modal\": {\n const { modalKey } = command.data;\n if (!ctx.openModal) {\n return {\n ok: false,\n error:\n \"This app has not wired up modal opening, so that modal cannot be opened.\",\n };\n }\n // An explicit `false` means the host app knows about modals but not this\n // one \u2014 usually because the dialog belongs to a screen the user has\n // left. Reported as a failure rather than swallowed: the model's\n // standing rule is never to claim something happened without a tool\n // result saying so, and \"ok\" to an unopened form is exactly that claim.\n // `undefined` stays a success, so an implementation returning nothing\n // behaves as it always did.\n if (ctx.openModal(modalKey) === false) {\n return {\n ok: false,\n error: `There is no \\`${modalKey}\\` dialog on this screen. It may belong to a different one.`,\n };\n }\n return { ok: true };\n }\n\n case \"run_frontend_action\": {\n const { actionName, input } = command.data;\n const action = ctx.store.getAction(actionName);\n if (!action) {\n // The runtime believes this action is registered but no handler\n // mounted \u2014 usually a component that owns it is not on this screen.\n return {\n ok: false,\n error: `No handler is currently mounted for \"${actionName}\".`,\n };\n }\n\n // A handler that waits on the user (a confirmation dialog, a password,\n // a file picker) outlives the runtime's wait, and the assistant then\n // reports as failed an action the user goes on to finish. Nothing else\n // tells the developer, so the console does.\n const started = Date.now();\n const slow = setTimeout(() => {\n console.warn(\n `[mihe] The \"${actionName}\" handler has run for ${ACTION_SLOW_MS / 1000}s. ` +\n `The runtime waits ${RUNTIME_ACK_WAIT_MS / 1000}s unless the action (timeoutSeconds) or the app sets longer, then tells the assistant it failed. ` +\n \"If it is waiting for the user, open the UI they need and return at once (say what you opened); \" +\n \"they finish it there.\",\n );\n }, ACTION_SLOW_MS);\n const late = () => {\n if (Date.now() - started > RUNTIME_ACK_WAIT_MS) {\n // The SDK doesn't know this action's limit (amendment 103), only the default.\n console.warn(\n `[mihe] The \"${actionName}\" handler finished after ${Math.round((Date.now() - started) / 1000)}s. ` +\n `If that was past its time limit (${RUNTIME_ACK_WAIT_MS / 1000}s unless the action sets timeoutSeconds or the app sets longer), ` +\n \"the assistant was told it failed whatever the outcome. If it waits on the user, return before they act; if it is slow by nature, give it timeoutSeconds.\",\n );\n }\n };\n\n try {\n const result = await action.handler(\n (input ?? {}) as Record<string, unknown>,\n );\n late();\n return { ok: true, result: result ?? { done: true } };\n } catch (cause) {\n late();\n // The host app's handler threw. Its message is the most useful thing\n // to hand back, so the assistant can tell the user what went wrong.\n return { ok: false, error: describeError(cause) };\n } finally {\n clearTimeout(slow);\n }\n }\n\n case \"set_view\": {\n const { controlKey, value } = command.data;\n const control = ctx.store.getControl(controlKey);\n if (!control) {\n return { ok: false, error: `\"${controlKey}\" is not on the page right now.` };\n }\n // Checked here as well as in the runtime, so the app's `onChange` is\n // only ever handed a value it declared, whatever sent the command.\n if (!control.options.some((option) => option.value === value)) {\n return { ok: false, error: `\"${value}\" is not one of \"${controlKey}\"'s options.` };\n }\n\n try {\n await control.onChange(value);\n } catch (cause) {\n return { ok: false, error: describeError(cause) };\n }\n\n const shown = (await ctx.viewPublished?.(controlKey, value)) ?? true;\n return shown\n ? { ok: true, result: { showing: value } }\n : {\n ok: false,\n error: `The app was asked to show \"${value}\", but \"${controlKey}\" still reports another tab.`,\n };\n }\n\n case \"read_table\": {\n const { tableKey, offset, limit } = command.data;\n const table = ctx.store.getTable(tableKey);\n if (!table) {\n return { ok: false, error: `\"${tableKey}\" is not on the page right now.` };\n }\n try {\n const rows = table.read(offset, limit);\n return {\n ok: true,\n result: {\n rows,\n offset,\n loaded: table.loadedRows,\n ...(table.total === undefined ? {} : { total: table.total }),\n },\n };\n } catch (cause) {\n return { ok: false, error: describeError(cause) };\n }\n }\n\n case \"query_table\": {\n const { tableKey, params, limit, show } = command.data;\n const table = ctx.store.getTable(tableKey);\n if (!table) {\n return { ok: false, error: `\"${tableKey}\" is not on the page right now.` };\n }\n if (!table.query) {\n return { ok: false, error: `\"${tableKey}\" cannot be searched.` };\n }\n // Checked here as well as in the runtime, so the app's `run` is only\n // ever handed parameters it declared, whatever sent the command.\n const invalid = checkQueryParams(table.query.params, params);\n if (invalid) return { ok: false, error: invalid };\n\n let found: { rows: TableRow[]; total?: number };\n try {\n found = await table.query.run(params);\n } catch (cause) {\n return { ok: false, error: describeError(cause) };\n }\n\n let shown = false;\n if (show && table.query.show) {\n try {\n await table.query.show(params);\n // Acked once the page has published again, for `navigate`'s\n // reason: the model's next step reads the screen.\n shown = (await ctx.screenPublished?.(currentPath() ?? \"\", { fresh: true })) ?? true;\n } catch (cause) {\n return { ok: false, error: describeError(cause) };\n }\n }\n\n return {\n ok: true,\n result: {\n rows: found.rows.slice(0, limit),\n ...(found.total === undefined ? {} : { total: found.total }),\n shown,\n },\n };\n }\n\n case \"read_chart\": {\n const { chartKey, series, limit } = command.data;\n const chart = ctx.store.getChart(chartKey);\n if (!chart) {\n return { ok: false, error: `\"${chartKey}\" is not on the page right now.` };\n }\n try {\n return { ok: true, result: { series: chart.read(series ?? undefined, limit) } };\n } catch (cause) {\n return { ok: false, error: describeError(cause) };\n }\n }\n\n case \"clear_highlight\": {\n ctx.highlighter.clear();\n return { ok: true };\n }\n\n default: {\n // An unknown command type means the runtime is ahead of this SDK.\n // Reporting it beats silently doing nothing.\n const unknown = command as { type: string };\n return {\n ok: false,\n error: `This app's SDK does not understand the \"${unknown.type}\" command. It may need updating.`,\n };\n }\n }\n}\n\nfunction notOnPage(componentKey: string): string {\n const registeredElsewhere = Boolean(findComponent(componentKey));\n return registeredElsewhere\n ? `\"${componentKey}\" is on the page but not visible.`\n : `\"${componentKey}\" is not on the page right now.`;\n}\n\nfunction describeError(cause: unknown): string {\n if (cause instanceof Error) return cause.message;\n if (typeof cause === \"string\") return cause;\n return \"The action failed.\";\n}\n", "/**\n * The developer's view of what each screen costs (amendment 67).\n *\n * With `debug` on the provider, the runtime reports what one answer on the\n * current screen re-sends to the model, and this turns that report into a\n * console line someone can act on: which part is large, and which tools make\n * it so. Estimates, about four characters a token. The dashboard's usage\n * page has the real bill.\n */\n\nimport type { ContextSize, ScreenId } from \"@mihe-dev/protocol\";\n\nexport interface ContextReport {\n screenId?: ScreenId;\n chat: ContextSize;\n voice?: ContextSize;\n}\n\n/** How many of the largest tools to name. The rest are counted. */\nconst LARGEST = 5;\n\nexport function formatContextReport(report: ContextReport): string {\n const where = report.screenId ? `\\`${report.screenId}\\`` : \"an unregistered page\";\n const lines = [`[mihe] Each answer on ${where} re-sends about:`];\n lines.push(` typed: ${describe(report.chat)}`);\n if (report.voice) lines.push(` voice: ${describe(report.voice)}`);\n\n const largest = (report.voice ?? report.chat).tools.slice(0, LARGEST);\n if (largest.length > 0) {\n lines.push(\n ` largest tools: ${largest.map((tool) => `${tool.name} ${tool.tokens}`).join(\", \")}`,\n );\n }\n return lines.join(\"\\n\");\n}\n\nfunction describe(size: ContextSize): string {\n const total = size.instructionTokens + size.toolTokens + size.screenTokens;\n return (\n `${total.toLocaleString(\"en\")} tokens \u2014 instructions ${size.instructionTokens}, ` +\n `${size.tools.length} tools ${size.toolTokens}, this screen ${size.screenTokens}`\n );\n}\n", "/**\n * Chart summaries, worked out where the numbers are (amendment 67, step 4).\n *\n * The model explains a trend from these figures, so it is handed the start,\n * end, high, low, total and largest steps rather than a list to do arithmetic\n * over, which models get wrong.\n */\n\nimport type { ChartPoint, ChartStep, SeriesSummary } from \"@mihe-dev/protocol\";\n\nexport function summarise(points: ChartPoint[], ordered: boolean): SeriesSummary | undefined {\n const [first] = points;\n if (!first) return undefined;\n const last = points[points.length - 1]!;\n\n let min = first;\n let max = first;\n let total = 0;\n let rise: ChartStep | undefined;\n let fall: ChartStep | undefined;\n\n points.forEach((point, i) => {\n total += point.y;\n if (point.y < min.y) min = point;\n if (point.y > max.y) max = point;\n if (!ordered || i === 0) return;\n const previous = points[i - 1]!;\n const change = point.y - previous.y;\n if (change > 0 && (!rise || change > rise.change)) {\n rise = { from: previous.x, to: point.x, change };\n }\n if (change < 0 && (!fall || change < fall.change)) {\n fall = { from: previous.x, to: point.x, change };\n }\n });\n\n return {\n points: points.length,\n first,\n last,\n min,\n max,\n total: Math.round(total * 100) / 100,\n ...(rise ? { largestRise: rise } : {}),\n ...(fall ? { largestFall: fall } : {}),\n };\n}\n\n/**\n * At most `limit` points, evenly spaced, keeping both ends. The summary\n * already carries the high and low, which thinning could skip.\n */\nexport function thin<T>(points: T[], limit: number): T[] {\n if (points.length <= limit) return points;\n if (limit <= 1) return points.slice(0, 1);\n const step = (points.length - 1) / (limit - 1);\n return Array.from({ length: limit }, (_, i) => points[Math.round(i * step)]!);\n}\n", "/**\n * What an app declares about tabs, tables and charts, and how each becomes\n * what the store holds (amendment 67, doc 08).\n *\n * Shared by the JavaScript API (`mihe.registerTable(\u2026)`) and the React hooks\n * (`useAssistantTable(\u2026)`), so the two can't send the model different things\n * for the same table. Each builder takes `latest`, a function returning the\n * declaration as it is now: rows, closures and data are read at the moment of\n * a read rather than captured at registration, so a table that re-renders\n * every second doesn't re-register every second.\n */\n\nimport type { ChartPoint } from \"@mihe-dev/protocol\";\n\nimport { summarise, thin } from \"./chart\";\nimport type {\n ChartDeclaration,\n QueryParamDeclaration,\n TableDeclaration,\n ViewControlDeclaration,\n} from \"./store\";\n\n/** A set of tabs the assistant can see and switch. */\nexport interface AssistantTabs {\n /** Unique on the screen, e.g. `reports.tabs`. The model refers to it by this. */\n id: string;\n /** What the tabs choose between, for the model: \"Report\", \"Period\". */\n label: string;\n tabs: { value: string; label: string }[];\n /** The tab showing now. */\n value: string | undefined;\n /** Switch to `value`. Called with one of `tabs`' values and nothing else. */\n onChange: (value: string) => void | Promise<void>;\n}\n\n/** A column a table exposes. Undeclared fields are never sent. */\nexport interface AssistantTableColumn<Row> {\n key: string;\n /** What the model calls it, and the key each row's cell is read under. */\n label: string;\n /** The cell's value. Defaults to `row[key]`. */\n value?: (row: Row) => unknown;\n}\n\n/** A table the assistant can read. */\nexport interface AssistantTable<Row> {\n /** Unique on the screen, e.g. `orders.table`. */\n id: string;\n label: string;\n columns: AssistantTableColumn<Row>[];\n /** The rows loaded now \u2014 the page on screen, for a paged table. */\n rows: Row[];\n rowId: (row: Row) => string;\n /**\n * The action input a row's id fills, such as `orderId`. The runtime tells\n * the model which actions accept it, from your registry and the user's\n * permissions. Leave it out for rows nothing acts on.\n */\n rowIdName?: string;\n /**\n * The screen a row opens, and the route params that open it:\n * `{ screenId: \"orders.detail\", params: (order) => ({ id: order.id }) }`.\n * Each row the model reads then carries exactly those params, so it never\n * has to guess which of a record's ids the link wants.\n */\n opens?: { screenId: string; params: (row: Row) => Record<string, string> };\n /** Rows in the whole list, when `rows` is one page of it. */\n total?: number;\n /**\n * Search past the loaded rows, with your own API and the user's own\n * session (amendment 67, step 3). The model fills in `params`; only the\n * keys you declare, and for an enum only its values, ever reach `run`.\n *\n * ```ts\n * query: {\n * params: {\n * search: { kind: \"text\", description: \"order number or customer name\" },\n * status: { kind: \"enum\", values: [\"pending\", \"paid\"] },\n * from: { kind: \"date\" },\n * },\n * run: async (p) => {\n * const res = await fetchOrders({ search: p.search, status: p.status, dateFrom: p.from });\n * return { rows: res.data, total: res.meta.total };\n * },\n * // Optional. Make the page show this search \u2014 set its filters and\n * // search box. Without it, results are read but not shown.\n * show: (p) => { setSearch(p.search ?? \"\"); setStatus(p.status ?? \"all\"); },\n * }\n * ```\n *\n * It only reads. A search is in `navigate`'s class: it changes what is\n * shown, never what is stored.\n */\n query?: AssistantTableQuery<Row>;\n}\n\n/** A search parameter: what the model may fill in. */\nexport type AssistantQueryParam =\n | { kind: \"text\"; description?: string }\n | { kind: \"enum\"; values: readonly string[]; description?: string }\n | { kind: \"date\"; description?: string };\n\n/** How a table is searched beyond the rows it has loaded. */\nexport interface AssistantTableQuery<Row> {\n params: Record<string, AssistantQueryParam>;\n run: (params: Record<string, string>) => Promise<{ rows: Row[]; total?: number }>;\n show?: (params: Record<string, string>) => void | Promise<void>;\n}\n\n/** A chart the assistant can explain. */\nexport interface AssistantChart<Datum> {\n /** Unique on the screen, e.g. `stock.trend`. */\n id: string;\n title: string;\n /** The rows the chart is drawn from: the same array your chart library gets. */\n data: Datum[];\n x: {\n key: keyof Datum & string;\n /** What the axis is: \"Month\", \"Product\". */\n label: string;\n /**\n * `ordered` for time and anything else with a sequence, `category` for a\n * set of things. Rises and falls are worked out only for the first.\n * Defaults to `ordered`.\n */\n kind?: \"ordered\" | \"category\";\n /** How an x value reads, such as \"Mar 26\" for \"2026-03-01\". */\n format?: (value: unknown) => string;\n };\n series: { key: keyof Datum & string; name: string; unit?: string }[];\n}\n\n/** Long cells are cut here: a notes field should not cost a page of tokens. */\nconst MAX_CELL_CHARS = 200;\n\nexport function toCell(value: unknown): string | number | boolean | null {\n if (value === null || value === undefined) return null;\n if (typeof value === \"number\" || typeof value === \"boolean\") return value;\n const text =\n value instanceof Date\n ? value.toISOString()\n : typeof value === \"string\"\n ? value\n : JSON.stringify(value);\n return text.length > MAX_CELL_CHARS ? `${text.slice(0, MAX_CELL_CHARS)}\u2026` : text;\n}\n\n/** The tabs as the store holds them. `onChange` is read at call time. */\nexport function tabsDeclaration(latest: () => AssistantTabs): ViewControlDeclaration {\n const tabs = latest();\n return {\n kind: \"tabs\",\n label: tabs.label,\n options: tabs.tabs.map(({ value, label }) => ({ value, label })),\n value: tabs.value,\n onChange: (value) => latest().onChange(value),\n };\n}\n\n/**\n * What identifies a tab set's declaration, without its closure: when this\n * changes, the store needs the new declaration.\n */\nexport function tabsSignature(tabs: AssistantTabs): string {\n return JSON.stringify({\n label: tabs.label,\n tabs: tabs.tabs.map(({ value, label }) => ({ value, label })),\n value: tabs.value,\n });\n}\n\n/** The query parameters a table accepts, as the store holds them. */\nfunction queryParams(query: AssistantTableQuery<unknown>): QueryParamDeclaration[] {\n return Object.entries(query.params).map(([key, param]) => ({\n key,\n kind: param.kind,\n values: param.kind === \"enum\" ? [...param.values] : [],\n description: param.description ?? \"\",\n }));\n}\n\n/**\n * What identifies a table's declaration, without its rows or closures: when\n * this changes, the store needs the new declaration. The rows' count is in\n * it, because the screen context reports how many are loaded.\n */\nexport function tableSignature<Row>(table: AssistantTable<Row>): string {\n return JSON.stringify({\n label: table.label,\n columns: table.columns.map(({ key, label }) => ({ key, label })),\n rowIdName: table.rowIdName,\n opens: table.opens?.screenId,\n total: table.total,\n loaded: table.rows.length,\n query: table.query\n ? {\n params: queryParams(table.query as AssistantTableQuery<unknown>),\n canShow: table.query.show !== undefined,\n }\n : undefined,\n });\n}\n\n/** The table as the store holds it. Rows and closures are read at call time. */\nexport function tableDeclaration<Row>(latest: () => AssistantTable<Row>): TableDeclaration {\n const table = latest();\n\n // One rendering for rows read and rows found, so a search result looks\n // exactly like a read: declared columns only, cells cut, `open` params.\n const render = (row: Row) => {\n const { rowId, columns, opens } = latest();\n const out: Record<string, string | number | boolean | null> = {};\n for (const column of columns) {\n const raw = column.value ? column.value(row) : (row as Record<string, unknown>)[column.key];\n out[column.label] = toCell(raw);\n }\n return {\n id: rowId(row),\n ...(opens ? { open: opens.params(row) } : {}),\n ...out,\n };\n };\n\n return {\n label: table.label,\n columns: table.columns.map(({ key, label }) => ({ key, label })),\n rowIdName: table.rowIdName,\n opens: table.opens?.screenId,\n total: table.total,\n loadedRows: table.rows.length,\n read: (offset, limit) => latest().rows.slice(offset, offset + limit).map(render),\n ...(table.query\n ? {\n query: {\n params: queryParams(table.query as AssistantTableQuery<unknown>),\n run: async (params: Record<string, string>) => {\n const query = latest().query;\n if (!query) throw new Error(\"This table can no longer be searched.\");\n const found = await query.run(params);\n return {\n rows: found.rows.map(render),\n ...(found.total === undefined ? {} : { total: found.total }),\n };\n },\n ...(table.query.show\n ? { show: (params: Record<string, string>) => latest().query?.show?.(params) }\n : {}),\n },\n }\n : {}),\n };\n}\n\n/** Each series' points, read from the chart as it is now. */\nfunction chartPoints<Datum>(chart: AssistantChart<Datum>, key: string): ChartPoint[] {\n const format = chart.x.format;\n return chart.data.flatMap((datum) => {\n const raw = (datum as Record<string, unknown>)[key];\n const y = typeof raw === \"number\" ? raw : raw == null ? NaN : Number(raw);\n if (!Number.isFinite(y)) return [];\n const xRaw = (datum as Record<string, unknown>)[chart.x.key];\n return [{ x: format ? format(xRaw) : String(xRaw), y }];\n });\n}\n\n/**\n * The chart as the store holds it: each series summarised here, in the\n * browser, and the points read at call time when an exact value is wanted.\n */\nexport function chartDeclaration<Datum>(latest: () => AssistantChart<Datum>): ChartDeclaration {\n const chart = latest();\n const ordered = chart.x.kind !== \"category\";\n return {\n title: chart.title,\n xLabel: chart.x.label,\n xKind: ordered ? \"ordered\" : \"category\",\n series: chart.series.map((s) => {\n const summary = summarise(chartPoints(chart, s.key), ordered);\n return {\n name: s.name,\n ...(s.unit ? { unit: s.unit } : {}),\n ...(summary ? { summary } : {}),\n };\n }),\n read: (name, limit) => {\n const current = latest();\n return current.series\n .filter((s) => name === undefined || s.name === name)\n .map((s) => ({ name: s.name, points: thin(chartPoints(current, s.key), limit) }));\n },\n };\n}\n\n/** What identifies a chart's declaration: its summaries, not its closures. */\nexport function chartSignature<Datum>(chart: AssistantChart<Datum>): string {\n const { read: _read, ...declared } = chartDeclaration(() => chart);\n return JSON.stringify(declared);\n}\n", "/**\n * The app's declaration of itself.\n *\n * Spec \u00A75.1 \u2014 the host app publishes assistant-readable context rather than\n * the assistant scraping the DOM. This store is where those declarations\n * accumulate: the current screen from `useAssistantScreen`, the components\n * that mounted under it from `useAssistantComponent`, and the action handlers\n * from `useAssistantAction`.\n *\n * Deliberately framework-free and dependency-free. React binds to it through\n * `useSyncExternalStore`, which keeps the store itself testable without a\n * renderer and avoids pulling a state library into a published SDK.\n */\n\nimport type {\n ChartPoint,\n ChartSeries,\n ChartXKind,\n ComponentContext,\n ScreenContext,\n ViewControl,\n} from \"@mihe-dev/protocol\";\n\n/** What a screen declares about itself, minus the components. */\nexport interface ScreenDeclaration {\n screenId: string;\n route: string;\n title: string;\n description?: string;\n keywords?: string[];\n /**\n * Summary values visible on screen. Keep this small and scalar \u2014 counts,\n * totals, statuses. It is context for the assistant, not a data dump, and\n * every byte here is sent on every screen change.\n */\n visibleData?: Record<string, unknown>;\n /**\n * The page's data has not arrived yet. While true, `visibleData` is not\n * sent, and a navigation to this screen is not reported done.\n *\n * Without it, the counts a page declares before its queries resolve are\n * zeros, and an assistant that has just navigated here reads them as the\n * truth: \"you have no raw materials\", on a page about to show sixteen.\n */\n loading?: boolean;\n}\n\n/** What a component declares about itself. */\nexport interface ComponentDeclaration {\n type: ComponentContext[\"type\"];\n label: string;\n description?: string;\n actions?: string[];\n}\n\n/**\n * A tab set, or another control that changes what the screen shows\n * (amendment 67). `onChange` is the app's own code, which is what lets this\n * work however the tabs are built.\n */\nexport interface ViewControlDeclaration {\n kind: ViewControl[\"kind\"];\n label: string;\n options: { value: string; label: string }[];\n value?: string;\n onChange: (value: string) => void | Promise<void>;\n}\n\n/**\n * A table the assistant can read (amendment 67, step 2). The rows stay here,\n * read through `read_table` when a question needs them.\n */\nexport interface TableDeclaration {\n label: string;\n columns: { key: string; label: string }[];\n /** The input name a row's id fills, such as `orderId`. */\n rowIdName?: string;\n /** The screen a row opens. Each row read carries its `open` params. */\n opens?: string;\n /** Rows in the whole list, when the page shows only some. */\n total?: number;\n /** How many rows are loaded, for the screen context. */\n loadedRows: number;\n /** The rows as they are right now, rendered to declared columns only. */\n read: (offset: number, limit: number) => TableRow[];\n /** How the table can be searched past its loaded rows (amendment 67, step 3). */\n query?: TableQueryDeclaration;\n}\n\n/** One parameter a table search accepts, as the page declared it. */\nexport interface QueryParamDeclaration {\n key: string;\n kind: \"text\" | \"enum\" | \"date\";\n /** For `enum`: the values accepted. */\n values: string[];\n description: string;\n}\n\n/** A table's search: what it takes, and the app's code that runs it. */\nexport interface TableQueryDeclaration {\n params: QueryParamDeclaration[];\n /** Runs the app's own search and renders the result to declared columns. */\n run: (params: Record<string, string>) => Promise<{ rows: TableRow[]; total?: number }>;\n /** Makes the table on screen show this search, when the page can. */\n show?: (params: Record<string, string>) => void | Promise<void>;\n}\n\n/**\n * One row as the model sees it: its id, the params that open it when the\n * table opens a screen, and each declared column by label.\n */\nexport type TableRow = { id: string; open?: Record<string, string> } & Record<\n string,\n string | number | boolean | null | Record<string, string> | undefined\n>;\n\n/** A chart the assistant can explain (amendment 67, step 4). */\nexport interface ChartDeclaration {\n title: string;\n xLabel: string;\n xKind: ChartXKind;\n /** Names, units and summaries, as the screen context carries them. */\n series: ChartSeries[];\n /** The points as they are right now, thinned to `limit` per series. */\n read: (series: string | undefined, limit: number) => { name: string; points: ChartPoint[] }[];\n}\n\n/** A frontend action the assistant may run. */\nexport interface ActionHandler {\n name: string;\n handler: (input: Record<string, unknown>) => unknown | Promise<unknown>;\n}\n\ntype Listener = () => void;\n\nexport class AssistantStore {\n private screen: ScreenDeclaration | null = null;\n /** Insertion-ordered, so the context sent to the model is stable. */\n private components = new Map<string, ComponentDeclaration>();\n private controls = new Map<string, ViewControlDeclaration>();\n private tables = new Map<string, TableDeclaration>();\n private charts = new Map<string, ChartDeclaration>();\n /** Values a component on the screen adds to its `visibleData`, by key. */\n private values = new Map<string, Record<string, unknown>>();\n private actions = new Map<string, ActionHandler>();\n private listeners = new Set<Listener>();\n\n /** Cached so `useSyncExternalStore` sees a stable reference between changes. */\n private snapshot: ScreenContext | null = null;\n private snapshotDirty = true;\n\n subscribe = (listener: Listener): (() => void) => {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n };\n\n /**\n * The current screen context, or null before any screen has registered.\n *\n * Returns the same object until something actually changes, so React can\n * bail out of re-rendering and the provider can skip republishing.\n */\n getScreenContext = (): ScreenContext | null => {\n if (!this.snapshotDirty) return this.snapshot;\n\n this.snapshot = this.screen\n ? {\n screenId: this.screen.screenId,\n route: this.screen.route,\n title: this.screen.title,\n description: this.screen.description ?? \"\",\n keywords: this.screen.keywords ?? [],\n visibleData: (this.screen.loading\n ? {}\n : {\n ...(this.screen.visibleData ?? {}),\n ...Object.fromEntries(this.values),\n }) as ScreenContext[\"visibleData\"],\n components: [...this.components.entries()].map(([componentKey, meta]) => ({\n componentKey,\n type: meta.type,\n label: meta.label,\n description: meta.description ?? \"\",\n actions: meta.actions ?? [],\n })),\n controls: [...this.controls.entries()].map(([controlKey, control]) => ({\n controlKey,\n kind: control.kind,\n label: control.label,\n options: control.options.map(({ value, label }) => ({ value, label })),\n ...(control.value === undefined ? {} : { value: control.value }),\n })),\n tables: [...this.tables.entries()].map(([tableKey, table]) => ({\n tableKey,\n label: table.label,\n columns: table.columns.map(({ key, label }) => ({ key, label })),\n loadedRows: table.loadedRows,\n ...(table.rowIdName ? { rowId: table.rowIdName } : {}),\n ...(table.opens ? { opens: table.opens } : {}),\n ...(table.total === undefined ? {} : { totalRows: table.total }),\n ...(table.query\n ? {\n query: {\n params: table.query.params.map(({ key, kind, values, description }) => ({\n key,\n kind,\n values,\n description,\n })),\n canShow: table.query.show !== undefined,\n },\n }\n : {}),\n })),\n charts: [...this.charts.entries()].map(([chartKey, chart]) => ({\n chartKey,\n title: chart.title,\n xLabel: chart.xLabel,\n xKind: chart.xKind,\n series: chart.series,\n })),\n }\n : null;\n\n this.snapshotDirty = false;\n return this.snapshot;\n };\n\n setScreen(screen: ScreenDeclaration): void {\n const previous = this.screen;\n this.screen = screen;\n\n // Components belong to the screen that registered them. Moving to a new\n // screen clears them, so the assistant is never told it can point at an\n // element from the page the user just left.\n if (previous && previous.screenId !== screen.screenId) {\n this.components.clear();\n this.controls.clear();\n this.tables.clear();\n this.charts.clear();\n this.values.clear();\n }\n this.invalidate();\n }\n\n clearScreen(screenId: string): void {\n // Ignore a stale unmount: React mounts the next screen before unmounting\n // the previous one, so a late cleanup must not wipe the new screen.\n if (this.screen?.screenId !== screenId) return;\n this.screen = null;\n this.components.clear();\n this.controls.clear();\n this.tables.clear();\n this.charts.clear();\n this.values.clear();\n this.invalidate();\n }\n\n registerComponent(componentKey: string, meta: ComponentDeclaration): void {\n this.components.set(componentKey, meta);\n this.invalidate();\n }\n\n unregisterComponent(componentKey: string): void {\n if (this.components.delete(componentKey)) this.invalidate();\n }\n\n registerControl(controlKey: string, control: ViewControlDeclaration): void {\n this.controls.set(controlKey, control);\n this.invalidate();\n }\n\n unregisterControl(controlKey: string): void {\n if (this.controls.delete(controlKey)) this.invalidate();\n }\n\n getControl(controlKey: string): ViewControlDeclaration | undefined {\n return this.controls.get(controlKey);\n }\n\n registerTable(tableKey: string, table: TableDeclaration): void {\n this.tables.set(tableKey, table);\n this.invalidate();\n }\n\n unregisterTable(tableKey: string): void {\n if (this.tables.delete(tableKey)) this.invalidate();\n }\n\n getTable(tableKey: string): TableDeclaration | undefined {\n return this.tables.get(tableKey);\n }\n\n setValues(key: string, values: Record<string, unknown>): void {\n this.values.set(key, values);\n this.invalidate();\n }\n\n clearValues(key: string): void {\n if (this.values.delete(key)) this.invalidate();\n }\n\n registerChart(chartKey: string, chart: ChartDeclaration): void {\n this.charts.set(chartKey, chart);\n this.invalidate();\n }\n\n unregisterChart(chartKey: string): void {\n if (this.charts.delete(chartKey)) this.invalidate();\n }\n\n getChart(chartKey: string): ChartDeclaration | undefined {\n return this.charts.get(chartKey);\n }\n\n registerAction(action: ActionHandler): void {\n if (this.actions.has(action.name)) {\n console.warn(\n `[assistant] Two handlers registered for action \"${action.name}\". ` +\n `The most recent one wins; the earlier one will never run.`,\n );\n }\n this.actions.set(action.name, action);\n // Handlers are not part of the published context, so no invalidate.\n }\n\n unregisterAction(name: string): void {\n this.actions.delete(name);\n }\n\n getAction(name: string): ActionHandler | undefined {\n return this.actions.get(name);\n }\n\n /** Whether the current screen says its data is still loading. */\n isScreenLoading(): boolean {\n return this.screen?.loading === true;\n }\n\n hasComponent(componentKey: string): boolean {\n return this.components.has(componentKey);\n }\n\n private invalidate(): void {\n this.snapshotDirty = true;\n for (const listener of this.listeners) listener();\n }\n}\n", "/**\n * Microphone capture and speaker playback for voice sessions.\n *\n * One format everywhere: **16-bit PCM, 24 kHz, mono, little endian**, matching\n * the voice socket and the provider behind it. Nothing on this path transcodes,\n * which is the point \u2014 a resampler in a latency-critical loop buys nothing when\n * every participant already agrees on the rate.\n *\n * Two browser realities shape the code:\n *\n * - **iOS Safari will not start audio without a user gesture.** Push-to-talk\n * makes that free: the button press *is* the gesture. `resume()` is called on\n * press rather than at construction for exactly this reason, and calling it\n * from anywhere else will produce a context stuck in `suspended` with no\n * error.\n * - **`AudioContext` may refuse the sample rate we ask for.** Chrome, Firefox,\n * and current Safari honour it; older Safari does not. So capture resamples\n * when it has to rather than assuming, and playback hands the browser a\n * buffer tagged 24 kHz and lets the graph resample it.\n */\n\nimport { VOICE_SAMPLE_RATE } from \"@mihe-dev/protocol\";\n\n/**\n * Frames sent up per second. 50 works out to 20 ms per frame \u2014 small enough\n * that the end of an utterance is not padded by a buffer waiting to fill, and\n * large enough that the socket is not doing a send per millisecond.\n */\nconst FRAME_MS = 20;\n\n/**\n * Playback runs this far ahead of the speaker.\n *\n * Without it every chunk that arrives a few milliseconds late produces an\n * audible click as the graph runs dry between buffers. With it, network jitter\n * up to this much is inaudible, at the cost of the same delay before the first\n * word. 200 ms is well under the threshold where a reply feels slow.\n */\nconst JITTER_BUFFER_MS = 200;\n\n/** The AudioWorklet processor, injected as a Blob so the package ships one file. */\nconst CAPTURE_WORKLET = `\nclass MiheCapture extends AudioWorkletProcessor {\n constructor(options) {\n super();\n // A render quantum is 128 samples \u2014 about 5 ms at 24 kHz \u2014 so posting one\n // per call means roughly 190 messages a second, each becoming its own\n // WebSocket frame and its own JSON envelope upstream. Under push-to-talk\n // that was merely wasteful; with the microphone open continuously it\n // saturated the relay and audio started being dropped, which reads to the\n // user as the assistant simply not answering.\n // Defaulted rather than assumed. An absent option would make \\`target\\`\n // undefined, every arithmetic result NaN, and the fill loop exit without\n // ever posting \u2014 total silence, no error, and nothing in any log to say\n // the microphone had stopped working.\n this.target = options?.processorOptions?.frameSamples || 480;\n this.buffer = new Float32Array(this.target);\n this.filled = 0;\n }\n\n process(inputs) {\n const channel = inputs[0] && inputs[0][0];\n // No input yet, or the track ended. Keep the node alive either way:\n // returning false here permanently removes it from the graph, and the\n // user is only between utterances.\n if (!channel) return true;\n\n let offset = 0;\n while (offset < channel.length) {\n const take = Math.min(this.target - this.filled, channel.length - offset);\n this.buffer.set(channel.subarray(offset, offset + take), this.filled);\n this.filled += take;\n offset += take;\n\n if (this.filled === this.target) {\n this.port.postMessage(this.buffer.slice());\n this.filled = 0;\n }\n }\n\n return true;\n }\n}\nregisterProcessor('mihe-capture', MiheCapture);\n`;\n\n/** Float samples in [-1, 1] to little-endian 16-bit PCM. */\nexport function toPcm16(samples: Float32Array): ArrayBuffer {\n const buffer = new ArrayBuffer(samples.length * 2);\n const view = new DataView(buffer);\n for (let i = 0; i < samples.length; i += 1) {\n // Clamped before scaling: a sample above 1 would wrap to a large negative\n // number, which is heard as a loud click rather than as clipping.\n const clamped = Math.max(-1, Math.min(1, samples[i] ?? 0));\n view.setInt16(i * 2, clamped * 0x7fff, true);\n }\n return buffer;\n}\n\n/** Little-endian 16-bit PCM back to float samples. */\nexport function fromPcm16(buffer: ArrayBuffer): Float32Array {\n const view = new DataView(buffer);\n const samples = new Float32Array(buffer.byteLength / 2);\n for (let i = 0; i < samples.length; i += 1) {\n samples[i] = view.getInt16(i * 2, true) / 0x7fff;\n }\n return samples;\n}\n\n/**\n * How loud a chunk of speech is, from 0 (silence) to 1 (as loud as speech gets).\n *\n * RMS rather than peak, because a peak jumps on every consonant and a ring\n * drawn from it flickers instead of breathing. Scaled by eye: ordinary speech\n * into a laptop microphone sits around 0.05 to 0.15 RMS, so a square root and\n * a gain put a normal voice in the middle of the range rather than at its foot.\n */\nexport function pcmLevel(pcm: ArrayBuffer): number {\n const samples = fromPcm16(pcm);\n if (samples.length === 0) return 0;\n let sum = 0;\n for (const sample of samples) sum += sample * sample;\n const rms = Math.sqrt(sum / samples.length);\n return Math.min(1, Math.sqrt(rms) * 1.8);\n}\n\n/**\n * Linear resample, used only when the browser refused our sample rate.\n *\n * Linear rather than anything better on purpose: the input is a voice at\n * 44.1 or 48 kHz going to 24 kHz, the output is fed to a speech model rather\n * than to a listener, and a windowed-sinc filter would cost more than it could\n * possibly be worth here. When the rates already match this is not called at\n * all.\n */\nexport function resample(\n samples: Float32Array,\n fromRate: number,\n toRate: number,\n): Float32Array {\n if (fromRate === toRate) return samples;\n\n const ratio = fromRate / toRate;\n const length = Math.floor(samples.length / ratio);\n const out = new Float32Array(length);\n\n for (let i = 0; i < length; i += 1) {\n const position = i * ratio;\n const index = Math.floor(position);\n const next = Math.min(index + 1, samples.length - 1);\n const fraction = position - index;\n out[i] = (samples[index] ?? 0) * (1 - fraction) + (samples[next] ?? 0) * fraction;\n }\n return out;\n}\n\n/**\n * The microphone, as a stream of PCM frames.\n *\n * Capture runs continuously once started and is *gated* rather than stopped\n * between utterances: `getUserMedia` takes hundreds of milliseconds and shows a\n * permission prompt the first time, so tearing it down on every button release\n * would clip the beginning of the next sentence \u2014 which is exactly where the\n * word the model needs most usually is.\n */\nexport class MicrophoneCapture {\n private context: AudioContext | null = null;\n private stream: MediaStream | null = null;\n private node: AudioWorkletNode | ScriptProcessorNode | null = null;\n private source: MediaStreamAudioSourceNode | null = null;\n private open = false;\n\n constructor(private readonly onFrame: (pcm: ArrayBuffer) => void) {}\n\n /** True while frames are being delivered. */\n get capturing(): boolean {\n return this.open;\n }\n\n /**\n * Ask for the microphone and build the graph. Must be called from a user\n * gesture on iOS Safari.\n */\n async start(): Promise<void> {\n if (this.context) {\n await this.context.resume();\n return;\n }\n\n this.stream = await navigator.mediaDevices.getUserMedia({\n audio: {\n channelCount: 1,\n // All three on: the assistant's own voice comes out of the same device\n // in a hands-free session, and without cancellation the model hears\n // itself and answers its own last sentence.\n echoCancellation: true,\n noiseSuppression: true,\n autoGainControl: true,\n },\n });\n\n const context = new AudioContext({ sampleRate: VOICE_SAMPLE_RATE });\n this.context = context;\n this.source = context.createMediaStreamSource(this.stream);\n\n const deliver = (samples: Float32Array) => {\n if (!this.open) return;\n this.onFrame(\n toPcm16(resample(samples, context.sampleRate, VOICE_SAMPLE_RATE)),\n );\n };\n\n try {\n const url = URL.createObjectURL(\n new Blob([CAPTURE_WORKLET], { type: \"application/javascript\" }),\n );\n await context.audioWorklet.addModule(url);\n URL.revokeObjectURL(url);\n\n const worklet = new AudioWorkletNode(context, \"mihe-capture\", {\n processorOptions: {\n // Measured in the context's own samples: the resample to 24 kHz\n // happens after this, on the main thread.\n frameSamples: Math.round((context.sampleRate * FRAME_MS) / 1000),\n },\n });\n worklet.port.onmessage = (event) =>\n deliver(event.data as Float32Array);\n this.source.connect(worklet);\n // Worklets are not pulled unless something downstream asks for their\n // output. A zero-gain node is the standard way to run the graph without\n // routing the microphone to the speaker.\n const sink = context.createGain();\n sink.gain.value = 0;\n worklet.connect(sink).connect(context.destination);\n this.node = worklet;\n } catch {\n // Older Safari, and any browser that blocks Blob worklet modules under a\n // strict CSP. Deprecated but universally present, and it runs on the\n // main thread, which for one mono voice track is fine.\n const frames = Math.max(\n 256,\n 2 ** Math.round(Math.log2((context.sampleRate * FRAME_MS) / 1000)),\n );\n const processor = context.createScriptProcessor(frames, 1, 1);\n processor.onaudioprocess = (event) =>\n deliver(new Float32Array(event.inputBuffer.getChannelData(0)));\n this.source.connect(processor);\n processor.connect(context.destination);\n this.node = processor;\n }\n\n await context.resume();\n }\n\n /** Begin delivering frames. */\n unmute(): void {\n this.open = true;\n }\n\n /** Stop delivering frames, without releasing the microphone. */\n mute(): void {\n this.open = false;\n }\n\n /** Release the device and tear down the graph. */\n async stop(): Promise<void> {\n this.open = false;\n this.node?.disconnect();\n this.source?.disconnect();\n this.stream?.getTracks().forEach((track) => track.stop());\n await this.context?.close().catch(() => undefined);\n this.node = null;\n this.source = null;\n this.stream = null;\n this.context = null;\n }\n}\n\n/**\n * The assistant's voice, as a queue of scheduled buffers.\n *\n * Chunks are scheduled back to back against the audio clock rather than played\n * on arrival, because `AudioBufferSourceNode.start()` with no time argument\n * plays *now* \u2014 and forty of those a second produce overlapping speech. The\n * running `nextStart` cursor is what turns a stream of chunks into continuous\n * audio.\n */\nexport class SpeakerQueue {\n /**\n * Called with each chunk's level at the moment it starts playing, not when\n * it arrives. The provider streams seconds ahead of the speaker, so a level\n * taken on arrival would animate words the user has not heard yet.\n */\n constructor(private readonly onLevel?: (level: number) => void) {}\n\n private levelTimers = new Set<ReturnType<typeof setTimeout>>();\n private context: AudioContext | null = null;\n private gain: GainNode | null = null;\n private sources = new Set<AudioBufferSourceNode>();\n /** Audio-clock time the next chunk should begin at. */\n private nextStart = 0;\n /** Audio-clock time the current run of speech began. */\n private startedAt = 0;\n\n /** Must be called from a user gesture on iOS Safari. */\n async start(): Promise<void> {\n if (this.context) {\n await this.context.resume();\n return;\n }\n const context = new AudioContext({ sampleRate: VOICE_SAMPLE_RATE });\n this.gain = context.createGain();\n this.gain.connect(context.destination);\n this.context = context;\n await context.resume();\n }\n\n /** Queue a chunk of assistant speech. */\n enqueue(pcm: ArrayBuffer): void {\n const context = this.context;\n if (!context || !this.gain) return;\n\n const samples = fromPcm16(pcm);\n if (samples.length === 0) return;\n\n // Tagged 24 kHz regardless of the context's own rate, so a browser that\n // refused our request resamples on playback instead of playing the\n // assistant at the wrong pitch.\n const buffer = context.createBuffer(1, samples.length, VOICE_SAMPLE_RATE);\n buffer.getChannelData(0).set(samples);\n\n const source = context.createBufferSource();\n source.buffer = buffer;\n source.connect(this.gain);\n\n const now = context.currentTime;\n if (this.nextStart < now) {\n // Either the first chunk of an utterance or a gap the network opened up.\n // Restarting the cursor ahead of `now` is what the jitter buffer is:\n // scheduling at `now` exactly means anything arriving late is already\n // too late.\n this.nextStart = now + JITTER_BUFFER_MS / 1000;\n this.startedAt = this.nextStart;\n }\n\n source.start(this.nextStart);\n\n if (this.onLevel) {\n const level = pcmLevel(pcm);\n const report = this.onLevel;\n const timer = setTimeout(\n () => {\n this.levelTimers.delete(timer);\n report(level);\n },\n Math.max(0, (this.nextStart - now) * 1000),\n );\n this.levelTimers.add(timer);\n }\n\n this.nextStart += buffer.duration;\n\n this.sources.add(source);\n source.onended = () => this.sources.delete(source);\n }\n\n /**\n * How much of the current utterance the user has actually heard, in\n * milliseconds.\n *\n * This is the number the runtime needs to truncate the model's history on a\n * barge-in, and it is not the same as how much has been *received*: the\n * provider streams several seconds ahead of the speaker, so trusting bytes\n * delivered would tell the model the user heard a sentence they were\n * interrupted three words into.\n */\n heardMs(): number {\n if (!this.context || this.startedAt === 0) return 0;\n const heard = this.context.currentTime - this.startedAt;\n return Math.max(0, Math.round(heard * 1000));\n }\n\n /** True while anything is scheduled or playing. */\n get speaking(): boolean {\n return this.sources.size > 0;\n }\n\n /** Drop everything queued \u2014 an interruption, or the end of a session. */\n flush(): void {\n for (const source of this.sources) {\n try {\n source.stop();\n } catch {\n // Already ended. `stop()` on a finished node throws in some browsers.\n }\n }\n this.sources.clear();\n this.nextStart = 0;\n this.startedAt = 0;\n\n // Levels for audio that will now never play, and a last zero so a ring\n // following them settles instead of freezing mid-word.\n for (const timer of this.levelTimers) clearTimeout(timer);\n this.levelTimers.clear();\n this.onLevel?.(0);\n }\n\n async stop(): Promise<void> {\n this.flush();\n this.gain?.disconnect();\n await this.context?.close().catch(() => undefined);\n this.gain = null;\n this.context = null;\n }\n}\n", "/**\n * The voice session (doc 03, phase 5a).\n *\n * A second, short-lived WebSocket carrying audio only. Commands and\n * confirmation cards keep arriving on the chat socket, which is the whole\n * design: voice is an input modality, not a second command channel, so a\n * confirmation card is the same card whether the user typed or spoke and there\n * is exactly one place that draws it.\n *\n * Push-to-talk, deliberately, before hands-free. It has no VAD edge cases, it\n * works on a phone the day it ships, and the button press doubles as the user\n * gesture iOS Safari requires before any audio can start. Hands-free is 5b.\n */\n\nimport {\n VOICE_SAMPLE_RATE,\n type VoiceClientMessage,\n type VoiceEndReason,\n type VoiceServerMessage,\n} from \"@mihe-dev/protocol\";\n\nimport { MicrophoneCapture, SpeakerQueue, pcmLevel } from \"./audio\";\n\nexport type VoiceState =\n | \"idle\"\n | \"connecting\"\n /** Connected, microphone closed, waiting for the user to press. */\n | \"listening\"\n /** The button is held down and audio is going up. */\n | \"recording\"\n /** The utterance is committed and the model has not answered yet. */\n | \"thinking\"\n | \"speaking\"\n | \"error\";\n\nexport type VoiceEvent =\n | { type: \"state\"; state: VoiceState }\n | {\n type: \"transcript\";\n role: \"user\" | \"assistant\";\n text: string;\n final: boolean;\n }\n | {\n type: \"error\";\n code: string;\n message: string;\n /** True when the session survived and the user can simply try again. */\n recoverable: boolean;\n fallbackToChat: boolean;\n }\n /** The duration ceiling is near. The session is still running. */\n | { type: \"ending_soon\"; secondsRemaining: number }\n /**\n * The session finished the way it was meant to \u2014 the minutes ran out, or\n * nobody spoke. Deliberately not an `error`: the chat socket is untouched and\n * the transcript is already in the conversation, so the user has lost\n * nothing and should not be told they have.\n */\n | { type: \"ended\"; reason: VoiceEndReason; message: string };\n\nexport interface VoiceSessionOptions {\n /** Voice socket URL, already carrying the single-use token. */\n url: string;\n /**\n * Hands-free: the microphone stays open, the provider decides when an\n * utterance ends, and the user can talk over the assistant.\n *\n * The trade is echo. Under push-to-talk the microphone is shut except while\n * the button is held, so the assistant physically cannot hear itself; here\n * it can, and browser echo cancellation is the only thing between the two.\n * On a laptop that is usually enough. On a phone with the speaker beside the\n * microphone it may not be, and the failure is the assistant answering its\n * own last sentence.\n */\n handsFree?: boolean;\n onEvent: (event: VoiceEvent) => void;\n /**\n * How loud each side is, 0 to 1, as it happens: the user's voice as it is\n * captured, the assistant's as it plays. Deliberately not a `VoiceEvent`:\n * it fires tens of times a second, and whatever listens should move a ring\n * rather than re-render a component tree.\n */\n onLevel?: (source: \"user\" | \"assistant\", level: number) => void;\n /** Injectable for tests. */\n socketFactory?: (url: string) => VoiceSocketLike;\n}\n\n/** The slice of `WebSocket` a voice session uses. */\nexport interface VoiceSocketLike {\n binaryType: string;\n send(data: string | ArrayBuffer): void;\n close(): void;\n onopen: ((event: unknown) => void) | null;\n onclose: ((event: unknown) => void) | null;\n onerror: ((event: unknown) => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n}\n\nexport class VoiceSession {\n private socket: VoiceSocketLike | null = null;\n private state: VoiceState = \"idle\";\n private mic: MicrophoneCapture;\n private speaker: SpeakerQueue;\n /**\n * Audio captured since a held press began, not yet sent.\n *\n * A press on a draggable orb is ambiguous for its first moment: it may be\n * the start of speech or the start of a drag. Holding the frames here means\n * neither answer costs anything. Confirmed, they go up in order and the first\n * word survives. Discarded, they never leave the browser, because the voice\n * socket has no way to take back audio it has been sent.\n */\n private held: ArrayBuffer[] | null = null;\n private stopped = false;\n /**\n * Milliseconds of audio captured since the last commit.\n *\n * Counted rather than timed from the button press, because those differ:\n * the microphone graph delivers in render quanta, so a press held for\n * exactly the floor produces slightly less audio than the clock suggests.\n * The provider measures the buffer, so we measure the buffer too.\n */\n private utteranceMs = 0;\n /** The assistant's line so far, joined from streamed transcript deltas. */\n private assistantLine = \"\";\n /** Reports playback position while speaking, for barge-in truncation. */\n private playbackTimer: ReturnType<typeof setInterval> | null = null;\n /**\n * The provider's shortest acceptable utterance, learned at `ready`.\n *\n * Defaults high enough to be safe before the runtime has told us, since\n * guessing low means the first press of a session is the one that fails.\n */\n private minUtteranceMs = 150;\n\n constructor(private readonly options: VoiceSessionOptions) {\n this.speaker = new SpeakerQueue((level) => options.onLevel?.(\"assistant\", level));\n this.mic = new MicrophoneCapture((pcm) => this.sendAudio(pcm));\n }\n\n get currentState(): VoiceState {\n return this.state;\n }\n\n /**\n * Open the socket and the audio devices.\n *\n * Must be called from a user gesture. Both the microphone permission prompt\n * and iOS Safari's audio unlock require one, and a session started from an\n * effect will connect happily and then be silent in both directions with no\n * error anywhere.\n */\n async start(): Promise<void> {\n if (this.socket) return;\n this.stopped = false;\n this.setState(\"connecting\");\n\n try {\n // Devices before the socket: a user who declines the microphone prompt\n // should not have consumed their single-use voice token, because the\n // next attempt would then fail for a second, unrelated reason.\n await this.mic.start();\n await this.speaker.start();\n } catch (cause) {\n this.emit({\n type: \"error\",\n code: \"microphone_unavailable\",\n message:\n cause instanceof Error && cause.name === \"NotAllowedError\"\n ? \"Microphone access was declined, so voice cannot start.\"\n : \"No microphone is available.\",\n // No microphone means no voice at all, so this one really is the end.\n recoverable: false,\n fallbackToChat: true,\n });\n this.setState(\"error\");\n return;\n }\n\n const factory =\n this.options.socketFactory ??\n ((url: string) => new WebSocket(url) as unknown as VoiceSocketLike);\n\n const socket = factory(this.options.url);\n socket.binaryType = \"arraybuffer\";\n this.socket = socket;\n\n socket.onmessage = (event) => {\n if (typeof event.data === \"string\") {\n this.handleControl(event.data);\n } else if (event.data instanceof ArrayBuffer) {\n this.speaker.enqueue(event.data);\n // The runtime never says `speaking`; the first audio of a reply is how\n // the client knows. Without this a push-to-talk session sat in\n // `thinking` after its first answer, `speech_done` only moves\n // `speaking` on, and every later press was refused.\n if (this.state === \"thinking\") this.setState(\"speaking\");\n }\n };\n\n socket.onclose = () => {\n // No reconnect, unlike the chat socket. A voice token is single use and\n // a provider session is not resumable, so reconnecting would mean a new\n // session with none of this one's context \u2014 better to end cleanly and\n // let the user press the button again.\n void this.stop();\n };\n\n socket.onerror = () => {\n // `onclose` always follows.\n };\n }\n\n /**\n * The user pressed the button. Push-to-talk only.\n *\n * With `hold`, capture starts but nothing is sent, and the assistant is not\n * interrupted, until `confirmUtterance`. `discardUtterance` drops it\n * instead. For a control where a press might turn out to be a drag.\n */\n beginUtterance(options?: { hold?: boolean }): void {\n if (this.options.handsFree) return;\n if (this.state !== \"listening\" && this.state !== \"speaking\") return;\n\n this.utteranceMs = 0;\n\n if (options?.hold) {\n this.held = [];\n this.mic.unmute();\n this.setState(\"recording\");\n return;\n }\n\n this.interruptPlayback();\n this.mic.unmute();\n this.setState(\"recording\");\n }\n\n /** A held press was speech after all: send what was captured, and carry on. */\n confirmUtterance(): void {\n const held = this.held;\n if (!held) return;\n this.held = null;\n this.interruptPlayback();\n for (const pcm of held) this.sendAudio(pcm);\n }\n\n /** A held press was a drag: drop what was captured, and send nothing. */\n discardUtterance(): void {\n if (!this.held) return;\n this.held = null;\n this.mic.mute();\n this.utteranceMs = 0;\n this.options.onLevel?.(\"user\", 0);\n this.setState(this.speaker.speaking ? \"speaking\" : \"listening\");\n }\n\n /**\n * Pressing while the assistant is talking is an interruption. Split out so a\n * held press does not cut the assistant off before it is known to be speech.\n */\n private interruptPlayback(): void {\n // Stop the playback the user has clearly stopped listening to, and tell\n // the runtime how much of it they actually heard so the model's history\n // matches their memory of the conversation.\n if (this.speaker.speaking) {\n this.sendControl({\n type: \"playback\",\n data: { ms: this.speaker.heardMs() },\n });\n this.speaker.flush();\n }\n }\n\n /** The user released the button. Push-to-talk only. */\n endUtterance(): void {\n if (this.options.handsFree) return;\n if (this.state !== \"recording\") return;\n // Released before anyone decided: a press that never moved is speech.\n this.confirmUtterance();\n this.mic.mute();\n this.options.onLevel?.(\"user\", 0);\n\n // A tap, not speech. Committing this comes back from the provider as\n // \"buffer too small\", which is a true statement about something the user\n // should never have been able to send \u2014 so it is caught here instead, and\n // again in the runtime, which is the authority for clients that are not\n // this one.\n if (this.utteranceMs < this.minUtteranceMs) {\n this.emit({\n type: \"error\",\n code: \"utterance_too_short\",\n message: \"Hold the button while you speak.\",\n recoverable: true,\n fallbackToChat: false,\n });\n this.setState(\"listening\");\n return;\n }\n\n this.sendControl({ type: \"commit\" });\n this.setState(\"thinking\");\n }\n\n async stop(): Promise<void> {\n if (this.stopped) return;\n this.stopped = true;\n\n if (this.playbackTimer !== null) {\n clearInterval(this.playbackTimer);\n this.playbackTimer = null;\n }\n\n this.sendControl({ type: \"end\" });\n this.socket?.close();\n this.socket = null;\n\n await Promise.all([this.mic.stop(), this.speaker.stop()]);\n this.setState(\"idle\");\n }\n\n // ------------------------------------------------------------- internals\n\n private handleControl(raw: string): void {\n let message: VoiceServerMessage;\n try {\n message = JSON.parse(raw) as VoiceServerMessage;\n } catch {\n return;\n }\n\n switch (message.type) {\n case \"ready\":\n // Tolerating an absent value keeps a newer SDK working against an older\n // runtime, where guessing low would fail the first press of a session.\n this.minUtteranceMs = message.data.minUtteranceMs ?? this.minUtteranceMs;\n\n // Hands-free opens the microphone here and not in `start`, and the\n // difference is a whole class of bug. Unmuting early meant capturing\n // from before the socket existed until the provider accepted its\n // configuration \u2014 a second or two of the room \u2014 and every frame after\n // the socket opened was sent, queued behind a relay still sitting in\n // `wait_for_ready`. It arrived in one burst the moment the loop began\n // reading, the provider's VAD read the burst as an utterance, and the\n // session answered something nobody said. Reliably at the start of a\n // session and never afterwards, because it can only happen once.\n //\n // The runtime already holds this rule for its own upstream: nothing is\n // sent until the provider has accepted the configuration. This is the\n // client keeping the same rule.\n //\n // Still open for the whole session once it opens \u2014 there is no press\n // to open it on, and opening per detected utterance would clip the\n // first word, which is usually the one carrying the intent.\n if (this.options.handsFree) this.mic.unmute();\n\n this.setState(\"listening\");\n break;\n\n case \"transcript\": {\n // The runtime forwards the provider's deltas as they arrive, a few\n // characters each, so the line so far is built here. Emitting the raw\n // delta made the caption show only the latest fragment, which read as\n // gibberish flickering over the orb.\n let text = message.data.text;\n if (message.data.role === \"assistant\") {\n if (message.data.final) {\n this.assistantLine = \"\";\n } else {\n this.assistantLine += text;\n text = this.assistantLine;\n }\n }\n this.emit({\n type: \"transcript\",\n role: message.data.role,\n text,\n final: message.data.final,\n });\n break;\n }\n\n case \"status\":\n // A status of `listening` means one thing: the user interrupted. The\n // queued audio has to go, or the assistant carries on out of the\n // speaker after being cut off \u2014 and the runtime has already truncated\n // its history to what was heard, so the two would disagree about the\n // conversation.\n //\n // This is safe only because a *finished* reply arrives as\n // `speech_done` instead. The relay used to send both, and flushing on\n // a completed response threw away everything still queued \u2014 heard as\n // every answer breaking off part-way through.\n if (\n message.data.state === \"listening\" &&\n this.speaker.speaking\n ) {\n this.speaker.flush();\n }\n // An interrupted line may never be marked final, and its leftovers\n // must not prefix the next reply's caption.\n if (message.data.state === \"listening\") this.assistantLine = \"\";\n\n // The server's view of the session, except while the button is down \u2014\n // the client knows about a press before the server does, and letting a\n // status frame overwrite `recording` makes the button flicker.\n if (this.state !== \"recording\") {\n this.setState(\n message.data.state === \"reconnecting\"\n ? \"connecting\"\n : message.data.state,\n );\n }\n break;\n\n case \"speech_done\":\n // From `thinking` too: a reply can finish with no audio at all, a tool\n // call and nothing said, and the session must still take the next\n // press.\n if (this.state === \"speaking\" || this.state === \"thinking\") {\n this.setState(\"listening\");\n }\n break;\n\n case \"ending_soon\":\n this.emit({\n type: \"ending_soon\",\n secondsRemaining: message.data.secondsRemaining,\n });\n break;\n\n case \"ended\":\n this.emit({\n type: \"ended\",\n reason: message.data.reason,\n message: message.data.message,\n });\n // `stop` leaves the state `idle`, which is the point: the microphone\n // button goes back to its resting look rather than the failed one, and\n // pressing it again starts a fresh session on the same conversation.\n void this.stop();\n break;\n\n case \"error\":\n this.emit({\n type: \"error\",\n code: message.data.code,\n message: message.data.message,\n recoverable: message.data.recoverable,\n fallbackToChat: message.data.fallbackToChat,\n });\n // A recoverable error is one operation failing, not the call ending.\n // Tearing the session down here was the bug: a mistimed press hung up\n // on the user and made them start over.\n if (!message.data.recoverable) {\n this.setState(\"error\");\n void this.stop();\n }\n break;\n }\n }\n\n private sendAudio(pcm: ArrayBuffer): void {\n // Hands-free sends throughout: the provider's VAD is what decides where an\n // utterance begins, and it cannot do that on audio it never receives.\n if (!this.options.handsFree && this.state !== \"recording\") return;\n if (this.held) {\n this.held.push(pcm);\n this.options.onLevel?.(\"user\", pcmLevel(pcm));\n return;\n }\n this.options.onLevel?.(\"user\", pcmLevel(pcm));\n // Two bytes per sample, one channel.\n this.utteranceMs += (pcm.byteLength * 1000) / (2 * VOICE_SAMPLE_RATE);\n try {\n this.socket?.send(pcm);\n } catch {\n // A send on a closing socket throws; `onclose` handles the rest.\n }\n }\n\n private sendControl(message: VoiceClientMessage): void {\n try {\n this.socket?.send(JSON.stringify(message));\n } catch {\n // Same.\n }\n }\n\n private setState(state: VoiceState): void {\n if (this.state === state) return;\n this.state = state;\n this.reportPlaybackWhileSpeaking();\n this.options.onEvent({ type: \"state\", state });\n }\n\n /**\n * Tell the runtime how much of the current reply has actually been heard,\n * a few times a second, for as long as the assistant is speaking.\n *\n * Push-to-talk does not need this: the interruption *is* a button press, so\n * the position can be measured at exactly that moment. Hands-free has no\n * such moment on this side \u2014 the provider notices the user talking, upstream\n * \u2014 so the runtime has to already hold a recent figure when it truncates.\n *\n * Without it, truncation falls back to nothing and the model's history keeps\n * the whole reply, including the part the user cut off. It then answers\n * follow-ups about words nobody heard.\n *\n * Four frames a second of small JSON, against fifty of audio.\n */\n private reportPlaybackWhileSpeaking(): void {\n const shouldReport =\n Boolean(this.options.handsFree) && this.state === \"speaking\";\n\n if (!shouldReport) {\n if (this.playbackTimer !== null) {\n clearInterval(this.playbackTimer);\n this.playbackTimer = null;\n }\n return;\n }\n\n if (this.playbackTimer !== null) return;\n\n this.playbackTimer = setInterval(() => {\n if (!this.speaker.speaking) return;\n this.sendControl({\n type: \"playback\",\n data: { ms: this.speaker.heardMs() },\n });\n }, 250);\n }\n\n private emit(event: VoiceEvent): void {\n this.options.onEvent(event);\n }\n}\n\n/**\n * Derive the voice socket URL from the chat socket URL.\n *\n * One `url` prop for the whole SDK rather than two: the two sockets are always\n * served by the same runtime, and a second prop would be a second thing to get\n * wrong in every integration for no configurability anyone wants.\n */\nexport function voiceUrl(\n chatUrl: string,\n token: string,\n handsFree = false,\n): string {\n const base = chatUrl.replace(/\\/v1\\/ws\\/?$/, \"/v1/voice\");\n const separator = base.includes(\"?\") ? \"&\" : \"?\";\n const mode = handsFree ? \"&handsFree=true\" : \"\";\n return `${base}${separator}token=${encodeURIComponent(token)}${mode}`;\n}\n", "/**\n * `Mihe` \u2014 the assistant without a framework (doc 08).\n *\n * The wiring between the store, the client and whatever UI draws it. It owns\n * three responsibilities and nothing else:\n *\n * 1. Publish the screen context whenever the app's declaration changes.\n * 2. Execute commands the runtime sends, and ack every one of them.\n * 3. Hold the conversation the UI renders.\n *\n * State is an immutable snapshot, replaced on every change, so React's\n * `useSyncExternalStore` reads it with no adapter: the React provider is a\n * thin wrapper over this class, and the two cannot disagree about what a\n * proposal or a voice event does.\n */\n\nimport type {\n KnowledgeSourceRef,\n ProposalStatus,\n ScreenContext,\n WidgetSettings,\n} from \"@mihe-dev/protocol\";\n\nimport {\n AssistantClient,\n type ClientEvent,\n type ConnectionState,\n type ProposalPayload,\n} from \"./client\";\nimport { appView, DESCRIBE_TIMEOUT_MS, hintedView, type CardView, type DescribeProposal, type ProposalDescription } from \"./card-view\";\nimport { executeCommand } from \"./commands\";\nimport { formatContextReport } from \"./debug\";\nimport {\n chartDeclaration,\n chartSignature,\n tableDeclaration,\n tableSignature,\n tabsDeclaration,\n tabsSignature,\n type AssistantChart,\n type AssistantTable,\n type AssistantTabs,\n} from \"./declarations\";\nimport { COMPONENT_ATTRIBUTE, HighlightEngine } from \"./highlight\";\nimport {\n AssistantStore,\n type ActionHandler,\n type ComponentDeclaration,\n type ScreenDeclaration,\n} from \"./store\";\nimport { VoiceSession, voiceUrl, type VoiceEvent, type VoiceState } from \"./voice\";\n\n/** How long to coalesce screen changes before publishing. */\nconst SCREEN_DEBOUNCE_MS = 60;\n\n/**\n * How long a navigate ack waits for the destination to publish. Comfortably\n * under the runtime's 10s command timeout, and long enough for a route that\n * loads data before registering itself.\n */\nconst SCREEN_ARRIVAL_TIMEOUT_MS = 2500;\n\nexport const SDK_VERSION = \"0.2.7\";\n\n/**\n * DOM id of the assistant panel, so a custom trigger can point\n * `aria-controls` at it.\n */\nexport const ASSISTANT_PANEL_ID = \"mihe-panel\";\n\nexport interface ChatMessage {\n id: string;\n role: \"user\" | \"assistant\";\n text: string;\n /** True while the assistant is still streaming into this message. */\n streaming?: boolean;\n /**\n * Documentation this answer drew on, if any.\n *\n * Worth surfacing rather than keeping for debugging: an answer a user can\n * trace to a named section of their own handbook is one they can check, and\n * \"where did that come from?\" is the first question anyone asks of an\n * assistant that suddenly knows their internal policy.\n */\n sources?: KnowledgeSourceRef[];\n /** Pictures the person attached, shown with their words. Mihe was sent the app's description, not these. */\n attachments?: { name: string; previewUrl?: string }[];\n}\n\n/**\n * A picture waiting to go with the next message (amendment 102). The app's\n * `describeAttachment` turns it into words; only the words reach the runtime.\n */\nexport interface PendingAttachment {\n id: string;\n name: string;\n /** A local preview (`blob:`), for the chip and the sent bubble. */\n previewUrl?: string;\n status: \"reading\" | \"ready\" | \"failed\";\n /** The app's description, once ready. */\n text?: string;\n /** Why it couldn't be read, when it failed. */\n error?: string;\n}\n\n/**\n * Turn a file the person attached into words for the assistant (amendment\n * 102). The paperclip appears only when the app passes this: images never go\n * anywhere the app didn't send them. Throw with a message the person can read\n * to refuse one.\n */\nexport type DescribeAttachment = (file: File) => Promise<string>;\n\n/** At most this many waiting at once; a picture is read for meaning, not collected. */\nexport const MAX_ATTACHMENTS = 4;\n/** How long the app has to describe one before the chip says it couldn't. */\nexport const ATTACHMENT_TIMEOUT_MS = 90_000;\n\n/**\n * The voice session, as the UI sees it.\n *\n * `available` is answered by the runtime rather than assumed: voice is a\n * per-app setting with a real price attached, so a microphone button that\n * appears and then fails on the first press is worse than no button. It stays\n * false until the runtime has actually issued a permit.\n */\nexport interface VoiceControls {\n available: boolean;\n state: VoiceState;\n /** True while the session is hands-free rather than push-to-talk. */\n handsFree: boolean;\n /** The line currently being spoken, either side. Empty between utterances. */\n caption: string;\n /**\n * Something about the session worth saying that is not a failure \u2014 the\n * ceiling approaching, or a session that has just reached it.\n *\n * Separate from `lastError` so a host app's own UI can draw the two\n * differently, which is the whole point: a session ending on time is the\n * ordinary case, and putting it in the same red box as a dead provider tells\n * the user their conversation broke when it did not.\n */\n notice: string | null;\n /**\n * Open the socket and the microphone. Must be called from a user gesture \u2014\n * both the permission prompt and iOS Safari's audio unlock require one.\n *\n * `handsFree` leaves the microphone open and lets the provider decide where\n * utterances begin and end, so the user can interrupt mid-reply. The cost is\n * that the assistant can hear itself; see `VoiceSessionOptions`.\n */\n start: (options?: {\n handsFree?: boolean;\n /**\n * Open the conversation panel as the session starts, so what is said\n * appears there as the session goes. True by default. The orb passes false, because it\n * shows voice in itself and the point of it is that the page stays visible.\n */\n openPanel?: boolean;\n }) => void;\n stop: () => void;\n /**\n * Push-to-talk: press and release.\n *\n * `press({ hold: true })` captures without sending until `confirm()`, and\n * `discard()` drops it: for a control where a press may turn into a drag.\n * Releasing a held press confirms it.\n */\n press: (options?: { hold?: boolean }) => void;\n release: () => void;\n confirm: () => void;\n discard: () => void;\n /**\n * Follow how loud each side is, 0 to 1, as it happens. Returns the\n * unsubscribe. Outside the state snapshot on purpose: it fires tens of\n * times a second, and a listener should move an element directly, not\n * re-render.\n */\n onLevel: (listener: (source: \"user\" | \"assistant\", level: number) => void) => () => void;\n}\n\n/** Everything a UI draws, as one immutable snapshot. */\nexport interface MiheState {\n connection: ConnectionState;\n messages: ChatMessage[];\n /** Confirmation cards awaiting a decision. */\n proposals: ProposalPayload[];\n /**\n * What each card says, by proposal id (amendment 99): the app's description\n * or the registry's hints. `\"pending\"` while the app is describing it, when\n * the card shows its title and keeps Confirm disabled.\n */\n cardViews: Record<string, CardView | \"pending\">;\n /** True from the moment a turn starts until it completes or fails. */\n busy: boolean;\n lastError: string | null;\n /**\n * Whether the assistant UI is showing.\n *\n * Held here rather than inside the widget so that anything in the host\n * app \u2014 a nav button, a command palette, a keyboard shortcut, a plain div \u2014\n * can open the assistant.\n */\n isOpen: boolean;\n /**\n * What the assistant calls itself, from the app's settings in the dashboard.\n * `null` until the handshake answers; a title passed in code still wins.\n */\n assistantName: string | null;\n /**\n * The customer's launcher icon, uploaded in the dashboard (amendment 58).\n * `null` means draw the Mihe mark.\n */\n launcherIconUrl: string | null;\n /**\n * How the widget presents itself, from the app's settings in the dashboard\n * (amendment 61). `null` until the handshake answers.\n */\n widgetSettings: WidgetSettings | null;\n /** Realtime voice (doc 03). */\n voice: VoiceControls;\n /** What the app takes as attachments (an `accept` list), or null when it takes none (no `describeAttachment`). */\n attachAccept: string | null;\n /** Pictures waiting to go with the next message. */\n attachments: PendingAttachment[];\n}\n\nexport interface MiheOptions {\n /** Runtime WebSocket URL. */\n url: string;\n /** The app's public key. Safe to ship in a browser bundle. */\n publicKey: string;\n /**\n * Fetches a signed user token from the host app's own backend.\n *\n * This is the app's authority, not the browser's: the runtime reads the\n * user's id and permissions from this token's verified claims and from\n * nothing else, so a user cannot grant themselves access by editing\n * anything on this side.\n */\n getUserToken: () => Promise<string>;\n /**\n * Route change handler. Wire this to your router \u2014 the default is a full\n * page load, which works but throws away app state.\n */\n navigate?: (route: string) => void;\n /**\n * Open a registered dialog. Return `false` when the key is not one this\n * app can open, so the assistant is told rather than reporting success for\n * a form that never appeared.\n */\n openModal?: (modalKey: string) => void | boolean;\n /**\n * Called when a confirmed action finishes, whatever the outcome.\n *\n * Exists because the assistant changes data the host app is already\n * displaying, and an app with a client-side cache has no way to know. The\n * usual implementation is one line \u2014 refetch what the page shows \u2014 and\n * without it a user watches an action succeed while the page goes on\n * showing what was true before it.\n */\n onActionComplete?: (action: {\n actionName: string;\n status: ProposalStatus;\n result?: unknown;\n }) => void;\n /**\n * Log what each screen costs to the console: how many tokens one answer\n * re-sends, and which tools make up most of it (amendment 67). Turn it on\n * while building an integration; leave it off in production.\n */\n debug?: boolean;\n /**\n * Say what a confirmation card means, from the app's own data (amendment\n * 99): before and after (\"$12 \u2192 $13\"), money in the right currency,\n * thumbnails of the photos involved. Handed exactly the input that will run;\n * describe that and nothing else. Without it, or when it returns null, the\n * card uses the registry's display hints. See `ProposalDescription`.\n */\n describeProposal?: DescribeProposal;\n /**\n * Let people attach pictures (amendment 102): the chat box gets a paperclip\n * and takes pasted images, and each is handed here to be described. The\n * assistant gets the description with the message; the picture stays with\n * the app. Without it there's no paperclip.\n */\n describeAttachment?: DescribeAttachment;\n /** What the paperclip offers, as an `accept` list. Default `image/*`. */\n attachmentAccept?: string;\n}\n\n/**\n * How the approaching ceiling is put to the user.\n *\n * Phrased here rather than by the runtime because it is a countdown, and the\n * runtime sends a number. Rounded to five seconds: the figure is approximate\n * by the time it has crossed a socket and been rendered, and a notice reading\n * \"27 seconds left\" claims a precision nobody has.\n */\nfunction endingSoonNotice(secondsRemaining: number): string {\n const rounded = Math.max(5, Math.round(secondsRemaining / 5) * 5);\n return `About ${rounded} seconds left of voice. The conversation carries on in text.`;\n}\n\nfunction messageId(prefix: string): string {\n return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n}\n\n/**\n * A declaration made through one of the `register\u2026` methods. Calling the\n * method again with the same id updates this entry, so the undo from the\n * first call still removes it; an undo from before an unregister finds a\n * different entry and does nothing, so a late cleanup cannot remove what the\n * next page declared.\n */\ninterface Declared<T, D> {\n latest: T;\n signature: string;\n declaration: D;\n}\n\n/** Create the assistant and connect it. */\nexport function createMihe(options: MiheOptions): Mihe {\n const mihe = new Mihe(options);\n mihe.connect();\n return mihe;\n}\n\nexport class Mihe {\n /** The app's declaration of itself. */\n readonly store = new AssistantStore();\n private readonly highlighter = new HighlightEngine();\n private options: MiheOptions;\n private state: MiheState;\n private readonly listeners = new Set<() => void>();\n\n private client: AssistantClient | null = null;\n private voiceSession: VoiceSession | null = null;\n /**\n * Set while the user is waiting on a permit they asked for.\n *\n * A token arriving with this false is one the runtime issued for a probe at\n * connect time, and starting a session on it would turn the microphone on\n * without anyone having asked.\n */\n private voiceRequested = false;\n /** The mode asked for, read when the permit comes back. */\n private requestedHandsFree = false;\n /** Whether the session being requested should open the panel. */\n private requestedOpenPanel = true;\n private readonly levelListeners = new Set<\n (source: \"user\" | \"assistant\", level: number) => void\n >();\n\n /** The route of the last screen context actually sent to the runtime. */\n private publishedRoute: string | null = null;\n /** The last screen context actually sent, for acks that check a view. */\n private publishedScreen: ScreenContext | null = null;\n /** Whether that screen had finished loading its data when it was sent. */\n private publishedSettled = false;\n /** Navigate and set_view acks waiting for the screen to report in. */\n private readonly screenWaiters = new Set<(screen: ScreenContext) => void>();\n private publishTimer: ReturnType<typeof setTimeout> | null = null;\n private unsubscribeStore: (() => void) | null = null;\n\n private readonly tabs = new Map<string, Declared<AssistantTabs, unknown>>();\n private readonly tables = new Map<string, Declared<AssistantTable<unknown>, unknown>>();\n private readonly charts = new Map<string, Declared<AssistantChart<unknown>, unknown>>();\n private readonly actions = new Map<string, Declared<ActionHandler[\"handler\"], ActionHandler>>();\n private lastScreen: string | null = null;\n\n constructor(options: MiheOptions) {\n this.options = { ...options };\n this.state = {\n connection: \"idle\",\n messages: [],\n proposals: [],\n cardViews: {},\n busy: false,\n lastError: null,\n isOpen: false,\n assistantName: null,\n launcherIconUrl: null,\n widgetSettings: null,\n attachAccept: options.describeAttachment ? options.attachmentAccept ?? \"image/*\" : null,\n attachments: [],\n voice: {\n available: false,\n state: \"idle\",\n handsFree: false,\n caption: \"\",\n notice: null,\n start: this.startVoiceRequest,\n stop: this.stopVoice,\n press: (options) => this.voiceSession?.beginUtterance(options),\n release: () => this.voiceSession?.endUtterance(),\n confirm: () => this.voiceSession?.confirmUtterance(),\n discard: () => this.voiceSession?.discardUtterance(),\n onLevel: (listener) => {\n this.levelListeners.add(listener);\n return () => {\n this.levelListeners.delete(listener);\n };\n },\n },\n };\n }\n\n // ---- state -------------------------------------------------------------\n\n /** The current state. The same object until something changes. */\n getState = (): MiheState => this.state;\n\n /** Called after every change of state. Returns the unsubscribe. */\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n };\n\n /** Voice controls and state; the same as `getState().voice`. */\n get voice(): VoiceControls {\n return this.state.voice;\n }\n\n private setState(patch: Partial<MiheState>): void {\n this.state = { ...this.state, ...patch };\n // Copied, so a listener that unsubscribes while being called is safe.\n for (const listener of [...this.listeners]) listener();\n }\n\n private setVoice(patch: Partial<VoiceControls>): void {\n this.setState({ voice: { ...this.state.voice, ...patch } });\n }\n\n private setMessages(update: (messages: ChatMessage[]) => ChatMessage[]): void {\n this.setState({ messages: update(this.state.messages) });\n }\n\n // ---- lifecycle ---------------------------------------------------------\n\n /**\n * Replace options. Callbacks take effect at once; a new `url`, `publicKey`\n * or `debug` reconnects, since each belongs to the session.\n */\n update(options: Partial<MiheOptions>): void {\n const previous = this.options;\n this.options = { ...previous, ...options };\n const attachAccept = this.options.describeAttachment ? this.options.attachmentAccept ?? \"image/*\" : null;\n if (attachAccept !== this.state.attachAccept) this.setState({ attachAccept });\n const identityChanged =\n this.options.url !== previous.url ||\n this.options.publicKey !== previous.publicKey ||\n Boolean(this.options.debug) !== Boolean(previous.debug);\n if (identityChanged && this.client) {\n this.disconnect();\n this.connect();\n }\n }\n\n /** Open the session. Called by `createMihe`; does nothing if connected. */\n connect(): void {\n if (this.client) return;\n const client = new AssistantClient({\n url: this.options.url,\n publicKey: this.options.publicKey,\n getUserToken: () => this.options.getUserToken(),\n sdkVersion: SDK_VERSION,\n onEvent: this.handleEvent,\n debug: this.options.debug ?? false,\n });\n this.client = client;\n\n // Debounced, because a screen with twenty components fires twenty\n // registrations as it mounts and they should travel as one snapshot.\n this.unsubscribeStore = this.store.subscribe(() => {\n if (this.publishTimer !== null) clearTimeout(this.publishTimer);\n this.publishTimer = setTimeout(this.publish, SCREEN_DEBOUNCE_MS);\n });\n this.publish();\n\n void client.connect();\n }\n\n /**\n * Close the session, the voice session and any highlight. The\n * conversation and the declarations stay, for a later `connect()`.\n */\n disconnect(): void {\n if (this.publishTimer !== null) clearTimeout(this.publishTimer);\n this.publishTimer = null;\n this.unsubscribeStore?.();\n this.unsubscribeStore = null;\n this.client?.close();\n this.client = null;\n void this.voiceSession?.stop();\n this.voiceSession = null;\n this.highlighter.clear();\n }\n\n /** Disconnect and drop every listener. The instance is done after this. */\n destroy(): void {\n this.disconnect();\n this.listeners.clear();\n this.levelListeners.clear();\n }\n\n // ---- conversation ------------------------------------------------------\n\n send = (text: string): void => {\n const trimmed = text.trim();\n const ready = this.state.attachments.filter((a) => a.status === \"ready\");\n // A picture still being read would arrive without its words; the send button waits too.\n if (this.state.attachments.some((a) => a.status === \"reading\")) return;\n if (!trimmed && !ready.length) return;\n\n // What the assistant reads: the words, then each picture as the app described it.\n const wire = [\n trimmed || \"(See the attached picture.)\",\n ...ready.map((a) => `[Attached picture \"${a.name}\", as the app describes it: ${a.text}]`),\n ].join(\"\\n\\n\");\n\n // Opening on send is what makes a \"Help me add a product\" button work as\n // one call: the reply has to be visible somewhere. Harmless for host apps\n // rendering their own always-visible panel.\n const id = messageId(\"msg\");\n const shown: ChatMessage = { id, role: \"user\", text: trimmed, ...(ready.length ? { attachments: ready.map((a) => ({ name: a.name, ...(a.previewUrl ? { previewUrl: a.previewUrl } : {}) })) } : {}) };\n // Failed chips go with the send too; their previews aren't shown anywhere now.\n for (const a of this.state.attachments) if (a.status === \"failed\" && a.previewUrl) URL.revokeObjectURL(a.previewUrl);\n this.setState({\n isOpen: true,\n messages: [...this.state.messages, shown],\n attachments: [],\n lastError: null,\n });\n this.client?.sendMessage(wire, id);\n };\n\n /**\n * Attach a picture to the next message (amendment 102). Does nothing when\n * the app takes no attachments. The app describes it now, so the words are\n * ready by the time the person presses send.\n */\n attach = (file: File): void => {\n const describe = this.options.describeAttachment;\n if (!describe) return;\n const patch = (id: string, change: Partial<PendingAttachment>) =>\n this.setState({ attachments: this.state.attachments.map((a) => (a.id === id ? { ...a, ...change } : a)) });\n if (this.state.attachments.length >= MAX_ATTACHMENTS) {\n this.setState({ lastError: `Up to ${MAX_ATTACHMENTS} pictures at a time.` });\n return;\n }\n if (!acceptsFile(this.state.attachAccept ?? \"\", file)) {\n this.setState({ lastError: `\"${file.name}\" isn't a kind of file this can read.` });\n return;\n }\n const id = messageId(\"att\");\n const previewUrl = file.type.startsWith(\"image/\") && typeof URL.createObjectURL === \"function\" ? URL.createObjectURL(file) : undefined;\n this.setState({ attachments: [...this.state.attachments, { id, name: file.name || \"picture\", status: \"reading\", ...(previewUrl ? { previewUrl } : {}) }], lastError: null });\n let timer: ReturnType<typeof setTimeout> | undefined;\n Promise.race([\n Promise.resolve().then(() => describe(file)),\n new Promise<never>((_, reject) => { timer = setTimeout(() => reject(new Error(\"It took too long to read.\")), ATTACHMENT_TIMEOUT_MS); }),\n ])\n .then((text) => {\n const words = typeof text === \"string\" ? text.trim() : \"\";\n patch(id, words ? { status: \"ready\", text: words } : { status: \"failed\", error: \"Nothing could be read from it.\" });\n // On a call nobody presses Send: a picture goes as soon as it's read, so\n // what's said next can be about it (amendment 102). Once all are read.\n if (words && this.onCall() && !this.state.attachments.some((a) => a.status === \"reading\")) this.send(\"\");\n })\n .catch((cause: unknown) => patch(id, { status: \"failed\", error: cause instanceof Error && cause.message ? cause.message : \"It couldn't be read.\" }))\n .finally(() => clearTimeout(timer));\n };\n\n /** A voice call is open (any state but idle, connecting or error). */\n private onCall(): boolean {\n return ![\"idle\", \"connecting\", \"error\"].includes(this.state.voice.state);\n }\n\n /** Take a waiting picture off the next message. */\n removeAttachment = (id: string): void => {\n const gone = this.state.attachments.find((a) => a.id === id);\n if (!gone) return;\n if (gone.previewUrl) URL.revokeObjectURL(gone.previewUrl);\n this.setState({ attachments: this.state.attachments.filter((a) => a.id !== id) });\n };\n\n /**\n * Work out what a card says (amendment 99). The app's description when it\n * gives one in time, else the registry's hints. Until then the card is\n * pending, so nobody confirms a card whose words are about to change.\n */\n private describe(proposal: ProposalPayload): void {\n const set = (view: CardView | \"pending\") => {\n // A card already answered or withdrawn doesn't come back.\n if (view !== \"pending\" && !this.state.proposals.some((p) => p.proposalId === proposal.proposalId)) return;\n this.setState({ cardViews: { ...this.state.cardViews, [proposal.proposalId]: view } });\n };\n const describe = this.options.describeProposal;\n if (!describe) { set(hintedView(proposal)); return; }\n set(\"pending\");\n let settled = false;\n const finish = (d: ProposalDescription | null | undefined) => {\n if (settled) return; settled = true;\n set(d && Array.isArray(d.lines) ? appView(d) : hintedView(proposal));\n };\n const timer = setTimeout(() => finish(null), DESCRIBE_TIMEOUT_MS);\n Promise.resolve()\n .then(() => describe(proposal))\n .then((d) => { clearTimeout(timer); finish(d); })\n .catch((cause) => {\n clearTimeout(timer);\n console.warn(\"[mihe] describeProposal failed; the card uses the registry's hints.\", cause);\n finish(null);\n });\n }\n\n respondToProposal = (proposalId: string, confirmed: boolean): void => {\n this.client?.confirmAction(proposalId, confirmed);\n if (!confirmed) {\n this.setState({\n proposals: this.state.proposals.filter((p) => p.proposalId !== proposalId),\n });\n }\n };\n\n open = (): void => {\n if (!this.state.isOpen) this.setState({ isOpen: true });\n };\n\n close = (): void => {\n if (this.state.isOpen) this.setState({ isOpen: false });\n };\n\n toggle = (): void => {\n this.setState({ isOpen: !this.state.isOpen });\n };\n\n /**\n * A one-tap \"ask this\", for suggestion chips and help links:\n * `chip.onclick = mihe.prompt(\"How do I add a product?\")`.\n */\n prompt(text: string): () => void {\n return () => this.send(text);\n }\n\n // ---- declarations ------------------------------------------------------\n\n /**\n * Declare the screen the user is on. Call it again when what the screen\n * shows changes; call the returned function when the user leaves it.\n *\n * `visibleData` is what makes answers concrete \u2014 \"you have 7 low-stock\n * products\" rather than \"check your stock levels\" \u2014 so include the summary\n * values already on screen, and only those.\n */\n setScreen(screen: ScreenDeclaration): () => void {\n const serialized = JSON.stringify(screen);\n // A render function that declares on every render would otherwise send\n // the same screen over the socket each time.\n if (serialized !== this.lastScreen || this.store.getScreenContext()?.screenId !== screen.screenId) {\n this.lastScreen = serialized;\n this.store.setScreen(JSON.parse(serialized) as ScreenDeclaration);\n }\n return () => {\n this.store.clearScreen(screen.screenId);\n if (this.store.getScreenContext() === null) this.lastScreen = null;\n };\n }\n\n /**\n * Declare a component the assistant can talk about or point at. Marks\n * `element` so it can be found; pass `null` for one that is described but\n * cannot be highlighted.\n */\n registerComponent(\n element: Element | null,\n componentKey: string,\n meta: ComponentDeclaration,\n ): () => void {\n element?.setAttribute(COMPONENT_ATTRIBUTE, componentKey);\n this.store.registerComponent(componentKey, { ...meta });\n return () => {\n this.store.unregisterComponent(componentKey);\n if (element?.getAttribute(COMPONENT_ATTRIBUTE) === componentKey) {\n element.removeAttribute(COMPONENT_ATTRIBUTE);\n }\n };\n }\n\n /**\n * Supply the code for an action the assistant may run in the browser.\n *\n * The action itself \u2014 its risk tier, permissions, and whether it needs\n * confirmation \u2014 is configured in the dashboard, not here. Whatever the\n * handler returns becomes the tool result the model sees, so return\n * something descriptive. Throwing reports the failure honestly.\n */\n registerAction(name: string, handler: ActionHandler[\"handler\"]): () => void {\n const existing = this.actions.get(name);\n if (existing && this.store.getAction(name) === existing.declaration) {\n existing.latest = handler;\n return () => this.undoAction(name, existing);\n }\n const entry: Declared<ActionHandler[\"handler\"], ActionHandler> = {\n latest: handler,\n signature: name,\n declaration: { name, handler: (input) => entry.latest(input) },\n };\n this.actions.set(name, entry);\n this.store.registerAction(entry.declaration);\n return () => this.undoAction(name, entry);\n }\n\n private undoAction(name: string, entry: Declared<ActionHandler[\"handler\"], ActionHandler>) {\n if (this.actions.get(name) !== entry) return;\n this.actions.delete(name);\n if (this.store.getAction(name) === entry.declaration) this.store.unregisterAction(name);\n }\n\n /**\n * Let the assistant see which tab is open and switch to another. Pass the\n * tab that is really showing as `value`, and call this again when it\n * changes: a switch is not reported done until `value` matches.\n */\n registerTabs(tabs: AssistantTabs): () => void {\n return this.declare(\n this.tabs,\n tabs.id,\n tabs,\n tabsSignature(tabs),\n (latest) => tabsDeclaration(latest),\n (id) => this.store.getControl(id),\n (id, declaration) => this.store.registerControl(id, declaration),\n (id) => this.store.unregisterControl(id),\n );\n }\n\n /**\n * Let the assistant read a table. The rows are not sent with the screen;\n * the model reads them when a question needs them, and only the declared\n * columns ever leave the browser (amendment 67). Call it again when the\n * rows change.\n */\n registerTable<Row>(table: AssistantTable<Row>): () => void {\n return this.declare(\n this.tables,\n table.id,\n table as AssistantTable<unknown>,\n tableSignature(table),\n (latest) => tableDeclaration(latest),\n (id) => this.store.getTable(id),\n (id, declaration) => this.store.registerTable(id, declaration),\n (id) => this.store.unregisterTable(id),\n );\n }\n\n /**\n * Let the assistant explain a chart. Each series is summarised here, in\n * the browser; the points are read when an exact value is wanted. Call it\n * again when the data changes.\n */\n registerChart<Datum>(chart: AssistantChart<Datum>): () => void {\n return this.declare(\n this.charts,\n chart.id,\n chart as AssistantChart<unknown>,\n chartSignature(chart),\n (latest) => chartDeclaration(latest),\n (id) => this.store.getChart(id),\n (id, declaration) => this.store.registerChart(id, declaration),\n (id) => this.store.unregisterChart(id),\n );\n }\n\n /**\n * Add figures to the screen's `visibleData` under `key`: the stat cards\n * inside a tab, a summary panel. Dropped when the screen changes.\n */\n setValues(key: string, values: Record<string, unknown>): () => void {\n this.store.setValues(key, JSON.parse(JSON.stringify(values)) as Record<string, unknown>);\n return () => this.store.clearValues(key);\n }\n\n /**\n * One declaration kind's register-update-undo, shared so tabs, tables and\n * charts behave the same. The store's entry is re-registered only when the\n * signature changes, or when the store no longer holds it because the\n * screen changed underneath.\n */\n private declare<T, D>(\n entries: Map<string, Declared<T, unknown>>,\n id: string,\n value: T,\n signature: string,\n build: (latest: () => T) => D,\n current: (id: string) => D | undefined,\n register: (id: string, declaration: D) => void,\n unregister: (id: string) => void,\n ): () => void {\n let entry = entries.get(id) as Declared<T, D> | undefined;\n if (entry && current(id) === entry.declaration) {\n entry.latest = value;\n if (entry.signature !== signature) {\n const target = entry;\n entry.signature = signature;\n entry.declaration = build(() => target.latest);\n register(id, entry.declaration);\n }\n } else {\n const fresh = { latest: value, signature } as Declared<T, D>;\n fresh.declaration = build(() => fresh.latest);\n entries.set(id, fresh as Declared<T, unknown>);\n register(id, fresh.declaration);\n entry = fresh;\n }\n const mine = entry;\n return () => {\n if (entries.get(id) !== mine) return;\n entries.delete(id);\n if (current(id) === mine.declaration) unregister(id);\n };\n }\n\n /**\n * Make an element open and close the assistant, keyboard and screen\n * reader included: a `div` with only a click handler is invisible to both,\n * and that is the most common way a custom trigger ships broken.\n */\n bindTrigger(element: HTMLElement): () => void {\n const native = element instanceof HTMLButtonElement;\n const added: string[] = [\"aria-expanded\", \"aria-controls\", \"aria-haspopup\"];\n if (!native) {\n element.setAttribute(\"role\", \"button\");\n added.push(\"role\");\n if (!element.hasAttribute(\"tabindex\")) {\n element.setAttribute(\"tabindex\", \"0\");\n added.push(\"tabindex\");\n }\n }\n element.setAttribute(\"aria-controls\", ASSISTANT_PANEL_ID);\n element.setAttribute(\"aria-haspopup\", \"dialog\");\n\n const sync = () => element.setAttribute(\"aria-expanded\", String(this.state.isOpen));\n const onClick = () => this.toggle();\n // Space and Enter are what a native button responds to; a button\n // already turns them into a click.\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n this.toggle();\n }\n };\n\n sync();\n const unsubscribe = this.subscribe(sync);\n element.addEventListener(\"click\", onClick);\n if (!native) element.addEventListener(\"keydown\", onKeyDown);\n\n return () => {\n unsubscribe();\n element.removeEventListener(\"click\", onClick);\n element.removeEventListener(\"keydown\", onKeyDown);\n for (const attribute of added) element.removeAttribute(attribute);\n };\n }\n\n // ---- the runtime's events ---------------------------------------------\n\n private handleEvent = (event: ClientEvent): void => {\n switch (event.type) {\n case \"state\":\n this.setState({ connection: event.state });\n // Re-sent on every change of connection, so a reconnected session\n // learns the screen without waiting for the user to move.\n if (this.publishTimer !== null) clearTimeout(this.publishTimer);\n this.publishTimer = null;\n this.publish();\n break;\n\n case \"turn_start\":\n this.setState({\n busy: true,\n messages: [\n ...this.state.messages,\n { id: event.turnId, role: \"assistant\", text: \"\", streaming: true },\n ],\n });\n break;\n\n case \"delta\":\n this.setMessages((current) =>\n current.map((message) =>\n message.id === event.turnId\n ? { ...message, text: message.text + event.text }\n : message,\n ),\n );\n break;\n\n case \"complete\":\n this.setState({\n busy: false,\n messages: this.state.messages.map((message) =>\n message.id === event.turnId\n ? {\n ...message,\n text: event.message,\n streaming: false,\n sources: event.sources.length ? event.sources : undefined,\n }\n : message,\n ),\n });\n break;\n\n case \"command\": {\n const ctx = {\n store: this.store,\n highlighter: this.highlighter,\n navigate:\n this.options.navigate ??\n ((route: string) => {\n window.location.assign(route);\n }),\n screenPublished: this.awaitScreenPublished,\n viewPublished: this.awaitViewPublished,\n openModal: this.options.openModal,\n };\n const client = this.client;\n // Always acks, including on a thrown handler, because the runtime is\n // blocking a turn until it hears back.\n void executeCommand(event.command, ctx)\n .then((result) =>\n client?.ackCommand(event.commandId, result.ok, result.result, result.error),\n )\n .catch((cause: unknown) =>\n client?.ackCommand(\n event.commandId,\n false,\n undefined,\n cause instanceof Error ? cause.message : \"The command failed.\",\n ),\n );\n break;\n }\n\n case \"proposal\":\n this.setState({ proposals: [...this.state.proposals, event.proposal] });\n this.describe(event.proposal);\n break;\n\n case \"action_status\": {\n if (event.status === \"executing\") break;\n\n // Retire the card and say what became of it. Without this the card\n // simply vanishes on approval and the assistant stays silent \u2014 which\n // reads as nothing having happened, for an action that may have just\n // notified a customer. The model does learn the outcome, but not\n // until its next turn, so it cannot be what tells the user.\n const settled = this.state.proposals.find((p) => p.proposalId === event.proposalId);\n\n if (settled) {\n // Keyed by the proposal, so a status arriving twice \u2014 a reconnect\n // replaying it, a double tap \u2014 settles to one line rather than two.\n const statusId = `status_${event.proposalId}`;\n if (event.status !== \"rejected\" && !this.state.messages.some((m) => m.id === statusId)) {\n // The label comes from the registry, like everything else on the\n // card, so this line cannot be steered by model output.\n const outcome =\n event.status === \"executed\"\n ? `${settled.label} \u2014 done.`\n : `${settled.label} \u2014 ${event.error ?? \"that did not work.\"}`;\n this.setMessages((messages) => [\n ...messages,\n { id: statusId, role: \"assistant\", text: outcome },\n ]);\n }\n\n // Host apps cache \u2014 an action that changed data server-side leaves\n // the page showing what was true a moment ago.\n this.options.onActionComplete?.({\n actionName: settled.actionName,\n status: event.status,\n result: event.result,\n });\n }\n\n this.setState({\n proposals: this.state.proposals.filter((p) => p.proposalId !== event.proposalId),\n ...(event.error ? { lastError: event.error } : {}),\n });\n break;\n }\n\n case \"voice_available\":\n this.setVoice({ available: event.available });\n break;\n\n case \"assistant_name\":\n this.setState({ assistantName: event.name });\n break;\n\n case \"launcher_icon\":\n this.setState({ launcherIconUrl: event.url });\n break;\n\n case \"widget_settings\":\n this.setState({ widgetSettings: event.settings });\n break;\n\n case \"context_report\":\n console.info(formatContextReport(event.report));\n break;\n\n case \"voice_token\":\n // Only ever the answer to a session the user actually asked for. It\n // used to double as an availability probe, which meant minting a\n // single-use credential to answer a yes/no question.\n if (!this.voiceRequested) break;\n this.voiceRequested = false;\n void this.startVoice(event.token);\n break;\n\n case \"error\":\n // Voice refusals are not chat errors and must not be shown as one:\n // the chat socket is fine, and the honest reading is \"voice is not\n // available here\", which is a button that should disappear.\n if (event.code.startsWith(\"voice_\")) {\n this.voiceRequested = false;\n this.setState({ busy: false, voice: { ...this.state.voice, available: false } });\n break;\n }\n this.setState({ busy: false, lastError: event.message });\n break;\n }\n };\n\n // ---- screen publishing -------------------------------------------------\n\n private publish = (): void => {\n this.publishTimer = null;\n const screen = this.store.getScreenContext();\n\n // A null screen is published, not skipped. The user has navigated to a\n // page the host app never declared, and the runtime has to be told \u2014\n // otherwise it answers with the last screen it heard about. Nothing is\n // sent before the first screen ever registers, since there is no move to\n // report yet.\n if (!screen && this.publishedRoute === null) return;\n\n this.client?.updateScreen(screen);\n this.publishedRoute = screen?.route ?? null;\n this.publishedScreen = screen;\n // Published either way, so the runtime knows where the user is and scopes\n // actions to it. But a navigation is not acked against a screen whose\n // data is still loading: the model's next step would read its empty\n // counts as the answer.\n this.publishedSettled = !this.store.isScreenLoading();\n if (!screen || !this.publishedSettled) return;\n // Copied, because a waiter that matches removes itself from the set.\n for (const waiter of [...this.screenWaiters]) waiter(screen);\n };\n\n /**\n * A navigate command cannot be acked until the destination has published\n * its context, or the runtime's next step would still be reasoning about\n * the screen the user just left. See `CommandContext.screenPublished`.\n */\n private awaitScreenPublished = (route: string, options?: { fresh?: boolean }) =>\n new Promise<boolean>((resolve) => {\n // Pages publish their path, never the query, so arrival is matched on\n // the path.\n const path = route.split(\"?\")[0];\n if (!options?.fresh && this.publishedRoute === path && this.publishedSettled) {\n return resolve(true);\n }\n const done = (arrived: boolean) => {\n this.screenWaiters.delete(waiter);\n clearTimeout(timer);\n resolve(arrived);\n };\n const waiter = (published: ScreenContext) => {\n if (published.route === path) done(true);\n };\n // Bounded, so a route the app never publishes stalls one turn rather\n // than hanging until the runtime's own ack timeout fires.\n const timer = setTimeout(() => done(false), SCREEN_ARRIVAL_TIMEOUT_MS);\n this.screenWaiters.add(waiter);\n });\n\n /**\n * A set_view ack waits the same way, for the tab the app was asked to show\n * to be the one the published screen reports. An app whose `onChange` did\n * nothing is reported as a failure rather than a switch that did not\n * happen.\n */\n private awaitViewPublished = (controlKey: string, value: string) =>\n new Promise<boolean>((resolve) => {\n const showing = (screen: ScreenContext | null) =>\n screen?.controls.some(\n (control) => control.controlKey === controlKey && control.value === value,\n ) ?? false;\n if (this.publishedSettled && showing(this.publishedScreen)) return resolve(true);\n\n const done = (shown: boolean) => {\n this.screenWaiters.delete(waiter);\n clearTimeout(timer);\n resolve(shown);\n };\n const waiter = (published: ScreenContext) => {\n if (showing(published)) done(true);\n };\n const timer = setTimeout(() => done(false), SCREEN_ARRIVAL_TIMEOUT_MS);\n this.screenWaiters.add(waiter);\n });\n\n // ---- voice -------------------------------------------------------------\n\n private startVoiceRequest = (options?: { handsFree?: boolean; openPanel?: boolean }) => {\n // A new session is not the place to still be reading why the last one\n // stopped.\n this.setVoice({ notice: null });\n // A fresh permit every time: they are single use and expire in a minute.\n this.requestedHandsFree = Boolean(options?.handsFree);\n this.requestedOpenPanel = options?.openPanel ?? true;\n this.voiceRequested = true;\n this.client?.requestVoiceToken();\n };\n\n private stopVoice = () => {\n this.voiceRequested = false;\n void this.voiceSession?.stop();\n this.voiceSession = null;\n // A countdown for a session the user has already closed is worse than no\n // notice: it is a wrong one.\n this.setVoice({ handsFree: false, notice: null });\n };\n\n private async startVoice(token: string): Promise<void> {\n const wanted = this.requestedHandsFree;\n this.setVoice({ handsFree: wanted });\n\n const session = new VoiceSession({\n url: voiceUrl(this.options.url, token, wanted),\n handsFree: wanted,\n onEvent: this.handleVoiceEvent,\n onLevel: (source, level) => {\n for (const listener of this.levelListeners) listener(source, level);\n },\n });\n this.voiceSession = session;\n // Opening the panel so what is said has somewhere to appear, for the\n // same reason `send` does, unless the caller shows voice itself.\n if (this.requestedOpenPanel) this.open();\n await session.start();\n }\n\n private handleVoiceEvent = (event: VoiceEvent): void => {\n switch (event.type) {\n case \"state\":\n this.setVoice({\n state: event.state,\n ...(event.state === \"idle\" || event.state === \"error\" ? { caption: \"\" } : {}),\n });\n break;\n\n case \"transcript\":\n // The caption is for words still being spoken. Once a line is final\n // it joins the transcript, so the caption is cleared rather than\n // showing the same sentence twice.\n if (event.final) {\n this.setState({\n voice: { ...this.state.voice, caption: \"\" },\n messages: [\n ...this.state.messages,\n { id: messageId(\"voice\"), role: event.role, text: event.text },\n ],\n });\n } else {\n this.setVoice({ caption: event.text });\n }\n break;\n\n case \"ending_soon\":\n this.setVoice({ notice: endingSoonNotice(event.secondsRemaining) });\n break;\n\n case \"ended\":\n // The runtime's own words, which say the conversation survives. The\n // session tore itself down, so nothing else will clear the mode; left\n // set, the next push-to-talk session would open as hands-free.\n this.setVoice({ caption: \"\", notice: event.message, handsFree: false });\n break;\n\n case \"error\":\n this.setState({ lastError: event.message });\n break;\n }\n };\n}\n\n/** Whether a file matches an `accept` list (\"image/*,.pdf\"). */\nexport function acceptsFile(accept: string, file: { name: string; type: string }): boolean {\n const name = file.name.toLowerCase();\n return accept.split(\",\").map((x) => x.trim().toLowerCase()).filter(Boolean).some((rule) =>\n rule.startsWith(\".\") ? name.endsWith(rule) : rule.endsWith(\"/*\") ? file.type.toLowerCase().startsWith(rule.slice(0, -1)) : file.type.toLowerCase() === rule);\n}\n", "/**\n * Building DOM without a framework, and without ever parsing a string.\n *\n * Text goes in as text nodes, so a product name holding `<img onerror=\u2026>`\n * is shown as those characters and never runs: the card's firebreak depends\n * on it. Nothing in the widget sets `innerHTML`.\n */\n\ntype Child = Node | string | null | undefined | false;\n\nexport interface Props {\n class?: string;\n /** Set with `setAttribute`; `false` and `undefined` leave it off. */\n attrs?: Record<string, string | number | boolean | undefined>;\n on?: { [K in keyof HTMLElementEventMap]?: (event: HTMLElementEventMap[K]) => void };\n}\n\nexport function h<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n props: Props = {},\n ...children: Child[]\n): HTMLElementTagNameMap[K] {\n const element = document.createElement(tag);\n if (props.class) element.className = props.class;\n setAttrs(element, props.attrs ?? {});\n for (const [type, listener] of Object.entries(props.on ?? {})) {\n element.addEventListener(type, listener as EventListener);\n }\n append(element, children);\n return element;\n}\n\nconst SVG = \"http://www.w3.org/2000/svg\";\n\nexport function svg(tag: string, attrs: Record<string, string | number>, ...children: Node[]): SVGElement {\n const element = document.createElementNS(SVG, tag);\n for (const [name, value] of Object.entries(attrs)) element.setAttribute(name, String(value));\n for (const child of children) element.appendChild(child);\n return element;\n}\n\nexport function setAttrs(element: Element, attrs: Record<string, string | number | boolean | undefined>): void {\n for (const [name, value] of Object.entries(attrs)) {\n if (value === undefined || value === false) element.removeAttribute(name);\n else element.setAttribute(name, value === true ? \"\" : String(value));\n }\n}\n\nexport function append(element: Node, children: Child[]): void {\n for (const child of children) {\n if (child === null || child === undefined || child === false) continue;\n element.appendChild(typeof child === \"string\" ? document.createTextNode(child) : child);\n }\n}\n\n/** Replace an element's children. */\nexport function replace(element: Node, ...children: Child[]): void {\n while (element.firstChild) element.removeChild(element.firstChild);\n append(element, children);\n}\n", "/**\n * The Mihe mark, the widget's default launcher icon (amendment 58).\n *\n * An oracle in an open ring, with one pair of arcs for its voice. Drawn inline\n * rather than loaded, so the default launcher needs no request and no\n * permission from the host page's Content-Security-Policy. A copy of the\n * React SDK's `mark.tsx`, which holds the same two paths.\n */\n\nimport { svg } from \"./dom\";\n\nconst TRANSFORM = \"translate(0.000000,1760.000000) scale(0.100000,-0.100000)\";\n\n/** The figure and the ring. */\nconst BODY = \"M8165 16868 c-450 -31 -1057 -131 -1495 -248 -76 -20 -156 -42 -180 -50 -14 -5 -50 -15 -80 -24 -146 -41 -273 -82 -360 -117 -25 -9 -74 -27 -110 -39 -36 -12 -74 -25 -85 -30 -11 -4 -40 -15 -65 -25 -25 -10 -54 -21 -65 -26 -11 -4 -85 -35 -165 -67 -218 -90 -530 -238 -715 -340 -38 -21 -106 -58 -150 -82 -44 -24 -108 -61 -143 -82 -34 -21 -65 -38 -68 -38 -2 0 -17 -8 -32 -19 -15 -10 -52 -34 -82 -53 -1161 -733 -2106 -1735 -2771 -2935 -74 -134 -219 -425 -219 -440 0 -4 -10 -24 -23 -43 -13 -19 -34 -60 -46 -90 -13 -30 -35 -80 -48 -110 -49 -112 -56 -127 -91 -220 -16 -41 -32 -84 -37 -95 -45 -114 -195 -563 -238 -715 -26 -88 -58 -212 -67 -255 -5 -22 -16 -69 -25 -105 -19 -78 -66 -317 -89 -455 -140 -836 -144 -1701 -11 -2525 9 -52 20 -122 25 -155 9 -53 17 -97 51 -262 16 -78 80 -337 105 -423 74 -256 219 -700 264 -810 26 -63 55 -135 79 -192 6 -13 24 -54 40 -91 17 -38 31 -70 31 -72 0 -3 105 -221 203 -425 39 -79 80 -154 128 -233 16 -26 29 -50 29 -52 0 -3 37 -65 82 -138 45 -72 88 -143 95 -157 64 -113 285 -440 339 -500 12 -14 43 -56 70 -95 42 -63 164 -220 221 -285 12 -14 36 -43 55 -66 250 -310 644 -706 1038 -1045 68 -59 396 -310 514 -395 88 -63 522 -341 596 -382 19 -10 80 -45 135 -77 120 -69 571 -298 670 -339 39 -16 107 -47 153 -68 46 -21 87 -38 93 -38 5 0 17 -4 27 -10 25 -13 78 -35 157 -63 39 -14 120 -43 180 -65 222 -82 417 -145 565 -183 41 -10 91 -23 110 -29 134 -37 514 -119 645 -139 836 -130 1685 -150 2385 -57 63 8 151 20 195 26 44 6 100 15 125 20 25 5 88 17 140 25 286 49 729 160 1015 256 198 67 349 119 370 129 11 6 72 30 135 54 63 24 142 56 175 71 103 46 118 52 183 79 53 22 224 108 572 287 750 385 1586 1059 2290 1844 219 244 499 612 666 873 32 52 91 144 131 205 91 141 268 456 366 652 87 172 167 341 167 350 0 4 6 21 14 38 56 124 212 529 241 627 9 30 21 64 26 75 4 11 16 45 25 75 9 30 25 82 36 115 54 166 146 535 178 710 82 461 106 640 137 1030 20 252 23 814 5 1015 -6 74 -17 191 -23 260 -12 141 -34 328 -49 410 -5 30 -16 100 -25 155 -9 55 -26 145 -37 200 -11 55 -24 118 -28 140 -4 22 -16 72 -25 110 -9 39 -20 86 -24 105 -203 889 -617 1816 -1181 2640 -141 205 -236 333 -350 470 -52 63 -113 138 -135 165 -203 255 -745 786 -1035 1016 -58 45 -138 109 -179 142 -228 181 -749 524 -1006 662 -49 26 -115 62 -145 78 -71 38 -346 176 -430 215 -138 64 -201 92 -206 92 -3 0 -35 14 -71 30 -35 17 -70 30 -77 30 -6 0 -16 4 -21 8 -9 8 -216 87 -387 148 -38 13 -98 35 -135 49 -38 13 -102 34 -143 45 -41 12 -97 28 -125 36 -180 53 -464 125 -610 154 -165 33 -214 43 -240 47 -16 2 -66 10 -110 18 -328 56 -573 84 -755 88 l-125 2 0 -515 0 -515 190 -21 c105 -12 242 -29 305 -39 63 -10 138 -21 165 -26 123 -18 192 -30 225 -39 19 -5 62 -14 95 -20 233 -44 615 -154 905 -260 47 -17 105 -38 130 -47 25 -8 59 -21 75 -28 17 -7 93 -39 170 -71 418 -173 867 -425 1345 -757 747 -518 1525 -1379 1970 -2182 18 -33 41 -73 50 -90 64 -116 84 -152 123 -231 25 -49 57 -118 72 -154 15 -36 36 -85 47 -110 42 -93 56 -127 78 -180 12 -30 26 -64 31 -75 61 -146 144 -376 194 -540 15 -49 31 -101 36 -115 9 -26 46 -175 74 -295 8 -38 20 -86 25 -105 15 -58 45 -214 80 -425 24 -141 62 -499 79 -745 34 -482 -17 -1111 -134 -1655 -14 -66 -30 -140 -35 -165 -8 -38 -22 -93 -65 -260 -28 -107 -86 -292 -127 -405 -59 -164 -118 -318 -128 -337 -6 -10 -10 -22 -10 -27 0 -6 -18 -49 -40 -96 -22 -47 -40 -88 -40 -90 0 -27 -247 -518 -314 -624 -14 -22 -26 -43 -26 -45 0 -37 -381 -596 -565 -829 -225 -283 -622 -706 -875 -929 -155 -137 -228 -198 -237 -198 -9 0 -127 185 -183 285 -12 22 -28 47 -35 55 -7 8 -32 49 -55 90 -24 41 -76 125 -115 185 -40 61 -123 195 -186 298 -63 103 -126 207 -141 230 -15 23 -31 49 -35 57 -3 8 -16 29 -27 45 -12 17 -26 39 -33 50 -7 11 -33 54 -59 95 -67 106 -620 996 -1072 1725 -58 94 -129 206 -158 251 -29 45 -65 103 -80 130 -15 27 -55 92 -89 144 -88 134 -280 445 -400 645 -56 94 -163 269 -239 389 -75 121 -135 224 -134 228 2 5 35 54 73 109 403 577 447 641 713 1032 134 197 262 399 262 413 0 8 -36 85 -79 172 -44 86 -142 285 -219 442 -77 157 -244 497 -372 755 -128 259 -297 601 -375 760 -78 160 -220 448 -315 640 -95 193 -264 535 -376 760 -400 810 -543 1101 -566 1155 -14 30 -33 74 -43 98 -17 39 -55 61 -55 31 0 -6 -35 -83 -79 -172 -43 -89 -106 -218 -139 -287 -68 -142 -323 -661 -607 -1235 -106 -214 -271 -550 -368 -745 -225 -457 -457 -927 -747 -1515 -363 -735 -420 -852 -420 -862 0 -10 166 -267 241 -373 21 -30 95 -138 164 -240 68 -102 160 -237 204 -300 80 -115 147 -212 296 -430 43 -63 93 -135 112 -159 42 -56 42 -57 -11 -134 -24 -35 -58 -90 -76 -122 -29 -52 -66 -114 -107 -180 -7 -11 -20 -33 -29 -50 -9 -16 -45 -73 -79 -125 -34 -52 -74 -117 -89 -145 -16 -27 -34 -57 -42 -66 -8 -8 -14 -19 -14 -23 0 -4 -10 -22 -23 -39 -12 -18 -28 -41 -34 -52 -7 -11 -33 -54 -59 -95 -26 -41 -58 -95 -72 -120 -13 -25 -35 -58 -47 -75 -21 -29 -238 -378 -619 -995 -96 -157 -210 -339 -252 -405 -104 -164 -269 -429 -343 -550 -33 -55 -92 -149 -131 -210 -38 -60 -78 -126 -90 -145 -11 -19 -25 -42 -30 -50 -5 -8 -44 -71 -86 -140 -42 -69 -104 -168 -138 -220 -34 -53 -66 -104 -73 -115 -6 -11 -76 -123 -155 -249 -79 -126 -145 -234 -147 -240 -2 -6 -21 -36 -41 -66 -76 -112 -170 -260 -170 -267 0 -5 -14 -24 -30 -43 -29 -35 -29 -35 -82 16 -29 28 -91 84 -138 124 -567 490 -1171 1224 -1508 1832 -47 85 -232 451 -274 543 -20 44 -53 115 -72 158 -20 42 -36 80 -36 83 0 3 -6 20 -14 37 -20 47 -40 99 -85 222 -79 217 -90 252 -140 420 -49 167 -85 311 -141 558 -120 534 -163 1390 -99 1962 41 369 37 343 84 595 48 260 75 373 159 683 39 143 224 654 286 792 110 242 174 371 314 630 563 1039 1532 2008 2644 2642 68 38 455 233 532 267 131 58 397 168 465 192 108 39 384 130 445 148 30 8 87 25 125 36 316 96 789 185 1190 225 94 9 176 20 183 24 12 5 16 287 12 1022 0 13 -27 15 -135 7z m660 -4508 c4 -16 11 -32 16 -35 5 -4 9 -12 9 -20 0 -8 19 -53 41 -102 71 -151 106 -226 129 -278 12 -27 39 -86 60 -130 21 -44 52 -111 70 -150 17 -38 44 -97 60 -130 15 -33 35 -76 43 -95 8 -19 31 -69 50 -110 62 -134 89 -194 107 -240 10 -25 46 -103 80 -175 90 -190 106 -224 133 -285 49 -108 70 -156 97 -215 15 -33 37 -82 50 -110 12 -27 40 -87 61 -133 40 -83 40 -83 6 -140 -19 -31 -50 -86 -69 -122 -65 -120 -91 -165 -140 -242 -26 -43 -48 -80 -48 -83 0 -2 -17 -33 -38 -67 -21 -35 -41 -70 -45 -78 -4 -8 -11 -22 -17 -30 -34 -54 -110 -181 -110 -185 0 -3 -11 -23 -25 -45 -14 -22 -46 -80 -72 -128 -25 -48 -54 -98 -63 -112 -41 -58 -150 -263 -150 -280 0 -10 15 -47 33 -82 62 -117 120 -223 128 -234 5 -6 48 -87 95 -180 47 -93 90 -173 94 -179 4 -5 27 -46 50 -90 23 -44 67 -127 98 -185 30 -58 59 -110 63 -116 11 -14 72 -130 111 -209 37 -77 56 -114 103 -200 142 -264 186 -348 323 -615 63 -124 128 -250 145 -280 33 -59 82 -154 250 -480 58 -113 129 -250 157 -305 96 -182 151 -288 268 -515 63 -124 135 -263 160 -310 24 -47 118 -229 207 -405 170 -333 225 -440 277 -534 17 -31 112 -216 211 -411 99 -195 188 -372 199 -393 11 -22 18 -40 16 -42 -10 -11 -431 -215 -443 -215 -2 0 -36 -15 -77 -34 -40 -18 -109 -48 -153 -66 -44 -18 -93 -38 -110 -45 -16 -7 -75 -29 -130 -50 -55 -20 -116 -43 -135 -50 -39 -14 -282 -90 -345 -107 -22 -6 -60 -16 -85 -24 -61 -17 -216 -55 -265 -64 -22 -4 -85 -17 -140 -28 -165 -33 -277 -52 -513 -87 -183 -27 -182 -27 -182 78 0 34 -4 98 -10 142 -5 44 -14 141 -20 215 -24 285 -30 360 -45 530 -9 96 -20 229 -25 295 -5 66 -17 206 -25 310 -9 105 -20 249 -25 320 -5 72 -12 148 -15 170 -3 22 -10 105 -15 185 -5 80 -17 228 -25 330 -9 102 -20 237 -24 300 -5 63 -19 225 -30 360 -11 135 -27 335 -36 445 -30 392 -37 461 -50 510 -8 28 -26 97 -41 155 -14 58 -37 146 -50 195 -41 154 -55 209 -64 245 -9 33 -23 89 -55 205 -7 28 -25 100 -40 160 -24 98 -49 196 -61 233 -8 27 -28 5 -40 -44 -6 -27 -24 -94 -38 -149 -15 -55 -43 -161 -62 -235 -20 -74 -49 -187 -65 -250 -17 -63 -39 -151 -50 -195 -23 -92 -39 -157 -69 -270 -30 -113 -36 -170 -75 -720 -8 -110 -21 -276 -30 -370 -8 -93 -19 -226 -25 -295 -5 -69 -12 -143 -15 -165 -3 -22 -10 -105 -16 -185 -24 -330 -41 -552 -59 -745 -22 -237 -31 -345 -40 -460 -5 -66 -16 -201 -25 -300 -8 -99 -19 -234 -25 -300 -15 -193 -40 -482 -50 -590 -5 -55 -9 -106 -10 -112 0 -18 -71 -17 -184 2 -50 9 -122 20 -161 26 -108 16 -241 39 -345 61 -52 11 -113 24 -135 28 -52 11 -235 56 -300 75 -27 7 -75 20 -106 29 -62 17 -397 127 -444 146 -16 7 -39 16 -50 21 -11 5 -56 22 -100 39 -214 82 -663 292 -724 339 -23 18 -23 18 62 182 46 90 130 252 185 359 55 107 121 236 147 285 26 50 100 194 165 320 64 127 146 286 182 355 36 69 100 193 143 275 43 83 106 204 140 270 34 66 109 210 165 320 108 211 202 391 280 540 26 50 70 133 97 185 28 52 112 217 188 365 76 149 141 275 145 280 4 6 54 100 110 210 57 110 123 238 148 285 25 47 63 119 85 160 22 41 71 133 110 205 39 71 107 202 152 290 45 88 91 177 103 197 12 21 37 66 55 100 19 35 51 95 72 133 238 435 243 447 218 497 -34 66 -100 186 -137 247 -19 31 -43 72 -53 91 -65 116 -87 155 -142 248 -34 57 -61 106 -61 108 0 2 -10 21 -23 42 -13 20 -44 75 -69 122 -25 47 -73 132 -107 188 -33 57 -61 106 -61 108 0 2 -29 52 -65 110 -68 111 -74 132 -51 180 26 52 103 218 126 269 13 28 41 88 62 135 22 47 54 114 70 150 48 104 78 169 124 267 24 51 44 98 44 103 0 6 4 18 10 28 13 22 90 192 90 198 0 2 23 52 51 112 54 112 81 171 205 440 19 41 34 76 34 78 0 3 15 37 34 77 18 40 43 95 56 122 12 28 43 93 67 145 25 52 49 105 54 117 12 32 36 28 44 -7z\";\n\n/** The two arcs, the only part of the mark in colour. */\nconst VOICE = \"M4399 12732 c-37 -42 -101 -117 -143 -166 -123 -145 -496 -679 -496 -712 0 -2 -18 -34 -41 -71 -100 -164 -271 -530 -369 -793 -17 -47 -36 -96 -42 -110 -35 -86 -134 -426 -172 -595 -50 -218 -49 -218 -81 -445 -85 -598 -74 -1269 30 -1820 8 -47 20 -107 24 -135 11 -60 68 -292 87 -355 8 -25 21 -67 29 -95 23 -79 72 -227 85 -260 7 -16 16 -39 20 -50 34 -90 73 -186 108 -265 11 -25 30 -70 43 -100 45 -106 197 -400 256 -493 14 -22 36 -60 50 -85 97 -175 409 -612 437 -612 6 0 85 66 176 147 91 80 199 176 240 212 41 36 100 89 130 117 30 29 73 67 95 86 123 107 120 82 23 209 -209 275 -459 726 -589 1064 -112 292 -165 474 -219 750 -19 96 -26 136 -50 287 -70 444 -45 1047 65 1543 9 39 20 88 24 110 149 680 553 1448 1043 1980 15 17 28 33 28 36 0 11 -703 699 -714 699 -6 0 -41 -35 -77 -78z M12835 12532 c-154 -152 -313 -307 -354 -344 -84 -76 -85 -63 16 -174 227 -250 460 -601 637 -959 70 -144 93 -192 130 -280 10 -22 21 -49 26 -60 5 -11 16 -42 25 -70 10 -27 21 -59 26 -70 23 -55 77 -234 112 -370 45 -175 56 -227 78 -345 24 -127 27 -151 58 -425 26 -231 28 -544 6 -780 -17 -180 -30 -267 -85 -570 -9 -47 -39 -165 -89 -350 -23 -83 -54 -178 -76 -230 -7 -16 -19 -48 -28 -70 -144 -374 -335 -726 -576 -1060 -56 -77 -100 -145 -99 -151 2 -5 68 -68 148 -140 80 -71 172 -154 205 -185 138 -128 375 -329 388 -329 23 0 437 582 437 614 0 3 16 30 35 60 62 98 305 576 305 601 0 3 14 35 31 72 54 117 135 338 174 478 124 434 155 590 214 1065 67 536 5 1382 -141 1930 -11 41 -24 89 -28 105 -24 91 -128 414 -175 543 -34 95 -127 306 -196 447 -145 295 -273 511 -475 795 -154 217 -416 530 -443 530 -3 0 -132 -125 -286 -278z\";\n\nexport function miheMark(size: number, body: string, voice: string): SVGElement {\n const mark = svg(\n \"svg\",\n { viewBox: \"0 0 1760 1760\", width: size, height: size, \"aria-hidden\": \"true\", focusable: \"false\" },\n svg(\"path\", { fill: body, \"fill-rule\": \"evenodd\", transform: TRANSFORM, d: BODY }),\n svg(\"path\", { fill: voice, \"fill-rule\": \"evenodd\", transform: TRANSFORM, d: VOICE }),\n );\n mark.style.display = \"block\";\n return mark;\n}\n", "/**\n * The widget's stylesheet, inside its shadow root.\n *\n * The shadow root is what keeps the host page out: a Tailwind reset or a\n * site's `button { \u2026 }` cannot select anything in here, and nothing here can\n * select the page. Inherited properties still cross the boundary, which is\n * why `:host` resets them all.\n *\n * Theming is through custom properties set on the element, the one styling\n * hook a shadow root lets through by design:\n *\n * ```css\n * mihe-assistant { --mihe-accent: #0a7d5a; --mihe-radius: 10px; }\n * ```\n *\n * The values match the React widget's.\n */\n\nexport const LAUNCHER_BG = \"#221f1b\";\nexport const LAUNCHER_INK = \"#eae7e4\";\nexport const LAUNCHER_VOICE = \"#5ac8cd\";\n\nexport const STYLES = `\n:host {\n all: initial;\n --mihe-accent: #4f7cff;\n --mihe-radius: 14px;\n --mihe-font: system-ui, -apple-system, 'Segoe UI', sans-serif;\n --mihe-z: 2147482000;\n}\n:host([layout=\"inline\"]) { display: block; width: 100%; height: 100%; }\n*, *::before, *::after { box-sizing: border-box; }\n[hidden] { display: none !important; }\nbutton { font: inherit; color: inherit; margin: 0; }\n\n.panel {\n display: flex; flex-direction: column; width: 100%; height: 100%;\n background: #fff; color: #0f172a;\n border-radius: var(--mihe-radius); border: 1px solid #e2e8f0;\n box-shadow: 0 20px 50px rgba(15,23,42,0.22);\n font: 14px/1.5 var(--mihe-font); overflow: hidden;\n}\n.floating {\n position: fixed; bottom: 20px;\n width: min(380px, calc(100vw - 40px)); height: min(560px, calc(100vh - 40px));\n z-index: var(--mihe-z);\n}\n.floating.right { right: 20px; }\n.floating.left { left: 20px; }\n.header {\n display: flex; align-items: center; justify-content: space-between;\n padding: 12px 14px; border-bottom: 1px solid #e2e8f0; font-weight: 600;\n}\n.header-title { display: flex; align-items: center; gap: 8px; }\n.status-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; background: #f59e0b; }\n.status-dot.open { background: #22c55e; }\n.icon-button { border: none; background: transparent; cursor: pointer; font-size: 14px; color: #64748b; }\n.transcript { flex: 1; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 10px; }\n.empty { color: #64748b; margin: 0; }\n.bubble-user, .bubble-assistant { max-width: 85%; padding: 8px 12px; white-space: pre-wrap; overflow-wrap: anywhere; }\n.bubble-user { align-self: flex-end; background: var(--mihe-accent); color: #fff; border-radius: 12px 12px 2px 12px; }\n.bubble-assistant { align-self: flex-start; background: #f1f5f9; border-radius: 12px 12px 12px 2px; }\n.md { white-space: normal; }\n.md p, .md ul, .md ol { margin: 0; }\n.md > * + * { margin-top: 6px; }\n.md ul, .md ol { padding-left: 18px; }\n.md ul { list-style: disc; }\n.md ol { list-style: decimal; }\n.md code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.92em; background: rgba(15,23,42,0.06); padding: 0 3px; border-radius: 3px; }\n.md a { color: inherit; }\n.thinking { color: #64748b; font-style: italic; }\n.sources { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; padding-top: 8px; border-top: 1px solid #e2e8f0; }\n.source { font-size: 11px; color: #475569; background: #f1f5f9; padding: 2px 8px; border-radius: 999px; }\n.source a { color: inherit; text-decoration: none; }\n.error { background: #fef2f2; color: #991b1b; padding: 8px 10px; border-radius: 8px; font-size: 13px; }\n.notice { color: #64748b; font-size: 13px; text-align: center; }\n.voice-notice { background: #f1f5f9; color: #475569; padding: 8px 10px; border-radius: 8px; font-size: 13px; }\n.composer { display: flex; gap: 8px; padding: 12px; border-top: 1px solid #e2e8f0; }\n.panel-body { display: flex; flex-direction: column; flex: 1; min-height: 0; }\n.panel-body[hidden], .call[hidden], .talk[hidden] { display: none; }\n.talk { margin-left: auto; margin-right: 4px; min-height: 32px; padding: 0 14px; border-radius: 999px; border: 1px solid #cbd5e1; background: #fff; cursor: pointer; font: 600 13px system-ui, sans-serif; }\n.call { flex: 1; display: flex; flex-direction: column; justify-content: flex-end; gap: 8px; padding: 12px; border-top: 1px solid #e2e8f0; }\n.call-cards { display: flex; flex-direction: column; gap: 8px; }\n.call-failed { margin: 0; padding: 8px 12px; border-radius: 10px; background: #fef2f2; color: #991b1b; border: 1px solid #fecaca; font-size: 13px; }\n.call-failed[hidden] { display: none; }\n.call-bar { display: flex; align-items: center; gap: 12px; padding: 8px 8px 8px 12px; border-radius: 999px; background: #fff; border: 1px solid rgba(15,23,42,0.1); box-shadow: 0 6px 18px rgba(15,23,42,0.16); min-width: 0; }\n.call-mark { position: relative; display: grid; place-items: center; width: 36px; height: 36px; flex: none; }\n.call-ring { position: absolute; inset: 0; border-radius: 50%; background: rgba(0,114,119,0.14); transition: transform 60ms linear; }\n.call-line { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.call-hold { flex: none; width: auto; min-height: 36px; padding: 0 14px; border-radius: 999px; font: 600 13px system-ui, sans-serif; user-select: none; touch-action: none; }\n.call-hold[hidden] { display: none; }\n.call-hold.recording { background: #007277; border-color: #007277; color: #fff; }\n.call-end { flex: none; min-height: 36px; padding: 0 16px; border-radius: 999px; border: none; background: #b91c1c; color: #fff; cursor: pointer; font: 600 13px system-ui, sans-serif; }\n.clip { flex: none; width: 38px; display: grid; place-items: center; border-radius: 8px; border: 1px solid #cbd5e1; background: #fff; cursor: pointer; color: #475569; }\n.clip[hidden], .chips[hidden] { display: none; }\n.chips { list-style: none; margin: 0; padding: 8px 12px 0; display: flex; flex-wrap: wrap; gap: 6px; border-top: 1px solid #e2e8f0; }\n.chip { display: flex; align-items: center; gap: 6px; padding: 4px 4px 4px 6px; border-radius: 8px; background: #f1f5f9; max-width: 100%; font-size: 12px; }\n.chip.failed { background: #fef2f2; color: #991b1b; }\n.chip img { width: 28px; height: 28px; border-radius: 4px; object-fit: cover; flex: none; }\n.chip-text { display: flex; flex-direction: column; min-width: 0; }\n.chip-name { max-width: 140px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }\n.chip-status { opacity: 0.75; }\n.chip-remove { border: none; background: transparent; cursor: pointer; width: 28px; height: 28px; border-radius: 6px; color: inherit; font-size: 12px; }\n.sent-pictures { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 6px; }\n.sent-pictures img { width: 64px; height: 64px; border-radius: 6px; object-fit: cover; display: block; }\n.sent-name { font-size: 12px; opacity: 0.85; }\n.input {\n flex: 1; min-width: 0; padding: 9px 12px; border-radius: 8px; border: 1px solid #cbd5e1;\n font: 14px var(--mihe-font); color: #0f172a; background: #fff; outline: none;\n}\n.send {\n flex: none; padding: 9px 16px; border-radius: 8px; border: none; background: var(--mihe-accent);\n color: #fff; cursor: pointer; font: 600 13px var(--mihe-font);\n}\n.send:disabled { opacity: 0.6; cursor: default; }\n.mic {\n width: 38px; border-radius: 8px; border: 1px solid #cbd5e1; background: #fff; cursor: pointer;\n font-size: 16px; user-select: none; -webkit-user-select: none; touch-action: none;\n}\n.mic.live { border-color: var(--mihe-accent); }\n.mic.recording { border-color: #dc2626; background: #fef2f2; }\n.hands-free {\n width: 30px; border-radius: 8px; border: 1px solid #cbd5e1; background: #fff; color: #475569;\n cursor: pointer; font-size: 15px; user-select: none; -webkit-user-select: none;\n}\n.pulse {\n display: block; width: 12px; height: 12px; margin: 0 auto; border-radius: 50%;\n background: var(--mihe-accent); animation: mihe-pulse 2s ease-in-out infinite;\n}\n.pulse.fast { animation-duration: 0.8s; }\n@keyframes mihe-pulse { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(0.6); opacity: 0.45; } }\n\n.card {\n border: 1px solid #e2e8f0; border-radius: 10px; padding: 12px; background: #fff; color: #0f172a;\n box-shadow: 0 2px 8px rgba(15,23,42,0.06); font: 14px/1.5 var(--mihe-font);\n}\n.card-header { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 6px; }\n.risk { font-size: 11px; font-weight: 600; padding: 2px 8px; border-radius: 999px; white-space: nowrap; background: #fef3c7; color: #92400e; }\n.risk-high { background: #fee2e2; color: #991b1b; }\n.card-description { margin: 0 0 10px; color: #475569; font-size: 13px; }\n.card-input { margin: 0 0 12px; display: flex; flex-direction: column; gap: 8px; max-height: 40vh; overflow-y: auto; }\n.card-row { display: flex; flex-wrap: wrap; gap: 8px; font-size: 13px; }\n.card-key { color: #64748b; min-width: 90px; margin: 0; }\n.card-value { margin: 0; font-weight: 500; overflow-wrap: anywhere; min-width: 0; flex: 1 1 160px; }\n.input-list { margin: 0; padding-left: 20px; display: grid; gap: 8px; }\n.input-text { white-space: pre-wrap; }\n.card-actions { display: flex; gap: 8px; justify-content: flex-end; }\n.card-summary { margin: 0 0 10px; font-size: 14px; font-weight: 600; color: #0f172a; }\n.card-note { margin: 0 0 12px; font-size: 12px; color: #64748b; }\n.card-more { margin: 0 0 12px; font-size: 12px; color: #64748b; }\n.card-more summary { cursor: pointer; }\n.card-more .card-description { margin: 6px 0 0; }\n.card-value-wide { flex-basis: 100%; }\n.card-images { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 4px; }\n.card-images img { width: 44px; height: 44px; object-fit: cover; border-radius: 6px; background: #f1f5f9; }\n.card-before { color: #94a3b8; font-weight: 400; }\n.card-arrow { color: #94a3b8; }\n.button-primary:disabled { opacity: 0.5; cursor: default; }\n.button-danger { background: #dc2626; }\n.card-media-row { display: flex; gap: 10px; align-items: flex-start; font-size: 13px; }\n.card-media-row .card-images { flex: none; max-width: 140px; margin-bottom: 0; }\n.card-media-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1; }\n.card-media-label { margin: 0; font-weight: 600; color: #0f172a; }\n.card-media-value { margin: 0; overflow-wrap: anywhere; }\n.card-media-strong { font-weight: 600; }\n.button-secondary {\n padding: 7px 14px; border-radius: 8px; border: 1px solid #cbd5e1; background: #fff;\n cursor: pointer; font: 500 13px var(--mihe-font);\n}\n.button-primary {\n padding: 7px 14px; border-radius: 8px; border: none; background: var(--mihe-accent); color: #fff;\n cursor: pointer; font: 600 13px var(--mihe-font);\n}\n\n.launcher {\n position: fixed; right: 20px; bottom: 20px; width: 52px; height: 52px; border-radius: 50%;\n border: none; padding: 0; display: grid; place-items: center; overflow: hidden;\n background: ${LAUNCHER_BG}; color: ${LAUNCHER_INK}; cursor: pointer;\n box-shadow: 0 8px 24px rgba(15,23,42,0.25); z-index: var(--mihe-z);\n}\n.launcher-image { width: 100%; height: 100%; object-fit: cover; display: block; }\n\n.orb {\n position: fixed; width: 56px; height: 56px; padding: 0; border: none; border-radius: 50%;\n background: ${LAUNCHER_BG}; color: ${LAUNCHER_INK}; box-shadow: 0 8px 24px rgba(15,23,42,0.25);\n z-index: var(--mihe-z); cursor: pointer; transition: left 180ms ease, top 180ms ease;\n touch-action: none; user-select: none; -webkit-user-select: none; -webkit-touch-callout: none;\n -webkit-tap-highlight-color: transparent;\n}\n.orb.dragging { cursor: grabbing; transition: none; }\n.orb:disabled { cursor: default; }\n.face { position: absolute; inset: 0; display: grid; place-items: center; border-radius: 50%; overflow: hidden; pointer-events: none; }\n.ring {\n position: absolute; inset: -6px; border-radius: 50%; border: 2px solid ${LAUNCHER_VOICE};\n pointer-events: none; transform-origin: center; will-change: transform;\n}\n.ring.idle { border-color: transparent; }\n.ring.recording { border-color: #e5484d; border-width: 3px; }\n.ring.thinking, .ring.connecting { border-style: dashed; }\n.ring.listening { animation: mihe-orb-breathe 2.4s ease-in-out infinite; }\n.ring.thinking { animation: mihe-orb-spin 1.2s linear infinite; }\n.ring.connecting { animation: mihe-orb-spin 2.4s linear infinite; }\n@keyframes mihe-orb-breathe { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } }\n@keyframes mihe-orb-spin { to { rotate: 360deg; } }\n.stop {\n position: fixed; width: 44px; height: 44px; border-radius: 50%; border: 1px solid rgba(15,23,42,0.12);\n background: #fff; box-shadow: 0 4px 14px rgba(15,23,42,0.18); display: grid; place-items: center;\n cursor: pointer; z-index: var(--mihe-z);\n}\n.stop-square { width: 12px; height: 12px; border-radius: 2px; background: #0f172a; }\n.popover {\n position: fixed; max-width: min(340px, calc(100vw - 40px)); z-index: calc(var(--mihe-z) + 1);\n display: flex; flex-direction: column; gap: 8px; font: 14px/1.45 var(--mihe-font);\n}\n.popover.cards { width: 320px; }\n.menu-item {\n display: inline-flex; align-items: center; gap: 10px; min-height: 44px; padding: 0 16px 0 6px;\n border-radius: 999px; border: 1px solid rgba(15,23,42,0.1); background: #fff; color: #0f172a;\n box-shadow: 0 6px 18px rgba(15,23,42,0.16); cursor: pointer; font: 500 14px var(--mihe-font);\n}\n.menu-icon {\n display: grid; place-items: center; width: 34px; height: 34px; border-radius: 50%;\n background: ${LAUNCHER_BG}; color: ${LAUNCHER_VOICE};\n}\n.orb-bubble {\n padding: 8px 12px; border-radius: 12px; background: #fff; color: #0f172a;\n border: 1px solid rgba(15,23,42,0.1); box-shadow: 0 6px 18px rgba(15,23,42,0.16);\n}\n.orb-bubble.error { background: #fef2f2; color: #991b1b; border-color: #fecaca; }\n\n@media (prefers-reduced-motion: reduce) {\n .pulse, .ring { animation: none !important; }\n .ring { transform: none !important; }\n .orb { transition: none; }\n}\n`;\n", "/**\n * What the launcher and the orb show: the host's own icon, the customer's\n * uploaded icon, or the Mihe mark on the brand's warm ink.\n */\n\nimport { h } from \"./dom\";\nimport { miheMark } from \"./mark\";\nimport { LAUNCHER_INK, LAUNCHER_VOICE } from \"./styles\";\n\n/**\n * The uploaded image fills the circle, so what the dashboard's preview shows\n * is what a user sees. If it cannot load (a Content-Security-Policy that does\n * not allow the storage's domain, say), it falls back to the mark instead of\n * an empty circle: `onFail` is told which URL failed, and the next draw skips\n * it.\n */\nexport function launcherFace(\n override: Node | undefined,\n url: string | null,\n failed: string | null,\n onFail: (url: string) => void,\n): Node {\n if (override) return override.cloneNode(true);\n if (url && url !== failed) {\n return h(\"img\", {\n class: \"launcher-image\",\n attrs: { src: url, alt: \"\", draggable: \"false\" },\n on: { error: () => onFail(url) },\n });\n }\n return miheMark(34, LAUNCHER_INK, LAUNCHER_VOICE);\n}\n", "/**\n * The approval surface for a proposed action, shared by the panel and the orb.\n *\n * What it says comes from the card's view (amendment 99): the app's own\n * description of the input that will run, or the registry's display hints,\n * or, with neither, every field of the input by its key. Never the\n * assistant's prose, so text smuggled into a product name or a document\n * cannot change what a user is shown they are about to approve. Everything is\n * added as text, so markup in it is shown rather than run.\n *\n * High-risk actions require this tap \u2014 the runtime refuses a spoken or typed\n * \"yes\" for them \u2014 so this is a security boundary, not decoration.\n */\n\nimport { hintedView, type CardLine, type CardView } from \"../card-view\";\nimport type { ProposalPayload } from \"../client\";\nimport { h } from \"./dom\";\n\nexport function confirmationCard(\n proposal: ProposalPayload,\n onRespond: (proposalId: string, confirmed: boolean) => void,\n view: CardView | \"pending\" = hintedView(proposal),\n): HTMLElement {\n const high = proposal.risk === \"high\";\n const pending = view === \"pending\";\n const v = pending ? null : view;\n const description = v && v.source !== \"plain\"\n ? h(\"details\", { class: \"card-more\" }, h(\"summary\", {}, \"More about this\"), h(\"p\", { class: \"card-description\" }, proposal.description))\n : h(\"p\", { class: \"card-description\" }, proposal.description);\n\n return h(\n \"div\",\n { class: \"card\", attrs: { role: \"group\", \"aria-label\": `Confirm: ${proposal.label}`, \"aria-busy\": pending ? \"true\" : undefined } },\n h(\n \"div\",\n { class: \"card-header\" },\n h(\"strong\", {}, proposal.label),\n h(\"span\", { class: high ? \"risk risk-high\" : \"risk\" }, high ? \"Needs care\" : \"Confirm\"),\n ),\n pending && h(\"p\", { class: \"card-description\" }, \"Checking the details\u2026\"),\n v?.summary && h(\"p\", { class: \"card-summary\" }, v.summary),\n v && v.source === \"plain\" && description,\n v && v.lines.length > 0 && h(\"dl\", { class: \"card-input\" }, ...v.lines.map(cardLine)),\n v?.note && h(\"p\", { class: \"card-note\" }, v.note),\n v && v.source !== \"plain\" && description,\n h(\n \"div\",\n { class: \"card-actions\" },\n h(\n \"button\",\n { class: \"button-secondary\", attrs: { type: \"button\" }, on: { click: () => onRespond(proposal.proposalId, false) } },\n \"Cancel\",\n ),\n h(\n \"button\",\n { class: high ? \"button-primary button-danger\" : \"button-primary\", attrs: { type: \"button\", disabled: pending ? \"\" : undefined }, on: { click: () => { if (!pending) onRespond(proposal.proposalId, true); } } },\n v?.confirmLabel ?? \"Confirm\",\n ),\n ),\n );\n}\n\n/** One line: its label, then pictures, what it was and what it becomes, and any list (each entry checkable on its own). */\nfunction cardLine(line: CardLine): HTMLElement {\n // With pictures: the pictures on the left, what they are beside them, so rows line up whatever the names.\n if (line.images?.length) {\n return h(\n \"div\",\n { class: \"card-media-row\" },\n h(\"span\", { class: \"card-images\" }, ...line.images.map((im) => h(\"img\", { attrs: { src: im.src, alt: im.alt ?? \"\", loading: \"lazy\", referrerpolicy: \"no-referrer\" } }))),\n h(\"span\", { class: \"card-media-text\" }, line.label && h(\"dt\", { class: \"card-media-label\" }, line.label), h(\"dd\", { class: line.label ? \"card-media-value\" : \"card-media-value card-media-strong\" }, ...lineValue(line))),\n );\n }\n const value = h(\n \"dd\",\n { class: line.label ? \"card-value\" : \"card-value card-value-wide\" },\n ...lineValue(line),\n );\n return h(\"div\", { class: \"card-row\" }, line.label && h(\"dt\", { class: \"card-key\" }, line.label), value);\n}\n\n/** What a line says: what it was, what it becomes, and any list. */\nfunction lineValue(line: CardLine) {\n return [\n line.before !== undefined && h(\"s\", { class: \"card-before\" }, line.before),\n line.before !== undefined && h(\"span\", { class: \"card-arrow\", attrs: { \"aria-label\": \"becomes\" } }, \" \u2192 \"),\n line.text !== undefined && h(\"span\", { class: \"input-text\" }, line.text),\n !!line.items?.length && h(\"ol\", { class: \"input-list\" }, ...line.items.map((item) => h(\"li\", {}, h(\"span\", { class: \"input-text\" }, item)))),\n ];\n}\n", "/** The orb menu's icons, the same strokes as the React SDK's. */\n\nimport { svg } from \"./dom\";\n\nfunction icon(...children: SVGElement[]): SVGElement {\n return svg(\n \"svg\",\n {\n viewBox: \"0 0 20 20\",\n width: 18,\n height: 18,\n fill: \"none\",\n stroke: \"currentColor\",\n \"stroke-width\": 1.7,\n \"stroke-linecap\": \"round\",\n \"stroke-linejoin\": \"round\",\n \"aria-hidden\": \"true\",\n },\n ...children,\n );\n}\n\nexport const chatIcon = () =>\n icon(svg(\"path\", { d: \"M4 4.5h12a1.5 1.5 0 0 1 1.5 1.5v7a1.5 1.5 0 0 1-1.5 1.5H9l-4 3v-3H4A1.5 1.5 0 0 1 2.5 13V6A1.5 1.5 0 0 1 4 4.5Z\" }));\n\nexport const micIcon = () =>\n icon(\n svg(\"rect\", { x: 7, y: 2.5, width: 6, height: 10, rx: 3 }),\n svg(\"path\", { d: \"M4.5 9.5a5.5 5.5 0 0 0 11 0M10 15v2.5\" }),\n );\n\nexport const waveIcon = () => icon(svg(\"path\", { d: \"M3 10v0M6.5 7v6M10 4v12M13.5 7v6M17 10v0\" }));\n\n/** The chat box's attach button. */\nexport const clipIcon = () => icon(svg(\"path\", { d: \"M15.5 9.5l-5.6 5.6a3.5 3.5 0 0 1-5-5L10.6 4.4a2.3 2.3 0 0 1 3.3 3.3L8.3 13.3a1.1 1.1 0 0 1-1.6-1.6L11.8 6.6\" }));\n", "/**\n * The orb: the widget as a circle, and voice shown in the circle itself\n * (amendment 61). The React SDK's `orb.tsx` in plain DOM; its comments carry\n * the reasoning, and the behaviour is the same:\n *\n * | Mode | Tap | Hold | Drag past 8px |\n * |--------------|--------------------------|--------------------------|--------------------------|\n * | Idle | menu, or chat | nothing | moves it, if draggable |\n * | Menu open | closes it | nothing | nothing |\n * | Hands-free | ends the session | nothing | moves it, if draggable |\n * | Push-to-talk | nothing | records, release sends | discards, then moves it |\n *\n * Two things the orb still shows with the panel closed: the confirmation\n * card, from the proposal payload, and refusals and errors, in a bubble.\n *\n * The orb button is built once and updated, never replaced, because a press\n * in progress holds pointer capture on it.\n */\n\nimport type { VoiceMode, WidgetCorner } from \"@mihe-dev/protocol\";\n\nimport type { Mihe, MiheState } from \"../mihe\";\nimport { confirmationCard } from \"./card\";\nimport { h, replace, setAttrs } from \"./dom\";\nimport { chatIcon, micIcon, waveIcon } from \"./icons\";\nimport { launcherFace } from \"./launcher\";\n\nexport const ORB_SIZE = 56;\nexport const MARGIN = 20;\nconst DRAG_THRESHOLD = 8;\n/** How long a press is held on the device before it counts as speech. */\nconst HOLD_WINDOW_MS = 150;\n\nconst POSITION_KEY = \"mihe:orb-position\";\nconst HINT_KEY = \"mihe:hold-hint-done\";\n\n/** Where a dragged orb rests: a side, and how far down the viewport. */\ninterface Rest {\n side: \"left\" | \"right\";\n /** Top of the orb as a fraction of the viewport's height, so it survives a resize. */\n y: number;\n}\n\nexport interface OrbOptions {\n heading: string;\n draggable: boolean;\n corner: WidgetCorner;\n voiceModes: VoiceMode[];\n /** Drawn in the orb in place of the uploaded icon or the mark. Cloned. */\n launcherIcon?: Node;\n /** Distance from the viewport's bottom and side edges, in pixels. */\n offset: { bottom: number; side: number };\n}\n\ntype MenuKey = \"chat\" | VoiceMode;\n\ninterface Press {\n x: number;\n y: number;\n offsetX: number;\n offsetY: number;\n dragging: boolean;\n recording: \"held\" | \"live\" | null;\n timer: ReturnType<typeof setTimeout> | null;\n}\n\nfunction readRest(): Rest | null {\n try {\n const raw = window.localStorage.getItem(POSITION_KEY);\n if (!raw) return null;\n const parsed = JSON.parse(raw) as Rest;\n if ((parsed.side === \"left\" || parsed.side === \"right\") && typeof parsed.y === \"number\") return parsed;\n } catch {\n // Private windows and blocked storage: fall back to the corner.\n }\n return null;\n}\n\nfunction writeRest(rest: Rest): void {\n try {\n window.localStorage.setItem(POSITION_KEY, JSON.stringify(rest));\n } catch {\n // Remembering is a convenience; forgetting is fine.\n }\n}\n\nfunction hintDone(): boolean {\n try {\n return window.localStorage.getItem(HINT_KEY) === \"1\";\n } catch {\n return false;\n }\n}\n\nfunction markHintDone(): void {\n try {\n window.localStorage.setItem(HINT_KEY, \"1\");\n } catch {\n // Shown again next time, which is harmless.\n }\n}\n\nexport class Orb {\n /** Everything the orb draws, as siblings in the shadow root. */\n readonly root: HTMLElement;\n private readonly button: HTMLButtonElement;\n private readonly ring: HTMLElement;\n private readonly face: HTMLElement;\n private readonly stop: HTMLButtonElement;\n private readonly menu: HTMLElement;\n private readonly cards: HTMLElement;\n private readonly bubble: HTMLElement;\n\n private options!: OrbOptions;\n private state!: MiheState;\n private menuOpen = false;\n private rest: Rest | null = null;\n private drag: { left: number; top: number } | null = null;\n private showHint = !hintDone();\n private press: Press | null = null;\n private previousVoiceState: string = \"idle\";\n private faceKey: unknown = undefined;\n private drawnProposals: unknown = undefined;\n private drawnViews: unknown = undefined;\n private failedIcon: string | null = null;\n private ringFrame = 0;\n private stopLevel: (() => void) | null = null;\n private readonly reducedMotion =\n typeof window !== \"undefined\" && window.matchMedia?.(\"(prefers-reduced-motion: reduce)\").matches === true;\n private readonly cleanups: (() => void)[] = [];\n\n constructor(private readonly mihe: Mihe) {\n this.ring = h(\"span\", { class: \"ring idle\", attrs: { \"aria-hidden\": \"true\" } });\n this.face = h(\"span\", { class: \"face\" });\n this.button = h(\n \"button\",\n {\n class: \"orb\",\n attrs: { type: \"button\" },\n on: {\n pointerdown: (event) => this.onPointerDown(event),\n pointermove: (event) => this.onPointerMove(event),\n pointerup: () => this.finish(false),\n pointercancel: () => this.finish(true),\n keydown: (event) => this.onKeyDown(event),\n keyup: (event) => this.onKeyUp(event),\n contextmenu: (event) => event.preventDefault(),\n },\n },\n this.ring,\n this.face,\n );\n // Always a visible way out that is not a gesture.\n this.stop = h(\n \"button\",\n {\n class: \"stop\",\n attrs: { type: \"button\", \"aria-label\": \"End voice\" },\n on: { click: () => this.mihe.voice.stop() },\n },\n h(\"span\", { class: \"stop-square\", attrs: { \"aria-hidden\": \"true\" } }),\n );\n this.menu = h(\"div\", { class: \"popover\", attrs: { role: \"menu\" }, on: { keydown: (event) => this.onMenuKeyDown(event) } });\n this.cards = h(\"div\", { class: \"popover cards\" });\n this.bubble = h(\"div\", { class: \"popover orb-bubble\", attrs: { role: \"status\", \"aria-live\": \"polite\" } });\n this.root = h(\"div\", {}, this.menu, this.cards, this.bubble, this.button, this.stop);\n\n const onResize = () => this.render();\n window.addEventListener(\"resize\", onResize);\n this.cleanups.push(() => window.removeEventListener(\"resize\", onResize));\n\n const onOutside = (event: PointerEvent) => {\n if (!this.menuOpen) return;\n const path = event.composedPath();\n if (!path.includes(this.menu) && !path.includes(this.button)) this.setMenu(false);\n };\n document.addEventListener(\"pointerdown\", onOutside);\n this.cleanups.push(() => document.removeEventListener(\"pointerdown\", onOutside));\n }\n\n update(state: MiheState, options: OrbOptions): void {\n if (this.options?.draggable !== options.draggable) {\n this.rest = options.draggable ? readRest() : null;\n }\n // \"Hold to talk\" stays until the first recording that reached the model.\n if (this.previousVoiceState === \"recording\" && state.voice.state === \"thinking\") {\n markHintDone();\n this.showHint = false;\n }\n this.previousVoiceState = state.voice.state;\n this.state = state;\n this.options = options;\n this.render();\n }\n\n destroy(): void {\n this.stopRing();\n if (this.press?.timer) clearTimeout(this.press.timer);\n for (const cleanup of this.cleanups) cleanup();\n }\n\n // ---- what the orb is doing --------------------------------------------\n\n private get voiceLive() {\n const state = this.state.voice.state;\n return state !== \"idle\" && state !== \"error\";\n }\n\n private get handsFree() {\n return this.voiceLive && this.state.voice.handsFree;\n }\n\n private get holdToTalk() {\n return this.voiceLive && !this.state.voice.handsFree;\n }\n\n private menuItems(): { key: MenuKey; label: string }[] {\n const allowed = this.state.voice.available ? this.options.voiceModes : [];\n return [\n { key: \"chat\", label: \"Chat\" },\n ...(allowed.includes(\"hands-free\") ? [{ key: \"hands-free\" as const, label: \"Talk\" }] : []),\n ...(allowed.includes(\"hold\") ? [{ key: \"hold\" as const, label: \"Hold to talk\" }] : []),\n ];\n }\n\n // ---- position ----------------------------------------------------------\n\n private viewport() {\n return { width: window.innerWidth, height: window.innerHeight };\n }\n\n private clampTop(top: number): number {\n const { height } = this.viewport();\n const lowest = height - this.options.offset.bottom - ORB_SIZE;\n return Math.min(Math.max(MARGIN, top), Math.max(MARGIN, lowest));\n }\n\n /** The side the orb sits on, which is also where the panel opens. */\n side(): \"left\" | \"right\" {\n const { width } = this.viewport();\n if (this.drag) return this.drag.left + ORB_SIZE / 2 < width / 2 ? \"left\" : \"right\";\n return this.rest?.side ?? (this.options?.corner === \"bottom-left\" ? \"left\" : \"right\");\n }\n\n private position() {\n const { width, height } = this.viewport();\n const side = this.side();\n const edge = this.options.offset.side;\n const left = this.drag ? this.drag.left : side === \"left\" ? edge : width - edge - ORB_SIZE;\n const top = this.drag\n ? this.drag.top\n : this.clampTop(this.rest ? this.rest.y * height : height - this.options.offset.bottom - ORB_SIZE);\n return { side, left, top, above: top + ORB_SIZE / 2 > height / 2 };\n }\n\n // ---- drawing -----------------------------------------------------------\n\n private render(): void {\n if (!this.state) return;\n const { voice, proposals, lastError, connection } = this.state;\n const { heading } = this.options;\n const { side, left, top, above } = this.position();\n const items = this.menuItems();\n const voiceLive = this.voiceLive;\n\n // The orb.\n const label = this.handsFree\n ? `Stop talking to ${heading}`\n : this.holdToTalk\n ? voice.state === \"recording\"\n ? \"Release to send\"\n : \"Hold to talk\"\n : items.length === 1\n ? `Open ${heading}`\n : `${heading}: choose chat or voice`;\n const menuish = items.length > 1 && !voiceLive;\n setAttrs(this.button, {\n \"aria-label\": label,\n \"aria-haspopup\": menuish ? \"menu\" : undefined,\n \"aria-expanded\": menuish ? String(this.menuOpen) : undefined,\n \"aria-pressed\": this.holdToTalk ? String(voice.state === \"recording\") : undefined,\n \"aria-busy\": String(voice.state === \"connecting\"),\n });\n this.button.disabled = connection !== \"open\" && !voiceLive;\n this.button.style.left = `${left}px`;\n this.button.style.top = `${top}px`;\n this.button.classList.toggle(\"dragging\", this.drag !== null);\n\n const ringState = !voiceLive\n ? \"idle\"\n : ([\"connecting\", \"thinking\", \"recording\", \"speaking\"] as const).find((s) => s === voice.state) ?? \"listening\";\n this.ring.className = `ring ${ringState}`;\n this.syncRing();\n\n const faceKey = [this.options.launcherIcon, this.state.launcherIconUrl, this.failedIcon];\n if (!sameList(faceKey, this.faceKey)) {\n this.faceKey = faceKey;\n replace(\n this.face,\n launcherFace(this.options.launcherIcon, this.state.launcherIconUrl, this.failedIcon, (url) => {\n this.failedIcon = url;\n this.render();\n }),\n );\n }\n\n this.stop.hidden = !voiceLive;\n this.stop.style.left = `${left + (side === \"right\" ? -52 : ORB_SIZE + 8)}px`;\n this.stop.style.top = `${top + (ORB_SIZE - 44) / 2}px`;\n\n // Floating things open beside the orb, towards the page and away from\n // the nearer edge, so nothing is drawn off-screen wherever it was dragged.\n const { height } = this.viewport();\n for (const popover of [this.menu, this.cards, this.bubble]) {\n popover.style.left = side === \"left\" ? `${this.options.offset.side}px` : \"\";\n popover.style.right = side === \"right\" ? `${this.options.offset.side}px` : \"\";\n popover.style.bottom = above ? `${height - top + 12}px` : \"\";\n popover.style.top = above ? \"\" : `${top + ORB_SIZE + 12}px`;\n }\n\n // The menu.\n this.menu.hidden = !this.menuOpen;\n this.menu.setAttribute(\"aria-label\", heading);\n this.menu.style.flexDirection = above ? \"column-reverse\" : \"column\";\n this.menu.style.alignItems = side === \"right\" ? \"flex-end\" : \"flex-start\";\n if (this.menuOpen && this.menu.childElementCount !== items.length) {\n replace(\n this.menu,\n ...items.map((item) =>\n h(\n \"button\",\n { class: \"menu-item\", attrs: { type: \"button\", role: \"menuitem\" }, on: { click: () => this.choose(item.key) } },\n h(\n \"span\",\n { class: \"menu-icon\", attrs: { \"aria-hidden\": \"true\" } },\n item.key === \"chat\" ? chatIcon() : item.key === \"hold\" ? micIcon() : waveIcon(),\n ),\n item.label,\n ),\n ),\n );\n }\n\n // The card, out of the orb. The session waits for this tap, and it is\n // the same card the panel draws, from the same payload.\n this.cards.hidden = this.menuOpen || proposals.length === 0;\n // A card is redrawn when the list changes, and when its view arrives (amendment 99).\n if (proposals !== this.drawnProposals || this.state.cardViews !== this.drawnViews) {\n this.drawnProposals = proposals;\n this.drawnViews = this.state.cardViews;\n replace(this.cards, ...proposals.map((p) => confirmationCard(p, this.mihe.respondToProposal, this.state.cardViews[p.proposalId])));\n }\n\n const bubble: { text: string; tone: \"quiet\" | \"error\" } | null =\n voice.state === \"connecting\"\n ? { text: \"Setting up the microphone\u2026\", tone: \"quiet\" }\n : lastError && (voiceLive || voice.state === \"error\")\n ? { text: lastError, tone: \"error\" }\n : voice.notice\n ? { text: voice.notice, tone: \"quiet\" }\n : this.holdToTalk && this.showHint && voice.state === \"listening\"\n ? { text: \"Hold to talk\", tone: \"quiet\" }\n : null;\n this.bubble.hidden = this.menuOpen || proposals.length > 0 || bubble === null;\n this.bubble.classList.toggle(\"error\", bubble?.tone === \"error\");\n if (this.bubble.textContent !== (bubble?.text ?? \"\")) this.bubble.textContent = bubble?.text ?? \"\";\n }\n\n /**\n * The ring follows the voice level without a redraw: a level arrives tens\n * of times a second, and each one only moves one element.\n */\n private syncRing(): void {\n const wanted = this.voiceLive && !this.reducedMotion;\n if (wanted === (this.stopLevel !== null)) return;\n if (!wanted) {\n this.stopRing();\n return;\n }\n let target = 0;\n let current = 0;\n this.stopLevel = this.mihe.voice.onLevel((source, level) => {\n const voice = this.mihe.getState().voice;\n // Only the side whose turn it is moves the ring.\n if (source === \"user\" && (voice.state === \"recording\" || voice.handsFree)) target = level;\n if (source === \"assistant\") target = level;\n });\n const tick = () => {\n // Quick to rise, slow to fall, which reads as breathing, not flicker.\n current += (target - current) * (target > current ? 0.45 : 0.12);\n target *= 0.92;\n this.ring.style.transform = `scale(${1 + current * 0.38})`;\n this.ringFrame = requestAnimationFrame(tick);\n };\n this.ringFrame = requestAnimationFrame(tick);\n }\n\n private stopRing(): void {\n this.stopLevel?.();\n this.stopLevel = null;\n cancelAnimationFrame(this.ringFrame);\n this.ring.style.transform = \"scale(1)\";\n }\n\n // ---- the menu ----------------------------------------------------------\n\n private setMenu(open: boolean): void {\n this.menuOpen = open;\n if (!open) replace(this.menu);\n this.render();\n // Focus moves into the menu so the arrow keys and Esc work at once.\n if (open) this.menu.querySelector(\"button\")?.focus();\n }\n\n private choose(key: MenuKey): void {\n this.setMenu(false);\n if (key === \"chat\") {\n this.mihe.open();\n } else {\n // Entering a voice mode asks for the microphone now, so the browser's\n // permission prompt does not swallow the first hold.\n this.mihe.voice.start({ handsFree: key === \"hands-free\", openPanel: false });\n }\n }\n\n private onMenuKeyDown(event: KeyboardEvent): void {\n const buttons = Array.from(this.menu.querySelectorAll(\"button\"));\n const active = (this.root.getRootNode() as Document | ShadowRoot).activeElement;\n const index = buttons.indexOf(active as HTMLButtonElement);\n if (event.key === \"Escape\") {\n event.preventDefault();\n this.setMenu(false);\n this.button.focus();\n } else if (event.key === \"ArrowDown\" || event.key === \"ArrowRight\") {\n event.preventDefault();\n buttons[(index + 1) % buttons.length]?.focus();\n } else if (event.key === \"ArrowUp\" || event.key === \"ArrowLeft\") {\n event.preventDefault();\n buttons[(index - 1 + buttons.length) % buttons.length]?.focus();\n }\n }\n\n // ---- the orb itself ----------------------------------------------------\n\n private tap(): void {\n if (this.menuOpen) {\n this.setMenu(false);\n return;\n }\n if (this.handsFree) {\n // Stops rather than pauses: a hands-free session is billed for every\n // second it is open.\n this.mihe.voice.stop();\n return;\n }\n if (this.holdToTalk) return; // a tap is too short to be speech\n if (this.menuItems().length === 1) this.mihe.open();\n else this.setMenu(true);\n }\n\n private onPointerDown(event: PointerEvent): void {\n if (event.button !== 0) return;\n this.button.setPointerCapture?.(event.pointerId);\n const { left, top } = this.position();\n const press: Press = {\n x: event.clientX,\n y: event.clientY,\n offsetX: event.clientX - left,\n offsetY: event.clientY - top,\n dragging: false,\n recording: null,\n timer: null,\n };\n this.press = press;\n\n const voice = this.mihe.getState().voice;\n if (this.holdToTalk && (voice.state === \"listening\" || voice.state === \"speaking\")) {\n if (this.options.draggable) {\n // Held on the device until it is clear this is speech, not a drag.\n voice.press({ hold: true });\n press.recording = \"held\";\n press.timer = setTimeout(() => {\n if (this.press === press && !press.dragging) {\n this.mihe.voice.confirm();\n press.recording = \"live\";\n }\n }, HOLD_WINDOW_MS);\n } else {\n voice.press();\n press.recording = \"live\";\n }\n }\n }\n\n private onPointerMove(event: PointerEvent): void {\n const press = this.press;\n if (!press || !this.options.draggable || this.menuOpen) return;\n // Once a recording is live, the finger is holding a microphone, not the orb.\n if (press.recording === \"live\") return;\n\n const moved = Math.hypot(event.clientX - press.x, event.clientY - press.y);\n if (!press.dragging && moved > DRAG_THRESHOLD) {\n press.dragging = true;\n if (press.recording === \"held\") {\n if (press.timer) clearTimeout(press.timer);\n this.mihe.voice.discard();\n press.recording = null;\n }\n }\n if (press.dragging) {\n const { width, height } = this.viewport();\n this.drag = {\n left: Math.min(Math.max(0, event.clientX - press.offsetX), width - ORB_SIZE),\n top: Math.min(Math.max(0, event.clientY - press.offsetY), height - ORB_SIZE),\n };\n this.render();\n }\n }\n\n private finish(cancelled: boolean): void {\n const press = this.press;\n this.press = null;\n if (!press) return;\n if (press.timer) clearTimeout(press.timer);\n\n if (press.dragging && this.drag) {\n // Snap to the nearer side, and keep the height.\n const { width, height } = this.viewport();\n this.rest = {\n side: this.drag.left + ORB_SIZE / 2 < width / 2 ? \"left\" : \"right\",\n y: this.clampTop(this.drag.top) / height,\n };\n writeRest(this.rest);\n this.drag = null;\n this.render();\n return;\n }\n this.drag = null;\n\n if (press.recording) {\n // Released: a held press that never moved is speech too.\n this.mihe.voice.release();\n return;\n }\n if (!cancelled) this.tap();\n }\n\n private onKeyDown(event: KeyboardEvent): void {\n if (event.repeat) return;\n const voice = this.mihe.getState().voice;\n if (this.holdToTalk && event.key === \" \") {\n event.preventDefault();\n if (voice.state === \"listening\" || voice.state === \"speaking\") voice.press();\n return;\n }\n if (event.key === \"Enter\" || event.key === \" \") {\n event.preventDefault();\n this.tap();\n }\n if (event.key === \"Escape\" && this.voiceLive) {\n event.preventDefault();\n voice.stop();\n }\n }\n\n private onKeyUp(event: KeyboardEvent): void {\n if (this.holdToTalk && event.key === \" \") {\n event.preventDefault();\n this.mihe.voice.release();\n }\n }\n}\n\nfunction sameList(a: unknown, b: unknown): boolean {\n return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((v, i) => v === b[i]);\n}\n", "/**\n * The little Markdown a model writes in a chat reply, read into a tree that\n * each widget draws with its own elements.\n *\n * Models write `**bold**`, lists and the odd `code` whatever the prompt says,\n * and a reply full of asterisks reads as broken. This reads that subset and\n * nothing more: paragraphs, bullet and numbered lists, headings (drawn as bold\n * lines), bold, italic, inline code and links. Never HTML: the tree holds only\n * strings, every renderer puts them in as text, and a link keeps only an\n * http(s) or mailto address. A reply is still model prose, so it gets no more\n * power than plain text had.\n *\n * Safe on a reply that is still streaming: an unclosed `**` stays as written\n * until its pair arrives.\n */\n\nexport type Inline =\n | { kind: \"text\"; text: string; bold?: boolean; italic?: boolean }\n | { kind: \"code\"; text: string }\n | { kind: \"link\"; text: string; href: string };\n\nexport type Block =\n | { kind: \"paragraph\"; lines: Inline[][] }\n | { kind: \"heading\"; inline: Inline[] }\n | { kind: \"list\"; ordered: boolean; start: number; items: Inline[][] };\n\nconst BULLET = /^\\s*[-*\u2022]\\s+(.*)$/;\nconst NUMBERED = /^\\s*(\\d{1,3})[.)]\\s+(.*)$/;\nconst HEADING = /^\\s{0,3}#{1,6}\\s+(.*?)\\s*#*\\s*$/;\n\nexport function parseMarkdown(text: string): Block[] {\n const blocks: Block[] = [];\n let paragraph: string[] = [];\n let list: { ordered: boolean; start: number; items: string[] } | null = null;\n\n const flush = () => {\n if (paragraph.length) blocks.push({ kind: \"paragraph\", lines: paragraph.map(parseInline) });\n if (list) blocks.push({ kind: \"list\", ordered: list.ordered, start: list.start, items: list.items.map(parseInline) });\n paragraph = [];\n list = null;\n };\n\n for (const raw of text.replace(/\\r\\n?/g, \"\\n\").split(\"\\n\")) {\n const line = raw.trimEnd();\n if (line.trim() === \"\") { flush(); continue; }\n const heading = HEADING.exec(line);\n if (heading) { flush(); blocks.push({ kind: \"heading\", inline: parseInline(heading[1] ?? \"\") }); continue; }\n const bullet = BULLET.exec(line);\n const numbered = bullet ? null : NUMBERED.exec(line);\n if (bullet || numbered) {\n const ordered = !!numbered;\n if (paragraph.length || (list && list.ordered !== ordered)) flush();\n if (!list) list = { ordered, start: numbered ? Number(numbered[1]) : 1, items: [] };\n list.items.push((bullet ? bullet[1] : numbered![2]) ?? \"\");\n continue;\n }\n // A line indented under a list item carries on that item.\n if (list && /^\\s+/.test(raw)) { list.items[list.items.length - 1] += ` ${line.trim()}`; continue; }\n if (list) flush();\n paragraph.push(line);\n }\n flush();\n return blocks;\n}\n\n/** Only addresses that open a page or a mail: never `javascript:` or `data:`. */\nexport function safeHref(href: string): string | null {\n const trimmed = href.trim();\n return /^(https?:\\/\\/|mailto:)/i.test(trimmed) ? trimmed : null;\n}\n\nconst INLINE = /`([^`\\n]+)`|\\[([^\\]\\n]+)\\]\\(([^)\\s]+)\\)|\\*\\*(?=\\S)([^*\\n]*?\\S)\\*\\*|__(?=\\S)([^_\\n]*?\\S)__|(?<![\\w*])\\*(?=[^\\s*])([^*\\n]*?[^\\s*])\\*(?![\\w*])|(?<![\\w_])_(?=[^\\s_])([^_\\n]*?[^\\s_])_(?![\\w_])/g;\n\nexport function parseInline(text: string): Inline[] {\n const out: Inline[] = [];\n const push = (part: Inline) => {\n const last = out[out.length - 1];\n if (part.kind === \"text\" && last?.kind === \"text\" && !last.bold === !part.bold && !last.italic === !part.italic) last.text += part.text;\n else out.push(part);\n };\n let at = 0;\n for (const m of text.matchAll(INLINE)) {\n const index = m.index ?? 0;\n if (index > at) push({ kind: \"text\", text: text.slice(at, index) });\n const [whole, code, linkText, href, bold1, bold2, italic1, italic2] = m;\n if (code !== undefined) push({ kind: \"code\", text: code });\n else if (linkText !== undefined) {\n const safe = safeHref(href ?? \"\");\n push(safe ? { kind: \"link\", text: linkText, href: safe } : { kind: \"text\", text: linkText });\n } else if (bold1 !== undefined || bold2 !== undefined) {\n // Italic inside bold is common enough (`**an _important_ note**`).\n for (const part of parseInline(bold1 ?? bold2 ?? \"\")) push(part.kind === \"text\" ? { ...part, bold: true } : part);\n } else if (italic1 !== undefined || italic2 !== undefined) push({ kind: \"text\", text: italic1 ?? italic2 ?? \"\", italic: true });\n else push({ kind: \"text\", text: whole });\n at = index + whole.length;\n }\n if (at < text.length) push({ kind: \"text\", text: text.slice(at) });\n return out;\n}\n", "/**\n * Draws a reply's Markdown tree (`../markdown.ts`) as DOM, through `h`, so\n * every piece of text goes in as a text node.\n */\nimport { parseMarkdown, type Inline } from \"../markdown\";\nimport { h } from \"./dom\";\n\nfunction inline(parts: Inline[]): Node[] {\n return parts.map((part) => {\n if (part.kind === \"code\") return h(\"code\", {}, part.text);\n if (part.kind === \"link\") return h(\"a\", { attrs: { href: part.href, target: \"_blank\", rel: \"noreferrer\" } }, part.text);\n let node: Node = document.createTextNode(part.text);\n if (part.italic) node = h(\"em\", {}, node);\n if (part.bold) node = h(\"strong\", {}, node);\n return node;\n });\n}\n\nexport function renderMarkdown(text: string): HTMLElement {\n return h(\n \"div\",\n { class: \"md\" },\n ...parseMarkdown(text).map((block) => {\n if (block.kind === \"heading\") return h(\"p\", {}, h(\"strong\", {}, ...inline(block.inline)));\n if (block.kind === \"list\") {\n const items = block.items.map((item) => h(\"li\", {}, ...inline(item)));\n return block.ordered ? h(\"ol\", { attrs: { start: block.start === 1 ? undefined : block.start } }, ...items) : h(\"ul\", {}, ...items);\n }\n return h(\"p\", {}, ...block.lines.flatMap((line, i) => (i ? [h(\"br\"), ...inline(line)] : inline(line))));\n }),\n );\n}\n", "/**\n * The conversation: transcript, confirmation cards, composer; and in the\n * classic form a Talk button in the header, which shows the call in place of\n * the conversation while it lasts (amendment 105). The chat box has no\n * microphone.\n *\n * Built once and updated. The composer and the microphone buttons in\n * particular are never rebuilt: a rebuilt input loses what the user was\n * typing, and a push-to-talk button replaced mid-press loses its pointer\n * capture, so the release lands elsewhere and the microphone stays open.\n */\n\nimport type { VoiceMode } from \"@mihe-dev/protocol\";\n\nimport { ASSISTANT_PANEL_ID, type Mihe, type MiheState } from \"../mihe\";\nimport { confirmationCard } from \"./card\";\nimport { h, replace } from \"./dom\";\nimport { renderMarkdown } from \"./markdown\";\nimport { clipIcon } from \"./icons\";\nimport { miheMark } from \"./mark\";\n\nexport const DEFAULT_VOICE_MODES: VoiceMode[] = [\"hands-free\", \"hold\"];\n\nexport interface PanelOptions {\n /** Header title; the assistant's name from the app's settings otherwise. */\n title?: string;\n placeholder?: string;\n showClose: boolean;\n /** A Talk button in the header, and the call in place of the conversation while it lasts. Off when the orb owns voice. */\n showVoice: boolean;\n}\n\nexport class Panel {\n readonly root: HTMLElement;\n private readonly heading: Text;\n private readonly dot: HTMLElement;\n private readonly closeButton: HTMLButtonElement;\n private readonly transcript: HTMLElement;\n private readonly input: HTMLInputElement;\n private readonly sendButton: HTMLButtonElement;\n /** The paperclip and its file input, shown only when the app describes attachments (amendment 102). */\n private readonly clip: HTMLButtonElement;\n private readonly fileInput: HTMLInputElement;\n private readonly chips: HTMLElement;\n private drawnAttachments: unknown = null;\n private readonly talk: HTMLButtonElement;\n private readonly call: CallView;\n private readonly body: HTMLElement;\n /** What the transcript was last drawn from, so it is redrawn only on change. */\n private drawn: unknown[] = [];\n\n constructor(\n private readonly mihe: Mihe,\n private options: PanelOptions,\n ) {\n this.heading = document.createTextNode(\"\");\n this.dot = h(\"span\", { class: \"status-dot\", attrs: { \"aria-hidden\": \"true\" } });\n this.closeButton = h(\n \"button\",\n {\n class: \"icon-button\",\n attrs: { type: \"button\", \"aria-label\": \"Close assistant\" },\n on: { click: () => this.mihe.close() },\n },\n \"\u2715\",\n );\n this.transcript = h(\"div\", { class: \"transcript\" });\n this.input = h(\"input\", {\n class: \"input\",\n attrs: { \"aria-label\": \"Message\" },\n on: {\n input: () => this.syncSend(this.mihe.getState()),\n // A pasted screenshot attaches; pasted words paste as usual.\n paste: (event) => {\n if (!this.mihe.getState().attachAccept) return;\n const files = Array.from(event.clipboardData?.files ?? []);\n if (!files.length) return;\n event.preventDefault();\n for (const file of files) this.mihe.attach(file);\n },\n },\n });\n this.fileInput = h(\"input\", {\n attrs: { type: \"file\", multiple: true, hidden: true },\n on: {\n change: () => {\n for (const file of Array.from(this.fileInput.files ?? [])) this.mihe.attach(file);\n this.fileInput.value = \"\";\n },\n },\n });\n this.clip = h(\n \"button\",\n { class: \"clip\", attrs: { type: \"button\", \"aria-label\": \"Attach a picture\", title: \"Attach a picture\" }, on: { click: () => this.fileInput.click() } },\n clipIcon(),\n );\n this.chips = h(\"ul\", { class: \"chips\", attrs: { \"aria-label\": \"Attached pictures\" } });\n this.sendButton = h(\"button\", { class: \"send\", attrs: { type: \"submit\" } }, \"Send\");\n this.talk = h(\"button\", { class: \"talk\", attrs: { type: \"button\", title: \"Talk instead of typing\" }, on: { click: () => {\n const modes = this.mihe.getState().widgetSettings?.voiceModes ?? DEFAULT_VOICE_MODES;\n this.mihe.voice.start(modes.includes(\"hands-free\") ? { handsFree: true } : undefined);\n } } }, \"Talk\");\n this.call = new CallView(mihe);\n\n const composer = h(\n \"form\",\n {\n class: \"composer\",\n on: {\n submit: (event) => {\n event.preventDefault();\n this.mihe.send(this.input.value);\n this.input.value = \"\";\n this.syncSend(this.mihe.getState());\n },\n },\n },\n this.clip,\n this.input,\n this.sendButton,\n // Last, so the message box stays the composer's first input.\n this.fileInput,\n );\n\n this.root = h(\n \"section\",\n { class: \"panel\", attrs: { id: ASSISTANT_PANEL_ID } },\n h(\"header\", { class: \"header\" }, h(\"div\", { class: \"header-title\" }, this.dot, this.heading), this.talk, this.closeButton),\n (this.body = h(\"div\", { class: \"panel-body\" }, this.transcript, this.chips, composer)),\n this.call.root,\n );\n }\n\n setOptions(options: PanelOptions): void {\n this.options = options;\n }\n\n update(state: MiheState): void {\n const heading = this.options.title ?? state.assistantName ?? \"Assistant\";\n this.heading.data = heading;\n this.root.setAttribute(\"aria-label\", heading);\n this.dot.classList.toggle(\"open\", state.connection === \"open\");\n this.closeButton.hidden = !this.options.showClose;\n this.input.placeholder = this.options.placeholder ?? \"Ask me anything about this app\u2026\";\n this.input.disabled = state.connection !== \"open\";\n this.clip.hidden = !state.attachAccept;\n this.clip.disabled = state.connection !== \"open\";\n if (state.attachAccept) this.fileInput.accept = state.attachAccept;\n if (state.attachments !== this.drawnAttachments) { this.drawnAttachments = state.attachments; this.drawChips(state); }\n this.syncSend(state);\n\n // The modes the app allows. Offering one it does not would be a button\n // the runtime refuses.\n const modes = state.widgetSettings?.voiceModes ?? DEFAULT_VOICE_MODES;\n const canTalk = this.options.showVoice && state.voice.available && modes.length > 0;\n const onCall = this.options.showVoice && state.voice.state !== \"idle\" && state.voice.state !== \"error\";\n this.talk.hidden = !canTalk || onCall;\n this.talk.disabled = state.connection !== \"open\";\n this.body.hidden = onCall;\n this.call.update(state, onCall);\n\n const inputs = [\n state.messages,\n state.proposals,\n state.cardViews,\n state.voice.state === \"connecting\",\n state.voice.notice,\n state.lastError,\n state.connection,\n ];\n if (inputs.every((value, index) => value === this.drawn[index])) return;\n this.drawn = inputs;\n this.drawTranscript(state);\n }\n\n private syncSend(state: MiheState): void {\n const reading = state.attachments.some((a) => a.status === \"reading\");\n const ready = state.attachments.some((a) => a.status === \"ready\");\n this.sendButton.disabled = state.busy || state.connection !== \"open\" || reading || (!this.input.value.trim() && !ready);\n }\n\n private drawChips(state: MiheState): void {\n this.chips.hidden = state.attachments.length === 0;\n replace(\n this.chips,\n ...state.attachments.map((a) =>\n h(\n \"li\",\n { class: a.status === \"failed\" ? \"chip failed\" : \"chip\" },\n a.previewUrl && h(\"img\", { attrs: { src: a.previewUrl, alt: \"\" } }),\n h(\n \"span\",\n { class: \"chip-text\" },\n h(\"span\", { class: \"chip-name\" }, a.name),\n h(\"span\", { class: \"chip-status\", attrs: a.status === \"failed\" ? { role: \"alert\" } : {} }, a.status === \"reading\" ? \"Reading\u2026\" : a.status === \"ready\" ? \"Ready\" : a.error ?? \"Couldn't read it\"),\n ),\n h(\"button\", { class: \"chip-remove\", attrs: { type: \"button\", \"aria-label\": `Remove ${a.name}` }, on: { click: () => this.mihe.removeAttachment(a.id) } }, \"\u2715\"),\n ),\n ),\n );\n }\n\n private drawTranscript(state: MiheState): void {\n replace(\n this.transcript,\n state.messages.length === 0 &&\n h(\n \"p\",\n { class: \"empty\" },\n \"Ask where something is, what a screen does, or for help getting something done.\",\n ),\n ...state.messages.map((message) =>\n h(\n \"div\",\n { class: message.role === \"user\" ? \"bubble-user\" : \"bubble-assistant\" },\n message.attachments && message.attachments.length > 0 &&\n h(\n \"div\",\n { class: \"sent-pictures\" },\n ...message.attachments.map((a) =>\n a.previewUrl ? h(\"img\", { attrs: { src: a.previewUrl, alt: a.name, title: a.name } }) : h(\"span\", { class: \"sent-name\" }, `\uD83D\uDCCE ${a.name}`),\n ),\n ),\n message.role === \"user\" ? message.text : renderMarkdown(message.text),\n message.streaming && message.text === \"\" && h(\"span\", { class: \"thinking\" }, \"thinking\u2026\"),\n message.sources && message.sources.length > 0 &&\n // Titles only. The snippet is what the model read, not something\n // a reader needs.\n h(\n \"div\",\n { class: \"sources\" },\n ...message.sources.map((source) =>\n h(\n \"span\",\n { class: \"source\" },\n source.url && /^https?:\\/\\//i.test(source.url)\n ? h(\"a\", { attrs: { href: source.url, target: \"_blank\", rel: \"noreferrer noopener\" } }, source.title)\n : source.title,\n ),\n ),\n ),\n ),\n ),\n ...state.proposals.map((proposal) => confirmationCard(proposal, this.mihe.respondToProposal, state.cardViews[proposal.proposalId])),\n // Setting up a voice session takes a moment, and the microphone stays\n // shut until it finishes (amendment 32). Without a word here the user\n // talks into a session that is not listening yet.\n state.voice.state === \"connecting\" && h(\"div\", { class: \"voice-notice\" }, \"Setting up the microphone\u2026\"),\n state.voice.notice && h(\"div\", { class: \"voice-notice\" }, state.voice.notice),\n state.lastError && h(\"div\", { class: \"error\" }, state.lastError),\n state.connection !== \"open\" &&\n h(\"div\", { class: \"notice\" }, state.connection === \"closed\" ? \"Disconnected.\" : \"Connecting\u2026\"),\n );\n this.transcript.scrollTo?.({ top: this.transcript.scrollHeight, behavior: \"smooth\" });\n }\n}\n\n/**\n * The microphone, in whichever of its two modes is running.\n *\n * Idle, it offers both: hold to talk, or hands-free. Once a session is live\n * only the running one is shown, with a way out.\n */\n/**\n * The call in place of the conversation (amendment 105): the mark moving with\n * whoever speaks, one line of what's said (held until the next begins), any\n * card that needs a tap, hold to talk when the call isn't hands-free, and End.\n * A copy of the React SDK's `call.tsx`.\n */\nclass CallView {\n readonly root: HTMLElement;\n private readonly cards: HTMLElement;\n private readonly ring: HTMLElement;\n private readonly line: HTMLElement;\n private readonly hold: HTMLButtonElement;\n private held = \"\";\n private failed: string | null = null;\n private lastState = \"idle\";\n private readonly failedLine: HTMLElement;\n private drawnCards: unknown = null;\n private stopLevel: (() => void) | null = null;\n\n constructor(private readonly mihe: Mihe) {\n this.cards = h(\"div\", { class: \"call-cards\" });\n this.ring = h(\"span\", { class: \"call-ring\" });\n this.line = h(\"span\", { class: \"call-line\", attrs: { \"aria-live\": \"polite\" } });\n this.hold = pushToTalk(mihe);\n this.hold.classList.add(\"call-hold\");\n const end = h(\"button\", { class: \"call-end\", attrs: { type: \"button\" }, on: { click: () => this.mihe.voice.stop() } }, \"End\");\n const mark = h(\"span\", { class: \"call-mark\", attrs: { \"aria-hidden\": \"true\" } }, this.ring, miheMark(26, \"#0f172a\", \"#007277\"));\n this.failedLine = h(\"p\", { class: \"call-failed\", attrs: { role: \"alert\" } });\n this.root = h(\"div\", { class: \"call\", attrs: { role: \"region\", \"aria-label\": \"Voice call\" } }, this.cards, this.failedLine, h(\"div\", { class: \"call-bar\" }, mark, this.line, this.hold, end));\n this.root.hidden = true;\n }\n\n update(state: MiheState, on: boolean): void {\n this.root.hidden = !on;\n const v = state.voice;\n if (!on) { this.held = \"\"; this.failed = null; this.failedLine.hidden = true; this.stopLevel?.(); this.stopLevel = null; return; }\n // A failed request says why on its own line until the next one starts (amendment 106).\n if (v.state === \"thinking\" && this.lastState !== \"thinking\") this.failed = null;\n if (state.lastError && state.lastError !== this.failedLine.dataset.seen) { this.failed = state.lastError; this.failedLine.dataset.seen = state.lastError; }\n this.lastState = v.state;\n this.failedLine.hidden = !this.failed;\n this.failedLine.textContent = this.failed ?? \"\";\n if (!this.stopLevel && !matchMedia?.(\"(prefers-reduced-motion: reduce)\").matches) {\n this.stopLevel = this.mihe.voice.onLevel((source, level) => {\n const now = this.mihe.getState().voice;\n if (source === \"assistant\" || now.handsFree || now.state === \"recording\") this.ring.style.transform = `scale(${1 + level * 0.38})`;\n });\n }\n if (v.caption) this.held = v.caption;\n const fallback = v.state === \"connecting\" ? \"Setting up the microphone\u2026\" : v.state === \"thinking\" ? \"Thinking\u2026\" : v.state === \"recording\" ? \"Listening\u2026\" : v.handsFree ? (v.state === \"speaking\" ? \"\" : \"Listening, just talk\") : \"Hold to talk\";\n const text = (v.caption || this.held || fallback).replace(/\\s+/g, \" \").trim();\n this.line.textContent = text.length <= 90 ? text : `\u2026${text.slice(text.length - 89).replace(/^\\S*\\s/, \"\")}`;\n this.line.title = text;\n this.hold.hidden = v.handsFree || v.state === \"connecting\";\n this.hold.textContent = v.state === \"recording\" ? \"Release to send\" : \"Hold to talk\";\n if (state.proposals !== this.drawnCards) {\n this.drawnCards = state.proposals;\n replace(this.cards, ...state.proposals.map((p) => confirmationCard(p, this.mihe.respondToProposal, state.cardViews[p.proposalId])));\n }\n }\n}\n\n/**\n * The push-to-talk button: hold to speak, release to send, the same gesture\n * on a mouse, a touchscreen and a keyboard.\n *\n * - **Pointer down, not click,** because the press begins the utterance and\n * the release ends it. It is also the gesture iOS Safari needs before any\n * audio, which is why the first press opens the session.\n * - **Pointer capture,** so a finger that slides off still ends the\n * utterance on release.\n * - **Space and Enter, held,** with key repeat ignored.\n *\n * State is read at the moment of each event, never captured.\n */\nfunction pushToTalk(mihe: Mihe): HTMLButtonElement {\n const phase = () => {\n const state = mihe.getState().voice.state;\n const connecting = state === \"connecting\";\n return { connecting, live: state !== \"idle\" && state !== \"error\" && !connecting };\n };\n const begin = (button: HTMLElement, pointerId?: number) => {\n const { connecting, live } = phase();\n if (connecting) return;\n if (!live) {\n mihe.voice.start();\n return;\n }\n if (pointerId !== undefined) button.setPointerCapture?.(pointerId);\n mihe.voice.press();\n };\n const end = () => {\n if (phase().live) mihe.voice.release();\n };\n\n const button: HTMLButtonElement = h(\n \"button\",\n {\n class: \"mic\",\n attrs: { type: \"button\" },\n on: {\n pointerdown: (event) => begin(button, event.pointerId),\n pointerup: end,\n pointercancel: end,\n keydown: (event) => {\n if (event.repeat) return;\n if (event.key === \" \" || event.key === \"Enter\") {\n event.preventDefault();\n begin(button);\n }\n },\n keyup: (event) => {\n if (event.key === \" \" || event.key === \"Enter\") {\n event.preventDefault();\n end();\n }\n },\n },\n },\n \"\\u{1F399}\",\n );\n return button;\n}\n", "/**\n * `<mihe-assistant>`: the widget as a web component, for any page and any\n * framework (doc 08). The orb by default, or the classic launcher and panel,\n * or just the panel inside a container of the host's.\n *\n * ```ts\n * import { createMihe, mountWidget } from \"@mihe-dev/js\";\n * const mihe = createMihe({ \u2026 });\n * mountWidget(mihe); // floating\n * mountWidget(mihe, { layout: \"inline\", container: sidebar }); // in a sidebar\n * ```\n *\n * Or in HTML, connected by script:\n *\n * ```html\n * <mihe-assistant form=\"classic\" corner=\"bottom-left\"></mihe-assistant>\n * <script type=\"module\">\n * defineMiheAssistant();\n * document.querySelector(\"mihe-assistant\").mihe = createMihe({ \u2026 });\n * </script>\n * ```\n *\n * Everything renders in a shadow root, so the host page's CSS cannot reach\n * it. One consequence: the panel's id is inside the shadow root, so a host\n * trigger's `aria-controls` cannot point at it.\n *\n * The class is defined when `defineMiheAssistant` first runs, never on\n * import: importing must work where `HTMLElement` does not exist, such as a\n * server render, and must not register anything a page did not ask for.\n */\n\nimport type { VoiceMode, WidgetCorner, WidgetForm, WidgetSettings } from \"@mihe-dev/protocol\";\n\nimport type { Mihe } from \"../mihe\";\nimport { h, replace, setAttrs } from \"./dom\";\nimport { launcherFace } from \"./launcher\";\nimport { Orb } from \"./orb\";\nimport { DEFAULT_VOICE_MODES, Panel } from \"./panel\";\nimport { STYLES } from \"./styles\";\n\nexport const ELEMENT_NAME = \"mihe-assistant\";\n\nexport interface WidgetOptions {\n /**\n * `\"floating\"`, the default: the orb or launcher, and a panel over the\n * page. `\"inline\"`: the panel alone, filling the element, for a sidebar or\n * a drawer the host app owns.\n */\n layout?: \"floating\" | \"inline\";\n /** Header title, and the launcher's accessible name. The app's assistant name otherwise. */\n title?: string;\n placeholder?: string;\n /** Show the orb or launcher. Off when the host app has its own trigger. */\n showLauncher?: boolean;\n /** Open on first mount. */\n defaultOpen?: boolean;\n /** Inline only: a close button in the header. */\n showClose?: boolean;\n /** Drawn in the orb or launcher in place of the uploaded icon. Cloned. */\n launcherIcon?: Node;\n /**\n * How far the orb, launcher and floating panel sit from the bottom and side\n * edges, in pixels; 20 by default. For a page with its own floating widget\n * in the same corner, such as a support chat: lift Mihe above it rather\n * than stacking the two.\n */\n offset?: { bottom?: number; side?: number };\n /*\n * The four below are settings in the dashboard (amendment 61), delivered\n * with the handshake. One passed here overrides the setting, except that\n * voice modes only narrow it: the runtime refuses a mode the app has not\n * allowed.\n */\n form?: WidgetForm;\n draggable?: boolean;\n corner?: WidgetCorner;\n voiceModes?: VoiceMode[];\n}\n\n/** `<mihe-assistant>`, once defined. */\nexport interface MiheAssistantElement extends HTMLElement {\n /** The assistant it draws. Setting it connects the element. */\n mihe: Mihe | null;\n /** Merged over the element's attributes. */\n options: WidgetOptions;\n}\n\n/** A setting passed in code, the app's setting otherwise, the SDK's default last. */\nfunction resolve(options: WidgetOptions, settings: WidgetSettings | null) {\n const allowed = settings?.voiceModes ?? DEFAULT_VOICE_MODES;\n return {\n form: options.form ?? settings?.form ?? \"orb\",\n draggable: options.draggable ?? settings?.draggable ?? false,\n corner: options.corner ?? settings?.corner ?? \"bottom-right\",\n voiceModes: options.voiceModes ? options.voiceModes.filter((mode) => allowed.includes(mode)) : allowed,\n };\n}\n\n/** Options written as attributes, for a widget placed in HTML. */\nfunction fromAttributes(element: HTMLElement): WidgetOptions {\n const read = (name: string) => element.getAttribute(name) ?? undefined;\n const flag = (name: string) => (element.hasAttribute(name) ? read(name) !== \"false\" : undefined);\n const modes = read(\"voice-modes\");\n return {\n layout: read(\"layout\") as WidgetOptions[\"layout\"],\n // Not `title`, which every element already has as its tooltip.\n title: read(\"heading\"),\n placeholder: read(\"placeholder\"),\n showLauncher: flag(\"show-launcher\"),\n defaultOpen: flag(\"default-open\"),\n showClose: flag(\"show-close\"),\n form: read(\"form\") as WidgetForm | undefined,\n draggable: flag(\"draggable\"),\n corner: read(\"corner\") as WidgetCorner | undefined,\n offset:\n read(\"offset-bottom\") === undefined && read(\"offset-side\") === undefined\n ? undefined\n : {\n bottom: read(\"offset-bottom\") === undefined ? undefined : Number(read(\"offset-bottom\")),\n side: read(\"offset-side\") === undefined ? undefined : Number(read(\"offset-side\")),\n },\n voiceModes: modes === undefined ? undefined : (modes.split(/[\\s,]+/).filter(Boolean) as VoiceMode[]),\n };\n}\n\nfunction withoutUndefined(options: WidgetOptions): WidgetOptions {\n return Object.fromEntries(Object.entries(options).filter(([, value]) => value !== undefined));\n}\n\n/** Register `<mihe-assistant>`. Safe to call more than once. */\nexport function defineMiheAssistant(): void {\n if (typeof customElements === \"undefined\" || customElements.get(ELEMENT_NAME)) return;\n\n class Element extends HTMLElement implements MiheAssistantElement {\n static observedAttributes = [\n \"layout\",\n \"heading\",\n \"placeholder\",\n \"show-launcher\",\n \"default-open\",\n \"show-close\",\n \"form\",\n \"draggable\",\n \"corner\",\n \"voice-modes\",\n \"offset-bottom\",\n \"offset-side\",\n ];\n\n private assistant: Mihe | null = null;\n private ownOptions: WidgetOptions = {};\n private unsubscribe: (() => void) | null = null;\n private shadow: ShadowRoot | null = null;\n private panel: Panel | null = null;\n private orb: Orb | null = null;\n private launcher: HTMLButtonElement | null = null;\n private launcherKey: unknown[] = [];\n private failedIcon: string | null = null;\n private openedByDefault = false;\n\n get mihe(): Mihe | null {\n return this.assistant;\n }\n\n set mihe(value: Mihe | null) {\n if (value === this.assistant) return;\n this.disconnectAssistant();\n this.assistant = value;\n if (this.isConnected) this.connectAssistant();\n }\n\n get options(): WidgetOptions {\n return this.ownOptions;\n }\n\n set options(value: WidgetOptions) {\n this.ownOptions = { ...value };\n this.render();\n }\n\n connectedCallback(): void {\n if (!this.shadow) {\n this.shadow = this.attachShadow({ mode: \"open\" });\n this.shadow.appendChild(h(\"style\", {}, STYLES));\n }\n this.connectAssistant();\n }\n\n disconnectedCallback(): void {\n this.disconnectAssistant();\n }\n\n attributeChangedCallback(): void {\n this.render();\n }\n\n private connectAssistant(): void {\n if (!this.assistant || this.unsubscribe || !this.shadow) return;\n const mihe = this.assistant;\n this.panel = new Panel(mihe, { showClose: true, showVoice: true });\n this.shadow.appendChild(this.panel.root);\n this.unsubscribe = mihe.subscribe(() => this.render());\n // Honoured once. After that the assistant owns open and closed, so a\n // host trigger and this option cannot fight over it.\n if (this.resolved().defaultOpen && !this.openedByDefault) {\n this.openedByDefault = true;\n mihe.open();\n }\n this.render();\n }\n\n private disconnectAssistant(): void {\n this.unsubscribe?.();\n this.unsubscribe = null;\n this.orb?.destroy();\n for (const part of [this.panel?.root, this.orb?.root, this.launcher]) part?.remove();\n this.panel = null;\n this.orb = null;\n this.launcher = null;\n this.launcherKey = [];\n }\n\n private resolved(): WidgetOptions {\n return { ...withoutUndefined(fromAttributes(this)), ...withoutUndefined(this.ownOptions) };\n }\n\n private render(): void {\n const mihe = this.assistant;\n if (!mihe || !this.shadow || !this.panel) return;\n const state = mihe.getState();\n const options = this.resolved();\n const widget = resolve(options, state.widgetSettings);\n const heading = options.title ?? state.assistantName ?? \"Assistant\";\n const offset = { bottom: options.offset?.bottom ?? 20, side: options.offset?.side ?? 20 };\n const showLauncher = options.showLauncher ?? true;\n const panel = this.panel;\n\n if (options.layout === \"inline\") {\n // For `:host([layout=\"inline\"])`. Set only when missing, because\n // setting an observed attribute calls back into this render.\n if (this.getAttribute(\"layout\") !== \"inline\") this.setAttribute(\"layout\", \"inline\");\n panel.setOptions({\n title: options.title,\n placeholder: options.placeholder,\n showClose: options.showClose ?? false,\n showVoice: true,\n });\n panel.root.classList.remove(\"floating\", \"left\", \"right\");\n panel.root.removeAttribute(\"style\");\n panel.root.hidden = false;\n panel.update(state);\n this.hideOrb();\n if (this.launcher) this.launcher.hidden = true;\n return;\n }\n\n if (widget.form === \"orb\") {\n if (this.launcher) this.launcher.hidden = true;\n if (!this.orb) {\n this.orb = new Orb(mihe);\n this.shadow.appendChild(this.orb.root);\n }\n this.orb.update(state, {\n heading,\n draggable: widget.draggable,\n corner: widget.corner,\n voiceModes: widget.voiceModes,\n launcherIcon: options.launcherIcon,\n offset,\n });\n // Chat open: the panel takes the orb's place, on the orb's side.\n // Voice is the orb's, so the panel has no microphone.\n this.orb.root.hidden = state.isOpen || !showLauncher;\n this.showPanel(state.isOpen, this.orb.side(), offset, {\n title: options.title,\n placeholder: options.placeholder,\n showClose: true,\n showVoice: false,\n });\n return;\n }\n\n this.hideOrb();\n this.showPanel(state.isOpen, \"right\", offset, {\n title: options.title,\n placeholder: options.placeholder,\n showClose: true,\n showVoice: true,\n });\n const launcher = this.drawLauncher(heading, options.launcherIcon, state.launcherIconUrl);\n launcher.hidden = state.isOpen || !showLauncher;\n launcher.style.bottom = `${offset.bottom}px`;\n launcher.style.right = `${offset.side}px`;\n }\n\n private showPanel(\n open: boolean,\n side: \"left\" | \"right\",\n offset: { bottom: number; side: number },\n options: ConstructorParameters<typeof Panel>[1],\n ) {\n const panel = this.panel!;\n panel.root.hidden = !open;\n panel.root.classList.add(\"floating\");\n panel.root.style.bottom = `${offset.bottom}px`;\n panel.root.style.left = side === \"left\" ? `${offset.side}px` : \"\";\n panel.root.style.right = side === \"right\" ? `${offset.side}px` : \"\";\n // The panel keeps to the viewport however far it is lifted.\n panel.root.style.height = `min(560px, calc(100vh - ${offset.bottom + 20}px))`;\n panel.setOptions(options);\n panel.update(this.assistant!.getState());\n }\n\n private hideOrb(): void {\n if (this.orb) this.orb.root.hidden = true;\n }\n\n private drawLauncher(heading: string, icon: Node | undefined, url: string | null): HTMLButtonElement {\n if (!this.launcher) {\n this.launcher = h(\"button\", {\n class: \"launcher\",\n attrs: { type: \"button\", \"aria-controls\": \"mihe-panel\", \"aria-expanded\": \"false\" },\n on: { click: () => this.assistant?.open() },\n });\n this.shadow!.appendChild(this.launcher);\n }\n setAttrs(this.launcher, { \"aria-label\": `Open ${heading}` });\n const key = [icon, url, this.failedIcon];\n if (key.some((value, index) => value !== this.launcherKey[index])) {\n this.launcherKey = key;\n replace(\n this.launcher,\n launcherFace(icon, url, this.failedIcon, (failed) => {\n this.failedIcon = failed;\n this.render();\n }),\n );\n }\n return this.launcher;\n }\n }\n\n customElements.define(ELEMENT_NAME, Element);\n}\n\nexport interface MountedWidget {\n element: MiheAssistantElement;\n /** Remove the widget. The assistant stays connected. */\n unmount: () => void;\n}\n\n/**\n * Put the widget on the page: floating over it by default, or inline in\n * `container` with `layout: \"inline\"`.\n */\nexport function mountWidget(\n mihe: Mihe,\n options: WidgetOptions & { container?: Element } = {},\n): MountedWidget {\n defineMiheAssistant();\n const { container, ...widget } = options;\n const element = document.createElement(ELEMENT_NAME) as MiheAssistantElement;\n element.options = widget;\n element.mihe = mihe;\n (container ?? document.body).appendChild(element);\n return { element, unmount: () => element.remove() };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["PROTOCOL_VERSION", "VOICE_SAMPLE_RATE", "MAX_RECONNECT_DELAY_MS", "AssistantClient", "options", "__publicField", "text", "clientMsgId", "screen", "commandId", "ok", "result", "error", "proposalId", "confirmed", "editedInput", "turnId", "token", "socket", "url", "auth", "event", "message", "delay", "backoff", "intervalMs", "state", "attempt", "base", "formatValue", "value", "entries", "quantity", "key", "name", "nested", "details", "humanizeKey", "spaced", "asSchema", "v", "hidden", "s", "title", "key", "humanizeKey", "formatField", "value", "schema", "fmt", "props", "visible", "k", "formatValue", "fillSummary", "template", "input", "missing", "out", "_", "hintedView", "proposal", "hinted", "p", "lines", "label", "x", "summary", "SAFE_SRC", "appView", "d", "str", "l", "im", "DESCRIBE_TIMEOUT_MS", "COMPONENT_ATTRIBUTE", "OVERLAY_ID", "activates", "element", "findComponent", "componentKey", "COMPONENT_ATTRIBUTE", "HighlightEngine", "__publicField", "rect", "event", "node", "message", "target", "overlay", "OVERLAY_ID", "caption", "ACTION_SLOW_MS", "RUNTIME_ACK_WAIT_MS", "checkQueryParams", "declared", "params", "key", "value", "param", "p", "isIsoDate", "MAX_QUERY_TEXT", "parsed", "currentPath", "currentUrl", "executeCommand", "command", "ctx", "route", "fresh", "componentKey", "message", "notOnPage", "modalKey", "actionName", "input", "action", "started", "slow", "late", "result", "cause", "describeError", "controlKey", "control", "option", "tableKey", "offset", "limit", "table", "show", "invalid", "found", "shown", "chartKey", "series", "chart", "findComponent", "formatContextReport", "report", "lines", "describe", "largest", "tool", "size", "summarise", "points", "ordered", "first", "last", "min", "max", "total", "rise", "fall", "point", "i", "previous", "change", "thin", "limit", "step", "_", "MAX_CELL_CHARS", "toCell", "value", "text", "tabsDeclaration", "latest", "tabs", "label", "tabsSignature", "queryParams", "query", "key", "param", "tableSignature", "table", "tableDeclaration", "render", "row", "rowId", "columns", "opens", "out", "column", "raw", "offset", "limit", "params", "found", "chartPoints", "chart", "format", "datum", "y", "xRaw", "chartDeclaration", "ordered", "s", "summary", "summarise", "name", "current", "thin", "chartSignature", "_read", "declared", "AssistantStore", "__publicField", "listener", "componentKey", "meta", "controlKey", "control", "value", "label", "tableKey", "table", "key", "kind", "values", "description", "chartKey", "chart", "screen", "previous", "screenId", "action", "name", "FRAME_MS", "JITTER_BUFFER_MS", "CAPTURE_WORKLET", "toPcm16", "samples", "buffer", "view", "clamped", "fromPcm16", "pcmLevel", "pcm", "sum", "sample", "rms", "resample", "fromRate", "toRate", "ratio", "length", "out", "i", "position", "index", "next", "fraction", "MicrophoneCapture", "onFrame", "__publicField", "context", "deliver", "url", "worklet", "event", "sink", "frames", "processor", "track", "SpeakerQueue", "onLevel", "source", "now", "level", "report", "timer", "heard", "VoiceSession", "options", "__publicField", "SpeakerQueue", "level", "MicrophoneCapture", "pcm", "cause", "socket", "url", "event", "held", "raw", "message", "text", "pcmLevel", "state", "voiceUrl", "chatUrl", "token", "handsFree", "base", "separator", "mode", "SCREEN_DEBOUNCE_MS", "SCREEN_ARRIVAL_TIMEOUT_MS", "SDK_VERSION", "ASSISTANT_PANEL_ID", "MAX_ATTACHMENTS", "ATTACHMENT_TIMEOUT_MS", "endingSoonNotice", "secondsRemaining", "messageId", "prefix", "createMihe", "options", "mihe", "Mihe", "__publicField", "AssistantStore", "HighlightEngine", "listener", "text", "trimmed", "ready", "a", "wire", "id", "shown", "file", "describe", "patch", "change", "acceptsFile", "previewUrl", "timer", "_", "reject", "words", "cause", "gone", "proposalId", "confirmed", "p", "event", "current", "message", "ctx", "route", "client", "executeCommand", "result", "settled", "statusId", "m", "outcome", "messages", "formatContextReport", "screen", "waiter", "resolve", "path", "done", "arrived", "published", "controlKey", "value", "showing", "control", "update", "previous", "attachAccept", "AssistantClient", "proposal", "set", "view", "hintedView", "finish", "d", "appView", "DESCRIBE_TIMEOUT_MS", "serialized", "element", "componentKey", "meta", "COMPONENT_ATTRIBUTE", "name", "handler", "existing", "entry", "input", "tabs", "tabsSignature", "latest", "tabsDeclaration", "declaration", "table", "tableSignature", "tableDeclaration", "chart", "chartSignature", "chartDeclaration", "key", "values", "entries", "signature", "build", "register", "unregister", "target", "fresh", "mine", "native", "added", "sync", "onClick", "onKeyDown", "unsubscribe", "attribute", "token", "wanted", "session", "VoiceSession", "voiceUrl", "source", "level", "accept", "x", "rule", "h", "tag", "props", "children", "element", "setAttrs", "type", "listener", "append", "SVG", "svg", "attrs", "name", "value", "child", "replace", "TRANSFORM", "BODY", "VOICE", "miheMark", "size", "body", "voice", "mark", "svg", "LAUNCHER_BG", "LAUNCHER_INK", "LAUNCHER_VOICE", "STYLES", "launcherFace", "override", "url", "failed", "onFail", "h", "miheMark", "LAUNCHER_INK", "LAUNCHER_VOICE", "confirmationCard", "proposal", "onRespond", "view", "hintedView", "high", "pending", "v", "description", "h", "cardLine", "line", "im", "lineValue", "value", "item", "icon", "children", "svg", "chatIcon", "micIcon", "waveIcon", "clipIcon", "ORB_SIZE", "MARGIN", "DRAG_THRESHOLD", "HOLD_WINDOW_MS", "POSITION_KEY", "HINT_KEY", "readRest", "raw", "parsed", "writeRest", "rest", "hintDone", "markHintDone", "Orb", "mihe", "__publicField", "h", "event", "onResize", "onOutside", "path", "state", "options", "cleanup", "allowed", "top", "height", "lowest", "width", "side", "edge", "left", "voice", "proposals", "lastError", "connection", "heading", "above", "items", "voiceLive", "label", "menuish", "setAttrs", "ringState", "s", "faceKey", "sameList", "replace", "launcherFace", "url", "popover", "item", "chatIcon", "micIcon", "waveIcon", "p", "confirmationCard", "bubble", "wanted", "target", "current", "source", "level", "tick", "open", "key", "buttons", "active", "index", "press", "moved", "cancelled", "a", "b", "v", "BULLET", "NUMBERED", "HEADING", "parseMarkdown", "text", "blocks", "paragraph", "list", "flush", "parseInline", "raw", "line", "heading", "bullet", "numbered", "ordered", "safeHref", "href", "trimmed", "INLINE", "out", "push", "part", "last", "at", "m", "index", "whole", "code", "linkText", "bold1", "bold2", "italic1", "italic2", "safe", "inline", "parts", "part", "h", "node", "renderMarkdown", "text", "parseMarkdown", "block", "items", "item", "line", "DEFAULT_VOICE_MODES", "Panel", "mihe", "options", "__publicField", "h", "event", "files", "file", "clipIcon", "modes", "CallView", "composer", "ASSISTANT_PANEL_ID", "state", "heading", "canTalk", "onCall", "inputs", "value", "index", "reading", "a", "ready", "replace", "message", "renderMarkdown", "source", "proposal", "confirmationCard", "pushToTalk", "end", "mark", "miheMark", "on", "v", "level", "now", "fallback", "text", "p", "phase", "connecting", "begin", "button", "pointerId", "live", "ELEMENT_NAME", "resolve", "options", "settings", "allowed", "DEFAULT_VOICE_MODES", "mode", "fromAttributes", "element", "read", "name", "flag", "modes", "withoutUndefined", "value", "defineMiheAssistant", "Element", "__publicField", "h", "STYLES", "mihe", "Panel", "part", "state", "widget", "heading", "offset", "showLauncher", "panel", "Orb", "launcher", "open", "side", "icon", "url", "setAttrs", "key", "index", "replace", "launcherFace", "failed", "mountWidget", "container"]
7
7
  }