@ai-matrx/chat 0.4.22 → 0.4.24
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.
- package/CHANGELOG.md +11 -0
- package/dist/agents/components/context-items/bodies/UnregisteredBody.d.ts +6 -3
- package/dist/agents/components/context-items/bodies/UnregisteredBody.js +3 -9
- package/dist/agents/components/inputs/resources/attach-resource.js +1 -0
- package/dist/agents/components/inputs/smart-input/SmartAgentInput.js +16 -1
- package/dist/agents/components/inputs/smart-input/composer/ComposerMenu.js +3 -2
- package/dist/agents/components/inputs/smart-input/composer/ComposerPlusMenu.js +41 -35
- package/dist/agents/components/messages-display/user/AgentUserMessage.js +3 -1
- package/dist/agents/components/shared/AgentConversationColumn.d.ts +10 -1
- package/dist/agents/components/shared/AgentConversationColumn.js +26 -2
- package/dist/agents/redux/execution-system/messages/messages.slice.js +6 -1
- package/dist/agents/redux/execution-system/thunks/await-launching-conversation.d.ts +16 -0
- package/dist/agents/redux/execution-system/thunks/await-launching-conversation.js +16 -0
- package/dist/agents/redux/execution-system/thunks/execute-instance.thunk.js +34 -1
- package/dist/agents/redux/execution-system/thunks/process-stream.js +11 -0
- package/dist/agents/redux/execution-system/thunks/smart-execute.thunk.js +10 -0
- package/dist/host/app-data-slots.d.ts +0 -1
- package/dist/host/app-data-slots.js +25 -5
- package/dist/host/bare-host-chrome.js +4 -0
- package/dist/host/contract.d.ts +1 -1
- package/dist/host/contract.js +2 -1
- package/dist/host/conversation-hooks-react.d.ts +2 -0
- package/dist/host/conversation-hooks-react.js +12 -0
- package/dist/host/conversation-hooks.d.ts +79 -0
- package/dist/host/conversation-hooks.js +91 -0
- package/dist/host/defaults/bare-block-dispatch.d.ts +6 -0
- package/dist/host/defaults/bare-block-dispatch.js +60 -0
- package/dist/host/defaults/files-host.d.ts +26 -0
- package/dist/host/defaults/files-host.js +212 -0
- package/dist/host/defaults/resource-picker-menu.d.ts +14 -0
- package/dist/host/defaults/resource-picker-menu.js +117 -0
- package/dist/host/defaults/setting-control-input.d.ts +11 -0
- package/dist/host/defaults/setting-control-input.js +91 -0
- package/dist/host/index.d.ts +1 -0
- package/dist/host/index.js +1 -0
- package/dist/host/react.d.ts +1 -0
- package/dist/host/react.js +2 -0
- package/dist/host/ui-slots.d.ts +25 -1
- package/dist/host/ui-slots.js +59 -15
- package/dist/host/window-openers.d.ts +2 -0
- package/dist/host/window-openers.js +4 -0
- package/dist/react.cjs +1 -1
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +1 -1
- package/dist/react.js.map +1 -1
- package/dist/tool-call-visualization/registry/registry.js +4 -4
- package/dist/ui/chat-panel/ChatPanel.d.ts +50 -0
- package/dist/ui/chat-panel/ChatPanel.js +175 -0
- package/dist/ui/chat-panel/drag.d.ts +87 -0
- package/dist/ui/chat-panel/drag.js +194 -0
- package/dist/window-panels/windows/agents/AgentRunWindow.js +17 -0
- package/dist/window-panels/windows/agents/agentRunWindowAddress.d.ts +19 -0
- package/dist/window-panels/windows/agents/agentRunWindowAddress.js +11 -1
- package/package.json +6 -6
package/dist/react.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/compat/model.ts","../src/compat/tool-state.ts","../src/compat/config.ts","../src/compat/ingest.ts","../src/compat/ports.ts","../src/compat/store.ts","../src/compat/react/provider.tsx","../src/compat/react/composer/content-default.tsx","../src/compat/react/composer/markdown.tsx","../src/compat/react/composer/files.ts","../src/compat/react/ports.ts","../src/compat/react/hooks.ts","../src/compat/react/content.tsx","../src/compat/react/icons.tsx","../src/compat/react/transcript/message-list.tsx","../src/compat/react/transcript/auto-scroll.ts","../src/compat/react/transcript/message-turn.tsx","../src/compat/react/plugins/message-actions.tsx","../src/compat/react/plain-text.ts","../src/compat/react/transcript/parts.tsx","../src/compat/react/transcript/tool-chip.tsx","../src/compat/react/composer/composer.tsx","../src/compat/react/composer/attachment-chips.tsx","../src/compat/react/composer/glyphs.tsx","../src/compat/react/plugins/run-controls.tsx","../src/compat/react/composer/provider.tsx","../src/compat/react/composer/send-intent.ts","../src/compat/react/composer/textarea.tsx","../src/compat/react/composer/use-attachments.ts","../src/compat/react/composer/voice-port.tsx","../src/compat/react/shells/conversation-source.ts","../src/compat/react/shells/switching.ts","../src/compat/react/shells/use-conversation-list.ts","../src/compat/react/shells/conversation-list.tsx","../src/compat/react/shells/surface.tsx","../src/compat/react/shells/shell-header.tsx","../src/compat/react/shells/drag.ts","../src/compat/react/shells/shell-page.tsx","../src/compat/react/shells/shell-dock.tsx","../src/compat/react/shells/shell-window.tsx","../src/compat/react/shells/shell-mobile.tsx","../src/compat/react/plugins/pickers.tsx"],"sourcesContent":["/**\n * The message / part model — layer 1 of `@ai-matrx/chat`.\n *\n * Deliberately the industry-canonical shape (Vercel AI SDK `UIMessage.parts[]`\n * plus its tool-part state machine), because our own\n * `@ai-matrx/agents/projection/request` already produces its equivalent and a\n * customer's agent already knows it. Nothing here imports React, Redux, a\n * framework, or `window`.\n *\n * Every part type below was chosen against BYTES, not against a guess: the\n * live corpus in `fixtures/corpus/` carries text, reasoning (both wire\n * shapes), tool lifecycle, warnings and a fatal error, and the persisted\n * `chat.message.content` rows carry `text` | `thinking` | `tool_call` |\n * `tool_result`.\n */\n\n/** JSON on the wire. Public JSON boundaries are `unknown`-typed by standard. */\nexport type ChatJsonValue =\n | string\n | number\n | boolean\n | null\n | ChatJsonValue[]\n | { [key: string]: ChatJsonValue };\n\nexport type ChatRole = \"user\" | \"assistant\" | \"system\" | \"tool\";\n\n/**\n * THE TOOL STATE MACHINE (the champion's, verbatim). A tool part only ever\n * moves forward along these states; `advanceToolState` in `./tool-state`\n * refuses anything else out loud rather than silently overwriting.\n */\nexport type ChatToolState =\n | \"input-streaming\"\n | \"input-available\"\n | \"approval-requested\"\n | \"output-available\"\n | \"output-error\"\n | \"output-denied\";\n\n/** One tool progress beat — the `tool_event`/`tool_progress` frames. */\nexport interface ChatToolProgress {\n /** The tool name the SERVER used on this beat: it differs from the call's\n * tool name mid-run (`web` → `web_search` → `web_research` in the corpus),\n * and flattening them loses what actually ran. */\n toolName: string;\n message: string;\n percent: number | null;\n /** Server timestamp in seconds, verbatim; null when the frame carried none. */\n at: number | null;\n}\n\nexport interface ChatTextPart {\n readonly type: \"text\";\n text: string;\n}\n\nexport interface ChatReasoningPart {\n readonly type: \"reasoning\";\n text: string;\n state: \"streaming\" | \"done\";\n}\n\nexport interface ChatToolPart {\n readonly type: \"tool\";\n /** The provider/server call id — the ONLY identity a tool part has. */\n toolCallId: string;\n toolName: string;\n state: ChatToolState;\n input: unknown;\n output: unknown;\n /** Present only in `output-error`; null otherwise. */\n errorText: string | null;\n progress: ChatToolProgress[];\n /** True once the server emitted `tool_delegated` — the CLIENT must run it. */\n delegated: boolean;\n}\n\nexport interface ChatFilePart {\n readonly type: \"file\";\n /** A durable Matrx file id. A signed URL is a handoff, never an identity. */\n fileId: string;\n name: string | null;\n mediaType: string | null;\n}\n\nexport interface ChatSourcePart {\n readonly type: \"source\";\n sourceId: string;\n url: string | null;\n title: string | null;\n}\n\nexport interface ChatRenderBlockPart {\n readonly type: \"render-block\";\n blockId: string;\n blockIndex: number;\n /** The content-IR kind name. */\n kind: string;\n content: string | null;\n data: unknown;\n status: \"streaming\" | \"complete\" | \"error\";\n}\n\nexport interface ChatAskUserPart {\n readonly type: \"ask-user\";\n askId: string;\n prompt: string;\n fields: unknown;\n answered: boolean;\n}\n\n/**\n * A failure the READER must see. `userMessage` is what a screen shows;\n * `remedy` is what the reader can do about it. Law 4 — nothing fails\n * silently, and a stand-in announces itself WITH a remedy.\n */\nexport interface ChatErrorPart {\n readonly type: \"error\";\n errorType: string | null;\n code: string | null;\n message: string;\n userMessage: string;\n recoverable: boolean;\n remedy: string | null;\n}\n\n/**\n * A non-fatal server notice (`warning` / `info` frames). It is a PART, not a\n * side channel, because the corpus proves a run can lose half its tools and\n * still answer — and a reader who is not told has been lied to.\n */\nexport interface ChatNoticePart {\n readonly type: \"notice\";\n level: \"info\" | \"low\" | \"medium\" | \"high\";\n code: string | null;\n userMessage: string;\n remedy: string | null;\n}\n\nexport type ChatPart =\n | ChatTextPart\n | ChatReasoningPart\n | ChatToolPart\n | ChatFilePart\n | ChatSourcePart\n | ChatRenderBlockPart\n | ChatAskUserPart\n | ChatErrorPart\n | ChatNoticePart;\n\nexport type ChatMessageStatus =\n | \"pending\"\n | \"streaming\"\n | \"complete\"\n | \"error\"\n | \"cancelled\";\n\nexport interface ChatMessageMetadata {\n /** The server's `X-Request-ID` for the turn this message belongs to. */\n requestId: string | null;\n /** The server `chat.message` row id once `record_update` announced it. */\n serverMessageId: string | null;\n /** Model id/name the server reported, when it reported one. Never a UUID on\n * screen — `renderModelRef` resolves it (phase 2). */\n modelRef: string | null;\n createdAt: string;\n /** Free-form server metadata (finish_reason, usage, …), verbatim. */\n raw: Record<string, unknown> | null;\n}\n\nexport interface ChatMessage {\n id: string;\n role: ChatRole;\n parts: ChatPart[];\n status: ChatMessageStatus;\n metadata: ChatMessageMetadata;\n}\n\n/**\n * THE THREAD STATE MACHINE.\n * `idle → submitted → streaming → (awaiting-approval) → idle | error`.\n */\nexport type ChatThreadStatus =\n | \"idle\"\n | \"submitted\"\n | \"streaming\"\n | \"awaiting-approval\"\n | \"error\";\n\nexport interface ChatThreadSnapshot {\n conversationId: string | null;\n /** Ordered oldest-first. */\n messages: readonly ChatMessage[];\n status: ChatThreadStatus;\n /** The server request id of the CURRENT or last turn — the only id the\n * server accepts for cancel. */\n requestId: string | null;\n /** Last server `phase`, verbatim. */\n phase: string | null;\n /**\n * TRUE when nothing is persisting this thread. The screen must SAY so with\n * the remedy (`ephemeralReason`) — an unannounced ephemeral thread is the\n * silent failure Law 4 forbids.\n */\n ephemeral: boolean;\n ephemeralReason: string | null;\n /** Monotonic; bumps on every applied change so hosts can cheap-compare. */\n revision: number;\n /** Last fatal error for the thread, if any. */\n error: ChatErrorPart | null;\n}\n\nlet idCounter = 0;\n\n/**\n * A collision-resistant local id. `crypto.randomUUID` when the runtime has it\n * (browsers, node >= 19, Deno, Bun), and a counter+random fallback that\n * announces nothing because it is not a stand-in for a capability — it is the\n * same guarantee by other means.\n */\nexport function newLocalId(prefix: string): string {\n const cryptoRef: { randomUUID?: () => string } | undefined = (\n globalThis as { crypto?: { randomUUID?: () => string } }\n ).crypto;\n if (typeof cryptoRef?.randomUUID === \"function\") {\n return `${prefix}_${cryptoRef.randomUUID()}`;\n }\n idCounter += 1;\n return `${prefix}_${Date.now().toString(36)}_${idCounter.toString(36)}_${Math.random()\n .toString(36)\n .slice(2, 10)}`;\n}\n\nexport function createChatMessage(input: {\n id?: string | undefined;\n role: ChatRole;\n parts?: ChatPart[] | undefined;\n status?: ChatMessageStatus | undefined;\n metadata?: Partial<ChatMessageMetadata> | undefined;\n}): ChatMessage {\n return {\n id: input.id ?? newLocalId(\"msg\"),\n role: input.role,\n parts: input.parts ?? [],\n status: input.status ?? \"complete\",\n metadata: {\n requestId: input.metadata?.requestId ?? null,\n serverMessageId: input.metadata?.serverMessageId ?? null,\n modelRef: input.metadata?.modelRef ?? null,\n createdAt: input.metadata?.createdAt ?? new Date().toISOString(),\n raw: input.metadata?.raw ?? null,\n },\n };\n}\n\n/** Every `text` part concatenated — what a copy button copies. */\nexport function messageText(message: ChatMessage): string {\n let out = \"\";\n for (const part of message.parts) if (part.type === \"text\") out += part.text;\n return out;\n}\n\n/** Every `reasoning` part concatenated. */\nexport function messageReasoning(message: ChatMessage): string {\n let out = \"\";\n for (const part of message.parts) if (part.type === \"reasoning\") out += part.text;\n return out;\n}\n\n/** Tool parts in first-seen order. */\nexport function messageTools(message: ChatMessage): ChatToolPart[] {\n return message.parts.filter((p): p is ChatToolPart => p.type === \"tool\");\n}\n","/**\n * THE TOOL-CALL STATE MACHINE.\n *\n * `input-streaming → input-available → approval-requested →\n * output-available | output-error | output-denied`\n *\n * It lives in the kernel, not in a renderer, for the reason C22 names: this is\n * exactly the logic four repositories re-derived four different ways. A tool\n * card that shows \"running\" after the run failed, or that re-opens a settled\n * call because a late duplicate frame arrived, is the class this file closes.\n *\n * The machine is MONOTONIC: the three output states are terminal, and a\n * transition that would move backwards is REFUSED and reported — never applied\n * silently (Law 4).\n */\n\nimport type { ChatMessage, ChatToolPart, ChatToolState } from \"./model\";\n\n/** Rank: a transition is legal only when it strictly increases, or is a\n * same-state refresh of a non-terminal state (progress beats). */\nconst RANK: Record<ChatToolState, number> = {\n \"input-streaming\": 0,\n \"input-available\": 1,\n \"approval-requested\": 2,\n \"output-available\": 3,\n \"output-error\": 3,\n \"output-denied\": 3,\n};\n\nconst TERMINAL: ReadonlySet<ChatToolState> = new Set<ChatToolState>([\n \"output-available\",\n \"output-error\",\n \"output-denied\",\n]);\n\nexport function isTerminalToolState(state: ChatToolState): boolean {\n return TERMINAL.has(state);\n}\n\nexport interface ToolStateTransition {\n /** The state to store. Equal to `from` when the transition was refused. */\n readonly state: ChatToolState;\n readonly applied: boolean;\n /** Present only when `applied` is false — say WHY, out loud. */\n readonly refusal: string | null;\n}\n\n/**\n * Advance a tool part. Returns the state to keep plus, when the move was\n * refused, a sentence a diagnostic sink can print verbatim.\n */\nexport function advanceToolState(\n from: ChatToolState,\n to: ChatToolState,\n): ToolStateTransition {\n if (from === to) {\n if (TERMINAL.has(from)) {\n return {\n state: from,\n applied: false,\n refusal: `tool state '${from}' is terminal and was re-announced; the duplicate frame was ignored`,\n };\n }\n return { state: to, applied: true, refusal: null };\n }\n if (TERMINAL.has(from)) {\n return {\n state: from,\n applied: false,\n refusal: `tool state '${from}' is terminal; refusing to move it to '${to}'`,\n };\n }\n const fromRank = RANK[from];\n const toRank = RANK[to];\n if (toRank < fromRank) {\n return {\n state: from,\n applied: false,\n refusal: `refusing to move a tool call backwards from '${from}' to '${to}'`,\n };\n }\n return { state: to, applied: true, refusal: null };\n}\n\n/**\n * Map ONE aidream tool lifecycle name onto the machine.\n *\n * Server truth, read off the live corpus and\n * `@ai-matrx/agents/projection/request`: the wire emits `tool_started`,\n * `tool_progress`, `tool_step`, `tool_result_preview`, `tool_completed`,\n * `tool_error`, `tool_delegated` — either as a top-level `event` or nested in\n * a `tool_event` frame's `data.event`.\n *\n * `null` means \"this frame carries no state change\" (a progress beat), which\n * is different from an unknown name — an unknown name is returned as\n * `\"unknown\"` so the caller can report it instead of dropping it.\n */\nexport function toolLifecycleToState(\n lifecycle: string,\n): ChatToolState | null | \"unknown\" {\n switch (lifecycle) {\n case \"tool_started\":\n return \"input-available\";\n case \"tool_progress\":\n case \"tool_step\":\n case \"tool_result_preview\":\n return null;\n case \"tool_delegated\":\n // The server handed the call to the CLIENT: the input is final and\n // nothing has run yet. It is NOT an approval request (the human is not\n // being asked) and it is NOT an output.\n return \"input-available\";\n case \"tool_approval_requested\":\n return \"approval-requested\";\n case \"tool_completed\":\n return \"output-available\";\n case \"tool_error\":\n return \"output-error\";\n case \"tool_denied\":\n return \"output-denied\";\n default:\n return \"unknown\";\n }\n}\n\n/** Every lifecycle name this machine understands — the guard's census. */\nexport const KNOWN_TOOL_LIFECYCLES: readonly string[] = [\n \"tool_started\",\n \"tool_progress\",\n \"tool_step\",\n \"tool_result_preview\",\n \"tool_delegated\",\n \"tool_approval_requested\",\n \"tool_completed\",\n \"tool_error\",\n \"tool_denied\",\n];\n\n\n/**\n * THE CLIENT'S OWN ANSWER.\n *\n * When the CLIENT answers a delegated tool (`tool_delegated` → the host runs it\n * → posts the result) the server learns the call is done from that post — but\n * for a delegated call it sends NO completion frame, because it hard-suspended\n * and closed the stream. A store that only moves on frames therefore leaves the\n * card on `input-available` (\"Working…\") for as long as the conversation stays\n * open. This is the defect all four hand-built chats had (found 2026-09-30 in\n * matrx-frontend, matrx-extend and matrx-local): the answerer is the first to\n * know, so it tells ITS OWN store, through this one door, in the same tick.\n */\nexport interface ChatToolAnswer {\n toolCallId: string;\n /** The result, on success. */\n output?: unknown;\n /** Set when the tool failed — the card goes to `output-error`. */\n errorText?: string | null;\n /** Set when the reader refused it — the card goes to `output-denied`. */\n denied?: boolean;\n}\n\nexport interface ChatToolSettlement {\n /** The transcript to keep — the SAME array when nothing changed. */\n readonly messages: readonly ChatMessage[];\n /** The message whose part changed, for the per-message wake. */\n readonly messageId: string | null;\n readonly applied: boolean;\n /** Present when `applied` is false — say WHY, out loud. */\n readonly refusal: string | null;\n}\n\n/** Pure: apply a client answer to the transcript, monotonically. */\nexport function settleToolAnswer(\n messages: readonly ChatMessage[],\n answer: ChatToolAnswer,\n): ChatToolSettlement {\n for (let m = messages.length - 1; m >= 0; m -= 1) {\n const message = messages[m];\n if (!message) continue;\n const index = message.parts.findIndex(\n (part) => part.type === \"tool\" && part.toolCallId === answer.toolCallId,\n );\n if (index === -1) continue;\n const part = message.parts[index] as ChatToolPart;\n const next: ChatToolState = answer.denied\n ? \"output-denied\"\n : answer.errorText != null\n ? \"output-error\"\n : \"output-available\";\n const transition = advanceToolState(part.state, next);\n if (!transition.applied) {\n return {\n messages,\n messageId: message.id,\n applied: false,\n refusal: transition.refusal,\n };\n }\n const settled: ChatToolPart = {\n ...part,\n state: transition.state,\n output: answer.output !== undefined ? answer.output : part.output,\n errorText: next === \"output-error\" ? (answer.errorText ?? null) : null,\n };\n const parts = message.parts.slice();\n parts[index] = settled;\n const copy = messages.slice();\n copy[m] = { ...message, parts };\n return { messages: copy, messageId: message.id, applied: true, refusal: null };\n }\n return {\n messages,\n messageId: null,\n applied: false,\n refusal: `no tool call ${answer.toolCallId} is in this transcript, so there was nothing to settle`,\n };\n}\n","/**\n * OPINIONS BECOME KNOBS (Law 6) — resolved in the kernel, never hardcoded and\n * never a question for a customer.\n *\n * Four layers, lowest to highest: **platform default → organization setting →\n * surface config → per-user preference**. Every knob has a platform default,\n * so a host that sets nothing still gets the platform's taste.\n *\n * One of these knobs is a SAFETY RULE and it lives here on purpose (C22):\n * `routing` carries matrx-local's \"a confidential thread never reaches a cloud\n * provider\". In the kernel it is a thread-level constraint the store ENFORCES\n * with a refusal — it never silently falls back to a cloud target.\n */\n\nexport type ChatSendKey = \"enter\" | \"mod-enter\";\nexport type ChatDensity = \"comfortable\" | \"compact\";\n\n/**\n * Where a thread's text is allowed to go.\n * - `\"any\"` — the platform default: any target the host offers.\n * - `\"local-only\"` — CONFIDENTIAL. The thread refuses a cloud target and says\n * why. This is the highest-severity rule in the design (§12 risk 4).\n */\nexport type ChatRoutingPolicy = \"any\" | \"local-only\";\n\n/** What a transport declares about itself so the kernel can judge it. */\nexport type ChatTargetLocality = \"local\" | \"cloud\";\n\nexport interface ChatFeatures {\n /** Enter sends, or Cmd/Ctrl+Enter sends. */\n sendKey: ChatSendKey;\n density: ChatDensity;\n /** Reasoning parts visible without a click. */\n showReasoningByDefault: boolean;\n /** Tool cards start expanded. */\n expandToolCardsByDefault: boolean;\n /** Transcript sticks to the bottom while the reader is at the bottom. */\n autoScroll: boolean;\n /** Read new assistant turns aloud as they arrive. */\n readAloudAutoPlay: boolean;\n /** Keep an unsent composer draft per conversation across reloads. */\n retainDrafts: boolean;\n /**\n * The composer may send while an answer is still arriving. OFF by the\n * platform default (a kernel thread runs one turn at a time). A host whose\n * send QUEUES the message behind the live turn — a coding session, where\n * the engine holds a per-session queue — turns it on; the composer then\n * shows Send (queue) beside Stop instead of Stop alone.\n */\n sendWhileBusy: boolean;\n /** THE ARCHIVED-ITEMS LAW: the conversation list hides archived by default. */\n hideArchivedConversations: boolean;\n /**\n * The per-turn action bar (copy, ask again, edit and send again, plus the\n * host's own). ON by the platform default — every serious chat has one, and\n * a reader who cannot copy an answer retypes it. An organization that does\n * not want it turns it OFF here rather than a host forking the transcript.\n */\n messageActions: boolean;\n /** Largest attachment the composer accepts, in BYTES. */\n attachmentMaxBytes: number;\n /**\n * How the platform's inline reasoning sentinel is treated.\n *\n * Ground truth (captured live 2026-09-17): the Google path does NOT emit\n * `reasoning_chunk`. It emits `reasoning {state:\"started\"}`, then ordinary\n * `chunk` frames whose text opens with `<reasoning>` and closes with\n * `</reasoning>`, then `reasoning {state:\"stopped\"}`.\n *\n * - `\"split\"` (platform default) — that span becomes a `reasoning` part, so\n * a reader never sees the raw sentinel in the answer.\n * - `\"inline\"` — the span stays in the answer text, byte-for-byte identical\n * to `@ai-matrx/agents/projection/request`'s `answer`. This exists so the\n * parity harness can prove EXACT equality with the shipped projection, and\n * so a host mid-migration can keep its current screen.\n */\n reasoningSentinel: \"split\" | \"inline\";\n /** Where this thread's text may go. */\n routing: ChatRoutingPolicy;\n}\n\nexport const CHAT_PLATFORM_DEFAULTS: Readonly<ChatFeatures> = Object.freeze({\n sendKey: \"enter\",\n density: \"comfortable\",\n showReasoningByDefault: false,\n expandToolCardsByDefault: false,\n autoScroll: true,\n readAloudAutoPlay: false,\n retainDrafts: true,\n sendWhileBusy: false,\n hideArchivedConversations: true,\n messageActions: true,\n attachmentMaxBytes: 25 * 1024 * 1024,\n reasoningSentinel: \"split\",\n routing: \"any\",\n});\n\nexport interface ChatConfigLayers {\n /** Organization setting rows, resolved by the host. */\n organization?: Partial<ChatFeatures>;\n /** This surface's configuration (a dock is not the full page). */\n surface?: Partial<ChatFeatures>;\n /** This person's preference. */\n user?: Partial<ChatFeatures>;\n}\n\n/** Resolve the four layers. Later layers win, key by key. */\nexport function resolveChatFeatures(layers: ChatConfigLayers = {}): ChatFeatures {\n return {\n ...CHAT_PLATFORM_DEFAULTS,\n ...stripUndefined(layers.organization),\n ...stripUndefined(layers.surface),\n ...stripUndefined(layers.user),\n };\n}\n\nfunction stripUndefined(\n value: Partial<ChatFeatures> | undefined,\n): Partial<ChatFeatures> {\n if (!value) return {};\n const out: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(value)) {\n if (entry !== undefined) out[key] = entry;\n }\n return out as Partial<ChatFeatures>;\n}\n\n/** The refusal a confidential thread produces instead of a silent fallback. */\nexport interface ChatRoutingRefusal {\n readonly allowed: false;\n readonly code: \"confidential_thread_cloud_target_refused\";\n readonly userMessage: string;\n readonly remedy: string;\n}\n\nexport type ChatRoutingDecision = { readonly allowed: true } | ChatRoutingRefusal;\n\n/**\n * THE CONFIDENTIAL-ROUTING RULE, in the kernel where it belongs.\n *\n * A thread pinned `local-only` REFUSES a cloud target. It does not downgrade,\n * it does not warn-and-send, and it does not quietly pick a different target:\n * the send fails with a named reason and a remedy. Re-deriving this per host\n * is how private text reaches a provider.\n */\nexport function decideRouting(\n policy: ChatRoutingPolicy,\n target: ChatTargetLocality,\n): ChatRoutingDecision {\n if (policy === \"local-only\" && target === \"cloud\") {\n return {\n allowed: false,\n code: \"confidential_thread_cloud_target_refused\",\n userMessage:\n \"This conversation is marked confidential, so it cannot be sent to a cloud AI provider.\",\n remedy:\n \"Choose a local engine for this conversation, or start a new conversation that is not marked confidential.\",\n };\n }\n return { allowed: true };\n}\n","/**\n * THE REDUCER — one live stream frame becomes message parts.\n *\n * Every branch below exists because the LIVE corpus in `fixtures/corpus/`\n * contains that frame. Nothing here was guessed from a client: the wire\n * vocabulary was read off bytes recorded from `server.app.matrxserver.com` on\n * 2026-09-17 and cross-checked against\n * `@ai-matrx/agents/projection/request`.\n *\n * The projector is deliberately MUTABLE. A streaming turn applies one frame\n * per token; rebuilding an immutable tree per token is the classic reason a\n * chat janks at 2,000 tokens. Immutability is provided where React needs it —\n * `snapshot()` hands back a fresh `ChatMessage` with a fresh `parts` array —\n * and the store coalesces those snapshots per animation frame.\n */\n\nimport {\n advanceToolState,\n toolLifecycleToState,\n} from \"./tool-state\";\nimport type { ChatFeatures } from \"./config\";\nimport {\n createChatMessage,\n type ChatErrorPart,\n type ChatMessage,\n type ChatNoticePart,\n type ChatPart,\n type ChatRenderBlockPart,\n type ChatReasoningPart,\n type ChatTextPart,\n type ChatToolPart,\n} from \"./model\";\nimport type { ChatStreamFrame } from \"./ports\";\n\n/** The reasoning sentinel the Google path wraps thinking text in. */\nexport const REASONING_OPEN = \"<reasoning>\";\nexport const REASONING_CLOSE = \"</reasoning>\";\n\nexport type ChatTurnOutcome =\n | \"streaming\"\n | \"awaiting-tools\"\n | \"complete\"\n | \"error\"\n | \"cancelled\";\n\nexport interface ChatTurnRefusal {\n code: string;\n message: string;\n detail?: unknown;\n}\n\nexport interface ChatTurnProjectorOptions {\n features: ChatFeatures;\n /** The message id for the assistant turn (kept stable across the stream). */\n messageId?: string | undefined;\n /** Every refusal / unknown frame — reported, never dropped (Law 4). */\n onRefusal?: ((refusal: ChatTurnRefusal) => void) | undefined;\n}\n\nexport interface ChatTurnProjector {\n apply(frame: ChatStreamFrame): void;\n /** A fresh, structurally-shared assistant message. */\n snapshot(): ChatMessage;\n readonly state: ChatTurnState;\n}\n\nexport interface ChatTurnState {\n outcome: ChatTurnOutcome;\n conversationId: string | null;\n requestId: string | null;\n phase: string | null;\n /** The `chat.message` row ids the server reserved, by role. */\n serverMessageIds: { user: string | null; assistant: string | null };\n completion: Record<string, unknown> | null;\n error: ChatErrorPart | null;\n /** Bumps on every applied frame — the store's cheap dirty check. */\n revision: number;\n lastTransportSeq: number;\n transportStreamId: string | null;\n /** Frames the projector did not understand; never silently zero. */\n unknownEvents: string[];\n}\n\nconst asRecord = (value: unknown): Record<string, unknown> =>\n value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n\nconst asString = (value: unknown): string | null =>\n typeof value === \"string\" ? value : null;\n\nconst asNumber = (value: unknown): number | null =>\n typeof value === \"number\" && Number.isFinite(value) ? value : null;\n\n/** Events the projector deliberately ignores — real frames with no part. */\nconst IGNORED_EVENTS: ReadonlySet<string> = new Set([\n \"heartbeat\",\n \"context_state\",\n \"context_trimmed\",\n \"record_update\",\n \"ping\",\n \"debug\",\n // `init`/`completion` pairs exist for five operations; only the\n // `user_request` completion is terminal, and `init` carries no part.\n \"init\",\n]);\n\nexport function createChatTurnProjector(\n options: ChatTurnProjectorOptions,\n): ChatTurnProjector {\n const features = options.features;\n const messageId = options.messageId ?? createChatMessage({ role: \"assistant\" }).id;\n const parts: ChatPart[] = [];\n const toolsByCallId = new Map<string, ChatToolPart>();\n const blocksById = new Map<string, ChatRenderBlockPart>();\n\n /** True between `reasoning {started}` and `reasoning {stopped}`. */\n let reasoningActive = false;\n /** True while inside an unterminated `<reasoning>` span in chunk text. */\n let insideSentinel = false;\n\n const state: ChatTurnState = {\n outcome: \"streaming\",\n conversationId: null,\n requestId: null,\n phase: null,\n serverMessageIds: { user: null, assistant: null },\n completion: null,\n error: null,\n revision: 0,\n lastTransportSeq: 0,\n transportStreamId: null,\n unknownEvents: [],\n };\n\n const refuse = (refusal: ChatTurnRefusal): void => {\n options.onRefusal?.(refusal);\n };\n\n const lastPart = (): ChatPart | undefined => parts[parts.length - 1];\n\n const appendText = (text: string): void => {\n const tail = lastPart();\n if (tail && tail.type === \"text\") {\n tail.text += text;\n return;\n }\n const part: ChatTextPart = { type: \"text\", text };\n parts.push(part);\n };\n\n const appendReasoning = (text: string): void => {\n const tail = lastPart();\n if (tail && tail.type === \"reasoning\" && tail.state === \"streaming\") {\n tail.text += text;\n return;\n }\n const part: ChatReasoningPart = { type: \"reasoning\", text, state: \"streaming\" };\n parts.push(part);\n };\n\n const closeReasoning = (): void => {\n for (let i = parts.length - 1; i >= 0; i -= 1) {\n const part = parts[i];\n if (part && part.type === \"reasoning\" && part.state === \"streaming\") {\n part.state = \"done\";\n return;\n }\n }\n };\n\n /**\n * Route ONE chunk of answer text.\n *\n * With `reasoningSentinel: \"inline\"` the text goes straight to the answer,\n * byte-for-byte identical to the shipped projection's `answer`. With\n * `\"split\"` (the platform default) the `<reasoning>…</reasoning>` span\n * becomes a reasoning part, so a reader never sees the raw sentinel.\n */\n const routeChunk = (text: string): void => {\n if (features.reasoningSentinel === \"inline\") {\n appendText(text);\n return;\n }\n let rest = text;\n while (rest.length > 0) {\n if (insideSentinel) {\n const close = rest.indexOf(REASONING_CLOSE);\n if (close === -1) {\n appendReasoning(rest);\n return;\n }\n appendReasoning(rest.slice(0, close));\n closeReasoning();\n insideSentinel = false;\n rest = rest.slice(close + REASONING_CLOSE.length);\n continue;\n }\n const open = rest.indexOf(REASONING_OPEN);\n if (open === -1) {\n appendText(rest);\n return;\n }\n if (open > 0) appendText(rest.slice(0, open));\n insideSentinel = true;\n rest = rest.slice(open + REASONING_OPEN.length);\n }\n };\n\n const toolFor = (callId: string, toolName: string): ChatToolPart => {\n const existing = toolsByCallId.get(callId);\n if (existing) return existing;\n const part: ChatToolPart = {\n type: \"tool\",\n toolCallId: callId,\n toolName,\n state: \"input-streaming\",\n input: undefined,\n output: undefined,\n errorText: null,\n progress: [],\n delegated: false,\n };\n toolsByCallId.set(callId, part);\n parts.push(part);\n return part;\n };\n\n const applyToolLifecycle = (\n lifecycle: string,\n data: Record<string, unknown>,\n ): void => {\n const callId = asString(data.call_id);\n const toolName = asString(data.tool_name);\n if (callId === null || toolName === null) {\n refuse({\n code: \"tool_frame_without_identity\",\n message: `a '${lifecycle}' frame arrived without call_id/tool_name and was ignored`,\n detail: data,\n });\n return;\n }\n const part = toolFor(callId, toolName);\n const payload = asRecord(data.data);\n const message = asString(data.message) ?? \"\";\n\n const next = toolLifecycleToState(lifecycle);\n if (next === \"unknown\") {\n state.unknownEvents.push(lifecycle);\n refuse({\n code: \"unknown_tool_lifecycle\",\n message: `tool lifecycle '${lifecycle}' is not in this package's state machine; the frame was recorded but not applied`,\n detail: data,\n });\n return;\n }\n\n if (lifecycle === \"tool_started\" && \"arguments\" in payload) {\n part.input = payload.arguments;\n }\n if (lifecycle === \"tool_delegated\") {\n part.delegated = true;\n if (\"arguments\" in payload) part.input = payload.arguments;\n }\n if (lifecycle === \"tool_completed\" && \"result\" in payload) {\n part.output = payload.result;\n }\n if (lifecycle === \"tool_error\") {\n part.errorText =\n asString(payload.error) ?? (message || `The ${toolName} tool failed.`);\n }\n\n if (next === null) {\n part.progress.push({\n toolName,\n message,\n percent: asNumber(payload.percent),\n at: asNumber(data.timestamp),\n });\n return;\n }\n\n const transition = advanceToolState(part.state, next);\n if (!transition.applied) {\n refuse({\n code: \"tool_state_transition_refused\",\n message: `${transition.refusal ?? \"refused\"} (call ${callId}, tool ${toolName})`,\n detail: data,\n });\n return;\n }\n part.state = transition.state;\n if (next === \"input-available\" && part.delegated) {\n state.outcome = \"awaiting-tools\";\n } else if (\n state.outcome === \"awaiting-tools\" &&\n (next === \"output-available\" || next === \"output-error\")\n ) {\n state.outcome = \"streaming\";\n }\n };\n\n const pushNotice = (\n level: ChatNoticePart[\"level\"],\n data: Record<string, unknown>,\n ): void => {\n const userMessage = asString(data.user_message) ?? \"\";\n if (!userMessage) return;\n const part: ChatNoticePart = {\n type: \"notice\",\n level,\n code: asString(data.code),\n userMessage,\n remedy: asString(asRecord(data.metadata).remedy),\n };\n parts.push(part);\n };\n\n const apply = (frame: ChatStreamFrame): void => {\n const streamSeq = asNumber(frame.stream_seq);\n const streamId = asString(frame.stream_id);\n const sameSegment = streamId === null || streamId === state.transportStreamId;\n if (sameSegment && streamSeq !== null && streamSeq <= state.lastTransportSeq) {\n // A replayed frame after a reconnect. Dropping it is the whole point of\n // the sequence: applying it doubles text.\n return;\n }\n state.lastTransportSeq =\n streamSeq === null\n ? state.lastTransportSeq\n : sameSegment\n ? Math.max(state.lastTransportSeq, streamSeq)\n : streamSeq;\n if (streamId !== null) state.transportStreamId = streamId;\n state.revision += 1;\n\n const data = asRecord(frame.data);\n\n switch (frame.event) {\n case \"chunk\": {\n const text = asString(data.text);\n if (text !== null) routeChunk(text);\n return;\n }\n case \"reasoning_chunk\": {\n const text = asString(data.text);\n if (text !== null) {\n reasoningActive = true;\n appendReasoning(text);\n }\n return;\n }\n case \"reasoning\": {\n const started = data.state === \"started\";\n reasoningActive = started;\n if (!started) closeReasoning();\n return;\n }\n case \"phase\": {\n const phase = asString(data.phase);\n if (phase !== null) state.phase = phase;\n return;\n }\n case \"data\": {\n if (data.type === \"conversation_id\") {\n const conversationId = asString(data.conversation_id);\n if (conversationId !== null) state.conversationId = conversationId;\n }\n return;\n }\n case \"record_reserved\": {\n // The server names the `chat.message` rows it reserved and, in\n // `metadata.role`, which side each one is. This is how an optimistic\n // user bubble learns its durable row id without a re-read.\n if (asString(data.table) !== \"message\") return;\n const recordId = asString(data.record_id);\n const role = asString(asRecord(data.metadata).role);\n if (recordId === null) return;\n if (role === \"user\") state.serverMessageIds.user = recordId;\n else if (role === \"assistant\") state.serverMessageIds.assistant = recordId;\n return;\n }\n case \"tool_event\": {\n const lifecycle = asString(data.event);\n if (lifecycle === null) {\n refuse({\n code: \"tool_event_without_lifecycle\",\n message: \"a tool_event frame carried no data.event and was ignored\",\n detail: data,\n });\n return;\n }\n applyToolLifecycle(lifecycle, data);\n return;\n }\n case \"tool_started\":\n case \"tool_progress\":\n case \"tool_step\":\n case \"tool_result_preview\":\n case \"tool_completed\":\n case \"tool_error\":\n case \"tool_delegated\": {\n applyToolLifecycle(frame.event, data);\n return;\n }\n case \"render_block\": {\n const blockId = asString(data.blockId);\n const blockIndex = asNumber(data.blockIndex);\n const kind = asString(data.type);\n if (blockId === null || blockIndex === null || kind === null) {\n refuse({\n code: \"render_block_without_identity\",\n message: \"a render_block frame lacked blockId/blockIndex/type and was ignored\",\n detail: data,\n });\n return;\n }\n const status =\n data.status === \"complete\" || data.status === \"error\"\n ? (data.status as \"complete\" | \"error\")\n : \"streaming\";\n const existing = blocksById.get(blockId);\n if (existing) {\n existing.status = status;\n existing.content = asString(data.content);\n existing.data = data.data;\n return;\n }\n const part: ChatRenderBlockPart = {\n type: \"render-block\",\n blockId,\n blockIndex,\n kind,\n content: asString(data.content),\n data: data.data,\n status,\n };\n blocksById.set(blockId, part);\n parts.push(part);\n return;\n }\n case \"warning\": {\n const level = asString(data.level);\n pushNotice(\n level === \"high\" || level === \"medium\" || level === \"low\" ? level : \"low\",\n data,\n );\n return;\n }\n case \"info\": {\n // `initial_message` is chrome (\"Initializing Matrx Chat…\"), not news.\n if (asString(data.code) === \"initial_message\") return;\n pushNotice(\"info\", data);\n return;\n }\n case \"error\": {\n const message =\n asString(data.message) ?? asString(data.user_message) ?? \"The run failed.\";\n const part: ChatErrorPart = {\n type: \"error\",\n errorType: asString(data.error_type),\n code: asString(data.code),\n message,\n userMessage: asString(data.user_message) ?? message,\n recoverable: data.recoverable === true,\n remedy: asString(asRecord(data.details).remedy),\n };\n parts.push(part);\n state.error = part;\n state.outcome = \"error\";\n return;\n }\n case \"completion\": {\n if (asString(data.operation) !== \"user_request\") return;\n state.completion = data;\n const status = asString(data.status);\n if (status === \"failed\") {\n state.outcome = \"error\";\n if (state.error === null) {\n const result = asRecord(data.result);\n const message =\n asString(result.error) ??\n asString(result.user_message) ??\n \"The run failed.\";\n const part: ChatErrorPart = {\n type: \"error\",\n errorType: \"run_failed\",\n code: null,\n message,\n userMessage: message,\n recoverable: true,\n remedy: null,\n };\n parts.push(part);\n state.error = part;\n }\n } else if (status === \"cancelled\") {\n state.outcome = \"cancelled\";\n } else if (state.outcome !== \"error\") {\n state.outcome = \"complete\";\n }\n return;\n }\n case \"end\": {\n if (state.outcome === \"streaming\" || state.outcome === \"awaiting-tools\") {\n state.outcome = \"complete\";\n }\n if (insideSentinel || reasoningActive) {\n closeReasoning();\n insideSentinel = false;\n reasoningActive = false;\n }\n return;\n }\n default: {\n if (IGNORED_EVENTS.has(frame.event)) return;\n state.unknownEvents.push(frame.event);\n refuse({\n code: \"unknown_stream_event\",\n message: `stream event '${frame.event}' is not in this package's vocabulary; it was recorded, not dropped`,\n detail: frame.data,\n });\n }\n }\n };\n\n const snapshot = (): ChatMessage => {\n const status =\n state.outcome === \"error\"\n ? \"error\"\n : state.outcome === \"cancelled\"\n ? \"cancelled\"\n : state.outcome === \"complete\"\n ? \"complete\"\n : \"streaming\";\n const completion = state.completion;\n return {\n id: messageId,\n role: \"assistant\",\n // Fresh array + fresh part objects: React compares by identity, and a\n // mutated-in-place part never re-renders.\n parts: parts.map((part) => ({ ...part }) as ChatPart),\n status,\n metadata: {\n requestId: state.requestId,\n serverMessageId: state.serverMessageIds.assistant,\n modelRef: completion ? extractModelRef(completion) : null,\n createdAt: new Date(0).toISOString(),\n raw: completion,\n },\n };\n };\n\n return {\n apply,\n snapshot,\n get state() {\n return state;\n },\n };\n}\n\n/**\n * The model the server actually billed, from the completion's usage block\n * (`result.total_usage.by_model` — one key per model that ran). Returns null\n * rather than a UUID or a guess: a screen never prints an id, and \"unknown\" is\n * the honest answer when the completion carried no usage.\n */\nexport function extractModelRef(\n completion: Record<string, unknown>,\n): string | null {\n const result = asRecord(completion.result);\n const usage = asRecord(result.total_usage);\n const byModel = asRecord(usage.by_model);\n const names = Object.keys(byModel);\n return names.length > 0 ? (names[names.length - 1] ?? null) : null;\n}\n","/**\n * HOST INJECTION SEAMS — the ports a host may override.\n *\n * Per THE ALL-INCLUSIVE LAW every port here either ships a working default in\n * this package (`./matrx`, or the in-file defaults below) or is genuinely host\n * identity. A host injects identity and nothing else to be up and running.\n *\n * Phase 1 declares the ports the KERNEL uses. `files`, `nav`, `shell`,\n * `renderContent`, `renderModelRef` and `capabilities` are phase 2's — they\n * are deliberately ABSENT here rather than declared-and-unused, because a port\n * nothing calls is untested theory that rots (C25's reasoning).\n */\n\nimport type { ChatMessage, ChatRole } from \"./model\";\nimport type { ChatTargetLocality } from \"./config\";\n\n/** The ONE true injection. Read from the client's own session, never a cached copy. */\nexport interface ChatIdentityPort {\n /** A fresh access token per call. Cached copies rot when the cookie rotates. */\n getAccessToken(): Promise<string | null> | string | null;\n userId: string;\n organizationId?: string | undefined;\n}\n\n/** One frame off the wire, already normalized by `@ai-matrx/agents/stream/ndjson`. */\nexport interface ChatStreamFrame {\n event: string;\n data: unknown;\n stream_id?: string | undefined;\n stream_seq?: number | undefined;\n}\n\n/** What a started turn hands back before any frame arrives. */\nexport interface ChatTurnHandle {\n /** The server's `X-Request-ID` — the ONLY id cancel accepts. */\n requestId: string | null;\n conversationId: string | null;\n frames: AsyncIterable<ChatStreamFrame>;\n}\n\nexport interface ChatSendRequest {\n conversationId: string | null;\n /** True when this turn creates the conversation. */\n isNewConversation: boolean;\n /** False for an ephemeral run: nothing read, nothing written. */\n store: boolean;\n text: string;\n /** Durable Matrx file ids attached to this turn. */\n fileIds: readonly string[];\n signal: AbortSignal;\n}\n\n/**\n * THE TRANSPORT PORT — subscribe-shaped, NOT a fetch.\n *\n * It is this shape because matrx-extend's MV3 stream lives in an offscreen\n * document and is relayed over `chrome.runtime`: an API that has only ever met\n * `fetch` ossifies wrong. A host that does use fetch implements this in a few\n * lines (`./matrx` is that implementation).\n */\nexport interface ChatTransportPort {\n /** Where this transport sends text. The kernel refuses `\"cloud\"` for a\n * confidential thread — so a transport that lies here defeats the rule, and\n * it is the transport author's obligation to be honest. */\n readonly locality: ChatTargetLocality;\n start(request: ChatSendRequest): Promise<ChatTurnHandle>;\n /** Cooperative stop at the next iteration boundary. */\n cancel(requestId: string): Promise<void>;\n}\n\n/** A conversation as the history list shows it. */\nexport interface ChatConversationSummary {\n id: string;\n title: string | null;\n updatedAt: string;\n createdAt: string;\n messageCount: number;\n archived: boolean;\n isFavorite: boolean;\n /** Which surface produced it — the history list's source filter. */\n sourceApp: string | null;\n sourceFeature: string | null;\n}\n\nexport interface ChatConversationListQuery {\n limit?: number | undefined;\n /** Keyset cursor: return rows older than this ISO timestamp. */\n before?: string | undefined;\n /**\n * THE ARCHIVED-ITEMS LAW. Default false — archived rows are hidden, and the\n * caller that shows the list must name what it hides.\n */\n includeArchived?: boolean | undefined;\n sourceApp?: string | undefined;\n sourceFeature?: string | undefined;\n /**\n * The page's own organization filter (`?org_filter=`). Absent/null = ALL the\n * person's organizations, the default on every visit — never the active\n * organization (active-org-is-never-a-list-filter.md).\n */\n organizationFilter?: string | null | undefined;\n}\n\nexport interface ChatConversationListPage {\n conversations: ChatConversationSummary[];\n /** How many rows the archive filter hid, so the footer can say so. */\n archivedHidden: number;\n /** Cursor for the next page, or null at the end. */\n nextBefore: string | null;\n}\n\n/**\n * THE PERSISTENCE BOUNDARY (C10). The `chat.*` schema belongs to aidream, so\n * this package does not OWN it — but `./matrx` ships the complete binding, so\n * a host gets working persistence for free and only a customer on their own\n * tables ever implements this.\n *\n * With no binding the thread runs ephemeral AND SAYS SO on screen\n * (`ChatThreadSnapshot.ephemeral` + `ephemeralReason`).\n */\nexport interface ChatPersistencePort {\n /** Full transcript of one conversation, oldest first. */\n loadThread(conversationId: string): Promise<ChatMessage[]>;\n listConversations(\n query: ChatConversationListQuery,\n ): Promise<ChatConversationListPage>;\n setArchived(conversationId: string, archived: boolean): Promise<void>;\n rename(conversationId: string, title: string): Promise<void>;\n}\n\n/** Typed diagnostic sink. Default: `console`. */\nexport interface ChatErrorSinkPort {\n capture(event: {\n code: string;\n message: string;\n detail?: unknown;\n }): void;\n}\n\n/** Toast / banner sink. Default: a no-op that still reaches the errorSink. */\nexport interface ChatNotifierPort {\n notify(event: {\n level: \"info\" | \"warning\" | \"error\";\n message: string;\n remedy?: string | undefined;\n }): void;\n}\n\n/** Drafts, density, sidebar state. Default: in-memory, announced once. */\nexport interface ChatPrefsPort {\n get(key: string): string | null;\n set(key: string, value: string): void;\n remove(key: string): void;\n}\n\nexport function createConsoleErrorSink(): ChatErrorSinkPort {\n return {\n capture(event) {\n console.error(`[ai-matrx/chat] ${event.code}: ${event.message}`, event.detail);\n },\n };\n}\n\n/**\n * The default notifier. It never swallows: everything it is handed also\n * reaches the console, with the remedy, because a notice nobody renders is a\n * silent failure.\n */\nexport function createConsoleNotifier(): ChatNotifierPort {\n return {\n notify(event) {\n const line = event.remedy\n ? `[ai-matrx/chat] ${event.message} — ${event.remedy}`\n : `[ai-matrx/chat] ${event.message}`;\n if (event.level === \"error\") console.error(line);\n else if (event.level === \"warning\") console.warn(line);\n else console.info(line);\n },\n };\n}\n\n/**\n * The memory prefs fallback. It ANNOUNCES ITSELF ONCE with the remedy the\n * first time it is written to, because a draft that silently evaporates on\n * reload is exactly the class Law 4 forbids.\n */\nexport function createMemoryPrefs(\n notifier: ChatNotifierPort = createConsoleNotifier(),\n): ChatPrefsPort {\n const map = new Map<string, string>();\n let announced = false;\n const announce = (): void => {\n if (announced) return;\n announced = true;\n notifier.notify({\n level: \"warning\",\n message:\n \"Chat preferences and drafts are being kept in memory only, so they will be lost when this page reloads.\",\n remedy:\n \"Give the chat a `prefs` port backed by localStorage (or your own store) to keep drafts across reloads.\",\n });\n };\n return {\n get: (key) => map.get(key) ?? null,\n set: (key, value) => {\n announce();\n map.set(key, value);\n },\n remove: (key) => {\n map.delete(key);\n },\n };\n}\n\n/**\n * `localStorage` prefs with the memory fallback. Every read AND write is\n * wrapped: a private window, blocked site data, or a full quota makes the\n * accessor THROW, and an unguarded prefs port takes the whole chat down with\n * it.\n */\nexport function createWebPrefs(\n notifier: ChatNotifierPort = createConsoleNotifier(),\n): ChatPrefsPort {\n const fallback = createMemoryPrefs(notifier);\n let store: Storage | null = null;\n try {\n store = (globalThis as { localStorage?: Storage }).localStorage ?? null;\n if (store) {\n const probe = \"__ai_matrx_chat_probe__\";\n store.setItem(probe, \"1\");\n store.removeItem(probe);\n }\n } catch {\n store = null;\n }\n if (!store) return fallback;\n const live = store;\n return {\n get: (key) => {\n // A write that fell back to memory (quota, or a storage that accepts the\n // probe and rejects real writes) is INVISIBLE to `getItem` — it returns\n // null rather than throwing, so a read that trusted it alone would hand\n // back an empty draft and the reader would watch their message vanish.\n try {\n const stored = live.getItem(key);\n if (stored !== null) return stored;\n } catch {\n /* fall through to the memory fallback */\n }\n return fallback.get(key);\n },\n set: (key, value) => {\n try {\n live.setItem(key, value);\n } catch {\n fallback.set(key, value);\n }\n },\n remove: (key) => {\n try {\n live.removeItem(key);\n } catch {\n /* the durable copy may not exist; the memory copy still must go */\n }\n fallback.remove(key);\n },\n };\n}\n\n/** Roles the kernel renders. Anything else is reported, never dropped. */\nexport const CHAT_ROLES: readonly ChatRole[] = [\n \"user\",\n \"assistant\",\n \"system\",\n \"tool\",\n];\n","/**\n * THE THREAD STORE — layer 1 of `@ai-matrx/chat`.\n *\n * `getSnapshot()` / `subscribe()` is the `useSyncExternalStore` source shape\n * (the AI SDK's proven one), plus `subscribeToMessage(id)` so ONE streaming\n * bubble re-renders without touching the list. There is no React here, no\n * Redux here, and no `window` touched at import time.\n *\n * Performance rules that are code, not prose:\n * 1. store writes are COALESCED per frame tick, never per token;\n * 2. only the streaming message's per-message subscribers are woken on a\n * token; the thread subscribers are woken on the same coalesced tick but\n * the message objects of every OTHER message keep their identity, so a\n * memoized list re-renders one row.\n */\n\nimport {\n createChatTurnProjector,\n type ChatTurnRefusal,\n} from \"./ingest\";\nimport {\n decideRouting,\n resolveChatFeatures,\n type ChatConfigLayers,\n type ChatFeatures,\n} from \"./config\";\nimport {\n createChatMessage,\n newLocalId,\n type ChatErrorPart,\n type ChatMessage,\n type ChatThreadSnapshot,\n type ChatThreadStatus,\n} from \"./model\";\nimport {\n createConsoleErrorSink,\n createConsoleNotifier,\n type ChatErrorSinkPort,\n type ChatNotifierPort,\n type ChatPersistencePort,\n type ChatTransportPort,\n} from \"./ports\";\nimport {\n settleToolAnswer,\n type ChatToolAnswer,\n type ChatToolSettlement,\n} from \"./tool-state\";\n\nexport interface ChatThreadConfig {\n transport: ChatTransportPort;\n /** Absent ⇒ the thread is EPHEMERAL and says so in its snapshot. */\n persistence?: ChatPersistencePort | undefined;\n /** Existing conversation to continue. Omit to start a new one on first send. */\n conversationId?: string | undefined;\n features?: ChatConfigLayers | undefined;\n errorSink?: ChatErrorSinkPort | undefined;\n notifier?: ChatNotifierPort | undefined;\n /**\n * Coalescing window. The default `null` means \"schedule on the runtime's\n * frame clock\" (`requestAnimationFrame` when there is one, a microtask\n * otherwise). A number is a millisecond timer, which is what tests use so\n * they never depend on a browser clock.\n */\n coalesceMs?: number | null | undefined;\n}\n\nexport interface ChatSendInput {\n text: string;\n fileIds?: readonly string[] | undefined;\n}\n\nexport interface ChatSendResult {\n /** False when the send was refused BEFORE anything left the machine. */\n accepted: boolean;\n /** Present when `accepted` is false — the reason, in the reader's words. */\n refusal: ChatErrorPart | null;\n}\n\nexport interface ChatThreadStore {\n getSnapshot(): ChatThreadSnapshot;\n subscribe(listener: () => void): () => void;\n /** Wake ONLY on this message. The whole point of the per-message seam. */\n subscribeToMessage(messageId: string, listener: () => void): () => void;\n getMessageSnapshot(messageId: string): ChatMessage | undefined;\n readonly features: ChatFeatures;\n send(input: ChatSendInput): Promise<ChatSendResult>;\n /** Cooperative stop. Resolves once the server acknowledged (or failed to). */\n cancel(): Promise<void>;\n /** Replace the transcript — used by history load and by tests. */\n setMessages(messages: readonly ChatMessage[]): void;\n /** Load a stored conversation through the persistence port. */\n loadConversation(conversationId: string): Promise<void>;\n /**\n * Whether `startNewConversation()` actually does anything here. The kernel\n * answers true; `/external-store` answers true only when the host gave it a\n * handler. A shell reads THIS to decide whether a \"New conversation\" control\n * exists at all — a New button that cannot start one is the dead control Law\n * 4 forbids.\n */\n readonly canStartNewConversation: boolean;\n /**\n * Empty this thread and forget its conversation id, so the NEXT send starts a\n * fresh conversation the server names.\n *\n * 🚨 WHY THIS IS ON THE KERNEL AND NOT THE HOST'S JOB. Until the lane merge\n * the conversation id was fixed for the life of the store, so \"New\n * conversation\" on the shipped `chat.conversation` source could only work if\n * the host threw the whole thread away and built another — which every\n * consumer would then have had to reimplement, including the timer, the abort\n * and the draft carry. The client still never invents an id: this clears the\n * id and lets the FIRST TURN mint it, which is exactly what the platform\n * does for a conversation that never existed.\n */\n startNewConversation(): Promise<void>;\n /**\n * The client answered a delegated tool — mark the call done HERE, now.\n *\n * The server sends no completion frame for a delegated call (it hard-suspended\n * and closed the stream), so a transcript that only moves on frames shows\n * \"Working…\" forever after the agent has visibly moved on. The host's answer\n * funnel calls this in the same tick it posts the result. Monotonic and\n * idempotent: a call already terminal is left alone and the refusal says why.\n */\n settleToolCall(answer: ChatToolAnswer): ChatToolSettlement;\n /** Release timers and abort an in-flight turn. */\n dispose(): void;\n}\n\ntype Scheduler = (run: () => void) => () => void;\n\nfunction createScheduler(coalesceMs: number | null | undefined): Scheduler {\n if (typeof coalesceMs === \"number\") {\n return (run) => {\n const handle = setTimeout(run, coalesceMs);\n return () => clearTimeout(handle);\n };\n }\n const raf = (\n globalThis as {\n requestAnimationFrame?: (cb: () => void) => number;\n cancelAnimationFrame?: (handle: number) => void;\n }\n ).requestAnimationFrame;\n if (typeof raf === \"function\") {\n const cancel = (globalThis as { cancelAnimationFrame?: (h: number) => void })\n .cancelAnimationFrame;\n return (run) => {\n const handle = raf(run);\n return () => cancel?.(handle);\n };\n }\n return (run) => {\n let cancelled = false;\n queueMicrotask(() => {\n if (!cancelled) run();\n });\n return () => {\n cancelled = true;\n };\n };\n}\n\nconst EPHEMERAL_REASON =\n \"This conversation is not being saved: no persistence is connected, so it disappears when you leave the page.\";\nconst EPHEMERAL_REMEDY =\n \"Pass a `persistence` port (createMatrxChatPersistence from @ai-matrx/chat/matrx) to keep conversations.\";\n\nexport function createChatThread(config: ChatThreadConfig): ChatThreadStore {\n const features = resolveChatFeatures(config.features);\n const errorSink = config.errorSink ?? createConsoleErrorSink();\n const notifier = config.notifier ?? createConsoleNotifier();\n const schedule = createScheduler(config.coalesceMs);\n\n let messages: ChatMessage[] = [];\n let status: ChatThreadStatus = \"idle\";\n let conversationId: string | null = config.conversationId ?? null;\n let requestId: string | null = null;\n let phase: string | null = null;\n let error: ChatErrorPart | null = null;\n let revision = 0;\n let snapshot: ChatThreadSnapshot = buildSnapshot();\n let abort: AbortController | null = null;\n let disposed = false;\n let announcedEphemeral = false;\n\n /**\n * WHICH CONVERSATION THE TRANSCRIPT IS SHOWING, as a number.\n *\n * 🚨 The defect this closes, found by the lane-merge test that switched away\n * mid-answer: cancelling a turn aborts the CONTROLLER, but a transport whose\n * frames are already queued (or one that ignores the signal, which a host may\n * legitimately write) delivers one more. `replaceMessage` appends when it\n * cannot find the id — so that late frame resurrected the previous\n * conversation's half-answer INTO the empty transcript the reader was now\n * looking at, under the new conversation's id. A stale write must be dropped,\n * not raced. Every write in `send` is stamped with the epoch it started in,\n * and `startNewConversation` / `loadConversation` bump it.\n */\n let epoch = 0;\n\n const threadListeners = new Set<() => void>();\n const messageListeners = new Map<string, Set<() => void>>();\n const dirtyMessages = new Set<string>();\n let cancelScheduled: (() => void) | null = null;\n\n function buildSnapshot(): ChatThreadSnapshot {\n return {\n conversationId,\n messages,\n status,\n requestId,\n phase,\n ephemeral: config.persistence === undefined,\n ephemeralReason:\n config.persistence === undefined\n ? `${EPHEMERAL_REASON} ${EPHEMERAL_REMEDY}`\n : null,\n revision,\n error,\n };\n }\n\n function commit(): void {\n revision += 1;\n snapshot = buildSnapshot();\n const woken = [...dirtyMessages];\n dirtyMessages.clear();\n for (const id of woken) {\n const listeners = messageListeners.get(id);\n if (!listeners) continue;\n for (const listener of listeners) listener();\n }\n for (const listener of threadListeners) listener();\n }\n\n function markDirty(messageId: string | null): void {\n if (messageId !== null) dirtyMessages.add(messageId);\n if (cancelScheduled !== null) return;\n cancelScheduled = schedule(() => {\n cancelScheduled = null;\n if (!disposed) commit();\n });\n }\n\n /** Commit immediately — state changes a caller must observe synchronously. */\n function commitNow(messageId: string | null): void {\n if (messageId !== null) dirtyMessages.add(messageId);\n cancelScheduled?.();\n cancelScheduled = null;\n commit();\n }\n\n function announceEphemeralOnce(): void {\n if (announcedEphemeral || config.persistence !== undefined) return;\n announcedEphemeral = true;\n notifier.notify({\n level: \"warning\",\n message: EPHEMERAL_REASON,\n remedy: EPHEMERAL_REMEDY,\n });\n }\n\n function replaceMessage(next: ChatMessage): void {\n const index = messages.findIndex((m) => m.id === next.id);\n if (index === -1) {\n messages = [...messages, next];\n return;\n }\n const copy = messages.slice();\n copy[index] = next;\n messages = copy;\n }\n\n function refuseSend(part: ChatErrorPart): ChatSendResult {\n error = part;\n status = \"error\";\n errorSink.capture({ code: part.code ?? \"chat_send_refused\", message: part.message });\n notifier.notify({\n level: \"error\",\n message: part.userMessage,\n ...(part.remedy === null ? {} : { remedy: part.remedy }),\n });\n commitNow(null);\n return { accepted: false, refusal: part };\n }\n\n async function send(input: ChatSendInput): Promise<ChatSendResult> {\n if (disposed) {\n return refuseSend({\n type: \"error\",\n errorType: \"disposed\",\n code: \"thread_disposed\",\n message: \"send() was called on a disposed thread\",\n userMessage: \"This conversation is no longer active.\",\n recoverable: false,\n remedy: \"Open the conversation again.\",\n });\n }\n if (status === \"submitted\" || status === \"streaming\") {\n return refuseSend({\n type: \"error\",\n errorType: \"busy\",\n code: \"turn_in_flight\",\n message: \"a turn is already streaming on this thread\",\n userMessage: \"The assistant is still answering.\",\n recoverable: true,\n remedy: \"Wait for the answer to finish, or press stop.\",\n });\n }\n\n // THE CONFIDENTIAL-ROUTING RULE, before a single byte leaves the machine.\n const routing = decideRouting(features.routing, config.transport.locality);\n if (!routing.allowed) {\n return refuseSend({\n type: \"error\",\n errorType: \"routing_refused\",\n code: routing.code,\n message: routing.userMessage,\n userMessage: routing.userMessage,\n recoverable: false,\n remedy: routing.remedy,\n });\n }\n\n announceEphemeralOnce();\n\n const fileIds = input.fileIds ?? [];\n const userMessage = createChatMessage({\n role: \"user\",\n parts: [{ type: \"text\", text: input.text }],\n status: \"complete\",\n });\n const assistantId = newLocalId(\"msg\");\n const assistantPlaceholder = createChatMessage({\n id: assistantId,\n role: \"assistant\",\n parts: [],\n status: \"pending\",\n });\n messages = [...messages, userMessage, assistantPlaceholder];\n error = null;\n status = \"submitted\";\n commitNow(assistantId);\n\n const turnEpoch = epoch;\n /** A write from a turn whose conversation is no longer on screen. */\n const stale = (): boolean => disposed || turnEpoch !== epoch;\n\n const controller = new AbortController();\n abort = controller;\n\n const projector = createChatTurnProjector({\n features,\n messageId: assistantId,\n onRefusal: (refusal: ChatTurnRefusal) =>\n errorSink.capture({\n code: refusal.code,\n message: refusal.message,\n detail: refusal.detail,\n }),\n });\n\n try {\n const handle = await config.transport.start({\n conversationId,\n isNewConversation: conversationId === null,\n store: config.persistence !== undefined,\n text: input.text,\n fileIds,\n signal: controller.signal,\n });\n if (stale()) return { accepted: true, refusal: null };\n requestId = handle.requestId;\n if (handle.conversationId !== null) conversationId = handle.conversationId;\n status = \"streaming\";\n commitNow(assistantId);\n\n if (stale()) return { accepted: true, refusal: null };\n for await (const frame of handle.frames) {\n // The reader has moved on. Stop reading the socket rather than painting\n // the previous conversation's answer into the one now on screen.\n if (stale()) return { accepted: true, refusal: null };\n projector.apply(frame);\n if (projector.state.conversationId !== null) {\n conversationId = projector.state.conversationId;\n }\n if (projector.state.phase !== null) phase = projector.state.phase;\n const live = projector.snapshot();\n replaceMessage({\n ...live,\n metadata: {\n ...live.metadata,\n requestId,\n createdAt: assistantPlaceholder.metadata.createdAt,\n },\n });\n status =\n projector.state.outcome === \"awaiting-tools\"\n ? \"awaiting-approval\"\n : \"streaming\";\n markDirty(assistantId);\n }\n\n if (stale()) return { accepted: true, refusal: null };\n const serverUserId = projector.state.serverMessageIds.user;\n if (serverUserId !== null) {\n replaceMessage({\n ...userMessage,\n metadata: { ...userMessage.metadata, serverMessageId: serverUserId },\n });\n }\n const final = projector.snapshot();\n replaceMessage({\n ...final,\n metadata: {\n ...final.metadata,\n requestId,\n createdAt: assistantPlaceholder.metadata.createdAt,\n },\n });\n error = projector.state.error;\n status = projector.state.outcome === \"error\" ? \"error\" : \"idle\";\n commitNow(assistantId);\n return { accepted: true, refusal: null };\n } catch (cause) {\n if (stale()) return { accepted: true, refusal: null };\n const aborted =\n controller.signal.aborted ||\n (cause instanceof Error && cause.name === \"AbortError\");\n const message = cause instanceof Error ? cause.message : String(cause);\n const part: ChatErrorPart = aborted\n ? {\n type: \"error\",\n errorType: \"cancelled\",\n code: \"turn_cancelled\",\n message: \"the turn was cancelled by the reader\",\n userMessage: \"You stopped this answer.\",\n recoverable: true,\n remedy: null,\n }\n : {\n type: \"error\",\n errorType: \"transport\",\n code: \"transport_failed\",\n message,\n userMessage: \"The connection to the assistant failed.\",\n recoverable: true,\n remedy: \"Try sending the message again.\",\n };\n const partial = projector.snapshot();\n replaceMessage({\n ...partial,\n // Partial text NEVER vanishes: the reader keeps what they already saw.\n parts: [...partial.parts, part],\n status: aborted ? \"cancelled\" : \"error\",\n metadata: {\n ...partial.metadata,\n requestId,\n createdAt: assistantPlaceholder.metadata.createdAt,\n },\n });\n if (!aborted) {\n error = part;\n status = \"error\";\n errorSink.capture({ code: part.code ?? \"transport_failed\", message });\n } else {\n status = \"idle\";\n }\n commitNow(assistantId);\n return { accepted: true, refusal: null };\n } finally {\n if (abort === controller) abort = null;\n }\n }\n\n async function cancel(): Promise<void> {\n const current = requestId;\n abort?.abort();\n if (current === null) return;\n try {\n await config.transport.cancel(current);\n } catch (cause) {\n errorSink.capture({\n code: \"cancel_failed\",\n message: cause instanceof Error ? cause.message : String(cause),\n });\n notifier.notify({\n level: \"warning\",\n message: \"The assistant did not confirm that it stopped.\",\n remedy: \"It stops at its next step; anything already written is saved.\",\n });\n }\n }\n\n async function loadConversation(id: string): Promise<void> {\n const persistence = config.persistence;\n if (persistence === undefined) {\n const part: ChatErrorPart = {\n type: \"error\",\n errorType: \"no_persistence\",\n code: \"history_unavailable\",\n message: \"loadConversation() was called on a thread with no persistence port\",\n userMessage: \"Past conversations are not available here.\",\n recoverable: false,\n remedy: EPHEMERAL_REMEDY,\n };\n refuseSend(part);\n return;\n }\n epoch += 1;\n const loaded = await persistence.loadThread(id);\n conversationId = id;\n messages = loaded.slice();\n status = \"idle\";\n error = null;\n commitNow(null);\n }\n\n /**\n * A fresh conversation on THIS store. An answer still arriving is stopped\n * first — leaving it running would append the old conversation's frames to\n * the empty transcript the reader is now looking at — and the stop is\n * cooperative, so what the server has already written is still saved where it\n * belongs. Doctrine: a disconnect never cancels server work, and this method\n * promises nothing more than it does.\n */\n async function startNewConversation(): Promise<void> {\n if (disposed) return;\n epoch += 1;\n if (status === \"streaming\" || status === \"submitted\") await cancel();\n abort = null;\n conversationId = null;\n requestId = null;\n phase = null;\n messages = [];\n error = null;\n status = \"idle\";\n commitNow(null);\n }\n\n return {\n getSnapshot: () => snapshot,\n subscribe(listener) {\n threadListeners.add(listener);\n return () => {\n threadListeners.delete(listener);\n };\n },\n subscribeToMessage(messageId, listener) {\n const existing = messageListeners.get(messageId) ?? new Set<() => void>();\n existing.add(listener);\n messageListeners.set(messageId, existing);\n return () => {\n existing.delete(listener);\n if (existing.size === 0) messageListeners.delete(messageId);\n };\n },\n // Reads the COMMITTED snapshot, never the in-flight draft: a per-message\n // hook that could see an uncommitted value tears against the thread.\n getMessageSnapshot: (messageId) =>\n snapshot.messages.find((m) => m.id === messageId),\n features,\n send,\n cancel,\n setMessages(next) {\n messages = next.slice();\n commitNow(null);\n },\n settleToolCall(answer) {\n const settlement = settleToolAnswer(messages, answer);\n if (!settlement.applied) {\n if (settlement.refusal !== null) {\n errorSink.capture({\n code: \"tool_state_transition_refused\",\n message: settlement.refusal,\n detail: answer,\n });\n }\n return settlement;\n }\n messages = settlement.messages.slice();\n commitNow(settlement.messageId);\n return settlement;\n },\n loadConversation,\n canStartNewConversation: true,\n startNewConversation,\n dispose() {\n disposed = true;\n cancelScheduled?.();\n cancelScheduled = null;\n abort?.abort();\n threadListeners.clear();\n messageListeners.clear();\n },\n };\n}\n","/**\n * `<ChatProvider>` — the ONE context every lane reads. Shared scaffolding.\n *\n * It carries three things and decides nothing else:\n * 1. THE THREAD STORE — the kernel's `ChatThreadStore`, or anything else with\n * that shape, which is exactly what `@ai-matrx/chat/external-store`\n * returns. That is why the prop is typed as the store interface and not as\n * a config object: a host whose state already lives in Redux or behind an\n * MV3 relay mounts the SAME provider, and the strangler in design §9 works\n * because of this one line.\n * 2. THE REACT PORTS — `renderContent`, `notifier`, `prefs`, `files` and\n * `voice`, each with a working default (THE ALL-INCLUSIVE LAW). The\n * `renderContent` default is the REAL one — `createChatContentRenderer`,\n * markdown plus content-IR kind routing — not the plain-text floor. A host\n * that mounts nothing gets formatted answers, which is what \"drop-in\"\n * means; `createPlainTextContentRenderer` stays exported as the\n * zero-dependency floor for a host that cannot take content-ir at all.\n * 3. THE SLOTS — the per-lane slot map.\n *\n * The knobs are NOT carried here: they are resolved in the kernel and read off\n * `thread.features`, so a knob has one resolution site and a host cannot end up\n * with a composer that thinks Enter sends and a transcript that does not.\n */\n\nimport {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\n\nimport type { ChatThreadStore } from \"../store\";\nimport type { ChatNotifierPort } from \"../ports\";\nimport { createConsoleNotifier, createWebPrefs } from \"../ports\";\nimport { createChatContentRenderer } from \"./composer/content-default\";\nimport { createUnavailableFilesPort } from \"./composer/files\";\nimport type { ChatReactPortOverrides, ChatReactPorts } from \"./ports\";\nimport { humanizeToolName } from \"./ports\";\nimport type { ChatSlots } from \"./slots\";\n\n/**\n * THE RETRY BUS.\n *\n * A failed turn's \"Try again\" must hand the reader's unsent words BACK to the\n * composer — retyping a paragraph the product lost is the cheapest possible\n * insult. The transcript (2A) and the composer (2B) are different lanes and\n * different components, so the handoff is a tiny subscribable rather than a\n * prop drilled between them.\n *\n * It is also the reason this is not a plain callback: when NOTHING is listening\n * — a transcript mounted with no composer, which is a legitimate embedding —\n * the words would vanish silently. Instead the bus says so, with the remedy.\n */\nexport interface ChatRetryBus {\n /** Hand text back to the composer. Returns false when nobody took it. */\n request(text: string): boolean;\n subscribe(listener: (text: string) => void): () => void;\n}\n\nexport function createChatRetryBus(notifier: ChatNotifierPort): ChatRetryBus {\n const listeners = new Set<(text: string) => void>();\n return {\n request(text) {\n if (listeners.size === 0) {\n notifier.notify({\n level: \"warning\",\n message:\n \"There is nowhere to put your message back, so it has not been restored for you.\",\n remedy:\n \"Copy your message from the conversation above and send it again, or mount this transcript inside a chat that has a message box.\",\n });\n return false;\n }\n for (const listener of listeners) listener(text);\n return true;\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n}\n\nexport interface ChatContextValue {\n thread: ChatThreadStore;\n ports: ChatReactPorts;\n slots: ChatSlots;\n retry: ChatRetryBus;\n}\n\nconst ChatContext = createContext<ChatContextValue | null>(null);\n\nexport interface ChatProviderProps {\n /**\n * The kernel store (`createChatThread`) or a host-owned source\n * (`createExternalThreadSource`). Both satisfy this interface — that is the\n * whole point of the interface.\n */\n thread: ChatThreadStore;\n ports?: ChatReactPortOverrides | undefined;\n slots?: ChatSlots | undefined;\n children: ReactNode;\n}\n\nexport function ChatProvider(props: ChatProviderProps): ReactNode {\n const { thread, ports, slots, children } = props;\n\n // The defaults are built ONCE per provider, not per render: the plain-text\n // renderer holds an \"already announced\" flag and the prefs port probes\n // localStorage,\n // and rebuilding either per render would re-announce forever and re-probe on\n // every keystroke.\n const [defaults] = useState(() => {\n const notifier = createConsoleNotifier();\n return { notifier, prefs: createWebPrefs(notifier) };\n });\n\n const notifier = ports?.notifier ?? defaults.notifier;\n\n // The retry bus AND the default content renderer are both keyed to the\n // EFFECTIVE notifier, not to the fallback one. A host that injects a notifier\n // and then never hears \"your markdown is not being formatted\" or \"there is\n // nowhere to put your message back\" has a silent failure the injection was\n // supposed to prevent — that is the bug this keying exists to stop.\n const keyedRef = useRef<{\n notifier: ChatNotifierPort;\n bus: ChatRetryBus;\n renderContent: ChatReactPorts[\"renderContent\"];\n files: ChatReactPorts[\"files\"];\n } | null>(null);\n if (keyedRef.current === null || keyedRef.current.notifier !== notifier) {\n keyedRef.current = {\n notifier,\n bus: createChatRetryBus(notifier),\n renderContent: createChatContentRenderer({ notifier }),\n // The stand-in holds an \"already announced\" flag, so it is built ONCE:\n // rebuilding it per render would re-refuse out loud on every keystroke.\n files: createUnavailableFilesPort(notifier),\n };\n }\n const retry = keyedRef.current.bus;\n const defaultRenderContent = keyedRef.current.renderContent;\n const defaultFiles = keyedRef.current.files;\n\n const value = useMemo<ChatContextValue>(\n () => ({\n thread,\n ports: {\n renderContent: ports?.renderContent ?? defaultRenderContent,\n notifier,\n prefs: ports?.prefs ?? defaults.prefs,\n files: ports?.files ?? defaultFiles,\n // `null` is a REAL answer here (\"this surface has no voice\"), so it is\n // only defaulted when the key is absent entirely.\n voice: ports?.voice ?? null,\n toolLabel: ports?.toolLabel ?? humanizeToolName,\n // `null` is a REAL answer here too (\"this surface does not choose a\n // model\"), so it is only defaulted when the key is absent entirely —\n // and the default is nothing drawn, never a dead control.\n modelPicker: ports?.modelPicker ?? null,\n // Same rule: `null` is the real answer \"this surface offers no chips\",\n // and the control row is then ABSENT rather than an empty band.\n quickset: ports?.quickset ?? null,\n },\n slots: slots ?? {},\n retry,\n }),\n [\n thread,\n ports?.renderContent,\n ports?.prefs,\n ports?.files,\n ports?.voice,\n ports?.toolLabel,\n ports?.modelPicker,\n ports?.quickset,\n notifier,\n slots,\n retry,\n defaultRenderContent,\n defaultFiles,\n defaults.prefs,\n ],\n );\n\n // A thread the provider outlives must not keep its timers and its in-flight\n // turn running. The provider does NOT dispose a thread it did not create —\n // the host owns that lifecycle — but it does warn when a thread is swapped\n // mid-stream, because the abandoned turn keeps billing.\n const previous = useRef(thread);\n useEffect(() => {\n if (previous.current === thread) return;\n const status = previous.current.getSnapshot().status;\n if (status === \"streaming\" || status === \"submitted\") {\n notifier.notify({\n level: \"warning\",\n message: \"The previous answer was still arriving when this chat switched conversations.\",\n remedy: \"Stop the answer before switching, or the run keeps going in the background.\",\n });\n }\n previous.current = thread;\n }, [thread, notifier]);\n\n return <ChatContext.Provider value={value}>{children}</ChatContext.Provider>;\n}\n\n/**\n * The context, or a THROWN error naming what is missing. A hook that returns a\n * silent `null` here produces a blank transcript with no explanation, which is\n * the failure mode Law 4 exists to close — and the stack trace is the remedy.\n */\nexport function useChatContext(): ChatContextValue {\n const value = useContext(ChatContext);\n if (value === null) {\n throw new Error(\n \"[@ai-matrx/chat] This component must be rendered inside <ChatProvider thread={…}>. Wrap your chat surface in one, passing a thread from createChatThread() or createExternalThreadSource().\",\n );\n }\n return value;\n}\n","/**\n * THE REAL `renderContent` DEFAULT — lane 2B, design §6.\n *\n * Two routes, one port:\n * - **prose** (`markdown` / `text`) → the package's safe markdown renderer,\n * incremental per message so a 2,000-token answer does not re-parse itself\n * 2,000 times;\n * - **a render block** (a content-IR kind) → `@ai-matrx/content-ir-react`'s\n * `KindInstanceRender`, the ONE door between \"I hold a kind-shaped value\"\n * and \"the reader sees it drawn properly\". Re-deriving kind routing here\n * would be a second renderer for the platform's kinds, which is banned.\n *\n * 🚨 THE PROVIDER-LESS CASE IS HONEST, NOT FATAL. `content-ir-react` THROWS\n * outside a `<ContentIrRenderProvider>` — correctly, for an app that meant to\n * mount one. But a chat dropped into a fresh Vite app has not mounted one, and\n * a thrown error there would take the whole transcript down over a single\n * block. So this renderer asks `useContentIrHostOrNull()` and, with no host,\n * renders the block's own text under ONE sentence naming what is missing and\n * how to fix it. Absent or honest — never a blank region, never a crash.\n *\n * This replaces `createPlainTextContentRenderer` as the shipped default a host\n * gets from `<ChatProvider>`. That stand-in stays exported: it is the zero-\n * dependency floor for a host that cannot take content-ir at all.\n */\n\nimport { useEffect, type ReactNode } from \"react\";\nimport { KindInstanceRender, useContentIrHostOrNull } from \"@ai-matrx/content-ir-react\";\n\nimport type { ChatNotifierPort } from \"../../ports\";\nimport { createConsoleNotifier } from \"../../ports\";\nimport type { ChatContentRequest, ChatRenderContentPort } from \"../ports\";\nimport { ChatMarkdown, createBlockCache, type MarkdownBlockCache } from \"./markdown\";\n\n/** The sentence a reader sees for a kind block with no content-IR host. */\nexport const NO_CONTENT_IR_HOST =\n \"This answer contains a structured block, and it is shown as plain text here because this app has not connected the content renderer.\";\nexport const NO_CONTENT_IR_HOST_REMEDY =\n \"Mount <ContentIrRenderProvider host={…}> from @ai-matrx/content-ir-react above your chat to draw structured blocks properly.\";\n\nfunction RenderBlock(props: {\n request: ChatContentRequest;\n cache: MarkdownBlockCache;\n notifier: ChatNotifierPort;\n announce(): void;\n}): ReactNode {\n const host = useContentIrHostOrNull();\n const announce = props.announce;\n useEffect(() => {\n if (host === null) announce();\n }, [host, announce]);\n\n if (host === null) {\n return (\n <div className=\"mx-chat-content\" data-mx-chat-content=\"block-standin\">\n <span className=\"mx-chat-standin\">{NO_CONTENT_IR_HOST}</span>\n <ChatMarkdown\n text={props.request.text}\n cacheKey={`${props.request.messageId}:${props.request.kind}`}\n cache={props.cache}\n />\n </div>\n );\n }\n\n return (\n <div className=\"mx-chat-content\" data-mx-chat-content=\"block\" data-kind={props.request.kind}>\n <KindInstanceRender\n kind={props.request.kind}\n value={props.request.data ?? props.request.text}\n variant=\"bare\"\n />\n </div>\n );\n}\n\nexport interface ChatContentRendererOptions {\n notifier?: ChatNotifierPort | undefined;\n /** Share one cache across surfaces, or pass your own for a test. */\n cache?: MarkdownBlockCache | undefined;\n}\n\n/**\n * Build the default renderer. The block cache is per-renderer, so two chats on\n * one page never share parsed blocks and a test never inherits another test's\n * cache.\n */\nexport function createChatContentRenderer(\n options: ChatContentRendererOptions = {},\n): ChatRenderContentPort {\n const notifier = options.notifier ?? createConsoleNotifier();\n const cache = options.cache ?? createBlockCache();\n // ONCE per renderer, not per block: a notice repeated per streamed frame is\n // noise, and noise is ignored.\n let announced = false;\n const announce = (): void => {\n if (announced) return;\n announced = true;\n notifier.notify({\n level: \"warning\",\n message: NO_CONTENT_IR_HOST,\n remedy: NO_CONTENT_IR_HOST_REMEDY,\n });\n };\n\n return function renderContent(request: ChatContentRequest): ReactNode {\n if (request.kind !== \"markdown\" && request.kind !== \"text\") {\n return (\n <RenderBlock\n request={request}\n cache={cache}\n notifier={notifier}\n announce={announce}\n />\n );\n }\n if (request.text === \"\") return null;\n return (\n <div className=\"mx-chat-content\" data-mx-chat-content=\"markdown\">\n <ChatMarkdown text={request.text} cacheKey={request.messageId} cache={cache} />\n </div>\n );\n };\n}\n","/**\n * THE SMALLEST MARKDOWN RENDERER THAT IS SAFE — lane 2B.\n *\n * 🚨 Read design §6 before growing this file. `matrx-frontend`'s\n * `components/mardown-display` is 108,271 lines and renders in agent apps,\n * workflows, notes, print and the studio. Absorbing it would triple this\n * package and violate C8 on its face. This is the PORT DEFAULT — the floor that\n * makes a fresh app correct with zero work — not a competitor to that tree. A\n * host with a richer pipeline injects it through `renderContent`.\n *\n * Three properties are non-negotiable, and each has a test that goes red alone:\n *\n * 1. **NO RAW HTML, EVER.** Model output is untrusted by construction. There is\n * no `dangerouslySetInnerHTML` in this file and there must never be one: a\n * chat package that injects model text as HTML ships a cross-site-scripting\n * hole to every consumer at once. Angle brackets are rendered as literal\n * text by React, which is the whole defence.\n * 2. **LINK SCHEMES ARE ALLOWLISTED.** `javascript:`, `data:` and `vbscript:`\n * hrefs are refused and the link degrades to its own text. A renderer that\n * only escapes HTML and then hands an anchor an arbitrary scheme has closed\n * one door and left the next one open.\n * 3. **PARSING IS INCREMENTAL.** A 2,000-token answer arrives as hundreds of\n * frames. Re-parsing the whole transcript per token is the classic failure\n * and the reason chats jank (design §7 rule 3). `createBlockCache()` keeps\n * the completed blocks of a growing string and re-parses ONLY the trailing\n * incomplete block.\n *\n * Reasoning is NOT handled here. The kernel already splits the platform's\n * `<reasoning>` span into its own part and the transcript draws it as an aside —\n * a markdown renderer that printed it as prose would undo that.\n */\n\nimport { Fragment, type ReactNode } from \"react\";\nimport { findTableEnd, rowCells, tableStartsAt, unescapeCellPipes } from \"@ai-matrx/content-ir/source\";\n\n/* ------------------------------------------------------------------ links -- */\n\n/**\n * The only schemes an anchor may carry. Everything else degrades to plain text.\n * `data:` is absent on purpose: `data:text/html` is a same-origin script vector.\n */\nconst SAFE_SCHEME = /^(https?:|mailto:|tel:)/i;\n/** A relative or anchor-local target a host page legitimately owns. */\nconst RELATIVE_TARGET = /^([./#?]|[A-Za-z0-9_-]+\\/)/;\n\nexport function safeHref(raw: string): string | null {\n const href = raw.trim();\n if (href === \"\") return null;\n if (SAFE_SCHEME.test(href)) return href;\n // A bare `foo:bar` with an unknown scheme is refused; a path is allowed.\n if (/^[A-Za-z][A-Za-z0-9+.-]*:/.test(href)) return null;\n if (RELATIVE_TARGET.test(href)) return href;\n return null;\n}\n\n/** Images load only over http(s), lazily, and never leak the referrer. */\nfunction safeImageSource(raw: string): string | null {\n const source = raw.trim();\n return /^https?:\\/\\//i.test(source) ? source : null;\n}\n\n/* ----------------------------------------------------------------- inline -- */\n\n/**\n * ONE pass, ordered by precedence: code span wins over everything (its body is\n * literal), then image, link, bold, italic, strikethrough, then a bare URL.\n *\n * Each alternative is a separate capture group so the branch is decided by\n * WHICH group matched, never by re-testing the slice — re-testing is how an\n * inline parser ends up quadratic on a long answer.\n */\n/**\n * 🚨 EVERY BODY IS BOUNDED, AND NONE OF THEM CROSSES A NEWLINE.\n *\n * This is not tidiness. The first version used `[\\s\\S]*?` bodies, and the\n * FIRST run against the live 9,313-character corpus answer took the test\n * runner out of memory: an unbounded lazy body matches across thousands of\n * characters, `renderInline` recurses into that whole span, and the span\n * matches again two characters shorter — thousands of levels deep, each\n * allocating its own node array. Bounding the body to one line and\n * {@link INLINE_BODY_MAX} characters is what makes the pass linear, and it is\n * also what real markdown means: emphasis does not span a blank line.\n */\nconst INLINE_BODY_MAX = 400;\n\n/**\n * 🚨 THE SOURCE, not a shared RegExp object. `renderInline` RECURSES (bold\n * inside a link, code inside a list item), and a `g`-flagged regex carries\n * `lastIndex` on the OBJECT: an inner call resets it to 0, the outer loop\n * resumes from 0, and the scan never terminates. That is not theory — it is\n * what the first version did, and it took the runner out of memory on the very\n * first live-corpus answer. A fresh matcher per call is the fix, and this\n * comment is the reason nobody \"optimises\" it back.\n */\nconst INLINE_SOURCE = [\n \"(`+)([^\\\\n]{0,400}?)\\\\1\", // 1,2 code span\n \"!\\\\[([^\\\\]\\\\n]*)\\\\]\\\\(((?:[^()\\\\s]|\\\\([^()\\\\s]*\\\\))+)\\\\)\", // 3,4 image\n \"\\\\[([^\\\\]\\\\n]+)\\\\]\\\\(((?:[^()\\\\s]|\\\\([^()\\\\s]*\\\\))+)\\\\)\", // 5,6 link\n // 🚨 The closing backreference names THIS alternative's group (\\\\7, \\\\9,\n // \\\\11). A bare \\\\1 in a joined pattern points at the CODE SPAN's group,\n // which is unmatched here and so matches empty: \"**Bold**\" rendered as\n // <strong>B</strong>old<em>*</em> (found on a live coding-sessions answer).\n \"(\\\\*\\\\*|__)(?=\\\\S)([^\\\\n]{0,400}?\\\\S)\\\\7\", // 7,8 bold\n \"(\\\\*|_)(?=\\\\S)([^*_\\\\n]{0,400}?\\\\S)\\\\9\", // 9,10 italic\n \"(~~)(?=\\\\S)([^\\\\n]{0,400}?\\\\S)\\\\11\", // 11,12 strikethrough\n \"(https?://[^\\\\s<>()]+)\", // 13 bare url\n].join(\"|\");\n\nexport function renderInline(\n text: string,\n keyPrefix: string,\n depth = 0,\n): ReactNode[] {\n // A hard floor under the recursion. Bounded bodies already make a runaway\n // impossible; this makes it impossible to REINTRODUCE one by loosening a\n // pattern, which is exactly how the first OOM happened.\n if (depth > 6 || text.length > INLINE_BODY_MAX * 64) return [text];\n const nodes: ReactNode[] = [];\n const matcher = new RegExp(INLINE_SOURCE, \"g\");\n let cursor = 0;\n let index = 0;\n let match = matcher.exec(text);\n while (match !== null) {\n if (match.index > cursor) nodes.push(text.slice(cursor, match.index));\n const key = `${keyPrefix}i${index}`;\n index += 1;\n\n if (match[1] !== undefined) {\n nodes.push(<code key={key}>{match[2] ?? \"\"}</code>);\n } else if (match[4] !== undefined) {\n const source = safeImageSource(match[4]);\n const alt = match[3] ?? \"\";\n nodes.push(\n source === null ? (\n <Fragment key={key}>{alt === \"\" ? match[4] : alt}</Fragment>\n ) : (\n <img\n key={key}\n className=\"mx-chat-md__image\"\n src={source}\n alt={alt}\n loading=\"lazy\"\n referrerPolicy=\"no-referrer\"\n />\n ),\n );\n } else if (match[6] !== undefined) {\n const href = safeHref(match[6]);\n const label = match[5] ?? \"\";\n nodes.push(\n href === null ? (\n // Law 4: the words survive; only the unsafe jump is refused.\n <Fragment key={key}>{label}</Fragment>\n ) : (\n <a key={key} href={href} target=\"_blank\" rel=\"noreferrer noopener\">\n {renderInline(label, `${key}-`, depth + 1)}\n </a>\n ),\n );\n } else if (match[8] !== undefined) {\n nodes.push(<strong key={key}>{renderInline(match[8], `${key}-`, depth + 1)}</strong>);\n } else if (match[10] !== undefined) {\n nodes.push(<em key={key}>{renderInline(match[10], `${key}-`, depth + 1)}</em>);\n } else if (match[12] !== undefined) {\n nodes.push(<del key={key}>{renderInline(match[12], `${key}-`, depth + 1)}</del>);\n } else if (match[13] !== undefined) {\n nodes.push(\n <a key={key} href={match[13]} target=\"_blank\" rel=\"noreferrer noopener\">\n {match[13]}\n </a>,\n );\n }\n cursor = match.index + match[0].length;\n match = matcher.exec(text);\n }\n if (cursor < text.length) nodes.push(text.slice(cursor));\n return nodes;\n}\n\n/* ------------------------------------------------------------------ blocks -- */\n\nexport type MarkdownBlock =\n | { type: \"code\"; language: string; body: string; closed: boolean }\n | { type: \"heading\"; level: number; body: string }\n | { type: \"quote\"; body: string }\n | { type: \"list\"; ordered: boolean; items: string[] }\n | { type: \"table\"; header: string[]; rows: string[][] }\n | { type: \"rule\" }\n | { type: \"paragraph\"; body: string };\n\nconst FENCE_LINE = /^\\s{0,3}(`{3,}|~{3,})\\s*([^\\s`]*)/;\nconst HEADING_LINE = /^(#{1,6})\\s+(.*)$/;\nconst BULLET_LINE = /^\\s{0,3}[-*+]\\s+(.*)$/;\nconst ORDERED_LINE = /^\\s{0,3}\\d{1,9}[.)]\\s+(.*)$/;\nconst QUOTE_LINE = /^\\s{0,3}>\\s?(.*)$/;\nconst RULE_LINE = /^\\s{0,3}([-*_])(\\s*\\1){2,}\\s*$/;\n/** A row's cells by THE GFM rule (@ai-matrx/content-ir/source): `\\|` stays in its cell and reads as `|`. */\nfunction tableCells(line: string): string[] {\n return rowCells(line).map(unescapeCellPipes);\n}\n\n/**\n * Parse markdown blocks from `lines`, returning the blocks AND the index of the\n * first line of the trailing block that may still grow. The cache uses that\n * index to re-parse only what is still arriving.\n */\nexport function parseBlocks(source: string): {\n blocks: MarkdownBlock[];\n /** CHARACTER offset of the last block's first line — where a growing string\n * may still change what has already been parsed. */\n openFrom: number;\n} {\n const lines = source.split(\"\\n\");\n // Character offset of each line's first character, so the cache can slice the\n // source rather than re-joining lines (re-joining loses a trailing newline\n // and silently shifts every later offset by one).\n const lineStart: number[] = [];\n {\n let at = 0;\n for (const line of lines) {\n lineStart.push(at);\n at += line.length + 1;\n }\n }\n const blocks: MarkdownBlock[] = [];\n let index = 0;\n let lastStart = 0;\n\n while (index < lines.length) {\n const line = lines[index] ?? \"\";\n\n if (line.trim() === \"\") {\n index += 1;\n continue;\n }\n // Marked AFTER the blank-line skip: a trailing blank line is not a block\n // start, and treating it as one makes the incremental cache believe the\n // whole answer is already stable and silently stop appending.\n lastStart = index;\n\n const fence = FENCE_LINE.exec(line);\n if (fence !== null) {\n const marker = fence[1] ?? \"```\";\n const language = fence[2] ?? \"\";\n const body: string[] = [];\n index += 1;\n let closed = false;\n while (index < lines.length) {\n const current = lines[index] ?? \"\";\n if (current.trimStart().startsWith(marker[0] ?? \"`\") && current.trim().length >= 3) {\n const close = FENCE_LINE.exec(current);\n if (close !== null && (close[1] ?? \"\").length >= marker.length) {\n closed = true;\n index += 1;\n break;\n }\n }\n body.push(current);\n index += 1;\n }\n // An UNCLOSED fence still renders as code. A half-arrived fence rendered\n // as prose reflows the instant it closes, and a jumping transcript is\n // what \"incremental\" means to a reader.\n blocks.push({ type: \"code\", language, body: body.join(\"\\n\"), closed });\n continue;\n }\n\n const heading = HEADING_LINE.exec(line);\n if (heading !== null) {\n blocks.push({\n type: \"heading\",\n level: (heading[1] ?? \"#\").length,\n body: heading[2] ?? \"\",\n });\n index += 1;\n continue;\n }\n\n if (RULE_LINE.test(line)) {\n blocks.push({ type: \"rule\" });\n index += 1;\n continue;\n }\n\n if (QUOTE_LINE.test(line)) {\n const body: string[] = [];\n while (index < lines.length) {\n const quote = QUOTE_LINE.exec(lines[index] ?? \"\");\n if (quote === null) break;\n body.push(quote[1] ?? \"\");\n index += 1;\n }\n blocks.push({ type: \"quote\", body: body.join(\"\\n\") });\n continue;\n }\n\n const bullet = BULLET_LINE.exec(line);\n const ordered = ORDERED_LINE.exec(line);\n if (bullet !== null || ordered !== null) {\n const isOrdered = ordered !== null;\n const items: string[] = [];\n while (index < lines.length) {\n const current = lines[index] ?? \"\";\n const match = isOrdered\n ? ORDERED_LINE.exec(current)\n : BULLET_LINE.exec(current);\n if (match === null) break;\n items.push(match[1] ?? \"\");\n index += 1;\n }\n blocks.push({ type: \"list\", ordered: isOrdered, items });\n continue;\n }\n\n // A table by THE GFM rule (verify-RC-B4): edge pipes optional, a line that\n // starts another block ends it, rows read at the header's width.\n if (tableStartsAt(lines, index)) {\n const header = tableCells(line);\n const end = findTableEnd(lines, index);\n index += 2;\n const rows: string[][] = [];\n while (index < end) {\n const cells = tableCells(lines[index] ?? \"\");\n rows.push(header.map((_h, k) => cells[k] ?? \"\"));\n index += 1;\n }\n blocks.push({ type: \"table\", header, rows });\n continue;\n }\n\n const paragraph: string[] = [];\n while (index < lines.length) {\n const current = lines[index] ?? \"\";\n if (\n current.trim() === \"\" ||\n FENCE_LINE.test(current) ||\n HEADING_LINE.test(current) ||\n BULLET_LINE.test(current) ||\n ORDERED_LINE.test(current) ||\n QUOTE_LINE.test(current) ||\n RULE_LINE.test(current)\n ) {\n break;\n }\n paragraph.push(current);\n index += 1;\n }\n blocks.push({ type: \"paragraph\", body: paragraph.join(\"\\n\") });\n }\n\n return { blocks, openFrom: lineStart[lastStart] ?? 0 };\n}\n\n/* ------------------------------------------------------- incremental cache -- */\n\nexport interface MarkdownBlockCache {\n parse(key: string, text: string): MarkdownBlock[];\n /** How many times the parser actually ran. The performance rule, measurable. */\n readonly parses: number;\n forget(key: string): void;\n}\n\n/**\n * THE INCREMENTAL RULE, as code.\n *\n * When the next text for a key is the previous text plus more (which is what a\n * stream is), everything before the last block start is reused verbatim and\n * only the tail is re-parsed. A renderer that re-parses a 40 KB answer on every\n * one of 2,000 frames is the reason chats jank at 2,000 tokens.\n */\nexport function createBlockCache(): MarkdownBlockCache {\n interface Entry {\n text: string;\n blocks: MarkdownBlock[];\n /** Blocks before `stableEnd`, which a longer string can never change. */\n stable: MarkdownBlock[];\n /** Character offset where the still-growing block begins. */\n stableEnd: number;\n }\n const entries = new Map<string, Entry>();\n let parses = 0;\n\n const cold = (key: string, text: string): MarkdownBlock[] => {\n parses += 1;\n const parsed = parseBlocks(text);\n entries.set(key, {\n text,\n blocks: parsed.blocks,\n stable: parsed.blocks.slice(0, Math.max(0, parsed.blocks.length - 1)),\n stableEnd: parsed.openFrom,\n });\n return parsed.blocks;\n };\n\n return {\n get parses() {\n return parses;\n },\n forget(key) {\n entries.delete(key);\n },\n parse(key, text) {\n const previous = entries.get(key);\n if (previous === undefined) return cold(key, text);\n if (previous.text === text) return previous.blocks;\n // Only a CONTINUATION can reuse anything. An edit, a regenerate or a\n // second message under the same key re-parses from scratch — reusing a\n // prefix that is no longer a prefix is how a renderer shows the previous\n // answer's first paragraph forever.\n if (!text.startsWith(previous.text)) return cold(key, text);\n\n parses += 1;\n const tail = parseBlocks(text.slice(previous.stableEnd));\n const blocks = [...previous.stable, ...tail.blocks];\n entries.set(key, {\n text,\n blocks,\n stable: blocks.slice(0, Math.max(0, blocks.length - 1)),\n stableEnd: previous.stableEnd + tail.openFrom,\n });\n return blocks;\n },\n };\n}\n\n/* ---------------------------------------------------------------- render -- */\n\nfunction renderBlock(block: MarkdownBlock, key: string): ReactNode {\n switch (block.type) {\n case \"code\":\n return (\n <pre key={key} className=\"mx-chat-md__pre\" data-language={block.language || undefined}>\n <code>{block.body}</code>\n </pre>\n );\n case \"heading\": {\n const level = Math.min(6, Math.max(1, block.level));\n const Tag = `h${level}` as \"h1\";\n return (\n <Tag key={key} className=\"mx-chat-md__heading\" data-level={level}>\n {renderInline(block.body, `${key}-`)}\n </Tag>\n );\n }\n case \"quote\":\n return (\n <blockquote key={key} className=\"mx-chat-md__quote\">\n {renderInline(block.body, `${key}-`)}\n </blockquote>\n );\n case \"list\":\n return block.ordered ? (\n <ol key={key} className=\"mx-chat-md__list\">\n {block.items.map((item, i) => (\n <li key={`${key}-${i}`}>{renderInline(item, `${key}-${i}-`)}</li>\n ))}\n </ol>\n ) : (\n <ul key={key} className=\"mx-chat-md__list\">\n {block.items.map((item, i) => (\n <li key={`${key}-${i}`}>{renderInline(item, `${key}-${i}-`)}</li>\n ))}\n </ul>\n );\n case \"table\":\n return (\n <div key={key} className=\"mx-chat-md__table-scroll\">\n <table className=\"mx-chat-md__table\">\n <thead>\n <tr>\n {block.header.map((cell, i) => (\n <th key={`${key}-h${i}`}>{renderInline(cell, `${key}-h${i}-`)}</th>\n ))}\n </tr>\n </thead>\n <tbody>\n {block.rows.map((row, r) => (\n <tr key={`${key}-r${r}`}>\n {row.map((cell, c) => (\n <td key={`${key}-r${r}c${c}`}>{renderInline(cell, `${key}-r${r}c${c}-`)}</td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n case \"rule\":\n return <hr key={key} className=\"mx-chat-md__rule\" />;\n case \"paragraph\":\n return (\n <p key={key} className=\"mx-chat-md__paragraph\">\n {renderInline(block.body, `${key}-`)}\n </p>\n );\n }\n}\n\nexport interface ChatMarkdownProps {\n text: string;\n /** Cache key — the message id. Two messages never share parsed blocks. */\n cacheKey: string;\n cache: MarkdownBlockCache;\n}\n\nexport function ChatMarkdown(props: ChatMarkdownProps): ReactNode {\n const blocks = props.cache.parse(props.cacheKey, props.text);\n return (\n <div className=\"mx-chat-md\">\n {blocks.map((block, index) => renderBlock(block, `b${index}`))}\n </div>\n );\n}\n","/**\n * THE `files` PORT — lane 2B, design §5.\n *\n * Upload / resolve / preview, injected by the host. The platform default is\n * `@ai-matrx/data/files` + `@ai-matrx/media` in a later lane; what ships TODAY\n * is the honest stand-in below.\n *\n * 🚨 LAW 4, applied precisely. When no host has wired attachments up, the\n * composer does not render an attach button that does nothing — the control is\n * ABSENT. But a reader can still paste a screenshot or drop a PDF onto the box\n * without ever seeing a button, and THAT path must not swallow the file: the\n * stand-in port refuses out loud, with the remedy, and the composer shows the\n * refusal where the chip would have been. Absent or honest; never dead.\n */\n\nimport { formatFileSize } from \"@ai-matrx/kit/format\";\n\nimport type { ChatNotifierPort } from \"../../ports\";\n\n/** What an upload produced. `fileId` is the durable identity a turn carries. */\nexport interface ChatUploadedFile {\n fileId: string;\n name: string;\n mediaType: string | null;\n sizeBytes: number;\n /** A preview URL, when the host can produce one. A signed URL is a handoff,\n * never an identity — the kernel only ever sends `fileId`. */\n previewUrl?: string | undefined;\n}\n\nexport interface ChatUploadOptions {\n /** Aborted when the reader removes the chip while it is still uploading. */\n signal: AbortSignal;\n /** 0..1. A port that cannot measure progress simply never calls this, and\n * the chip shows an indeterminate state rather than a fake percentage. */\n onProgress(fraction: number): void;\n}\n\nexport interface ChatFilesPort {\n /**\n * FALSE when this port cannot actually store anything. The composer reads it\n * to decide whether an attach button exists at all — a button wired to a port\n * that always refuses is the \"dead control\" Law 4 forbids.\n */\n readonly available: boolean;\n /** `accept` for the file input, when the host wants to narrow it. */\n readonly accept?: string | undefined;\n upload(file: File, options: ChatUploadOptions): Promise<ChatUploadedFile>;\n}\n\n/** The sentence a reader sees when nothing is wired up. Exported so the test\n * asserts the SAME string the screen shows, never a paraphrase. */\nexport const ATTACHMENTS_UNAVAILABLE =\n \"Attachments aren't set up here, so this file has not been sent.\";\nexport const ATTACHMENTS_UNAVAILABLE_REMEDY =\n \"Give <ChatComposerProvider> a `files` port (your upload service) to attach files to a message.\";\n\n/**\n * The shipped default: it refuses, loudly, with the remedy — and it says so\n * ONCE per port so a reader who drops five files gets one sentence, not five.\n */\nexport function createUnavailableFilesPort(\n notifier: ChatNotifierPort,\n): ChatFilesPort {\n let announced = false;\n return {\n available: false,\n upload(): Promise<ChatUploadedFile> {\n if (!announced) {\n announced = true;\n notifier.notify({\n level: \"warning\",\n message: ATTACHMENTS_UNAVAILABLE,\n remedy: ATTACHMENTS_UNAVAILABLE_REMEDY,\n });\n }\n return Promise.reject(new Error(ATTACHMENTS_UNAVAILABLE));\n },\n };\n}\n\n\nexport function tooLargeSentence(name: string, limitBytes: number): string {\n return `${name} is larger than the ${formatFileSize(limitBytes)} limit for this chat, so it has not been attached.`;\n}\n","/**\n * THE REACT-LAYER PORTS — shared scaffolding, owned by no single lane.\n *\n * The kernel's ports (`src/ports.ts`) are framework-free by construction: a\n * transport, a persistence binding and a prefs store have no React in them.\n * These three DO, because their return value is pixels or their default reads\n * the DOM, so they live here rather than polluting layer 1.\n *\n * Per THE ALL-INCLUSIVE LAW every port here ships a working default. A host\n * that injects nothing still gets a correct, honest screen.\n *\n * 🚨 ADDING A PORT: a port that nothing in the package CALLS is untested theory\n * that rots. `nav`, `shell`, `renderModelRef` and `capabilities` are named in\n * the design and are deliberately ABSENT until the lane that calls them lands —\n * the same rule phase 1 followed.\n *\n * `files` and `voice` USED to live in lane 2B's own `<ChatComposerProvider>`\n * context, because the lane contract forbade 2B editing this shared file while\n * 2A and 2C were in flight. Design §5 shows ONE port map, and at the lane merge\n * that is what they became: both are fields here, defaulted once by\n * `<ChatProvider>`. `<ChatComposerProvider>` survives as a scoped OVERRIDE — a\n * host with two composers on one page (a docked chat and a floating one) can\n * still give them different upload services — and it now inherits these\n * defaults instead of building a second set.\n */\n\nimport { humanizeIdentifier } from \"@ai-matrx/kit/text-case\";\nimport type { ReactNode } from \"react\";\n\nimport type { ChatRole } from \"../model\";\nimport type { ChatNotifierPort, ChatPrefsPort } from \"../ports\";\nimport type { ChatFilesPort } from \"./composer/files\";\nimport type { ChatQuicksetPort } from \"./plugins/quickset\";\nimport type { ChatVoicePort } from \"./composer/voice-port\";\n\n/**\n * One piece of content to turn into pixels.\n *\n * `kind` is the discriminator: `\"markdown\"` for ordinary prose, or the\n * content-IR kind name for a `render-block` part. A renderer that does not know\n * a kind must render the text honestly rather than nothing — a blank region is\n * the silent failure Law 4 forbids.\n */\nexport interface ChatContentRequest {\n kind: string;\n /** The text to render. Always present; empty string for a data-only block. */\n text: string;\n /** A render block's structured payload, when it carried one. */\n data?: unknown;\n /** True while the producing turn is still streaming — renderers must be\n * incremental and must never reflow the whole transcript per token. */\n streaming: boolean;\n role: ChatRole;\n messageId: string;\n}\n\n/**\n * THE `renderContent` PORT (design §6 — the one deliberate scope cut).\n *\n * `components/mardown-display` is 108,271 lines rendering in agent apps,\n * workflows, notes, print and the studio. Absorbing it would triple this\n * package and violate C8 on its face, so it is a SEAM with a working default\n * and a named retirement path, not an absorption.\n *\n * The default this package ships is the minimal SAFE one (plain text with\n * fenced code preserved, never HTML injection). The rich default — content-IR\n * kind routing plus a real markdown renderer — is lane 2b's, and a host's own\n * pipeline is what it injects during its migration.\n */\nexport type ChatRenderContentPort = (request: ChatContentRequest) => ReactNode;\n\n/**\n * THE `toolLabel` PORT — what a tool call is CALLED in front of a reader.\n *\n * A tool's name is a machine identifier (`workflow_catalog`, `web_search`). A\n * Creator watching an agent spend their money is owed a sentence, not a symbol:\n * the live review of 2026-09-18 read \"workflow_catalog Done Done\" at eye level\n * in the Conductor. The host that OWNS the tools owns their words, so this is a\n * port — and because a port with no default is a hole, the package ships a\n * humanizer (`humanizeToolName`) rather than the raw identifier. A host whose\n * tools have real names injects them; one that does not at least never shows\n * `snake_case` to a person.\n *\n * The machine name is not lost: it stays on the chip as `data-tool`, which is\n * what tests, the census and Developer Mode read.\n */\nexport type ChatToolLabelPort = (toolName: string) => string;\n\n/** What the model picker is handed on every render. */\nexport interface ChatModelPickerContext {\n className: string;\n}\n\n/**\n * THE `modelPicker` PORT (design §5.1 capability 4) — phase 4.\n *\n * `ModelListDropdown` has NOT moved into `@ai-matrx/agents/catalog/react` yet,\n * and until it does this package cannot render a model list without inventing\n * a roster — which would be the fifth picker on the platform, the exact thing\n * the picker ruling forbids. So the host renders its own here, the package\n * positions it, and `null` means the surface does not choose a model and NO\n * control is drawn: absent, never dead (Law 4). When the follow-on lane moves\n * the roster into the agents package, the shipped default becomes that one\n * component and this port keeps its shape.\n */\nexport type ChatModelPickerPort = (context: ChatModelPickerContext) => ReactNode;\n\n/**\n * The shipped default: the identifier through THE humanizer\n * (`@ai-matrx/kit/text-case` `humanizeIdentifier`): `workflow_catalog` →\n * \"Workflow Catalog\". An empty name becomes \"Working…\", because a chip with no\n * name at all is the bare spinner the transcript refuses to draw.\n */\nexport function humanizeToolName(toolName: string): string {\n return humanizeIdentifier(toolName) || \"Working…\";\n}\n\n/** The React ports a `<ChatProvider>` accepts. Every one has a default. */\nexport interface ChatReactPorts {\n renderContent: ChatRenderContentPort;\n notifier: ChatNotifierPort;\n prefs: ChatPrefsPort;\n /**\n * Upload / resolve / preview. The shipped default REFUSES out loud with the\n * remedy, and the composer renders no attach button at all — absent, never\n * dead (Law 4).\n */\n files: ChatFilesPort;\n /**\n * Dictation and read-aloud. `null` means the surface has no voice, and NO\n * microphone is rendered — a greyed-out one is the dead control Law 4\n * forbids. `createBrowserAudioVoicePort()` is the platform implementation.\n */\n voice: ChatVoicePort | null;\n /** What a tool call is called in front of a reader. Defaults to\n * `humanizeToolName` — never the raw identifier. */\n toolLabel: ChatToolLabelPort;\n /**\n * The model picker, rendered by the host. `null` means this surface does not\n * choose a model and nothing is drawn — see `ChatModelPickerPort`.\n */\n modelPicker: ChatModelPickerPort | null;\n /**\n * The prompt chips a host offers above the box. `null` means it offers\n * none, and NO row is drawn — an empty band above the composer is dead\n * pixels. A chip fills the box; it never sends (see `ChatQuicksetPort`).\n */\n quickset: ChatQuicksetPort | null;\n}\n\nexport type ChatReactPortOverrides = Partial<ChatReactPorts>;\n","/**\n * THE BASE HOOKS — shared scaffolding, layer 3 of the design.\n *\n * All four read the kernel through `useSyncExternalStore`, which is the shape\n * the champion (`@ai-sdk/react`) proved and the reason the store was written as\n * `getSnapshot`/`subscribe` in phase 1. Nothing here holds a second copy of\n * thread state: a hook that mirrors the store into `useState` tears under\n * concurrent rendering and drifts the moment the host mutates the store from\n * outside React.\n *\n * `useChatMessage(id)` is the performance seam that makes a 2,000-token answer\n * possible: it subscribes to ONE message, so a streaming bubble re-renders\n * while the other 200 rows do not.\n */\n\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n type KeyboardEvent as ReactKeyboardEvent,\n} from \"react\";\n\nimport type { ChatFeatures } from \"../config\";\nimport type { ChatMessage, ChatThreadSnapshot } from \"../model\";\nimport { messageText } from \"../model\";\nimport type { ChatSendResult } from \"../store\";\nimport { useChatContext } from \"./provider\";\nimport type { ChatReactPorts } from \"./ports\";\nimport type { ChatSlots } from \"./slots\";\n\nexport interface UseChatThreadResult {\n snapshot: ChatThreadSnapshot;\n messages: readonly ChatMessage[];\n status: ChatThreadSnapshot[\"status\"];\n /** True while a turn is in flight — the ONE test a stop button uses. */\n isBusy: boolean;\n conversationId: string | null;\n ephemeral: boolean;\n ephemeralReason: string | null;\n error: ChatThreadSnapshot[\"error\"];\n send(text: string, fileIds?: readonly string[]): Promise<ChatSendResult>;\n cancel(): Promise<void>;\n}\n\n/** The whole thread. Use `useChatMessage` for a row — not this. */\nexport function useChatThread(): UseChatThreadResult {\n const { thread } = useChatContext();\n const snapshot = useSyncExternalStore(\n thread.subscribe,\n thread.getSnapshot,\n thread.getSnapshot,\n );\n\n const send = useCallback(\n (text: string, fileIds?: readonly string[]) =>\n thread.send(fileIds === undefined ? { text } : { text, fileIds }),\n [thread],\n );\n const cancel = useCallback(() => thread.cancel(), [thread]);\n\n return useMemo(\n () => ({\n snapshot,\n messages: snapshot.messages,\n status: snapshot.status,\n isBusy: snapshot.status === \"submitted\" || snapshot.status === \"streaming\",\n conversationId: snapshot.conversationId,\n ephemeral: snapshot.ephemeral,\n ephemeralReason: snapshot.ephemeralReason,\n error: snapshot.error,\n send,\n cancel,\n }),\n [snapshot, send, cancel],\n );\n}\n\n/**\n * ONE message, on its own subscription.\n *\n * Returns `undefined` for an id the thread does not hold — a row that was\n * removed while its component was unmounting is ordinary, not an error.\n */\nexport function useChatMessage(messageId: string): ChatMessage | undefined {\n const { thread } = useChatContext();\n\n const subscribe = useCallback(\n (listener: () => void) => thread.subscribeToMessage(messageId, listener),\n [thread, messageId],\n );\n const getSnapshot = useCallback(\n () => thread.getMessageSnapshot(messageId),\n [thread, messageId],\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/** The resolved knobs (platform → org → surface → user), from the kernel. */\nexport function useChatConfig(): ChatFeatures {\n return useChatContext().thread.features;\n}\n\n/** The React ports, each already defaulted by the provider. */\nexport function useChatPorts(): ChatReactPorts {\n return useChatContext().ports;\n}\n\n/** The slot map. Empty object when the host passed none. */\nexport function useChatSlots(): ChatSlots {\n return useChatContext().slots;\n}\n\nexport interface UseChatComposerStateResult {\n text: string;\n setText(next: string): void;\n /** False when the box is blank, or while a turn is in flight unless\n * `features.sendWhileBusy` (the host queues). */\n canSend: boolean;\n isBusy: boolean;\n send(): Promise<ChatSendResult | null>;\n stop(): Promise<void>;\n /**\n * The keyboard contract, decided by `features.sendKey`: Enter sends and\n * Shift+Enter newlines, or Cmd/Ctrl+Enter sends and Enter newlines. Esc\n * ALWAYS stops a running answer. Wire this to the input's `onKeyDown`.\n */\n onKeyDown(event: ReactKeyboardEvent): void;\n /** True when a failed turn just handed its words back. Clears on the next\n * edit, so a composer can flash the box once without it sticking. */\n restoredFromFailure: boolean;\n}\n\nconst DRAFT_PREFIX = \"ai-matrx.chat.draft.\";\n\n/**\n * COMPOSER STATE — the logic half, with no opinion about pixels.\n *\n * Lane 2B renders this. It lives in shared scaffolding rather than in 2B\n * because 2A needs one half of it TODAY: a failed turn's \"Try again\" hands the\n * reader's unsent words back through the retry bus, and there has to be exactly\n * one place that catches them. Two sources of composer state is how a draft\n * gets lost.\n *\n * Drafts survive reload when `features.retainDrafts` is on, keyed per\n * conversation, through the `prefs` port — which already announces itself when\n * it is memory-only.\n */\nexport function useChatComposerState(): UseChatComposerStateResult {\n const { thread, ports, retry } = useChatContext();\n const features = thread.features;\n const snapshot = useSyncExternalStore(\n thread.subscribe,\n thread.getSnapshot,\n thread.getSnapshot,\n );\n const conversationId = snapshot.conversationId;\n const isBusy = snapshot.status === \"submitted\" || snapshot.status === \"streaming\";\n\n const draftKey = `${DRAFT_PREFIX}${conversationId ?? \"new\"}`;\n const [text, setTextState] = useState<string>(() =>\n features.retainDrafts ? (ports.prefs.get(draftKey) ?? \"\") : \"\",\n );\n const [restoredFromFailure, setRestored] = useState(false);\n\n // The draft key changes when the conversation does — including the moment a\n // NEW conversation learns its server id mid-send. Carrying the in-progress\n // text across that rename is the difference between a draft that survives and\n // one that vanishes the first time someone types during the first turn.\n const keyRef = useRef(draftKey);\n useEffect(() => {\n if (keyRef.current === draftKey) return;\n const previousKey = keyRef.current;\n keyRef.current = draftKey;\n if (!features.retainDrafts) return;\n const carried = ports.prefs.get(previousKey);\n if (carried !== null && carried !== \"\") {\n ports.prefs.set(draftKey, carried);\n ports.prefs.remove(previousKey);\n setTextState(carried);\n return;\n }\n setTextState(ports.prefs.get(draftKey) ?? \"\");\n }, [draftKey, features.retainDrafts, ports.prefs]);\n\n const setText = useCallback(\n (next: string) => {\n setTextState(next);\n setRestored(false);\n if (!features.retainDrafts) return;\n if (next === \"\") ports.prefs.remove(keyRef.current);\n else ports.prefs.set(keyRef.current, next);\n },\n [features.retainDrafts, ports.prefs],\n );\n\n // Catch the words a failed turn hands back. Registered here and NOWHERE else,\n // so \"Try again\" has exactly one destination.\n useEffect(\n () =>\n retry.subscribe((restored) => {\n setTextState(restored);\n setRestored(true);\n if (features.retainDrafts) ports.prefs.set(keyRef.current, restored);\n }),\n [retry, features.retainDrafts, ports.prefs],\n );\n\n const send = useCallback(async (): Promise<ChatSendResult | null> => {\n const body = text.trim();\n if (body === \"\" || (isBusy && !features.sendWhileBusy)) return null;\n // The box is cleared OPTIMISTICALLY and the draft with it; if the kernel\n // refuses the send, the words come straight back rather than being lost.\n setTextState(\"\");\n setRestored(false);\n if (features.retainDrafts) ports.prefs.remove(keyRef.current);\n const result = await thread.send({ text: body });\n if (!result.accepted) {\n setTextState(body);\n if (features.retainDrafts) ports.prefs.set(keyRef.current, body);\n }\n return result;\n }, [text, isBusy, thread, features.retainDrafts, features.sendWhileBusy, ports.prefs]);\n\n const stop = useCallback(() => thread.cancel(), [thread]);\n\n const onKeyDown = useCallback(\n (event: ReactKeyboardEvent) => {\n if (event.key === \"Escape\") {\n if (!isBusy) return;\n event.preventDefault();\n void thread.cancel();\n return;\n }\n if (event.key !== \"Enter\") return;\n const withModifier = event.metaKey || event.ctrlKey;\n const sends =\n features.sendKey === \"enter\"\n ? !event.shiftKey && !withModifier\n : withModifier;\n if (!sends) return;\n event.preventDefault();\n void send();\n },\n [features.sendKey, isBusy, send, thread],\n );\n\n return {\n text,\n setText,\n canSend: text.trim() !== \"\" && (!isBusy || features.sendWhileBusy),\n isBusy,\n send,\n stop,\n onKeyDown,\n restoredFromFailure,\n };\n}\n\n/**\n * The text of the user turn that produced a given assistant message — what\n * \"Try again\" hands back. Exported because 2B's composer and 2A's failure card\n * must agree on what \"the unsent text\" means.\n */\nexport function precedingUserText(\n messages: readonly ChatMessage[],\n messageId: string,\n): string {\n const index = messages.findIndex((message) => message.id === messageId);\n if (index <= 0) return \"\";\n for (let cursor = index - 1; cursor >= 0; cursor -= 1) {\n const candidate = messages[cursor];\n if (candidate && candidate.role === \"user\") return messageText(candidate);\n }\n return \"\";\n}\n","/**\n * THE MINIMAL SAFE `renderContent` DEFAULT — and it announces its own limits.\n *\n * This renderer does exactly three things: it splits blank-line-separated\n * paragraphs, it preserves fenced code blocks as `<pre>`, and it never, under\n * any circumstance, puts model output into `dangerouslySetInnerHTML`. Model\n * text is untrusted by construction; a chat package that injects it as HTML\n * ships a cross-site-scripting hole to every consumer at once.\n *\n * It is a STAND-IN, and Law 4 binds stand-ins: the first time it is handed text\n * carrying markdown it cannot render — a heading, a table, a link, emphasis —\n * it says so ONCE, with the remedy. A reader looking at raw `**bold**` on a\n * screen while the package pretends everything is fine is exactly the class\n * that law exists to close.\n *\n * 🚨 THIS IS NO LONGER THE SHIPPED DEFAULT. `createChatContentRenderer`\n * (`./composer/content-default`) is — real markdown plus `@ai-matrx/content-ir-react`\n * kind routing. This one stays exported as the ZERO-DEPENDENCY FLOOR: a host\n * that cannot take content-ir at all passes it as `renderContent` and gets safe\n * plain text that says out loud what it is not formatting.\n */\n\nimport type { ReactNode } from \"react\";\n\nimport type { ChatNotifierPort } from \"../ports\";\nimport { createConsoleNotifier } from \"../ports\";\nimport type { ChatContentRequest, ChatRenderContentPort } from \"./ports\";\nimport { findTableStart } from \"@ai-matrx/content-ir/source\";\n\nconst FENCE = /```/;\n\n/**\n * Markdown this renderer would print raw. Deliberately narrow: an apostrophe or\n * an underscore inside a word must not trip the announcement, or the notice\n * becomes noise and a reader learns to ignore it.\n */\nconst UNRENDERED_MARKDOWN = [\n /^#{1,6}\\s+\\S/m, // headings\n /\\*\\*[^\\s*][^*]*\\*\\*/, // bold\n /\\[[^\\]]+\\]\\([^)\\s]+\\)/, // links\n /^\\s*[-*+]\\s+\\S/m, // bullets\n { test: (text: string) => findTableStart(text.split(\"\\n\")) !== -1 }, // tables, by THE GFM rule (content-ir/source)\n];\n\nfunction splitFencedBlocks(text: string): { code: boolean; body: string }[] {\n if (!FENCE.test(text)) return [{ code: false, body: text }];\n const segments: { code: boolean; body: string }[] = [];\n // An ODD number of fences means the last block is still arriving. It is\n // rendered as code anyway: a half-arrived fence rendered as prose reflows the\n // instant it closes, and a jumping transcript is what \"incremental\" means to\n // a reader.\n const pieces = text.split(\"```\");\n for (let index = 0; index < pieces.length; index += 1) {\n const body = pieces[index] ?? \"\";\n if (body === \"\") continue;\n segments.push({ code: index % 2 === 1, body });\n }\n return segments;\n}\n\nfunction renderProse(body: string, keyPrefix: string): ReactNode[] {\n const paragraphs = body.split(/\\n{2,}/);\n const nodes: ReactNode[] = [];\n for (let index = 0; index < paragraphs.length; index += 1) {\n const paragraph = paragraphs[index] ?? \"\";\n if (paragraph.trim() === \"\") continue;\n nodes.push(<p key={`${keyPrefix}-p${index}`}>{paragraph}</p>);\n }\n return nodes;\n}\n\n/**\n * Build the default renderer. The announcement is per-renderer, not per-module,\n * so two chats on one page do not share a \"you have been told\" flag and a test\n * never inherits another test's silence.\n */\nexport function createPlainTextContentRenderer(\n notifier: ChatNotifierPort = createConsoleNotifier(),\n): ChatRenderContentPort {\n let announced = false;\n\n const announceOnce = (): void => {\n if (announced) return;\n announced = true;\n notifier.notify({\n level: \"warning\",\n message:\n \"This chat is showing the assistant's answer as plain text, so markdown such as headings, links, bold and tables appears exactly as written instead of being formatted.\",\n remedy:\n \"Give <ChatProvider> a `renderContent` port (your own markdown/kind renderer) to format answers.\",\n });\n };\n\n return function renderContent(request: ChatContentRequest): ReactNode {\n const text = request.text;\n if (text === \"\") return null;\n\n // A render block is structured content this floor cannot lay out. It is\n // shown as its text with an honest label rather than dropped.\n if (request.kind !== \"markdown\" && request.kind !== \"text\") {\n return (\n <div className=\"mx-chat-content\" data-mx-chat-content=\"block\">\n <span className=\"mx-chat-standin\">\n {`This answer contains a ${request.kind} block. It is shown as text here until a content renderer is connected.`}\n </span>\n {renderProse(text, \"block\")}\n </div>\n );\n }\n\n for (const pattern of UNRENDERED_MARKDOWN) {\n if (pattern.test(text)) {\n announceOnce();\n break;\n }\n }\n\n const segments = splitFencedBlocks(text);\n return (\n <div className=\"mx-chat-content\" data-mx-chat-content=\"plain\">\n {segments.map((segment, index) =>\n segment.code ? (\n <pre key={`c${index}`}>\n <code>{segment.body.replace(/^[^\\n]*\\n/, \"\")}</code>\n </pre>\n ) : (\n renderProse(segment.body, `s${index}`)\n ),\n )}\n </div>\n );\n };\n}\n","/**\n * PACKAGE-OWNED GLYPHS. Two of them, inline, sized in `em`, `currentColor`.\n *\n * Deliberately NOT a dependency on `@ai-matrx/icons`: two 200-byte paths do not\n * justify an edge in the sibling graph, and an icon that inherits colour and\n * size from its button is exactly what the re-skin contract needs. Lanes that\n * need a real icon set (the composer's attach/mic/send) take the dependency\n * then, at their own call site.\n */\n\nimport type { ReactNode } from \"react\";\n\nexport function ChevronRightGlyph(): ReactNode {\n return (\n <svg\n className=\"mx-chat-chevron\"\n width=\"1em\"\n height=\"1em\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <path d=\"M6 3.5 10.5 8 6 12.5\" />\n </svg>\n );\n}\n\nexport function ArrowDownGlyph(): ReactNode {\n return (\n <svg\n width=\"1em\"\n height=\"1em\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <path d=\"M8 3v10M3.5 8.5 8 13l4.5-4.5\" />\n </svg>\n );\n}\n","/**\n * THE MESSAGE LIST — the transcript's viewport, ordering, windowing,\n * auto-scroll and its accessibility contract.\n *\n * WINDOWING IS VIRTUAL-FRIENDLY, NOT SILENTLY VIRTUAL. Above\n * `virtualizeAbove` messages the list renders the tail and puts an EXPLICIT\n * \"Show earlier messages\" boundary at the top, rather than mounting and\n * unmounting rows under the reader as they scroll. That is a deliberate choice\n * from design §7 rule 4: silent windowing breaks in-page find (⌘F stops finding\n * text that is in the conversation), breaks scroll anchoring, and breaks\n * copy-all. The rows are keyed and memoized per id and each subscribes to its\n * own message, so the expensive half of virtualization — not re-rendering\n * everything on every token — is already paid. A host that truly needs row\n * recycling at 10,000 messages wraps this list's children; it does not have to\n * fork it.\n *\n * THE ACCESSIBILITY CONTRACT, which is a shipped feature here and not host\n * homework:\n * - the transcript is `role=\"log\"` with `aria-live=\"polite\"` and\n * `aria-relevant=\"additions\"`. `additions` is the load-bearing word: a\n * screen reader announces NEW nodes and ignores text mutating inside an\n * existing node, so a streaming answer does not read out character by\n * character. Token-level live regions are what make an AI chat unusable\n * with a screen reader.\n * - a separate polite status region publishes the streaming answer at\n * SENTENCE boundaries, so a blind reader hears the answer as it arrives in\n * usable units instead of either a flood or silence.\n * - each turn is an `article` with an accessible name.\n */\n\nimport { useEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\n\nimport { TapTargetButton } from \"@ai-matrx/tap-target\";\n\nimport type { ChatMessage } from \"../../model\";\nimport { messageText } from \"../../model\";\nimport { ArrowDownGlyph } from \"../icons\";\nimport { useChatConfig, useChatSlots, useChatThread } from \"../hooks\";\nimport { useStickToBottom } from \"./auto-scroll\";\nimport { ChatMessageTurn } from \"./message-turn\";\n\nexport interface ChatMessageListProps {\n className?: string | undefined;\n /** Render a window with an explicit earlier-boundary above this count. */\n virtualizeAbove?: number | undefined;\n /** How many more turns \"Show earlier messages\" reveals. */\n pageSize?: number | undefined;\n /** Accessible name for the transcript region. */\n label?: string | undefined;\n}\n\nconst DEFAULT_WINDOW = 80;\nconst DEFAULT_PAGE = 40;\n\nexport function ChatMessageList(props: ChatMessageListProps): ReactNode {\n const virtualizeAbove = props.virtualizeAbove ?? DEFAULT_WINDOW;\n const pageSize = props.pageSize ?? DEFAULT_PAGE;\n const label = props.label ?? \"Conversation\";\n\n const thread = useChatThread();\n const features = useChatConfig();\n const slots = useChatSlots();\n const messages = thread.messages;\n\n const [revealed, setRevealed] = useState(virtualizeAbove);\n const visible = useMemo<readonly ChatMessage[]>(() => {\n if (messages.length <= revealed) return messages;\n return messages.slice(messages.length - revealed);\n }, [messages, revealed]);\n const hiddenEarlier = messages.length - visible.length;\n\n const stick = useStickToBottom(features.autoScroll);\n const { onContentChange } = stick;\n\n // The content-change signal is the thread's own revision counter: it bumps on\n // every applied change and on nothing else, so this fires exactly as often as\n // the store commits — which is once per coalesced frame, not once per token.\n const revision = thread.snapshot.revision;\n useEffect(() => {\n onContentChange();\n }, [revision, onContentChange]);\n\n const announcement = useSentenceAnnouncement(messages, thread.conversationId);\n\n return (\n <div className={joinClass(\"mx-chat__viewport\", props.className)}>\n {thread.ephemeral && thread.ephemeralReason !== null ? (\n <p className=\"mx-chat__ephemeral\" role=\"note\">\n {thread.ephemeralReason}\n </p>\n ) : null}\n\n <div\n className=\"mx-chat__scroll\"\n ref={stick.ref}\n role=\"log\"\n aria-live=\"polite\"\n aria-relevant=\"additions\"\n aria-label={label}\n data-pinned={stick.pinned}\n tabIndex={0}\n >\n {slots.transcriptHeader ?? null}\n\n {messages.length === 0 ? (\n renderEmpty(slots.empty)\n ) : (\n <ol className=\"mx-chat__messages\">\n {hiddenEarlier > 0 ? (\n <li className=\"mx-chat__earlier\">\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n onClick={() => setRevealed((count) => count + pageSize)}\n >\n {`Show earlier messages (${hiddenEarlier} hidden)`}\n </button>\n </li>\n ) : null}\n {visible.map((message, index) => {\n // THE TURN RHYTHM. A turn that continues the SAME speaker is a\n // paragraph, not a new turn: the assistant has no bubble, so two\n // of its answers in a row separated by the full inter-turn gap\n // read as one reply with a hole in it. The neighbour check lives\n // HERE because the turn itself subscribes to one message and must\n // not learn about its siblings (that subscription is what keeps a\n // long transcript from re-rendering on every token). The first\n // visible row never continues anything — above it is the\n // \"Show earlier messages\" boundary, not the previous speaker.\n const continued =\n index > 0 && visible[index - 1]?.role === message.role;\n return (\n <li key={message.id} {...(continued ? { \"data-continued\": \"true\" } : {})}>\n <ChatMessageTurn messageId={message.id} continued={continued} />\n </li>\n );\n })}\n </ol>\n )}\n\n {slots.transcriptFooter ?? null}\n </div>\n\n {/* The sentence-boundary announcer. Off-screen, never focusable, and\n deliberately OUTSIDE the log so the two regions cannot double-read. */}\n <p className=\"mx-chat__sr-only\" role=\"status\" aria-live=\"polite\">\n {announcement}\n </p>\n\n {stick.hasUnseenBelow ? (\n <div className=\"mx-chat__jump\">\n <TapTargetButton\n icon={<ArrowDownGlyph />}\n ariaLabel=\"Jump to the latest message\"\n tooltip={false}\n onClick={stick.jumpToLatest}\n />\n </div>\n ) : null}\n </div>\n );\n}\n\nfunction joinClass(base: string, extra: string | undefined): string {\n return extra === undefined || extra === \"\" ? base : `${base} ${extra}`;\n}\n\nfunction renderEmpty(slot: ReactNode | (() => ReactNode) | undefined): ReactNode {\n if (typeof slot === \"function\") return slot();\n if (slot !== undefined) return slot;\n return <ChatEmptyState />;\n}\n\n/** The shipped empty state. Replaceable through the `empty` slot. */\nexport function ChatEmptyState(props: {\n title?: string | undefined;\n body?: string | undefined;\n}): ReactNode {\n return (\n <div className=\"mx-chat-empty\">\n <span className=\"mx-chat-empty__title\">{props.title ?? \"Start the conversation\"}</span>\n <span>{props.body ?? \"Ask a question, describe what you need, or paste something in.\"}</span>\n </div>\n );\n}\n\n/**\n * Publish the streaming answer to a screen reader at SENTENCE boundaries.\n *\n * Only the completed sentences are ever published, so the announcement changes\n * a handful of times per answer instead of once per token, and a reader hears\n * whole thoughts. When the turn finishes, anything after the last terminator is\n * released too — otherwise an answer ending without punctuation is never read.\n */\nimport { speakableText } from \"../plain-text\";\n\n// Re-exported where it has always been, so no consumer import breaks.\nexport { speakableText };\n\nconst TERMINATORS = /[.!?。!?][\"')\\]]?\\s/g;\n\n/**\n * The live region belongs to the conversation ON SCREEN.\n *\n * Independent live review, 2026-09-18: after switching conversations the\n * region still held the whole finished reply of the conversation the reader\n * had LEFT — a screen reader that caught up late read out an answer from\n * another transcript as if it had just arrived. Switching clears the region\n * and marks the newly loaded transcript as already-heard, so what is announced\n * from then on is only what this conversation says NEXT: loaded history is\n * something the reader can go and read, never something that just happened.\n */\nexport function useSentenceAnnouncement(\n messages: readonly ChatMessage[],\n conversationId: string | null,\n): string {\n const [announced, setAnnounced] = useState(\"\");\n const publishedRef = useRef(0);\n\n const last = messages[messages.length - 1];\n const isAssistant = last !== undefined && last.role === \"assistant\";\n const text = isAssistant ? speakableText(messageText(last)) : \"\";\n const done = isAssistant && last.status !== \"streaming\" && last.status !== \"pending\";\n const messageId = last?.id ?? \"\";\n\n const idRef = useRef(messageId);\n if (idRef.current !== messageId) {\n idRef.current = messageId;\n publishedRef.current = 0;\n }\n\n const conversationRef = useRef(conversationId);\n if (conversationRef.current !== conversationId) {\n // An unsaved thread ADOPTING the id the server just named it is not a\n // switch — the words being announced are the ones still arriving. Only a\n // move away from a conversation that already had an identity is.\n const switched = conversationRef.current !== null;\n conversationRef.current = conversationId;\n if (switched) {\n idRef.current = messageId;\n publishedRef.current = text.length;\n if (announced !== \"\") setAnnounced(\"\");\n }\n }\n\n useEffect(() => {\n if (!isAssistant) return;\n TERMINATORS.lastIndex = 0;\n let boundary = 0;\n let match: RegExpExecArray | null = TERMINATORS.exec(text);\n while (match !== null) {\n boundary = match.index + match[0].length;\n match = TERMINATORS.exec(text);\n }\n const upTo = done ? text.length : boundary;\n if (upTo <= publishedRef.current) return;\n const next = text.slice(publishedRef.current, upTo).trim();\n publishedRef.current = upTo;\n if (next !== \"\") setAnnounced(next);\n }, [text, done, isAssistant]);\n\n return announced;\n}\n","/**\n * STICK TO BOTTOM — by READER INTENT, never by content (design §7 rule 5).\n *\n * The rule the champions share and the one everyone gets wrong: the transcript\n * follows new content only while the reader is AT the bottom. The moment they\n * scroll up they are reading, and yanking them back down mid-sentence is the\n * single most hated behaviour a chat can have. Pinning is released on that\n * scroll and restored when they return to the bottom themselves — or when they\n * press the \"jump to latest\" affordance, which is the only thing allowed to\n * scroll on their behalf.\n *\n * Two details that are load-bearing and easy to lose:\n * - the release must distinguish the READER's scroll from OUR scroll, or the\n * programmatic scroll that follows a token releases the pin it just used;\n * - `atBottom` needs a tolerance. Sub-pixel layout, a growing caret and\n * fractional device pixels mean an exact equality test reports \"not at the\n * bottom\" on a transcript that visibly is.\n */\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\n/** Pixels from the true bottom that still count as \"at the bottom\". */\nexport const STICK_TOLERANCE_PX = 48;\n\nexport interface StickToBottom {\n /** Attach to the scrolling element. */\n ref: (node: HTMLElement | null) => void;\n /** True while the transcript is following new content. */\n pinned: boolean;\n /** True when there is newer content the reader has not scrolled to. */\n hasUnseenBelow: boolean;\n /** Scroll to the bottom and re-pin. The \"jump to latest\" action. */\n jumpToLatest(): void;\n /** Call whenever the transcript's content changed. */\n onContentChange(): void;\n}\n\nexport function useStickToBottom(enabled: boolean): StickToBottom {\n const elementRef = useRef<HTMLElement | null>(null);\n const pinnedRef = useRef(true);\n const programmaticRef = useRef(false);\n const [pinned, setPinned] = useState(true);\n const [hasUnseenBelow, setHasUnseen] = useState(false);\n\n const isAtBottom = useCallback((node: HTMLElement): boolean => {\n const distance = node.scrollHeight - node.scrollTop - node.clientHeight;\n return distance <= STICK_TOLERANCE_PX;\n }, []);\n\n const scrollToBottom = useCallback((node: HTMLElement): void => {\n // Mark OUR scroll so the scroll handler it fires does not read as the\n // reader changing their mind.\n programmaticRef.current = true;\n node.scrollTop = node.scrollHeight;\n }, []);\n\n const handleScroll = useCallback((): void => {\n const node = elementRef.current;\n if (node === null) return;\n if (programmaticRef.current) {\n programmaticRef.current = false;\n return;\n }\n const bottom = isAtBottom(node);\n pinnedRef.current = bottom;\n setPinned(bottom);\n if (bottom) setHasUnseen(false);\n }, [isAtBottom]);\n\n const ref = useCallback(\n (node: HTMLElement | null) => {\n const previous = elementRef.current;\n if (previous !== null) previous.removeEventListener(\"scroll\", handleScroll);\n elementRef.current = node;\n if (node !== null) node.addEventListener(\"scroll\", handleScroll, { passive: true });\n },\n [handleScroll],\n );\n\n useEffect(\n () => () => {\n const node = elementRef.current;\n if (node !== null) node.removeEventListener(\"scroll\", handleScroll);\n },\n [handleScroll],\n );\n\n const onContentChange = useCallback((): void => {\n const node = elementRef.current;\n if (node === null) return;\n if (!enabled) return;\n if (pinnedRef.current) {\n scrollToBottom(node);\n return;\n }\n // Not pinned: the reader is up the page and something new arrived below.\n setHasUnseen(true);\n }, [enabled, scrollToBottom]);\n\n const jumpToLatest = useCallback((): void => {\n const node = elementRef.current;\n if (node === null) return;\n scrollToBottom(node);\n pinnedRef.current = true;\n setPinned(true);\n setHasUnseen(false);\n }, [scrollToBottom]);\n\n return { ref, pinned, hasUnseenBelow, jumpToLatest, onContentChange };\n}\n","/**\n * ONE TURN. Memoized, and subscribed to ONE message.\n *\n * This component is the reason a 2,000-token answer does not jank: it reads\n * `useChatMessage(id)`, which wakes on that message alone, and it is wrapped in\n * `memo` so the other 200 rows in a long transcript do not re-render when a\n * token lands. De-memoize it and the streaming budget in design §7 goes red —\n * there is a falsifiability leg that proves exactly that.\n */\n\nimport { memo, type ReactNode } from \"react\";\n\nimport type { ChatMessage, ChatPart } from \"../../model\";\nimport { messageText } from \"../../model\";\nimport { precedingUserText, useChatConfig, useChatMessage, useChatPorts, useChatSlots } from \"../hooks\";\nimport { useChatContext } from \"../provider\";\nimport { ChatMessageActions } from \"../plugins/message-actions\";\nimport {\n ChatAskUserPartView,\n ChatFailurePartView,\n ChatFilePartView,\n ChatNoticePartView,\n ChatReasoningAside,\n ChatRenderBlockPartView,\n ChatSourcePartView,\n ChatTextPartView,\n ChatToolPartView,\n} from \"./parts\";\n\nexport interface ChatMessageTurnProps {\n messageId: string;\n /**\n * This turn continues the SAME speaker as the row above it. Decided by the\n * list (which is the only thing that can see a turn's neighbours) and carried\n * on the article as `data-continued` so a host can style the turn itself, not\n * only its row.\n */\n continued?: boolean | undefined;\n}\n\nexport const ChatMessageTurn = memo(function ChatMessageTurn(\n props: ChatMessageTurnProps,\n): ReactNode {\n const message = useChatMessage(props.messageId);\n const features = useChatConfig();\n const { renderContent } = useChatPorts();\n const slots = useChatSlots();\n const { thread, retry } = useChatContext();\n\n if (message === undefined) return null;\n\n const streaming = message.status === \"streaming\" || message.status === \"pending\";\n const role = message.role;\n\n if (role === \"user\") {\n return (\n <article\n className=\"mx-chat-turn\"\n data-role=\"user\"\n {...(props.continued ? { \"data-continued\": \"true\" } : {})}\n data-message-id={message.id}\n aria-label=\"Your message\"\n >\n <div className=\"mx-chat-turn__bubble\">\n {renderContent({\n kind: \"markdown\",\n text: messageText(message),\n streaming: false,\n role,\n messageId: message.id,\n })}\n </div>\n {renderMeta(message, slots.renderTurnMeta)}\n {features.messageActions ? <ChatMessageActions message={message} /> : null}\n </article>\n );\n }\n\n const retryText = precedingUserText(thread.getSnapshot().messages, message.id);\n\n return (\n <article\n className=\"mx-chat-turn\"\n data-role={role}\n {...(props.continued ? { \"data-continued\": \"true\" } : {})}\n data-status={message.status}\n data-message-id={message.id}\n aria-label=\"Assistant message\"\n aria-busy={streaming || undefined}\n >\n <div className=\"mx-chat-turn__body\">\n {message.parts.map((part, index) => {\n const override = slots.renderPart?.(part, { message, index, streaming });\n if (override !== undefined) {\n return <div key={keyFor(part, index)}>{override}</div>;\n }\n return (\n <div key={keyFor(part, index)}>\n {renderPart(part, {\n message,\n streaming,\n renderContent,\n expandTools: features.expandToolCardsByDefault,\n showReasoning: features.showReasoningByDefault,\n retryText,\n onRetry: (text: string) => retry.request(text),\n })}\n </div>\n );\n })}\n\n {/* A turn that has been accepted but has said nothing yet still has to\n look ALIVE. A blank region while the server thinks is how a reader\n concludes the product is broken. */}\n {streaming && message.parts.length === 0 ? (\n <span className=\"mx-chat-caret\" data-mx-chat=\"waiting\" aria-hidden=\"true\" />\n ) : null}\n\n {/* A REPLY THAT WAS STOPPED SAYS SO, HERE, FOR GOOD. A toast outside\n the panel is not a marker: it disappears, and what is left is a\n half-sentence the transcript presents as the answer (live review,\n 2026-09-18). Quiet, never a card — the words the reader already saw\n stay exactly as they were. It is skipped when the turn already\n carries the cancelled card, because the kernel's own stop appends\n one and two markers for one event is noise. */}\n {showStoppedMarker(message) ? (\n <p className=\"mx-chat-turn__stopped\" role=\"note\">\n Stopped\n </p>\n ) : null}\n </div>\n {renderMeta(message, slots.renderTurnMeta)}\n {features.messageActions ? <ChatMessageActions message={message} /> : null}\n </article>\n );\n});\n\nfunction showStoppedMarker(message: ChatMessage): boolean {\n if (message.status !== \"cancelled\") return false;\n return !message.parts.some(\n (part) => part.type === \"error\" && part.errorType === \"cancelled\",\n );\n}\n\nfunction keyFor(part: ChatPart, index: number): string {\n if (part.type === \"tool\") return `tool:${part.toolCallId}`;\n if (part.type === \"render-block\") return `block:${part.blockId}`;\n return `${part.type}:${index}`;\n}\n\nfunction renderMeta(\n message: ChatMessage,\n slot: ((message: ChatMessage) => ReactNode | undefined) | undefined,\n): ReactNode {\n const override = slot?.(message);\n if (override !== undefined) return override;\n // The model name, when the server reported one. NEVER a UUID and never a\n // guess: `renderModelRef` is a later lane's port, and until it exists the\n // honest thing is to print what the completion actually named, or nothing.\n if (message.metadata.modelRef === null) return null;\n return <div className=\"mx-chat-turn__meta\">{message.metadata.modelRef}</div>;\n}\n\ninterface PartRenderContext {\n message: ChatMessage;\n streaming: boolean;\n renderContent: ReturnType<typeof useChatPorts>[\"renderContent\"];\n expandTools: boolean;\n showReasoning: boolean;\n retryText: string;\n onRetry(text: string): void;\n}\n\nfunction renderPart(part: ChatPart, context: PartRenderContext): ReactNode {\n switch (part.type) {\n case \"text\":\n return (\n <ChatTextPartView\n part={part}\n renderContent={context.renderContent}\n streaming={context.streaming}\n role={context.message.role}\n messageId={context.message.id}\n />\n );\n case \"reasoning\":\n return <ChatReasoningAside part={part} defaultOpen={context.showReasoning} />;\n case \"tool\":\n return <ChatToolPartView part={part} defaultExpanded={context.expandTools} />;\n case \"notice\":\n return <ChatNoticePartView part={part} />;\n case \"error\":\n return (\n <ChatFailurePartView\n part={part}\n retryText={context.retryText}\n onRetry={context.onRetry}\n />\n );\n case \"render-block\":\n return (\n <ChatRenderBlockPartView\n part={part}\n renderContent={context.renderContent}\n messageId={context.message.id}\n role={context.message.role}\n />\n );\n case \"file\":\n return <ChatFilePartView part={part} />;\n case \"source\":\n return <ChatSourcePartView part={part} />;\n case \"ask-user\":\n return <ChatAskUserPartView part={part} />;\n default: {\n // An exhaustiveness check that FAILS THE BUILD when a part type is added\n // to the model and nobody taught the transcript to draw it. A silent\n // `return null` here is how a part type ships invisible.\n const unreachable: never = part;\n return unreachable;\n }\n }\n}\n","/**\n * PHASE 4, CAPABILITY 12 — MESSAGE ACTIONS.\n *\n * The bar under a finished turn: copy it, run it again, put your own words\n * back in the box to change them, and whatever the HOST can offer that only it\n * can name (a hand-off into a step, \"go deeper\", an export).\n *\n * THE RULES THIS FILE IS WRITTEN AGAINST, each of them a real failure:\n *\n * 1. **No icon without a label.** Every action is a real `<button>` whose\n * visible content is its words. A row of unlabelled glyphs under an answer\n * is a guessing game, and it is the first thing a keyboard or screen-reader\n * user loses.\n * 2. **Keyboard reachable, in reading order.** The bar is a plain element with\n * buttons in DOM order inside a named group, so Tab reaches them where the\n * eye expects and a screen reader says which turn they belong to. No\n * `tabIndex`, no roving focus, no hover-only reveal: an action that only\n * exists under a mouse pointer does not exist.\n * 3. **ABSENT, never dead.** \"Run it again\" is missing when there is nothing\n * to run again; \"Edit\" is missing on an empty message; the whole bar is\n * missing while the turn is still arriving. Nothing here is ever a greyed\n * control with no sentence (Law 4).\n * 4. **Nothing fails silently.** A browser with no clipboard permission is\n * TOLD about, with the remedy, through the notifier — the same sink every\n * other refusal in this package uses.\n *\n * WHERE THE WORDS GO. \"Edit and send again\" hands the message back through THE\n * RETRY BUS — the one destination the composer already listens on — rather\n * than inventing a second path into the box. If nothing is listening the bus\n * says so itself, which is exactly why it is a bus.\n */\n\nimport { copyText } from \"@ai-matrx/kit/clipboard\";\nimport { useCallback, useEffect, useRef, useState, type ReactNode } from \"react\";\n\nimport type { ChatMessage } from \"../../model\";\nimport { messageText } from \"../../model\";\nimport { precedingUserText, useChatPorts, useChatSlots } from \"../hooks\";\nimport { useChatContext } from \"../provider\";\nimport { speakableText } from \"../plain-text\";\n\nexport const CLIPBOARD_REFUSED = \"Your message could not be copied.\";\nexport const CLIPBOARD_REFUSED_REMEDY =\n \"Select the text and copy it with your keyboard — this browser did not allow the page to use the clipboard.\";\n\n/** How long the bar says \"Copied\" before going back to its own words. */\nconst COPIED_MS = 2000;\n\nexport interface ChatMessageActionsProps {\n message: ChatMessage;\n}\n\n/**\n * The action bar for ONE turn.\n *\n * Rendered by `<ChatMessageTurn>` when `features.messageActions` is on (the\n * platform default), and exported so a host composing its own turn can mount\n * it directly.\n */\nexport function ChatMessageActions(props: ChatMessageActionsProps): ReactNode {\n const { message } = props;\n const { notifier } = useChatPorts();\n const slots = useChatSlots();\n const { thread, retry } = useChatContext();\n const [copied, setCopied] = useState<\"text\" | \"markdown\" | null>(null);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(\n () => () => {\n if (timer.current !== null) clearTimeout(timer.current);\n },\n [],\n );\n\n const copy = useCallback(\n (value: string, which: \"text\" | \"markdown\") => {\n // THE one clipboard door (kit `copyText`): async write, textarea fallback, never throws.\n // \"Copied\" shows only when the words landed; a refusal says so once, through the host.\n void copyText(value, {\n failureMessage: CLIPBOARD_REFUSED,\n notify: (message, kind) => {\n if (kind === \"error\") notifier.notify({ level: \"warning\", message, remedy: CLIPBOARD_REFUSED_REMEDY });\n },\n }).then((copiedOk) => {\n if (!copiedOk) return;\n setCopied(which);\n if (timer.current !== null) clearTimeout(timer.current);\n timer.current = setTimeout(() => setCopied(null), COPIED_MS);\n });\n },\n [notifier],\n );\n\n // A turn still arriving has no finished words to copy and nothing to run\n // again — the bar appears when the turn is over, not before.\n if (message.status === \"streaming\" || message.status === \"pending\") return null;\n\n const raw = messageText(message);\n const host = slots.messageActions?.(message);\n const isUser = message.role === \"user\";\n // What \"run it again\" would send: the words that produced THIS answer.\n const again = isUser ? \"\" : precedingUserText(thread.getSnapshot().messages, message.id);\n\n if (raw === \"\" && host === undefined) return null;\n\n return (\n <div\n className=\"mx-chat-actions\"\n role=\"group\"\n aria-label={isUser ? \"Actions for your message\" : \"Actions for this answer\"}\n data-message-id={message.id}\n >\n {raw === \"\" ? null : (\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n data-action=\"copy-text\"\n onClick={() => copy(speakableText(raw), \"text\")}\n >\n {copied === \"text\" ? \"Copied\" : \"Copy\"}\n </button>\n )}\n\n {/* Two copies, because they are two different things a reader wants: the\n words to paste into a message, and the markdown to paste into a\n document. The assistant's answer is the only one that carries\n formatting worth keeping, so the user's own turn offers one. */}\n {raw === \"\" || isUser ? null : (\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n data-action=\"copy-markdown\"\n onClick={() => copy(raw, \"markdown\")}\n >\n {copied === \"markdown\" ? \"Copied\" : \"Copy as markdown\"}\n </button>\n )}\n\n {again === \"\" ? null : (\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n data-action=\"retry\"\n onClick={() => void thread.send({ text: again })}\n >\n Ask again\n </button>\n )}\n\n {isUser && raw !== \"\" ? (\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n data-action=\"edit\"\n onClick={() => retry.request(raw)}\n >\n Edit and send again\n </button>\n ) : null}\n\n {host}\n\n {/* The confirmation is SAID, not only shown: a sighted reader sees the\n button's word change, and everyone else hears this. It exists ONLY\n while there is something to say — an always-present empty live region\n under every turn is a second `role=\"status\"` in the transcript, and\n the transcript already owns the one that announces answers. */}\n {copied === null ? null : (\n <span className=\"mx-chat__sr-only\" role=\"status\" aria-live=\"polite\">\n Copied to the clipboard\n </span>\n )}\n </div>\n );\n}\n","/**\n * PLAIN WORDS — SHARED, because two lanes need the same stripping.\n *\n * It lives here rather than in the transcript because phase 4's \"Copy\" hands a\n * reader the same prose the screen reader hears, and a plugin importing it out\n * of `transcript/message-list.tsx` made the two files a CYCLE: the list\n * imports the turn, the turn imports the action bar, the action bar imported\n * the list. Under that cycle the live region came up EMPTY and four transcript\n * tests went red — which is exactly the kind of quiet breakage a cycle causes.\n */\n\nimport { findCodeRanges } from \"@ai-matrx/content-ir/source\";\n\n/**\n * WHAT A SCREEN READER HEARS — AND WHAT \"COPY\" PUTS ON THE CLIPBOARD — IS\n * PROSE, NOT SYNTAX.\n *\n * The transcript renders markdown through `renderContent`; this region carries\n * the SAME answer as text, so it carried the asterisks, hashes and URLs with it\n * — a reader heard 'star star Summarize Page Text star star' (live review,\n * 2026-09-18). This strips the marks and keeps the words, including the link's\n * text and the code span's content, which are part of the sentence.\n */\nexport function speakableText(text: string): string {\n // Code first, by THE ONE CommonMark code-range rule (@ai-matrx/content-ir\n // /source): a fence is heard as \"code block\", a code span keeps its words.\n // A naive /```…```/ here paired a mid-sentence \"(in a ```python fence)\"\n // with the next real fence and read the prose between them as \"code block\".\n // Code is held behind placeholders so the markup passes never touch it.\n const held: string[] = [];\n let staged = \"\";\n let at = 0;\n for (const r of findCodeRanges(text)) {\n if (r.start < at) continue;\n staged += text.slice(at, r.start);\n const raw = text.slice(r.start, r.end);\n held.push(r.kind === \"fence\" ? \" code block \" : spanWords(raw));\n staged += `\\uE300${held.length - 1}\\uE301`;\n at = r.end;\n }\n staged += text.slice(at);\n return staged\n .replace(/!\\[([^\\]]*)\\]\\([^)]*\\)/g, \"$1\")\n .replace(/\\[([^\\]]*)\\]\\([^)]*\\)/g, \"$1\")\n .replace(/^\\s{0,3}#{1,6}\\s+/gm, \"\")\n .replace(/^\\s{0,3}>\\s?/gm, \"\")\n .replace(/^\\s{0,3}([*+-]|\\d+\\.)\\s+/gm, \"\")\n .replace(/(\\*\\*\\*|___)(.+?)\\1/g, \"$2\")\n .replace(/(\\*\\*|__)(.+?)\\1/g, \"$2\")\n .replace(/(\\*|_)(.+?)\\1/g, \"$2\")\n .replace(/~~(.+?)~~/g, \"$1\")\n .replace(/^\\s{0,3}([-*_]\\s?){3,}$/gm, \"\")\n .replace(/\\uE300(\\d+)\\uE301/g, (_m, k: string) => held[Number(k)] ?? \"\")\n .replace(/[ \\t]{2,}/g, \" \");\n}\n\n/** A code span's words: the backtick runs off, one padding space each side off (CommonMark). */\nfunction spanWords(raw: string): string {\n const ticks = /^`+/.exec(raw)?.[0].length ?? 0;\n const inner = raw.slice(ticks, raw.length - ticks);\n return /^ .* $/s.test(inner) && inner.trim() ? inner.slice(1, -1) : inner;\n}\n","/**\n * PART RENDERERS — one part of one message becomes pixels.\n *\n * Every text-shaped part goes through the `renderContent` PORT, never through a\n * markdown tree owned by this file (design §6). That is the one deliberate\n * scope cut in the whole package, and it is why a 108k-line rendering system\n * did not have to be absorbed before a chat could ship.\n */\n\nimport { useState, type ReactNode } from \"react\";\n\nimport { Button, ErrorBox } from \"@ai-matrx/design-system\";\n\nimport type {\n ChatAskUserPart,\n ChatErrorPart,\n ChatFilePart,\n ChatNoticePart,\n ChatReasoningPart,\n ChatRenderBlockPart,\n ChatSourcePart,\n ChatTextPart,\n ChatToolPart,\n} from \"../../model\";\nimport { ChevronRightGlyph } from \"../icons\";\nimport type { ChatRenderContentPort } from \"../ports\";\nimport { ChatToolChip } from \"./tool-chip\";\n\n/* ---------------------------------------------------------------- text ---- */\n\nexport interface ChatTextPartViewProps {\n part: ChatTextPart;\n renderContent: ChatRenderContentPort;\n streaming: boolean;\n role: \"user\" | \"assistant\" | \"system\" | \"tool\";\n messageId: string;\n}\n\nexport function ChatTextPartView(props: ChatTextPartViewProps): ReactNode {\n return (\n <>\n {props.renderContent({\n kind: \"markdown\",\n text: props.part.text,\n streaming: props.streaming,\n role: props.role,\n messageId: props.messageId,\n })}\n </>\n );\n}\n\n/* ----------------------------------------------------------- reasoning ---- */\n\n/**\n * THE REASONING ASIDE — collapsed by default, and it says what it is.\n *\n * Two facts from the live capture shape this. First, the platform does not emit\n * `reasoning_chunk` on the Google path: the thinking arrives as ordinary chunk\n * text wrapped in a `<reasoning>` sentinel, which the kernel splits out. Second,\n * today's `/chat` prints that raw sentinel on screen because the shipped\n * projection concatenates it into the answer. A reader must never see it, and\n * the reasoning must never be mistaken for the answer — hence a separate,\n * quiet, collapsed region rather than dimmer prose in the same flow.\n */\nexport interface ChatReasoningAsideProps {\n part: ChatReasoningPart;\n defaultOpen: boolean;\n}\n\nexport function ChatReasoningAside(props: ChatReasoningAsideProps): ReactNode {\n const { part, defaultOpen } = props;\n const [open, setOpen] = useState(defaultOpen);\n const streaming = part.state === \"streaming\";\n const label = streaming ? \"Thinking…\" : \"Thought process\";\n\n return (\n <div className=\"mx-chat-reasoning\" data-streaming={streaming}>\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n aria-expanded={open}\n onClick={() => setOpen((value) => !value)}\n >\n <span data-open={open} className=\"mx-chat-chevron-wrap\">\n <ChevronRightGlyph />\n </span>\n {label}\n </button>\n {open ? (\n <div className=\"mx-chat-reasoning__body\" data-mx-chat-reasoning=\"body\">\n {part.text}\n </div>\n ) : null}\n </div>\n );\n}\n\n/* -------------------------------------------------------------- notice ---- */\n\n/**\n * A server notice, IN the transcript rather than in a toast.\n *\n * The corpus proves a run can lose half its requested tools and answer anyway.\n * A reader who is not told has been lied to — and a toast that disappeared\n * before they looked up is the same lie with better manners.\n */\nexport function ChatNoticePartView(props: { part: ChatNoticePart }): ReactNode {\n const { part } = props;\n return (\n <div className=\"mx-chat-notice\" data-level={part.level} role=\"note\">\n <span>{part.userMessage}</span>\n {part.remedy === null ? null : (\n <span className=\"mx-chat-notice__remedy\">{part.remedy}</span>\n )}\n </div>\n );\n}\n\n/* ------------------------------------------------------------- failure ---- */\n\n/**\n * THE FAILURE CARD — a plain sentence, a remedy, and Try again.\n *\n * \"Try again\" carries the reader's unsent words back to the message box through\n * the retry bus. Retyping a paragraph the product lost is the cheapest possible\n * insult, and the bus refuses to swallow it silently when no message box is\n * mounted.\n */\nexport interface ChatFailurePartViewProps {\n part: ChatErrorPart;\n /** The words that produced this failed turn. Empty when there are none. */\n retryText: string;\n onRetry(text: string): void;\n}\n\nexport function ChatFailurePartView(props: ChatFailurePartViewProps): ReactNode {\n const { part, retryText, onRetry } = props;\n return (\n <ErrorBox\n as=\"div\"\n className=\"mx-chat-failure\"\n data-code={part.code ?? \"unknown\"}\n message={part.userMessage}\n error={part}\n operation=\"Answer the last message\"\n unsavedInput={retryText === \"\" ? undefined : { message: retryText }}\n origin=\"chat/ChatFailurePart\"\n >\n <p className=\"mx-chat-failure__sentence\">{part.userMessage}</p>\n {part.remedy === null ? null : (\n <p className=\"mx-chat-failure__remedy\">{part.remedy}</p>\n )}\n {retryText === \"\" ? null : (\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n className=\"mx-chat-focusable\"\n onClick={() => onRetry(retryText)}\n >\n Try again\n </Button>\n )}\n </ErrorBox>\n );\n}\n\n/* ---------------------------------------------------------------- tool ---- */\n\nexport function ChatToolPartView(props: {\n part: ChatToolPart;\n defaultExpanded: boolean;\n}): ReactNode {\n return <ChatToolChip part={props.part} defaultExpanded={props.defaultExpanded} />;\n}\n\n/* -------------------------------------------------- render block / rest ---- */\n\nexport function ChatRenderBlockPartView(props: {\n part: ChatRenderBlockPart;\n renderContent: ChatRenderContentPort;\n messageId: string;\n role: \"user\" | \"assistant\" | \"system\" | \"tool\";\n}): ReactNode {\n const { part } = props;\n return (\n <div data-mx-chat-block={part.kind} data-status={part.status}>\n {props.renderContent({\n kind: part.kind,\n text: part.content ?? \"\",\n data: part.data,\n streaming: part.status === \"streaming\",\n role: props.role,\n messageId: props.messageId,\n })}\n </div>\n );\n}\n\n/**\n * Parts whose CAPABILITY is not in this lane. Each says exactly what it is and\n * what would show it — never a blank, never a dead control (Law 4).\n */\nexport function ChatFilePartView(props: { part: ChatFilePart }): ReactNode {\n const name = props.part.name ?? props.part.fileId;\n return (\n <span className=\"mx-chat-standin\" data-mx-chat-standin=\"file\">\n {`Attached file: ${name}. Previews appear once the attachments capability is turned on for this chat.`}\n </span>\n );\n}\n\nexport function ChatSourcePartView(props: { part: ChatSourcePart }): ReactNode {\n const { part } = props;\n const label = part.title ?? part.url ?? part.sourceId;\n return (\n <span className=\"mx-chat-standin\" data-mx-chat-standin=\"source\">\n {`Source: ${label}`}\n </span>\n );\n}\n\nexport function ChatAskUserPartView(props: { part: ChatAskUserPart }): ReactNode {\n return (\n <span className=\"mx-chat-standin\" data-mx-chat-standin=\"ask-user\">\n {props.part.answered\n ? `You already answered: ${props.part.prompt}`\n : `The assistant is waiting for your answer: ${props.part.prompt}. This chat cannot collect it yet — answer in a chat with the approvals capability turned on, or stop this run.`}\n </span>\n );\n}\n","/**\n * THE BASIC TOOL CHIP — the transcript's floor for a tool call.\n *\n * It is driven entirely by the kernel's tool state machine\n * (`input-streaming → input-available → approval-requested → output-available |\n * output-error | output-denied`), which is the champion's vocabulary and which\n * the live corpus already walks: `tool_started` with arguments, three\n * `tool_progress` beats under three DIFFERENT tool names, then `tool_completed`\n * with the result.\n *\n * Three things it refuses to do, each a real failure seen in the wild:\n * - it never shows a bare spinner with no name. A reader watching an agent\n * spend their money is owed the tool's name and what it is doing.\n * - it never drops the progress beats, and it keeps the SERVER's tool name per\n * beat (`web` → `web_search` → `web_research`); flattening them to the\n * call's name loses what actually ran.\n * - it never renders an error state as \"Done\".\n *\n * Lane 2B's `/tools` registry replaces this chip's BODY with a per-tool\n * renderer. The chip, its state machine and its header stay — a registry entry\n * is a richer body, not a second card. That is the `body` prop below: at the\n * lane merge `<ChatToolCard>` stopped carrying its own copy of this header and\n * became this chip with a body passed in, so the two can no longer drift in\n * markup, in state words, or in expand behaviour.\n */\n\nimport { useState, type ReactNode } from \"react\";\n\nimport type { ChatToolPart, ChatToolState } from \"../../model\";\nimport { useChatPorts } from \"../hooks\";\nimport { ChevronRightGlyph } from \"../icons\";\n\n/**\n * What the state means to a READER, not to a protocol. THE ONE TABLE: `/tools`\n * re-exports this rather than restating it, because a tool that reads \"Done\" in\n * one card and \"Completed\" in another is drift nobody notices for a month.\n */\nexport const CHAT_TOOL_STATE_LABEL: Record<ChatToolState, string> = {\n \"input-streaming\": \"Preparing\",\n \"input-available\": \"Running\",\n \"approval-requested\": \"Needs your approval\",\n \"output-available\": \"Done\",\n \"output-error\": \"Failed\",\n \"output-denied\": \"Declined\",\n};\n\nfunction formatPayload(value: unknown): string {\n if (value === undefined) return \"\";\n if (typeof value === \"string\") return value;\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n // A payload with a cycle or a BigInt still has to say something.\n return String(value);\n }\n}\n\nexport interface ChatToolChipProps {\n part: ChatToolPart;\n /** From `features.expandToolCardsByDefault`. */\n defaultExpanded: boolean;\n /**\n * Replace what the chip shows when it is open. This is the ONE seam `/tools`\n * uses: a registered renderer is a richer BODY under the same header, the\n * same state words and the same expand behaviour — never a second card.\n * Absent ⇒ the chip's own body (arguments, progress beats, error, result).\n */\n body?: ReactNode | undefined;\n /** Diagnostic marker for the surrounding element, e.g. which renderer drew\n * the body. Read by tests and by the census; never shown to a reader. */\n rendererTag?: string | undefined;\n}\n\nexport function ChatToolChip(props: ChatToolChipProps): ReactNode {\n const { part, defaultExpanded, body } = props;\n const [expanded, setExpanded] = useState(defaultExpanded);\n\n const { toolLabel } = useChatPorts();\n const latest = part.progress[part.progress.length - 1];\n const input = formatPayload(part.input);\n const output = formatPayload(part.output);\n const stateLabel = CHAT_TOOL_STATE_LABEL[part.state];\n // THE HEADER SAYS EACH THING ONCE. Servers send a completion beat whose\n // whole content is the word the state column already carries (\"Done\"), and\n // the chip read \"Done Done\" in front of a Creator (live review 2026-09-18).\n // The beat is not thrown away — it stays in the body, under Progress.\n const beat =\n latest !== undefined &&\n latest.message !== \"\" &&\n latest.message.trim().toLowerCase() !== stateLabel.toLowerCase()\n ? latest.message\n : null;\n const hasBody =\n input !== \"\" || output !== \"\" || part.errorText !== null || part.progress.length > 0;\n\n return (\n <div\n className=\"mx-chat-tool\"\n data-state={part.state}\n data-tool={part.toolName}\n {...(props.rendererTag === undefined\n ? {}\n : { \"data-mx-chat-tool-renderer\": props.rendererTag })}\n >\n <button\n type=\"button\"\n className=\"mx-chat-tool__header mx-chat-focusable\"\n aria-expanded={expanded}\n // A chip with nothing to show still toggles rather than sitting there\n // looking disabled (Law 4) — it says so in the body instead.\n onClick={() => setExpanded((open) => !open)}\n >\n <span data-open={expanded} className=\"mx-chat-chevron-wrap\">\n <ChevronRightGlyph />\n </span>\n {/* WHAT IS HAPPENING, never the identifier — `toolLabel` is the port\n and `data-tool` above keeps the machine name for the machines. */}\n <span className=\"mx-chat-tool__name\">{toolLabel(part.toolName)}</span>\n {beat === null ? null : <span className=\"mx-chat-tool__beat\">{beat}</span>}\n <span className=\"mx-chat-tool__state\">{stateLabel}</span>\n </button>\n\n {expanded ? (\n body !== undefined ? (\n body\n ) : (\n <div className=\"mx-chat-tool__body\">\n {input !== \"\" ? (\n <>\n <span className=\"mx-chat-tool__label\">What it was asked</span>\n <pre className=\"mx-chat-tool__payload\">{input}</pre>\n </>\n ) : null}\n\n {part.progress.length > 0 ? (\n <>\n <span className=\"mx-chat-tool__label\">Progress</span>\n <ul className=\"mx-chat-tool__progress\">\n {part.progress.map((beat, index) => (\n <li key={`${beat.toolName}-${index}`}>\n <span data-tool-beat={beat.toolName}>{beat.toolName}</span>\n {beat.message === \"\" ? null : ` — ${beat.message}`}\n {beat.percent === null ? null : ` (${Math.round(beat.percent)}%)`}\n </li>\n ))}\n </ul>\n </>\n ) : null}\n\n {part.errorText !== null ? (\n <>\n <span className=\"mx-chat-tool__label\">What went wrong</span>\n <p className=\"mx-chat-failure__sentence\">{part.errorText}</p>\n </>\n ) : null}\n\n {output !== \"\" ? (\n <>\n <span className=\"mx-chat-tool__label\">What it returned</span>\n <pre className=\"mx-chat-tool__payload\">{output}</pre>\n </>\n ) : null}\n\n {hasBody ? null : (\n <span className=\"mx-chat-standin\">\n {part.state === \"input-streaming\"\n ? \"This tool has not said what it is doing yet.\"\n : \"This tool ran without reporting any details.\"}\n </span>\n )}\n </div>\n )\n ) : null}\n </div>\n );\n}\n","/**\n * `<ChatComposer>` — lane 2B's headline component.\n *\n * 🚨 THE THREE FLAWS THE OWNER REJECTED TODAY, in another composer, and the\n * reason this file's geometry is written down rather than left to taste:\n *\n * 1. **ONE soft rounded container owns the focus ring.** The ring is drawn on\n * the shell via `:focus-within`; the textarea inside has no outline at\n * all. Two rings is a box inside a box.\n * 2. **NO divider line above the composer.** The transcript ends, the\n * composer begins, and nothing draws a rule between them.\n * 3. **Tight, balanced padding.** The same inline padding on both ends, the\n * action row optically aligned with the text, no dead band under the box.\n *\n * Everything stateful comes from `useChatComposerState()` (shared scaffolding).\n * This component RENDERS it. It adds no second source of composer state — two\n * sources is how a draft gets lost — and it re-resolves no knob: `sendKey`,\n * `retainDrafts` and `attachmentMaxBytes` all arrive from `thread.features`.\n *\n * Behaviours ported from real composers that have been running in production:\n * - **IME composition never sends.** `event.nativeEvent.isComposing` is\n * checked before the key contract runs, or Japanese/Chinese input submits a\n * half-committed candidate on the Enter that accepts it.\n * - **`draftSeed` never overwrites typed text** (workflow-studio). A seed\n * lands only into an empty box.\n * - **↑ in an empty box edits the last message you sent** (the Claude/ChatGPT\n * interaction language; matrx-frontend's composer does NOT have it).\n * - **Voice recovery is part of voice** (workflow-studio): the recovery\n * affordance renders itself away when there is nothing to recover.\n */\n\nimport {\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n type ClipboardEvent as ReactClipboardEvent,\n type DragEvent as ReactDragEvent,\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n} from \"react\";\n\nimport { messageText } from \"../../model\";\nimport { useChatComposerState, useChatConfig, useChatPorts, useChatSlots } from \"../hooks\";\nimport { useChatContext } from \"../provider\";\nimport { ChatAttachmentChips } from \"./attachment-chips\";\nimport { ATTACHMENTS_UNAVAILABLE, ATTACHMENTS_UNAVAILABLE_REMEDY } from \"./files\";\nimport { ChatRunControls } from \"../plugins/run-controls\";\nimport { useChatComposerPorts } from \"./provider\";\nimport { composerSendIntent } from \"./send-intent\";\nimport { ChatComposerInput } from \"./textarea\";\nimport { AttachGlyph, SendGlyph, StopGlyph } from \"./glyphs\";\nimport { useChatAttachments } from \"./use-attachments\";\n\n/** Geometry, in one place, so a re-skin is token values and these two numbers. */\nconst MIN_FIELD_HEIGHT_PX = 24;\nconst MAX_FIELD_HEIGHT_PX = 208;\n\nexport interface ChatComposerProps {\n /**\n * Text to put in an EMPTY box — a suggestion, a deep link, a \"continue from\n * here\". It never overwrites what the reader has typed.\n */\n draftSeed?: string | null | undefined;\n placeholder?: string | undefined;\n autoFocus?: boolean | undefined;\n /**\n * The composer refuses input and SAYS WHY. A held composer with no sentence\n * is the dead control Law 4 forbids, so passing `held` without\n * `heldReason` is a type error.\n */\n held?: { reason: string } | undefined;\n className?: string | undefined;\n}\n\nfunction dropHasFiles(event: ReactDragEvent): boolean {\n const types = event.dataTransfer?.types;\n if (types === undefined) return false;\n return Array.from(types).includes(\"Files\");\n}\n\nexport function ChatComposer(props: ChatComposerProps): ReactNode {\n const composer = useChatComposerState();\n const features = useChatConfig();\n const { notifier } = useChatPorts();\n const slots = useChatSlots();\n const { files, voice } = useChatComposerPorts();\n // The composer reads the STORE, not `useChatThread()`: subscribing the\n // message box to the whole thread would re-render it on every coalesced\n // streaming tick, for a list it only reads when ↑ is pressed.\n const { thread } = useChatContext();\n const fieldRef = useRef<HTMLTextAreaElement | null>(null);\n const noticeId = useId();\n const [voiceBusy, setVoiceBusy] = useState(false);\n const [dragging, setDragging] = useState(false);\n const [unavailable, setUnavailable] = useState(false);\n const dragDepth = useRef(0);\n // `doSend` is defined below `onKeyDown`; a ref keeps ONE send path without\n // reordering the file into something unreadable.\n const sendRef = useRef<() => Promise<void>>(async () => undefined);\n\n const attachments = useChatAttachments({\n files,\n maxBytes: features.attachmentMaxBytes,\n notifier,\n });\n\n /* ---- draftSeed: into an EMPTY box only -------------------------------- */\n const seenSeed = useRef<string | null>(null);\n useEffect(() => {\n const seed = props.draftSeed;\n if (seed === null || seed === undefined || seed === \"\") return;\n if (seenSeed.current === seed) return;\n seenSeed.current = seed;\n if (composer.text.trim() !== \"\") return;\n composer.setText(seed);\n // `composer.text` is deliberately NOT a dependency: re-running this the\n // moment the reader clears the box would re-seed it under their cursor.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [props.draftSeed]);\n\n /* ---- files in by any route ------------------------------------------- */\n const takeFiles = useCallback(\n (incoming: readonly File[]) => {\n if (incoming.length === 0) return;\n if (!files.available) {\n // No chip, no silence: the port itself announces once with the remedy,\n // and the composer shows the same sentence in its notice line.\n void files\n .upload(incoming[0] as File, {\n signal: new AbortController().signal,\n onProgress: () => undefined,\n })\n .catch(() => undefined);\n setUnavailable(true);\n return;\n }\n attachments.add(incoming);\n },\n [attachments, files],\n );\n\n const onPaste = useCallback(\n (event: ReactClipboardEvent<HTMLTextAreaElement>) => {\n const items = event.clipboardData?.files;\n if (items === undefined || items.length === 0) return;\n // A pasted screenshot arrives as a File with no name in some browsers;\n // the chip still needs one, so the port's own default naming applies.\n event.preventDefault();\n takeFiles(Array.from(items));\n },\n [takeFiles],\n );\n\n const onDrop = useCallback(\n (event: ReactDragEvent<HTMLFormElement>) => {\n if (!dropHasFiles(event)) return;\n event.preventDefault();\n dragDepth.current = 0;\n setDragging(false);\n takeFiles(Array.from(event.dataTransfer.files));\n },\n [takeFiles],\n );\n\n /* ---- keyboard --------------------------------------------------------- */\n const onKeyDown = useCallback(\n (event: ReactKeyboardEvent<HTMLTextAreaElement>) => {\n // THE IME RULE HAS ONE HOME: `composerSendIntent`. An early return here\n // too would double-guard it, and a behaviour guarded twice is a\n // behaviour neither guard can be proven to hold — `test:falsifiable`\n // caught exactly that: removing one guard left the suite green.\n const composing = event.nativeEvent.isComposing;\n\n if (\n !composing &&\n event.key === \"ArrowUp\" &&\n composer.text === \"\" &&\n !event.shiftKey &&\n !event.metaKey &&\n !event.ctrlKey\n ) {\n const messages = thread.getSnapshot().messages;\n for (let cursor = messages.length - 1; cursor >= 0; cursor -= 1) {\n const candidate = messages[cursor];\n if (candidate === undefined || candidate.role !== \"user\") continue;\n const text = messageText(candidate);\n if (text === \"\") break;\n event.preventDefault();\n composer.setText(text);\n return;\n }\n return;\n }\n\n const intent = composerSendIntent(\n {\n key: event.key,\n shiftKey: event.shiftKey,\n metaKey: event.metaKey,\n ctrlKey: event.ctrlKey,\n isComposing: composing,\n isBusy: composer.isBusy,\n },\n features.sendKey,\n );\n if (intent === \"send\") {\n event.preventDefault();\n void sendRef.current();\n return;\n }\n if (intent === \"stop\") {\n event.preventDefault();\n void composer.stop();\n }\n },\n [composer, features.sendKey, thread],\n );\n\n /* ---- send ------------------------------------------------------------- */\n const held = props.held ?? null;\n const blockedReason =\n held !== null\n ? held.reason\n : voiceBusy\n ? \"Finish recording to send your message.\"\n : attachments.uploading\n ? \"Your files are still uploading.\"\n : null;\n\n const canSend = composer.canSend && blockedReason === null;\n\n const doSend = useCallback(async () => {\n if (!canSend) return;\n const body = composer.text.trim();\n const ids = attachments.readyFileIds;\n if (ids.length === 0) {\n await composer.send();\n } else {\n composer.setText(\"\");\n const result = await thread.send({ text: body, fileIds: ids });\n if (!result.accepted) composer.setText(body);\n else attachments.clear();\n }\n // Focus returns to the box after a send — a reader who has to click back\n // into the composer after every message notices every single time.\n fieldRef.current?.focus();\n }, [attachments, canSend, composer, thread]);\n sendRef.current = doSend;\n\n const onSubmit = useCallback(\n (event: { preventDefault(): void }) => {\n event.preventDefault();\n void doSend();\n },\n [doSend],\n );\n\n const sendLabel = composer.isBusy\n ? \"Stop the answer\"\n : blockedReason !== null\n ? blockedReason\n : \"Send message\";\n // `features.sendWhileBusy`: the host queues, so Send stays beside Stop while\n // there is something to send — never a dead Send, and Stop never hides.\n const queueing = composer.isBusy && features.sendWhileBusy && composer.text.trim() !== \"\";\n const queueLabel =\n blockedReason !== null ? blockedReason : \"Queue message — it sends when this answer finishes\";\n\n const voiceContext = {\n value: composer.text,\n onValueChange: composer.setText,\n busy: composer.isBusy,\n onBusyChange: setVoiceBusy,\n className: \"mx-chat-composer__icon-button mx-chat-focusable\",\n };\n\n return (\n <form\n className={`mx-chat-composer${props.className === undefined ? \"\" : ` ${props.className}`}`}\n data-dragging={dragging || undefined}\n data-busy={composer.isBusy || undefined}\n onSubmit={onSubmit}\n onDragEnter={(event) => {\n if (!dropHasFiles(event)) return;\n dragDepth.current += 1;\n setDragging(true);\n }}\n onDragOver={(event) => {\n if (dropHasFiles(event)) event.preventDefault();\n }}\n onDragLeave={(event) => {\n if (!dropHasFiles(event)) return;\n // A counter, not a boolean: dragging over a child fires `dragleave` on\n // the parent, and a boolean makes the overlay flicker off mid-drag.\n dragDepth.current = Math.max(0, dragDepth.current - 1);\n if (dragDepth.current === 0) setDragging(false);\n }}\n onDrop={onDrop}\n >\n {slots.composerAbove}\n\n {/* PHASE 4's run-control row. It renders ITSELF away when the host has\n given neither controls nor chips, and it writes through THIS\n composer's setter — never a second source of composer state. */}\n <ChatRunControls\n onFill={composer.setText}\n busy={composer.isBusy}\n drafting={composer.text.trim() !== \"\"}\n />\n\n {/* ONE SHELL. The ring lives here (:focus-within), not on the field. */}\n <div className=\"mx-chat-composer__shell\">\n {attachments.attachments.length > 0 ? (\n <ChatAttachmentChips\n attachments={attachments.attachments}\n onRemove={attachments.remove}\n onRetry={attachments.retry}\n />\n ) : null}\n\n <ChatComposerInput\n value={composer.text}\n onValueChange={composer.setText}\n onKeyDown={onKeyDown}\n onPaste={onPaste}\n placeholder={props.placeholder ?? \"Send a message\"}\n minHeightPx={MIN_FIELD_HEIGHT_PX}\n maxHeightPx={MAX_FIELD_HEIGHT_PX}\n disabled={held !== null}\n label=\"Message\"\n describedBy={noticeId}\n inputRef={fieldRef}\n {...(props.autoFocus === undefined ? {} : { autoFocus: props.autoFocus })}\n />\n\n <div className=\"mx-chat-composer__row\">\n <div className=\"mx-chat-composer__left\">\n {/* ABSENT, never dead: no attach button when nothing can store a\n file. Paste and drop still answer honestly through the port. */}\n {files.available ? (\n <label className=\"mx-chat-composer__icon-button mx-chat-focusable\">\n <AttachGlyph />\n <span className=\"mx-chat__sr-only\">Attach files</span>\n <input\n type=\"file\"\n multiple\n aria-label=\"Attach files\"\n {...(files.accept === undefined ? {} : { accept: files.accept })}\n onChange={(event) => {\n const list = event.target.files;\n if (list !== null) takeFiles(Array.from(list));\n event.target.value = \"\";\n }}\n />\n </label>\n ) : null}\n\n {voice === null ? null : voice.renderButton(voiceContext)}\n {slots.composerPickers}\n {slots.composerToolbar}\n </div>\n\n <div className=\"mx-chat-composer__right\">\n {slots.composerRunControls}\n {/* The stop button REPLACES send during a stream. It never sits\n beside it looking dead (design §7, Law 4). */}\n {queueing ? (\n <button\n type=\"submit\"\n className=\"mx-chat-composer__send mx-chat-focusable\"\n data-mode=\"queue\"\n aria-label={queueLabel}\n title={queueLabel}\n disabled={!canSend}\n >\n <SendGlyph />\n </button>\n ) : null}\n {composer.isBusy ? (\n <button\n type=\"button\"\n className=\"mx-chat-composer__send mx-chat-focusable\"\n data-mode=\"stop\"\n aria-label={sendLabel}\n title={sendLabel}\n onClick={() => void composer.stop()}\n >\n <StopGlyph />\n </button>\n ) : (\n <button\n type=\"submit\"\n className=\"mx-chat-composer__send mx-chat-focusable\"\n data-mode=\"send\"\n aria-label={sendLabel}\n title={sendLabel}\n disabled={!canSend}\n >\n <SendGlyph />\n </button>\n )}\n </div>\n </div>\n </div>\n\n {/* THE HONEST LINE. One region, always present in the accessibility tree,\n so a reason for a refusal is announced rather than only implied by a\n button that will not move. */}\n <div className=\"mx-chat-composer__notice\" id={noticeId} role=\"status\" aria-live=\"polite\">\n {held !== null ? (\n <span className=\"mx-chat-standin\">{held.reason}</span>\n ) : unavailable ? (\n <span className=\"mx-chat-standin\">\n {`${ATTACHMENTS_UNAVAILABLE} ${ATTACHMENTS_UNAVAILABLE_REMEDY}`}\n </span>\n ) : blockedReason !== null && composer.text.trim() !== \"\" ? (\n <span className=\"mx-chat-standin\">{blockedReason}</span>\n ) : composer.restoredFromFailure ? (\n <span className=\"mx-chat-standin\">\n Your message is back in the box. Send it again when you are ready.\n </span>\n ) : null}\n {voice?.renderRecovery?.(voiceContext) ?? null}\n </div>\n\n {slots.composerBelow}\n\n {dragging ? (\n <div className=\"mx-chat-composer__drop\" aria-live=\"polite\">\n {files.available\n ? \"Drop files to attach them to your message\"\n : ATTACHMENTS_UNAVAILABLE}\n </div>\n ) : null}\n </form>\n );\n}\n","/**\n * ATTACHMENT CHIPS — lane 2B.\n *\n * Inline, INSIDE the composer's one rounded shell, above the text. Not a\n * separate panel and not a toast: an attachment is part of the message being\n * written, so it lives in the same object.\n *\n * Three things a chip never does:\n * - it never shows a percentage the port did not report. With no measurable\n * progress the chip says \"Uploading…\" and the bar is indeterminate, because\n * a fake 60% that sits there is a lie a reader will believe.\n * - it never hides a failure. A failed chip carries the sentence AND a \"Try\n * again\" that re-uploads the same bytes — except when retrying cannot\n * possibly work (a file over the limit), where the button is ABSENT.\n * - it never loses the remove control while uploading: removing aborts.\n */\n\nimport type { ReactNode } from \"react\";\n\nimport { formatFileSize } from \"@ai-matrx/kit/format\";\nimport { RemoveGlyph } from \"./glyphs\";\nimport type { ChatAttachment } from \"./use-attachments\";\n\nexport interface ChatAttachmentChipsProps {\n attachments: readonly ChatAttachment[];\n onRemove(key: string): void;\n onRetry(key: string): void;\n}\n\nexport function ChatAttachmentChips(props: ChatAttachmentChipsProps): ReactNode {\n return (\n <ul className=\"mx-chat-attachments\" aria-label=\"Attachments on this message\">\n {props.attachments.map((attachment) => (\n <li\n key={attachment.key}\n className=\"mx-chat-attachment\"\n data-status={attachment.status}\n >\n <span className=\"mx-chat-attachment__name\" title={attachment.name}>\n {attachment.name}\n </span>\n <span className=\"mx-chat-attachment__meta\">\n {attachment.status === \"uploading\"\n ? attachment.progress === null\n ? \"Uploading…\"\n : `Uploading ${Math.round(attachment.progress * 100)}%`\n : attachment.status === \"failed\"\n ? \"Not attached\"\n : formatFileSize(attachment.sizeBytes)}\n </span>\n\n {attachment.status === \"uploading\" ? (\n <span\n className=\"mx-chat-attachment__bar\"\n role=\"progressbar\"\n aria-label={`Uploading ${attachment.name}`}\n {...(attachment.progress === null\n ? {}\n : {\n \"aria-valuenow\": Math.round(attachment.progress * 100),\n \"aria-valuemin\": 0,\n \"aria-valuemax\": 100,\n })}\n data-indeterminate={attachment.progress === null || undefined}\n >\n <span\n className=\"mx-chat-attachment__fill\"\n style={\n attachment.progress === null\n ? undefined\n : { width: `${Math.round(attachment.progress * 100)}%` }\n }\n />\n </span>\n ) : null}\n\n {attachment.failure === null ? null : (\n <span className=\"mx-chat-attachment__failure\">{attachment.failure}</span>\n )}\n\n {attachment.status === \"failed\" && attachment.retryable ? (\n <button\n type=\"button\"\n className=\"mx-chat-attachment__retry mx-chat-focusable\"\n onClick={() => props.onRetry(attachment.key)}\n >\n Try again\n </button>\n ) : null}\n\n <button\n type=\"button\"\n className=\"mx-chat-attachment__remove mx-chat-focusable\"\n aria-label={`Remove ${attachment.name}`}\n title={`Remove ${attachment.name}`}\n onClick={() => props.onRemove(attachment.key)}\n >\n <RemoveGlyph />\n </button>\n </li>\n ))}\n </ul>\n );\n}\n","/**\n * 2B's GLYPHS. Inline, sized in `em`, `currentColor` — the same reasoning as\n * `../icons.tsx`: four small paths do not justify an edge in the sibling graph,\n * and an icon that inherits colour and size from its button is exactly what the\n * re-skin contract needs. Every one is `aria-hidden`; the BUTTON carries the\n * name.\n */\n\nimport type { ReactNode } from \"react\";\n\nfunction frame(children: ReactNode): ReactNode {\n return (\n <svg\n width=\"1em\"\n height=\"1em\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.6\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n {children}\n </svg>\n );\n}\n\nexport function SendGlyph(): ReactNode {\n return frame(<path d=\"M8 13V3.5M3.75 7.75 8 3.5l4.25 4.25\" />);\n}\n\nexport function StopGlyph(): ReactNode {\n return frame(<rect x=\"4.25\" y=\"4.25\" width=\"7.5\" height=\"7.5\" rx=\"1.5\" fill=\"currentColor\" stroke=\"none\" />);\n}\n\nexport function AttachGlyph(): ReactNode {\n return frame(\n <path d=\"M10.6 5.1 5.9 9.8a1.7 1.7 0 0 0 2.4 2.4l4.7-4.7a3.2 3.2 0 0 0-4.5-4.5L3.6 7.9a4.7 4.7 0 0 0 6.7 6.7\" />,\n );\n}\n\nexport function RemoveGlyph(): ReactNode {\n return frame(<path d=\"M4.5 4.5l7 7M11.5 4.5l-7 7\" />);\n}\n","/**\n * PHASE 4 — THE RUN-CONTROL ROW, immediately above the message box.\n *\n * There are now TWO places a host puts something above the transcript's end,\n * and they are different on purpose:\n *\n * `surfaceBanner` (2C) full width, under the shell's header — a run tray,\n * a review bar, a policy notice. The studio's\n * `DockRunTray` rides it.\n * `composerControls` (4) the SMALL row that belongs to the box: the chips a\n * turn just earned, a hand-off offer, a mode switch.\n * It scrolls with nothing and sits where the reader's\n * eye already is when they are about to type.\n *\n * 🚨 IT DOES NOT EXIST WHEN IT IS EMPTY. A host that gives neither controls nor\n * chips gets no row, no border and no gap — an empty band above the box is\n * dead pixels that push the composer down for nothing.\n *\n * 🚨 IT HOLDS NO COMPOSER STATE. The chips write through the setter the\n * composer hands down, never through a second `useChatComposerState()` — two\n * sources of composer state is how a draft gets lost, which is the rule lane\n * 2B was written against and the reason this takes props instead of a hook.\n */\n\nimport type { ReactNode } from \"react\";\n\nimport { useChatPorts, useChatSlots } from \"../hooks\";\nimport { useChatContext } from \"../provider\";\n\nexport interface ChatRunControlsProps {\n /**\n * Put words in the box. THE CHIP NEVER SENDS — see the `quickset` port: a\n * one-click paid run from a row the reader was only reading spends their\n * money on a guess.\n */\n onFill(text: string): void;\n /** True while a turn is in flight; the chips stand down rather than queue. */\n busy: boolean;\n /** True when the box already has words in it — a chip would overwrite them. */\n drafting: boolean;\n}\n\nexport function ChatRunControls(props: ChatRunControlsProps): ReactNode {\n const slots = useChatSlots();\n const { quickset } = useChatPorts();\n const { thread } = useChatContext();\n\n const snapshot = thread.getSnapshot();\n const chips =\n quickset === null || props.busy || props.drafting\n ? []\n : quickset.chips({\n conversationId: snapshot.conversationId,\n busy: props.busy,\n empty: snapshot.messages.length === 0,\n });\n\n const controls = slots.composerControls;\n if (controls === undefined && chips.length === 0) return null;\n\n return (\n <div className=\"mx-chat-run-controls\">\n {controls}\n {chips.length === 0 ? null : (\n <div\n className=\"mx-chat-quickset\"\n role=\"group\"\n aria-label=\"Things you could ask\"\n >\n {chips.map((chip) => (\n <button\n key={chip.id}\n type=\"button\"\n className=\"mx-chat-quickset__chip mx-chat-focusable\"\n data-quickset={chip.id}\n onClick={() => props.onFill(chip.text)}\n >\n {chip.label}\n </button>\n ))}\n </div>\n )}\n </div>\n );\n}\n","/**\n * `<ChatComposerProvider>` — a SCOPED OVERRIDE of the `files` and `voice`\n * ports, and nothing else.\n *\n * 🚨 WHAT CHANGED AT THE LANE MERGE. Lane 2B built this as a second port\n * carrier, because `src/react/ports.ts` was shared scaffolding that lanes 2A\n * and 2C were reading and the lane contract forbade 2B editing it mid-flight.\n * The deviation was filed at the time; this is it closed. `files` and `voice`\n * are now fields on `ChatReactPorts`, defaulted ONCE by `<ChatProvider>`, which\n * is the single port map design §5 specifies.\n *\n * WHY THIS COMPONENT STILL EXISTS RATHER THAN BEING DELETED: a page can hold\n * two composers over one chat provider — the Workflow Studio's docked Conductor\n * and its floating one — and they can legitimately differ (one has an upload\n * service, the other does not). Overriding for a subtree is that, and it is a\n * real requirement rather than a leftover. A host with one composer never\n * mounts it: `<ChatProvider ports={{ files, voice }}>` is the whole story.\n *\n * Without any override the composer still works: it gets the provider's\n * stand-in files port, which refuses out loud with the remedy, and no voice\n * button at all (absent, not dead).\n */\n\nimport { createContext, useContext, useMemo, type ReactNode } from \"react\";\n\nimport { useChatPorts } from \"../hooks\";\nimport type { ChatFilesPort } from \"./files\";\nimport type { ChatVoicePort } from \"./voice-port\";\n\nexport interface ChatComposerPorts {\n files: ChatFilesPort;\n /** Absent ⇒ no microphone button is rendered. */\n voice: ChatVoicePort | null;\n}\n\nconst ComposerPortsContext = createContext<ChatComposerPorts | null>(null);\n\nexport interface ChatComposerProviderProps {\n files?: ChatFilesPort | undefined;\n voice?: ChatVoicePort | undefined;\n children: ReactNode;\n}\n\nexport function ChatComposerProvider(props: ChatComposerProviderProps): ReactNode {\n // INHERIT, never rebuild. A provider that manufactured its own stand-in here\n // would silently discard a `files` port the host gave `<ChatProvider>` — the\n // exact class of silent override this merge exists to remove.\n const inherited = useChatComposerPorts();\n const { files, voice } = props;\n const value = useMemo<ChatComposerPorts>(\n () => ({\n files: files ?? inherited.files,\n voice: voice ?? inherited.voice,\n }),\n [files, voice, inherited.files, inherited.voice],\n );\n return (\n <ComposerPortsContext.Provider value={value}>\n {props.children}\n </ComposerPortsContext.Provider>\n );\n}\n\n/**\n * The composer's ports: the nearest override if there is one, otherwise the\n * `<ChatProvider>` defaults. It never throws — a transcript-plus-composer\n * mounted with no override is the ordinary case and must render a working\n * message box.\n */\nexport function useChatComposerPorts(): ChatComposerPorts {\n const scoped = useContext(ComposerPortsContext);\n const ports = useChatPorts();\n return useMemo<ChatComposerPorts>(\n () => scoped ?? { files: ports.files, voice: ports.voice },\n [scoped, ports.files, ports.voice],\n );\n}\n","/**\n * THE SEND-KEY DECISION, as one pure function.\n *\n * ⚠️ WHY IT EXISTS AT ALL — read before deleting it. `useChatComposerState()`\n * (shared scaffolding) already carries an `onKeyDown` implementing the same\n * contract, and it calls its OWN `send()`, which knows nothing about\n * attachments. A composer that delegated its Enter key to that handler would\n * silently drop every uploaded file from the turn the reader pressed Enter on —\n * the file would sit in a chip, the message would go without it, and nobody\n * would find out until the assistant answered about a document it never saw.\n *\n * So the composer decides the key itself and routes EVERY send through one\n * place. It is not a second source of composer state: the only input is\n * `features.sendKey`, a knob resolved once in the kernel.\n *\n * `react-composer.test.tsx` § \"THE ONE CONTRACT\" asserts this function and the\n * hook's handler agree across the whole key matrix, so the two cannot drift.\n * At lane merge the hook should call this function and the duplication goes.\n */\n\nimport type { ChatSendKey } from \"../../config\";\n\nexport type ChatKeyIntent = \"send\" | \"stop\" | \"newline\" | \"pass\";\n\nexport interface ChatKeyFacts {\n key: string;\n shiftKey: boolean;\n metaKey: boolean;\n ctrlKey: boolean;\n /** An IME candidate being committed. It is NEVER a send. */\n isComposing: boolean;\n /** True while a turn is in flight — Escape only means \"stop\" then. */\n isBusy: boolean;\n}\n\nexport function composerSendIntent(\n facts: ChatKeyFacts,\n sendKey: ChatSendKey,\n): ChatKeyIntent {\n if (facts.isComposing) return \"pass\";\n if (facts.key === \"Escape\") return facts.isBusy ? \"stop\" : \"pass\";\n if (facts.key !== \"Enter\") return \"pass\";\n const withModifier = facts.metaKey || facts.ctrlKey;\n const sends =\n sendKey === \"enter\" ? !facts.shiftKey && !withModifier : withModifier;\n return sends ? \"send\" : \"newline\";\n}\n","/**\n * THE INPUT FIELD — lane 2B.\n *\n * 🚨 IT OWNS NO FOCUS RING. The ring belongs to the composer's ONE rounded\n * shell (`:focus-within`), so a reader sees a single object light up rather\n * than a box inside a box. `outline: none` on this field is therefore part of\n * the contract, not a style opinion — and the shell's ring is what makes it\n * accessible. The owner rejected exactly this (a ring on the inner field, a\n * divider above the box, loose padding) in another composer TODAY.\n *\n * The auto-grow mechanism is ported from matrx-frontend's `AgentTextarea` +\n * `textarea-line-grid.ts`, which is the only near-pure file in that 7,900-line\n * tree and has three earned details:\n *\n * 1. **Zero-measure, then restore.** Height is set to `0px` to read\n * `scrollHeight`, then written back to the starting height before the\n * browser paints, with transitions suppressed for the measurement. Without\n * that the box ratchets upward and never comes back down.\n * 2. **Snap to the line grid.** A height between two lines leaves a half-line\n * of dead space that shifts on every keystroke.\n * 3. **Skip the write when nothing changed.** A keystroke that does not change\n * the line count must not touch `style.height` at all, or the box flutters\n * while someone types.\n *\n * THE iOS ZOOM FLOOR is CSS, not JS, and it ships in `styles.css` unlayered:\n * any `font-size` under 16px on a focused field makes mobile Safari zoom the\n * page and never zoom back. An inline `style.fontSize` would defeat a host's\n * theme, so the rule lives in the sheet where a host can see it.\n */\n\nimport {\n useCallback,\n useLayoutEffect,\n useRef,\n type ClipboardEvent as ReactClipboardEvent,\n type CSSProperties,\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n type Ref,\n} from \"react\";\n\n/** Pure: the snapped height for a measured content height. Tested directly. */\nexport function snapToLineGrid(\n contentHeight: number,\n lineHeight: number,\n chrome: number,\n): number {\n if (!Number.isFinite(lineHeight) || lineHeight <= 0) return contentHeight;\n const safeChrome = Number.isFinite(chrome) && chrome > 0 ? chrome : 0;\n const lines = Math.max(1, Math.round((contentHeight - safeChrome) / lineHeight));\n return lines * lineHeight + safeChrome;\n}\n\nexport interface ChatComposerInputProps {\n value: string;\n onValueChange(next: string): void;\n onKeyDown(event: ReactKeyboardEvent<HTMLTextAreaElement>): void;\n onPaste?(event: ReactClipboardEvent<HTMLTextAreaElement>): void;\n placeholder: string;\n /** Largest height in pixels before the field scrolls instead of growing. */\n maxHeightPx: number;\n minHeightPx: number;\n disabled: boolean;\n label: string;\n describedBy?: string | undefined;\n inputRef?: Ref<HTMLTextAreaElement> | undefined;\n autoFocus?: boolean | undefined;\n}\n\nexport function ChatComposerInput(props: ChatComposerInputProps): ReactNode {\n const internal = useRef<HTMLTextAreaElement | null>(null);\n\n const resize = useCallback(() => {\n const element = internal.current;\n if (element === null) return;\n\n const startHeight = element.offsetHeight;\n const scrollTop = element.scrollTop;\n const computed = getComputedStyle(element);\n const lineHeight = Number.parseFloat(computed.lineHeight);\n const chrome =\n Number.parseFloat(computed.paddingTop || \"0\") +\n Number.parseFloat(computed.paddingBottom || \"0\") +\n Number.parseFloat(computed.borderTopWidth || \"0\") +\n Number.parseFloat(computed.borderBottomWidth || \"0\");\n\n // Suppress the transition for the measurement, or the browser animates\n // through 0px and the box visibly collapses on every keystroke.\n element.style.transitionProperty = \"none\";\n element.style.height = \"0px\";\n const contentHeight = element.scrollHeight;\n element.style.height = `${startHeight}px`;\n // Force a style commit before the transition is restored.\n void element.offsetHeight;\n element.style.transitionProperty = \"\";\n element.scrollTop = scrollTop;\n\n const snapped = snapToLineGrid(contentHeight, lineHeight, chrome);\n const natural = Math.max(\n props.minHeightPx,\n Math.min(snapped, props.maxHeightPx),\n );\n // THE WRITE-SKIP GUARANTEE.\n if (natural === startHeight) return;\n element.style.height = `${natural}px`;\n }, [props.maxHeightPx, props.minHeightPx]);\n\n useLayoutEffect(() => {\n resize();\n }, [resize, props.value]);\n\n const setRef = useCallback(\n (node: HTMLTextAreaElement | null) => {\n internal.current = node;\n const forwarded = props.inputRef;\n if (typeof forwarded === \"function\") forwarded(node);\n else if (forwarded !== null && forwarded !== undefined) {\n (forwarded as { current: HTMLTextAreaElement | null }).current = node;\n }\n },\n [props.inputRef],\n );\n\n const style: CSSProperties = { maxHeight: props.maxHeightPx };\n\n return (\n <textarea\n ref={setRef}\n className=\"mx-chat-composer__field\"\n value={props.value}\n rows={1}\n style={style}\n placeholder={props.placeholder}\n aria-label={props.label}\n {...(props.describedBy === undefined ? {} : { \"aria-describedby\": props.describedBy })}\n disabled={props.disabled}\n autoFocus={props.autoFocus === true}\n onChange={(event) => props.onValueChange(event.target.value)}\n onKeyDown={props.onKeyDown}\n {...(props.onPaste === undefined ? {} : { onPaste: props.onPaste })}\n />\n );\n}\n","/**\n * ATTACHMENT STATE — lane 2B.\n *\n * One chip per file, with a real lifecycle: `uploading → ready | failed`, a\n * percentage when the port reports one, a retry that re-runs the SAME file\n * (kept in memory for exactly that reason), and a remove that ABORTS an upload\n * in flight instead of leaving it billing in the background.\n *\n * Rules that are code here, not prose:\n * - a send only ever carries `ready` ids. A half-uploaded file silently\n * dropped from a turn is the reader discovering later that the assistant\n * never saw their document.\n * - the size limit is `features.attachmentMaxBytes` — a KNOB (Law 6), not a\n * constant in this file — and exceeding it is refused with the file's name\n * and the limit in the sentence, before a byte is uploaded.\n * - a refusal is a CHIP in the failed state, not a toast that scrolls away.\n */\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport type { ChatNotifierPort } from \"../../ports\";\nimport { newLocalId } from \"../../model\";\nimport {\n tooLargeSentence,\n type ChatFilesPort,\n type ChatUploadedFile,\n} from \"./files\";\n\nexport type ChatAttachmentStatus = \"uploading\" | \"ready\" | \"failed\";\n\nexport interface ChatAttachment {\n /** Local chip identity. Stable across a retry so the chip does not jump. */\n key: string;\n name: string;\n sizeBytes: number;\n mediaType: string | null;\n status: ChatAttachmentStatus;\n /** 0..1 while uploading, or null when the port cannot measure. */\n progress: number | null;\n /** Present once ready — the ONLY thing a turn carries. */\n fileId: string | null;\n previewUrl: string | null;\n /** Present when failed: what a reader can read, never a stack. */\n failure: string | null;\n /** True when a retry can work at all (a size refusal never can). */\n retryable: boolean;\n}\n\nexport interface UseChatAttachmentsResult {\n attachments: readonly ChatAttachment[];\n /** Ids for the next send. Only `ready` chips. */\n readyFileIds: readonly string[];\n /** True while any chip is still uploading — the send button waits. */\n uploading: boolean;\n add(files: readonly File[]): void;\n remove(key: string): void;\n retry(key: string): void;\n /** Called after a successful send. */\n clear(): void;\n}\n\ninterface Pending {\n file: File;\n controller: AbortController;\n}\n\nexport function useChatAttachments(options: {\n files: ChatFilesPort;\n maxBytes: number;\n notifier: ChatNotifierPort;\n}): UseChatAttachmentsResult {\n const { files, maxBytes, notifier } = options;\n const [attachments, setAttachments] = useState<ChatAttachment[]>([]);\n const pending = useRef(new Map<string, Pending>());\n // The File objects survive a failure so \"Try again\" re-uploads the SAME\n // bytes. Asking a reader to find the file again because we threw it away is\n // the cheapest possible insult.\n const retainedFiles = useRef(new Map<string, File>());\n const mounted = useRef(true);\n\n useEffect(() => {\n mounted.current = true;\n return () => {\n mounted.current = false;\n // An unmounted composer must not leave uploads running. They cost money\n // and they can never reach a chip again.\n for (const entry of pending.current.values()) entry.controller.abort();\n pending.current.clear();\n };\n }, []);\n\n const patch = useCallback((key: string, next: Partial<ChatAttachment>) => {\n if (!mounted.current) return;\n setAttachments((current) =>\n current.map((item) => (item.key === key ? { ...item, ...next } : item)),\n );\n }, []);\n\n const run = useCallback(\n (key: string, file: File) => {\n const controller = new AbortController();\n pending.current.set(key, { file, controller });\n retainedFiles.current.set(key, file);\n patch(key, { status: \"uploading\", progress: null, failure: null });\n\n files\n .upload(file, {\n signal: controller.signal,\n onProgress: (fraction) => {\n if (controller.signal.aborted) return;\n patch(key, { progress: Math.max(0, Math.min(1, fraction)) });\n },\n })\n .then((uploaded: ChatUploadedFile) => {\n pending.current.delete(key);\n if (controller.signal.aborted) return;\n patch(key, {\n status: \"ready\",\n progress: 1,\n fileId: uploaded.fileId,\n previewUrl: uploaded.previewUrl ?? null,\n mediaType: uploaded.mediaType,\n failure: null,\n });\n })\n .catch((cause: unknown) => {\n pending.current.delete(key);\n if (controller.signal.aborted) return;\n patch(key, {\n status: \"failed\",\n progress: null,\n failure:\n cause instanceof Error && cause.message !== \"\"\n ? cause.message\n : \"This file could not be uploaded.\",\n retryable: true,\n });\n });\n },\n [files, patch],\n );\n\n const add = useCallback(\n (incoming: readonly File[]) => {\n if (incoming.length === 0) return;\n const created: ChatAttachment[] = [];\n const startable: { key: string; file: File }[] = [];\n\n for (const file of incoming) {\n const key = newLocalId(\"att\");\n if (file.size > maxBytes) {\n // Refused BEFORE a byte moves, and the refusal becomes a chip the\n // reader can see and dismiss — never a vanished file.\n const sentence = tooLargeSentence(file.name, maxBytes);\n created.push({\n key,\n name: file.name,\n sizeBytes: file.size,\n mediaType: file.type === \"\" ? null : file.type,\n status: \"failed\",\n progress: null,\n fileId: null,\n previewUrl: null,\n failure: sentence,\n retryable: false,\n });\n notifier.notify({\n level: \"warning\",\n message: sentence,\n remedy: \"Attach a smaller file, or ask an administrator to raise the limit for this workspace.\",\n });\n continue;\n }\n created.push({\n key,\n name: file.name,\n sizeBytes: file.size,\n mediaType: file.type === \"\" ? null : file.type,\n status: \"uploading\",\n progress: null,\n fileId: null,\n previewUrl: null,\n failure: null,\n retryable: true,\n });\n startable.push({ key, file });\n }\n\n setAttachments((current) => [...current, ...created]);\n for (const entry of startable) run(entry.key, entry.file);\n },\n [maxBytes, notifier, run],\n );\n\n const remove = useCallback((key: string) => {\n const entry = pending.current.get(key);\n if (entry !== undefined) {\n entry.controller.abort();\n pending.current.delete(key);\n }\n retainedFiles.current.delete(key);\n setAttachments((current) => current.filter((item) => item.key !== key));\n }, []);\n\n const retry = useCallback(\n (key: string) => {\n const file = retainedFiles.current.get(key);\n if (file === undefined) return;\n run(key, file);\n },\n [run],\n );\n\n const clear = useCallback(() => {\n for (const entry of pending.current.values()) entry.controller.abort();\n pending.current.clear();\n retainedFiles.current.clear();\n setAttachments([]);\n }, []);\n\n return {\n attachments,\n readyFileIds: attachments\n .filter((item) => item.status === \"ready\" && item.fileId !== null)\n .map((item) => item.fileId as string),\n uploading: attachments.some((item) => item.status === \"uploading\"),\n add,\n remove,\n retry,\n clear,\n };\n}\n","/**\n * THE VOICE SLOT — lane 2B.\n *\n * The package does not re-implement dictation: `@ai-matrx/browser-audio`\n * already owns permissions, chunked transcription, the recovery store and the\n * error vocabulary, and re-deriving any of that per host is exactly what this\n * campaign exists to stop. What 2B owns is the SEAM and its two rules, both\n * lifted from the workflow-studio composer that has been running this for real:\n *\n * 1. The button is ABSENT when the host has not enabled voice. It is never a\n * greyed-out microphone (Law 4).\n * 2. RECOVERY IS PART OF VOICE. A transcription that fails after the reader\n * has already spoken must not lose the words: browser-audio keeps the\n * recording and its recovery control offers it back, rendering itself away\n * when there is nothing to recover.\n *\n * 🚨 WHY THE IMPORT IS LAZY, AND WHY THAT IS NOT A STYLE CHOICE.\n * `@ai-matrx/browser-audio` publishes **ESM-only export conditions** — its\n * `publishConfig.exports` carry `types`/`import` and no `require` — so a static\n * `import` of it puts `require(\"@ai-matrx/browser-audio/react\")` into this\n * package's CJS build, and every CJS consumer of `@ai-matrx/chat/react` dies at\n * LOAD with `ERR_PACKAGE_PATH_NOT_EXPORTED`, whether or not they ever wanted\n * voice. The packed-tarball canary caught exactly that. Loading it on demand\n * keeps `import()` in both builds (Node supports `import()` inside CJS), so the\n * dependency is paid for only by the host that turns voice on. When\n * browser-audio ships dual conditions this can go back to a static import.\n */\n\nimport { useEffect, useState, type ComponentType, type ReactNode } from \"react\";\nimport type { BrowserAudioKernel } from \"@ai-matrx/browser-audio/core\";\n\n/** What the composer hands a voice implementation on every render. */\nexport interface ChatVoiceContext {\n /** The composer's current text — voice APPENDS to it, never replaces it. */\n value: string;\n onValueChange(next: string): void;\n /** True while a turn is streaming: dictation into a running turn is allowed,\n * but a host may choose to disable its control. */\n busy: boolean;\n /** Called with true while the recorder holds the turn, so the composer can\n * refuse to send half-dictated text. */\n onBusyChange(busy: boolean): void;\n className: string;\n}\n\n/**\n * Two renders, not one: the microphone sits in the composer's action row and\n * the recovery affordance sits under the box, where it can appear and vanish\n * without moving the send button.\n */\nexport interface ChatVoicePort {\n renderButton(context: ChatVoiceContext): ReactNode;\n renderRecovery?(context: ChatVoiceContext): ReactNode;\n}\n\ninterface AudioModule {\n VoiceInputButton: ComponentType<Record<string, unknown>>;\n VoiceRecoveryButton: ComponentType<Record<string, unknown>>;\n}\n\n/** One load per page, shared by every composer on it. */\nlet audioModule: Promise<AudioModule> | null = null;\nfunction loadBrowserAudio(): Promise<AudioModule> {\n audioModule ??= import(\"@ai-matrx/browser-audio/react\") as unknown as Promise<AudioModule>;\n return audioModule;\n}\n\n/**\n * Renders nothing until the recorder module has loaded, and says so out loud if\n * it never does. A microphone that silently fails to appear is the invisible\n * failure Law 4 forbids; this one names the reason.\n */\nfunction LazyAudio(props: {\n pick(module: AudioModule): ComponentType<Record<string, unknown>>;\n componentProps: Record<string, unknown>;\n onError(message: string): void;\n}): ReactNode {\n const [module, setModule] = useState<AudioModule | null>(null);\n const { onError } = props;\n useEffect(() => {\n let live = true;\n loadBrowserAudio().then(\n (loaded) => {\n if (live) setModule(loaded);\n },\n (cause: unknown) => {\n if (!live) return;\n onError(cause instanceof Error ? cause.message : String(cause));\n },\n );\n return () => {\n live = false;\n };\n }, [onError]);\n\n if (module === null) return null;\n const Component = props.pick(module);\n return <Component {...props.componentProps} />;\n}\n\nexport interface BrowserAudioVoicePortOptions {\n label?: string | undefined;\n /** Where a load failure is reported. Defaults to the console. */\n onLoadError?: ((message: string) => void) | undefined;\n}\n\n/** The shipped implementation over `@ai-matrx/browser-audio`. */\nexport function createBrowserAudioVoicePort(\n kernel: BrowserAudioKernel,\n options: BrowserAudioVoicePortOptions = {},\n): ChatVoicePort {\n const onError =\n options.onLoadError ??\n ((message: string) => {\n console.error(\n `[ai-matrx/chat] The microphone could not be loaded, so voice input is unavailable in this chat. — Check that @ai-matrx/browser-audio is installed and reachable from this build. (${message})`,\n );\n });\n\n return {\n renderButton(context) {\n return (\n <LazyAudio\n pick={(module) => module.VoiceInputButton}\n onError={onError}\n componentProps={{\n kernel,\n value: context.value,\n onValueChange: context.onValueChange,\n onBusyChange: context.onBusyChange,\n className: context.className,\n ...(options.label === undefined ? {} : { label: options.label }),\n }}\n />\n );\n },\n renderRecovery(context) {\n // Renders NOTHING when there is nothing to recover — that is the\n // component's own contract, and it is why this is safe to mount always.\n return (\n <LazyAudio\n pick={(module) => module.VoiceRecoveryButton}\n onError={onError}\n componentProps={{\n kernel,\n value: context.value,\n onValueChange: context.onValueChange,\n className: \"mx-chat-composer__recover mx-chat-focusable\",\n }}\n />\n );\n },\n };\n}\n","/**\n * THE `conversationSource` PORT — where the list of conversations comes from.\n *\n * Most hosts want the platform answer: `chat.conversation` rows read under RLS\n * through `@ai-matrx/chat/matrx`. `createPersistenceConversationSource` wraps\n * the persistence port and is the shipped default, so a host that already\n * passes `persistence` gets a working history list with no extra wiring (THE\n * ALL-INCLUSIVE LAW).\n *\n * 🚨 BUT SOME HOSTS' SERVERS NAME THE CONVERSATIONS, and the client must never\n * invent an id. The Workflow Studio is the live example: its launcher derives a\n * BOUNDED LADDER of ids per person × target (`conversation_context/\n * launcher_conversations.py` — generation 0 is the id that already existed,\n * generation N appends `#N` to the same uuid5 seed) and REFUSES any id off that\n * ladder, because a conversation id is an authority: it names whose transcript\n * the agent continues. Such a host supplies its own source, whose `list` returns\n * what the launcher listed and whose `startNew` returns the id the SERVER\n * answered with. Nothing in this package mints one.\n *\n * THE ARCHIVED-ITEMS LAW is in the query's shape, not in a caller's discipline:\n * `archive` is the platform tri-state from\n * `@ai-matrx/design-system`'s `ArchiveFilter`, its default hides archived rows,\n * and the page REPORTS `archivedHidden` so the footer can name what it hid.\n */\n\nimport {\n DEFAULT_ARCHIVE_FILTER,\n type ArchiveFilterValue,\n} from \"@ai-matrx/design-system\";\n\nimport type {\n ChatConversationSummary,\n ChatPersistencePort,\n} from \"../../ports\";\n\nexport interface ChatConversationSourceQuery {\n /** The platform tri-state. `active` (the default) hides archived rows. */\n archive: ArchiveFilterValue;\n /** Free text the reader typed. Only meaningful when the source answers it\n * server-side — see `searchesServerSide`. */\n search?: string | undefined;\n sourceApp?: string | undefined;\n sourceFeature?: string | undefined;\n limit?: number | undefined;\n /** Keyset cursor from a previous page's `nextBefore`. */\n before?: string | null | undefined;\n}\n\nexport interface ChatConversationSourcePage {\n conversations: readonly ChatConversationSummary[];\n /**\n * How many rows this page HID because of the archive filter, so the footer\n * can name them. Under `archive: \"archived\"` it counts the active rows hidden\n * instead — the footer says which, because a number with no noun is a lie\n * waiting to happen.\n */\n archivedHidden: number;\n /** Cursor for the next page, or null when the source reached the end. */\n nextBefore: string | null;\n}\n\n/**\n * WHERE THE CONVERSATIONS COME FROM.\n *\n * An optional method that is ABSENT means the capability does not exist here,\n * and the list renders no control for it. That is the point: a rename button\n * that cannot rename is the dead control Law 4 forbids, and a source is the one\n * place that truth is known.\n */\nexport interface ChatConversationSource {\n /** Diagnostic name — appears in the error sink, never on screen. */\n readonly id: string;\n /**\n * Whether `query.search` reaches the server. False means the reader's text\n * filters only what is already loaded, AND THE LIST SAYS SO.\n */\n readonly searchesServerSide: boolean;\n list(query: ChatConversationSourceQuery): Promise<ChatConversationSourcePage>;\n /**\n * Begin a new conversation and return THE ID THE SOURCE NAMED — the Workflow\n * Studio ladder's next generation, for instance.\n *\n * PRESENCE IS THE CAPABILITY, exactly like `rename` and `setArchived`. A\n * source that does not name ids omits this method, and the shell falls back\n * to the host's `onNewConversation` or, failing that, to the thread's own\n * `startNewConversation()`. With none of the three the New control is ABSENT\n * rather than dead (Law 4).\n */\n startNew?(): Promise<string>;\n rename?(conversationId: string, title: string): Promise<void>;\n setArchived?(conversationId: string, archived: boolean): Promise<void>;\n}\n\n/** How many pages the default source will walk to fill one screen. */\nconst MAX_PAGE_WALK = 8;\nconst DEFAULT_LIMIT = 30;\n\n/**\n * THE SHIPPED DEFAULT — the `chat.*` schema through the persistence port.\n *\n * `listConversations` speaks a BOOLEAN (`includeArchived`) while the platform\n * control speaks THREE states, so \"Archived only\" is assembled here: ask for\n * everything and keep the archived rows, walking the keyset cursor until the\n * page is full. That walk is bounded, and when it stops early it leaves\n * `nextBefore` set — so the reader gets a real \"Load more\" instead of a list\n * that quietly claims to be complete.\n */\nexport function createPersistenceConversationSource(\n persistence: ChatPersistencePort,\n): ChatConversationSource {\n const source: ChatConversationSource = {\n id: \"chat.conversation\",\n // `ChatSelectQuery` has no text predicate, so a title search here would be\n // a client-side sieve dressed as a server query. It says so instead.\n searchesServerSide: false,\n\n async list(query) {\n const limit = query.limit ?? DEFAULT_LIMIT;\n const archive = query.archive ?? DEFAULT_ARCHIVE_FILTER;\n const includeArchived = archive !== \"active\";\n\n const kept: ChatConversationSummary[] = [];\n let hidden = 0;\n let before = query.before ?? undefined;\n let nextBefore: string | null = null;\n\n for (let page = 0; page < MAX_PAGE_WALK; page += 1) {\n const result = await persistence.listConversations({\n limit,\n includeArchived,\n ...(before === undefined || before === null ? {} : { before }),\n ...(query.sourceApp === undefined ? {} : { sourceApp: query.sourceApp }),\n ...(query.sourceFeature === undefined\n ? {}\n : { sourceFeature: query.sourceFeature }),\n });\n\n // Under `active` the port did the hiding and counted it for us.\n hidden += result.archivedHidden;\n\n for (const conversation of result.conversations) {\n if (archive === \"archived\" && !conversation.archived) {\n hidden += 1;\n continue;\n }\n if (kept.length === limit) break;\n kept.push(conversation);\n }\n\n nextBefore = result.nextBefore;\n if (kept.length >= limit || result.nextBefore === null) break;\n before = result.nextBefore;\n }\n\n return { conversations: kept, archivedHidden: hidden, nextBefore };\n },\n\n // NO `startNew`, on purpose: the platform mints a conversation id with the\n // FIRST TURN (`isNewConversation` in the kernel's send), so there is no id\n // to hand back before the reader has said anything — and inventing one here\n // is exactly what a client may never do. The New control still works on\n // this source: the thread's own `startNewConversation()` empties it and\n // forgets its id, and the next turn is what names the conversation.\n\n rename(conversationId, title) {\n return persistence.rename(conversationId, title);\n },\n\n setArchived(conversationId, archived) {\n return persistence.setArchived(conversationId, archived);\n },\n };\n return source;\n}\n","/**\n * SWITCHING CONVERSATIONS — one rule, and it is honest about what it costs.\n *\n * Three things happen, in this order, and none of them is optional:\n *\n * 1. THE HOST MAY REFUSE, IN ITS OWN WORDS. A host knows things this package\n * cannot: the Workflow Studio refuses while an agent's write is waiting for\n * the Creator to decide, because the question that answers it lives in THIS\n * conversation, and walking away would leave the canvas holding a change\n * with nothing on screen asking about it. The guard returns the SENTENCE,\n * not a boolean — a refusal the reader cannot read is a dead control.\n *\n * 2. AN IN-FLIGHT ANSWER IS STOPPED, AND WE SAY WHAT THAT DOES NOT PROMISE.\n * Doctrine: a disconnect never cancels server work. So the notice says the\n * reply stopped arriving here and that work already in motion may still\n * land — it never claims to have cancelled anything (Law 4). A stream in\n * flight is NOT a refusal; readers switch away from a bad turn constantly\n * and blocking that is the worse product.\n *\n * 3. THE NEW TRANSCRIPT IS LOADED, and the id used is one the SOURCE named.\n */\n\nimport type { ChatNotifierPort } from \"../../ports\";\nimport type { ChatThreadStore } from \"../../store\";\n\n/**\n * A host's veto over leaving this conversation right now.\n *\n * Return `null` to allow, or the sentence the reader should see. It is called\n * for a switch AND for starting a new conversation.\n */\nexport type ChatConversationSwitchGuard = (\n target: { conversationId: string | null; isNew: boolean },\n) => string | null;\n\nexport const STREAM_INTERRUPTED_NOTICE =\n \"Stopped that reply here so this conversation could change.\";\nexport const STREAM_INTERRUPTED_REMEDY =\n \"Anything the assistant had already started may still finish and be saved to the conversation you left.\";\n\nexport interface SwitchChatConversationInput {\n thread: ChatThreadStore;\n notifier: ChatNotifierPort;\n /** The conversation to open, or null for a brand-new one. */\n conversationId: string | null;\n guard?: ChatConversationSwitchGuard | undefined;\n /**\n * The host's own handler. Present for a host whose thread store is created\n * per conversation (the ordinary case for a NEW conversation, because the\n * kernel mints its id with the first turn). When it is present it REPLACES\n * the package's `loadConversation` call — the host is swapping the thread.\n */\n onSwitch?: ((conversationId: string | null) => void | Promise<void>) | undefined;\n}\n\nexport interface SwitchChatConversationResult {\n switched: boolean;\n /** The host guard's own sentence, when it refused. */\n refusal: string | null;\n /** True when a running answer was stopped to make the switch. */\n interrupted: boolean;\n}\n\nexport async function switchChatConversation(\n input: SwitchChatConversationInput,\n): Promise<SwitchChatConversationResult> {\n const { thread, notifier, conversationId, guard, onSwitch } = input;\n const isNew = conversationId === null;\n\n const refusal = guard?.({ conversationId, isNew }) ?? null;\n if (refusal !== null) {\n notifier.notify({ level: \"info\", message: refusal });\n return { switched: false, refusal, interrupted: false };\n }\n\n const status = thread.getSnapshot().status;\n const busy = status === \"streaming\" || status === \"submitted\";\n if (busy) {\n await thread.cancel();\n notifier.notify({\n level: \"info\",\n message: STREAM_INTERRUPTED_NOTICE,\n remedy: STREAM_INTERRUPTED_REMEDY,\n });\n }\n\n if (onSwitch !== undefined) {\n await onSwitch(conversationId);\n return { switched: true, refusal: null, interrupted: busy };\n }\n\n // No host handler. An existing conversation loads through the persistence\n // port; a NEW one is started ON THIS STORE when the store can do it — the\n // kernel clears its conversation id and lets the first turn mint the next\n // one, which is why the studio's dock does not have to swap threads to give\n // a reader a New button.\n if (conversationId === null) {\n if (thread.canStartNewConversation) {\n await thread.startNewConversation();\n return { switched: true, refusal: null, interrupted: busy };\n }\n notifier.notify({\n level: \"warning\",\n message: \"A new conversation could not be started here.\",\n remedy:\n \"This chat's state is owned by the application around it. Give it an `onNewConversation` handler, an `onStartNewConversation` on its external store, or a conversationSource whose server names the new conversation.\",\n });\n return { switched: false, refusal: null, interrupted: busy };\n }\n\n await thread.loadConversation(conversationId);\n return { switched: true, refusal: null, interrupted: busy };\n}\n","/**\n * `useChatConversationList()` — the history list's whole logic, with no opinion\n * about pixels. `<ChatConversationList/>` renders it; the four shells arrange\n * it; a host that wants its own list markup uses this hook and keeps every rule\n * below.\n *\n * THE ARCHIVED-ITEMS LAW lives here, not in the component: the filter starts at\n * the platform default (`features.hideArchivedConversations` decides which of\n * `active` / `all` that is), the source hides archived rows in the READ, and\n * `archivedHidden` comes back so the footer can NAME what it hid.\n *\n * DRAFTS: a conversation the reader started typing in and left shows it. The\n * text is the composer's — written by `useChatComposerState` through the\n * `prefs` port — and this hook only READS it. Two writers of one draft is how a\n * draft gets lost.\n */\n\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from \"react\";\n\nimport {\n DEFAULT_ARCHIVE_FILTER,\n type ArchiveFilterValue,\n} from \"@ai-matrx/design-system\";\n\nimport type { ChatConversationSummary } from \"../../ports\";\nimport { useChatConfig, useChatPorts } from \"../hooks\";\nimport { useChatContext } from \"../provider\";\nimport type {\n ChatConversationSource,\n ChatConversationSourcePage,\n} from \"./conversation-source\";\nimport {\n switchChatConversation,\n type ChatConversationSwitchGuard,\n} from \"./switching\";\n\n/**\n * The composer's draft key, restated for the READER of a draft.\n *\n * It is not imported from `../hooks` because that file belongs to no lane and\n * this one does — so the agreement is proven instead of assumed:\n * `react-shells.test.tsx` writes a draft through the REAL\n * `useChatComposerState` and requires this function to find it. Drift turns\n * that test red.\n */\nexport function chatDraftKey(conversationId: string | null): string {\n return `ai-matrx.chat.draft.${conversationId ?? \"new\"}`;\n}\n\nexport interface UseChatConversationListOptions {\n /** Where the conversations come from. */\n source: ChatConversationSource;\n /** Which row is current. Defaults to the thread's own conversation id. */\n activeConversationId?: string | null | undefined;\n /** The host's veto, in the host's own words. */\n guard?: ChatConversationSwitchGuard | undefined;\n /** Host-owned switching (it swaps the thread). Absent ⇒ the package loads\n * the conversation through the persistence port. */\n onSwitch?: ((conversationId: string | null) => void | Promise<void>) | undefined;\n /** Host-owned \"start a new one\" — it hands the chat a fresh thread. */\n onNewConversation?: (() => void | Promise<void>) | undefined;\n pageSize?: number | undefined;\n sourceApp?: string | undefined;\n sourceFeature?: string | undefined;\n}\n\nexport interface ChatConversationRowModel {\n conversation: ChatConversationSummary;\n /** True for the conversation on screen right now. */\n current: boolean;\n /** The unsent words waiting in this conversation, or null. */\n draft: string | null;\n}\n\nexport interface UseChatConversationListResult {\n rows: readonly ChatConversationRowModel[];\n /** How many rows the archive filter hid, for the footer to name. */\n archivedHidden: number;\n /** Which noun `archivedHidden` counts, so the footer never prints a bare\n * number: \"archived\" under the default, \"active\" under \"Archived only\". */\n hiddenNoun: \"archived\" | \"active\";\n loading: boolean;\n /** A read that failed, in the reader's words. Never an empty list pretending\n * the account has no conversations. */\n error: string | null;\n hasMore: boolean;\n archive: ArchiveFilterValue;\n setArchive(next: ArchiveFilterValue): void;\n search: string;\n setSearch(next: string): void;\n /** False when the source cannot search the server: the list says so. */\n searchesServerSide: boolean;\n /** How many loaded rows the reader's text filtered out of view. */\n searchHidden: number;\n refresh(): void;\n loadMore(): void;\n canStartNew: boolean;\n canRename: boolean;\n canArchive: boolean;\n startNew(): Promise<boolean>;\n open(conversationId: string): Promise<boolean>;\n rename(conversationId: string, title: string): Promise<boolean>;\n setArchived(conversationId: string, archived: boolean): Promise<boolean>;\n /** The host guard's sentence from the last refusal, for an inline notice. */\n refusal: string | null;\n dismissRefusal(): void;\n}\n\nconst DEFAULT_PAGE_SIZE = 30;\n\nexport function useChatConversationList(\n options: UseChatConversationListOptions,\n): UseChatConversationListResult {\n const { source, guard, onSwitch, onNewConversation } = options;\n const { thread } = useChatContext();\n const ports = useChatPorts();\n const features = useChatConfig();\n const pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE;\n\n const [archive, setArchiveState] = useState<ArchiveFilterValue>(() =>\n features.hideArchivedConversations ? DEFAULT_ARCHIVE_FILTER : \"all\",\n );\n const [search, setSearch] = useState(\"\");\n const [conversations, setConversations] = useState<ChatConversationSummary[]>([]);\n const [archivedHidden, setArchivedHidden] = useState(0);\n const [nextBefore, setNextBefore] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [refusal, setRefusal] = useState<string | null>(null);\n const [nonce, setNonce] = useState(0);\n const [pending, setPending] = useState<string | null>(null);\n\n // Every read is sequence-guarded: a slow \"Active only\" landing after a fast\n // \"Archived only\" would otherwise repaint the list with the wrong rows.\n const sequence = useRef(0);\n\n /**\n * THE SOURCE AND THE HOST'S CALLBACKS LIVE IN REFS, AND THE LOAD EFFECT DOES\n * NOT DEPEND ON THEM.\n *\n * `<ChatPage history={useChatConversationList({ source: createSource(…) })}/>`\n * is the shape a host will write, and it builds a new source object on every\n * render. If the read effect depended on that identity it would fetch, set\n * state, re-render, build a new source and fetch again — an infinite loop\n * that pins a CPU and hammers the database. The reader's actual inputs\n * (the filter, the text, the page size, the scope) are primitives, and those\n * are what the effect watches.\n */\n const sourceRef = useRef(source);\n sourceRef.current = source;\n const guardRef = useRef(guard);\n guardRef.current = guard;\n const onSwitchRef = useRef(onSwitch);\n onSwitchRef.current = onSwitch;\n const onNewRef = useRef(onNewConversation);\n onNewRef.current = onNewConversation;\n\n const load = useCallback(\n async (before: string | null) => {\n const ticket = (sequence.current += 1);\n setLoading(true);\n try {\n const page: ChatConversationSourcePage = await sourceRef.current.list({\n archive,\n limit: pageSize,\n ...(search === \"\" ? {} : { search }),\n ...(before === null ? {} : { before }),\n ...(options.sourceApp === undefined ? {} : { sourceApp: options.sourceApp }),\n ...(options.sourceFeature === undefined\n ? {}\n : { sourceFeature: options.sourceFeature }),\n });\n if (ticket !== sequence.current) return;\n setConversations((previous) =>\n before === null\n ? [...page.conversations]\n : [...previous, ...page.conversations],\n );\n setArchivedHidden((previous) =>\n before === null ? page.archivedHidden : previous + page.archivedHidden,\n );\n setNextBefore(page.nextBefore);\n setError(null);\n } catch (cause) {\n if (ticket !== sequence.current) return;\n const message =\n cause instanceof Error && cause.message !== \"\"\n ? cause.message\n : \"The list of conversations could not be read.\";\n setError(message);\n ports.notifier.notify({\n level: \"error\",\n message: \"Your earlier conversations could not be loaded.\",\n remedy: \"Try again in a moment; nothing in this conversation is affected.\",\n });\n } finally {\n if (ticket === sequence.current) setLoading(false);\n }\n },\n [archive, search, pageSize, options.sourceApp, options.sourceFeature, ports.notifier],\n );\n\n useEffect(() => {\n void load(null);\n }, [load, nonce]);\n\n const refresh = useCallback(() => setNonce((n) => n + 1), []);\n const loadMore = useCallback(() => {\n if (nextBefore === null) return;\n void load(nextBefore);\n }, [load, nextBefore]);\n\n const setArchive = useCallback((next: ArchiveFilterValue) => {\n setArchiveState(next);\n setConversations([]);\n setArchivedHidden(0);\n setNextBefore(null);\n }, []);\n\n // ONE value off the store, not the whole snapshot: `useChatThread()` here\n // would re-render the sidebar on every coalesced streaming tick, and a\n // conversation list that repaints 60 times a second is the performance bug\n // design §7 rule 2 exists to prevent.\n const readConversationId = useCallback(\n () => thread.getSnapshot().conversationId,\n [thread],\n );\n const threadConversationId = useSyncExternalStore(\n thread.subscribe,\n readConversationId,\n readConversationId,\n );\n const activeConversationId =\n options.activeConversationId === undefined\n ? threadConversationId\n : options.activeConversationId;\n\n const needle = search.trim().toLowerCase();\n const visible = useMemo(() => {\n if (needle === \"\" || source.searchesServerSide) return conversations;\n return conversations.filter((conversation) =>\n (conversation.title ?? \"\").toLowerCase().includes(needle),\n );\n }, [conversations, needle, source.searchesServerSide]);\n\n const rows = useMemo<ChatConversationRowModel[]>(\n () =>\n visible.map((conversation) => {\n const draft = ports.prefs.get(chatDraftKey(conversation.id));\n return {\n conversation,\n current: conversation.id === activeConversationId,\n draft: draft === null || draft === \"\" ? null : draft,\n };\n }),\n // `pending` is in the dependency list so a rename or an archive repaints\n // the drafts too — the prefs port is not subscribable, and a row model that\n // never re-reads it shows yesterday's draft.\n [visible, activeConversationId, ports.prefs, pending],\n );\n\n const switchTo = useCallback(\n async (conversationId: string | null): Promise<boolean> => {\n setPending(conversationId);\n try {\n const result = await switchChatConversation({\n thread,\n notifier: ports.notifier,\n conversationId,\n ...(guardRef.current === undefined ? {} : { guard: guardRef.current }),\n ...(onSwitchRef.current === undefined\n ? {}\n : { onSwitch: onSwitchRef.current }),\n });\n setRefusal(result.refusal);\n return result.switched;\n } finally {\n setPending(null);\n }\n },\n [thread, ports.notifier],\n );\n\n /**\n * THREE WAYS A NEW CONVERSATION CAN EXIST, and the control appears if ANY of\n * them does. In order of authority: the source's server names the id; the\n * host hands the chat a fresh thread; or the thread itself can start one\n * (the kernel, which clears its id and lets the first turn mint the next).\n * None of them is the client inventing an id. With none of the three the\n * control is ABSENT rather than dead.\n */\n const canStartNew =\n source.startNew !== undefined ||\n onNewConversation !== undefined ||\n thread.canStartNewConversation;\n\n const startNew = useCallback(async (): Promise<boolean> => {\n const current = sourceRef.current;\n const onNew = onNewRef.current;\n if (current.startNew !== undefined) {\n const named = await current.startNew();\n const switched = await switchTo(named);\n if (switched) refresh();\n return switched;\n }\n if (onNew === undefined) {\n // Through the ONE switch path, so the host's guard, the stop of an\n // in-flight answer and its notice all still apply to a new conversation.\n const switched = await switchTo(null);\n if (switched) refresh();\n return switched;\n }\n const refused = guardRef.current?.({ conversationId: null, isNew: true }) ?? null;\n if (refused !== null) {\n setRefusal(refused);\n ports.notifier.notify({ level: \"info\", message: refused });\n return false;\n }\n const status = thread.getSnapshot().status;\n if (status === \"streaming\" || status === \"submitted\") await thread.cancel();\n setRefusal(null);\n await onNew();\n refresh();\n return true;\n }, [switchTo, ports.notifier, thread, refresh]);\n\n const open = useCallback(\n async (conversationId: string) => switchTo(conversationId),\n [switchTo],\n );\n\n const rename = useCallback(\n async (conversationId: string, title: string): Promise<boolean> => {\n const trimmed = title.trim();\n const rename_ = sourceRef.current.rename;\n if (rename_ === undefined || trimmed === \"\") return false;\n try {\n await rename_(conversationId, trimmed);\n } catch {\n ports.notifier.notify({\n level: \"error\",\n message: \"That conversation could not be renamed.\",\n remedy: \"Its name is unchanged. Try again in a moment.\",\n });\n return false;\n }\n setConversations((previous) =>\n previous.map((conversation) =>\n conversation.id === conversationId\n ? { ...conversation, title: trimmed }\n : conversation,\n ),\n );\n return true;\n },\n [ports.notifier],\n );\n\n const setArchived = useCallback(\n async (conversationId: string, archived: boolean): Promise<boolean> => {\n const setArchived_ = sourceRef.current.setArchived;\n if (setArchived_ === undefined) return false;\n try {\n await setArchived_(conversationId, archived);\n } catch {\n ports.notifier.notify({\n level: \"error\",\n message: archived\n ? \"That conversation could not be archived.\"\n : \"That conversation could not be brought back.\",\n remedy: \"It is unchanged. Try again in a moment.\",\n });\n return false;\n }\n // Re-read rather than patch in place: whether the row still belongs in\n // this view is the FILTER's answer, and `archivedHidden` must change with\n // it or the footer starts lying.\n refresh();\n return true;\n },\n [ports.notifier, refresh],\n );\n\n return {\n rows,\n archivedHidden,\n hiddenNoun: archive === \"archived\" ? \"active\" : \"archived\",\n loading,\n error,\n hasMore: nextBefore !== null,\n archive,\n setArchive,\n search,\n setSearch,\n searchesServerSide: source.searchesServerSide,\n searchHidden: needle === \"\" ? 0 : conversations.length - visible.length,\n refresh,\n loadMore,\n canStartNew,\n canRename: source.rename !== undefined,\n canArchive: source.setArchived !== undefined,\n startNew,\n open,\n rename,\n setArchived,\n refusal,\n dismissRefusal: useCallback(() => setRefusal(null), []),\n };\n}\n","/**\n * `<ChatConversationList/>` — the history list every shell puts beside the\n * thread.\n *\n * What it shows per row, and why: the TITLE (or the reader's own first words\n * when the row has no title yet — an id is never a name), WHEN it was last\n * touched in relative words, HOW MUCH was said when the source knows, and an\n * unsent DRAFT when one is waiting. That is exactly the line the Workflow\n * Studio's own history popover shows today, and it is the line Claude and\n * ChatGPT show: a person picks a conversation by what it was about and when.\n *\n * THE ARCHIVED-ITEMS LAW, visibly: the platform `ArchiveFilter` sits in the\n * header, the default hides archived rows, revealing them is ONE click, and the\n * FOOTER NAMES WHAT IS HIDDEN — never a bare number, never silence.\n *\n * Every control here is absent or honest (Law 4). Rename and archive render\n * only when the source can do them; \"New conversation\" renders only when\n * something can actually start one.\n */\n\nimport { useEffect, useRef, useState, type ReactNode } from \"react\";\n\nimport { ArchiveFilter, ErrorBox } from \"@ai-matrx/design-system\";\nimport { formatRelativeTime } from \"@ai-matrx/kit/format\";\n\nimport type { ChatConversationSummary } from \"../../ports\";\nimport { useChatSlots } from \"../hooks\";\nimport type {\n ChatConversationRowModel,\n UseChatConversationListResult,\n} from \"./use-conversation-list\";\n\n/**\n * \"4 messages · 2 days ago\" — plain words.\n *\n * A message count of zero is NOT printed as \"0 messages\": a conversation\n * nobody has written in yet says so, because a zero dressed as a count reads as\n * a broken row.\n */\nexport function conversationMetaLine(\n conversation: ChatConversationSummary,\n now?: number,\n): string {\n // \"2 days ago\" in the reader's language — kit's `intl` voice, which is the\n // `Intl.RelativeTimeFormat` body this file used to carry.\n const when = conversation.updatedAt\n ? formatRelativeTime(conversation.updatedAt, { style: \"intl\", now, fallback: \"\" }) || null\n : null;\n const count = conversation.messageCount;\n const said =\n count <= 0 ? \"Nothing said yet\" : count === 1 ? \"1 message\" : `${count} messages`;\n return when === null ? said : `${said} · ${when}`;\n}\n\n/** The title a row shows when the row has none of its own. */\nexport const UNTITLED_CONVERSATION = \"New conversation\";\n\nexport interface ChatConversationListProps {\n list: UseChatConversationListResult;\n /** Accessible name for the list region. */\n label?: string | undefined;\n /** Hide the search box on a surface too narrow for it (the dock at 320px). */\n showSearch?: boolean | undefined;\n className?: string | undefined;\n}\n\nexport function ChatConversationList(props: ChatConversationListProps): ReactNode {\n const { list } = props;\n const slots = useChatSlots();\n const label = props.label ?? \"Your conversations\";\n const showSearch = props.showSearch ?? true;\n\n return (\n <div className={joinClass(\"mx-chat-history\", props.className)}>\n <div className=\"mx-chat-history__header\">\n {slots.conversationListHeader ?? null}\n {list.canStartNew ? (\n <button\n type=\"button\"\n className=\"mx-chat-history__new mx-chat-focusable\"\n onClick={() => void list.startNew()}\n >\n New conversation\n </button>\n ) : null}\n {showSearch ? (\n <input\n type=\"search\"\n className=\"mx-chat-history__search mx-chat-focusable\"\n value={list.search}\n placeholder=\"Search conversations\"\n aria-label=\"Search conversations\"\n onChange={(event) => list.setSearch(event.target.value)}\n />\n ) : null}\n {/* THE ARCHIVED-ITEMS LAW says a list that HIDES archived rows must let\n the reader reveal them in one click. It does not say a surface where\n nothing can ever be archived must carry a tri-state that changes\n nothing — that is the dead control Law 4 forbids, and the Workflow\n Studio's launcher ladder (whose conversations the server names and\n never archives) is the live case that found it. */}\n {list.canArchive ? (\n <ArchiveFilter\n value={list.archive}\n onValueChange={list.setArchive}\n aria-label=\"Show archived conversations\"\n />\n ) : null}\n </div>\n\n {list.refusal !== null ? (\n <p className=\"mx-chat-history__refusal\" role=\"status\">\n {list.refusal}\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n onClick={list.dismissRefusal}\n >\n Dismiss\n </button>\n </p>\n ) : null}\n\n {list.error !== null ? (\n <ErrorBox className=\"mx-chat-history__error\" error={list.error} operation=\"Load earlier conversations\" origin=\"chat/ConversationList\">\n Your earlier conversations could not be loaded.\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n onClick={list.refresh}\n >\n Try again\n </button>\n </ErrorBox>\n ) : null}\n\n <ul className=\"mx-chat-history__list\" aria-label={label} aria-busy={list.loading}>\n {list.rows.map((row) => (\n <ConversationRow key={row.conversation.id} row={row} list={list} />\n ))}\n </ul>\n\n {list.rows.length === 0 && !list.loading && list.error === null ? (\n <p className=\"mx-chat-history__empty\">\n {list.search.trim() === \"\"\n ? \"No conversations here yet.\"\n : \"No conversations match what you typed.\"}\n </p>\n ) : null}\n\n {list.hasMore ? (\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-history__more mx-chat-focusable\"\n onClick={list.loadMore}\n disabled={list.loading}\n >\n {list.loading ? \"Loading…\" : \"Load more\"}\n </button>\n ) : null}\n\n <ConversationListFooter list={list} />\n {slots.conversationListFooter ?? null}\n </div>\n );\n}\n\n/**\n * THE FOOTER THAT NAMES WHAT IS HIDDEN.\n *\n * The law's words: the default hides archived rows, revealing them is one\n * click, and counts describe what the list actually renders. So this line says\n * the number AND the noun AND offers the click — a footer that printed \"3\n * hidden\" would be the screen lying by omission.\n */\nexport function ConversationListFooter({\n list,\n}: {\n list: UseChatConversationListResult;\n}): ReactNode {\n const lines: ReactNode[] = [];\n\n if (list.archivedHidden > 0) {\n const noun = list.hiddenNoun;\n const count = list.archivedHidden;\n lines.push(\n <span key=\"archive\">\n {count === 1\n ? `1 ${noun} conversation is hidden.`\n : `${count} ${noun} conversations are hidden.`}{\" \"}\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n onClick={() => list.setArchive(\"all\")}\n >\n Show them\n </button>\n </span>,\n );\n }\n\n if (!list.searchesServerSide && list.search.trim() !== \"\") {\n lines.push(\n <span key=\"search\">\n Searching only the conversations loaded so far.\n {list.hasMore ? \" Load more to search further.\" : \"\"}\n </span>,\n );\n }\n\n if (lines.length === 0) return null;\n return (\n <p className=\"mx-chat-history__footer\" role=\"note\">\n {lines}\n </p>\n );\n}\n\nfunction ConversationRow({\n row,\n list,\n}: {\n row: ChatConversationRowModel;\n list: UseChatConversationListResult;\n}): ReactNode {\n const slots = useChatSlots();\n const [renaming, setRenaming] = useState(false);\n const conversation = row.conversation;\n const title = conversation.title ?? UNTITLED_CONVERSATION;\n\n const override = slots.conversationRow?.(row);\n if (override !== undefined) return <li className=\"mx-chat-history__row\">{override}</li>;\n\n return (\n <li className=\"mx-chat-history__row\" data-current={row.current}>\n {renaming && list.canRename ? (\n <RenameField\n title={title}\n onCancel={() => setRenaming(false)}\n onCommit={async (next) => {\n await list.rename(conversation.id, next);\n setRenaming(false);\n }}\n />\n ) : (\n <button\n type=\"button\"\n className=\"mx-chat-history__open mx-chat-focusable\"\n aria-current={row.current ? \"true\" : undefined}\n onClick={() => void list.open(conversation.id)}\n >\n <span className=\"mx-chat-history__title\">{title}</span>\n <span className=\"mx-chat-history__meta\">\n {conversationMetaLine(conversation)}\n </span>\n {row.draft !== null ? (\n <span className=\"mx-chat-history__draft\">\n Unsent: {shorten(row.draft)}\n </span>\n ) : null}\n </button>\n )}\n\n <span className=\"mx-chat-history__actions\">\n {list.canRename && !renaming ? (\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n onClick={() => setRenaming(true)}\n >\n Rename\n </button>\n ) : null}\n {list.canArchive ? (\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n onClick={() => void list.setArchived(conversation.id, !conversation.archived)}\n >\n {conversation.archived ? \"Bring back\" : \"Archive\"}\n </button>\n ) : null}\n </span>\n </li>\n );\n}\n\nfunction RenameField({\n title,\n onCommit,\n onCancel,\n}: {\n title: string;\n onCommit: (next: string) => Promise<void>;\n onCancel: () => void;\n}): ReactNode {\n const [value, setValue] = useState(title);\n const ref = useRef<HTMLInputElement>(null);\n useEffect(() => ref.current?.select(), []);\n return (\n <input\n ref={ref}\n className=\"mx-chat-history__rename mx-chat-focusable\"\n value={value}\n aria-label={`Rename ${title}`}\n onChange={(event) => setValue(event.target.value)}\n onBlur={onCancel}\n onKeyDown={(event) => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n void onCommit(value);\n }\n if (event.key === \"Escape\") {\n event.preventDefault();\n onCancel();\n }\n }}\n />\n );\n}\n\nconst DRAFT_PREVIEW = 64;\n\nfunction shorten(text: string): string {\n const flat = text.replace(/\\s+/g, \" \").trim();\n return flat.length <= DRAFT_PREVIEW ? flat : `${flat.slice(0, DRAFT_PREVIEW - 1)}…`;\n}\n\nfunction joinClass(...parts: (string | undefined)[]): string {\n return parts.filter((part) => part !== undefined && part !== \"\").join(\" \");\n}\n","/**\n * `<ChatSurface/>` — THE ONE SURFACE. All four shells render this and differ\n * only in what they put around it.\n *\n * That is the whole reason the shells are cheap: the transcript's\n * accessibility contract, the stick-to-bottom rule, the ephemeral notice and\n * the composer's keyboard behaviour are decided ONCE here, so a docked panel\n * and a phone can never drift into two different chats.\n *\n * 🚨 THE COMPOSER IS A REQUIRED PROP, NOT A STUB.\n * The composer is lane 2B's. A shell that shipped a placeholder box would be a\n * dead control wearing a composer's clothes (Law 4), and a shell that silently\n * rendered no composer would be a chat you cannot type in. So the seam is a\n * required slot: a host passes `<ChatComposer/>` the moment 2B lands, and until\n * then passes its own — and the type system, not a comment, enforces it.\n *\n * `<ChatSurface/>` is also the embedded form of the chat: a host that wants the\n * conversation inside its own page renders this directly, with no shell.\n */\n\nimport type { ReactNode } from \"react\";\n\nimport { ChatMessageList, type ChatMessageListProps } from \"../transcript\";\nimport { useChatSlots } from \"../hooks\";\n\nexport interface ChatSurfaceProps {\n /** Lane 2B's composer. Required — see the note above. */\n composer: ReactNode;\n /** Forwarded to the transcript (windowing, page size, accessible name). */\n transcript?: ChatMessageListProps | undefined;\n /** Full-width, between the shell's header and the transcript. */\n banner?: ReactNode | undefined;\n className?: string | undefined;\n}\n\nexport function ChatSurface(props: ChatSurfaceProps): ReactNode {\n const slots = useChatSlots();\n return (\n <div className={joinClass(\"mx-chat mx-chat-surface\", props.className)}>\n {props.banner ?? slots.surfaceBanner ?? null}\n <ChatMessageList {...(props.transcript ?? {})} />\n <div className=\"mx-chat-surface__composer\">{props.composer}</div>\n </div>\n );\n}\n\nexport function joinClass(...parts: (string | undefined | false)[]): string {\n return parts.filter((part): part is string => typeof part === \"string\" && part !== \"\").join(\" \");\n}\n","/**\n * `<ChatShellHeader/>` — the one header the dock, the window and the phone\n * share.\n *\n * Modelled on the Workflow Studio's `DockShell` header, which is the shape that\n * survived contact with real Creators: an icon, a TITLE that may be a\n * breadcrumb back to where you came from, a subtitle line that the WORKING\n * INDICATOR takes over while the agent is busy, then the host's own actions.\n *\n * The working indicator replacing the subtitle (rather than sitting beside it)\n * is deliberate: two lines that both claim to describe the panel's state is how\n * a header starts contradicting itself, and \"Conductor is working…\" is the more\n * urgent of the two for exactly as long as it is true.\n *\n * 🚨 NO SUBTITLE MEANS ONE ROW, NOT AN EMPTY SECOND LINE. A host that gives\n * the header only a title gets a single compact row: the working indicator\n * moves up BESIDE the title, and the subtitle element is not rendered at all.\n * A blank line held open for a sentence nobody wrote is the height the\n * Workflow Studio's Conductor dock was told to give back to the canvas\n * (2026-09-18).\n */\n\nimport type { ReactNode } from \"react\";\n\nexport interface ChatShellHeaderProps {\n /** Left of the title. A host's own glyph; the package ships none here. */\n icon?: ReactNode | undefined;\n /** The panel's name, or a breadcrumb element that returns somewhere. */\n title?: ReactNode | undefined;\n /** What this panel is about, in the reader's words. */\n subtitle?: ReactNode | undefined;\n /** Host controls: pop out, settings, close. Rendered at the end. */\n actions?: ReactNode | undefined;\n /** True while the agent is working — replaces the subtitle. */\n working?: boolean | undefined;\n workingLabel?: string | undefined;\n /** Makes the header a drag handle (the floating window). */\n dragHandleProps?: Record<string, unknown> | undefined;\n className?: string | undefined;\n}\n\nexport function ChatShellHeader(props: ChatShellHeaderProps): ReactNode {\n const workingLabel = props.workingLabel ?? \"Working…\";\n const hasSubtitleRow = props.subtitle !== undefined;\n const working =\n props.working === true ? (\n <span className=\"mx-chat-shell__working\" role=\"status\">\n <span className=\"mx-chat-shell__pulse\" aria-hidden=\"true\" />\n {workingLabel}\n </span>\n ) : null;\n return (\n <div\n className={joinHeaderClass(\"mx-chat-shell__header\", props.className)}\n data-draggable={props.dragHandleProps !== undefined}\n {...(props.dragHandleProps ?? {})}\n >\n {props.icon === undefined ? null : (\n <span className=\"mx-chat-shell__icon\">{props.icon}</span>\n )}\n <div className=\"mx-chat-shell__titles\">\n {props.title === undefined ? null : (\n <div className=\"mx-chat-shell__title\">\n {props.title}\n {hasSubtitleRow ? null : (\n <span className=\"mx-chat-shell__working-inline\">{working}</span>\n )}\n </div>\n )}\n {hasSubtitleRow ? (\n <div className=\"mx-chat-shell__subtitle\">\n {working ?? props.subtitle}\n </div>\n ) : null}\n </div>\n {props.actions === undefined ? null : (\n <span className=\"mx-chat-shell__actions\">{props.actions}</span>\n )}\n </div>\n );\n}\n\nfunction joinHeaderClass(...parts: (string | undefined)[]): string {\n return parts.filter((part) => part !== undefined && part !== \"\").join(\" \");\n}\n","/**\n * `useDraggablePanel()` — dragging that does not re-render the chat.\n *\n * THE RULE, lifted from the Workflow Studio's own `FloatingChatPanel` because\n * it was paid for there: a pointer move paints through CSS CUSTOM PROPERTIES\n * inside ONE requestAnimationFrame, and the position is committed to React\n * state EXACTLY ONCE, on release. Writing React state per pointer event\n * re-renders the transcript sixty times a second while an answer is streaming\n * into it, which is precisely the budget design §7 sets out to protect.\n *\n * Two details that look like noise and are not:\n * - the paint offsets are CLEARED in a layout effect on the frame that adopts\n * the committed position, so the panel never jumps by the offset twice;\n * - a pending frame is cancelled on unmount, because a callback holding a\n * detached element is a leak that only shows up under a long session.\n */\n\nimport {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type PointerEvent as ReactPointerEvent,\n type RefObject,\n} from \"react\";\n\nexport interface DraggablePanelPosition {\n x: number;\n y: number;\n}\n\nexport interface UseResizableWidthOptions {\n /** The host's stored width. This hook never owns it. */\n width: number;\n min: number;\n max: number;\n /** Which edge the handle sits on — the side the panel grows FROM. */\n edge: \"start\" | \"end\";\n /** Told once, on release, and on every keyboard step. */\n onWidthChange?: ((next: number) => void) | undefined;\n}\n\nexport interface ResizableWidthHandle {\n panelRef: RefObject<HTMLElement | null>;\n resizing: boolean;\n handleProps: {\n role: \"separator\";\n tabIndex: 0;\n \"aria-orientation\": \"vertical\";\n \"aria-valuenow\": number;\n \"aria-valuemin\": number;\n \"aria-valuemax\": number;\n onPointerDown(event: ReactPointerEvent): void;\n onPointerMove(event: ReactPointerEvent): void;\n onPointerUp(event: ReactPointerEvent): void;\n onPointerCancel(event: ReactPointerEvent): void;\n onKeyDown(event: { key: string; preventDefault(): void }): void;\n onDoubleClick(): void;\n };\n}\n\nconst KEYBOARD_STEP_PX = 24;\n\n/**\n * THE WIDTH IS THE HOST'S, ALWAYS.\n *\n * The package never writes a width to storage: which pref store a surface uses,\n * and whether the width is per-user or per-organization, is the host's to know\n * (Law 6 — opinions become knobs, and the knob is theirs). So this hook paints\n * the drag live and hands the final number back through one callback.\n *\n * The keyboard path is not decoration: a separator that only answers a pointer\n * is unusable with a keyboard or a screen reader, and a panel whose size cannot\n * be changed without a mouse is a dead control for the people who need it most.\n */\nexport function useResizableWidth(\n options: UseResizableWidthOptions,\n): ResizableWidthHandle {\n const { width, min, max, edge, onWidthChange } = options;\n const panelRef = useRef<HTMLElement | null>(null);\n const [resizing, setResizing] = useState(false);\n const frameRef = useRef<number | null>(null);\n const dragRef = useRef<{ startX: number; startWidth: number; latest: number } | null>(\n null,\n );\n const initialRef = useRef(width);\n\n const clamp = useCallback(\n (next: number) => Math.max(min, Math.min(max, Math.round(next))),\n [min, max],\n );\n\n const commit = useCallback(\n (next: number) => {\n panelRef.current?.style.removeProperty(\"width\");\n onWidthChange?.(clamp(next));\n },\n [clamp, onWidthChange],\n );\n\n const end = useCallback(() => {\n const drag = dragRef.current;\n if (frameRef.current !== null) {\n globalThis.cancelAnimationFrame?.(frameRef.current);\n frameRef.current = null;\n }\n dragRef.current = null;\n setResizing(false);\n if (drag !== null) commit(drag.latest);\n }, [commit]);\n\n useEffect(\n () => () => {\n if (frameRef.current !== null) globalThis.cancelAnimationFrame?.(frameRef.current);\n },\n [],\n );\n\n return {\n panelRef,\n resizing,\n handleProps: {\n role: \"separator\",\n tabIndex: 0,\n \"aria-orientation\": \"vertical\",\n \"aria-valuenow\": width,\n \"aria-valuemin\": min,\n \"aria-valuemax\": max,\n onPointerDown(event) {\n event.preventDefault();\n dragRef.current = { startX: event.clientX, startWidth: width, latest: width };\n setResizing(true);\n (event.target as HTMLElement).setPointerCapture?.(event.pointerId);\n },\n onPointerMove(event) {\n const drag = dragRef.current;\n if (drag === null) return;\n const delta =\n edge === \"end\" ? event.clientX - drag.startX : drag.startX - event.clientX;\n drag.latest = clamp(drag.startWidth + delta);\n if (frameRef.current !== null) return;\n const schedule = globalThis.requestAnimationFrame;\n const paint = (): void => {\n frameRef.current = null;\n const active = dragRef.current;\n if (active === null || panelRef.current === null) return;\n panelRef.current.style.width = `${active.latest}px`;\n };\n if (typeof schedule === \"function\") frameRef.current = schedule(paint);\n else paint();\n },\n onPointerUp: end,\n onPointerCancel: end,\n onKeyDown(event) {\n const grow = edge === \"end\" ? \"ArrowRight\" : \"ArrowLeft\";\n const shrink = edge === \"end\" ? \"ArrowLeft\" : \"ArrowRight\";\n if (event.key === grow) {\n event.preventDefault();\n commit(width + KEYBOARD_STEP_PX);\n return;\n }\n if (event.key === shrink) {\n event.preventDefault();\n commit(width - KEYBOARD_STEP_PX);\n }\n },\n onDoubleClick() {\n commit(initialRef.current);\n },\n },\n };\n}\n\nexport interface UseDraggablePanelOptions {\n initial: DraggablePanelPosition;\n /** Keep this much of the panel on screen on every edge. */\n keepVisiblePx?: number | undefined;\n /** Told once per drag, on release — a host that persists the position. */\n onCommit?: ((position: DraggablePanelPosition) => void) | undefined;\n}\n\nexport interface DraggablePanelHandle {\n position: DraggablePanelPosition;\n /** Attach to the element that MOVES. */\n panelRef: RefObject<HTMLElement | null>;\n /** Spread onto the element you drag BY (the header). */\n dragHandleProps: {\n onPointerDown(event: ReactPointerEvent): void;\n onPointerMove(event: ReactPointerEvent): void;\n onPointerUp(event: ReactPointerEvent): void;\n onPointerCancel(event: ReactPointerEvent): void;\n };\n /** True while a pointer is down — a shell suppresses transitions with it. */\n dragging: boolean;\n}\n\nconst DEFAULT_KEEP_VISIBLE = 64;\n\nexport function useDraggablePanel(\n options: UseDraggablePanelOptions,\n): DraggablePanelHandle {\n const keepVisible = options.keepVisiblePx ?? DEFAULT_KEEP_VISIBLE;\n const [position, setPosition] = useState<DraggablePanelPosition>(options.initial);\n const [dragging, setDragging] = useState(false);\n const panelRef = useRef<HTMLElement | null>(null);\n const frameRef = useRef<number | null>(null);\n const adoptingRef = useRef(false);\n const dragRef = useRef<{\n dx: number;\n dy: number;\n origin: DraggablePanelPosition;\n latest: DraggablePanelPosition;\n } | null>(null);\n\n const clamp = useCallback(\n (next: DraggablePanelPosition): DraggablePanelPosition => {\n const width = globalThis.innerWidth ?? 0;\n const height = globalThis.innerHeight ?? 0;\n if (width === 0 || height === 0) return next;\n return {\n x: Math.max(0, Math.min(width - keepVisible, next.x)),\n y: Math.max(0, Math.min(height - keepVisible, next.y)),\n };\n },\n [keepVisible],\n );\n\n const onPointerDown = useCallback(\n (event: ReactPointerEvent) => {\n // A control inside the header is a control, not a drag handle.\n if ((event.target as HTMLElement).closest(\"button,a,input,select,textarea\")) return;\n event.preventDefault();\n dragRef.current = {\n dx: event.clientX - position.x,\n dy: event.clientY - position.y,\n origin: position,\n latest: position,\n };\n setDragging(true);\n (event.target as HTMLElement).setPointerCapture?.(event.pointerId);\n },\n [position],\n );\n\n const onPointerMove = useCallback(\n (event: ReactPointerEvent) => {\n const drag = dragRef.current;\n if (drag === null) return;\n drag.latest = clamp({ x: event.clientX - drag.dx, y: event.clientY - drag.dy });\n if (frameRef.current !== null) return;\n const schedule = globalThis.requestAnimationFrame;\n const paint = (): void => {\n frameRef.current = null;\n const active = dragRef.current;\n const element = panelRef.current;\n if (active === null || element === null) return;\n element.style.setProperty(\"--mx-chat-panel-dx\", `${active.latest.x - active.origin.x}px`);\n element.style.setProperty(\"--mx-chat-panel-dy\", `${active.latest.y - active.origin.y}px`);\n };\n if (typeof schedule === \"function\") frameRef.current = schedule(paint);\n else paint();\n },\n [clamp],\n );\n\n const end = useCallback(() => {\n const drag = dragRef.current;\n if (frameRef.current !== null) {\n globalThis.cancelAnimationFrame?.(frameRef.current);\n frameRef.current = null;\n }\n dragRef.current = null;\n setDragging(false);\n if (drag === null) return;\n adoptingRef.current = true;\n setPosition(drag.latest);\n options.onCommit?.(drag.latest);\n }, [options]);\n\n useLayoutEffect(() => {\n if (!adoptingRef.current) return;\n adoptingRef.current = false;\n panelRef.current?.style.removeProperty(\"--mx-chat-panel-dx\");\n panelRef.current?.style.removeProperty(\"--mx-chat-panel-dy\");\n });\n\n useEffect(\n () => () => {\n if (frameRef.current !== null) globalThis.cancelAnimationFrame?.(frameRef.current);\n },\n [],\n );\n\n return {\n position,\n panelRef,\n dragging,\n dragHandleProps: {\n onPointerDown,\n onPointerMove,\n onPointerUp: end,\n onPointerCancel: end,\n },\n };\n}\n","/**\n * `<ChatPage/>` — the full-page chat: the conversation list beside the thread.\n *\n * The sidebar is REQUIRED here, because that is what makes this shell a page\n * rather than a bare surface: a `/chat` route with no way to reach yesterday's\n * conversation is a dead end. A host that wants only the thread renders\n * `<ChatSurface/>`.\n *\n * Below `--mx-chat-breakpoint` the sidebar leaves the row and becomes a\n * disclosure above the transcript — the same rows, one tap away, never a\n * squeezed 120px column. `<ChatMobile/>` is the deliberate phone shell; this\n * collapse is what keeps the desktop page honest when the window is narrow.\n */\n\nimport { useState, type ReactNode } from \"react\";\n\nimport { ChatConversationList } from \"./conversation-list\";\nimport { ChatShellHeader, type ChatShellHeaderProps } from \"./shell-header\";\nimport { ChatSurface, joinClass, type ChatSurfaceProps } from \"./surface\";\nimport type { UseChatConversationListResult } from \"./use-conversation-list\";\n\nexport interface ChatPageProps extends ChatSurfaceProps {\n /** The history sidebar's state, from `useChatConversationList()`. */\n history: UseChatConversationListResult;\n header?: ChatShellHeaderProps | undefined;\n /** Accessible name for the page region. */\n label?: string | undefined;\n}\n\nexport function ChatPage(props: ChatPageProps): ReactNode {\n const { history, header, label, className, ...surface } = props;\n const [listOpen, setListOpen] = useState(false);\n\n return (\n <div\n className={joinClass(\"mx-chat-shell mx-chat-shell--page\", className)}\n data-shell=\"page\"\n role=\"region\"\n aria-label={label ?? \"Chat\"}\n >\n {header === undefined ? null : <ChatShellHeader {...header} />}\n <div className=\"mx-chat-shell__row\">\n <aside className=\"mx-chat-shell__sidebar\" aria-label=\"Your conversations\">\n <ChatConversationList list={history} />\n </aside>\n <div className=\"mx-chat-shell__narrow-history\">\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n aria-expanded={listOpen}\n onClick={() => setListOpen((open) => !open)}\n >\n {listOpen ? \"Hide conversations\" : \"Your conversations\"}\n </button>\n {listOpen ? <ChatConversationList list={history} /> : null}\n </div>\n <ChatSurface {...surface} className=\"mx-chat-shell__main\" />\n </div>\n </div>\n );\n}\n","/**\n * `<ChatDock/>` — the chat docked to one side of a working surface.\n *\n * THIS IS THE SHELL THE WORKFLOW STUDIO ADOPTS (design §9, consumer #1). It is\n * modelled directly on that app's `DockShell`, and the pieces it takes are the\n * pieces that app actually passes today:\n *\n * • a breadcrumb TITLE, because the dock's occupant changes — the Conductor,\n * then a step's Steward, with a way back;\n * • an ACTIONS slot for pop-out and close;\n * • a WORKING indicator, because a Creator watching a canvas needs to know\n * the agent is mid-turn without looking at the transcript;\n * • a HOST-OWNED WIDTH: the Studio stores it in its own `ws.layout.v1` blob\n * alongside the inspector's width, and the canvas keeps a minimum — so the\n * package paints the drag and hands back ONE number. It never stores a\n * width, and it never decides the maximum, because only the host knows what\n * else is in the row.\n *\n * The history control lives in the header as a disclosure rather than a second\n * column: a 360px dock has no room for a sidebar, and the Studio's own answer\n * (a small button that opens the list) is the right one.\n */\n\nimport { useId, useState, type ReactNode } from \"react\";\n\nimport { ChatConversationList } from \"./conversation-list\";\nimport { useResizableWidth } from \"./drag\";\nimport { ChatShellHeader, type ChatShellHeaderProps } from \"./shell-header\";\nimport { ChatSurface, joinClass, type ChatSurfaceProps } from \"./surface\";\nimport type { UseChatConversationListResult } from \"./use-conversation-list\";\n\nexport const CHAT_DOCK_MIN_WIDTH = 320;\nexport const CHAT_DOCK_MAX_WIDTH = 720;\n\nexport interface ChatDockProps extends ChatSurfaceProps {\n header?: ChatShellHeaderProps | undefined;\n /** Present ⇒ the header carries \"Conversations\" and the list opens under it. */\n history?: UseChatConversationListResult | undefined;\n /** Which side of the row the dock sits on. Decides the resize edge. */\n side?: \"start\" | \"end\" | undefined;\n /** The host's stored width, in pixels. The package never stores one. */\n width?: number | undefined;\n /** Called ONCE per resize gesture, and on each keyboard step. */\n onWidthChange?: ((next: number) => void) | undefined;\n minWidth?: number | undefined;\n maxWidth?: number | undefined;\n /** Accessible name for the dock. */\n label?: string | undefined;\n}\n\nexport function ChatDock(props: ChatDockProps): ReactNode {\n const {\n header,\n history,\n side = \"start\",\n width = CHAT_DOCK_MIN_WIDTH,\n onWidthChange,\n minWidth = CHAT_DOCK_MIN_WIDTH,\n maxWidth = CHAT_DOCK_MAX_WIDTH,\n label,\n className,\n ...surface\n } = props;\n\n const [historyOpen, setHistoryOpen] = useState(false);\n const historyId = useId();\n const resize = useResizableWidth({\n width,\n min: minWidth,\n max: maxWidth,\n edge: side === \"start\" ? \"end\" : \"start\",\n ...(onWidthChange === undefined ? {} : { onWidthChange }),\n });\n\n const historyToggle =\n history === undefined ? null : (\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n aria-expanded={historyOpen}\n aria-controls={historyId}\n onClick={() => setHistoryOpen((open) => !open)}\n >\n Conversations\n </button>\n );\n\n return (\n <aside\n ref={resize.panelRef as React.RefObject<HTMLElement>}\n className={joinClass(\"mx-chat-shell mx-chat-shell--dock\", className)}\n data-shell=\"dock\"\n data-side={side}\n data-resizing={resize.resizing}\n style={{ width }}\n role=\"complementary\"\n aria-label={label ?? \"Chat\"}\n >\n {onWidthChange === undefined ? null : (\n // ABSENT, NEVER DEAD: a host that does not own a width gets no handle\n // rather than one that drags and springs back (Law 4).\n <div\n className=\"mx-chat-shell__resize mx-chat-focusable\"\n aria-label=\"Resize the chat panel\"\n {...resize.handleProps}\n />\n )}\n <ChatShellHeader\n {...(header ?? {})}\n actions={\n <>\n {historyToggle}\n {header?.actions ?? null}\n </>\n }\n />\n {history !== undefined && historyOpen ? (\n <div className=\"mx-chat-shell__history\" id={historyId}>\n <ChatConversationList list={history} showSearch={false} />\n </div>\n ) : null}\n <ChatSurface {...surface} className=\"mx-chat-shell__main\" />\n </aside>\n );\n}\n","/**\n * `<ChatWindow/>` — the floating, draggable chat over a host's own screen.\n *\n * Two rules it exists to keep, both learned in the Workflow Studio:\n *\n * 1. DRAGGING NEVER RE-RENDERS THE CHAT. The header is the handle,\n * `useDraggablePanel` paints the move through CSS variables on one\n * animation frame, and the position is committed once on release. A long\n * drag over a streaming answer costs zero React renders.\n *\n * 2. THE Z-LAYER IS A TOKEN, NEVER A NUMBER HERE. `--mx-chat-z-window` is\n * declared in `tokens.css` and a host RE-DECLARES it to place this window\n * in its own stacking order. That is not a style preference: the Studio's\n * issue panel at z-50 once swallowed the pop-out chat's own dock button,\n * and a package that hardcodes a z-index guarantees that class of bug in\n * every consumer. A chat window must never cover the host's controls.\n */\n\nimport { useId, useState, type ReactNode } from \"react\";\n\nimport { ChatConversationList } from \"./conversation-list\";\nimport { useDraggablePanel, type DraggablePanelPosition } from \"./drag\";\nimport { ChatShellHeader, type ChatShellHeaderProps } from \"./shell-header\";\nimport { ChatSurface, joinClass, type ChatSurfaceProps } from \"./surface\";\nimport type { UseChatConversationListResult } from \"./use-conversation-list\";\n\nexport const CHAT_WINDOW_WIDTH = 400;\n\nexport interface ChatWindowProps extends ChatSurfaceProps {\n header?: ChatShellHeaderProps | undefined;\n /**\n * Present ⇒ the header carries \"Conversations\" and the list opens under it,\n * exactly as in `<ChatDock/>`.\n *\n * 🚨 It is here because a floating chat and a docked one are usually the SAME\n * conversation seen two ways, and a reader who pops the panel out does not\n * expect their history to disappear with the dock. The Workflow Studio found\n * that on a live review: its floating Steward had no controls at all while\n * the docked one did.\n */\n history?: UseChatConversationListResult | undefined;\n /** Where it opens. Defaults to the top-right, inset from the edge. */\n initialPosition?: DraggablePanelPosition | undefined;\n width?: number | undefined;\n /** Told once per drag — a host that remembers where the reader left it. */\n onPositionCommit?: ((position: DraggablePanelPosition) => void) | undefined;\n label?: string | undefined;\n}\n\nexport function ChatWindow(props: ChatWindowProps): ReactNode {\n const {\n header,\n history,\n initialPosition,\n width = CHAT_WINDOW_WIDTH,\n onPositionCommit,\n label,\n className,\n ...surface\n } = props;\n\n const [fallback] = useState<DraggablePanelPosition>(() => ({\n x: Math.max(8, (globalThis.innerWidth ?? width + 32) - width - 24),\n y: 96,\n }));\n\n const [historyOpen, setHistoryOpen] = useState(false);\n const historyId = useId();\n const historyToggle =\n history === undefined ? null : (\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n aria-expanded={historyOpen}\n aria-controls={historyId}\n // A drag handle that also holds buttons must not start a drag from one.\n onPointerDown={(event) => event.stopPropagation()}\n onClick={() => setHistoryOpen((open) => !open)}\n >\n Conversations\n </button>\n );\n\n const drag = useDraggablePanel({\n initial: initialPosition ?? fallback,\n ...(onPositionCommit === undefined ? {} : { onCommit: onPositionCommit }),\n });\n\n return (\n <div\n ref={drag.panelRef as React.RefObject<HTMLDivElement>}\n className={joinClass(\"mx-chat-shell mx-chat-shell--window\", className)}\n data-shell=\"window\"\n data-dragging={drag.dragging}\n role=\"dialog\"\n aria-label={label ?? \"Chat\"}\n style={{ left: drag.position.x, top: drag.position.y, width }}\n >\n <ChatShellHeader\n {...(header ?? {})}\n dragHandleProps={drag.dragHandleProps}\n actions={\n <>\n {historyToggle}\n {header?.actions ?? null}\n </>\n }\n />\n {history !== undefined && historyOpen ? (\n <div className=\"mx-chat-shell__history\" id={historyId}>\n <ChatConversationList list={history} showSearch={false} />\n </div>\n ) : null}\n <ChatSurface {...surface} className=\"mx-chat-shell__main\" />\n </div>\n );\n}\n","/**\n * `<ChatMobile/>` — the phone shell: a full-bleed stack, with history as a\n * sheet.\n *\n * Why a sheet and not a column: at 375px a sidebar is either a squeezed strip\n * nobody can read a title in, or it takes the whole screen and the thread is\n * gone. Every phone chat worth copying — Claude, ChatGPT, Messages — puts the\n * conversation list behind one tap and gives the whole width to the thread.\n *\n * Three things this shell owns that the desktop ones do not:\n * • the composer sits above the SAFE-AREA INSET, so the send control is not\n * under the home indicator;\n * • the sheet traps nothing and closes on Escape and on a tap outside, because\n * a sheet you cannot leave is the dead end Law 4 forbids;\n * • opening a conversation CLOSES the sheet, since the reader's next act is\n * reading it.\n */\n\nimport { useCallback, useEffect, type ReactNode } from \"react\";\n\nimport { ChatConversationList } from \"./conversation-list\";\nimport { ChatShellHeader, type ChatShellHeaderProps } from \"./shell-header\";\nimport { ChatSurface, joinClass, type ChatSurfaceProps } from \"./surface\";\nimport type { UseChatConversationListResult } from \"./use-conversation-list\";\n\nexport interface ChatMobileProps extends ChatSurfaceProps {\n header?: ChatShellHeaderProps | undefined;\n history?: UseChatConversationListResult | undefined;\n /** The sheet is the HOST's state, so a back gesture or a route can close it. */\n historyOpen?: boolean | undefined;\n onHistoryOpenChange?: ((open: boolean) => void) | undefined;\n label?: string | undefined;\n}\n\nexport function ChatMobile(props: ChatMobileProps): ReactNode {\n const {\n header,\n history,\n historyOpen = false,\n onHistoryOpenChange,\n label,\n className,\n ...surface\n } = props;\n\n const close = useCallback(\n () => onHistoryOpenChange?.(false),\n [onHistoryOpenChange],\n );\n\n useEffect(() => {\n if (!historyOpen) return;\n const onKey = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") close();\n };\n globalThis.addEventListener?.(\"keydown\", onKey);\n return () => globalThis.removeEventListener?.(\"keydown\", onKey);\n }, [historyOpen, close]);\n\n const canOpenHistory = history !== undefined && onHistoryOpenChange !== undefined;\n\n return (\n <div\n className={joinClass(\"mx-chat-shell mx-chat-shell--mobile\", className)}\n data-shell=\"mobile\"\n role=\"region\"\n aria-label={label ?? \"Chat\"}\n >\n <ChatShellHeader\n {...(header ?? {})}\n actions={\n <>\n {canOpenHistory ? (\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n aria-expanded={historyOpen}\n onClick={() => onHistoryOpenChange?.(!historyOpen)}\n >\n Conversations\n </button>\n ) : null}\n {header?.actions ?? null}\n </>\n }\n />\n <ChatSurface {...surface} className=\"mx-chat-shell__main\" />\n\n {canOpenHistory && historyOpen ? (\n <>\n <div\n className=\"mx-chat-shell__scrim\"\n onClick={close}\n aria-hidden=\"true\"\n />\n <div\n className=\"mx-chat-shell__sheet\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Your conversations\"\n >\n <div className=\"mx-chat-shell__sheet-grip\" aria-hidden=\"true\" />\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable mx-chat-shell__sheet-close\"\n onClick={close}\n >\n Close\n </button>\n <ChatConversationList\n list={wrapClosingOnOpen(history, close)}\n label=\"Your conversations\"\n />\n </div>\n </>\n ) : null}\n </div>\n );\n}\n\n/**\n * Opening a conversation closes the sheet — but ONLY when the switch actually\n * happened. A host guard that refuses (an unanswered question on the screen\n * behind) leaves the sheet open, with the refusal visible in it, because\n * closing the sheet would hide the sentence explaining why nothing moved.\n */\nfunction wrapClosingOnOpen(\n list: UseChatConversationListResult,\n close: () => void,\n): UseChatConversationListResult {\n return {\n ...list,\n async open(conversationId: string) {\n const switched = await list.open(conversationId);\n if (switched) close();\n return switched;\n },\n async startNew() {\n const started = await list.startNew();\n if (started) close();\n return started;\n },\n };\n}\n","/**\n * PHASE 4, CAPABILITY 3 + 4 — THE PICKERS, AS SLOTS AND NOTHING ELSE.\n *\n * 🚨 THIS PACKAGE NEVER FETCHES AN AGENT LIST. Arman ruled on 2026-09-08 that\n * the agent picker lives in `@ai-matrx/agents` and that the host receives an\n * id; there is ONE picker on the platform and a chat package that grew a\n * second would be the fifth agent selector in the fleet, which is the exact\n * failure this campaign exists to end. So this file MOUNTS\n * `@ai-matrx/agents/catalog/react`'s `AgentListDropdown` over the catalog the\n * HOST already created and provided, and does nothing else: no `agx_get_list_full`\n * read, no `agx_search`, no membership rule, no ordering, no cache.\n * `pnpm check:canonical-pickers` fails this package's build if that changes.\n *\n * 🚨 WHY THE IMPORT IS LAZY. Same reason as the voice port, plus one of its\n * own. The catalog UI is a popover, a drawer, tabs, sorts, categories, tags and\n * a detail card — a real screen — and a host that never shows a picker should\n * not pay for it in the bundle it got for a transcript. Loading it on demand\n * also keeps `import()` (not `require`) in the CJS build, which is what stopped\n * `ERR_PACKAGE_PATH_NOT_EXPORTED` killing every CJS consumer of `./react` when\n * browser-audio did this. The module is loaded ONCE per page and shared.\n *\n * THE MODEL PICKER IS A PORT, NOT A COMPONENT (design §5.1 capability 4).\n * `ModelListDropdown` has not moved into `agents/catalog/react` yet, so the\n * package would have to invent a roster to render one — which is precisely the\n * fifth picker. Until that lane lands the host renders its own through\n * `ports.modelPicker`, and a host that has none renders NOTHING: absent, never\n * a dead control (Law 4).\n */\n\nimport {\n useEffect,\n useState,\n type ComponentType,\n type ReactNode,\n} from \"react\";\n\nimport { useChatPorts } from \"../hooks\";\n\ninterface CatalogModule {\n AgentListDropdown: ComponentType<Record<string, unknown>>;\n}\n\n/** One load per page, shared by every picker on it. */\nlet catalogModule: Promise<CatalogModule> | null = null;\nfunction loadAgentCatalog(): Promise<CatalogModule> {\n catalogModule ??= import(\"@ai-matrx/agents/catalog/react\") as unknown as Promise<CatalogModule>;\n return catalogModule;\n}\n\n/** Test seam ONLY: the suites replace the real catalog with a stand-in so a\n * render test does not have to mount a Radix popover to prove a slot. It is\n * not exported from the package barrel and a host has no use for it. */\nexport function __setAgentCatalogModuleForTests(\n module: CatalogModule | Promise<CatalogModule> | null,\n): void {\n if (module === null) {\n catalogModule = null;\n return;\n }\n catalogModule = module instanceof Promise ? module : Promise.resolve(module);\n}\n\nexport const AGENT_CATALOG_UNAVAILABLE =\n \"The list of agents could not be opened.\";\nexport const AGENT_CATALOG_UNAVAILABLE_REMEDY =\n \"Reload the page and try again; if it keeps happening, the agent directory is not reachable from here.\";\n\nexport interface ChatAgentPickerProps {\n /**\n * The agent this conversation is already pointed at, so the list can pin and\n * highlight it. `null` is a real answer — a thread that has not chosen yet.\n */\n activeAgentId?: string | null | undefined;\n /**\n * The chosen reference: an agent id, or `mandate:<key>` for the platform\n * default row. THE HOST DECIDES WHAT THAT MEANS — this package does not hold\n * the selection, because the selection is the host's to persist and to send.\n */\n onSelect(agentRef: string): void;\n /** The trigger's words. Defaults to the catalog's own. */\n label?: string | undefined;\n /** Offer the platform default agent for this mandate at the top of the list. */\n defaultMandateKey?: string | undefined;\n /** The short (h-5) trigger, for a dense composer row. */\n compact?: boolean | undefined;\n className?: string | undefined;\n}\n\n/**\n * The agent picker, for `slots.composerPickers`.\n *\n * It must be rendered inside the host's `AgentCatalogProvider` — that provider\n * IS the catalog, and it is the host's because the host owns the session the\n * list is read with. Without one the dropdown throws its own named error, which\n * is the honest outcome: a picker with no catalog is not a picker.\n */\nexport function ChatAgentPicker(props: ChatAgentPickerProps): ReactNode {\n const { notifier } = useChatPorts();\n const [module, setModule] = useState<CatalogModule | null>(null);\n const [failed, setFailed] = useState(false);\n\n useEffect(() => {\n let live = true;\n loadAgentCatalog().then(\n (loaded) => {\n if (live) setModule(loaded);\n },\n () => {\n if (!live) return;\n setFailed(true);\n notifier.notify({\n level: \"error\",\n message: AGENT_CATALOG_UNAVAILABLE,\n remedy: AGENT_CATALOG_UNAVAILABLE_REMEDY,\n });\n },\n );\n return () => {\n live = false;\n };\n }, [notifier]);\n\n // A picker that never loads SAYS SO rather than leaving a hole where the\n // reader expects to choose who answers.\n if (failed) {\n return (\n <span className=\"mx-chat-standin\" role=\"status\">\n {`${AGENT_CATALOG_UNAVAILABLE} ${AGENT_CATALOG_UNAVAILABLE_REMEDY}`}\n </span>\n );\n }\n if (module === null) return null;\n\n const Dropdown = module.AgentListDropdown;\n return (\n <Dropdown\n onSelect={props.onSelect}\n activeAgentId={props.activeAgentId ?? null}\n compact={props.compact ?? true}\n noBorder\n {...(props.label === undefined ? {} : { label: props.label })}\n {...(props.defaultMandateKey === undefined\n ? {}\n : { defaultMandateKey: props.defaultMandateKey })}\n {...(props.className === undefined ? {} : { className: props.className })}\n />\n );\n}\n\nexport interface ChatModelPickerProps {\n className?: string | undefined;\n}\n\n/**\n * The model picker, rendered by the HOST through `ports.modelPicker`.\n *\n * `null` is a real answer (\"this surface does not choose a model\"), and it\n * renders nothing at all — never a disabled control, never a stand-in sentence\n * for a capability the surface deliberately does not have.\n */\nexport function ChatModelPicker(props: ChatModelPickerProps): ReactNode {\n const { modelPicker } = useChatPorts();\n if (modelPicker === null) return null;\n return modelPicker({\n className: props.className ?? \"mx-chat-plugin-picker\",\n });\n}\n\nexport interface ChatComposerPickersProps extends ChatAgentPickerProps {\n /** Leave the agent picker out — a surface whose brain is a Mandate, not a\n * chosen agent, renders NOTHING here rather than a picker that would lie\n * about what answering it. */\n agentPicker?: boolean | undefined;\n}\n\n/**\n * Both pickers in the order a composer reads left to right: who answers, then\n * what they think with. Drop it into `slots.composerPickers`.\n */\nexport function ChatComposerPickers(props: ChatComposerPickersProps): ReactNode {\n const { agentPicker = true, ...agent } = props;\n return (\n <span className=\"mx-chat-plugin-pickers\">\n {agentPicker ? <ChatAgentPicker {...agent} /> : null}\n <ChatModelPicker />\n </span>\n );\n}\n"],"mappings":"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","names":["part","frame","frame","useEffect","jsx","jsxs","jsx","notifier","useEffect","useEffect","useMemo","useRef","useState","useMemo","useState","useRef","useEffect","jsx","jsxs","jsx","useEffect","useMemo","useRef","useState","useCallback","useEffect","useRef","useState","useCallback","useEffect","useRef","useState","jsx","jsxs","useState","useRef","useEffect","useCallback","message","useState","useState","Fragment","jsx","jsxs","useState","beat","Fragment","jsx","jsxs","useState","jsx","jsxs","ChatMessageTurn","jsx","jsxs","useState","useMemo","useEffect","useState","useRef","useEffect","useCallback","useEffect","useRef","useState","formatFileSize","jsx","jsx","jsxs","formatFileSize","jsx","jsxs","createContext","useContext","useMemo","jsx","createContext","useMemo","useContext","useCallback","useRef","jsx","useCallback","useEffect","useRef","useState","useState","useRef","useEffect","useCallback","jsx","jsxs","useRef","useState","useEffect","useCallback","useEffect","useState","jsx","useCallback","useEffect","useMemo","useRef","useState","useSyncExternalStore","DEFAULT_ARCHIVE_FILTER","useState","DEFAULT_ARCHIVE_FILTER","useRef","useCallback","useEffect","useSyncExternalStore","useMemo","useEffect","useRef","useState","ErrorBox","jsx","jsxs","joinClass","ErrorBox","useState","useRef","useEffect","jsx","jsxs","joinClass","jsx","jsxs","useCallback","useEffect","useLayoutEffect","useRef","useState","useState","jsx","jsxs","useState","joinClass","useId","useState","Fragment","jsx","jsxs","useState","useId","joinClass","useId","useState","Fragment","jsx","jsxs","useState","useId","joinClass","useCallback","useEffect","Fragment","jsx","jsxs","useCallback","useEffect","joinClass","useEffect","useState","jsx","jsxs","useState","useEffect","jsx","jsxs"]}
|
|
1
|
+
{"version":3,"sources":["../src/compat/model.ts","../src/compat/tool-state.ts","../src/compat/config.ts","../src/compat/ingest.ts","../src/compat/ports.ts","../src/compat/store.ts","../src/compat/react/provider.tsx","../src/compat/react/composer/content-default.tsx","../src/compat/react/composer/markdown.tsx","../src/compat/react/composer/files.ts","../src/compat/react/ports.ts","../src/compat/react/hooks.ts","../src/compat/react/content.tsx","../src/compat/react/icons.tsx","../src/compat/react/transcript/message-list.tsx","../src/compat/react/transcript/auto-scroll.ts","../src/compat/react/transcript/message-turn.tsx","../src/compat/react/plugins/message-actions.tsx","../src/compat/react/plain-text.ts","../src/compat/react/transcript/parts.tsx","../src/compat/react/transcript/tool-chip.tsx","../src/compat/react/composer/composer.tsx","../src/compat/react/composer/attachment-chips.tsx","../src/compat/react/composer/glyphs.tsx","../src/compat/react/plugins/run-controls.tsx","../src/compat/react/composer/provider.tsx","../src/compat/react/composer/send-intent.ts","../src/compat/react/composer/textarea.tsx","../src/compat/react/composer/use-attachments.ts","../src/compat/react/composer/voice-port.tsx","../src/compat/react/shells/conversation-source.ts","../src/compat/react/shells/switching.ts","../src/compat/react/shells/use-conversation-list.ts","../src/compat/react/shells/conversation-list.tsx","../src/compat/react/shells/surface.tsx","../src/compat/react/shells/shell-header.tsx","../src/ui/chat-panel/drag.ts","../src/compat/react/shells/shell-page.tsx","../src/compat/react/shells/shell-dock.tsx","../src/compat/react/shells/shell-window.tsx","../src/compat/react/shells/shell-mobile.tsx","../src/compat/react/plugins/pickers.tsx"],"sourcesContent":["/**\n * The message / part model — layer 1 of `@ai-matrx/chat`.\n *\n * Deliberately the industry-canonical shape (Vercel AI SDK `UIMessage.parts[]`\n * plus its tool-part state machine), because our own\n * `@ai-matrx/agents/projection/request` already produces its equivalent and a\n * customer's agent already knows it. Nothing here imports React, Redux, a\n * framework, or `window`.\n *\n * Every part type below was chosen against BYTES, not against a guess: the\n * live corpus in `fixtures/corpus/` carries text, reasoning (both wire\n * shapes), tool lifecycle, warnings and a fatal error, and the persisted\n * `chat.message.content` rows carry `text` | `thinking` | `tool_call` |\n * `tool_result`.\n */\n\n/** JSON on the wire. Public JSON boundaries are `unknown`-typed by standard. */\nexport type ChatJsonValue =\n | string\n | number\n | boolean\n | null\n | ChatJsonValue[]\n | { [key: string]: ChatJsonValue };\n\nexport type ChatRole = \"user\" | \"assistant\" | \"system\" | \"tool\";\n\n/**\n * THE TOOL STATE MACHINE (the champion's, verbatim). A tool part only ever\n * moves forward along these states; `advanceToolState` in `./tool-state`\n * refuses anything else out loud rather than silently overwriting.\n */\nexport type ChatToolState =\n | \"input-streaming\"\n | \"input-available\"\n | \"approval-requested\"\n | \"output-available\"\n | \"output-error\"\n | \"output-denied\";\n\n/** One tool progress beat — the `tool_event`/`tool_progress` frames. */\nexport interface ChatToolProgress {\n /** The tool name the SERVER used on this beat: it differs from the call's\n * tool name mid-run (`web` → `web_search` → `web_research` in the corpus),\n * and flattening them loses what actually ran. */\n toolName: string;\n message: string;\n percent: number | null;\n /** Server timestamp in seconds, verbatim; null when the frame carried none. */\n at: number | null;\n}\n\nexport interface ChatTextPart {\n readonly type: \"text\";\n text: string;\n}\n\nexport interface ChatReasoningPart {\n readonly type: \"reasoning\";\n text: string;\n state: \"streaming\" | \"done\";\n}\n\nexport interface ChatToolPart {\n readonly type: \"tool\";\n /** The provider/server call id — the ONLY identity a tool part has. */\n toolCallId: string;\n toolName: string;\n state: ChatToolState;\n input: unknown;\n output: unknown;\n /** Present only in `output-error`; null otherwise. */\n errorText: string | null;\n progress: ChatToolProgress[];\n /** True once the server emitted `tool_delegated` — the CLIENT must run it. */\n delegated: boolean;\n}\n\nexport interface ChatFilePart {\n readonly type: \"file\";\n /** A durable Matrx file id. A signed URL is a handoff, never an identity. */\n fileId: string;\n name: string | null;\n mediaType: string | null;\n}\n\nexport interface ChatSourcePart {\n readonly type: \"source\";\n sourceId: string;\n url: string | null;\n title: string | null;\n}\n\nexport interface ChatRenderBlockPart {\n readonly type: \"render-block\";\n blockId: string;\n blockIndex: number;\n /** The content-IR kind name. */\n kind: string;\n content: string | null;\n data: unknown;\n status: \"streaming\" | \"complete\" | \"error\";\n}\n\nexport interface ChatAskUserPart {\n readonly type: \"ask-user\";\n askId: string;\n prompt: string;\n fields: unknown;\n answered: boolean;\n}\n\n/**\n * A failure the READER must see. `userMessage` is what a screen shows;\n * `remedy` is what the reader can do about it. Law 4 — nothing fails\n * silently, and a stand-in announces itself WITH a remedy.\n */\nexport interface ChatErrorPart {\n readonly type: \"error\";\n errorType: string | null;\n code: string | null;\n message: string;\n userMessage: string;\n recoverable: boolean;\n remedy: string | null;\n}\n\n/**\n * A non-fatal server notice (`warning` / `info` frames). It is a PART, not a\n * side channel, because the corpus proves a run can lose half its tools and\n * still answer — and a reader who is not told has been lied to.\n */\nexport interface ChatNoticePart {\n readonly type: \"notice\";\n level: \"info\" | \"low\" | \"medium\" | \"high\";\n code: string | null;\n userMessage: string;\n remedy: string | null;\n}\n\nexport type ChatPart =\n | ChatTextPart\n | ChatReasoningPart\n | ChatToolPart\n | ChatFilePart\n | ChatSourcePart\n | ChatRenderBlockPart\n | ChatAskUserPart\n | ChatErrorPart\n | ChatNoticePart;\n\nexport type ChatMessageStatus =\n | \"pending\"\n | \"streaming\"\n | \"complete\"\n | \"error\"\n | \"cancelled\";\n\nexport interface ChatMessageMetadata {\n /** The server's `X-Request-ID` for the turn this message belongs to. */\n requestId: string | null;\n /** The server `chat.message` row id once `record_update` announced it. */\n serverMessageId: string | null;\n /** Model id/name the server reported, when it reported one. Never a UUID on\n * screen — `renderModelRef` resolves it (phase 2). */\n modelRef: string | null;\n createdAt: string;\n /** Free-form server metadata (finish_reason, usage, …), verbatim. */\n raw: Record<string, unknown> | null;\n}\n\nexport interface ChatMessage {\n id: string;\n role: ChatRole;\n parts: ChatPart[];\n status: ChatMessageStatus;\n metadata: ChatMessageMetadata;\n}\n\n/**\n * THE THREAD STATE MACHINE.\n * `idle → submitted → streaming → (awaiting-approval) → idle | error`.\n */\nexport type ChatThreadStatus =\n | \"idle\"\n | \"submitted\"\n | \"streaming\"\n | \"awaiting-approval\"\n | \"error\";\n\nexport interface ChatThreadSnapshot {\n conversationId: string | null;\n /** Ordered oldest-first. */\n messages: readonly ChatMessage[];\n status: ChatThreadStatus;\n /** The server request id of the CURRENT or last turn — the only id the\n * server accepts for cancel. */\n requestId: string | null;\n /** Last server `phase`, verbatim. */\n phase: string | null;\n /**\n * TRUE when nothing is persisting this thread. The screen must SAY so with\n * the remedy (`ephemeralReason`) — an unannounced ephemeral thread is the\n * silent failure Law 4 forbids.\n */\n ephemeral: boolean;\n ephemeralReason: string | null;\n /** Monotonic; bumps on every applied change so hosts can cheap-compare. */\n revision: number;\n /** Last fatal error for the thread, if any. */\n error: ChatErrorPart | null;\n}\n\nlet idCounter = 0;\n\n/**\n * A collision-resistant local id. `crypto.randomUUID` when the runtime has it\n * (browsers, node >= 19, Deno, Bun), and a counter+random fallback that\n * announces nothing because it is not a stand-in for a capability — it is the\n * same guarantee by other means.\n */\nexport function newLocalId(prefix: string): string {\n const cryptoRef: { randomUUID?: () => string } | undefined = (\n globalThis as { crypto?: { randomUUID?: () => string } }\n ).crypto;\n if (typeof cryptoRef?.randomUUID === \"function\") {\n return `${prefix}_${cryptoRef.randomUUID()}`;\n }\n idCounter += 1;\n return `${prefix}_${Date.now().toString(36)}_${idCounter.toString(36)}_${Math.random()\n .toString(36)\n .slice(2, 10)}`;\n}\n\nexport function createChatMessage(input: {\n id?: string | undefined;\n role: ChatRole;\n parts?: ChatPart[] | undefined;\n status?: ChatMessageStatus | undefined;\n metadata?: Partial<ChatMessageMetadata> | undefined;\n}): ChatMessage {\n return {\n id: input.id ?? newLocalId(\"msg\"),\n role: input.role,\n parts: input.parts ?? [],\n status: input.status ?? \"complete\",\n metadata: {\n requestId: input.metadata?.requestId ?? null,\n serverMessageId: input.metadata?.serverMessageId ?? null,\n modelRef: input.metadata?.modelRef ?? null,\n createdAt: input.metadata?.createdAt ?? new Date().toISOString(),\n raw: input.metadata?.raw ?? null,\n },\n };\n}\n\n/** Every `text` part concatenated — what a copy button copies. */\nexport function messageText(message: ChatMessage): string {\n let out = \"\";\n for (const part of message.parts) if (part.type === \"text\") out += part.text;\n return out;\n}\n\n/** Every `reasoning` part concatenated. */\nexport function messageReasoning(message: ChatMessage): string {\n let out = \"\";\n for (const part of message.parts) if (part.type === \"reasoning\") out += part.text;\n return out;\n}\n\n/** Tool parts in first-seen order. */\nexport function messageTools(message: ChatMessage): ChatToolPart[] {\n return message.parts.filter((p): p is ChatToolPart => p.type === \"tool\");\n}\n","/**\n * THE TOOL-CALL STATE MACHINE.\n *\n * `input-streaming → input-available → approval-requested →\n * output-available | output-error | output-denied`\n *\n * It lives in the kernel, not in a renderer, for the reason C22 names: this is\n * exactly the logic four repositories re-derived four different ways. A tool\n * card that shows \"running\" after the run failed, or that re-opens a settled\n * call because a late duplicate frame arrived, is the class this file closes.\n *\n * The machine is MONOTONIC: the three output states are terminal, and a\n * transition that would move backwards is REFUSED and reported — never applied\n * silently (Law 4).\n */\n\nimport type { ChatMessage, ChatToolPart, ChatToolState } from \"./model\";\n\n/** Rank: a transition is legal only when it strictly increases, or is a\n * same-state refresh of a non-terminal state (progress beats). */\nconst RANK: Record<ChatToolState, number> = {\n \"input-streaming\": 0,\n \"input-available\": 1,\n \"approval-requested\": 2,\n \"output-available\": 3,\n \"output-error\": 3,\n \"output-denied\": 3,\n};\n\nconst TERMINAL: ReadonlySet<ChatToolState> = new Set<ChatToolState>([\n \"output-available\",\n \"output-error\",\n \"output-denied\",\n]);\n\nexport function isTerminalToolState(state: ChatToolState): boolean {\n return TERMINAL.has(state);\n}\n\nexport interface ToolStateTransition {\n /** The state to store. Equal to `from` when the transition was refused. */\n readonly state: ChatToolState;\n readonly applied: boolean;\n /** Present only when `applied` is false — say WHY, out loud. */\n readonly refusal: string | null;\n}\n\n/**\n * Advance a tool part. Returns the state to keep plus, when the move was\n * refused, a sentence a diagnostic sink can print verbatim.\n */\nexport function advanceToolState(\n from: ChatToolState,\n to: ChatToolState,\n): ToolStateTransition {\n if (from === to) {\n if (TERMINAL.has(from)) {\n return {\n state: from,\n applied: false,\n refusal: `tool state '${from}' is terminal and was re-announced; the duplicate frame was ignored`,\n };\n }\n return { state: to, applied: true, refusal: null };\n }\n if (TERMINAL.has(from)) {\n return {\n state: from,\n applied: false,\n refusal: `tool state '${from}' is terminal; refusing to move it to '${to}'`,\n };\n }\n const fromRank = RANK[from];\n const toRank = RANK[to];\n if (toRank < fromRank) {\n return {\n state: from,\n applied: false,\n refusal: `refusing to move a tool call backwards from '${from}' to '${to}'`,\n };\n }\n return { state: to, applied: true, refusal: null };\n}\n\n/**\n * Map ONE aidream tool lifecycle name onto the machine.\n *\n * Server truth, read off the live corpus and\n * `@ai-matrx/agents/projection/request`: the wire emits `tool_started`,\n * `tool_progress`, `tool_step`, `tool_result_preview`, `tool_completed`,\n * `tool_error`, `tool_delegated` — either as a top-level `event` or nested in\n * a `tool_event` frame's `data.event`.\n *\n * `null` means \"this frame carries no state change\" (a progress beat), which\n * is different from an unknown name — an unknown name is returned as\n * `\"unknown\"` so the caller can report it instead of dropping it.\n */\nexport function toolLifecycleToState(\n lifecycle: string,\n): ChatToolState | null | \"unknown\" {\n switch (lifecycle) {\n case \"tool_started\":\n return \"input-available\";\n case \"tool_progress\":\n case \"tool_step\":\n case \"tool_result_preview\":\n return null;\n case \"tool_delegated\":\n // The server handed the call to the CLIENT: the input is final and\n // nothing has run yet. It is NOT an approval request (the human is not\n // being asked) and it is NOT an output.\n return \"input-available\";\n case \"tool_approval_requested\":\n return \"approval-requested\";\n case \"tool_completed\":\n return \"output-available\";\n case \"tool_error\":\n return \"output-error\";\n case \"tool_denied\":\n return \"output-denied\";\n default:\n return \"unknown\";\n }\n}\n\n/** Every lifecycle name this machine understands — the guard's census. */\nexport const KNOWN_TOOL_LIFECYCLES: readonly string[] = [\n \"tool_started\",\n \"tool_progress\",\n \"tool_step\",\n \"tool_result_preview\",\n \"tool_delegated\",\n \"tool_approval_requested\",\n \"tool_completed\",\n \"tool_error\",\n \"tool_denied\",\n];\n\n\n/**\n * THE CLIENT'S OWN ANSWER.\n *\n * When the CLIENT answers a delegated tool (`tool_delegated` → the host runs it\n * → posts the result) the server learns the call is done from that post — but\n * for a delegated call it sends NO completion frame, because it hard-suspended\n * and closed the stream. A store that only moves on frames therefore leaves the\n * card on `input-available` (\"Working…\") for as long as the conversation stays\n * open. This is the defect all four hand-built chats had (found 2026-09-30 in\n * matrx-frontend, matrx-extend and matrx-local): the answerer is the first to\n * know, so it tells ITS OWN store, through this one door, in the same tick.\n */\nexport interface ChatToolAnswer {\n toolCallId: string;\n /** The result, on success. */\n output?: unknown;\n /** Set when the tool failed — the card goes to `output-error`. */\n errorText?: string | null;\n /** Set when the reader refused it — the card goes to `output-denied`. */\n denied?: boolean;\n}\n\nexport interface ChatToolSettlement {\n /** The transcript to keep — the SAME array when nothing changed. */\n readonly messages: readonly ChatMessage[];\n /** The message whose part changed, for the per-message wake. */\n readonly messageId: string | null;\n readonly applied: boolean;\n /** Present when `applied` is false — say WHY, out loud. */\n readonly refusal: string | null;\n}\n\n/** Pure: apply a client answer to the transcript, monotonically. */\nexport function settleToolAnswer(\n messages: readonly ChatMessage[],\n answer: ChatToolAnswer,\n): ChatToolSettlement {\n for (let m = messages.length - 1; m >= 0; m -= 1) {\n const message = messages[m];\n if (!message) continue;\n const index = message.parts.findIndex(\n (part) => part.type === \"tool\" && part.toolCallId === answer.toolCallId,\n );\n if (index === -1) continue;\n const part = message.parts[index] as ChatToolPart;\n const next: ChatToolState = answer.denied\n ? \"output-denied\"\n : answer.errorText != null\n ? \"output-error\"\n : \"output-available\";\n const transition = advanceToolState(part.state, next);\n if (!transition.applied) {\n return {\n messages,\n messageId: message.id,\n applied: false,\n refusal: transition.refusal,\n };\n }\n const settled: ChatToolPart = {\n ...part,\n state: transition.state,\n output: answer.output !== undefined ? answer.output : part.output,\n errorText: next === \"output-error\" ? (answer.errorText ?? null) : null,\n };\n const parts = message.parts.slice();\n parts[index] = settled;\n const copy = messages.slice();\n copy[m] = { ...message, parts };\n return { messages: copy, messageId: message.id, applied: true, refusal: null };\n }\n return {\n messages,\n messageId: null,\n applied: false,\n refusal: `no tool call ${answer.toolCallId} is in this transcript, so there was nothing to settle`,\n };\n}\n","/**\n * OPINIONS BECOME KNOBS (Law 6) — resolved in the kernel, never hardcoded and\n * never a question for a customer.\n *\n * Four layers, lowest to highest: **platform default → organization setting →\n * surface config → per-user preference**. Every knob has a platform default,\n * so a host that sets nothing still gets the platform's taste.\n *\n * One of these knobs is a SAFETY RULE and it lives here on purpose (C22):\n * `routing` carries matrx-local's \"a confidential thread never reaches a cloud\n * provider\". In the kernel it is a thread-level constraint the store ENFORCES\n * with a refusal — it never silently falls back to a cloud target.\n */\n\nexport type ChatSendKey = \"enter\" | \"mod-enter\";\nexport type ChatDensity = \"comfortable\" | \"compact\";\n\n/**\n * Where a thread's text is allowed to go.\n * - `\"any\"` — the platform default: any target the host offers.\n * - `\"local-only\"` — CONFIDENTIAL. The thread refuses a cloud target and says\n * why. This is the highest-severity rule in the design (§12 risk 4).\n */\nexport type ChatRoutingPolicy = \"any\" | \"local-only\";\n\n/** What a transport declares about itself so the kernel can judge it. */\nexport type ChatTargetLocality = \"local\" | \"cloud\";\n\nexport interface ChatFeatures {\n /** Enter sends, or Cmd/Ctrl+Enter sends. */\n sendKey: ChatSendKey;\n density: ChatDensity;\n /** Reasoning parts visible without a click. */\n showReasoningByDefault: boolean;\n /** Tool cards start expanded. */\n expandToolCardsByDefault: boolean;\n /** Transcript sticks to the bottom while the reader is at the bottom. */\n autoScroll: boolean;\n /** Read new assistant turns aloud as they arrive. */\n readAloudAutoPlay: boolean;\n /** Keep an unsent composer draft per conversation across reloads. */\n retainDrafts: boolean;\n /**\n * The composer may send while an answer is still arriving. OFF by the\n * platform default (a kernel thread runs one turn at a time). A host whose\n * send QUEUES the message behind the live turn — a coding session, where\n * the engine holds a per-session queue — turns it on; the composer then\n * shows Send (queue) beside Stop instead of Stop alone.\n */\n sendWhileBusy: boolean;\n /** THE ARCHIVED-ITEMS LAW: the conversation list hides archived by default. */\n hideArchivedConversations: boolean;\n /**\n * The per-turn action bar (copy, ask again, edit and send again, plus the\n * host's own). ON by the platform default — every serious chat has one, and\n * a reader who cannot copy an answer retypes it. An organization that does\n * not want it turns it OFF here rather than a host forking the transcript.\n */\n messageActions: boolean;\n /** Largest attachment the composer accepts, in BYTES. */\n attachmentMaxBytes: number;\n /**\n * How the platform's inline reasoning sentinel is treated.\n *\n * Ground truth (captured live 2026-09-17): the Google path does NOT emit\n * `reasoning_chunk`. It emits `reasoning {state:\"started\"}`, then ordinary\n * `chunk` frames whose text opens with `<reasoning>` and closes with\n * `</reasoning>`, then `reasoning {state:\"stopped\"}`.\n *\n * - `\"split\"` (platform default) — that span becomes a `reasoning` part, so\n * a reader never sees the raw sentinel in the answer.\n * - `\"inline\"` — the span stays in the answer text, byte-for-byte identical\n * to `@ai-matrx/agents/projection/request`'s `answer`. This exists so the\n * parity harness can prove EXACT equality with the shipped projection, and\n * so a host mid-migration can keep its current screen.\n */\n reasoningSentinel: \"split\" | \"inline\";\n /** Where this thread's text may go. */\n routing: ChatRoutingPolicy;\n}\n\nexport const CHAT_PLATFORM_DEFAULTS: Readonly<ChatFeatures> = Object.freeze({\n sendKey: \"enter\",\n density: \"comfortable\",\n showReasoningByDefault: false,\n expandToolCardsByDefault: false,\n autoScroll: true,\n readAloudAutoPlay: false,\n retainDrafts: true,\n sendWhileBusy: false,\n hideArchivedConversations: true,\n messageActions: true,\n attachmentMaxBytes: 25 * 1024 * 1024,\n reasoningSentinel: \"split\",\n routing: \"any\",\n});\n\nexport interface ChatConfigLayers {\n /** Organization setting rows, resolved by the host. */\n organization?: Partial<ChatFeatures>;\n /** This surface's configuration (a dock is not the full page). */\n surface?: Partial<ChatFeatures>;\n /** This person's preference. */\n user?: Partial<ChatFeatures>;\n}\n\n/** Resolve the four layers. Later layers win, key by key. */\nexport function resolveChatFeatures(layers: ChatConfigLayers = {}): ChatFeatures {\n return {\n ...CHAT_PLATFORM_DEFAULTS,\n ...stripUndefined(layers.organization),\n ...stripUndefined(layers.surface),\n ...stripUndefined(layers.user),\n };\n}\n\nfunction stripUndefined(\n value: Partial<ChatFeatures> | undefined,\n): Partial<ChatFeatures> {\n if (!value) return {};\n const out: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(value)) {\n if (entry !== undefined) out[key] = entry;\n }\n return out as Partial<ChatFeatures>;\n}\n\n/** The refusal a confidential thread produces instead of a silent fallback. */\nexport interface ChatRoutingRefusal {\n readonly allowed: false;\n readonly code: \"confidential_thread_cloud_target_refused\";\n readonly userMessage: string;\n readonly remedy: string;\n}\n\nexport type ChatRoutingDecision = { readonly allowed: true } | ChatRoutingRefusal;\n\n/**\n * THE CONFIDENTIAL-ROUTING RULE, in the kernel where it belongs.\n *\n * A thread pinned `local-only` REFUSES a cloud target. It does not downgrade,\n * it does not warn-and-send, and it does not quietly pick a different target:\n * the send fails with a named reason and a remedy. Re-deriving this per host\n * is how private text reaches a provider.\n */\nexport function decideRouting(\n policy: ChatRoutingPolicy,\n target: ChatTargetLocality,\n): ChatRoutingDecision {\n if (policy === \"local-only\" && target === \"cloud\") {\n return {\n allowed: false,\n code: \"confidential_thread_cloud_target_refused\",\n userMessage:\n \"This conversation is marked confidential, so it cannot be sent to a cloud AI provider.\",\n remedy:\n \"Choose a local engine for this conversation, or start a new conversation that is not marked confidential.\",\n };\n }\n return { allowed: true };\n}\n","/**\n * THE REDUCER — one live stream frame becomes message parts.\n *\n * Every branch below exists because the LIVE corpus in `fixtures/corpus/`\n * contains that frame. Nothing here was guessed from a client: the wire\n * vocabulary was read off bytes recorded from `server.app.matrxserver.com` on\n * 2026-09-17 and cross-checked against\n * `@ai-matrx/agents/projection/request`.\n *\n * The projector is deliberately MUTABLE. A streaming turn applies one frame\n * per token; rebuilding an immutable tree per token is the classic reason a\n * chat janks at 2,000 tokens. Immutability is provided where React needs it —\n * `snapshot()` hands back a fresh `ChatMessage` with a fresh `parts` array —\n * and the store coalesces those snapshots per animation frame.\n */\n\nimport {\n advanceToolState,\n toolLifecycleToState,\n} from \"./tool-state\";\nimport type { ChatFeatures } from \"./config\";\nimport {\n createChatMessage,\n type ChatErrorPart,\n type ChatMessage,\n type ChatNoticePart,\n type ChatPart,\n type ChatRenderBlockPart,\n type ChatReasoningPart,\n type ChatTextPart,\n type ChatToolPart,\n} from \"./model\";\nimport type { ChatStreamFrame } from \"./ports\";\n\n/** The reasoning sentinel the Google path wraps thinking text in. */\nexport const REASONING_OPEN = \"<reasoning>\";\nexport const REASONING_CLOSE = \"</reasoning>\";\n\nexport type ChatTurnOutcome =\n | \"streaming\"\n | \"awaiting-tools\"\n | \"complete\"\n | \"error\"\n | \"cancelled\";\n\nexport interface ChatTurnRefusal {\n code: string;\n message: string;\n detail?: unknown;\n}\n\nexport interface ChatTurnProjectorOptions {\n features: ChatFeatures;\n /** The message id for the assistant turn (kept stable across the stream). */\n messageId?: string | undefined;\n /** Every refusal / unknown frame — reported, never dropped (Law 4). */\n onRefusal?: ((refusal: ChatTurnRefusal) => void) | undefined;\n}\n\nexport interface ChatTurnProjector {\n apply(frame: ChatStreamFrame): void;\n /** A fresh, structurally-shared assistant message. */\n snapshot(): ChatMessage;\n readonly state: ChatTurnState;\n}\n\nexport interface ChatTurnState {\n outcome: ChatTurnOutcome;\n conversationId: string | null;\n requestId: string | null;\n phase: string | null;\n /** The `chat.message` row ids the server reserved, by role. */\n serverMessageIds: { user: string | null; assistant: string | null };\n completion: Record<string, unknown> | null;\n error: ChatErrorPart | null;\n /** Bumps on every applied frame — the store's cheap dirty check. */\n revision: number;\n lastTransportSeq: number;\n transportStreamId: string | null;\n /** Frames the projector did not understand; never silently zero. */\n unknownEvents: string[];\n}\n\nconst asRecord = (value: unknown): Record<string, unknown> =>\n value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n\nconst asString = (value: unknown): string | null =>\n typeof value === \"string\" ? value : null;\n\nconst asNumber = (value: unknown): number | null =>\n typeof value === \"number\" && Number.isFinite(value) ? value : null;\n\n/** Events the projector deliberately ignores — real frames with no part. */\nconst IGNORED_EVENTS: ReadonlySet<string> = new Set([\n \"heartbeat\",\n \"context_state\",\n \"context_trimmed\",\n \"record_update\",\n \"ping\",\n \"debug\",\n // `init`/`completion` pairs exist for five operations; only the\n // `user_request` completion is terminal, and `init` carries no part.\n \"init\",\n]);\n\nexport function createChatTurnProjector(\n options: ChatTurnProjectorOptions,\n): ChatTurnProjector {\n const features = options.features;\n const messageId = options.messageId ?? createChatMessage({ role: \"assistant\" }).id;\n const parts: ChatPart[] = [];\n const toolsByCallId = new Map<string, ChatToolPart>();\n const blocksById = new Map<string, ChatRenderBlockPart>();\n\n /** True between `reasoning {started}` and `reasoning {stopped}`. */\n let reasoningActive = false;\n /** True while inside an unterminated `<reasoning>` span in chunk text. */\n let insideSentinel = false;\n\n const state: ChatTurnState = {\n outcome: \"streaming\",\n conversationId: null,\n requestId: null,\n phase: null,\n serverMessageIds: { user: null, assistant: null },\n completion: null,\n error: null,\n revision: 0,\n lastTransportSeq: 0,\n transportStreamId: null,\n unknownEvents: [],\n };\n\n const refuse = (refusal: ChatTurnRefusal): void => {\n options.onRefusal?.(refusal);\n };\n\n const lastPart = (): ChatPart | undefined => parts[parts.length - 1];\n\n const appendText = (text: string): void => {\n const tail = lastPart();\n if (tail && tail.type === \"text\") {\n tail.text += text;\n return;\n }\n const part: ChatTextPart = { type: \"text\", text };\n parts.push(part);\n };\n\n const appendReasoning = (text: string): void => {\n const tail = lastPart();\n if (tail && tail.type === \"reasoning\" && tail.state === \"streaming\") {\n tail.text += text;\n return;\n }\n const part: ChatReasoningPart = { type: \"reasoning\", text, state: \"streaming\" };\n parts.push(part);\n };\n\n const closeReasoning = (): void => {\n for (let i = parts.length - 1; i >= 0; i -= 1) {\n const part = parts[i];\n if (part && part.type === \"reasoning\" && part.state === \"streaming\") {\n part.state = \"done\";\n return;\n }\n }\n };\n\n /**\n * Route ONE chunk of answer text.\n *\n * With `reasoningSentinel: \"inline\"` the text goes straight to the answer,\n * byte-for-byte identical to the shipped projection's `answer`. With\n * `\"split\"` (the platform default) the `<reasoning>…</reasoning>` span\n * becomes a reasoning part, so a reader never sees the raw sentinel.\n */\n const routeChunk = (text: string): void => {\n if (features.reasoningSentinel === \"inline\") {\n appendText(text);\n return;\n }\n let rest = text;\n while (rest.length > 0) {\n if (insideSentinel) {\n const close = rest.indexOf(REASONING_CLOSE);\n if (close === -1) {\n appendReasoning(rest);\n return;\n }\n appendReasoning(rest.slice(0, close));\n closeReasoning();\n insideSentinel = false;\n rest = rest.slice(close + REASONING_CLOSE.length);\n continue;\n }\n const open = rest.indexOf(REASONING_OPEN);\n if (open === -1) {\n appendText(rest);\n return;\n }\n if (open > 0) appendText(rest.slice(0, open));\n insideSentinel = true;\n rest = rest.slice(open + REASONING_OPEN.length);\n }\n };\n\n const toolFor = (callId: string, toolName: string): ChatToolPart => {\n const existing = toolsByCallId.get(callId);\n if (existing) return existing;\n const part: ChatToolPart = {\n type: \"tool\",\n toolCallId: callId,\n toolName,\n state: \"input-streaming\",\n input: undefined,\n output: undefined,\n errorText: null,\n progress: [],\n delegated: false,\n };\n toolsByCallId.set(callId, part);\n parts.push(part);\n return part;\n };\n\n const applyToolLifecycle = (\n lifecycle: string,\n data: Record<string, unknown>,\n ): void => {\n const callId = asString(data.call_id);\n const toolName = asString(data.tool_name);\n if (callId === null || toolName === null) {\n refuse({\n code: \"tool_frame_without_identity\",\n message: `a '${lifecycle}' frame arrived without call_id/tool_name and was ignored`,\n detail: data,\n });\n return;\n }\n const part = toolFor(callId, toolName);\n const payload = asRecord(data.data);\n const message = asString(data.message) ?? \"\";\n\n const next = toolLifecycleToState(lifecycle);\n if (next === \"unknown\") {\n state.unknownEvents.push(lifecycle);\n refuse({\n code: \"unknown_tool_lifecycle\",\n message: `tool lifecycle '${lifecycle}' is not in this package's state machine; the frame was recorded but not applied`,\n detail: data,\n });\n return;\n }\n\n if (lifecycle === \"tool_started\" && \"arguments\" in payload) {\n part.input = payload.arguments;\n }\n if (lifecycle === \"tool_delegated\") {\n part.delegated = true;\n if (\"arguments\" in payload) part.input = payload.arguments;\n }\n if (lifecycle === \"tool_completed\" && \"result\" in payload) {\n part.output = payload.result;\n }\n if (lifecycle === \"tool_error\") {\n part.errorText =\n asString(payload.error) ?? (message || `The ${toolName} tool failed.`);\n }\n\n if (next === null) {\n part.progress.push({\n toolName,\n message,\n percent: asNumber(payload.percent),\n at: asNumber(data.timestamp),\n });\n return;\n }\n\n const transition = advanceToolState(part.state, next);\n if (!transition.applied) {\n refuse({\n code: \"tool_state_transition_refused\",\n message: `${transition.refusal ?? \"refused\"} (call ${callId}, tool ${toolName})`,\n detail: data,\n });\n return;\n }\n part.state = transition.state;\n if (next === \"input-available\" && part.delegated) {\n state.outcome = \"awaiting-tools\";\n } else if (\n state.outcome === \"awaiting-tools\" &&\n (next === \"output-available\" || next === \"output-error\")\n ) {\n state.outcome = \"streaming\";\n }\n };\n\n const pushNotice = (\n level: ChatNoticePart[\"level\"],\n data: Record<string, unknown>,\n ): void => {\n const userMessage = asString(data.user_message) ?? \"\";\n if (!userMessage) return;\n const part: ChatNoticePart = {\n type: \"notice\",\n level,\n code: asString(data.code),\n userMessage,\n remedy: asString(asRecord(data.metadata).remedy),\n };\n parts.push(part);\n };\n\n const apply = (frame: ChatStreamFrame): void => {\n const streamSeq = asNumber(frame.stream_seq);\n const streamId = asString(frame.stream_id);\n const sameSegment = streamId === null || streamId === state.transportStreamId;\n if (sameSegment && streamSeq !== null && streamSeq <= state.lastTransportSeq) {\n // A replayed frame after a reconnect. Dropping it is the whole point of\n // the sequence: applying it doubles text.\n return;\n }\n state.lastTransportSeq =\n streamSeq === null\n ? state.lastTransportSeq\n : sameSegment\n ? Math.max(state.lastTransportSeq, streamSeq)\n : streamSeq;\n if (streamId !== null) state.transportStreamId = streamId;\n state.revision += 1;\n\n const data = asRecord(frame.data);\n\n switch (frame.event) {\n case \"chunk\": {\n const text = asString(data.text);\n if (text !== null) routeChunk(text);\n return;\n }\n case \"reasoning_chunk\": {\n const text = asString(data.text);\n if (text !== null) {\n reasoningActive = true;\n appendReasoning(text);\n }\n return;\n }\n case \"reasoning\": {\n const started = data.state === \"started\";\n reasoningActive = started;\n if (!started) closeReasoning();\n return;\n }\n case \"phase\": {\n const phase = asString(data.phase);\n if (phase !== null) state.phase = phase;\n return;\n }\n case \"data\": {\n if (data.type === \"conversation_id\") {\n const conversationId = asString(data.conversation_id);\n if (conversationId !== null) state.conversationId = conversationId;\n }\n return;\n }\n case \"record_reserved\": {\n // The server names the `chat.message` rows it reserved and, in\n // `metadata.role`, which side each one is. This is how an optimistic\n // user bubble learns its durable row id without a re-read.\n if (asString(data.table) !== \"message\") return;\n const recordId = asString(data.record_id);\n const role = asString(asRecord(data.metadata).role);\n if (recordId === null) return;\n if (role === \"user\") state.serverMessageIds.user = recordId;\n else if (role === \"assistant\") state.serverMessageIds.assistant = recordId;\n return;\n }\n case \"tool_event\": {\n const lifecycle = asString(data.event);\n if (lifecycle === null) {\n refuse({\n code: \"tool_event_without_lifecycle\",\n message: \"a tool_event frame carried no data.event and was ignored\",\n detail: data,\n });\n return;\n }\n applyToolLifecycle(lifecycle, data);\n return;\n }\n case \"tool_started\":\n case \"tool_progress\":\n case \"tool_step\":\n case \"tool_result_preview\":\n case \"tool_completed\":\n case \"tool_error\":\n case \"tool_delegated\": {\n applyToolLifecycle(frame.event, data);\n return;\n }\n case \"render_block\": {\n const blockId = asString(data.blockId);\n const blockIndex = asNumber(data.blockIndex);\n const kind = asString(data.type);\n if (blockId === null || blockIndex === null || kind === null) {\n refuse({\n code: \"render_block_without_identity\",\n message: \"a render_block frame lacked blockId/blockIndex/type and was ignored\",\n detail: data,\n });\n return;\n }\n const status =\n data.status === \"complete\" || data.status === \"error\"\n ? (data.status as \"complete\" | \"error\")\n : \"streaming\";\n const existing = blocksById.get(blockId);\n if (existing) {\n existing.status = status;\n existing.content = asString(data.content);\n existing.data = data.data;\n return;\n }\n const part: ChatRenderBlockPart = {\n type: \"render-block\",\n blockId,\n blockIndex,\n kind,\n content: asString(data.content),\n data: data.data,\n status,\n };\n blocksById.set(blockId, part);\n parts.push(part);\n return;\n }\n case \"warning\": {\n const level = asString(data.level);\n pushNotice(\n level === \"high\" || level === \"medium\" || level === \"low\" ? level : \"low\",\n data,\n );\n return;\n }\n case \"info\": {\n // `initial_message` is chrome (\"Initializing Matrx Chat…\"), not news.\n if (asString(data.code) === \"initial_message\") return;\n pushNotice(\"info\", data);\n return;\n }\n case \"error\": {\n const message =\n asString(data.message) ?? asString(data.user_message) ?? \"The run failed.\";\n const part: ChatErrorPart = {\n type: \"error\",\n errorType: asString(data.error_type),\n code: asString(data.code),\n message,\n userMessage: asString(data.user_message) ?? message,\n recoverable: data.recoverable === true,\n remedy: asString(asRecord(data.details).remedy),\n };\n parts.push(part);\n state.error = part;\n state.outcome = \"error\";\n return;\n }\n case \"completion\": {\n if (asString(data.operation) !== \"user_request\") return;\n state.completion = data;\n const status = asString(data.status);\n if (status === \"failed\") {\n state.outcome = \"error\";\n if (state.error === null) {\n const result = asRecord(data.result);\n const message =\n asString(result.error) ??\n asString(result.user_message) ??\n \"The run failed.\";\n const part: ChatErrorPart = {\n type: \"error\",\n errorType: \"run_failed\",\n code: null,\n message,\n userMessage: message,\n recoverable: true,\n remedy: null,\n };\n parts.push(part);\n state.error = part;\n }\n } else if (status === \"cancelled\") {\n state.outcome = \"cancelled\";\n } else if (state.outcome !== \"error\") {\n state.outcome = \"complete\";\n }\n return;\n }\n case \"end\": {\n if (state.outcome === \"streaming\" || state.outcome === \"awaiting-tools\") {\n state.outcome = \"complete\";\n }\n if (insideSentinel || reasoningActive) {\n closeReasoning();\n insideSentinel = false;\n reasoningActive = false;\n }\n return;\n }\n default: {\n if (IGNORED_EVENTS.has(frame.event)) return;\n state.unknownEvents.push(frame.event);\n refuse({\n code: \"unknown_stream_event\",\n message: `stream event '${frame.event}' is not in this package's vocabulary; it was recorded, not dropped`,\n detail: frame.data,\n });\n }\n }\n };\n\n const snapshot = (): ChatMessage => {\n const status =\n state.outcome === \"error\"\n ? \"error\"\n : state.outcome === \"cancelled\"\n ? \"cancelled\"\n : state.outcome === \"complete\"\n ? \"complete\"\n : \"streaming\";\n const completion = state.completion;\n return {\n id: messageId,\n role: \"assistant\",\n // Fresh array + fresh part objects: React compares by identity, and a\n // mutated-in-place part never re-renders.\n parts: parts.map((part) => ({ ...part }) as ChatPart),\n status,\n metadata: {\n requestId: state.requestId,\n serverMessageId: state.serverMessageIds.assistant,\n modelRef: completion ? extractModelRef(completion) : null,\n createdAt: new Date(0).toISOString(),\n raw: completion,\n },\n };\n };\n\n return {\n apply,\n snapshot,\n get state() {\n return state;\n },\n };\n}\n\n/**\n * The model the server actually billed, from the completion's usage block\n * (`result.total_usage.by_model` — one key per model that ran). Returns null\n * rather than a UUID or a guess: a screen never prints an id, and \"unknown\" is\n * the honest answer when the completion carried no usage.\n */\nexport function extractModelRef(\n completion: Record<string, unknown>,\n): string | null {\n const result = asRecord(completion.result);\n const usage = asRecord(result.total_usage);\n const byModel = asRecord(usage.by_model);\n const names = Object.keys(byModel);\n return names.length > 0 ? (names[names.length - 1] ?? null) : null;\n}\n","/**\n * HOST INJECTION SEAMS — the ports a host may override.\n *\n * Per THE ALL-INCLUSIVE LAW every port here either ships a working default in\n * this package (`./matrx`, or the in-file defaults below) or is genuinely host\n * identity. A host injects identity and nothing else to be up and running.\n *\n * Phase 1 declares the ports the KERNEL uses. `files`, `nav`, `shell`,\n * `renderContent`, `renderModelRef` and `capabilities` are phase 2's — they\n * are deliberately ABSENT here rather than declared-and-unused, because a port\n * nothing calls is untested theory that rots (C25's reasoning).\n */\n\nimport type { ChatMessage, ChatRole } from \"./model\";\nimport type { ChatTargetLocality } from \"./config\";\n\n/** The ONE true injection. Read from the client's own session, never a cached copy. */\nexport interface ChatIdentityPort {\n /** A fresh access token per call. Cached copies rot when the cookie rotates. */\n getAccessToken(): Promise<string | null> | string | null;\n userId: string;\n organizationId?: string | undefined;\n}\n\n/** One frame off the wire, already normalized by `@ai-matrx/agents/stream/ndjson`. */\nexport interface ChatStreamFrame {\n event: string;\n data: unknown;\n stream_id?: string | undefined;\n stream_seq?: number | undefined;\n}\n\n/** What a started turn hands back before any frame arrives. */\nexport interface ChatTurnHandle {\n /** The server's `X-Request-ID` — the ONLY id cancel accepts. */\n requestId: string | null;\n conversationId: string | null;\n frames: AsyncIterable<ChatStreamFrame>;\n}\n\nexport interface ChatSendRequest {\n conversationId: string | null;\n /** True when this turn creates the conversation. */\n isNewConversation: boolean;\n /** False for an ephemeral run: nothing read, nothing written. */\n store: boolean;\n text: string;\n /** Durable Matrx file ids attached to this turn. */\n fileIds: readonly string[];\n signal: AbortSignal;\n}\n\n/**\n * THE TRANSPORT PORT — subscribe-shaped, NOT a fetch.\n *\n * It is this shape because matrx-extend's MV3 stream lives in an offscreen\n * document and is relayed over `chrome.runtime`: an API that has only ever met\n * `fetch` ossifies wrong. A host that does use fetch implements this in a few\n * lines (`./matrx` is that implementation).\n */\nexport interface ChatTransportPort {\n /** Where this transport sends text. The kernel refuses `\"cloud\"` for a\n * confidential thread — so a transport that lies here defeats the rule, and\n * it is the transport author's obligation to be honest. */\n readonly locality: ChatTargetLocality;\n start(request: ChatSendRequest): Promise<ChatTurnHandle>;\n /** Cooperative stop at the next iteration boundary. */\n cancel(requestId: string): Promise<void>;\n}\n\n/** A conversation as the history list shows it. */\nexport interface ChatConversationSummary {\n id: string;\n title: string | null;\n updatedAt: string;\n createdAt: string;\n messageCount: number;\n archived: boolean;\n isFavorite: boolean;\n /** Which surface produced it — the history list's source filter. */\n sourceApp: string | null;\n sourceFeature: string | null;\n}\n\nexport interface ChatConversationListQuery {\n limit?: number | undefined;\n /** Keyset cursor: return rows older than this ISO timestamp. */\n before?: string | undefined;\n /**\n * THE ARCHIVED-ITEMS LAW. Default false — archived rows are hidden, and the\n * caller that shows the list must name what it hides.\n */\n includeArchived?: boolean | undefined;\n sourceApp?: string | undefined;\n sourceFeature?: string | undefined;\n /**\n * The page's own organization filter (`?org_filter=`). Absent/null = ALL the\n * person's organizations, the default on every visit — never the active\n * organization (active-org-is-never-a-list-filter.md).\n */\n organizationFilter?: string | null | undefined;\n}\n\nexport interface ChatConversationListPage {\n conversations: ChatConversationSummary[];\n /** How many rows the archive filter hid, so the footer can say so. */\n archivedHidden: number;\n /** Cursor for the next page, or null at the end. */\n nextBefore: string | null;\n}\n\n/**\n * THE PERSISTENCE BOUNDARY (C10). The `chat.*` schema belongs to aidream, so\n * this package does not OWN it — but `./matrx` ships the complete binding, so\n * a host gets working persistence for free and only a customer on their own\n * tables ever implements this.\n *\n * With no binding the thread runs ephemeral AND SAYS SO on screen\n * (`ChatThreadSnapshot.ephemeral` + `ephemeralReason`).\n */\nexport interface ChatPersistencePort {\n /** Full transcript of one conversation, oldest first. */\n loadThread(conversationId: string): Promise<ChatMessage[]>;\n listConversations(\n query: ChatConversationListQuery,\n ): Promise<ChatConversationListPage>;\n setArchived(conversationId: string, archived: boolean): Promise<void>;\n rename(conversationId: string, title: string): Promise<void>;\n}\n\n/** Typed diagnostic sink. Default: `console`. */\nexport interface ChatErrorSinkPort {\n capture(event: {\n code: string;\n message: string;\n detail?: unknown;\n }): void;\n}\n\n/** Toast / banner sink. Default: a no-op that still reaches the errorSink. */\nexport interface ChatNotifierPort {\n notify(event: {\n level: \"info\" | \"warning\" | \"error\";\n message: string;\n remedy?: string | undefined;\n }): void;\n}\n\n/** Drafts, density, sidebar state. Default: in-memory, announced once. */\nexport interface ChatPrefsPort {\n get(key: string): string | null;\n set(key: string, value: string): void;\n remove(key: string): void;\n}\n\nexport function createConsoleErrorSink(): ChatErrorSinkPort {\n return {\n capture(event) {\n console.error(`[ai-matrx/chat] ${event.code}: ${event.message}`, event.detail);\n },\n };\n}\n\n/**\n * The default notifier. It never swallows: everything it is handed also\n * reaches the console, with the remedy, because a notice nobody renders is a\n * silent failure.\n */\nexport function createConsoleNotifier(): ChatNotifierPort {\n return {\n notify(event) {\n const line = event.remedy\n ? `[ai-matrx/chat] ${event.message} — ${event.remedy}`\n : `[ai-matrx/chat] ${event.message}`;\n if (event.level === \"error\") console.error(line);\n else if (event.level === \"warning\") console.warn(line);\n else console.info(line);\n },\n };\n}\n\n/**\n * The memory prefs fallback. It ANNOUNCES ITSELF ONCE with the remedy the\n * first time it is written to, because a draft that silently evaporates on\n * reload is exactly the class Law 4 forbids.\n */\nexport function createMemoryPrefs(\n notifier: ChatNotifierPort = createConsoleNotifier(),\n): ChatPrefsPort {\n const map = new Map<string, string>();\n let announced = false;\n const announce = (): void => {\n if (announced) return;\n announced = true;\n notifier.notify({\n level: \"warning\",\n message:\n \"Chat preferences and drafts are being kept in memory only, so they will be lost when this page reloads.\",\n remedy:\n \"Give the chat a `prefs` port backed by localStorage (or your own store) to keep drafts across reloads.\",\n });\n };\n return {\n get: (key) => map.get(key) ?? null,\n set: (key, value) => {\n announce();\n map.set(key, value);\n },\n remove: (key) => {\n map.delete(key);\n },\n };\n}\n\n/**\n * `localStorage` prefs with the memory fallback. Every read AND write is\n * wrapped: a private window, blocked site data, or a full quota makes the\n * accessor THROW, and an unguarded prefs port takes the whole chat down with\n * it.\n */\nexport function createWebPrefs(\n notifier: ChatNotifierPort = createConsoleNotifier(),\n): ChatPrefsPort {\n const fallback = createMemoryPrefs(notifier);\n let store: Storage | null = null;\n try {\n store = (globalThis as { localStorage?: Storage }).localStorage ?? null;\n if (store) {\n const probe = \"__ai_matrx_chat_probe__\";\n store.setItem(probe, \"1\");\n store.removeItem(probe);\n }\n } catch {\n store = null;\n }\n if (!store) return fallback;\n const live = store;\n return {\n get: (key) => {\n // A write that fell back to memory (quota, or a storage that accepts the\n // probe and rejects real writes) is INVISIBLE to `getItem` — it returns\n // null rather than throwing, so a read that trusted it alone would hand\n // back an empty draft and the reader would watch their message vanish.\n try {\n const stored = live.getItem(key);\n if (stored !== null) return stored;\n } catch {\n /* fall through to the memory fallback */\n }\n return fallback.get(key);\n },\n set: (key, value) => {\n try {\n live.setItem(key, value);\n } catch {\n fallback.set(key, value);\n }\n },\n remove: (key) => {\n try {\n live.removeItem(key);\n } catch {\n /* the durable copy may not exist; the memory copy still must go */\n }\n fallback.remove(key);\n },\n };\n}\n\n/** Roles the kernel renders. Anything else is reported, never dropped. */\nexport const CHAT_ROLES: readonly ChatRole[] = [\n \"user\",\n \"assistant\",\n \"system\",\n \"tool\",\n];\n","/**\n * THE THREAD STORE — layer 1 of `@ai-matrx/chat`.\n *\n * `getSnapshot()` / `subscribe()` is the `useSyncExternalStore` source shape\n * (the AI SDK's proven one), plus `subscribeToMessage(id)` so ONE streaming\n * bubble re-renders without touching the list. There is no React here, no\n * Redux here, and no `window` touched at import time.\n *\n * Performance rules that are code, not prose:\n * 1. store writes are COALESCED per frame tick, never per token;\n * 2. only the streaming message's per-message subscribers are woken on a\n * token; the thread subscribers are woken on the same coalesced tick but\n * the message objects of every OTHER message keep their identity, so a\n * memoized list re-renders one row.\n */\n\nimport {\n createChatTurnProjector,\n type ChatTurnRefusal,\n} from \"./ingest\";\nimport {\n decideRouting,\n resolveChatFeatures,\n type ChatConfigLayers,\n type ChatFeatures,\n} from \"./config\";\nimport {\n createChatMessage,\n newLocalId,\n type ChatErrorPart,\n type ChatMessage,\n type ChatThreadSnapshot,\n type ChatThreadStatus,\n} from \"./model\";\nimport {\n createConsoleErrorSink,\n createConsoleNotifier,\n type ChatErrorSinkPort,\n type ChatNotifierPort,\n type ChatPersistencePort,\n type ChatTransportPort,\n} from \"./ports\";\nimport {\n settleToolAnswer,\n type ChatToolAnswer,\n type ChatToolSettlement,\n} from \"./tool-state\";\n\nexport interface ChatThreadConfig {\n transport: ChatTransportPort;\n /** Absent ⇒ the thread is EPHEMERAL and says so in its snapshot. */\n persistence?: ChatPersistencePort | undefined;\n /** Existing conversation to continue. Omit to start a new one on first send. */\n conversationId?: string | undefined;\n features?: ChatConfigLayers | undefined;\n errorSink?: ChatErrorSinkPort | undefined;\n notifier?: ChatNotifierPort | undefined;\n /**\n * Coalescing window. The default `null` means \"schedule on the runtime's\n * frame clock\" (`requestAnimationFrame` when there is one, a microtask\n * otherwise). A number is a millisecond timer, which is what tests use so\n * they never depend on a browser clock.\n */\n coalesceMs?: number | null | undefined;\n}\n\nexport interface ChatSendInput {\n text: string;\n fileIds?: readonly string[] | undefined;\n}\n\nexport interface ChatSendResult {\n /** False when the send was refused BEFORE anything left the machine. */\n accepted: boolean;\n /** Present when `accepted` is false — the reason, in the reader's words. */\n refusal: ChatErrorPart | null;\n}\n\nexport interface ChatThreadStore {\n getSnapshot(): ChatThreadSnapshot;\n subscribe(listener: () => void): () => void;\n /** Wake ONLY on this message. The whole point of the per-message seam. */\n subscribeToMessage(messageId: string, listener: () => void): () => void;\n getMessageSnapshot(messageId: string): ChatMessage | undefined;\n readonly features: ChatFeatures;\n send(input: ChatSendInput): Promise<ChatSendResult>;\n /** Cooperative stop. Resolves once the server acknowledged (or failed to). */\n cancel(): Promise<void>;\n /** Replace the transcript — used by history load and by tests. */\n setMessages(messages: readonly ChatMessage[]): void;\n /** Load a stored conversation through the persistence port. */\n loadConversation(conversationId: string): Promise<void>;\n /**\n * Whether `startNewConversation()` actually does anything here. The kernel\n * answers true; `/external-store` answers true only when the host gave it a\n * handler. A shell reads THIS to decide whether a \"New conversation\" control\n * exists at all — a New button that cannot start one is the dead control Law\n * 4 forbids.\n */\n readonly canStartNewConversation: boolean;\n /**\n * Empty this thread and forget its conversation id, so the NEXT send starts a\n * fresh conversation the server names.\n *\n * 🚨 WHY THIS IS ON THE KERNEL AND NOT THE HOST'S JOB. Until the lane merge\n * the conversation id was fixed for the life of the store, so \"New\n * conversation\" on the shipped `chat.conversation` source could only work if\n * the host threw the whole thread away and built another — which every\n * consumer would then have had to reimplement, including the timer, the abort\n * and the draft carry. The client still never invents an id: this clears the\n * id and lets the FIRST TURN mint it, which is exactly what the platform\n * does for a conversation that never existed.\n */\n startNewConversation(): Promise<void>;\n /**\n * The client answered a delegated tool — mark the call done HERE, now.\n *\n * The server sends no completion frame for a delegated call (it hard-suspended\n * and closed the stream), so a transcript that only moves on frames shows\n * \"Working…\" forever after the agent has visibly moved on. The host's answer\n * funnel calls this in the same tick it posts the result. Monotonic and\n * idempotent: a call already terminal is left alone and the refusal says why.\n */\n settleToolCall(answer: ChatToolAnswer): ChatToolSettlement;\n /** Release timers and abort an in-flight turn. */\n dispose(): void;\n}\n\ntype Scheduler = (run: () => void) => () => void;\n\nfunction createScheduler(coalesceMs: number | null | undefined): Scheduler {\n if (typeof coalesceMs === \"number\") {\n return (run) => {\n const handle = setTimeout(run, coalesceMs);\n return () => clearTimeout(handle);\n };\n }\n const raf = (\n globalThis as {\n requestAnimationFrame?: (cb: () => void) => number;\n cancelAnimationFrame?: (handle: number) => void;\n }\n ).requestAnimationFrame;\n if (typeof raf === \"function\") {\n const cancel = (globalThis as { cancelAnimationFrame?: (h: number) => void })\n .cancelAnimationFrame;\n return (run) => {\n const handle = raf(run);\n return () => cancel?.(handle);\n };\n }\n return (run) => {\n let cancelled = false;\n queueMicrotask(() => {\n if (!cancelled) run();\n });\n return () => {\n cancelled = true;\n };\n };\n}\n\nconst EPHEMERAL_REASON =\n \"This conversation is not being saved: no persistence is connected, so it disappears when you leave the page.\";\nconst EPHEMERAL_REMEDY =\n \"Pass a `persistence` port (createMatrxChatPersistence from @ai-matrx/chat/matrx) to keep conversations.\";\n\nexport function createChatThread(config: ChatThreadConfig): ChatThreadStore {\n const features = resolveChatFeatures(config.features);\n const errorSink = config.errorSink ?? createConsoleErrorSink();\n const notifier = config.notifier ?? createConsoleNotifier();\n const schedule = createScheduler(config.coalesceMs);\n\n let messages: ChatMessage[] = [];\n let status: ChatThreadStatus = \"idle\";\n let conversationId: string | null = config.conversationId ?? null;\n let requestId: string | null = null;\n let phase: string | null = null;\n let error: ChatErrorPart | null = null;\n let revision = 0;\n let snapshot: ChatThreadSnapshot = buildSnapshot();\n let abort: AbortController | null = null;\n let disposed = false;\n let announcedEphemeral = false;\n\n /**\n * WHICH CONVERSATION THE TRANSCRIPT IS SHOWING, as a number.\n *\n * 🚨 The defect this closes, found by the lane-merge test that switched away\n * mid-answer: cancelling a turn aborts the CONTROLLER, but a transport whose\n * frames are already queued (or one that ignores the signal, which a host may\n * legitimately write) delivers one more. `replaceMessage` appends when it\n * cannot find the id — so that late frame resurrected the previous\n * conversation's half-answer INTO the empty transcript the reader was now\n * looking at, under the new conversation's id. A stale write must be dropped,\n * not raced. Every write in `send` is stamped with the epoch it started in,\n * and `startNewConversation` / `loadConversation` bump it.\n */\n let epoch = 0;\n\n const threadListeners = new Set<() => void>();\n const messageListeners = new Map<string, Set<() => void>>();\n const dirtyMessages = new Set<string>();\n let cancelScheduled: (() => void) | null = null;\n\n function buildSnapshot(): ChatThreadSnapshot {\n return {\n conversationId,\n messages,\n status,\n requestId,\n phase,\n ephemeral: config.persistence === undefined,\n ephemeralReason:\n config.persistence === undefined\n ? `${EPHEMERAL_REASON} ${EPHEMERAL_REMEDY}`\n : null,\n revision,\n error,\n };\n }\n\n function commit(): void {\n revision += 1;\n snapshot = buildSnapshot();\n const woken = [...dirtyMessages];\n dirtyMessages.clear();\n for (const id of woken) {\n const listeners = messageListeners.get(id);\n if (!listeners) continue;\n for (const listener of listeners) listener();\n }\n for (const listener of threadListeners) listener();\n }\n\n function markDirty(messageId: string | null): void {\n if (messageId !== null) dirtyMessages.add(messageId);\n if (cancelScheduled !== null) return;\n cancelScheduled = schedule(() => {\n cancelScheduled = null;\n if (!disposed) commit();\n });\n }\n\n /** Commit immediately — state changes a caller must observe synchronously. */\n function commitNow(messageId: string | null): void {\n if (messageId !== null) dirtyMessages.add(messageId);\n cancelScheduled?.();\n cancelScheduled = null;\n commit();\n }\n\n function announceEphemeralOnce(): void {\n if (announcedEphemeral || config.persistence !== undefined) return;\n announcedEphemeral = true;\n notifier.notify({\n level: \"warning\",\n message: EPHEMERAL_REASON,\n remedy: EPHEMERAL_REMEDY,\n });\n }\n\n function replaceMessage(next: ChatMessage): void {\n const index = messages.findIndex((m) => m.id === next.id);\n if (index === -1) {\n messages = [...messages, next];\n return;\n }\n const copy = messages.slice();\n copy[index] = next;\n messages = copy;\n }\n\n function refuseSend(part: ChatErrorPart): ChatSendResult {\n error = part;\n status = \"error\";\n errorSink.capture({ code: part.code ?? \"chat_send_refused\", message: part.message });\n notifier.notify({\n level: \"error\",\n message: part.userMessage,\n ...(part.remedy === null ? {} : { remedy: part.remedy }),\n });\n commitNow(null);\n return { accepted: false, refusal: part };\n }\n\n async function send(input: ChatSendInput): Promise<ChatSendResult> {\n if (disposed) {\n return refuseSend({\n type: \"error\",\n errorType: \"disposed\",\n code: \"thread_disposed\",\n message: \"send() was called on a disposed thread\",\n userMessage: \"This conversation is no longer active.\",\n recoverable: false,\n remedy: \"Open the conversation again.\",\n });\n }\n if (status === \"submitted\" || status === \"streaming\") {\n return refuseSend({\n type: \"error\",\n errorType: \"busy\",\n code: \"turn_in_flight\",\n message: \"a turn is already streaming on this thread\",\n userMessage: \"The assistant is still answering.\",\n recoverable: true,\n remedy: \"Wait for the answer to finish, or press stop.\",\n });\n }\n\n // THE CONFIDENTIAL-ROUTING RULE, before a single byte leaves the machine.\n const routing = decideRouting(features.routing, config.transport.locality);\n if (!routing.allowed) {\n return refuseSend({\n type: \"error\",\n errorType: \"routing_refused\",\n code: routing.code,\n message: routing.userMessage,\n userMessage: routing.userMessage,\n recoverable: false,\n remedy: routing.remedy,\n });\n }\n\n announceEphemeralOnce();\n\n const fileIds = input.fileIds ?? [];\n const userMessage = createChatMessage({\n role: \"user\",\n parts: [{ type: \"text\", text: input.text }],\n status: \"complete\",\n });\n const assistantId = newLocalId(\"msg\");\n const assistantPlaceholder = createChatMessage({\n id: assistantId,\n role: \"assistant\",\n parts: [],\n status: \"pending\",\n });\n messages = [...messages, userMessage, assistantPlaceholder];\n error = null;\n status = \"submitted\";\n commitNow(assistantId);\n\n const turnEpoch = epoch;\n /** A write from a turn whose conversation is no longer on screen. */\n const stale = (): boolean => disposed || turnEpoch !== epoch;\n\n const controller = new AbortController();\n abort = controller;\n\n const projector = createChatTurnProjector({\n features,\n messageId: assistantId,\n onRefusal: (refusal: ChatTurnRefusal) =>\n errorSink.capture({\n code: refusal.code,\n message: refusal.message,\n detail: refusal.detail,\n }),\n });\n\n try {\n const handle = await config.transport.start({\n conversationId,\n isNewConversation: conversationId === null,\n store: config.persistence !== undefined,\n text: input.text,\n fileIds,\n signal: controller.signal,\n });\n if (stale()) return { accepted: true, refusal: null };\n requestId = handle.requestId;\n if (handle.conversationId !== null) conversationId = handle.conversationId;\n status = \"streaming\";\n commitNow(assistantId);\n\n if (stale()) return { accepted: true, refusal: null };\n for await (const frame of handle.frames) {\n // The reader has moved on. Stop reading the socket rather than painting\n // the previous conversation's answer into the one now on screen.\n if (stale()) return { accepted: true, refusal: null };\n projector.apply(frame);\n if (projector.state.conversationId !== null) {\n conversationId = projector.state.conversationId;\n }\n if (projector.state.phase !== null) phase = projector.state.phase;\n const live = projector.snapshot();\n replaceMessage({\n ...live,\n metadata: {\n ...live.metadata,\n requestId,\n createdAt: assistantPlaceholder.metadata.createdAt,\n },\n });\n status =\n projector.state.outcome === \"awaiting-tools\"\n ? \"awaiting-approval\"\n : \"streaming\";\n markDirty(assistantId);\n }\n\n if (stale()) return { accepted: true, refusal: null };\n const serverUserId = projector.state.serverMessageIds.user;\n if (serverUserId !== null) {\n replaceMessage({\n ...userMessage,\n metadata: { ...userMessage.metadata, serverMessageId: serverUserId },\n });\n }\n const final = projector.snapshot();\n replaceMessage({\n ...final,\n metadata: {\n ...final.metadata,\n requestId,\n createdAt: assistantPlaceholder.metadata.createdAt,\n },\n });\n error = projector.state.error;\n status = projector.state.outcome === \"error\" ? \"error\" : \"idle\";\n commitNow(assistantId);\n return { accepted: true, refusal: null };\n } catch (cause) {\n if (stale()) return { accepted: true, refusal: null };\n const aborted =\n controller.signal.aborted ||\n (cause instanceof Error && cause.name === \"AbortError\");\n const message = cause instanceof Error ? cause.message : String(cause);\n const part: ChatErrorPart = aborted\n ? {\n type: \"error\",\n errorType: \"cancelled\",\n code: \"turn_cancelled\",\n message: \"the turn was cancelled by the reader\",\n userMessage: \"You stopped this answer.\",\n recoverable: true,\n remedy: null,\n }\n : {\n type: \"error\",\n errorType: \"transport\",\n code: \"transport_failed\",\n message,\n userMessage: \"The connection to the assistant failed.\",\n recoverable: true,\n remedy: \"Try sending the message again.\",\n };\n const partial = projector.snapshot();\n replaceMessage({\n ...partial,\n // Partial text NEVER vanishes: the reader keeps what they already saw.\n parts: [...partial.parts, part],\n status: aborted ? \"cancelled\" : \"error\",\n metadata: {\n ...partial.metadata,\n requestId,\n createdAt: assistantPlaceholder.metadata.createdAt,\n },\n });\n if (!aborted) {\n error = part;\n status = \"error\";\n errorSink.capture({ code: part.code ?? \"transport_failed\", message });\n } else {\n status = \"idle\";\n }\n commitNow(assistantId);\n return { accepted: true, refusal: null };\n } finally {\n if (abort === controller) abort = null;\n }\n }\n\n async function cancel(): Promise<void> {\n const current = requestId;\n abort?.abort();\n if (current === null) return;\n try {\n await config.transport.cancel(current);\n } catch (cause) {\n errorSink.capture({\n code: \"cancel_failed\",\n message: cause instanceof Error ? cause.message : String(cause),\n });\n notifier.notify({\n level: \"warning\",\n message: \"The assistant did not confirm that it stopped.\",\n remedy: \"It stops at its next step; anything already written is saved.\",\n });\n }\n }\n\n async function loadConversation(id: string): Promise<void> {\n const persistence = config.persistence;\n if (persistence === undefined) {\n const part: ChatErrorPart = {\n type: \"error\",\n errorType: \"no_persistence\",\n code: \"history_unavailable\",\n message: \"loadConversation() was called on a thread with no persistence port\",\n userMessage: \"Past conversations are not available here.\",\n recoverable: false,\n remedy: EPHEMERAL_REMEDY,\n };\n refuseSend(part);\n return;\n }\n epoch += 1;\n const loaded = await persistence.loadThread(id);\n conversationId = id;\n messages = loaded.slice();\n status = \"idle\";\n error = null;\n commitNow(null);\n }\n\n /**\n * A fresh conversation on THIS store. An answer still arriving is stopped\n * first — leaving it running would append the old conversation's frames to\n * the empty transcript the reader is now looking at — and the stop is\n * cooperative, so what the server has already written is still saved where it\n * belongs. Doctrine: a disconnect never cancels server work, and this method\n * promises nothing more than it does.\n */\n async function startNewConversation(): Promise<void> {\n if (disposed) return;\n epoch += 1;\n if (status === \"streaming\" || status === \"submitted\") await cancel();\n abort = null;\n conversationId = null;\n requestId = null;\n phase = null;\n messages = [];\n error = null;\n status = \"idle\";\n commitNow(null);\n }\n\n return {\n getSnapshot: () => snapshot,\n subscribe(listener) {\n threadListeners.add(listener);\n return () => {\n threadListeners.delete(listener);\n };\n },\n subscribeToMessage(messageId, listener) {\n const existing = messageListeners.get(messageId) ?? new Set<() => void>();\n existing.add(listener);\n messageListeners.set(messageId, existing);\n return () => {\n existing.delete(listener);\n if (existing.size === 0) messageListeners.delete(messageId);\n };\n },\n // Reads the COMMITTED snapshot, never the in-flight draft: a per-message\n // hook that could see an uncommitted value tears against the thread.\n getMessageSnapshot: (messageId) =>\n snapshot.messages.find((m) => m.id === messageId),\n features,\n send,\n cancel,\n setMessages(next) {\n messages = next.slice();\n commitNow(null);\n },\n settleToolCall(answer) {\n const settlement = settleToolAnswer(messages, answer);\n if (!settlement.applied) {\n if (settlement.refusal !== null) {\n errorSink.capture({\n code: \"tool_state_transition_refused\",\n message: settlement.refusal,\n detail: answer,\n });\n }\n return settlement;\n }\n messages = settlement.messages.slice();\n commitNow(settlement.messageId);\n return settlement;\n },\n loadConversation,\n canStartNewConversation: true,\n startNewConversation,\n dispose() {\n disposed = true;\n cancelScheduled?.();\n cancelScheduled = null;\n abort?.abort();\n threadListeners.clear();\n messageListeners.clear();\n },\n };\n}\n","/**\n * `<ChatProvider>` — the ONE context every lane reads. Shared scaffolding.\n *\n * It carries three things and decides nothing else:\n * 1. THE THREAD STORE — the kernel's `ChatThreadStore`, or anything else with\n * that shape, which is exactly what `@ai-matrx/chat/external-store`\n * returns. That is why the prop is typed as the store interface and not as\n * a config object: a host whose state already lives in Redux or behind an\n * MV3 relay mounts the SAME provider, and the strangler in design §9 works\n * because of this one line.\n * 2. THE REACT PORTS — `renderContent`, `notifier`, `prefs`, `files` and\n * `voice`, each with a working default (THE ALL-INCLUSIVE LAW). The\n * `renderContent` default is the REAL one — `createChatContentRenderer`,\n * markdown plus content-IR kind routing — not the plain-text floor. A host\n * that mounts nothing gets formatted answers, which is what \"drop-in\"\n * means; `createPlainTextContentRenderer` stays exported as the\n * zero-dependency floor for a host that cannot take content-ir at all.\n * 3. THE SLOTS — the per-lane slot map.\n *\n * The knobs are NOT carried here: they are resolved in the kernel and read off\n * `thread.features`, so a knob has one resolution site and a host cannot end up\n * with a composer that thinks Enter sends and a transcript that does not.\n */\n\nimport {\n createContext,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\n\nimport type { ChatThreadStore } from \"../store\";\nimport type { ChatNotifierPort } from \"../ports\";\nimport { createConsoleNotifier, createWebPrefs } from \"../ports\";\nimport { createChatContentRenderer } from \"./composer/content-default\";\nimport { createUnavailableFilesPort } from \"./composer/files\";\nimport type { ChatReactPortOverrides, ChatReactPorts } from \"./ports\";\nimport { humanizeToolName } from \"./ports\";\nimport type { ChatSlots } from \"./slots\";\n\n/**\n * THE RETRY BUS.\n *\n * A failed turn's \"Try again\" must hand the reader's unsent words BACK to the\n * composer — retyping a paragraph the product lost is the cheapest possible\n * insult. The transcript (2A) and the composer (2B) are different lanes and\n * different components, so the handoff is a tiny subscribable rather than a\n * prop drilled between them.\n *\n * It is also the reason this is not a plain callback: when NOTHING is listening\n * — a transcript mounted with no composer, which is a legitimate embedding —\n * the words would vanish silently. Instead the bus says so, with the remedy.\n */\nexport interface ChatRetryBus {\n /** Hand text back to the composer. Returns false when nobody took it. */\n request(text: string): boolean;\n subscribe(listener: (text: string) => void): () => void;\n}\n\nexport function createChatRetryBus(notifier: ChatNotifierPort): ChatRetryBus {\n const listeners = new Set<(text: string) => void>();\n return {\n request(text) {\n if (listeners.size === 0) {\n notifier.notify({\n level: \"warning\",\n message:\n \"There is nowhere to put your message back, so it has not been restored for you.\",\n remedy:\n \"Copy your message from the conversation above and send it again, or mount this transcript inside a chat that has a message box.\",\n });\n return false;\n }\n for (const listener of listeners) listener(text);\n return true;\n },\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n };\n}\n\nexport interface ChatContextValue {\n thread: ChatThreadStore;\n ports: ChatReactPorts;\n slots: ChatSlots;\n retry: ChatRetryBus;\n}\n\nconst ChatContext = createContext<ChatContextValue | null>(null);\n\nexport interface ChatProviderProps {\n /**\n * The kernel store (`createChatThread`) or a host-owned source\n * (`createExternalThreadSource`). Both satisfy this interface — that is the\n * whole point of the interface.\n */\n thread: ChatThreadStore;\n ports?: ChatReactPortOverrides | undefined;\n slots?: ChatSlots | undefined;\n children: ReactNode;\n}\n\nexport function ChatProvider(props: ChatProviderProps): ReactNode {\n const { thread, ports, slots, children } = props;\n\n // The defaults are built ONCE per provider, not per render: the plain-text\n // renderer holds an \"already announced\" flag and the prefs port probes\n // localStorage,\n // and rebuilding either per render would re-announce forever and re-probe on\n // every keystroke.\n const [defaults] = useState(() => {\n const notifier = createConsoleNotifier();\n return { notifier, prefs: createWebPrefs(notifier) };\n });\n\n const notifier = ports?.notifier ?? defaults.notifier;\n\n // The retry bus AND the default content renderer are both keyed to the\n // EFFECTIVE notifier, not to the fallback one. A host that injects a notifier\n // and then never hears \"your markdown is not being formatted\" or \"there is\n // nowhere to put your message back\" has a silent failure the injection was\n // supposed to prevent — that is the bug this keying exists to stop.\n const keyedRef = useRef<{\n notifier: ChatNotifierPort;\n bus: ChatRetryBus;\n renderContent: ChatReactPorts[\"renderContent\"];\n files: ChatReactPorts[\"files\"];\n } | null>(null);\n if (keyedRef.current === null || keyedRef.current.notifier !== notifier) {\n keyedRef.current = {\n notifier,\n bus: createChatRetryBus(notifier),\n renderContent: createChatContentRenderer({ notifier }),\n // The stand-in holds an \"already announced\" flag, so it is built ONCE:\n // rebuilding it per render would re-refuse out loud on every keystroke.\n files: createUnavailableFilesPort(notifier),\n };\n }\n const retry = keyedRef.current.bus;\n const defaultRenderContent = keyedRef.current.renderContent;\n const defaultFiles = keyedRef.current.files;\n\n const value = useMemo<ChatContextValue>(\n () => ({\n thread,\n ports: {\n renderContent: ports?.renderContent ?? defaultRenderContent,\n notifier,\n prefs: ports?.prefs ?? defaults.prefs,\n files: ports?.files ?? defaultFiles,\n // `null` is a REAL answer here (\"this surface has no voice\"), so it is\n // only defaulted when the key is absent entirely.\n voice: ports?.voice ?? null,\n toolLabel: ports?.toolLabel ?? humanizeToolName,\n // `null` is a REAL answer here too (\"this surface does not choose a\n // model\"), so it is only defaulted when the key is absent entirely —\n // and the default is nothing drawn, never a dead control.\n modelPicker: ports?.modelPicker ?? null,\n // Same rule: `null` is the real answer \"this surface offers no chips\",\n // and the control row is then ABSENT rather than an empty band.\n quickset: ports?.quickset ?? null,\n },\n slots: slots ?? {},\n retry,\n }),\n [\n thread,\n ports?.renderContent,\n ports?.prefs,\n ports?.files,\n ports?.voice,\n ports?.toolLabel,\n ports?.modelPicker,\n ports?.quickset,\n notifier,\n slots,\n retry,\n defaultRenderContent,\n defaultFiles,\n defaults.prefs,\n ],\n );\n\n // A thread the provider outlives must not keep its timers and its in-flight\n // turn running. The provider does NOT dispose a thread it did not create —\n // the host owns that lifecycle — but it does warn when a thread is swapped\n // mid-stream, because the abandoned turn keeps billing.\n const previous = useRef(thread);\n useEffect(() => {\n if (previous.current === thread) return;\n const status = previous.current.getSnapshot().status;\n if (status === \"streaming\" || status === \"submitted\") {\n notifier.notify({\n level: \"warning\",\n message: \"The previous answer was still arriving when this chat switched conversations.\",\n remedy: \"Stop the answer before switching, or the run keeps going in the background.\",\n });\n }\n previous.current = thread;\n }, [thread, notifier]);\n\n return <ChatContext.Provider value={value}>{children}</ChatContext.Provider>;\n}\n\n/**\n * The context, or a THROWN error naming what is missing. A hook that returns a\n * silent `null` here produces a blank transcript with no explanation, which is\n * the failure mode Law 4 exists to close — and the stack trace is the remedy.\n */\nexport function useChatContext(): ChatContextValue {\n const value = useContext(ChatContext);\n if (value === null) {\n throw new Error(\n \"[@ai-matrx/chat] This component must be rendered inside <ChatProvider thread={…}>. Wrap your chat surface in one, passing a thread from createChatThread() or createExternalThreadSource().\",\n );\n }\n return value;\n}\n","/**\n * THE REAL `renderContent` DEFAULT — lane 2B, design §6.\n *\n * Two routes, one port:\n * - **prose** (`markdown` / `text`) → the package's safe markdown renderer,\n * incremental per message so a 2,000-token answer does not re-parse itself\n * 2,000 times;\n * - **a render block** (a content-IR kind) → `@ai-matrx/content-ir-react`'s\n * `KindInstanceRender`, the ONE door between \"I hold a kind-shaped value\"\n * and \"the reader sees it drawn properly\". Re-deriving kind routing here\n * would be a second renderer for the platform's kinds, which is banned.\n *\n * 🚨 THE PROVIDER-LESS CASE IS HONEST, NOT FATAL. `content-ir-react` THROWS\n * outside a `<ContentIrRenderProvider>` — correctly, for an app that meant to\n * mount one. But a chat dropped into a fresh Vite app has not mounted one, and\n * a thrown error there would take the whole transcript down over a single\n * block. So this renderer asks `useContentIrHostOrNull()` and, with no host,\n * renders the block's own text under ONE sentence naming what is missing and\n * how to fix it. Absent or honest — never a blank region, never a crash.\n *\n * This replaces `createPlainTextContentRenderer` as the shipped default a host\n * gets from `<ChatProvider>`. That stand-in stays exported: it is the zero-\n * dependency floor for a host that cannot take content-ir at all.\n */\n\nimport { useEffect, type ReactNode } from \"react\";\nimport { KindInstanceRender, useContentIrHostOrNull } from \"@ai-matrx/content-ir-react\";\n\nimport type { ChatNotifierPort } from \"../../ports\";\nimport { createConsoleNotifier } from \"../../ports\";\nimport type { ChatContentRequest, ChatRenderContentPort } from \"../ports\";\nimport { ChatMarkdown, createBlockCache, type MarkdownBlockCache } from \"./markdown\";\n\n/** The sentence a reader sees for a kind block with no content-IR host. */\nexport const NO_CONTENT_IR_HOST =\n \"This answer contains a structured block, and it is shown as plain text here because this app has not connected the content renderer.\";\nexport const NO_CONTENT_IR_HOST_REMEDY =\n \"Mount <ContentIrRenderProvider host={…}> from @ai-matrx/content-ir-react above your chat to draw structured blocks properly.\";\n\nfunction RenderBlock(props: {\n request: ChatContentRequest;\n cache: MarkdownBlockCache;\n notifier: ChatNotifierPort;\n announce(): void;\n}): ReactNode {\n const host = useContentIrHostOrNull();\n const announce = props.announce;\n useEffect(() => {\n if (host === null) announce();\n }, [host, announce]);\n\n if (host === null) {\n return (\n <div className=\"mx-chat-content\" data-mx-chat-content=\"block-standin\">\n <span className=\"mx-chat-standin\">{NO_CONTENT_IR_HOST}</span>\n <ChatMarkdown\n text={props.request.text}\n cacheKey={`${props.request.messageId}:${props.request.kind}`}\n cache={props.cache}\n />\n </div>\n );\n }\n\n return (\n <div className=\"mx-chat-content\" data-mx-chat-content=\"block\" data-kind={props.request.kind}>\n <KindInstanceRender\n kind={props.request.kind}\n value={props.request.data ?? props.request.text}\n variant=\"bare\"\n />\n </div>\n );\n}\n\nexport interface ChatContentRendererOptions {\n notifier?: ChatNotifierPort | undefined;\n /** Share one cache across surfaces, or pass your own for a test. */\n cache?: MarkdownBlockCache | undefined;\n}\n\n/**\n * Build the default renderer. The block cache is per-renderer, so two chats on\n * one page never share parsed blocks and a test never inherits another test's\n * cache.\n */\nexport function createChatContentRenderer(\n options: ChatContentRendererOptions = {},\n): ChatRenderContentPort {\n const notifier = options.notifier ?? createConsoleNotifier();\n const cache = options.cache ?? createBlockCache();\n // ONCE per renderer, not per block: a notice repeated per streamed frame is\n // noise, and noise is ignored.\n let announced = false;\n const announce = (): void => {\n if (announced) return;\n announced = true;\n notifier.notify({\n level: \"warning\",\n message: NO_CONTENT_IR_HOST,\n remedy: NO_CONTENT_IR_HOST_REMEDY,\n });\n };\n\n return function renderContent(request: ChatContentRequest): ReactNode {\n if (request.kind !== \"markdown\" && request.kind !== \"text\") {\n return (\n <RenderBlock\n request={request}\n cache={cache}\n notifier={notifier}\n announce={announce}\n />\n );\n }\n if (request.text === \"\") return null;\n return (\n <div className=\"mx-chat-content\" data-mx-chat-content=\"markdown\">\n <ChatMarkdown text={request.text} cacheKey={request.messageId} cache={cache} />\n </div>\n );\n };\n}\n","/**\n * THE SMALLEST MARKDOWN RENDERER THAT IS SAFE — lane 2B.\n *\n * 🚨 Read design §6 before growing this file. `matrx-frontend`'s\n * `components/mardown-display` is 108,271 lines and renders in agent apps,\n * workflows, notes, print and the studio. Absorbing it would triple this\n * package and violate C8 on its face. This is the PORT DEFAULT — the floor that\n * makes a fresh app correct with zero work — not a competitor to that tree. A\n * host with a richer pipeline injects it through `renderContent`.\n *\n * Three properties are non-negotiable, and each has a test that goes red alone:\n *\n * 1. **NO RAW HTML, EVER.** Model output is untrusted by construction. There is\n * no `dangerouslySetInnerHTML` in this file and there must never be one: a\n * chat package that injects model text as HTML ships a cross-site-scripting\n * hole to every consumer at once. Angle brackets are rendered as literal\n * text by React, which is the whole defence.\n * 2. **LINK SCHEMES ARE ALLOWLISTED.** `javascript:`, `data:` and `vbscript:`\n * hrefs are refused and the link degrades to its own text. A renderer that\n * only escapes HTML and then hands an anchor an arbitrary scheme has closed\n * one door and left the next one open.\n * 3. **PARSING IS INCREMENTAL.** A 2,000-token answer arrives as hundreds of\n * frames. Re-parsing the whole transcript per token is the classic failure\n * and the reason chats jank (design §7 rule 3). `createBlockCache()` keeps\n * the completed blocks of a growing string and re-parses ONLY the trailing\n * incomplete block.\n *\n * Reasoning is NOT handled here. The kernel already splits the platform's\n * `<reasoning>` span into its own part and the transcript draws it as an aside —\n * a markdown renderer that printed it as prose would undo that.\n */\n\nimport { Fragment, type ReactNode } from \"react\";\nimport { findTableEnd, rowCells, tableStartsAt, unescapeCellPipes } from \"@ai-matrx/content-ir/source\";\n\n/* ------------------------------------------------------------------ links -- */\n\n/**\n * The only schemes an anchor may carry. Everything else degrades to plain text.\n * `data:` is absent on purpose: `data:text/html` is a same-origin script vector.\n */\nconst SAFE_SCHEME = /^(https?:|mailto:|tel:)/i;\n/** A relative or anchor-local target a host page legitimately owns. */\nconst RELATIVE_TARGET = /^([./#?]|[A-Za-z0-9_-]+\\/)/;\n\nexport function safeHref(raw: string): string | null {\n const href = raw.trim();\n if (href === \"\") return null;\n if (SAFE_SCHEME.test(href)) return href;\n // A bare `foo:bar` with an unknown scheme is refused; a path is allowed.\n if (/^[A-Za-z][A-Za-z0-9+.-]*:/.test(href)) return null;\n if (RELATIVE_TARGET.test(href)) return href;\n return null;\n}\n\n/** Images load only over http(s), lazily, and never leak the referrer. */\nfunction safeImageSource(raw: string): string | null {\n const source = raw.trim();\n return /^https?:\\/\\//i.test(source) ? source : null;\n}\n\n/* ----------------------------------------------------------------- inline -- */\n\n/**\n * ONE pass, ordered by precedence: code span wins over everything (its body is\n * literal), then image, link, bold, italic, strikethrough, then a bare URL.\n *\n * Each alternative is a separate capture group so the branch is decided by\n * WHICH group matched, never by re-testing the slice — re-testing is how an\n * inline parser ends up quadratic on a long answer.\n */\n/**\n * 🚨 EVERY BODY IS BOUNDED, AND NONE OF THEM CROSSES A NEWLINE.\n *\n * This is not tidiness. The first version used `[\\s\\S]*?` bodies, and the\n * FIRST run against the live 9,313-character corpus answer took the test\n * runner out of memory: an unbounded lazy body matches across thousands of\n * characters, `renderInline` recurses into that whole span, and the span\n * matches again two characters shorter — thousands of levels deep, each\n * allocating its own node array. Bounding the body to one line and\n * {@link INLINE_BODY_MAX} characters is what makes the pass linear, and it is\n * also what real markdown means: emphasis does not span a blank line.\n */\nconst INLINE_BODY_MAX = 400;\n\n/**\n * 🚨 THE SOURCE, not a shared RegExp object. `renderInline` RECURSES (bold\n * inside a link, code inside a list item), and a `g`-flagged regex carries\n * `lastIndex` on the OBJECT: an inner call resets it to 0, the outer loop\n * resumes from 0, and the scan never terminates. That is not theory — it is\n * what the first version did, and it took the runner out of memory on the very\n * first live-corpus answer. A fresh matcher per call is the fix, and this\n * comment is the reason nobody \"optimises\" it back.\n */\nconst INLINE_SOURCE = [\n \"(`+)([^\\\\n]{0,400}?)\\\\1\", // 1,2 code span\n \"!\\\\[([^\\\\]\\\\n]*)\\\\]\\\\(((?:[^()\\\\s]|\\\\([^()\\\\s]*\\\\))+)\\\\)\", // 3,4 image\n \"\\\\[([^\\\\]\\\\n]+)\\\\]\\\\(((?:[^()\\\\s]|\\\\([^()\\\\s]*\\\\))+)\\\\)\", // 5,6 link\n // 🚨 The closing backreference names THIS alternative's group (\\\\7, \\\\9,\n // \\\\11). A bare \\\\1 in a joined pattern points at the CODE SPAN's group,\n // which is unmatched here and so matches empty: \"**Bold**\" rendered as\n // <strong>B</strong>old<em>*</em> (found on a live coding-sessions answer).\n \"(\\\\*\\\\*|__)(?=\\\\S)([^\\\\n]{0,400}?\\\\S)\\\\7\", // 7,8 bold\n \"(\\\\*|_)(?=\\\\S)([^*_\\\\n]{0,400}?\\\\S)\\\\9\", // 9,10 italic\n \"(~~)(?=\\\\S)([^\\\\n]{0,400}?\\\\S)\\\\11\", // 11,12 strikethrough\n \"(https?://[^\\\\s<>()]+)\", // 13 bare url\n].join(\"|\");\n\nexport function renderInline(\n text: string,\n keyPrefix: string,\n depth = 0,\n): ReactNode[] {\n // A hard floor under the recursion. Bounded bodies already make a runaway\n // impossible; this makes it impossible to REINTRODUCE one by loosening a\n // pattern, which is exactly how the first OOM happened.\n if (depth > 6 || text.length > INLINE_BODY_MAX * 64) return [text];\n const nodes: ReactNode[] = [];\n const matcher = new RegExp(INLINE_SOURCE, \"g\");\n let cursor = 0;\n let index = 0;\n let match = matcher.exec(text);\n while (match !== null) {\n if (match.index > cursor) nodes.push(text.slice(cursor, match.index));\n const key = `${keyPrefix}i${index}`;\n index += 1;\n\n if (match[1] !== undefined) {\n nodes.push(<code key={key}>{match[2] ?? \"\"}</code>);\n } else if (match[4] !== undefined) {\n const source = safeImageSource(match[4]);\n const alt = match[3] ?? \"\";\n nodes.push(\n source === null ? (\n <Fragment key={key}>{alt === \"\" ? match[4] : alt}</Fragment>\n ) : (\n <img\n key={key}\n className=\"mx-chat-md__image\"\n src={source}\n alt={alt}\n loading=\"lazy\"\n referrerPolicy=\"no-referrer\"\n />\n ),\n );\n } else if (match[6] !== undefined) {\n const href = safeHref(match[6]);\n const label = match[5] ?? \"\";\n nodes.push(\n href === null ? (\n // Law 4: the words survive; only the unsafe jump is refused.\n <Fragment key={key}>{label}</Fragment>\n ) : (\n <a key={key} href={href} target=\"_blank\" rel=\"noreferrer noopener\">\n {renderInline(label, `${key}-`, depth + 1)}\n </a>\n ),\n );\n } else if (match[8] !== undefined) {\n nodes.push(<strong key={key}>{renderInline(match[8], `${key}-`, depth + 1)}</strong>);\n } else if (match[10] !== undefined) {\n nodes.push(<em key={key}>{renderInline(match[10], `${key}-`, depth + 1)}</em>);\n } else if (match[12] !== undefined) {\n nodes.push(<del key={key}>{renderInline(match[12], `${key}-`, depth + 1)}</del>);\n } else if (match[13] !== undefined) {\n nodes.push(\n <a key={key} href={match[13]} target=\"_blank\" rel=\"noreferrer noopener\">\n {match[13]}\n </a>,\n );\n }\n cursor = match.index + match[0].length;\n match = matcher.exec(text);\n }\n if (cursor < text.length) nodes.push(text.slice(cursor));\n return nodes;\n}\n\n/* ------------------------------------------------------------------ blocks -- */\n\nexport type MarkdownBlock =\n | { type: \"code\"; language: string; body: string; closed: boolean }\n | { type: \"heading\"; level: number; body: string }\n | { type: \"quote\"; body: string }\n | { type: \"list\"; ordered: boolean; items: string[] }\n | { type: \"table\"; header: string[]; rows: string[][] }\n | { type: \"rule\" }\n | { type: \"paragraph\"; body: string };\n\nconst FENCE_LINE = /^\\s{0,3}(`{3,}|~{3,})\\s*([^\\s`]*)/;\nconst HEADING_LINE = /^(#{1,6})\\s+(.*)$/;\nconst BULLET_LINE = /^\\s{0,3}[-*+]\\s+(.*)$/;\nconst ORDERED_LINE = /^\\s{0,3}\\d{1,9}[.)]\\s+(.*)$/;\nconst QUOTE_LINE = /^\\s{0,3}>\\s?(.*)$/;\nconst RULE_LINE = /^\\s{0,3}([-*_])(\\s*\\1){2,}\\s*$/;\n/** A row's cells by THE GFM rule (@ai-matrx/content-ir/source): `\\|` stays in its cell and reads as `|`. */\nfunction tableCells(line: string): string[] {\n return rowCells(line).map(unescapeCellPipes);\n}\n\n/**\n * Parse markdown blocks from `lines`, returning the blocks AND the index of the\n * first line of the trailing block that may still grow. The cache uses that\n * index to re-parse only what is still arriving.\n */\nexport function parseBlocks(source: string): {\n blocks: MarkdownBlock[];\n /** CHARACTER offset of the last block's first line — where a growing string\n * may still change what has already been parsed. */\n openFrom: number;\n} {\n const lines = source.split(\"\\n\");\n // Character offset of each line's first character, so the cache can slice the\n // source rather than re-joining lines (re-joining loses a trailing newline\n // and silently shifts every later offset by one).\n const lineStart: number[] = [];\n {\n let at = 0;\n for (const line of lines) {\n lineStart.push(at);\n at += line.length + 1;\n }\n }\n const blocks: MarkdownBlock[] = [];\n let index = 0;\n let lastStart = 0;\n\n while (index < lines.length) {\n const line = lines[index] ?? \"\";\n\n if (line.trim() === \"\") {\n index += 1;\n continue;\n }\n // Marked AFTER the blank-line skip: a trailing blank line is not a block\n // start, and treating it as one makes the incremental cache believe the\n // whole answer is already stable and silently stop appending.\n lastStart = index;\n\n const fence = FENCE_LINE.exec(line);\n if (fence !== null) {\n const marker = fence[1] ?? \"```\";\n const language = fence[2] ?? \"\";\n const body: string[] = [];\n index += 1;\n let closed = false;\n while (index < lines.length) {\n const current = lines[index] ?? \"\";\n if (current.trimStart().startsWith(marker[0] ?? \"`\") && current.trim().length >= 3) {\n const close = FENCE_LINE.exec(current);\n if (close !== null && (close[1] ?? \"\").length >= marker.length) {\n closed = true;\n index += 1;\n break;\n }\n }\n body.push(current);\n index += 1;\n }\n // An UNCLOSED fence still renders as code. A half-arrived fence rendered\n // as prose reflows the instant it closes, and a jumping transcript is\n // what \"incremental\" means to a reader.\n blocks.push({ type: \"code\", language, body: body.join(\"\\n\"), closed });\n continue;\n }\n\n const heading = HEADING_LINE.exec(line);\n if (heading !== null) {\n blocks.push({\n type: \"heading\",\n level: (heading[1] ?? \"#\").length,\n body: heading[2] ?? \"\",\n });\n index += 1;\n continue;\n }\n\n if (RULE_LINE.test(line)) {\n blocks.push({ type: \"rule\" });\n index += 1;\n continue;\n }\n\n if (QUOTE_LINE.test(line)) {\n const body: string[] = [];\n while (index < lines.length) {\n const quote = QUOTE_LINE.exec(lines[index] ?? \"\");\n if (quote === null) break;\n body.push(quote[1] ?? \"\");\n index += 1;\n }\n blocks.push({ type: \"quote\", body: body.join(\"\\n\") });\n continue;\n }\n\n const bullet = BULLET_LINE.exec(line);\n const ordered = ORDERED_LINE.exec(line);\n if (bullet !== null || ordered !== null) {\n const isOrdered = ordered !== null;\n const items: string[] = [];\n while (index < lines.length) {\n const current = lines[index] ?? \"\";\n const match = isOrdered\n ? ORDERED_LINE.exec(current)\n : BULLET_LINE.exec(current);\n if (match === null) break;\n items.push(match[1] ?? \"\");\n index += 1;\n }\n blocks.push({ type: \"list\", ordered: isOrdered, items });\n continue;\n }\n\n // A table by THE GFM rule (verify-RC-B4): edge pipes optional, a line that\n // starts another block ends it, rows read at the header's width.\n if (tableStartsAt(lines, index)) {\n const header = tableCells(line);\n const end = findTableEnd(lines, index);\n index += 2;\n const rows: string[][] = [];\n while (index < end) {\n const cells = tableCells(lines[index] ?? \"\");\n rows.push(header.map((_h, k) => cells[k] ?? \"\"));\n index += 1;\n }\n blocks.push({ type: \"table\", header, rows });\n continue;\n }\n\n const paragraph: string[] = [];\n while (index < lines.length) {\n const current = lines[index] ?? \"\";\n if (\n current.trim() === \"\" ||\n FENCE_LINE.test(current) ||\n HEADING_LINE.test(current) ||\n BULLET_LINE.test(current) ||\n ORDERED_LINE.test(current) ||\n QUOTE_LINE.test(current) ||\n RULE_LINE.test(current)\n ) {\n break;\n }\n paragraph.push(current);\n index += 1;\n }\n blocks.push({ type: \"paragraph\", body: paragraph.join(\"\\n\") });\n }\n\n return { blocks, openFrom: lineStart[lastStart] ?? 0 };\n}\n\n/* ------------------------------------------------------- incremental cache -- */\n\nexport interface MarkdownBlockCache {\n parse(key: string, text: string): MarkdownBlock[];\n /** How many times the parser actually ran. The performance rule, measurable. */\n readonly parses: number;\n forget(key: string): void;\n}\n\n/**\n * THE INCREMENTAL RULE, as code.\n *\n * When the next text for a key is the previous text plus more (which is what a\n * stream is), everything before the last block start is reused verbatim and\n * only the tail is re-parsed. A renderer that re-parses a 40 KB answer on every\n * one of 2,000 frames is the reason chats jank at 2,000 tokens.\n */\nexport function createBlockCache(): MarkdownBlockCache {\n interface Entry {\n text: string;\n blocks: MarkdownBlock[];\n /** Blocks before `stableEnd`, which a longer string can never change. */\n stable: MarkdownBlock[];\n /** Character offset where the still-growing block begins. */\n stableEnd: number;\n }\n const entries = new Map<string, Entry>();\n let parses = 0;\n\n const cold = (key: string, text: string): MarkdownBlock[] => {\n parses += 1;\n const parsed = parseBlocks(text);\n entries.set(key, {\n text,\n blocks: parsed.blocks,\n stable: parsed.blocks.slice(0, Math.max(0, parsed.blocks.length - 1)),\n stableEnd: parsed.openFrom,\n });\n return parsed.blocks;\n };\n\n return {\n get parses() {\n return parses;\n },\n forget(key) {\n entries.delete(key);\n },\n parse(key, text) {\n const previous = entries.get(key);\n if (previous === undefined) return cold(key, text);\n if (previous.text === text) return previous.blocks;\n // Only a CONTINUATION can reuse anything. An edit, a regenerate or a\n // second message under the same key re-parses from scratch — reusing a\n // prefix that is no longer a prefix is how a renderer shows the previous\n // answer's first paragraph forever.\n if (!text.startsWith(previous.text)) return cold(key, text);\n\n parses += 1;\n const tail = parseBlocks(text.slice(previous.stableEnd));\n const blocks = [...previous.stable, ...tail.blocks];\n entries.set(key, {\n text,\n blocks,\n stable: blocks.slice(0, Math.max(0, blocks.length - 1)),\n stableEnd: previous.stableEnd + tail.openFrom,\n });\n return blocks;\n },\n };\n}\n\n/* ---------------------------------------------------------------- render -- */\n\nfunction renderBlock(block: MarkdownBlock, key: string): ReactNode {\n switch (block.type) {\n case \"code\":\n return (\n <pre key={key} className=\"mx-chat-md__pre\" data-language={block.language || undefined}>\n <code>{block.body}</code>\n </pre>\n );\n case \"heading\": {\n const level = Math.min(6, Math.max(1, block.level));\n const Tag = `h${level}` as \"h1\";\n return (\n <Tag key={key} className=\"mx-chat-md__heading\" data-level={level}>\n {renderInline(block.body, `${key}-`)}\n </Tag>\n );\n }\n case \"quote\":\n return (\n <blockquote key={key} className=\"mx-chat-md__quote\">\n {renderInline(block.body, `${key}-`)}\n </blockquote>\n );\n case \"list\":\n return block.ordered ? (\n <ol key={key} className=\"mx-chat-md__list\">\n {block.items.map((item, i) => (\n <li key={`${key}-${i}`}>{renderInline(item, `${key}-${i}-`)}</li>\n ))}\n </ol>\n ) : (\n <ul key={key} className=\"mx-chat-md__list\">\n {block.items.map((item, i) => (\n <li key={`${key}-${i}`}>{renderInline(item, `${key}-${i}-`)}</li>\n ))}\n </ul>\n );\n case \"table\":\n return (\n <div key={key} className=\"mx-chat-md__table-scroll\">\n <table className=\"mx-chat-md__table\">\n <thead>\n <tr>\n {block.header.map((cell, i) => (\n <th key={`${key}-h${i}`}>{renderInline(cell, `${key}-h${i}-`)}</th>\n ))}\n </tr>\n </thead>\n <tbody>\n {block.rows.map((row, r) => (\n <tr key={`${key}-r${r}`}>\n {row.map((cell, c) => (\n <td key={`${key}-r${r}c${c}`}>{renderInline(cell, `${key}-r${r}c${c}-`)}</td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n case \"rule\":\n return <hr key={key} className=\"mx-chat-md__rule\" />;\n case \"paragraph\":\n return (\n <p key={key} className=\"mx-chat-md__paragraph\">\n {renderInline(block.body, `${key}-`)}\n </p>\n );\n }\n}\n\nexport interface ChatMarkdownProps {\n text: string;\n /** Cache key — the message id. Two messages never share parsed blocks. */\n cacheKey: string;\n cache: MarkdownBlockCache;\n}\n\nexport function ChatMarkdown(props: ChatMarkdownProps): ReactNode {\n const blocks = props.cache.parse(props.cacheKey, props.text);\n return (\n <div className=\"mx-chat-md\">\n {blocks.map((block, index) => renderBlock(block, `b${index}`))}\n </div>\n );\n}\n","/**\n * THE `files` PORT — lane 2B, design §5.\n *\n * Upload / resolve / preview, injected by the host. The platform default is\n * `@ai-matrx/data/files` + `@ai-matrx/media` in a later lane; what ships TODAY\n * is the honest stand-in below.\n *\n * 🚨 LAW 4, applied precisely. When no host has wired attachments up, the\n * composer does not render an attach button that does nothing — the control is\n * ABSENT. But a reader can still paste a screenshot or drop a PDF onto the box\n * without ever seeing a button, and THAT path must not swallow the file: the\n * stand-in port refuses out loud, with the remedy, and the composer shows the\n * refusal where the chip would have been. Absent or honest; never dead.\n */\n\nimport { formatFileSize } from \"@ai-matrx/kit/format\";\n\nimport type { ChatNotifierPort } from \"../../ports\";\n\n/** What an upload produced. `fileId` is the durable identity a turn carries. */\nexport interface ChatUploadedFile {\n fileId: string;\n name: string;\n mediaType: string | null;\n sizeBytes: number;\n /** A preview URL, when the host can produce one. A signed URL is a handoff,\n * never an identity — the kernel only ever sends `fileId`. */\n previewUrl?: string | undefined;\n}\n\nexport interface ChatUploadOptions {\n /** Aborted when the reader removes the chip while it is still uploading. */\n signal: AbortSignal;\n /** 0..1. A port that cannot measure progress simply never calls this, and\n * the chip shows an indeterminate state rather than a fake percentage. */\n onProgress(fraction: number): void;\n}\n\nexport interface ChatFilesPort {\n /**\n * FALSE when this port cannot actually store anything. The composer reads it\n * to decide whether an attach button exists at all — a button wired to a port\n * that always refuses is the \"dead control\" Law 4 forbids.\n */\n readonly available: boolean;\n /** `accept` for the file input, when the host wants to narrow it. */\n readonly accept?: string | undefined;\n upload(file: File, options: ChatUploadOptions): Promise<ChatUploadedFile>;\n}\n\n/** The sentence a reader sees when nothing is wired up. Exported so the test\n * asserts the SAME string the screen shows, never a paraphrase. */\nexport const ATTACHMENTS_UNAVAILABLE =\n \"Attachments aren't set up here, so this file has not been sent.\";\nexport const ATTACHMENTS_UNAVAILABLE_REMEDY =\n \"Give <ChatComposerProvider> a `files` port (your upload service) to attach files to a message.\";\n\n/**\n * The shipped default: it refuses, loudly, with the remedy — and it says so\n * ONCE per port so a reader who drops five files gets one sentence, not five.\n */\nexport function createUnavailableFilesPort(\n notifier: ChatNotifierPort,\n): ChatFilesPort {\n let announced = false;\n return {\n available: false,\n upload(): Promise<ChatUploadedFile> {\n if (!announced) {\n announced = true;\n notifier.notify({\n level: \"warning\",\n message: ATTACHMENTS_UNAVAILABLE,\n remedy: ATTACHMENTS_UNAVAILABLE_REMEDY,\n });\n }\n return Promise.reject(new Error(ATTACHMENTS_UNAVAILABLE));\n },\n };\n}\n\n\nexport function tooLargeSentence(name: string, limitBytes: number): string {\n return `${name} is larger than the ${formatFileSize(limitBytes)} limit for this chat, so it has not been attached.`;\n}\n","/**\n * THE REACT-LAYER PORTS — shared scaffolding, owned by no single lane.\n *\n * The kernel's ports (`src/ports.ts`) are framework-free by construction: a\n * transport, a persistence binding and a prefs store have no React in them.\n * These three DO, because their return value is pixels or their default reads\n * the DOM, so they live here rather than polluting layer 1.\n *\n * Per THE ALL-INCLUSIVE LAW every port here ships a working default. A host\n * that injects nothing still gets a correct, honest screen.\n *\n * 🚨 ADDING A PORT: a port that nothing in the package CALLS is untested theory\n * that rots. `nav`, `shell`, `renderModelRef` and `capabilities` are named in\n * the design and are deliberately ABSENT until the lane that calls them lands —\n * the same rule phase 1 followed.\n *\n * `files` and `voice` USED to live in lane 2B's own `<ChatComposerProvider>`\n * context, because the lane contract forbade 2B editing this shared file while\n * 2A and 2C were in flight. Design §5 shows ONE port map, and at the lane merge\n * that is what they became: both are fields here, defaulted once by\n * `<ChatProvider>`. `<ChatComposerProvider>` survives as a scoped OVERRIDE — a\n * host with two composers on one page (a docked chat and a floating one) can\n * still give them different upload services — and it now inherits these\n * defaults instead of building a second set.\n */\n\nimport { humanizeIdentifier } from \"@ai-matrx/kit/text-case\";\nimport type { ReactNode } from \"react\";\n\nimport type { ChatRole } from \"../model\";\nimport type { ChatNotifierPort, ChatPrefsPort } from \"../ports\";\nimport type { ChatFilesPort } from \"./composer/files\";\nimport type { ChatQuicksetPort } from \"./plugins/quickset\";\nimport type { ChatVoicePort } from \"./composer/voice-port\";\n\n/**\n * One piece of content to turn into pixels.\n *\n * `kind` is the discriminator: `\"markdown\"` for ordinary prose, or the\n * content-IR kind name for a `render-block` part. A renderer that does not know\n * a kind must render the text honestly rather than nothing — a blank region is\n * the silent failure Law 4 forbids.\n */\nexport interface ChatContentRequest {\n kind: string;\n /** The text to render. Always present; empty string for a data-only block. */\n text: string;\n /** A render block's structured payload, when it carried one. */\n data?: unknown;\n /** True while the producing turn is still streaming — renderers must be\n * incremental and must never reflow the whole transcript per token. */\n streaming: boolean;\n role: ChatRole;\n messageId: string;\n}\n\n/**\n * THE `renderContent` PORT (design §6 — the one deliberate scope cut).\n *\n * `components/mardown-display` is 108,271 lines rendering in agent apps,\n * workflows, notes, print and the studio. Absorbing it would triple this\n * package and violate C8 on its face, so it is a SEAM with a working default\n * and a named retirement path, not an absorption.\n *\n * The default this package ships is the minimal SAFE one (plain text with\n * fenced code preserved, never HTML injection). The rich default — content-IR\n * kind routing plus a real markdown renderer — is lane 2b's, and a host's own\n * pipeline is what it injects during its migration.\n */\nexport type ChatRenderContentPort = (request: ChatContentRequest) => ReactNode;\n\n/**\n * THE `toolLabel` PORT — what a tool call is CALLED in front of a reader.\n *\n * A tool's name is a machine identifier (`workflow_catalog`, `web_search`). A\n * Creator watching an agent spend their money is owed a sentence, not a symbol:\n * the live review of 2026-09-18 read \"workflow_catalog Done Done\" at eye level\n * in the Conductor. The host that OWNS the tools owns their words, so this is a\n * port — and because a port with no default is a hole, the package ships a\n * humanizer (`humanizeToolName`) rather than the raw identifier. A host whose\n * tools have real names injects them; one that does not at least never shows\n * `snake_case` to a person.\n *\n * The machine name is not lost: it stays on the chip as `data-tool`, which is\n * what tests, the census and Developer Mode read.\n */\nexport type ChatToolLabelPort = (toolName: string) => string;\n\n/** What the model picker is handed on every render. */\nexport interface ChatModelPickerContext {\n className: string;\n}\n\n/**\n * THE `modelPicker` PORT (design §5.1 capability 4) — phase 4.\n *\n * `ModelListDropdown` has NOT moved into `@ai-matrx/agents/catalog/react` yet,\n * and until it does this package cannot render a model list without inventing\n * a roster — which would be the fifth picker on the platform, the exact thing\n * the picker ruling forbids. So the host renders its own here, the package\n * positions it, and `null` means the surface does not choose a model and NO\n * control is drawn: absent, never dead (Law 4). When the follow-on lane moves\n * the roster into the agents package, the shipped default becomes that one\n * component and this port keeps its shape.\n */\nexport type ChatModelPickerPort = (context: ChatModelPickerContext) => ReactNode;\n\n/**\n * The shipped default: the identifier through THE humanizer\n * (`@ai-matrx/kit/text-case` `humanizeIdentifier`): `workflow_catalog` →\n * \"Workflow Catalog\". An empty name becomes \"Working…\", because a chip with no\n * name at all is the bare spinner the transcript refuses to draw.\n */\nexport function humanizeToolName(toolName: string): string {\n return humanizeIdentifier(toolName) || \"Working…\";\n}\n\n/** The React ports a `<ChatProvider>` accepts. Every one has a default. */\nexport interface ChatReactPorts {\n renderContent: ChatRenderContentPort;\n notifier: ChatNotifierPort;\n prefs: ChatPrefsPort;\n /**\n * Upload / resolve / preview. The shipped default REFUSES out loud with the\n * remedy, and the composer renders no attach button at all — absent, never\n * dead (Law 4).\n */\n files: ChatFilesPort;\n /**\n * Dictation and read-aloud. `null` means the surface has no voice, and NO\n * microphone is rendered — a greyed-out one is the dead control Law 4\n * forbids. `createBrowserAudioVoicePort()` is the platform implementation.\n */\n voice: ChatVoicePort | null;\n /** What a tool call is called in front of a reader. Defaults to\n * `humanizeToolName` — never the raw identifier. */\n toolLabel: ChatToolLabelPort;\n /**\n * The model picker, rendered by the host. `null` means this surface does not\n * choose a model and nothing is drawn — see `ChatModelPickerPort`.\n */\n modelPicker: ChatModelPickerPort | null;\n /**\n * The prompt chips a host offers above the box. `null` means it offers\n * none, and NO row is drawn — an empty band above the composer is dead\n * pixels. A chip fills the box; it never sends (see `ChatQuicksetPort`).\n */\n quickset: ChatQuicksetPort | null;\n}\n\nexport type ChatReactPortOverrides = Partial<ChatReactPorts>;\n","/**\n * THE BASE HOOKS — shared scaffolding, layer 3 of the design.\n *\n * All four read the kernel through `useSyncExternalStore`, which is the shape\n * the champion (`@ai-sdk/react`) proved and the reason the store was written as\n * `getSnapshot`/`subscribe` in phase 1. Nothing here holds a second copy of\n * thread state: a hook that mirrors the store into `useState` tears under\n * concurrent rendering and drifts the moment the host mutates the store from\n * outside React.\n *\n * `useChatMessage(id)` is the performance seam that makes a 2,000-token answer\n * possible: it subscribes to ONE message, so a streaming bubble re-renders\n * while the other 200 rows do not.\n */\n\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n type KeyboardEvent as ReactKeyboardEvent,\n} from \"react\";\n\nimport type { ChatFeatures } from \"../config\";\nimport type { ChatMessage, ChatThreadSnapshot } from \"../model\";\nimport { messageText } from \"../model\";\nimport type { ChatSendResult } from \"../store\";\nimport { useChatContext } from \"./provider\";\nimport type { ChatReactPorts } from \"./ports\";\nimport type { ChatSlots } from \"./slots\";\n\nexport interface UseChatThreadResult {\n snapshot: ChatThreadSnapshot;\n messages: readonly ChatMessage[];\n status: ChatThreadSnapshot[\"status\"];\n /** True while a turn is in flight — the ONE test a stop button uses. */\n isBusy: boolean;\n conversationId: string | null;\n ephemeral: boolean;\n ephemeralReason: string | null;\n error: ChatThreadSnapshot[\"error\"];\n send(text: string, fileIds?: readonly string[]): Promise<ChatSendResult>;\n cancel(): Promise<void>;\n}\n\n/** The whole thread. Use `useChatMessage` for a row — not this. */\nexport function useChatThread(): UseChatThreadResult {\n const { thread } = useChatContext();\n const snapshot = useSyncExternalStore(\n thread.subscribe,\n thread.getSnapshot,\n thread.getSnapshot,\n );\n\n const send = useCallback(\n (text: string, fileIds?: readonly string[]) =>\n thread.send(fileIds === undefined ? { text } : { text, fileIds }),\n [thread],\n );\n const cancel = useCallback(() => thread.cancel(), [thread]);\n\n return useMemo(\n () => ({\n snapshot,\n messages: snapshot.messages,\n status: snapshot.status,\n isBusy: snapshot.status === \"submitted\" || snapshot.status === \"streaming\",\n conversationId: snapshot.conversationId,\n ephemeral: snapshot.ephemeral,\n ephemeralReason: snapshot.ephemeralReason,\n error: snapshot.error,\n send,\n cancel,\n }),\n [snapshot, send, cancel],\n );\n}\n\n/**\n * ONE message, on its own subscription.\n *\n * Returns `undefined` for an id the thread does not hold — a row that was\n * removed while its component was unmounting is ordinary, not an error.\n */\nexport function useChatMessage(messageId: string): ChatMessage | undefined {\n const { thread } = useChatContext();\n\n const subscribe = useCallback(\n (listener: () => void) => thread.subscribeToMessage(messageId, listener),\n [thread, messageId],\n );\n const getSnapshot = useCallback(\n () => thread.getMessageSnapshot(messageId),\n [thread, messageId],\n );\n\n return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n}\n\n/** The resolved knobs (platform → org → surface → user), from the kernel. */\nexport function useChatConfig(): ChatFeatures {\n return useChatContext().thread.features;\n}\n\n/** The React ports, each already defaulted by the provider. */\nexport function useChatPorts(): ChatReactPorts {\n return useChatContext().ports;\n}\n\n/** The slot map. Empty object when the host passed none. */\nexport function useChatSlots(): ChatSlots {\n return useChatContext().slots;\n}\n\nexport interface UseChatComposerStateResult {\n text: string;\n setText(next: string): void;\n /** False when the box is blank, or while a turn is in flight unless\n * `features.sendWhileBusy` (the host queues). */\n canSend: boolean;\n isBusy: boolean;\n send(): Promise<ChatSendResult | null>;\n stop(): Promise<void>;\n /**\n * The keyboard contract, decided by `features.sendKey`: Enter sends and\n * Shift+Enter newlines, or Cmd/Ctrl+Enter sends and Enter newlines. Esc\n * ALWAYS stops a running answer. Wire this to the input's `onKeyDown`.\n */\n onKeyDown(event: ReactKeyboardEvent): void;\n /** True when a failed turn just handed its words back. Clears on the next\n * edit, so a composer can flash the box once without it sticking. */\n restoredFromFailure: boolean;\n}\n\nconst DRAFT_PREFIX = \"ai-matrx.chat.draft.\";\n\n/**\n * COMPOSER STATE — the logic half, with no opinion about pixels.\n *\n * Lane 2B renders this. It lives in shared scaffolding rather than in 2B\n * because 2A needs one half of it TODAY: a failed turn's \"Try again\" hands the\n * reader's unsent words back through the retry bus, and there has to be exactly\n * one place that catches them. Two sources of composer state is how a draft\n * gets lost.\n *\n * Drafts survive reload when `features.retainDrafts` is on, keyed per\n * conversation, through the `prefs` port — which already announces itself when\n * it is memory-only.\n */\nexport function useChatComposerState(): UseChatComposerStateResult {\n const { thread, ports, retry } = useChatContext();\n const features = thread.features;\n const snapshot = useSyncExternalStore(\n thread.subscribe,\n thread.getSnapshot,\n thread.getSnapshot,\n );\n const conversationId = snapshot.conversationId;\n const isBusy = snapshot.status === \"submitted\" || snapshot.status === \"streaming\";\n\n const draftKey = `${DRAFT_PREFIX}${conversationId ?? \"new\"}`;\n const [text, setTextState] = useState<string>(() =>\n features.retainDrafts ? (ports.prefs.get(draftKey) ?? \"\") : \"\",\n );\n const [restoredFromFailure, setRestored] = useState(false);\n\n // The draft key changes when the conversation does — including the moment a\n // NEW conversation learns its server id mid-send. Carrying the in-progress\n // text across that rename is the difference between a draft that survives and\n // one that vanishes the first time someone types during the first turn.\n const keyRef = useRef(draftKey);\n useEffect(() => {\n if (keyRef.current === draftKey) return;\n const previousKey = keyRef.current;\n keyRef.current = draftKey;\n if (!features.retainDrafts) return;\n const carried = ports.prefs.get(previousKey);\n if (carried !== null && carried !== \"\") {\n ports.prefs.set(draftKey, carried);\n ports.prefs.remove(previousKey);\n setTextState(carried);\n return;\n }\n setTextState(ports.prefs.get(draftKey) ?? \"\");\n }, [draftKey, features.retainDrafts, ports.prefs]);\n\n const setText = useCallback(\n (next: string) => {\n setTextState(next);\n setRestored(false);\n if (!features.retainDrafts) return;\n if (next === \"\") ports.prefs.remove(keyRef.current);\n else ports.prefs.set(keyRef.current, next);\n },\n [features.retainDrafts, ports.prefs],\n );\n\n // Catch the words a failed turn hands back. Registered here and NOWHERE else,\n // so \"Try again\" has exactly one destination.\n useEffect(\n () =>\n retry.subscribe((restored) => {\n setTextState(restored);\n setRestored(true);\n if (features.retainDrafts) ports.prefs.set(keyRef.current, restored);\n }),\n [retry, features.retainDrafts, ports.prefs],\n );\n\n const send = useCallback(async (): Promise<ChatSendResult | null> => {\n const body = text.trim();\n if (body === \"\" || (isBusy && !features.sendWhileBusy)) return null;\n // The box is cleared OPTIMISTICALLY and the draft with it; if the kernel\n // refuses the send, the words come straight back rather than being lost.\n setTextState(\"\");\n setRestored(false);\n if (features.retainDrafts) ports.prefs.remove(keyRef.current);\n const result = await thread.send({ text: body });\n if (!result.accepted) {\n setTextState(body);\n if (features.retainDrafts) ports.prefs.set(keyRef.current, body);\n }\n return result;\n }, [text, isBusy, thread, features.retainDrafts, features.sendWhileBusy, ports.prefs]);\n\n const stop = useCallback(() => thread.cancel(), [thread]);\n\n const onKeyDown = useCallback(\n (event: ReactKeyboardEvent) => {\n if (event.key === \"Escape\") {\n if (!isBusy) return;\n event.preventDefault();\n void thread.cancel();\n return;\n }\n if (event.key !== \"Enter\") return;\n const withModifier = event.metaKey || event.ctrlKey;\n const sends =\n features.sendKey === \"enter\"\n ? !event.shiftKey && !withModifier\n : withModifier;\n if (!sends) return;\n event.preventDefault();\n void send();\n },\n [features.sendKey, isBusy, send, thread],\n );\n\n return {\n text,\n setText,\n canSend: text.trim() !== \"\" && (!isBusy || features.sendWhileBusy),\n isBusy,\n send,\n stop,\n onKeyDown,\n restoredFromFailure,\n };\n}\n\n/**\n * The text of the user turn that produced a given assistant message — what\n * \"Try again\" hands back. Exported because 2B's composer and 2A's failure card\n * must agree on what \"the unsent text\" means.\n */\nexport function precedingUserText(\n messages: readonly ChatMessage[],\n messageId: string,\n): string {\n const index = messages.findIndex((message) => message.id === messageId);\n if (index <= 0) return \"\";\n for (let cursor = index - 1; cursor >= 0; cursor -= 1) {\n const candidate = messages[cursor];\n if (candidate && candidate.role === \"user\") return messageText(candidate);\n }\n return \"\";\n}\n","/**\n * THE MINIMAL SAFE `renderContent` DEFAULT — and it announces its own limits.\n *\n * This renderer does exactly three things: it splits blank-line-separated\n * paragraphs, it preserves fenced code blocks as `<pre>`, and it never, under\n * any circumstance, puts model output into `dangerouslySetInnerHTML`. Model\n * text is untrusted by construction; a chat package that injects it as HTML\n * ships a cross-site-scripting hole to every consumer at once.\n *\n * It is a STAND-IN, and Law 4 binds stand-ins: the first time it is handed text\n * carrying markdown it cannot render — a heading, a table, a link, emphasis —\n * it says so ONCE, with the remedy. A reader looking at raw `**bold**` on a\n * screen while the package pretends everything is fine is exactly the class\n * that law exists to close.\n *\n * 🚨 THIS IS NO LONGER THE SHIPPED DEFAULT. `createChatContentRenderer`\n * (`./composer/content-default`) is — real markdown plus `@ai-matrx/content-ir-react`\n * kind routing. This one stays exported as the ZERO-DEPENDENCY FLOOR: a host\n * that cannot take content-ir at all passes it as `renderContent` and gets safe\n * plain text that says out loud what it is not formatting.\n */\n\nimport type { ReactNode } from \"react\";\n\nimport type { ChatNotifierPort } from \"../ports\";\nimport { createConsoleNotifier } from \"../ports\";\nimport type { ChatContentRequest, ChatRenderContentPort } from \"./ports\";\nimport { findTableStart } from \"@ai-matrx/content-ir/source\";\n\nconst FENCE = /```/;\n\n/**\n * Markdown this renderer would print raw. Deliberately narrow: an apostrophe or\n * an underscore inside a word must not trip the announcement, or the notice\n * becomes noise and a reader learns to ignore it.\n */\nconst UNRENDERED_MARKDOWN = [\n /^#{1,6}\\s+\\S/m, // headings\n /\\*\\*[^\\s*][^*]*\\*\\*/, // bold\n /\\[[^\\]]+\\]\\([^)\\s]+\\)/, // links\n /^\\s*[-*+]\\s+\\S/m, // bullets\n { test: (text: string) => findTableStart(text.split(\"\\n\")) !== -1 }, // tables, by THE GFM rule (content-ir/source)\n];\n\nfunction splitFencedBlocks(text: string): { code: boolean; body: string }[] {\n if (!FENCE.test(text)) return [{ code: false, body: text }];\n const segments: { code: boolean; body: string }[] = [];\n // An ODD number of fences means the last block is still arriving. It is\n // rendered as code anyway: a half-arrived fence rendered as prose reflows the\n // instant it closes, and a jumping transcript is what \"incremental\" means to\n // a reader.\n const pieces = text.split(\"```\");\n for (let index = 0; index < pieces.length; index += 1) {\n const body = pieces[index] ?? \"\";\n if (body === \"\") continue;\n segments.push({ code: index % 2 === 1, body });\n }\n return segments;\n}\n\nfunction renderProse(body: string, keyPrefix: string): ReactNode[] {\n const paragraphs = body.split(/\\n{2,}/);\n const nodes: ReactNode[] = [];\n for (let index = 0; index < paragraphs.length; index += 1) {\n const paragraph = paragraphs[index] ?? \"\";\n if (paragraph.trim() === \"\") continue;\n nodes.push(<p key={`${keyPrefix}-p${index}`}>{paragraph}</p>);\n }\n return nodes;\n}\n\n/**\n * Build the default renderer. The announcement is per-renderer, not per-module,\n * so two chats on one page do not share a \"you have been told\" flag and a test\n * never inherits another test's silence.\n */\nexport function createPlainTextContentRenderer(\n notifier: ChatNotifierPort = createConsoleNotifier(),\n): ChatRenderContentPort {\n let announced = false;\n\n const announceOnce = (): void => {\n if (announced) return;\n announced = true;\n notifier.notify({\n level: \"warning\",\n message:\n \"This chat is showing the assistant's answer as plain text, so markdown such as headings, links, bold and tables appears exactly as written instead of being formatted.\",\n remedy:\n \"Give <ChatProvider> a `renderContent` port (your own markdown/kind renderer) to format answers.\",\n });\n };\n\n return function renderContent(request: ChatContentRequest): ReactNode {\n const text = request.text;\n if (text === \"\") return null;\n\n // A render block is structured content this floor cannot lay out. It is\n // shown as its text with an honest label rather than dropped.\n if (request.kind !== \"markdown\" && request.kind !== \"text\") {\n return (\n <div className=\"mx-chat-content\" data-mx-chat-content=\"block\">\n <span className=\"mx-chat-standin\">\n {`This answer contains a ${request.kind} block. It is shown as text here until a content renderer is connected.`}\n </span>\n {renderProse(text, \"block\")}\n </div>\n );\n }\n\n for (const pattern of UNRENDERED_MARKDOWN) {\n if (pattern.test(text)) {\n announceOnce();\n break;\n }\n }\n\n const segments = splitFencedBlocks(text);\n return (\n <div className=\"mx-chat-content\" data-mx-chat-content=\"plain\">\n {segments.map((segment, index) =>\n segment.code ? (\n <pre key={`c${index}`}>\n <code>{segment.body.replace(/^[^\\n]*\\n/, \"\")}</code>\n </pre>\n ) : (\n renderProse(segment.body, `s${index}`)\n ),\n )}\n </div>\n );\n };\n}\n","/**\n * PACKAGE-OWNED GLYPHS. Two of them, inline, sized in `em`, `currentColor`.\n *\n * Deliberately NOT a dependency on `@ai-matrx/icons`: two 200-byte paths do not\n * justify an edge in the sibling graph, and an icon that inherits colour and\n * size from its button is exactly what the re-skin contract needs. Lanes that\n * need a real icon set (the composer's attach/mic/send) take the dependency\n * then, at their own call site.\n */\n\nimport type { ReactNode } from \"react\";\n\nexport function ChevronRightGlyph(): ReactNode {\n return (\n <svg\n className=\"mx-chat-chevron\"\n width=\"1em\"\n height=\"1em\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <path d=\"M6 3.5 10.5 8 6 12.5\" />\n </svg>\n );\n}\n\nexport function ArrowDownGlyph(): ReactNode {\n return (\n <svg\n width=\"1em\"\n height=\"1em\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <path d=\"M8 3v10M3.5 8.5 8 13l4.5-4.5\" />\n </svg>\n );\n}\n","/**\n * THE MESSAGE LIST — the transcript's viewport, ordering, windowing,\n * auto-scroll and its accessibility contract.\n *\n * WINDOWING IS VIRTUAL-FRIENDLY, NOT SILENTLY VIRTUAL. Above\n * `virtualizeAbove` messages the list renders the tail and puts an EXPLICIT\n * \"Show earlier messages\" boundary at the top, rather than mounting and\n * unmounting rows under the reader as they scroll. That is a deliberate choice\n * from design §7 rule 4: silent windowing breaks in-page find (⌘F stops finding\n * text that is in the conversation), breaks scroll anchoring, and breaks\n * copy-all. The rows are keyed and memoized per id and each subscribes to its\n * own message, so the expensive half of virtualization — not re-rendering\n * everything on every token — is already paid. A host that truly needs row\n * recycling at 10,000 messages wraps this list's children; it does not have to\n * fork it.\n *\n * THE ACCESSIBILITY CONTRACT, which is a shipped feature here and not host\n * homework:\n * - the transcript is `role=\"log\"` with `aria-live=\"polite\"` and\n * `aria-relevant=\"additions\"`. `additions` is the load-bearing word: a\n * screen reader announces NEW nodes and ignores text mutating inside an\n * existing node, so a streaming answer does not read out character by\n * character. Token-level live regions are what make an AI chat unusable\n * with a screen reader.\n * - a separate polite status region publishes the streaming answer at\n * SENTENCE boundaries, so a blind reader hears the answer as it arrives in\n * usable units instead of either a flood or silence.\n * - each turn is an `article` with an accessible name.\n */\n\nimport { useEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\n\nimport { TapTargetButton } from \"@ai-matrx/tap-target\";\n\nimport type { ChatMessage } from \"../../model\";\nimport { messageText } from \"../../model\";\nimport { ArrowDownGlyph } from \"../icons\";\nimport { useChatConfig, useChatSlots, useChatThread } from \"../hooks\";\nimport { useStickToBottom } from \"./auto-scroll\";\nimport { ChatMessageTurn } from \"./message-turn\";\n\nexport interface ChatMessageListProps {\n className?: string | undefined;\n /** Render a window with an explicit earlier-boundary above this count. */\n virtualizeAbove?: number | undefined;\n /** How many more turns \"Show earlier messages\" reveals. */\n pageSize?: number | undefined;\n /** Accessible name for the transcript region. */\n label?: string | undefined;\n}\n\nconst DEFAULT_WINDOW = 80;\nconst DEFAULT_PAGE = 40;\n\nexport function ChatMessageList(props: ChatMessageListProps): ReactNode {\n const virtualizeAbove = props.virtualizeAbove ?? DEFAULT_WINDOW;\n const pageSize = props.pageSize ?? DEFAULT_PAGE;\n const label = props.label ?? \"Conversation\";\n\n const thread = useChatThread();\n const features = useChatConfig();\n const slots = useChatSlots();\n const messages = thread.messages;\n\n const [revealed, setRevealed] = useState(virtualizeAbove);\n const visible = useMemo<readonly ChatMessage[]>(() => {\n if (messages.length <= revealed) return messages;\n return messages.slice(messages.length - revealed);\n }, [messages, revealed]);\n const hiddenEarlier = messages.length - visible.length;\n\n const stick = useStickToBottom(features.autoScroll);\n const { onContentChange } = stick;\n\n // The content-change signal is the thread's own revision counter: it bumps on\n // every applied change and on nothing else, so this fires exactly as often as\n // the store commits — which is once per coalesced frame, not once per token.\n const revision = thread.snapshot.revision;\n useEffect(() => {\n onContentChange();\n }, [revision, onContentChange]);\n\n const announcement = useSentenceAnnouncement(messages, thread.conversationId);\n\n return (\n <div className={joinClass(\"mx-chat__viewport\", props.className)}>\n {thread.ephemeral && thread.ephemeralReason !== null ? (\n <p className=\"mx-chat__ephemeral\" role=\"note\">\n {thread.ephemeralReason}\n </p>\n ) : null}\n\n <div\n className=\"mx-chat__scroll\"\n ref={stick.ref}\n role=\"log\"\n aria-live=\"polite\"\n aria-relevant=\"additions\"\n aria-label={label}\n data-pinned={stick.pinned}\n tabIndex={0}\n >\n {slots.transcriptHeader ?? null}\n\n {messages.length === 0 ? (\n renderEmpty(slots.empty)\n ) : (\n <ol className=\"mx-chat__messages\">\n {hiddenEarlier > 0 ? (\n <li className=\"mx-chat__earlier\">\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n onClick={() => setRevealed((count) => count + pageSize)}\n >\n {`Show earlier messages (${hiddenEarlier} hidden)`}\n </button>\n </li>\n ) : null}\n {visible.map((message, index) => {\n // THE TURN RHYTHM. A turn that continues the SAME speaker is a\n // paragraph, not a new turn: the assistant has no bubble, so two\n // of its answers in a row separated by the full inter-turn gap\n // read as one reply with a hole in it. The neighbour check lives\n // HERE because the turn itself subscribes to one message and must\n // not learn about its siblings (that subscription is what keeps a\n // long transcript from re-rendering on every token). The first\n // visible row never continues anything — above it is the\n // \"Show earlier messages\" boundary, not the previous speaker.\n const continued =\n index > 0 && visible[index - 1]?.role === message.role;\n return (\n <li key={message.id} {...(continued ? { \"data-continued\": \"true\" } : {})}>\n <ChatMessageTurn messageId={message.id} continued={continued} />\n </li>\n );\n })}\n </ol>\n )}\n\n {slots.transcriptFooter ?? null}\n </div>\n\n {/* The sentence-boundary announcer. Off-screen, never focusable, and\n deliberately OUTSIDE the log so the two regions cannot double-read. */}\n <p className=\"mx-chat__sr-only\" role=\"status\" aria-live=\"polite\">\n {announcement}\n </p>\n\n {stick.hasUnseenBelow ? (\n <div className=\"mx-chat__jump\">\n <TapTargetButton\n icon={<ArrowDownGlyph />}\n ariaLabel=\"Jump to the latest message\"\n tooltip={false}\n onClick={stick.jumpToLatest}\n />\n </div>\n ) : null}\n </div>\n );\n}\n\nfunction joinClass(base: string, extra: string | undefined): string {\n return extra === undefined || extra === \"\" ? base : `${base} ${extra}`;\n}\n\nfunction renderEmpty(slot: ReactNode | (() => ReactNode) | undefined): ReactNode {\n if (typeof slot === \"function\") return slot();\n if (slot !== undefined) return slot;\n return <ChatEmptyState />;\n}\n\n/** The shipped empty state. Replaceable through the `empty` slot. */\nexport function ChatEmptyState(props: {\n title?: string | undefined;\n body?: string | undefined;\n}): ReactNode {\n return (\n <div className=\"mx-chat-empty\">\n <span className=\"mx-chat-empty__title\">{props.title ?? \"Start the conversation\"}</span>\n <span>{props.body ?? \"Ask a question, describe what you need, or paste something in.\"}</span>\n </div>\n );\n}\n\n/**\n * Publish the streaming answer to a screen reader at SENTENCE boundaries.\n *\n * Only the completed sentences are ever published, so the announcement changes\n * a handful of times per answer instead of once per token, and a reader hears\n * whole thoughts. When the turn finishes, anything after the last terminator is\n * released too — otherwise an answer ending without punctuation is never read.\n */\nimport { speakableText } from \"../plain-text\";\n\n// Re-exported where it has always been, so no consumer import breaks.\nexport { speakableText };\n\nconst TERMINATORS = /[.!?。!?][\"')\\]]?\\s/g;\n\n/**\n * The live region belongs to the conversation ON SCREEN.\n *\n * Independent live review, 2026-09-18: after switching conversations the\n * region still held the whole finished reply of the conversation the reader\n * had LEFT — a screen reader that caught up late read out an answer from\n * another transcript as if it had just arrived. Switching clears the region\n * and marks the newly loaded transcript as already-heard, so what is announced\n * from then on is only what this conversation says NEXT: loaded history is\n * something the reader can go and read, never something that just happened.\n */\nexport function useSentenceAnnouncement(\n messages: readonly ChatMessage[],\n conversationId: string | null,\n): string {\n const [announced, setAnnounced] = useState(\"\");\n const publishedRef = useRef(0);\n\n const last = messages[messages.length - 1];\n const isAssistant = last !== undefined && last.role === \"assistant\";\n const text = isAssistant ? speakableText(messageText(last)) : \"\";\n const done = isAssistant && last.status !== \"streaming\" && last.status !== \"pending\";\n const messageId = last?.id ?? \"\";\n\n const idRef = useRef(messageId);\n if (idRef.current !== messageId) {\n idRef.current = messageId;\n publishedRef.current = 0;\n }\n\n const conversationRef = useRef(conversationId);\n if (conversationRef.current !== conversationId) {\n // An unsaved thread ADOPTING the id the server just named it is not a\n // switch — the words being announced are the ones still arriving. Only a\n // move away from a conversation that already had an identity is.\n const switched = conversationRef.current !== null;\n conversationRef.current = conversationId;\n if (switched) {\n idRef.current = messageId;\n publishedRef.current = text.length;\n if (announced !== \"\") setAnnounced(\"\");\n }\n }\n\n useEffect(() => {\n if (!isAssistant) return;\n TERMINATORS.lastIndex = 0;\n let boundary = 0;\n let match: RegExpExecArray | null = TERMINATORS.exec(text);\n while (match !== null) {\n boundary = match.index + match[0].length;\n match = TERMINATORS.exec(text);\n }\n const upTo = done ? text.length : boundary;\n if (upTo <= publishedRef.current) return;\n const next = text.slice(publishedRef.current, upTo).trim();\n publishedRef.current = upTo;\n if (next !== \"\") setAnnounced(next);\n }, [text, done, isAssistant]);\n\n return announced;\n}\n","/**\n * STICK TO BOTTOM — by READER INTENT, never by content (design §7 rule 5).\n *\n * The rule the champions share and the one everyone gets wrong: the transcript\n * follows new content only while the reader is AT the bottom. The moment they\n * scroll up they are reading, and yanking them back down mid-sentence is the\n * single most hated behaviour a chat can have. Pinning is released on that\n * scroll and restored when they return to the bottom themselves — or when they\n * press the \"jump to latest\" affordance, which is the only thing allowed to\n * scroll on their behalf.\n *\n * Two details that are load-bearing and easy to lose:\n * - the release must distinguish the READER's scroll from OUR scroll, or the\n * programmatic scroll that follows a token releases the pin it just used;\n * - `atBottom` needs a tolerance. Sub-pixel layout, a growing caret and\n * fractional device pixels mean an exact equality test reports \"not at the\n * bottom\" on a transcript that visibly is.\n */\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\n/** Pixels from the true bottom that still count as \"at the bottom\". */\nexport const STICK_TOLERANCE_PX = 48;\n\nexport interface StickToBottom {\n /** Attach to the scrolling element. */\n ref: (node: HTMLElement | null) => void;\n /** True while the transcript is following new content. */\n pinned: boolean;\n /** True when there is newer content the reader has not scrolled to. */\n hasUnseenBelow: boolean;\n /** Scroll to the bottom and re-pin. The \"jump to latest\" action. */\n jumpToLatest(): void;\n /** Call whenever the transcript's content changed. */\n onContentChange(): void;\n}\n\nexport function useStickToBottom(enabled: boolean): StickToBottom {\n const elementRef = useRef<HTMLElement | null>(null);\n const pinnedRef = useRef(true);\n const programmaticRef = useRef(false);\n const [pinned, setPinned] = useState(true);\n const [hasUnseenBelow, setHasUnseen] = useState(false);\n\n const isAtBottom = useCallback((node: HTMLElement): boolean => {\n const distance = node.scrollHeight - node.scrollTop - node.clientHeight;\n return distance <= STICK_TOLERANCE_PX;\n }, []);\n\n const scrollToBottom = useCallback((node: HTMLElement): void => {\n // Mark OUR scroll so the scroll handler it fires does not read as the\n // reader changing their mind.\n programmaticRef.current = true;\n node.scrollTop = node.scrollHeight;\n }, []);\n\n const handleScroll = useCallback((): void => {\n const node = elementRef.current;\n if (node === null) return;\n if (programmaticRef.current) {\n programmaticRef.current = false;\n return;\n }\n const bottom = isAtBottom(node);\n pinnedRef.current = bottom;\n setPinned(bottom);\n if (bottom) setHasUnseen(false);\n }, [isAtBottom]);\n\n const ref = useCallback(\n (node: HTMLElement | null) => {\n const previous = elementRef.current;\n if (previous !== null) previous.removeEventListener(\"scroll\", handleScroll);\n elementRef.current = node;\n if (node !== null) node.addEventListener(\"scroll\", handleScroll, { passive: true });\n },\n [handleScroll],\n );\n\n useEffect(\n () => () => {\n const node = elementRef.current;\n if (node !== null) node.removeEventListener(\"scroll\", handleScroll);\n },\n [handleScroll],\n );\n\n const onContentChange = useCallback((): void => {\n const node = elementRef.current;\n if (node === null) return;\n if (!enabled) return;\n if (pinnedRef.current) {\n scrollToBottom(node);\n return;\n }\n // Not pinned: the reader is up the page and something new arrived below.\n setHasUnseen(true);\n }, [enabled, scrollToBottom]);\n\n const jumpToLatest = useCallback((): void => {\n const node = elementRef.current;\n if (node === null) return;\n scrollToBottom(node);\n pinnedRef.current = true;\n setPinned(true);\n setHasUnseen(false);\n }, [scrollToBottom]);\n\n return { ref, pinned, hasUnseenBelow, jumpToLatest, onContentChange };\n}\n","/**\n * ONE TURN. Memoized, and subscribed to ONE message.\n *\n * This component is the reason a 2,000-token answer does not jank: it reads\n * `useChatMessage(id)`, which wakes on that message alone, and it is wrapped in\n * `memo` so the other 200 rows in a long transcript do not re-render when a\n * token lands. De-memoize it and the streaming budget in design §7 goes red —\n * there is a falsifiability leg that proves exactly that.\n */\n\nimport { memo, type ReactNode } from \"react\";\n\nimport type { ChatMessage, ChatPart } from \"../../model\";\nimport { messageText } from \"../../model\";\nimport { precedingUserText, useChatConfig, useChatMessage, useChatPorts, useChatSlots } from \"../hooks\";\nimport { useChatContext } from \"../provider\";\nimport { ChatMessageActions } from \"../plugins/message-actions\";\nimport {\n ChatAskUserPartView,\n ChatFailurePartView,\n ChatFilePartView,\n ChatNoticePartView,\n ChatReasoningAside,\n ChatRenderBlockPartView,\n ChatSourcePartView,\n ChatTextPartView,\n ChatToolPartView,\n} from \"./parts\";\n\nexport interface ChatMessageTurnProps {\n messageId: string;\n /**\n * This turn continues the SAME speaker as the row above it. Decided by the\n * list (which is the only thing that can see a turn's neighbours) and carried\n * on the article as `data-continued` so a host can style the turn itself, not\n * only its row.\n */\n continued?: boolean | undefined;\n}\n\nexport const ChatMessageTurn = memo(function ChatMessageTurn(\n props: ChatMessageTurnProps,\n): ReactNode {\n const message = useChatMessage(props.messageId);\n const features = useChatConfig();\n const { renderContent } = useChatPorts();\n const slots = useChatSlots();\n const { thread, retry } = useChatContext();\n\n if (message === undefined) return null;\n\n const streaming = message.status === \"streaming\" || message.status === \"pending\";\n const role = message.role;\n\n if (role === \"user\") {\n return (\n <article\n className=\"mx-chat-turn\"\n data-role=\"user\"\n {...(props.continued ? { \"data-continued\": \"true\" } : {})}\n data-message-id={message.id}\n aria-label=\"Your message\"\n >\n <div className=\"mx-chat-turn__bubble\">\n {renderContent({\n kind: \"markdown\",\n text: messageText(message),\n streaming: false,\n role,\n messageId: message.id,\n })}\n </div>\n {renderMeta(message, slots.renderTurnMeta)}\n {features.messageActions ? <ChatMessageActions message={message} /> : null}\n </article>\n );\n }\n\n const retryText = precedingUserText(thread.getSnapshot().messages, message.id);\n\n return (\n <article\n className=\"mx-chat-turn\"\n data-role={role}\n {...(props.continued ? { \"data-continued\": \"true\" } : {})}\n data-status={message.status}\n data-message-id={message.id}\n aria-label=\"Assistant message\"\n aria-busy={streaming || undefined}\n >\n <div className=\"mx-chat-turn__body\">\n {message.parts.map((part, index) => {\n const override = slots.renderPart?.(part, { message, index, streaming });\n if (override !== undefined) {\n return <div key={keyFor(part, index)}>{override}</div>;\n }\n return (\n <div key={keyFor(part, index)}>\n {renderPart(part, {\n message,\n streaming,\n renderContent,\n expandTools: features.expandToolCardsByDefault,\n showReasoning: features.showReasoningByDefault,\n retryText,\n onRetry: (text: string) => retry.request(text),\n })}\n </div>\n );\n })}\n\n {/* A turn that has been accepted but has said nothing yet still has to\n look ALIVE. A blank region while the server thinks is how a reader\n concludes the product is broken. */}\n {streaming && message.parts.length === 0 ? (\n <span className=\"mx-chat-caret\" data-mx-chat=\"waiting\" aria-hidden=\"true\" />\n ) : null}\n\n {/* A REPLY THAT WAS STOPPED SAYS SO, HERE, FOR GOOD. A toast outside\n the panel is not a marker: it disappears, and what is left is a\n half-sentence the transcript presents as the answer (live review,\n 2026-09-18). Quiet, never a card — the words the reader already saw\n stay exactly as they were. It is skipped when the turn already\n carries the cancelled card, because the kernel's own stop appends\n one and two markers for one event is noise. */}\n {showStoppedMarker(message) ? (\n <p className=\"mx-chat-turn__stopped\" role=\"note\">\n Stopped\n </p>\n ) : null}\n </div>\n {renderMeta(message, slots.renderTurnMeta)}\n {features.messageActions ? <ChatMessageActions message={message} /> : null}\n </article>\n );\n});\n\nfunction showStoppedMarker(message: ChatMessage): boolean {\n if (message.status !== \"cancelled\") return false;\n return !message.parts.some(\n (part) => part.type === \"error\" && part.errorType === \"cancelled\",\n );\n}\n\nfunction keyFor(part: ChatPart, index: number): string {\n if (part.type === \"tool\") return `tool:${part.toolCallId}`;\n if (part.type === \"render-block\") return `block:${part.blockId}`;\n return `${part.type}:${index}`;\n}\n\nfunction renderMeta(\n message: ChatMessage,\n slot: ((message: ChatMessage) => ReactNode | undefined) | undefined,\n): ReactNode {\n const override = slot?.(message);\n if (override !== undefined) return override;\n // The model name, when the server reported one. NEVER a UUID and never a\n // guess: `renderModelRef` is a later lane's port, and until it exists the\n // honest thing is to print what the completion actually named, or nothing.\n if (message.metadata.modelRef === null) return null;\n return <div className=\"mx-chat-turn__meta\">{message.metadata.modelRef}</div>;\n}\n\ninterface PartRenderContext {\n message: ChatMessage;\n streaming: boolean;\n renderContent: ReturnType<typeof useChatPorts>[\"renderContent\"];\n expandTools: boolean;\n showReasoning: boolean;\n retryText: string;\n onRetry(text: string): void;\n}\n\nfunction renderPart(part: ChatPart, context: PartRenderContext): ReactNode {\n switch (part.type) {\n case \"text\":\n return (\n <ChatTextPartView\n part={part}\n renderContent={context.renderContent}\n streaming={context.streaming}\n role={context.message.role}\n messageId={context.message.id}\n />\n );\n case \"reasoning\":\n return <ChatReasoningAside part={part} defaultOpen={context.showReasoning} />;\n case \"tool\":\n return <ChatToolPartView part={part} defaultExpanded={context.expandTools} />;\n case \"notice\":\n return <ChatNoticePartView part={part} />;\n case \"error\":\n return (\n <ChatFailurePartView\n part={part}\n retryText={context.retryText}\n onRetry={context.onRetry}\n />\n );\n case \"render-block\":\n return (\n <ChatRenderBlockPartView\n part={part}\n renderContent={context.renderContent}\n messageId={context.message.id}\n role={context.message.role}\n />\n );\n case \"file\":\n return <ChatFilePartView part={part} />;\n case \"source\":\n return <ChatSourcePartView part={part} />;\n case \"ask-user\":\n return <ChatAskUserPartView part={part} />;\n default: {\n // An exhaustiveness check that FAILS THE BUILD when a part type is added\n // to the model and nobody taught the transcript to draw it. A silent\n // `return null` here is how a part type ships invisible.\n const unreachable: never = part;\n return unreachable;\n }\n }\n}\n","/**\n * PHASE 4, CAPABILITY 12 — MESSAGE ACTIONS.\n *\n * The bar under a finished turn: copy it, run it again, put your own words\n * back in the box to change them, and whatever the HOST can offer that only it\n * can name (a hand-off into a step, \"go deeper\", an export).\n *\n * THE RULES THIS FILE IS WRITTEN AGAINST, each of them a real failure:\n *\n * 1. **No icon without a label.** Every action is a real `<button>` whose\n * visible content is its words. A row of unlabelled glyphs under an answer\n * is a guessing game, and it is the first thing a keyboard or screen-reader\n * user loses.\n * 2. **Keyboard reachable, in reading order.** The bar is a plain element with\n * buttons in DOM order inside a named group, so Tab reaches them where the\n * eye expects and a screen reader says which turn they belong to. No\n * `tabIndex`, no roving focus, no hover-only reveal: an action that only\n * exists under a mouse pointer does not exist.\n * 3. **ABSENT, never dead.** \"Run it again\" is missing when there is nothing\n * to run again; \"Edit\" is missing on an empty message; the whole bar is\n * missing while the turn is still arriving. Nothing here is ever a greyed\n * control with no sentence (Law 4).\n * 4. **Nothing fails silently.** A browser with no clipboard permission is\n * TOLD about, with the remedy, through the notifier — the same sink every\n * other refusal in this package uses.\n *\n * WHERE THE WORDS GO. \"Edit and send again\" hands the message back through THE\n * RETRY BUS — the one destination the composer already listens on — rather\n * than inventing a second path into the box. If nothing is listening the bus\n * says so itself, which is exactly why it is a bus.\n */\n\nimport { copyText } from \"@ai-matrx/kit/clipboard\";\nimport { useCallback, useEffect, useRef, useState, type ReactNode } from \"react\";\n\nimport type { ChatMessage } from \"../../model\";\nimport { messageText } from \"../../model\";\nimport { precedingUserText, useChatPorts, useChatSlots } from \"../hooks\";\nimport { useChatContext } from \"../provider\";\nimport { speakableText } from \"../plain-text\";\n\nexport const CLIPBOARD_REFUSED = \"Your message could not be copied.\";\nexport const CLIPBOARD_REFUSED_REMEDY =\n \"Select the text and copy it with your keyboard — this browser did not allow the page to use the clipboard.\";\n\n/** How long the bar says \"Copied\" before going back to its own words. */\nconst COPIED_MS = 2000;\n\nexport interface ChatMessageActionsProps {\n message: ChatMessage;\n}\n\n/**\n * The action bar for ONE turn.\n *\n * Rendered by `<ChatMessageTurn>` when `features.messageActions` is on (the\n * platform default), and exported so a host composing its own turn can mount\n * it directly.\n */\nexport function ChatMessageActions(props: ChatMessageActionsProps): ReactNode {\n const { message } = props;\n const { notifier } = useChatPorts();\n const slots = useChatSlots();\n const { thread, retry } = useChatContext();\n const [copied, setCopied] = useState<\"text\" | \"markdown\" | null>(null);\n const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(\n () => () => {\n if (timer.current !== null) clearTimeout(timer.current);\n },\n [],\n );\n\n const copy = useCallback(\n (value: string, which: \"text\" | \"markdown\") => {\n // THE one clipboard door (kit `copyText`): async write, textarea fallback, never throws.\n // \"Copied\" shows only when the words landed; a refusal says so once, through the host.\n void copyText(value, {\n failureMessage: CLIPBOARD_REFUSED,\n notify: (message, kind) => {\n if (kind === \"error\") notifier.notify({ level: \"warning\", message, remedy: CLIPBOARD_REFUSED_REMEDY });\n },\n }).then((copiedOk) => {\n if (!copiedOk) return;\n setCopied(which);\n if (timer.current !== null) clearTimeout(timer.current);\n timer.current = setTimeout(() => setCopied(null), COPIED_MS);\n });\n },\n [notifier],\n );\n\n // A turn still arriving has no finished words to copy and nothing to run\n // again — the bar appears when the turn is over, not before.\n if (message.status === \"streaming\" || message.status === \"pending\") return null;\n\n const raw = messageText(message);\n const host = slots.messageActions?.(message);\n const isUser = message.role === \"user\";\n // What \"run it again\" would send: the words that produced THIS answer.\n const again = isUser ? \"\" : precedingUserText(thread.getSnapshot().messages, message.id);\n\n if (raw === \"\" && host === undefined) return null;\n\n return (\n <div\n className=\"mx-chat-actions\"\n role=\"group\"\n aria-label={isUser ? \"Actions for your message\" : \"Actions for this answer\"}\n data-message-id={message.id}\n >\n {raw === \"\" ? null : (\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n data-action=\"copy-text\"\n onClick={() => copy(speakableText(raw), \"text\")}\n >\n {copied === \"text\" ? \"Copied\" : \"Copy\"}\n </button>\n )}\n\n {/* Two copies, because they are two different things a reader wants: the\n words to paste into a message, and the markdown to paste into a\n document. The assistant's answer is the only one that carries\n formatting worth keeping, so the user's own turn offers one. */}\n {raw === \"\" || isUser ? null : (\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n data-action=\"copy-markdown\"\n onClick={() => copy(raw, \"markdown\")}\n >\n {copied === \"markdown\" ? \"Copied\" : \"Copy as markdown\"}\n </button>\n )}\n\n {again === \"\" ? null : (\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n data-action=\"retry\"\n onClick={() => void thread.send({ text: again })}\n >\n Ask again\n </button>\n )}\n\n {isUser && raw !== \"\" ? (\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n data-action=\"edit\"\n onClick={() => retry.request(raw)}\n >\n Edit and send again\n </button>\n ) : null}\n\n {host}\n\n {/* The confirmation is SAID, not only shown: a sighted reader sees the\n button's word change, and everyone else hears this. It exists ONLY\n while there is something to say — an always-present empty live region\n under every turn is a second `role=\"status\"` in the transcript, and\n the transcript already owns the one that announces answers. */}\n {copied === null ? null : (\n <span className=\"mx-chat__sr-only\" role=\"status\" aria-live=\"polite\">\n Copied to the clipboard\n </span>\n )}\n </div>\n );\n}\n","/**\n * PLAIN WORDS — SHARED, because two lanes need the same stripping.\n *\n * It lives here rather than in the transcript because phase 4's \"Copy\" hands a\n * reader the same prose the screen reader hears, and a plugin importing it out\n * of `transcript/message-list.tsx` made the two files a CYCLE: the list\n * imports the turn, the turn imports the action bar, the action bar imported\n * the list. Under that cycle the live region came up EMPTY and four transcript\n * tests went red — which is exactly the kind of quiet breakage a cycle causes.\n */\n\nimport { findCodeRanges } from \"@ai-matrx/content-ir/source\";\n\n/**\n * WHAT A SCREEN READER HEARS — AND WHAT \"COPY\" PUTS ON THE CLIPBOARD — IS\n * PROSE, NOT SYNTAX.\n *\n * The transcript renders markdown through `renderContent`; this region carries\n * the SAME answer as text, so it carried the asterisks, hashes and URLs with it\n * — a reader heard 'star star Summarize Page Text star star' (live review,\n * 2026-09-18). This strips the marks and keeps the words, including the link's\n * text and the code span's content, which are part of the sentence.\n */\nexport function speakableText(text: string): string {\n // Code first, by THE ONE CommonMark code-range rule (@ai-matrx/content-ir\n // /source): a fence is heard as \"code block\", a code span keeps its words.\n // A naive /```…```/ here paired a mid-sentence \"(in a ```python fence)\"\n // with the next real fence and read the prose between them as \"code block\".\n // Code is held behind placeholders so the markup passes never touch it.\n const held: string[] = [];\n let staged = \"\";\n let at = 0;\n for (const r of findCodeRanges(text)) {\n if (r.start < at) continue;\n staged += text.slice(at, r.start);\n const raw = text.slice(r.start, r.end);\n held.push(r.kind === \"fence\" ? \" code block \" : spanWords(raw));\n staged += `\\uE300${held.length - 1}\\uE301`;\n at = r.end;\n }\n staged += text.slice(at);\n return staged\n .replace(/!\\[([^\\]]*)\\]\\([^)]*\\)/g, \"$1\")\n .replace(/\\[([^\\]]*)\\]\\([^)]*\\)/g, \"$1\")\n .replace(/^\\s{0,3}#{1,6}\\s+/gm, \"\")\n .replace(/^\\s{0,3}>\\s?/gm, \"\")\n .replace(/^\\s{0,3}([*+-]|\\d+\\.)\\s+/gm, \"\")\n .replace(/(\\*\\*\\*|___)(.+?)\\1/g, \"$2\")\n .replace(/(\\*\\*|__)(.+?)\\1/g, \"$2\")\n .replace(/(\\*|_)(.+?)\\1/g, \"$2\")\n .replace(/~~(.+?)~~/g, \"$1\")\n .replace(/^\\s{0,3}([-*_]\\s?){3,}$/gm, \"\")\n .replace(/\\uE300(\\d+)\\uE301/g, (_m, k: string) => held[Number(k)] ?? \"\")\n .replace(/[ \\t]{2,}/g, \" \");\n}\n\n/** A code span's words: the backtick runs off, one padding space each side off (CommonMark). */\nfunction spanWords(raw: string): string {\n const ticks = /^`+/.exec(raw)?.[0].length ?? 0;\n const inner = raw.slice(ticks, raw.length - ticks);\n return /^ .* $/s.test(inner) && inner.trim() ? inner.slice(1, -1) : inner;\n}\n","/**\n * PART RENDERERS — one part of one message becomes pixels.\n *\n * Every text-shaped part goes through the `renderContent` PORT, never through a\n * markdown tree owned by this file (design §6). That is the one deliberate\n * scope cut in the whole package, and it is why a 108k-line rendering system\n * did not have to be absorbed before a chat could ship.\n */\n\nimport { useState, type ReactNode } from \"react\";\n\nimport { Button, ErrorBox } from \"@ai-matrx/design-system\";\n\nimport type {\n ChatAskUserPart,\n ChatErrorPart,\n ChatFilePart,\n ChatNoticePart,\n ChatReasoningPart,\n ChatRenderBlockPart,\n ChatSourcePart,\n ChatTextPart,\n ChatToolPart,\n} from \"../../model\";\nimport { ChevronRightGlyph } from \"../icons\";\nimport type { ChatRenderContentPort } from \"../ports\";\nimport { ChatToolChip } from \"./tool-chip\";\n\n/* ---------------------------------------------------------------- text ---- */\n\nexport interface ChatTextPartViewProps {\n part: ChatTextPart;\n renderContent: ChatRenderContentPort;\n streaming: boolean;\n role: \"user\" | \"assistant\" | \"system\" | \"tool\";\n messageId: string;\n}\n\nexport function ChatTextPartView(props: ChatTextPartViewProps): ReactNode {\n return (\n <>\n {props.renderContent({\n kind: \"markdown\",\n text: props.part.text,\n streaming: props.streaming,\n role: props.role,\n messageId: props.messageId,\n })}\n </>\n );\n}\n\n/* ----------------------------------------------------------- reasoning ---- */\n\n/**\n * THE REASONING ASIDE — collapsed by default, and it says what it is.\n *\n * Two facts from the live capture shape this. First, the platform does not emit\n * `reasoning_chunk` on the Google path: the thinking arrives as ordinary chunk\n * text wrapped in a `<reasoning>` sentinel, which the kernel splits out. Second,\n * today's `/chat` prints that raw sentinel on screen because the shipped\n * projection concatenates it into the answer. A reader must never see it, and\n * the reasoning must never be mistaken for the answer — hence a separate,\n * quiet, collapsed region rather than dimmer prose in the same flow.\n */\nexport interface ChatReasoningAsideProps {\n part: ChatReasoningPart;\n defaultOpen: boolean;\n}\n\nexport function ChatReasoningAside(props: ChatReasoningAsideProps): ReactNode {\n const { part, defaultOpen } = props;\n const [open, setOpen] = useState(defaultOpen);\n const streaming = part.state === \"streaming\";\n const label = streaming ? \"Thinking…\" : \"Thought process\";\n\n return (\n <div className=\"mx-chat-reasoning\" data-streaming={streaming}>\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n aria-expanded={open}\n onClick={() => setOpen((value) => !value)}\n >\n <span data-open={open} className=\"mx-chat-chevron-wrap\">\n <ChevronRightGlyph />\n </span>\n {label}\n </button>\n {open ? (\n <div className=\"mx-chat-reasoning__body\" data-mx-chat-reasoning=\"body\">\n {part.text}\n </div>\n ) : null}\n </div>\n );\n}\n\n/* -------------------------------------------------------------- notice ---- */\n\n/**\n * A server notice, IN the transcript rather than in a toast.\n *\n * The corpus proves a run can lose half its requested tools and answer anyway.\n * A reader who is not told has been lied to — and a toast that disappeared\n * before they looked up is the same lie with better manners.\n */\nexport function ChatNoticePartView(props: { part: ChatNoticePart }): ReactNode {\n const { part } = props;\n return (\n <div className=\"mx-chat-notice\" data-level={part.level} role=\"note\">\n <span>{part.userMessage}</span>\n {part.remedy === null ? null : (\n <span className=\"mx-chat-notice__remedy\">{part.remedy}</span>\n )}\n </div>\n );\n}\n\n/* ------------------------------------------------------------- failure ---- */\n\n/**\n * THE FAILURE CARD — a plain sentence, a remedy, and Try again.\n *\n * \"Try again\" carries the reader's unsent words back to the message box through\n * the retry bus. Retyping a paragraph the product lost is the cheapest possible\n * insult, and the bus refuses to swallow it silently when no message box is\n * mounted.\n */\nexport interface ChatFailurePartViewProps {\n part: ChatErrorPart;\n /** The words that produced this failed turn. Empty when there are none. */\n retryText: string;\n onRetry(text: string): void;\n}\n\nexport function ChatFailurePartView(props: ChatFailurePartViewProps): ReactNode {\n const { part, retryText, onRetry } = props;\n return (\n <ErrorBox\n as=\"div\"\n className=\"mx-chat-failure\"\n data-code={part.code ?? \"unknown\"}\n message={part.userMessage}\n error={part}\n operation=\"Answer the last message\"\n unsavedInput={retryText === \"\" ? undefined : { message: retryText }}\n origin=\"chat/ChatFailurePart\"\n >\n <p className=\"mx-chat-failure__sentence\">{part.userMessage}</p>\n {part.remedy === null ? null : (\n <p className=\"mx-chat-failure__remedy\">{part.remedy}</p>\n )}\n {retryText === \"\" ? null : (\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n className=\"mx-chat-focusable\"\n onClick={() => onRetry(retryText)}\n >\n Try again\n </Button>\n )}\n </ErrorBox>\n );\n}\n\n/* ---------------------------------------------------------------- tool ---- */\n\nexport function ChatToolPartView(props: {\n part: ChatToolPart;\n defaultExpanded: boolean;\n}): ReactNode {\n return <ChatToolChip part={props.part} defaultExpanded={props.defaultExpanded} />;\n}\n\n/* -------------------------------------------------- render block / rest ---- */\n\nexport function ChatRenderBlockPartView(props: {\n part: ChatRenderBlockPart;\n renderContent: ChatRenderContentPort;\n messageId: string;\n role: \"user\" | \"assistant\" | \"system\" | \"tool\";\n}): ReactNode {\n const { part } = props;\n return (\n <div data-mx-chat-block={part.kind} data-status={part.status}>\n {props.renderContent({\n kind: part.kind,\n text: part.content ?? \"\",\n data: part.data,\n streaming: part.status === \"streaming\",\n role: props.role,\n messageId: props.messageId,\n })}\n </div>\n );\n}\n\n/**\n * Parts whose CAPABILITY is not in this lane. Each says exactly what it is and\n * what would show it — never a blank, never a dead control (Law 4).\n */\nexport function ChatFilePartView(props: { part: ChatFilePart }): ReactNode {\n const name = props.part.name ?? props.part.fileId;\n return (\n <span className=\"mx-chat-standin\" data-mx-chat-standin=\"file\">\n {`Attached file: ${name}. Previews appear once the attachments capability is turned on for this chat.`}\n </span>\n );\n}\n\nexport function ChatSourcePartView(props: { part: ChatSourcePart }): ReactNode {\n const { part } = props;\n const label = part.title ?? part.url ?? part.sourceId;\n return (\n <span className=\"mx-chat-standin\" data-mx-chat-standin=\"source\">\n {`Source: ${label}`}\n </span>\n );\n}\n\nexport function ChatAskUserPartView(props: { part: ChatAskUserPart }): ReactNode {\n return (\n <span className=\"mx-chat-standin\" data-mx-chat-standin=\"ask-user\">\n {props.part.answered\n ? `You already answered: ${props.part.prompt}`\n : `The assistant is waiting for your answer: ${props.part.prompt}. This chat cannot collect it yet — answer in a chat with the approvals capability turned on, or stop this run.`}\n </span>\n );\n}\n","/**\n * THE BASIC TOOL CHIP — the transcript's floor for a tool call.\n *\n * It is driven entirely by the kernel's tool state machine\n * (`input-streaming → input-available → approval-requested → output-available |\n * output-error | output-denied`), which is the champion's vocabulary and which\n * the live corpus already walks: `tool_started` with arguments, three\n * `tool_progress` beats under three DIFFERENT tool names, then `tool_completed`\n * with the result.\n *\n * Three things it refuses to do, each a real failure seen in the wild:\n * - it never shows a bare spinner with no name. A reader watching an agent\n * spend their money is owed the tool's name and what it is doing.\n * - it never drops the progress beats, and it keeps the SERVER's tool name per\n * beat (`web` → `web_search` → `web_research`); flattening them to the\n * call's name loses what actually ran.\n * - it never renders an error state as \"Done\".\n *\n * Lane 2B's `/tools` registry replaces this chip's BODY with a per-tool\n * renderer. The chip, its state machine and its header stay — a registry entry\n * is a richer body, not a second card. That is the `body` prop below: at the\n * lane merge `<ChatToolCard>` stopped carrying its own copy of this header and\n * became this chip with a body passed in, so the two can no longer drift in\n * markup, in state words, or in expand behaviour.\n */\n\nimport { useState, type ReactNode } from \"react\";\n\nimport type { ChatToolPart, ChatToolState } from \"../../model\";\nimport { useChatPorts } from \"../hooks\";\nimport { ChevronRightGlyph } from \"../icons\";\n\n/**\n * What the state means to a READER, not to a protocol. THE ONE TABLE: `/tools`\n * re-exports this rather than restating it, because a tool that reads \"Done\" in\n * one card and \"Completed\" in another is drift nobody notices for a month.\n */\nexport const CHAT_TOOL_STATE_LABEL: Record<ChatToolState, string> = {\n \"input-streaming\": \"Preparing\",\n \"input-available\": \"Running\",\n \"approval-requested\": \"Needs your approval\",\n \"output-available\": \"Done\",\n \"output-error\": \"Failed\",\n \"output-denied\": \"Declined\",\n};\n\nfunction formatPayload(value: unknown): string {\n if (value === undefined) return \"\";\n if (typeof value === \"string\") return value;\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n // A payload with a cycle or a BigInt still has to say something.\n return String(value);\n }\n}\n\nexport interface ChatToolChipProps {\n part: ChatToolPart;\n /** From `features.expandToolCardsByDefault`. */\n defaultExpanded: boolean;\n /**\n * Replace what the chip shows when it is open. This is the ONE seam `/tools`\n * uses: a registered renderer is a richer BODY under the same header, the\n * same state words and the same expand behaviour — never a second card.\n * Absent ⇒ the chip's own body (arguments, progress beats, error, result).\n */\n body?: ReactNode | undefined;\n /** Diagnostic marker for the surrounding element, e.g. which renderer drew\n * the body. Read by tests and by the census; never shown to a reader. */\n rendererTag?: string | undefined;\n}\n\nexport function ChatToolChip(props: ChatToolChipProps): ReactNode {\n const { part, defaultExpanded, body } = props;\n const [expanded, setExpanded] = useState(defaultExpanded);\n\n const { toolLabel } = useChatPorts();\n const latest = part.progress[part.progress.length - 1];\n const input = formatPayload(part.input);\n const output = formatPayload(part.output);\n const stateLabel = CHAT_TOOL_STATE_LABEL[part.state];\n // THE HEADER SAYS EACH THING ONCE. Servers send a completion beat whose\n // whole content is the word the state column already carries (\"Done\"), and\n // the chip read \"Done Done\" in front of a Creator (live review 2026-09-18).\n // The beat is not thrown away — it stays in the body, under Progress.\n const beat =\n latest !== undefined &&\n latest.message !== \"\" &&\n latest.message.trim().toLowerCase() !== stateLabel.toLowerCase()\n ? latest.message\n : null;\n const hasBody =\n input !== \"\" || output !== \"\" || part.errorText !== null || part.progress.length > 0;\n\n return (\n <div\n className=\"mx-chat-tool\"\n data-state={part.state}\n data-tool={part.toolName}\n {...(props.rendererTag === undefined\n ? {}\n : { \"data-mx-chat-tool-renderer\": props.rendererTag })}\n >\n <button\n type=\"button\"\n className=\"mx-chat-tool__header mx-chat-focusable\"\n aria-expanded={expanded}\n // A chip with nothing to show still toggles rather than sitting there\n // looking disabled (Law 4) — it says so in the body instead.\n onClick={() => setExpanded((open) => !open)}\n >\n <span data-open={expanded} className=\"mx-chat-chevron-wrap\">\n <ChevronRightGlyph />\n </span>\n {/* WHAT IS HAPPENING, never the identifier — `toolLabel` is the port\n and `data-tool` above keeps the machine name for the machines. */}\n <span className=\"mx-chat-tool__name\">{toolLabel(part.toolName)}</span>\n {beat === null ? null : <span className=\"mx-chat-tool__beat\">{beat}</span>}\n <span className=\"mx-chat-tool__state\">{stateLabel}</span>\n </button>\n\n {expanded ? (\n body !== undefined ? (\n body\n ) : (\n <div className=\"mx-chat-tool__body\">\n {input !== \"\" ? (\n <>\n <span className=\"mx-chat-tool__label\">What it was asked</span>\n <pre className=\"mx-chat-tool__payload\">{input}</pre>\n </>\n ) : null}\n\n {part.progress.length > 0 ? (\n <>\n <span className=\"mx-chat-tool__label\">Progress</span>\n <ul className=\"mx-chat-tool__progress\">\n {part.progress.map((beat, index) => (\n <li key={`${beat.toolName}-${index}`}>\n <span data-tool-beat={beat.toolName}>{beat.toolName}</span>\n {beat.message === \"\" ? null : ` — ${beat.message}`}\n {beat.percent === null ? null : ` (${Math.round(beat.percent)}%)`}\n </li>\n ))}\n </ul>\n </>\n ) : null}\n\n {part.errorText !== null ? (\n <>\n <span className=\"mx-chat-tool__label\">What went wrong</span>\n <p className=\"mx-chat-failure__sentence\">{part.errorText}</p>\n </>\n ) : null}\n\n {output !== \"\" ? (\n <>\n <span className=\"mx-chat-tool__label\">What it returned</span>\n <pre className=\"mx-chat-tool__payload\">{output}</pre>\n </>\n ) : null}\n\n {hasBody ? null : (\n <span className=\"mx-chat-standin\">\n {part.state === \"input-streaming\"\n ? \"This tool has not said what it is doing yet.\"\n : \"This tool ran without reporting any details.\"}\n </span>\n )}\n </div>\n )\n ) : null}\n </div>\n );\n}\n","/**\n * `<ChatComposer>` — lane 2B's headline component.\n *\n * 🚨 THE THREE FLAWS THE OWNER REJECTED TODAY, in another composer, and the\n * reason this file's geometry is written down rather than left to taste:\n *\n * 1. **ONE soft rounded container owns the focus ring.** The ring is drawn on\n * the shell via `:focus-within`; the textarea inside has no outline at\n * all. Two rings is a box inside a box.\n * 2. **NO divider line above the composer.** The transcript ends, the\n * composer begins, and nothing draws a rule between them.\n * 3. **Tight, balanced padding.** The same inline padding on both ends, the\n * action row optically aligned with the text, no dead band under the box.\n *\n * Everything stateful comes from `useChatComposerState()` (shared scaffolding).\n * This component RENDERS it. It adds no second source of composer state — two\n * sources is how a draft gets lost — and it re-resolves no knob: `sendKey`,\n * `retainDrafts` and `attachmentMaxBytes` all arrive from `thread.features`.\n *\n * Behaviours ported from real composers that have been running in production:\n * - **IME composition never sends.** `event.nativeEvent.isComposing` is\n * checked before the key contract runs, or Japanese/Chinese input submits a\n * half-committed candidate on the Enter that accepts it.\n * - **`draftSeed` never overwrites typed text** (workflow-studio). A seed\n * lands only into an empty box.\n * - **↑ in an empty box edits the last message you sent** (the Claude/ChatGPT\n * interaction language; matrx-frontend's composer does NOT have it).\n * - **Voice recovery is part of voice** (workflow-studio): the recovery\n * affordance renders itself away when there is nothing to recover.\n */\n\nimport {\n useCallback,\n useEffect,\n useId,\n useRef,\n useState,\n type ClipboardEvent as ReactClipboardEvent,\n type DragEvent as ReactDragEvent,\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n} from \"react\";\n\nimport { messageText } from \"../../model\";\nimport { useChatComposerState, useChatConfig, useChatPorts, useChatSlots } from \"../hooks\";\nimport { useChatContext } from \"../provider\";\nimport { ChatAttachmentChips } from \"./attachment-chips\";\nimport { ATTACHMENTS_UNAVAILABLE, ATTACHMENTS_UNAVAILABLE_REMEDY } from \"./files\";\nimport { ChatRunControls } from \"../plugins/run-controls\";\nimport { useChatComposerPorts } from \"./provider\";\nimport { composerSendIntent } from \"./send-intent\";\nimport { ChatComposerInput } from \"./textarea\";\nimport { AttachGlyph, SendGlyph, StopGlyph } from \"./glyphs\";\nimport { useChatAttachments } from \"./use-attachments\";\n\n/** Geometry, in one place, so a re-skin is token values and these two numbers. */\nconst MIN_FIELD_HEIGHT_PX = 24;\nconst MAX_FIELD_HEIGHT_PX = 208;\n\nexport interface ChatComposerProps {\n /**\n * Text to put in an EMPTY box — a suggestion, a deep link, a \"continue from\n * here\". It never overwrites what the reader has typed.\n */\n draftSeed?: string | null | undefined;\n placeholder?: string | undefined;\n autoFocus?: boolean | undefined;\n /**\n * The composer refuses input and SAYS WHY. A held composer with no sentence\n * is the dead control Law 4 forbids, so passing `held` without\n * `heldReason` is a type error.\n */\n held?: { reason: string } | undefined;\n className?: string | undefined;\n}\n\nfunction dropHasFiles(event: ReactDragEvent): boolean {\n const types = event.dataTransfer?.types;\n if (types === undefined) return false;\n return Array.from(types).includes(\"Files\");\n}\n\nexport function ChatComposer(props: ChatComposerProps): ReactNode {\n const composer = useChatComposerState();\n const features = useChatConfig();\n const { notifier } = useChatPorts();\n const slots = useChatSlots();\n const { files, voice } = useChatComposerPorts();\n // The composer reads the STORE, not `useChatThread()`: subscribing the\n // message box to the whole thread would re-render it on every coalesced\n // streaming tick, for a list it only reads when ↑ is pressed.\n const { thread } = useChatContext();\n const fieldRef = useRef<HTMLTextAreaElement | null>(null);\n const noticeId = useId();\n const [voiceBusy, setVoiceBusy] = useState(false);\n const [dragging, setDragging] = useState(false);\n const [unavailable, setUnavailable] = useState(false);\n const dragDepth = useRef(0);\n // `doSend` is defined below `onKeyDown`; a ref keeps ONE send path without\n // reordering the file into something unreadable.\n const sendRef = useRef<() => Promise<void>>(async () => undefined);\n\n const attachments = useChatAttachments({\n files,\n maxBytes: features.attachmentMaxBytes,\n notifier,\n });\n\n /* ---- draftSeed: into an EMPTY box only -------------------------------- */\n const seenSeed = useRef<string | null>(null);\n useEffect(() => {\n const seed = props.draftSeed;\n if (seed === null || seed === undefined || seed === \"\") return;\n if (seenSeed.current === seed) return;\n seenSeed.current = seed;\n if (composer.text.trim() !== \"\") return;\n composer.setText(seed);\n // `composer.text` is deliberately NOT a dependency: re-running this the\n // moment the reader clears the box would re-seed it under their cursor.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [props.draftSeed]);\n\n /* ---- files in by any route ------------------------------------------- */\n const takeFiles = useCallback(\n (incoming: readonly File[]) => {\n if (incoming.length === 0) return;\n if (!files.available) {\n // No chip, no silence: the port itself announces once with the remedy,\n // and the composer shows the same sentence in its notice line.\n void files\n .upload(incoming[0] as File, {\n signal: new AbortController().signal,\n onProgress: () => undefined,\n })\n .catch(() => undefined);\n setUnavailable(true);\n return;\n }\n attachments.add(incoming);\n },\n [attachments, files],\n );\n\n const onPaste = useCallback(\n (event: ReactClipboardEvent<HTMLTextAreaElement>) => {\n const items = event.clipboardData?.files;\n if (items === undefined || items.length === 0) return;\n // A pasted screenshot arrives as a File with no name in some browsers;\n // the chip still needs one, so the port's own default naming applies.\n event.preventDefault();\n takeFiles(Array.from(items));\n },\n [takeFiles],\n );\n\n const onDrop = useCallback(\n (event: ReactDragEvent<HTMLFormElement>) => {\n if (!dropHasFiles(event)) return;\n event.preventDefault();\n dragDepth.current = 0;\n setDragging(false);\n takeFiles(Array.from(event.dataTransfer.files));\n },\n [takeFiles],\n );\n\n /* ---- keyboard --------------------------------------------------------- */\n const onKeyDown = useCallback(\n (event: ReactKeyboardEvent<HTMLTextAreaElement>) => {\n // THE IME RULE HAS ONE HOME: `composerSendIntent`. An early return here\n // too would double-guard it, and a behaviour guarded twice is a\n // behaviour neither guard can be proven to hold — `test:falsifiable`\n // caught exactly that: removing one guard left the suite green.\n const composing = event.nativeEvent.isComposing;\n\n if (\n !composing &&\n event.key === \"ArrowUp\" &&\n composer.text === \"\" &&\n !event.shiftKey &&\n !event.metaKey &&\n !event.ctrlKey\n ) {\n const messages = thread.getSnapshot().messages;\n for (let cursor = messages.length - 1; cursor >= 0; cursor -= 1) {\n const candidate = messages[cursor];\n if (candidate === undefined || candidate.role !== \"user\") continue;\n const text = messageText(candidate);\n if (text === \"\") break;\n event.preventDefault();\n composer.setText(text);\n return;\n }\n return;\n }\n\n const intent = composerSendIntent(\n {\n key: event.key,\n shiftKey: event.shiftKey,\n metaKey: event.metaKey,\n ctrlKey: event.ctrlKey,\n isComposing: composing,\n isBusy: composer.isBusy,\n },\n features.sendKey,\n );\n if (intent === \"send\") {\n event.preventDefault();\n void sendRef.current();\n return;\n }\n if (intent === \"stop\") {\n event.preventDefault();\n void composer.stop();\n }\n },\n [composer, features.sendKey, thread],\n );\n\n /* ---- send ------------------------------------------------------------- */\n const held = props.held ?? null;\n const blockedReason =\n held !== null\n ? held.reason\n : voiceBusy\n ? \"Finish recording to send your message.\"\n : attachments.uploading\n ? \"Your files are still uploading.\"\n : null;\n\n const canSend = composer.canSend && blockedReason === null;\n\n const doSend = useCallback(async () => {\n if (!canSend) return;\n const body = composer.text.trim();\n const ids = attachments.readyFileIds;\n if (ids.length === 0) {\n await composer.send();\n } else {\n composer.setText(\"\");\n const result = await thread.send({ text: body, fileIds: ids });\n if (!result.accepted) composer.setText(body);\n else attachments.clear();\n }\n // Focus returns to the box after a send — a reader who has to click back\n // into the composer after every message notices every single time.\n fieldRef.current?.focus();\n }, [attachments, canSend, composer, thread]);\n sendRef.current = doSend;\n\n const onSubmit = useCallback(\n (event: { preventDefault(): void }) => {\n event.preventDefault();\n void doSend();\n },\n [doSend],\n );\n\n const sendLabel = composer.isBusy\n ? \"Stop the answer\"\n : blockedReason !== null\n ? blockedReason\n : \"Send message\";\n // `features.sendWhileBusy`: the host queues, so Send stays beside Stop while\n // there is something to send — never a dead Send, and Stop never hides.\n const queueing = composer.isBusy && features.sendWhileBusy && composer.text.trim() !== \"\";\n const queueLabel =\n blockedReason !== null ? blockedReason : \"Queue message — it sends when this answer finishes\";\n\n const voiceContext = {\n value: composer.text,\n onValueChange: composer.setText,\n busy: composer.isBusy,\n onBusyChange: setVoiceBusy,\n className: \"mx-chat-composer__icon-button mx-chat-focusable\",\n };\n\n return (\n <form\n className={`mx-chat-composer${props.className === undefined ? \"\" : ` ${props.className}`}`}\n data-dragging={dragging || undefined}\n data-busy={composer.isBusy || undefined}\n onSubmit={onSubmit}\n onDragEnter={(event) => {\n if (!dropHasFiles(event)) return;\n dragDepth.current += 1;\n setDragging(true);\n }}\n onDragOver={(event) => {\n if (dropHasFiles(event)) event.preventDefault();\n }}\n onDragLeave={(event) => {\n if (!dropHasFiles(event)) return;\n // A counter, not a boolean: dragging over a child fires `dragleave` on\n // the parent, and a boolean makes the overlay flicker off mid-drag.\n dragDepth.current = Math.max(0, dragDepth.current - 1);\n if (dragDepth.current === 0) setDragging(false);\n }}\n onDrop={onDrop}\n >\n {slots.composerAbove}\n\n {/* PHASE 4's run-control row. It renders ITSELF away when the host has\n given neither controls nor chips, and it writes through THIS\n composer's setter — never a second source of composer state. */}\n <ChatRunControls\n onFill={composer.setText}\n busy={composer.isBusy}\n drafting={composer.text.trim() !== \"\"}\n />\n\n {/* ONE SHELL. The ring lives here (:focus-within), not on the field. */}\n <div className=\"mx-chat-composer__shell\">\n {attachments.attachments.length > 0 ? (\n <ChatAttachmentChips\n attachments={attachments.attachments}\n onRemove={attachments.remove}\n onRetry={attachments.retry}\n />\n ) : null}\n\n <ChatComposerInput\n value={composer.text}\n onValueChange={composer.setText}\n onKeyDown={onKeyDown}\n onPaste={onPaste}\n placeholder={props.placeholder ?? \"Send a message\"}\n minHeightPx={MIN_FIELD_HEIGHT_PX}\n maxHeightPx={MAX_FIELD_HEIGHT_PX}\n disabled={held !== null}\n label=\"Message\"\n describedBy={noticeId}\n inputRef={fieldRef}\n {...(props.autoFocus === undefined ? {} : { autoFocus: props.autoFocus })}\n />\n\n <div className=\"mx-chat-composer__row\">\n <div className=\"mx-chat-composer__left\">\n {/* ABSENT, never dead: no attach button when nothing can store a\n file. Paste and drop still answer honestly through the port. */}\n {files.available ? (\n <label className=\"mx-chat-composer__icon-button mx-chat-focusable\">\n <AttachGlyph />\n <span className=\"mx-chat__sr-only\">Attach files</span>\n <input\n type=\"file\"\n multiple\n aria-label=\"Attach files\"\n {...(files.accept === undefined ? {} : { accept: files.accept })}\n onChange={(event) => {\n const list = event.target.files;\n if (list !== null) takeFiles(Array.from(list));\n event.target.value = \"\";\n }}\n />\n </label>\n ) : null}\n\n {voice === null ? null : voice.renderButton(voiceContext)}\n {slots.composerPickers}\n {slots.composerToolbar}\n </div>\n\n <div className=\"mx-chat-composer__right\">\n {slots.composerRunControls}\n {/* The stop button REPLACES send during a stream. It never sits\n beside it looking dead (design §7, Law 4). */}\n {queueing ? (\n <button\n type=\"submit\"\n className=\"mx-chat-composer__send mx-chat-focusable\"\n data-mode=\"queue\"\n aria-label={queueLabel}\n title={queueLabel}\n disabled={!canSend}\n >\n <SendGlyph />\n </button>\n ) : null}\n {composer.isBusy ? (\n <button\n type=\"button\"\n className=\"mx-chat-composer__send mx-chat-focusable\"\n data-mode=\"stop\"\n aria-label={sendLabel}\n title={sendLabel}\n onClick={() => void composer.stop()}\n >\n <StopGlyph />\n </button>\n ) : (\n <button\n type=\"submit\"\n className=\"mx-chat-composer__send mx-chat-focusable\"\n data-mode=\"send\"\n aria-label={sendLabel}\n title={sendLabel}\n disabled={!canSend}\n >\n <SendGlyph />\n </button>\n )}\n </div>\n </div>\n </div>\n\n {/* THE HONEST LINE. One region, always present in the accessibility tree,\n so a reason for a refusal is announced rather than only implied by a\n button that will not move. */}\n <div className=\"mx-chat-composer__notice\" id={noticeId} role=\"status\" aria-live=\"polite\">\n {held !== null ? (\n <span className=\"mx-chat-standin\">{held.reason}</span>\n ) : unavailable ? (\n <span className=\"mx-chat-standin\">\n {`${ATTACHMENTS_UNAVAILABLE} ${ATTACHMENTS_UNAVAILABLE_REMEDY}`}\n </span>\n ) : blockedReason !== null && composer.text.trim() !== \"\" ? (\n <span className=\"mx-chat-standin\">{blockedReason}</span>\n ) : composer.restoredFromFailure ? (\n <span className=\"mx-chat-standin\">\n Your message is back in the box. Send it again when you are ready.\n </span>\n ) : null}\n {voice?.renderRecovery?.(voiceContext) ?? null}\n </div>\n\n {slots.composerBelow}\n\n {dragging ? (\n <div className=\"mx-chat-composer__drop\" aria-live=\"polite\">\n {files.available\n ? \"Drop files to attach them to your message\"\n : ATTACHMENTS_UNAVAILABLE}\n </div>\n ) : null}\n </form>\n );\n}\n","/**\n * ATTACHMENT CHIPS — lane 2B.\n *\n * Inline, INSIDE the composer's one rounded shell, above the text. Not a\n * separate panel and not a toast: an attachment is part of the message being\n * written, so it lives in the same object.\n *\n * Three things a chip never does:\n * - it never shows a percentage the port did not report. With no measurable\n * progress the chip says \"Uploading…\" and the bar is indeterminate, because\n * a fake 60% that sits there is a lie a reader will believe.\n * - it never hides a failure. A failed chip carries the sentence AND a \"Try\n * again\" that re-uploads the same bytes — except when retrying cannot\n * possibly work (a file over the limit), where the button is ABSENT.\n * - it never loses the remove control while uploading: removing aborts.\n */\n\nimport type { ReactNode } from \"react\";\n\nimport { formatFileSize } from \"@ai-matrx/kit/format\";\nimport { RemoveGlyph } from \"./glyphs\";\nimport type { ChatAttachment } from \"./use-attachments\";\n\nexport interface ChatAttachmentChipsProps {\n attachments: readonly ChatAttachment[];\n onRemove(key: string): void;\n onRetry(key: string): void;\n}\n\nexport function ChatAttachmentChips(props: ChatAttachmentChipsProps): ReactNode {\n return (\n <ul className=\"mx-chat-attachments\" aria-label=\"Attachments on this message\">\n {props.attachments.map((attachment) => (\n <li\n key={attachment.key}\n className=\"mx-chat-attachment\"\n data-status={attachment.status}\n >\n <span className=\"mx-chat-attachment__name\" title={attachment.name}>\n {attachment.name}\n </span>\n <span className=\"mx-chat-attachment__meta\">\n {attachment.status === \"uploading\"\n ? attachment.progress === null\n ? \"Uploading…\"\n : `Uploading ${Math.round(attachment.progress * 100)}%`\n : attachment.status === \"failed\"\n ? \"Not attached\"\n : formatFileSize(attachment.sizeBytes)}\n </span>\n\n {attachment.status === \"uploading\" ? (\n <span\n className=\"mx-chat-attachment__bar\"\n role=\"progressbar\"\n aria-label={`Uploading ${attachment.name}`}\n {...(attachment.progress === null\n ? {}\n : {\n \"aria-valuenow\": Math.round(attachment.progress * 100),\n \"aria-valuemin\": 0,\n \"aria-valuemax\": 100,\n })}\n data-indeterminate={attachment.progress === null || undefined}\n >\n <span\n className=\"mx-chat-attachment__fill\"\n style={\n attachment.progress === null\n ? undefined\n : { width: `${Math.round(attachment.progress * 100)}%` }\n }\n />\n </span>\n ) : null}\n\n {attachment.failure === null ? null : (\n <span className=\"mx-chat-attachment__failure\">{attachment.failure}</span>\n )}\n\n {attachment.status === \"failed\" && attachment.retryable ? (\n <button\n type=\"button\"\n className=\"mx-chat-attachment__retry mx-chat-focusable\"\n onClick={() => props.onRetry(attachment.key)}\n >\n Try again\n </button>\n ) : null}\n\n <button\n type=\"button\"\n className=\"mx-chat-attachment__remove mx-chat-focusable\"\n aria-label={`Remove ${attachment.name}`}\n title={`Remove ${attachment.name}`}\n onClick={() => props.onRemove(attachment.key)}\n >\n <RemoveGlyph />\n </button>\n </li>\n ))}\n </ul>\n );\n}\n","/**\n * 2B's GLYPHS. Inline, sized in `em`, `currentColor` — the same reasoning as\n * `../icons.tsx`: four small paths do not justify an edge in the sibling graph,\n * and an icon that inherits colour and size from its button is exactly what the\n * re-skin contract needs. Every one is `aria-hidden`; the BUTTON carries the\n * name.\n */\n\nimport type { ReactNode } from \"react\";\n\nfunction frame(children: ReactNode): ReactNode {\n return (\n <svg\n width=\"1em\"\n height=\"1em\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.6\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n {children}\n </svg>\n );\n}\n\nexport function SendGlyph(): ReactNode {\n return frame(<path d=\"M8 13V3.5M3.75 7.75 8 3.5l4.25 4.25\" />);\n}\n\nexport function StopGlyph(): ReactNode {\n return frame(<rect x=\"4.25\" y=\"4.25\" width=\"7.5\" height=\"7.5\" rx=\"1.5\" fill=\"currentColor\" stroke=\"none\" />);\n}\n\nexport function AttachGlyph(): ReactNode {\n return frame(\n <path d=\"M10.6 5.1 5.9 9.8a1.7 1.7 0 0 0 2.4 2.4l4.7-4.7a3.2 3.2 0 0 0-4.5-4.5L3.6 7.9a4.7 4.7 0 0 0 6.7 6.7\" />,\n );\n}\n\nexport function RemoveGlyph(): ReactNode {\n return frame(<path d=\"M4.5 4.5l7 7M11.5 4.5l-7 7\" />);\n}\n","/**\n * PHASE 4 — THE RUN-CONTROL ROW, immediately above the message box.\n *\n * There are now TWO places a host puts something above the transcript's end,\n * and they are different on purpose:\n *\n * `surfaceBanner` (2C) full width, under the shell's header — a run tray,\n * a review bar, a policy notice. The studio's\n * `DockRunTray` rides it.\n * `composerControls` (4) the SMALL row that belongs to the box: the chips a\n * turn just earned, a hand-off offer, a mode switch.\n * It scrolls with nothing and sits where the reader's\n * eye already is when they are about to type.\n *\n * 🚨 IT DOES NOT EXIST WHEN IT IS EMPTY. A host that gives neither controls nor\n * chips gets no row, no border and no gap — an empty band above the box is\n * dead pixels that push the composer down for nothing.\n *\n * 🚨 IT HOLDS NO COMPOSER STATE. The chips write through the setter the\n * composer hands down, never through a second `useChatComposerState()` — two\n * sources of composer state is how a draft gets lost, which is the rule lane\n * 2B was written against and the reason this takes props instead of a hook.\n */\n\nimport type { ReactNode } from \"react\";\n\nimport { useChatPorts, useChatSlots } from \"../hooks\";\nimport { useChatContext } from \"../provider\";\n\nexport interface ChatRunControlsProps {\n /**\n * Put words in the box. THE CHIP NEVER SENDS — see the `quickset` port: a\n * one-click paid run from a row the reader was only reading spends their\n * money on a guess.\n */\n onFill(text: string): void;\n /** True while a turn is in flight; the chips stand down rather than queue. */\n busy: boolean;\n /** True when the box already has words in it — a chip would overwrite them. */\n drafting: boolean;\n}\n\nexport function ChatRunControls(props: ChatRunControlsProps): ReactNode {\n const slots = useChatSlots();\n const { quickset } = useChatPorts();\n const { thread } = useChatContext();\n\n const snapshot = thread.getSnapshot();\n const chips =\n quickset === null || props.busy || props.drafting\n ? []\n : quickset.chips({\n conversationId: snapshot.conversationId,\n busy: props.busy,\n empty: snapshot.messages.length === 0,\n });\n\n const controls = slots.composerControls;\n if (controls === undefined && chips.length === 0) return null;\n\n return (\n <div className=\"mx-chat-run-controls\">\n {controls}\n {chips.length === 0 ? null : (\n <div\n className=\"mx-chat-quickset\"\n role=\"group\"\n aria-label=\"Things you could ask\"\n >\n {chips.map((chip) => (\n <button\n key={chip.id}\n type=\"button\"\n className=\"mx-chat-quickset__chip mx-chat-focusable\"\n data-quickset={chip.id}\n onClick={() => props.onFill(chip.text)}\n >\n {chip.label}\n </button>\n ))}\n </div>\n )}\n </div>\n );\n}\n","/**\n * `<ChatComposerProvider>` — a SCOPED OVERRIDE of the `files` and `voice`\n * ports, and nothing else.\n *\n * 🚨 WHAT CHANGED AT THE LANE MERGE. Lane 2B built this as a second port\n * carrier, because `src/react/ports.ts` was shared scaffolding that lanes 2A\n * and 2C were reading and the lane contract forbade 2B editing it mid-flight.\n * The deviation was filed at the time; this is it closed. `files` and `voice`\n * are now fields on `ChatReactPorts`, defaulted ONCE by `<ChatProvider>`, which\n * is the single port map design §5 specifies.\n *\n * WHY THIS COMPONENT STILL EXISTS RATHER THAN BEING DELETED: a page can hold\n * two composers over one chat provider — the Workflow Studio's docked Conductor\n * and its floating one — and they can legitimately differ (one has an upload\n * service, the other does not). Overriding for a subtree is that, and it is a\n * real requirement rather than a leftover. A host with one composer never\n * mounts it: `<ChatProvider ports={{ files, voice }}>` is the whole story.\n *\n * Without any override the composer still works: it gets the provider's\n * stand-in files port, which refuses out loud with the remedy, and no voice\n * button at all (absent, not dead).\n */\n\nimport { createContext, useContext, useMemo, type ReactNode } from \"react\";\n\nimport { useChatPorts } from \"../hooks\";\nimport type { ChatFilesPort } from \"./files\";\nimport type { ChatVoicePort } from \"./voice-port\";\n\nexport interface ChatComposerPorts {\n files: ChatFilesPort;\n /** Absent ⇒ no microphone button is rendered. */\n voice: ChatVoicePort | null;\n}\n\nconst ComposerPortsContext = createContext<ChatComposerPorts | null>(null);\n\nexport interface ChatComposerProviderProps {\n files?: ChatFilesPort | undefined;\n voice?: ChatVoicePort | undefined;\n children: ReactNode;\n}\n\nexport function ChatComposerProvider(props: ChatComposerProviderProps): ReactNode {\n // INHERIT, never rebuild. A provider that manufactured its own stand-in here\n // would silently discard a `files` port the host gave `<ChatProvider>` — the\n // exact class of silent override this merge exists to remove.\n const inherited = useChatComposerPorts();\n const { files, voice } = props;\n const value = useMemo<ChatComposerPorts>(\n () => ({\n files: files ?? inherited.files,\n voice: voice ?? inherited.voice,\n }),\n [files, voice, inherited.files, inherited.voice],\n );\n return (\n <ComposerPortsContext.Provider value={value}>\n {props.children}\n </ComposerPortsContext.Provider>\n );\n}\n\n/**\n * The composer's ports: the nearest override if there is one, otherwise the\n * `<ChatProvider>` defaults. It never throws — a transcript-plus-composer\n * mounted with no override is the ordinary case and must render a working\n * message box.\n */\nexport function useChatComposerPorts(): ChatComposerPorts {\n const scoped = useContext(ComposerPortsContext);\n const ports = useChatPorts();\n return useMemo<ChatComposerPorts>(\n () => scoped ?? { files: ports.files, voice: ports.voice },\n [scoped, ports.files, ports.voice],\n );\n}\n","/**\n * THE SEND-KEY DECISION, as one pure function.\n *\n * ⚠️ WHY IT EXISTS AT ALL — read before deleting it. `useChatComposerState()`\n * (shared scaffolding) already carries an `onKeyDown` implementing the same\n * contract, and it calls its OWN `send()`, which knows nothing about\n * attachments. A composer that delegated its Enter key to that handler would\n * silently drop every uploaded file from the turn the reader pressed Enter on —\n * the file would sit in a chip, the message would go without it, and nobody\n * would find out until the assistant answered about a document it never saw.\n *\n * So the composer decides the key itself and routes EVERY send through one\n * place. It is not a second source of composer state: the only input is\n * `features.sendKey`, a knob resolved once in the kernel.\n *\n * `react-composer.test.tsx` § \"THE ONE CONTRACT\" asserts this function and the\n * hook's handler agree across the whole key matrix, so the two cannot drift.\n * At lane merge the hook should call this function and the duplication goes.\n */\n\nimport type { ChatSendKey } from \"../../config\";\n\nexport type ChatKeyIntent = \"send\" | \"stop\" | \"newline\" | \"pass\";\n\nexport interface ChatKeyFacts {\n key: string;\n shiftKey: boolean;\n metaKey: boolean;\n ctrlKey: boolean;\n /** An IME candidate being committed. It is NEVER a send. */\n isComposing: boolean;\n /** True while a turn is in flight — Escape only means \"stop\" then. */\n isBusy: boolean;\n}\n\nexport function composerSendIntent(\n facts: ChatKeyFacts,\n sendKey: ChatSendKey,\n): ChatKeyIntent {\n if (facts.isComposing) return \"pass\";\n if (facts.key === \"Escape\") return facts.isBusy ? \"stop\" : \"pass\";\n if (facts.key !== \"Enter\") return \"pass\";\n const withModifier = facts.metaKey || facts.ctrlKey;\n const sends =\n sendKey === \"enter\" ? !facts.shiftKey && !withModifier : withModifier;\n return sends ? \"send\" : \"newline\";\n}\n","/**\n * THE INPUT FIELD — lane 2B.\n *\n * 🚨 IT OWNS NO FOCUS RING. The ring belongs to the composer's ONE rounded\n * shell (`:focus-within`), so a reader sees a single object light up rather\n * than a box inside a box. `outline: none` on this field is therefore part of\n * the contract, not a style opinion — and the shell's ring is what makes it\n * accessible. The owner rejected exactly this (a ring on the inner field, a\n * divider above the box, loose padding) in another composer TODAY.\n *\n * The auto-grow mechanism is ported from matrx-frontend's `AgentTextarea` +\n * `textarea-line-grid.ts`, which is the only near-pure file in that 7,900-line\n * tree and has three earned details:\n *\n * 1. **Zero-measure, then restore.** Height is set to `0px` to read\n * `scrollHeight`, then written back to the starting height before the\n * browser paints, with transitions suppressed for the measurement. Without\n * that the box ratchets upward and never comes back down.\n * 2. **Snap to the line grid.** A height between two lines leaves a half-line\n * of dead space that shifts on every keystroke.\n * 3. **Skip the write when nothing changed.** A keystroke that does not change\n * the line count must not touch `style.height` at all, or the box flutters\n * while someone types.\n *\n * THE iOS ZOOM FLOOR is CSS, not JS, and it ships in `styles.css` unlayered:\n * any `font-size` under 16px on a focused field makes mobile Safari zoom the\n * page and never zoom back. An inline `style.fontSize` would defeat a host's\n * theme, so the rule lives in the sheet where a host can see it.\n */\n\nimport {\n useCallback,\n useLayoutEffect,\n useRef,\n type ClipboardEvent as ReactClipboardEvent,\n type CSSProperties,\n type KeyboardEvent as ReactKeyboardEvent,\n type ReactNode,\n type Ref,\n} from \"react\";\n\n/** Pure: the snapped height for a measured content height. Tested directly. */\nexport function snapToLineGrid(\n contentHeight: number,\n lineHeight: number,\n chrome: number,\n): number {\n if (!Number.isFinite(lineHeight) || lineHeight <= 0) return contentHeight;\n const safeChrome = Number.isFinite(chrome) && chrome > 0 ? chrome : 0;\n const lines = Math.max(1, Math.round((contentHeight - safeChrome) / lineHeight));\n return lines * lineHeight + safeChrome;\n}\n\nexport interface ChatComposerInputProps {\n value: string;\n onValueChange(next: string): void;\n onKeyDown(event: ReactKeyboardEvent<HTMLTextAreaElement>): void;\n onPaste?(event: ReactClipboardEvent<HTMLTextAreaElement>): void;\n placeholder: string;\n /** Largest height in pixels before the field scrolls instead of growing. */\n maxHeightPx: number;\n minHeightPx: number;\n disabled: boolean;\n label: string;\n describedBy?: string | undefined;\n inputRef?: Ref<HTMLTextAreaElement> | undefined;\n autoFocus?: boolean | undefined;\n}\n\nexport function ChatComposerInput(props: ChatComposerInputProps): ReactNode {\n const internal = useRef<HTMLTextAreaElement | null>(null);\n\n const resize = useCallback(() => {\n const element = internal.current;\n if (element === null) return;\n\n const startHeight = element.offsetHeight;\n const scrollTop = element.scrollTop;\n const computed = getComputedStyle(element);\n const lineHeight = Number.parseFloat(computed.lineHeight);\n const chrome =\n Number.parseFloat(computed.paddingTop || \"0\") +\n Number.parseFloat(computed.paddingBottom || \"0\") +\n Number.parseFloat(computed.borderTopWidth || \"0\") +\n Number.parseFloat(computed.borderBottomWidth || \"0\");\n\n // Suppress the transition for the measurement, or the browser animates\n // through 0px and the box visibly collapses on every keystroke.\n element.style.transitionProperty = \"none\";\n element.style.height = \"0px\";\n const contentHeight = element.scrollHeight;\n element.style.height = `${startHeight}px`;\n // Force a style commit before the transition is restored.\n void element.offsetHeight;\n element.style.transitionProperty = \"\";\n element.scrollTop = scrollTop;\n\n const snapped = snapToLineGrid(contentHeight, lineHeight, chrome);\n const natural = Math.max(\n props.minHeightPx,\n Math.min(snapped, props.maxHeightPx),\n );\n // THE WRITE-SKIP GUARANTEE.\n if (natural === startHeight) return;\n element.style.height = `${natural}px`;\n }, [props.maxHeightPx, props.minHeightPx]);\n\n useLayoutEffect(() => {\n resize();\n }, [resize, props.value]);\n\n const setRef = useCallback(\n (node: HTMLTextAreaElement | null) => {\n internal.current = node;\n const forwarded = props.inputRef;\n if (typeof forwarded === \"function\") forwarded(node);\n else if (forwarded !== null && forwarded !== undefined) {\n (forwarded as { current: HTMLTextAreaElement | null }).current = node;\n }\n },\n [props.inputRef],\n );\n\n const style: CSSProperties = { maxHeight: props.maxHeightPx };\n\n return (\n <textarea\n ref={setRef}\n className=\"mx-chat-composer__field\"\n value={props.value}\n rows={1}\n style={style}\n placeholder={props.placeholder}\n aria-label={props.label}\n {...(props.describedBy === undefined ? {} : { \"aria-describedby\": props.describedBy })}\n disabled={props.disabled}\n autoFocus={props.autoFocus === true}\n onChange={(event) => props.onValueChange(event.target.value)}\n onKeyDown={props.onKeyDown}\n {...(props.onPaste === undefined ? {} : { onPaste: props.onPaste })}\n />\n );\n}\n","/**\n * ATTACHMENT STATE — lane 2B.\n *\n * One chip per file, with a real lifecycle: `uploading → ready | failed`, a\n * percentage when the port reports one, a retry that re-runs the SAME file\n * (kept in memory for exactly that reason), and a remove that ABORTS an upload\n * in flight instead of leaving it billing in the background.\n *\n * Rules that are code here, not prose:\n * - a send only ever carries `ready` ids. A half-uploaded file silently\n * dropped from a turn is the reader discovering later that the assistant\n * never saw their document.\n * - the size limit is `features.attachmentMaxBytes` — a KNOB (Law 6), not a\n * constant in this file — and exceeding it is refused with the file's name\n * and the limit in the sentence, before a byte is uploaded.\n * - a refusal is a CHIP in the failed state, not a toast that scrolls away.\n */\n\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport type { ChatNotifierPort } from \"../../ports\";\nimport { newLocalId } from \"../../model\";\nimport {\n tooLargeSentence,\n type ChatFilesPort,\n type ChatUploadedFile,\n} from \"./files\";\n\nexport type ChatAttachmentStatus = \"uploading\" | \"ready\" | \"failed\";\n\nexport interface ChatAttachment {\n /** Local chip identity. Stable across a retry so the chip does not jump. */\n key: string;\n name: string;\n sizeBytes: number;\n mediaType: string | null;\n status: ChatAttachmentStatus;\n /** 0..1 while uploading, or null when the port cannot measure. */\n progress: number | null;\n /** Present once ready — the ONLY thing a turn carries. */\n fileId: string | null;\n previewUrl: string | null;\n /** Present when failed: what a reader can read, never a stack. */\n failure: string | null;\n /** True when a retry can work at all (a size refusal never can). */\n retryable: boolean;\n}\n\nexport interface UseChatAttachmentsResult {\n attachments: readonly ChatAttachment[];\n /** Ids for the next send. Only `ready` chips. */\n readyFileIds: readonly string[];\n /** True while any chip is still uploading — the send button waits. */\n uploading: boolean;\n add(files: readonly File[]): void;\n remove(key: string): void;\n retry(key: string): void;\n /** Called after a successful send. */\n clear(): void;\n}\n\ninterface Pending {\n file: File;\n controller: AbortController;\n}\n\nexport function useChatAttachments(options: {\n files: ChatFilesPort;\n maxBytes: number;\n notifier: ChatNotifierPort;\n}): UseChatAttachmentsResult {\n const { files, maxBytes, notifier } = options;\n const [attachments, setAttachments] = useState<ChatAttachment[]>([]);\n const pending = useRef(new Map<string, Pending>());\n // The File objects survive a failure so \"Try again\" re-uploads the SAME\n // bytes. Asking a reader to find the file again because we threw it away is\n // the cheapest possible insult.\n const retainedFiles = useRef(new Map<string, File>());\n const mounted = useRef(true);\n\n useEffect(() => {\n mounted.current = true;\n return () => {\n mounted.current = false;\n // An unmounted composer must not leave uploads running. They cost money\n // and they can never reach a chip again.\n for (const entry of pending.current.values()) entry.controller.abort();\n pending.current.clear();\n };\n }, []);\n\n const patch = useCallback((key: string, next: Partial<ChatAttachment>) => {\n if (!mounted.current) return;\n setAttachments((current) =>\n current.map((item) => (item.key === key ? { ...item, ...next } : item)),\n );\n }, []);\n\n const run = useCallback(\n (key: string, file: File) => {\n const controller = new AbortController();\n pending.current.set(key, { file, controller });\n retainedFiles.current.set(key, file);\n patch(key, { status: \"uploading\", progress: null, failure: null });\n\n files\n .upload(file, {\n signal: controller.signal,\n onProgress: (fraction) => {\n if (controller.signal.aborted) return;\n patch(key, { progress: Math.max(0, Math.min(1, fraction)) });\n },\n })\n .then((uploaded: ChatUploadedFile) => {\n pending.current.delete(key);\n if (controller.signal.aborted) return;\n patch(key, {\n status: \"ready\",\n progress: 1,\n fileId: uploaded.fileId,\n previewUrl: uploaded.previewUrl ?? null,\n mediaType: uploaded.mediaType,\n failure: null,\n });\n })\n .catch((cause: unknown) => {\n pending.current.delete(key);\n if (controller.signal.aborted) return;\n patch(key, {\n status: \"failed\",\n progress: null,\n failure:\n cause instanceof Error && cause.message !== \"\"\n ? cause.message\n : \"This file could not be uploaded.\",\n retryable: true,\n });\n });\n },\n [files, patch],\n );\n\n const add = useCallback(\n (incoming: readonly File[]) => {\n if (incoming.length === 0) return;\n const created: ChatAttachment[] = [];\n const startable: { key: string; file: File }[] = [];\n\n for (const file of incoming) {\n const key = newLocalId(\"att\");\n if (file.size > maxBytes) {\n // Refused BEFORE a byte moves, and the refusal becomes a chip the\n // reader can see and dismiss — never a vanished file.\n const sentence = tooLargeSentence(file.name, maxBytes);\n created.push({\n key,\n name: file.name,\n sizeBytes: file.size,\n mediaType: file.type === \"\" ? null : file.type,\n status: \"failed\",\n progress: null,\n fileId: null,\n previewUrl: null,\n failure: sentence,\n retryable: false,\n });\n notifier.notify({\n level: \"warning\",\n message: sentence,\n remedy: \"Attach a smaller file, or ask an administrator to raise the limit for this workspace.\",\n });\n continue;\n }\n created.push({\n key,\n name: file.name,\n sizeBytes: file.size,\n mediaType: file.type === \"\" ? null : file.type,\n status: \"uploading\",\n progress: null,\n fileId: null,\n previewUrl: null,\n failure: null,\n retryable: true,\n });\n startable.push({ key, file });\n }\n\n setAttachments((current) => [...current, ...created]);\n for (const entry of startable) run(entry.key, entry.file);\n },\n [maxBytes, notifier, run],\n );\n\n const remove = useCallback((key: string) => {\n const entry = pending.current.get(key);\n if (entry !== undefined) {\n entry.controller.abort();\n pending.current.delete(key);\n }\n retainedFiles.current.delete(key);\n setAttachments((current) => current.filter((item) => item.key !== key));\n }, []);\n\n const retry = useCallback(\n (key: string) => {\n const file = retainedFiles.current.get(key);\n if (file === undefined) return;\n run(key, file);\n },\n [run],\n );\n\n const clear = useCallback(() => {\n for (const entry of pending.current.values()) entry.controller.abort();\n pending.current.clear();\n retainedFiles.current.clear();\n setAttachments([]);\n }, []);\n\n return {\n attachments,\n readyFileIds: attachments\n .filter((item) => item.status === \"ready\" && item.fileId !== null)\n .map((item) => item.fileId as string),\n uploading: attachments.some((item) => item.status === \"uploading\"),\n add,\n remove,\n retry,\n clear,\n };\n}\n","/**\n * THE VOICE SLOT — lane 2B.\n *\n * The package does not re-implement dictation: `@ai-matrx/browser-audio`\n * already owns permissions, chunked transcription, the recovery store and the\n * error vocabulary, and re-deriving any of that per host is exactly what this\n * campaign exists to stop. What 2B owns is the SEAM and its two rules, both\n * lifted from the workflow-studio composer that has been running this for real:\n *\n * 1. The button is ABSENT when the host has not enabled voice. It is never a\n * greyed-out microphone (Law 4).\n * 2. RECOVERY IS PART OF VOICE. A transcription that fails after the reader\n * has already spoken must not lose the words: browser-audio keeps the\n * recording and its recovery control offers it back, rendering itself away\n * when there is nothing to recover.\n *\n * 🚨 WHY THE IMPORT IS LAZY, AND WHY THAT IS NOT A STYLE CHOICE.\n * `@ai-matrx/browser-audio` publishes **ESM-only export conditions** — its\n * `publishConfig.exports` carry `types`/`import` and no `require` — so a static\n * `import` of it puts `require(\"@ai-matrx/browser-audio/react\")` into this\n * package's CJS build, and every CJS consumer of `@ai-matrx/chat/react` dies at\n * LOAD with `ERR_PACKAGE_PATH_NOT_EXPORTED`, whether or not they ever wanted\n * voice. The packed-tarball canary caught exactly that. Loading it on demand\n * keeps `import()` in both builds (Node supports `import()` inside CJS), so the\n * dependency is paid for only by the host that turns voice on. When\n * browser-audio ships dual conditions this can go back to a static import.\n */\n\nimport { useEffect, useState, type ComponentType, type ReactNode } from \"react\";\nimport type { BrowserAudioKernel } from \"@ai-matrx/browser-audio/core\";\n\n/** What the composer hands a voice implementation on every render. */\nexport interface ChatVoiceContext {\n /** The composer's current text — voice APPENDS to it, never replaces it. */\n value: string;\n onValueChange(next: string): void;\n /** True while a turn is streaming: dictation into a running turn is allowed,\n * but a host may choose to disable its control. */\n busy: boolean;\n /** Called with true while the recorder holds the turn, so the composer can\n * refuse to send half-dictated text. */\n onBusyChange(busy: boolean): void;\n className: string;\n}\n\n/**\n * Two renders, not one: the microphone sits in the composer's action row and\n * the recovery affordance sits under the box, where it can appear and vanish\n * without moving the send button.\n */\nexport interface ChatVoicePort {\n renderButton(context: ChatVoiceContext): ReactNode;\n renderRecovery?(context: ChatVoiceContext): ReactNode;\n}\n\ninterface AudioModule {\n VoiceInputButton: ComponentType<Record<string, unknown>>;\n VoiceRecoveryButton: ComponentType<Record<string, unknown>>;\n}\n\n/** One load per page, shared by every composer on it. */\nlet audioModule: Promise<AudioModule> | null = null;\nfunction loadBrowserAudio(): Promise<AudioModule> {\n audioModule ??= import(\"@ai-matrx/browser-audio/react\") as unknown as Promise<AudioModule>;\n return audioModule;\n}\n\n/**\n * Renders nothing until the recorder module has loaded, and says so out loud if\n * it never does. A microphone that silently fails to appear is the invisible\n * failure Law 4 forbids; this one names the reason.\n */\nfunction LazyAudio(props: {\n pick(module: AudioModule): ComponentType<Record<string, unknown>>;\n componentProps: Record<string, unknown>;\n onError(message: string): void;\n}): ReactNode {\n const [module, setModule] = useState<AudioModule | null>(null);\n const { onError } = props;\n useEffect(() => {\n let live = true;\n loadBrowserAudio().then(\n (loaded) => {\n if (live) setModule(loaded);\n },\n (cause: unknown) => {\n if (!live) return;\n onError(cause instanceof Error ? cause.message : String(cause));\n },\n );\n return () => {\n live = false;\n };\n }, [onError]);\n\n if (module === null) return null;\n const Component = props.pick(module);\n return <Component {...props.componentProps} />;\n}\n\nexport interface BrowserAudioVoicePortOptions {\n label?: string | undefined;\n /** Where a load failure is reported. Defaults to the console. */\n onLoadError?: ((message: string) => void) | undefined;\n}\n\n/** The shipped implementation over `@ai-matrx/browser-audio`. */\nexport function createBrowserAudioVoicePort(\n kernel: BrowserAudioKernel,\n options: BrowserAudioVoicePortOptions = {},\n): ChatVoicePort {\n const onError =\n options.onLoadError ??\n ((message: string) => {\n console.error(\n `[ai-matrx/chat] The microphone could not be loaded, so voice input is unavailable in this chat. — Check that @ai-matrx/browser-audio is installed and reachable from this build. (${message})`,\n );\n });\n\n return {\n renderButton(context) {\n return (\n <LazyAudio\n pick={(module) => module.VoiceInputButton}\n onError={onError}\n componentProps={{\n kernel,\n value: context.value,\n onValueChange: context.onValueChange,\n onBusyChange: context.onBusyChange,\n className: context.className,\n ...(options.label === undefined ? {} : { label: options.label }),\n }}\n />\n );\n },\n renderRecovery(context) {\n // Renders NOTHING when there is nothing to recover — that is the\n // component's own contract, and it is why this is safe to mount always.\n return (\n <LazyAudio\n pick={(module) => module.VoiceRecoveryButton}\n onError={onError}\n componentProps={{\n kernel,\n value: context.value,\n onValueChange: context.onValueChange,\n className: \"mx-chat-composer__recover mx-chat-focusable\",\n }}\n />\n );\n },\n };\n}\n","/**\n * THE `conversationSource` PORT — where the list of conversations comes from.\n *\n * Most hosts want the platform answer: `chat.conversation` rows read under RLS\n * through `@ai-matrx/chat/matrx`. `createPersistenceConversationSource` wraps\n * the persistence port and is the shipped default, so a host that already\n * passes `persistence` gets a working history list with no extra wiring (THE\n * ALL-INCLUSIVE LAW).\n *\n * 🚨 BUT SOME HOSTS' SERVERS NAME THE CONVERSATIONS, and the client must never\n * invent an id. The Workflow Studio is the live example: its launcher derives a\n * BOUNDED LADDER of ids per person × target (`conversation_context/\n * launcher_conversations.py` — generation 0 is the id that already existed,\n * generation N appends `#N` to the same uuid5 seed) and REFUSES any id off that\n * ladder, because a conversation id is an authority: it names whose transcript\n * the agent continues. Such a host supplies its own source, whose `list` returns\n * what the launcher listed and whose `startNew` returns the id the SERVER\n * answered with. Nothing in this package mints one.\n *\n * THE ARCHIVED-ITEMS LAW is in the query's shape, not in a caller's discipline:\n * `archive` is the platform tri-state from\n * `@ai-matrx/design-system`'s `ArchiveFilter`, its default hides archived rows,\n * and the page REPORTS `archivedHidden` so the footer can name what it hid.\n */\n\nimport {\n DEFAULT_ARCHIVE_FILTER,\n type ArchiveFilterValue,\n} from \"@ai-matrx/design-system\";\n\nimport type {\n ChatConversationSummary,\n ChatPersistencePort,\n} from \"../../ports\";\n\nexport interface ChatConversationSourceQuery {\n /** The platform tri-state. `active` (the default) hides archived rows. */\n archive: ArchiveFilterValue;\n /** Free text the reader typed. Only meaningful when the source answers it\n * server-side — see `searchesServerSide`. */\n search?: string | undefined;\n sourceApp?: string | undefined;\n sourceFeature?: string | undefined;\n limit?: number | undefined;\n /** Keyset cursor from a previous page's `nextBefore`. */\n before?: string | null | undefined;\n}\n\nexport interface ChatConversationSourcePage {\n conversations: readonly ChatConversationSummary[];\n /**\n * How many rows this page HID because of the archive filter, so the footer\n * can name them. Under `archive: \"archived\"` it counts the active rows hidden\n * instead — the footer says which, because a number with no noun is a lie\n * waiting to happen.\n */\n archivedHidden: number;\n /** Cursor for the next page, or null when the source reached the end. */\n nextBefore: string | null;\n}\n\n/**\n * WHERE THE CONVERSATIONS COME FROM.\n *\n * An optional method that is ABSENT means the capability does not exist here,\n * and the list renders no control for it. That is the point: a rename button\n * that cannot rename is the dead control Law 4 forbids, and a source is the one\n * place that truth is known.\n */\nexport interface ChatConversationSource {\n /** Diagnostic name — appears in the error sink, never on screen. */\n readonly id: string;\n /**\n * Whether `query.search` reaches the server. False means the reader's text\n * filters only what is already loaded, AND THE LIST SAYS SO.\n */\n readonly searchesServerSide: boolean;\n list(query: ChatConversationSourceQuery): Promise<ChatConversationSourcePage>;\n /**\n * Begin a new conversation and return THE ID THE SOURCE NAMED — the Workflow\n * Studio ladder's next generation, for instance.\n *\n * PRESENCE IS THE CAPABILITY, exactly like `rename` and `setArchived`. A\n * source that does not name ids omits this method, and the shell falls back\n * to the host's `onNewConversation` or, failing that, to the thread's own\n * `startNewConversation()`. With none of the three the New control is ABSENT\n * rather than dead (Law 4).\n */\n startNew?(): Promise<string>;\n rename?(conversationId: string, title: string): Promise<void>;\n setArchived?(conversationId: string, archived: boolean): Promise<void>;\n}\n\n/** How many pages the default source will walk to fill one screen. */\nconst MAX_PAGE_WALK = 8;\nconst DEFAULT_LIMIT = 30;\n\n/**\n * THE SHIPPED DEFAULT — the `chat.*` schema through the persistence port.\n *\n * `listConversations` speaks a BOOLEAN (`includeArchived`) while the platform\n * control speaks THREE states, so \"Archived only\" is assembled here: ask for\n * everything and keep the archived rows, walking the keyset cursor until the\n * page is full. That walk is bounded, and when it stops early it leaves\n * `nextBefore` set — so the reader gets a real \"Load more\" instead of a list\n * that quietly claims to be complete.\n */\nexport function createPersistenceConversationSource(\n persistence: ChatPersistencePort,\n): ChatConversationSource {\n const source: ChatConversationSource = {\n id: \"chat.conversation\",\n // `ChatSelectQuery` has no text predicate, so a title search here would be\n // a client-side sieve dressed as a server query. It says so instead.\n searchesServerSide: false,\n\n async list(query) {\n const limit = query.limit ?? DEFAULT_LIMIT;\n const archive = query.archive ?? DEFAULT_ARCHIVE_FILTER;\n const includeArchived = archive !== \"active\";\n\n const kept: ChatConversationSummary[] = [];\n let hidden = 0;\n let before = query.before ?? undefined;\n let nextBefore: string | null = null;\n\n for (let page = 0; page < MAX_PAGE_WALK; page += 1) {\n const result = await persistence.listConversations({\n limit,\n includeArchived,\n ...(before === undefined || before === null ? {} : { before }),\n ...(query.sourceApp === undefined ? {} : { sourceApp: query.sourceApp }),\n ...(query.sourceFeature === undefined\n ? {}\n : { sourceFeature: query.sourceFeature }),\n });\n\n // Under `active` the port did the hiding and counted it for us.\n hidden += result.archivedHidden;\n\n for (const conversation of result.conversations) {\n if (archive === \"archived\" && !conversation.archived) {\n hidden += 1;\n continue;\n }\n if (kept.length === limit) break;\n kept.push(conversation);\n }\n\n nextBefore = result.nextBefore;\n if (kept.length >= limit || result.nextBefore === null) break;\n before = result.nextBefore;\n }\n\n return { conversations: kept, archivedHidden: hidden, nextBefore };\n },\n\n // NO `startNew`, on purpose: the platform mints a conversation id with the\n // FIRST TURN (`isNewConversation` in the kernel's send), so there is no id\n // to hand back before the reader has said anything — and inventing one here\n // is exactly what a client may never do. The New control still works on\n // this source: the thread's own `startNewConversation()` empties it and\n // forgets its id, and the next turn is what names the conversation.\n\n rename(conversationId, title) {\n return persistence.rename(conversationId, title);\n },\n\n setArchived(conversationId, archived) {\n return persistence.setArchived(conversationId, archived);\n },\n };\n return source;\n}\n","/**\n * SWITCHING CONVERSATIONS — one rule, and it is honest about what it costs.\n *\n * Three things happen, in this order, and none of them is optional:\n *\n * 1. THE HOST MAY REFUSE, IN ITS OWN WORDS. A host knows things this package\n * cannot: the Workflow Studio refuses while an agent's write is waiting for\n * the Creator to decide, because the question that answers it lives in THIS\n * conversation, and walking away would leave the canvas holding a change\n * with nothing on screen asking about it. The guard returns the SENTENCE,\n * not a boolean — a refusal the reader cannot read is a dead control.\n *\n * 2. AN IN-FLIGHT ANSWER IS STOPPED, AND WE SAY WHAT THAT DOES NOT PROMISE.\n * Doctrine: a disconnect never cancels server work. So the notice says the\n * reply stopped arriving here and that work already in motion may still\n * land — it never claims to have cancelled anything (Law 4). A stream in\n * flight is NOT a refusal; readers switch away from a bad turn constantly\n * and blocking that is the worse product.\n *\n * 3. THE NEW TRANSCRIPT IS LOADED, and the id used is one the SOURCE named.\n */\n\nimport type { ChatNotifierPort } from \"../../ports\";\nimport type { ChatThreadStore } from \"../../store\";\n\n/**\n * A host's veto over leaving this conversation right now.\n *\n * Return `null` to allow, or the sentence the reader should see. It is called\n * for a switch AND for starting a new conversation.\n */\nexport type ChatConversationSwitchGuard = (\n target: { conversationId: string | null; isNew: boolean },\n) => string | null;\n\nexport const STREAM_INTERRUPTED_NOTICE =\n \"Stopped that reply here so this conversation could change.\";\nexport const STREAM_INTERRUPTED_REMEDY =\n \"Anything the assistant had already started may still finish and be saved to the conversation you left.\";\n\nexport interface SwitchChatConversationInput {\n thread: ChatThreadStore;\n notifier: ChatNotifierPort;\n /** The conversation to open, or null for a brand-new one. */\n conversationId: string | null;\n guard?: ChatConversationSwitchGuard | undefined;\n /**\n * The host's own handler. Present for a host whose thread store is created\n * per conversation (the ordinary case for a NEW conversation, because the\n * kernel mints its id with the first turn). When it is present it REPLACES\n * the package's `loadConversation` call — the host is swapping the thread.\n */\n onSwitch?: ((conversationId: string | null) => void | Promise<void>) | undefined;\n}\n\nexport interface SwitchChatConversationResult {\n switched: boolean;\n /** The host guard's own sentence, when it refused. */\n refusal: string | null;\n /** True when a running answer was stopped to make the switch. */\n interrupted: boolean;\n}\n\nexport async function switchChatConversation(\n input: SwitchChatConversationInput,\n): Promise<SwitchChatConversationResult> {\n const { thread, notifier, conversationId, guard, onSwitch } = input;\n const isNew = conversationId === null;\n\n const refusal = guard?.({ conversationId, isNew }) ?? null;\n if (refusal !== null) {\n notifier.notify({ level: \"info\", message: refusal });\n return { switched: false, refusal, interrupted: false };\n }\n\n const status = thread.getSnapshot().status;\n const busy = status === \"streaming\" || status === \"submitted\";\n if (busy) {\n await thread.cancel();\n notifier.notify({\n level: \"info\",\n message: STREAM_INTERRUPTED_NOTICE,\n remedy: STREAM_INTERRUPTED_REMEDY,\n });\n }\n\n if (onSwitch !== undefined) {\n await onSwitch(conversationId);\n return { switched: true, refusal: null, interrupted: busy };\n }\n\n // No host handler. An existing conversation loads through the persistence\n // port; a NEW one is started ON THIS STORE when the store can do it — the\n // kernel clears its conversation id and lets the first turn mint the next\n // one, which is why the studio's dock does not have to swap threads to give\n // a reader a New button.\n if (conversationId === null) {\n if (thread.canStartNewConversation) {\n await thread.startNewConversation();\n return { switched: true, refusal: null, interrupted: busy };\n }\n notifier.notify({\n level: \"warning\",\n message: \"A new conversation could not be started here.\",\n remedy:\n \"This chat's state is owned by the application around it. Give it an `onNewConversation` handler, an `onStartNewConversation` on its external store, or a conversationSource whose server names the new conversation.\",\n });\n return { switched: false, refusal: null, interrupted: busy };\n }\n\n await thread.loadConversation(conversationId);\n return { switched: true, refusal: null, interrupted: busy };\n}\n","/**\n * `useChatConversationList()` — the history list's whole logic, with no opinion\n * about pixels. `<ChatConversationList/>` renders it; the four shells arrange\n * it; a host that wants its own list markup uses this hook and keeps every rule\n * below.\n *\n * THE ARCHIVED-ITEMS LAW lives here, not in the component: the filter starts at\n * the platform default (`features.hideArchivedConversations` decides which of\n * `active` / `all` that is), the source hides archived rows in the READ, and\n * `archivedHidden` comes back so the footer can NAME what it hid.\n *\n * DRAFTS: a conversation the reader started typing in and left shows it. The\n * text is the composer's — written by `useChatComposerState` through the\n * `prefs` port — and this hook only READS it. Two writers of one draft is how a\n * draft gets lost.\n */\n\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from \"react\";\n\nimport {\n DEFAULT_ARCHIVE_FILTER,\n type ArchiveFilterValue,\n} from \"@ai-matrx/design-system\";\n\nimport type { ChatConversationSummary } from \"../../ports\";\nimport { useChatConfig, useChatPorts } from \"../hooks\";\nimport { useChatContext } from \"../provider\";\nimport type {\n ChatConversationSource,\n ChatConversationSourcePage,\n} from \"./conversation-source\";\nimport {\n switchChatConversation,\n type ChatConversationSwitchGuard,\n} from \"./switching\";\n\n/**\n * The composer's draft key, restated for the READER of a draft.\n *\n * It is not imported from `../hooks` because that file belongs to no lane and\n * this one does — so the agreement is proven instead of assumed:\n * `react-shells.test.tsx` writes a draft through the REAL\n * `useChatComposerState` and requires this function to find it. Drift turns\n * that test red.\n */\nexport function chatDraftKey(conversationId: string | null): string {\n return `ai-matrx.chat.draft.${conversationId ?? \"new\"}`;\n}\n\nexport interface UseChatConversationListOptions {\n /** Where the conversations come from. */\n source: ChatConversationSource;\n /** Which row is current. Defaults to the thread's own conversation id. */\n activeConversationId?: string | null | undefined;\n /** The host's veto, in the host's own words. */\n guard?: ChatConversationSwitchGuard | undefined;\n /** Host-owned switching (it swaps the thread). Absent ⇒ the package loads\n * the conversation through the persistence port. */\n onSwitch?: ((conversationId: string | null) => void | Promise<void>) | undefined;\n /** Host-owned \"start a new one\" — it hands the chat a fresh thread. */\n onNewConversation?: (() => void | Promise<void>) | undefined;\n pageSize?: number | undefined;\n sourceApp?: string | undefined;\n sourceFeature?: string | undefined;\n}\n\nexport interface ChatConversationRowModel {\n conversation: ChatConversationSummary;\n /** True for the conversation on screen right now. */\n current: boolean;\n /** The unsent words waiting in this conversation, or null. */\n draft: string | null;\n}\n\nexport interface UseChatConversationListResult {\n rows: readonly ChatConversationRowModel[];\n /** How many rows the archive filter hid, for the footer to name. */\n archivedHidden: number;\n /** Which noun `archivedHidden` counts, so the footer never prints a bare\n * number: \"archived\" under the default, \"active\" under \"Archived only\". */\n hiddenNoun: \"archived\" | \"active\";\n loading: boolean;\n /** A read that failed, in the reader's words. Never an empty list pretending\n * the account has no conversations. */\n error: string | null;\n hasMore: boolean;\n archive: ArchiveFilterValue;\n setArchive(next: ArchiveFilterValue): void;\n search: string;\n setSearch(next: string): void;\n /** False when the source cannot search the server: the list says so. */\n searchesServerSide: boolean;\n /** How many loaded rows the reader's text filtered out of view. */\n searchHidden: number;\n refresh(): void;\n loadMore(): void;\n canStartNew: boolean;\n canRename: boolean;\n canArchive: boolean;\n startNew(): Promise<boolean>;\n open(conversationId: string): Promise<boolean>;\n rename(conversationId: string, title: string): Promise<boolean>;\n setArchived(conversationId: string, archived: boolean): Promise<boolean>;\n /** The host guard's sentence from the last refusal, for an inline notice. */\n refusal: string | null;\n dismissRefusal(): void;\n}\n\nconst DEFAULT_PAGE_SIZE = 30;\n\nexport function useChatConversationList(\n options: UseChatConversationListOptions,\n): UseChatConversationListResult {\n const { source, guard, onSwitch, onNewConversation } = options;\n const { thread } = useChatContext();\n const ports = useChatPorts();\n const features = useChatConfig();\n const pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE;\n\n const [archive, setArchiveState] = useState<ArchiveFilterValue>(() =>\n features.hideArchivedConversations ? DEFAULT_ARCHIVE_FILTER : \"all\",\n );\n const [search, setSearch] = useState(\"\");\n const [conversations, setConversations] = useState<ChatConversationSummary[]>([]);\n const [archivedHidden, setArchivedHidden] = useState(0);\n const [nextBefore, setNextBefore] = useState<string | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [refusal, setRefusal] = useState<string | null>(null);\n const [nonce, setNonce] = useState(0);\n const [pending, setPending] = useState<string | null>(null);\n\n // Every read is sequence-guarded: a slow \"Active only\" landing after a fast\n // \"Archived only\" would otherwise repaint the list with the wrong rows.\n const sequence = useRef(0);\n\n /**\n * THE SOURCE AND THE HOST'S CALLBACKS LIVE IN REFS, AND THE LOAD EFFECT DOES\n * NOT DEPEND ON THEM.\n *\n * `<ChatPage history={useChatConversationList({ source: createSource(…) })}/>`\n * is the shape a host will write, and it builds a new source object on every\n * render. If the read effect depended on that identity it would fetch, set\n * state, re-render, build a new source and fetch again — an infinite loop\n * that pins a CPU and hammers the database. The reader's actual inputs\n * (the filter, the text, the page size, the scope) are primitives, and those\n * are what the effect watches.\n */\n const sourceRef = useRef(source);\n sourceRef.current = source;\n const guardRef = useRef(guard);\n guardRef.current = guard;\n const onSwitchRef = useRef(onSwitch);\n onSwitchRef.current = onSwitch;\n const onNewRef = useRef(onNewConversation);\n onNewRef.current = onNewConversation;\n\n const load = useCallback(\n async (before: string | null) => {\n const ticket = (sequence.current += 1);\n setLoading(true);\n try {\n const page: ChatConversationSourcePage = await sourceRef.current.list({\n archive,\n limit: pageSize,\n ...(search === \"\" ? {} : { search }),\n ...(before === null ? {} : { before }),\n ...(options.sourceApp === undefined ? {} : { sourceApp: options.sourceApp }),\n ...(options.sourceFeature === undefined\n ? {}\n : { sourceFeature: options.sourceFeature }),\n });\n if (ticket !== sequence.current) return;\n setConversations((previous) =>\n before === null\n ? [...page.conversations]\n : [...previous, ...page.conversations],\n );\n setArchivedHidden((previous) =>\n before === null ? page.archivedHidden : previous + page.archivedHidden,\n );\n setNextBefore(page.nextBefore);\n setError(null);\n } catch (cause) {\n if (ticket !== sequence.current) return;\n const message =\n cause instanceof Error && cause.message !== \"\"\n ? cause.message\n : \"The list of conversations could not be read.\";\n setError(message);\n ports.notifier.notify({\n level: \"error\",\n message: \"Your earlier conversations could not be loaded.\",\n remedy: \"Try again in a moment; nothing in this conversation is affected.\",\n });\n } finally {\n if (ticket === sequence.current) setLoading(false);\n }\n },\n [archive, search, pageSize, options.sourceApp, options.sourceFeature, ports.notifier],\n );\n\n useEffect(() => {\n void load(null);\n }, [load, nonce]);\n\n const refresh = useCallback(() => setNonce((n) => n + 1), []);\n const loadMore = useCallback(() => {\n if (nextBefore === null) return;\n void load(nextBefore);\n }, [load, nextBefore]);\n\n const setArchive = useCallback((next: ArchiveFilterValue) => {\n setArchiveState(next);\n setConversations([]);\n setArchivedHidden(0);\n setNextBefore(null);\n }, []);\n\n // ONE value off the store, not the whole snapshot: `useChatThread()` here\n // would re-render the sidebar on every coalesced streaming tick, and a\n // conversation list that repaints 60 times a second is the performance bug\n // design §7 rule 2 exists to prevent.\n const readConversationId = useCallback(\n () => thread.getSnapshot().conversationId,\n [thread],\n );\n const threadConversationId = useSyncExternalStore(\n thread.subscribe,\n readConversationId,\n readConversationId,\n );\n const activeConversationId =\n options.activeConversationId === undefined\n ? threadConversationId\n : options.activeConversationId;\n\n const needle = search.trim().toLowerCase();\n const visible = useMemo(() => {\n if (needle === \"\" || source.searchesServerSide) return conversations;\n return conversations.filter((conversation) =>\n (conversation.title ?? \"\").toLowerCase().includes(needle),\n );\n }, [conversations, needle, source.searchesServerSide]);\n\n const rows = useMemo<ChatConversationRowModel[]>(\n () =>\n visible.map((conversation) => {\n const draft = ports.prefs.get(chatDraftKey(conversation.id));\n return {\n conversation,\n current: conversation.id === activeConversationId,\n draft: draft === null || draft === \"\" ? null : draft,\n };\n }),\n // `pending` is in the dependency list so a rename or an archive repaints\n // the drafts too — the prefs port is not subscribable, and a row model that\n // never re-reads it shows yesterday's draft.\n [visible, activeConversationId, ports.prefs, pending],\n );\n\n const switchTo = useCallback(\n async (conversationId: string | null): Promise<boolean> => {\n setPending(conversationId);\n try {\n const result = await switchChatConversation({\n thread,\n notifier: ports.notifier,\n conversationId,\n ...(guardRef.current === undefined ? {} : { guard: guardRef.current }),\n ...(onSwitchRef.current === undefined\n ? {}\n : { onSwitch: onSwitchRef.current }),\n });\n setRefusal(result.refusal);\n return result.switched;\n } finally {\n setPending(null);\n }\n },\n [thread, ports.notifier],\n );\n\n /**\n * THREE WAYS A NEW CONVERSATION CAN EXIST, and the control appears if ANY of\n * them does. In order of authority: the source's server names the id; the\n * host hands the chat a fresh thread; or the thread itself can start one\n * (the kernel, which clears its id and lets the first turn mint the next).\n * None of them is the client inventing an id. With none of the three the\n * control is ABSENT rather than dead.\n */\n const canStartNew =\n source.startNew !== undefined ||\n onNewConversation !== undefined ||\n thread.canStartNewConversation;\n\n const startNew = useCallback(async (): Promise<boolean> => {\n const current = sourceRef.current;\n const onNew = onNewRef.current;\n if (current.startNew !== undefined) {\n const named = await current.startNew();\n const switched = await switchTo(named);\n if (switched) refresh();\n return switched;\n }\n if (onNew === undefined) {\n // Through the ONE switch path, so the host's guard, the stop of an\n // in-flight answer and its notice all still apply to a new conversation.\n const switched = await switchTo(null);\n if (switched) refresh();\n return switched;\n }\n const refused = guardRef.current?.({ conversationId: null, isNew: true }) ?? null;\n if (refused !== null) {\n setRefusal(refused);\n ports.notifier.notify({ level: \"info\", message: refused });\n return false;\n }\n const status = thread.getSnapshot().status;\n if (status === \"streaming\" || status === \"submitted\") await thread.cancel();\n setRefusal(null);\n await onNew();\n refresh();\n return true;\n }, [switchTo, ports.notifier, thread, refresh]);\n\n const open = useCallback(\n async (conversationId: string) => switchTo(conversationId),\n [switchTo],\n );\n\n const rename = useCallback(\n async (conversationId: string, title: string): Promise<boolean> => {\n const trimmed = title.trim();\n const rename_ = sourceRef.current.rename;\n if (rename_ === undefined || trimmed === \"\") return false;\n try {\n await rename_(conversationId, trimmed);\n } catch {\n ports.notifier.notify({\n level: \"error\",\n message: \"That conversation could not be renamed.\",\n remedy: \"Its name is unchanged. Try again in a moment.\",\n });\n return false;\n }\n setConversations((previous) =>\n previous.map((conversation) =>\n conversation.id === conversationId\n ? { ...conversation, title: trimmed }\n : conversation,\n ),\n );\n return true;\n },\n [ports.notifier],\n );\n\n const setArchived = useCallback(\n async (conversationId: string, archived: boolean): Promise<boolean> => {\n const setArchived_ = sourceRef.current.setArchived;\n if (setArchived_ === undefined) return false;\n try {\n await setArchived_(conversationId, archived);\n } catch {\n ports.notifier.notify({\n level: \"error\",\n message: archived\n ? \"That conversation could not be archived.\"\n : \"That conversation could not be brought back.\",\n remedy: \"It is unchanged. Try again in a moment.\",\n });\n return false;\n }\n // Re-read rather than patch in place: whether the row still belongs in\n // this view is the FILTER's answer, and `archivedHidden` must change with\n // it or the footer starts lying.\n refresh();\n return true;\n },\n [ports.notifier, refresh],\n );\n\n return {\n rows,\n archivedHidden,\n hiddenNoun: archive === \"archived\" ? \"active\" : \"archived\",\n loading,\n error,\n hasMore: nextBefore !== null,\n archive,\n setArchive,\n search,\n setSearch,\n searchesServerSide: source.searchesServerSide,\n searchHidden: needle === \"\" ? 0 : conversations.length - visible.length,\n refresh,\n loadMore,\n canStartNew,\n canRename: source.rename !== undefined,\n canArchive: source.setArchived !== undefined,\n startNew,\n open,\n rename,\n setArchived,\n refusal,\n dismissRefusal: useCallback(() => setRefusal(null), []),\n };\n}\n","/**\n * `<ChatConversationList/>` — the history list every shell puts beside the\n * thread.\n *\n * What it shows per row, and why: the TITLE (or the reader's own first words\n * when the row has no title yet — an id is never a name), WHEN it was last\n * touched in relative words, HOW MUCH was said when the source knows, and an\n * unsent DRAFT when one is waiting. That is exactly the line the Workflow\n * Studio's own history popover shows today, and it is the line Claude and\n * ChatGPT show: a person picks a conversation by what it was about and when.\n *\n * THE ARCHIVED-ITEMS LAW, visibly: the platform `ArchiveFilter` sits in the\n * header, the default hides archived rows, revealing them is ONE click, and the\n * FOOTER NAMES WHAT IS HIDDEN — never a bare number, never silence.\n *\n * Every control here is absent or honest (Law 4). Rename and archive render\n * only when the source can do them; \"New conversation\" renders only when\n * something can actually start one.\n */\n\nimport { useEffect, useRef, useState, type ReactNode } from \"react\";\n\nimport { ArchiveFilter, ErrorBox } from \"@ai-matrx/design-system\";\nimport { formatRelativeTime } from \"@ai-matrx/kit/format\";\n\nimport type { ChatConversationSummary } from \"../../ports\";\nimport { useChatSlots } from \"../hooks\";\nimport type {\n ChatConversationRowModel,\n UseChatConversationListResult,\n} from \"./use-conversation-list\";\n\n/**\n * \"4 messages · 2 days ago\" — plain words.\n *\n * A message count of zero is NOT printed as \"0 messages\": a conversation\n * nobody has written in yet says so, because a zero dressed as a count reads as\n * a broken row.\n */\nexport function conversationMetaLine(\n conversation: ChatConversationSummary,\n now?: number,\n): string {\n // \"2 days ago\" in the reader's language — kit's `intl` voice, which is the\n // `Intl.RelativeTimeFormat` body this file used to carry.\n const when = conversation.updatedAt\n ? formatRelativeTime(conversation.updatedAt, { style: \"intl\", now, fallback: \"\" }) || null\n : null;\n const count = conversation.messageCount;\n const said =\n count <= 0 ? \"Nothing said yet\" : count === 1 ? \"1 message\" : `${count} messages`;\n return when === null ? said : `${said} · ${when}`;\n}\n\n/** The title a row shows when the row has none of its own. */\nexport const UNTITLED_CONVERSATION = \"New conversation\";\n\nexport interface ChatConversationListProps {\n list: UseChatConversationListResult;\n /** Accessible name for the list region. */\n label?: string | undefined;\n /** Hide the search box on a surface too narrow for it (the dock at 320px). */\n showSearch?: boolean | undefined;\n className?: string | undefined;\n}\n\nexport function ChatConversationList(props: ChatConversationListProps): ReactNode {\n const { list } = props;\n const slots = useChatSlots();\n const label = props.label ?? \"Your conversations\";\n const showSearch = props.showSearch ?? true;\n\n return (\n <div className={joinClass(\"mx-chat-history\", props.className)}>\n <div className=\"mx-chat-history__header\">\n {slots.conversationListHeader ?? null}\n {list.canStartNew ? (\n <button\n type=\"button\"\n className=\"mx-chat-history__new mx-chat-focusable\"\n onClick={() => void list.startNew()}\n >\n New conversation\n </button>\n ) : null}\n {showSearch ? (\n <input\n type=\"search\"\n className=\"mx-chat-history__search mx-chat-focusable\"\n value={list.search}\n placeholder=\"Search conversations\"\n aria-label=\"Search conversations\"\n onChange={(event) => list.setSearch(event.target.value)}\n />\n ) : null}\n {/* THE ARCHIVED-ITEMS LAW says a list that HIDES archived rows must let\n the reader reveal them in one click. It does not say a surface where\n nothing can ever be archived must carry a tri-state that changes\n nothing — that is the dead control Law 4 forbids, and the Workflow\n Studio's launcher ladder (whose conversations the server names and\n never archives) is the live case that found it. */}\n {list.canArchive ? (\n <ArchiveFilter\n value={list.archive}\n onValueChange={list.setArchive}\n aria-label=\"Show archived conversations\"\n />\n ) : null}\n </div>\n\n {list.refusal !== null ? (\n <p className=\"mx-chat-history__refusal\" role=\"status\">\n {list.refusal}\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n onClick={list.dismissRefusal}\n >\n Dismiss\n </button>\n </p>\n ) : null}\n\n {list.error !== null ? (\n <ErrorBox className=\"mx-chat-history__error\" error={list.error} operation=\"Load earlier conversations\" origin=\"chat/ConversationList\">\n Your earlier conversations could not be loaded.\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n onClick={list.refresh}\n >\n Try again\n </button>\n </ErrorBox>\n ) : null}\n\n <ul className=\"mx-chat-history__list\" aria-label={label} aria-busy={list.loading}>\n {list.rows.map((row) => (\n <ConversationRow key={row.conversation.id} row={row} list={list} />\n ))}\n </ul>\n\n {list.rows.length === 0 && !list.loading && list.error === null ? (\n <p className=\"mx-chat-history__empty\">\n {list.search.trim() === \"\"\n ? \"No conversations here yet.\"\n : \"No conversations match what you typed.\"}\n </p>\n ) : null}\n\n {list.hasMore ? (\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-history__more mx-chat-focusable\"\n onClick={list.loadMore}\n disabled={list.loading}\n >\n {list.loading ? \"Loading…\" : \"Load more\"}\n </button>\n ) : null}\n\n <ConversationListFooter list={list} />\n {slots.conversationListFooter ?? null}\n </div>\n );\n}\n\n/**\n * THE FOOTER THAT NAMES WHAT IS HIDDEN.\n *\n * The law's words: the default hides archived rows, revealing them is one\n * click, and counts describe what the list actually renders. So this line says\n * the number AND the noun AND offers the click — a footer that printed \"3\n * hidden\" would be the screen lying by omission.\n */\nexport function ConversationListFooter({\n list,\n}: {\n list: UseChatConversationListResult;\n}): ReactNode {\n const lines: ReactNode[] = [];\n\n if (list.archivedHidden > 0) {\n const noun = list.hiddenNoun;\n const count = list.archivedHidden;\n lines.push(\n <span key=\"archive\">\n {count === 1\n ? `1 ${noun} conversation is hidden.`\n : `${count} ${noun} conversations are hidden.`}{\" \"}\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n onClick={() => list.setArchive(\"all\")}\n >\n Show them\n </button>\n </span>,\n );\n }\n\n if (!list.searchesServerSide && list.search.trim() !== \"\") {\n lines.push(\n <span key=\"search\">\n Searching only the conversations loaded so far.\n {list.hasMore ? \" Load more to search further.\" : \"\"}\n </span>,\n );\n }\n\n if (lines.length === 0) return null;\n return (\n <p className=\"mx-chat-history__footer\" role=\"note\">\n {lines}\n </p>\n );\n}\n\nfunction ConversationRow({\n row,\n list,\n}: {\n row: ChatConversationRowModel;\n list: UseChatConversationListResult;\n}): ReactNode {\n const slots = useChatSlots();\n const [renaming, setRenaming] = useState(false);\n const conversation = row.conversation;\n const title = conversation.title ?? UNTITLED_CONVERSATION;\n\n const override = slots.conversationRow?.(row);\n if (override !== undefined) return <li className=\"mx-chat-history__row\">{override}</li>;\n\n return (\n <li className=\"mx-chat-history__row\" data-current={row.current}>\n {renaming && list.canRename ? (\n <RenameField\n title={title}\n onCancel={() => setRenaming(false)}\n onCommit={async (next) => {\n await list.rename(conversation.id, next);\n setRenaming(false);\n }}\n />\n ) : (\n <button\n type=\"button\"\n className=\"mx-chat-history__open mx-chat-focusable\"\n aria-current={row.current ? \"true\" : undefined}\n onClick={() => void list.open(conversation.id)}\n >\n <span className=\"mx-chat-history__title\">{title}</span>\n <span className=\"mx-chat-history__meta\">\n {conversationMetaLine(conversation)}\n </span>\n {row.draft !== null ? (\n <span className=\"mx-chat-history__draft\">\n Unsent: {shorten(row.draft)}\n </span>\n ) : null}\n </button>\n )}\n\n <span className=\"mx-chat-history__actions\">\n {list.canRename && !renaming ? (\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n onClick={() => setRenaming(true)}\n >\n Rename\n </button>\n ) : null}\n {list.canArchive ? (\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n onClick={() => void list.setArchived(conversation.id, !conversation.archived)}\n >\n {conversation.archived ? \"Bring back\" : \"Archive\"}\n </button>\n ) : null}\n </span>\n </li>\n );\n}\n\nfunction RenameField({\n title,\n onCommit,\n onCancel,\n}: {\n title: string;\n onCommit: (next: string) => Promise<void>;\n onCancel: () => void;\n}): ReactNode {\n const [value, setValue] = useState(title);\n const ref = useRef<HTMLInputElement>(null);\n useEffect(() => ref.current?.select(), []);\n return (\n <input\n ref={ref}\n className=\"mx-chat-history__rename mx-chat-focusable\"\n value={value}\n aria-label={`Rename ${title}`}\n onChange={(event) => setValue(event.target.value)}\n onBlur={onCancel}\n onKeyDown={(event) => {\n if (event.key === \"Enter\") {\n event.preventDefault();\n void onCommit(value);\n }\n if (event.key === \"Escape\") {\n event.preventDefault();\n onCancel();\n }\n }}\n />\n );\n}\n\nconst DRAFT_PREVIEW = 64;\n\nfunction shorten(text: string): string {\n const flat = text.replace(/\\s+/g, \" \").trim();\n return flat.length <= DRAFT_PREVIEW ? flat : `${flat.slice(0, DRAFT_PREVIEW - 1)}…`;\n}\n\nfunction joinClass(...parts: (string | undefined)[]): string {\n return parts.filter((part) => part !== undefined && part !== \"\").join(\" \");\n}\n","/**\n * `<ChatSurface/>` — THE ONE SURFACE. All four shells render this and differ\n * only in what they put around it.\n *\n * That is the whole reason the shells are cheap: the transcript's\n * accessibility contract, the stick-to-bottom rule, the ephemeral notice and\n * the composer's keyboard behaviour are decided ONCE here, so a docked panel\n * and a phone can never drift into two different chats.\n *\n * 🚨 THE COMPOSER IS A REQUIRED PROP, NOT A STUB.\n * The composer is lane 2B's. A shell that shipped a placeholder box would be a\n * dead control wearing a composer's clothes (Law 4), and a shell that silently\n * rendered no composer would be a chat you cannot type in. So the seam is a\n * required slot: a host passes `<ChatComposer/>` the moment 2B lands, and until\n * then passes its own — and the type system, not a comment, enforces it.\n *\n * `<ChatSurface/>` is also the embedded form of the chat: a host that wants the\n * conversation inside its own page renders this directly, with no shell.\n */\n\nimport type { ReactNode } from \"react\";\n\nimport { ChatMessageList, type ChatMessageListProps } from \"../transcript\";\nimport { useChatSlots } from \"../hooks\";\n\nexport interface ChatSurfaceProps {\n /** Lane 2B's composer. Required — see the note above. */\n composer: ReactNode;\n /** Forwarded to the transcript (windowing, page size, accessible name). */\n transcript?: ChatMessageListProps | undefined;\n /** Full-width, between the shell's header and the transcript. */\n banner?: ReactNode | undefined;\n className?: string | undefined;\n}\n\nexport function ChatSurface(props: ChatSurfaceProps): ReactNode {\n const slots = useChatSlots();\n return (\n <div className={joinClass(\"mx-chat mx-chat-surface\", props.className)}>\n {props.banner ?? slots.surfaceBanner ?? null}\n <ChatMessageList {...(props.transcript ?? {})} />\n <div className=\"mx-chat-surface__composer\">{props.composer}</div>\n </div>\n );\n}\n\nexport function joinClass(...parts: (string | undefined | false)[]): string {\n return parts.filter((part): part is string => typeof part === \"string\" && part !== \"\").join(\" \");\n}\n","/**\n * `<ChatShellHeader/>` — the one header the dock, the window and the phone\n * share.\n *\n * Modelled on the Workflow Studio's `DockShell` header, which is the shape that\n * survived contact with real Creators: an icon, a TITLE that may be a\n * breadcrumb back to where you came from, a subtitle line that the WORKING\n * INDICATOR takes over while the agent is busy, then the host's own actions.\n *\n * The working indicator replacing the subtitle (rather than sitting beside it)\n * is deliberate: two lines that both claim to describe the panel's state is how\n * a header starts contradicting itself, and \"Conductor is working…\" is the more\n * urgent of the two for exactly as long as it is true.\n *\n * 🚨 NO SUBTITLE MEANS ONE ROW, NOT AN EMPTY SECOND LINE. A host that gives\n * the header only a title gets a single compact row: the working indicator\n * moves up BESIDE the title, and the subtitle element is not rendered at all.\n * A blank line held open for a sentence nobody wrote is the height the\n * Workflow Studio's Conductor dock was told to give back to the canvas\n * (2026-09-18).\n */\n\nimport type { ReactNode } from \"react\";\n\nexport interface ChatShellHeaderProps {\n /** Left of the title. A host's own glyph; the package ships none here. */\n icon?: ReactNode | undefined;\n /** The panel's name, or a breadcrumb element that returns somewhere. */\n title?: ReactNode | undefined;\n /** What this panel is about, in the reader's words. */\n subtitle?: ReactNode | undefined;\n /** Host controls: pop out, settings, close. Rendered at the end. */\n actions?: ReactNode | undefined;\n /** True while the agent is working — replaces the subtitle. */\n working?: boolean | undefined;\n workingLabel?: string | undefined;\n /** Makes the header a drag handle (the floating window). */\n dragHandleProps?: Record<string, unknown> | undefined;\n className?: string | undefined;\n}\n\nexport function ChatShellHeader(props: ChatShellHeaderProps): ReactNode {\n const workingLabel = props.workingLabel ?? \"Working…\";\n const hasSubtitleRow = props.subtitle !== undefined;\n const working =\n props.working === true ? (\n <span className=\"mx-chat-shell__working\" role=\"status\">\n <span className=\"mx-chat-shell__pulse\" aria-hidden=\"true\" />\n {workingLabel}\n </span>\n ) : null;\n return (\n <div\n className={joinHeaderClass(\"mx-chat-shell__header\", props.className)}\n data-draggable={props.dragHandleProps !== undefined}\n {...(props.dragHandleProps ?? {})}\n >\n {props.icon === undefined ? null : (\n <span className=\"mx-chat-shell__icon\">{props.icon}</span>\n )}\n <div className=\"mx-chat-shell__titles\">\n {props.title === undefined ? null : (\n <div className=\"mx-chat-shell__title\">\n {props.title}\n {hasSubtitleRow ? null : (\n <span className=\"mx-chat-shell__working-inline\">{working}</span>\n )}\n </div>\n )}\n {hasSubtitleRow ? (\n <div className=\"mx-chat-shell__subtitle\">\n {working ?? props.subtitle}\n </div>\n ) : null}\n </div>\n {props.actions === undefined ? null : (\n <span className=\"mx-chat-shell__actions\">{props.actions}</span>\n )}\n </div>\n );\n}\n\nfunction joinHeaderClass(...parts: (string | undefined)[]): string {\n return parts.filter((part) => part !== undefined && part !== \"\").join(\" \");\n}\n","/**\n * `useDraggablePanel()` — dragging that does not re-render the chat.\n *\n * THE RULE, lifted from the Workflow Studio's own `FloatingChatPanel` because\n * it was paid for there: a pointer move paints through CSS CUSTOM PROPERTIES\n * inside ONE requestAnimationFrame, and the position is committed to React\n * state EXACTLY ONCE, on release. Writing React state per pointer event\n * re-renders the transcript sixty times a second while an answer is streaming\n * into it, which is precisely the budget design §7 sets out to protect.\n *\n * Two details that look like noise and are not:\n * - the paint offsets are CLEARED in a layout effect on the frame that adopts\n * the committed position, so the panel never jumps by the offset twice;\n * - a pending frame is cancelled on unmount, because a callback holding a\n * detached element is a leak that only shows up under a long session.\n */\n\nimport {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type PointerEvent as ReactPointerEvent,\n type RefObject,\n} from \"react\";\n\nexport interface DraggablePanelPosition {\n x: number;\n y: number;\n}\n\nexport interface UseResizableWidthOptions {\n /** The host's stored width. This hook never owns it. */\n width: number;\n min: number;\n max: number;\n /** Which edge the handle sits on — the side the panel grows FROM. */\n edge: \"start\" | \"end\";\n /** Told once, on release, and on every keyboard step. */\n onWidthChange?: ((next: number) => void) | undefined;\n}\n\nexport interface ResizableWidthHandle {\n panelRef: RefObject<HTMLElement | null>;\n resizing: boolean;\n handleProps: {\n role: \"separator\";\n tabIndex: 0;\n \"aria-orientation\": \"vertical\";\n \"aria-valuenow\": number;\n \"aria-valuemin\": number;\n \"aria-valuemax\": number;\n onPointerDown(event: ReactPointerEvent): void;\n onPointerMove(event: ReactPointerEvent): void;\n onPointerUp(event: ReactPointerEvent): void;\n onPointerCancel(event: ReactPointerEvent): void;\n onKeyDown(event: { key: string; preventDefault(): void }): void;\n onDoubleClick(): void;\n };\n}\n\nconst KEYBOARD_STEP_PX = 24;\n\n/**\n * THE WIDTH IS THE HOST'S, ALWAYS.\n *\n * The package never writes a width to storage: which pref store a surface uses,\n * and whether the width is per-user or per-organization, is the host's to know\n * (Law 6 — opinions become knobs, and the knob is theirs). So this hook paints\n * the drag live and hands the final number back through one callback.\n *\n * The keyboard path is not decoration: a separator that only answers a pointer\n * is unusable with a keyboard or a screen reader, and a panel whose size cannot\n * be changed without a mouse is a dead control for the people who need it most.\n */\nexport function useResizableWidth(\n options: UseResizableWidthOptions,\n): ResizableWidthHandle {\n const { width, min, max, edge, onWidthChange } = options;\n const panelRef = useRef<HTMLElement | null>(null);\n const [resizing, setResizing] = useState(false);\n const frameRef = useRef<number | null>(null);\n const dragRef = useRef<{ startX: number; startWidth: number; latest: number } | null>(\n null,\n );\n const initialRef = useRef(width);\n\n const clamp = useCallback(\n (next: number) => Math.max(min, Math.min(max, Math.round(next))),\n [min, max],\n );\n\n const commit = useCallback(\n (next: number) => {\n panelRef.current?.style.removeProperty(\"width\");\n onWidthChange?.(clamp(next));\n },\n [clamp, onWidthChange],\n );\n\n const end = useCallback(() => {\n const drag = dragRef.current;\n if (frameRef.current !== null) {\n globalThis.cancelAnimationFrame?.(frameRef.current);\n frameRef.current = null;\n }\n dragRef.current = null;\n setResizing(false);\n if (drag !== null) commit(drag.latest);\n }, [commit]);\n\n useEffect(\n () => () => {\n if (frameRef.current !== null) globalThis.cancelAnimationFrame?.(frameRef.current);\n },\n [],\n );\n\n return {\n panelRef,\n resizing,\n handleProps: {\n role: \"separator\",\n tabIndex: 0,\n \"aria-orientation\": \"vertical\",\n \"aria-valuenow\": width,\n \"aria-valuemin\": min,\n \"aria-valuemax\": max,\n onPointerDown(event) {\n event.preventDefault();\n dragRef.current = { startX: event.clientX, startWidth: width, latest: width };\n setResizing(true);\n (event.target as HTMLElement).setPointerCapture?.(event.pointerId);\n },\n onPointerMove(event) {\n const drag = dragRef.current;\n if (drag === null) return;\n const delta =\n edge === \"end\" ? event.clientX - drag.startX : drag.startX - event.clientX;\n drag.latest = clamp(drag.startWidth + delta);\n if (frameRef.current !== null) return;\n const schedule = globalThis.requestAnimationFrame;\n const paint = (): void => {\n frameRef.current = null;\n const active = dragRef.current;\n if (active === null || panelRef.current === null) return;\n panelRef.current.style.width = `${active.latest}px`;\n };\n if (typeof schedule === \"function\") frameRef.current = schedule(paint);\n else paint();\n },\n onPointerUp: end,\n onPointerCancel: end,\n onKeyDown(event) {\n const grow = edge === \"end\" ? \"ArrowRight\" : \"ArrowLeft\";\n const shrink = edge === \"end\" ? \"ArrowLeft\" : \"ArrowRight\";\n if (event.key === grow) {\n event.preventDefault();\n commit(width + KEYBOARD_STEP_PX);\n return;\n }\n if (event.key === shrink) {\n event.preventDefault();\n commit(width - KEYBOARD_STEP_PX);\n }\n },\n onDoubleClick() {\n commit(initialRef.current);\n },\n },\n };\n}\n\nexport interface UseDraggablePanelOptions {\n initial: DraggablePanelPosition;\n /** Keep this much of the panel on screen on every edge. */\n keepVisiblePx?: number | undefined;\n /** Told once per drag, on release — a host that persists the position. */\n onCommit?: ((position: DraggablePanelPosition) => void) | undefined;\n}\n\nexport interface DraggablePanelHandle {\n position: DraggablePanelPosition;\n /** Attach to the element that MOVES. */\n panelRef: RefObject<HTMLElement | null>;\n /** Spread onto the element you drag BY (the header). */\n dragHandleProps: {\n onPointerDown(event: ReactPointerEvent): void;\n onPointerMove(event: ReactPointerEvent): void;\n onPointerUp(event: ReactPointerEvent): void;\n onPointerCancel(event: ReactPointerEvent): void;\n };\n /** True while a pointer is down — a shell suppresses transitions with it. */\n dragging: boolean;\n}\n\nconst DEFAULT_KEEP_VISIBLE = 64;\n\nexport function useDraggablePanel(\n options: UseDraggablePanelOptions,\n): DraggablePanelHandle {\n const keepVisible = options.keepVisiblePx ?? DEFAULT_KEEP_VISIBLE;\n const [position, setPosition] = useState<DraggablePanelPosition>(options.initial);\n const [dragging, setDragging] = useState(false);\n const panelRef = useRef<HTMLElement | null>(null);\n const frameRef = useRef<number | null>(null);\n const adoptingRef = useRef(false);\n const dragRef = useRef<{\n dx: number;\n dy: number;\n origin: DraggablePanelPosition;\n latest: DraggablePanelPosition;\n } | null>(null);\n\n const clamp = useCallback(\n (next: DraggablePanelPosition): DraggablePanelPosition => {\n const width = globalThis.innerWidth ?? 0;\n const height = globalThis.innerHeight ?? 0;\n if (width === 0 || height === 0) return next;\n return {\n x: Math.max(0, Math.min(width - keepVisible, next.x)),\n y: Math.max(0, Math.min(height - keepVisible, next.y)),\n };\n },\n [keepVisible],\n );\n\n const onPointerDown = useCallback(\n (event: ReactPointerEvent) => {\n // A control inside the header is a control, not a drag handle.\n if ((event.target as HTMLElement).closest(\"button,a,input,select,textarea\")) return;\n event.preventDefault();\n dragRef.current = {\n dx: event.clientX - position.x,\n dy: event.clientY - position.y,\n origin: position,\n latest: position,\n };\n setDragging(true);\n (event.target as HTMLElement).setPointerCapture?.(event.pointerId);\n },\n [position],\n );\n\n const onPointerMove = useCallback(\n (event: ReactPointerEvent) => {\n const drag = dragRef.current;\n if (drag === null) return;\n drag.latest = clamp({ x: event.clientX - drag.dx, y: event.clientY - drag.dy });\n if (frameRef.current !== null) return;\n const schedule = globalThis.requestAnimationFrame;\n const paint = (): void => {\n frameRef.current = null;\n const active = dragRef.current;\n const element = panelRef.current;\n if (active === null || element === null) return;\n element.style.setProperty(\"--mx-chat-panel-dx\", `${active.latest.x - active.origin.x}px`);\n element.style.setProperty(\"--mx-chat-panel-dy\", `${active.latest.y - active.origin.y}px`);\n };\n if (typeof schedule === \"function\") frameRef.current = schedule(paint);\n else paint();\n },\n [clamp],\n );\n\n const end = useCallback(() => {\n const drag = dragRef.current;\n if (frameRef.current !== null) {\n globalThis.cancelAnimationFrame?.(frameRef.current);\n frameRef.current = null;\n }\n dragRef.current = null;\n setDragging(false);\n if (drag === null) return;\n adoptingRef.current = true;\n setPosition(drag.latest);\n options.onCommit?.(drag.latest);\n }, [options]);\n\n useLayoutEffect(() => {\n if (!adoptingRef.current) return;\n adoptingRef.current = false;\n panelRef.current?.style.removeProperty(\"--mx-chat-panel-dx\");\n panelRef.current?.style.removeProperty(\"--mx-chat-panel-dy\");\n });\n\n useEffect(\n () => () => {\n if (frameRef.current !== null) globalThis.cancelAnimationFrame?.(frameRef.current);\n },\n [],\n );\n\n return {\n position,\n panelRef,\n dragging,\n dragHandleProps: {\n onPointerDown,\n onPointerMove,\n onPointerUp: end,\n onPointerCancel: end,\n },\n };\n}\n","/**\n * `<ChatPage/>` — the full-page chat: the conversation list beside the thread.\n *\n * The sidebar is REQUIRED here, because that is what makes this shell a page\n * rather than a bare surface: a `/chat` route with no way to reach yesterday's\n * conversation is a dead end. A host that wants only the thread renders\n * `<ChatSurface/>`.\n *\n * Below `--mx-chat-breakpoint` the sidebar leaves the row and becomes a\n * disclosure above the transcript — the same rows, one tap away, never a\n * squeezed 120px column. `<ChatMobile/>` is the deliberate phone shell; this\n * collapse is what keeps the desktop page honest when the window is narrow.\n */\n\nimport { useState, type ReactNode } from \"react\";\n\nimport { ChatConversationList } from \"./conversation-list\";\nimport { ChatShellHeader, type ChatShellHeaderProps } from \"./shell-header\";\nimport { ChatSurface, joinClass, type ChatSurfaceProps } from \"./surface\";\nimport type { UseChatConversationListResult } from \"./use-conversation-list\";\n\nexport interface ChatPageProps extends ChatSurfaceProps {\n /** The history sidebar's state, from `useChatConversationList()`. */\n history: UseChatConversationListResult;\n header?: ChatShellHeaderProps | undefined;\n /** Accessible name for the page region. */\n label?: string | undefined;\n}\n\nexport function ChatPage(props: ChatPageProps): ReactNode {\n const { history, header, label, className, ...surface } = props;\n const [listOpen, setListOpen] = useState(false);\n\n return (\n <div\n className={joinClass(\"mx-chat-shell mx-chat-shell--page\", className)}\n data-shell=\"page\"\n role=\"region\"\n aria-label={label ?? \"Chat\"}\n >\n {header === undefined ? null : <ChatShellHeader {...header} />}\n <div className=\"mx-chat-shell__row\">\n <aside className=\"mx-chat-shell__sidebar\" aria-label=\"Your conversations\">\n <ChatConversationList list={history} />\n </aside>\n <div className=\"mx-chat-shell__narrow-history\">\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n aria-expanded={listOpen}\n onClick={() => setListOpen((open) => !open)}\n >\n {listOpen ? \"Hide conversations\" : \"Your conversations\"}\n </button>\n {listOpen ? <ChatConversationList list={history} /> : null}\n </div>\n <ChatSurface {...surface} className=\"mx-chat-shell__main\" />\n </div>\n </div>\n );\n}\n","/**\n * `<ChatDock/>` — the chat docked to one side of a working surface.\n *\n * THIS IS THE SHELL THE WORKFLOW STUDIO ADOPTS (design §9, consumer #1). It is\n * modelled directly on that app's `DockShell`, and the pieces it takes are the\n * pieces that app actually passes today:\n *\n * • a breadcrumb TITLE, because the dock's occupant changes — the Conductor,\n * then a step's Steward, with a way back;\n * • an ACTIONS slot for pop-out and close;\n * • a WORKING indicator, because a Creator watching a canvas needs to know\n * the agent is mid-turn without looking at the transcript;\n * • a HOST-OWNED WIDTH: the Studio stores it in its own `ws.layout.v1` blob\n * alongside the inspector's width, and the canvas keeps a minimum — so the\n * package paints the drag and hands back ONE number. It never stores a\n * width, and it never decides the maximum, because only the host knows what\n * else is in the row.\n *\n * The history control lives in the header as a disclosure rather than a second\n * column: a 360px dock has no room for a sidebar, and the Studio's own answer\n * (a small button that opens the list) is the right one.\n */\n\nimport { useId, useState, type ReactNode } from \"react\";\n\nimport { ChatConversationList } from \"./conversation-list\";\nimport { useResizableWidth } from \"./drag\";\nimport { ChatShellHeader, type ChatShellHeaderProps } from \"./shell-header\";\nimport { ChatSurface, joinClass, type ChatSurfaceProps } from \"./surface\";\nimport type { UseChatConversationListResult } from \"./use-conversation-list\";\n\nexport const CHAT_DOCK_MIN_WIDTH = 320;\nexport const CHAT_DOCK_MAX_WIDTH = 720;\n\nexport interface ChatDockProps extends ChatSurfaceProps {\n header?: ChatShellHeaderProps | undefined;\n /** Present ⇒ the header carries \"Conversations\" and the list opens under it. */\n history?: UseChatConversationListResult | undefined;\n /** Which side of the row the dock sits on. Decides the resize edge. */\n side?: \"start\" | \"end\" | undefined;\n /** The host's stored width, in pixels. The package never stores one. */\n width?: number | undefined;\n /** Called ONCE per resize gesture, and on each keyboard step. */\n onWidthChange?: ((next: number) => void) | undefined;\n minWidth?: number | undefined;\n maxWidth?: number | undefined;\n /** Accessible name for the dock. */\n label?: string | undefined;\n}\n\nexport function ChatDock(props: ChatDockProps): ReactNode {\n const {\n header,\n history,\n side = \"start\",\n width = CHAT_DOCK_MIN_WIDTH,\n onWidthChange,\n minWidth = CHAT_DOCK_MIN_WIDTH,\n maxWidth = CHAT_DOCK_MAX_WIDTH,\n label,\n className,\n ...surface\n } = props;\n\n const [historyOpen, setHistoryOpen] = useState(false);\n const historyId = useId();\n const resize = useResizableWidth({\n width,\n min: minWidth,\n max: maxWidth,\n edge: side === \"start\" ? \"end\" : \"start\",\n ...(onWidthChange === undefined ? {} : { onWidthChange }),\n });\n\n const historyToggle =\n history === undefined ? null : (\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n aria-expanded={historyOpen}\n aria-controls={historyId}\n onClick={() => setHistoryOpen((open) => !open)}\n >\n Conversations\n </button>\n );\n\n return (\n <aside\n ref={resize.panelRef as React.RefObject<HTMLElement>}\n className={joinClass(\"mx-chat-shell mx-chat-shell--dock\", className)}\n data-shell=\"dock\"\n data-side={side}\n data-resizing={resize.resizing}\n style={{ width }}\n role=\"complementary\"\n aria-label={label ?? \"Chat\"}\n >\n {onWidthChange === undefined ? null : (\n // ABSENT, NEVER DEAD: a host that does not own a width gets no handle\n // rather than one that drags and springs back (Law 4).\n <div\n className=\"mx-chat-shell__resize mx-chat-focusable\"\n aria-label=\"Resize the chat panel\"\n {...resize.handleProps}\n />\n )}\n <ChatShellHeader\n {...(header ?? {})}\n actions={\n <>\n {historyToggle}\n {header?.actions ?? null}\n </>\n }\n />\n {history !== undefined && historyOpen ? (\n <div className=\"mx-chat-shell__history\" id={historyId}>\n <ChatConversationList list={history} showSearch={false} />\n </div>\n ) : null}\n <ChatSurface {...surface} className=\"mx-chat-shell__main\" />\n </aside>\n );\n}\n","/**\n * `<ChatWindow/>` — the floating, draggable chat over a host's own screen.\n *\n * Two rules it exists to keep, both learned in the Workflow Studio:\n *\n * 1. DRAGGING NEVER RE-RENDERS THE CHAT. The header is the handle,\n * `useDraggablePanel` paints the move through CSS variables on one\n * animation frame, and the position is committed once on release. A long\n * drag over a streaming answer costs zero React renders.\n *\n * 2. THE Z-LAYER IS A TOKEN, NEVER A NUMBER HERE. `--mx-chat-z-window` is\n * declared in `tokens.css` and a host RE-DECLARES it to place this window\n * in its own stacking order. That is not a style preference: the Studio's\n * issue panel at z-50 once swallowed the pop-out chat's own dock button,\n * and a package that hardcodes a z-index guarantees that class of bug in\n * every consumer. A chat window must never cover the host's controls.\n */\n\nimport { useId, useState, type ReactNode } from \"react\";\n\nimport { ChatConversationList } from \"./conversation-list\";\nimport { useDraggablePanel, type DraggablePanelPosition } from \"./drag\";\nimport { ChatShellHeader, type ChatShellHeaderProps } from \"./shell-header\";\nimport { ChatSurface, joinClass, type ChatSurfaceProps } from \"./surface\";\nimport type { UseChatConversationListResult } from \"./use-conversation-list\";\n\nexport const CHAT_WINDOW_WIDTH = 400;\n\nexport interface ChatWindowProps extends ChatSurfaceProps {\n header?: ChatShellHeaderProps | undefined;\n /**\n * Present ⇒ the header carries \"Conversations\" and the list opens under it,\n * exactly as in `<ChatDock/>`.\n *\n * 🚨 It is here because a floating chat and a docked one are usually the SAME\n * conversation seen two ways, and a reader who pops the panel out does not\n * expect their history to disappear with the dock. The Workflow Studio found\n * that on a live review: its floating Steward had no controls at all while\n * the docked one did.\n */\n history?: UseChatConversationListResult | undefined;\n /** Where it opens. Defaults to the top-right, inset from the edge. */\n initialPosition?: DraggablePanelPosition | undefined;\n width?: number | undefined;\n /** Told once per drag — a host that remembers where the reader left it. */\n onPositionCommit?: ((position: DraggablePanelPosition) => void) | undefined;\n label?: string | undefined;\n}\n\nexport function ChatWindow(props: ChatWindowProps): ReactNode {\n const {\n header,\n history,\n initialPosition,\n width = CHAT_WINDOW_WIDTH,\n onPositionCommit,\n label,\n className,\n ...surface\n } = props;\n\n const [fallback] = useState<DraggablePanelPosition>(() => ({\n x: Math.max(8, (globalThis.innerWidth ?? width + 32) - width - 24),\n y: 96,\n }));\n\n const [historyOpen, setHistoryOpen] = useState(false);\n const historyId = useId();\n const historyToggle =\n history === undefined ? null : (\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n aria-expanded={historyOpen}\n aria-controls={historyId}\n // A drag handle that also holds buttons must not start a drag from one.\n onPointerDown={(event) => event.stopPropagation()}\n onClick={() => setHistoryOpen((open) => !open)}\n >\n Conversations\n </button>\n );\n\n const drag = useDraggablePanel({\n initial: initialPosition ?? fallback,\n ...(onPositionCommit === undefined ? {} : { onCommit: onPositionCommit }),\n });\n\n return (\n <div\n ref={drag.panelRef as React.RefObject<HTMLDivElement>}\n className={joinClass(\"mx-chat-shell mx-chat-shell--window\", className)}\n data-shell=\"window\"\n data-dragging={drag.dragging}\n role=\"dialog\"\n aria-label={label ?? \"Chat\"}\n style={{ left: drag.position.x, top: drag.position.y, width }}\n >\n <ChatShellHeader\n {...(header ?? {})}\n dragHandleProps={drag.dragHandleProps}\n actions={\n <>\n {historyToggle}\n {header?.actions ?? null}\n </>\n }\n />\n {history !== undefined && historyOpen ? (\n <div className=\"mx-chat-shell__history\" id={historyId}>\n <ChatConversationList list={history} showSearch={false} />\n </div>\n ) : null}\n <ChatSurface {...surface} className=\"mx-chat-shell__main\" />\n </div>\n );\n}\n","/**\n * `<ChatMobile/>` — the phone shell: a full-bleed stack, with history as a\n * sheet.\n *\n * Why a sheet and not a column: at 375px a sidebar is either a squeezed strip\n * nobody can read a title in, or it takes the whole screen and the thread is\n * gone. Every phone chat worth copying — Claude, ChatGPT, Messages — puts the\n * conversation list behind one tap and gives the whole width to the thread.\n *\n * Three things this shell owns that the desktop ones do not:\n * • the composer sits above the SAFE-AREA INSET, so the send control is not\n * under the home indicator;\n * • the sheet traps nothing and closes on Escape and on a tap outside, because\n * a sheet you cannot leave is the dead end Law 4 forbids;\n * • opening a conversation CLOSES the sheet, since the reader's next act is\n * reading it.\n */\n\nimport { useCallback, useEffect, type ReactNode } from \"react\";\n\nimport { ChatConversationList } from \"./conversation-list\";\nimport { ChatShellHeader, type ChatShellHeaderProps } from \"./shell-header\";\nimport { ChatSurface, joinClass, type ChatSurfaceProps } from \"./surface\";\nimport type { UseChatConversationListResult } from \"./use-conversation-list\";\n\nexport interface ChatMobileProps extends ChatSurfaceProps {\n header?: ChatShellHeaderProps | undefined;\n history?: UseChatConversationListResult | undefined;\n /** The sheet is the HOST's state, so a back gesture or a route can close it. */\n historyOpen?: boolean | undefined;\n onHistoryOpenChange?: ((open: boolean) => void) | undefined;\n label?: string | undefined;\n}\n\nexport function ChatMobile(props: ChatMobileProps): ReactNode {\n const {\n header,\n history,\n historyOpen = false,\n onHistoryOpenChange,\n label,\n className,\n ...surface\n } = props;\n\n const close = useCallback(\n () => onHistoryOpenChange?.(false),\n [onHistoryOpenChange],\n );\n\n useEffect(() => {\n if (!historyOpen) return;\n const onKey = (event: KeyboardEvent): void => {\n if (event.key === \"Escape\") close();\n };\n globalThis.addEventListener?.(\"keydown\", onKey);\n return () => globalThis.removeEventListener?.(\"keydown\", onKey);\n }, [historyOpen, close]);\n\n const canOpenHistory = history !== undefined && onHistoryOpenChange !== undefined;\n\n return (\n <div\n className={joinClass(\"mx-chat-shell mx-chat-shell--mobile\", className)}\n data-shell=\"mobile\"\n role=\"region\"\n aria-label={label ?? \"Chat\"}\n >\n <ChatShellHeader\n {...(header ?? {})}\n actions={\n <>\n {canOpenHistory ? (\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable\"\n aria-expanded={historyOpen}\n onClick={() => onHistoryOpenChange?.(!historyOpen)}\n >\n Conversations\n </button>\n ) : null}\n {header?.actions ?? null}\n </>\n }\n />\n <ChatSurface {...surface} className=\"mx-chat-shell__main\" />\n\n {canOpenHistory && historyOpen ? (\n <>\n <div\n className=\"mx-chat-shell__scrim\"\n onClick={close}\n aria-hidden=\"true\"\n />\n <div\n className=\"mx-chat-shell__sheet\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Your conversations\"\n >\n <div className=\"mx-chat-shell__sheet-grip\" aria-hidden=\"true\" />\n <button\n type=\"button\"\n className=\"mx-chat-quiet-button mx-chat-focusable mx-chat-shell__sheet-close\"\n onClick={close}\n >\n Close\n </button>\n <ChatConversationList\n list={wrapClosingOnOpen(history, close)}\n label=\"Your conversations\"\n />\n </div>\n </>\n ) : null}\n </div>\n );\n}\n\n/**\n * Opening a conversation closes the sheet — but ONLY when the switch actually\n * happened. A host guard that refuses (an unanswered question on the screen\n * behind) leaves the sheet open, with the refusal visible in it, because\n * closing the sheet would hide the sentence explaining why nothing moved.\n */\nfunction wrapClosingOnOpen(\n list: UseChatConversationListResult,\n close: () => void,\n): UseChatConversationListResult {\n return {\n ...list,\n async open(conversationId: string) {\n const switched = await list.open(conversationId);\n if (switched) close();\n return switched;\n },\n async startNew() {\n const started = await list.startNew();\n if (started) close();\n return started;\n },\n };\n}\n","/**\n * PHASE 4, CAPABILITY 3 + 4 — THE PICKERS, AS SLOTS AND NOTHING ELSE.\n *\n * 🚨 THIS PACKAGE NEVER FETCHES AN AGENT LIST. Arman ruled on 2026-09-08 that\n * the agent picker lives in `@ai-matrx/agents` and that the host receives an\n * id; there is ONE picker on the platform and a chat package that grew a\n * second would be the fifth agent selector in the fleet, which is the exact\n * failure this campaign exists to end. So this file MOUNTS\n * `@ai-matrx/agents/catalog/react`'s `AgentListDropdown` over the catalog the\n * HOST already created and provided, and does nothing else: no `agx_get_list_full`\n * read, no `agx_search`, no membership rule, no ordering, no cache.\n * `pnpm check:canonical-pickers` fails this package's build if that changes.\n *\n * 🚨 WHY THE IMPORT IS LAZY. Same reason as the voice port, plus one of its\n * own. The catalog UI is a popover, a drawer, tabs, sorts, categories, tags and\n * a detail card — a real screen — and a host that never shows a picker should\n * not pay for it in the bundle it got for a transcript. Loading it on demand\n * also keeps `import()` (not `require`) in the CJS build, which is what stopped\n * `ERR_PACKAGE_PATH_NOT_EXPORTED` killing every CJS consumer of `./react` when\n * browser-audio did this. The module is loaded ONCE per page and shared.\n *\n * THE MODEL PICKER IS A PORT, NOT A COMPONENT (design §5.1 capability 4).\n * `ModelListDropdown` has not moved into `agents/catalog/react` yet, so the\n * package would have to invent a roster to render one — which is precisely the\n * fifth picker. Until that lane lands the host renders its own through\n * `ports.modelPicker`, and a host that has none renders NOTHING: absent, never\n * a dead control (Law 4).\n */\n\nimport {\n useEffect,\n useState,\n type ComponentType,\n type ReactNode,\n} from \"react\";\n\nimport { useChatPorts } from \"../hooks\";\n\ninterface CatalogModule {\n AgentListDropdown: ComponentType<Record<string, unknown>>;\n}\n\n/** One load per page, shared by every picker on it. */\nlet catalogModule: Promise<CatalogModule> | null = null;\nfunction loadAgentCatalog(): Promise<CatalogModule> {\n catalogModule ??= import(\"@ai-matrx/agents/catalog/react\") as unknown as Promise<CatalogModule>;\n return catalogModule;\n}\n\n/** Test seam ONLY: the suites replace the real catalog with a stand-in so a\n * render test does not have to mount a Radix popover to prove a slot. It is\n * not exported from the package barrel and a host has no use for it. */\nexport function __setAgentCatalogModuleForTests(\n module: CatalogModule | Promise<CatalogModule> | null,\n): void {\n if (module === null) {\n catalogModule = null;\n return;\n }\n catalogModule = module instanceof Promise ? module : Promise.resolve(module);\n}\n\nexport const AGENT_CATALOG_UNAVAILABLE =\n \"The list of agents could not be opened.\";\nexport const AGENT_CATALOG_UNAVAILABLE_REMEDY =\n \"Reload the page and try again; if it keeps happening, the agent directory is not reachable from here.\";\n\nexport interface ChatAgentPickerProps {\n /**\n * The agent this conversation is already pointed at, so the list can pin and\n * highlight it. `null` is a real answer — a thread that has not chosen yet.\n */\n activeAgentId?: string | null | undefined;\n /**\n * The chosen reference: an agent id, or `mandate:<key>` for the platform\n * default row. THE HOST DECIDES WHAT THAT MEANS — this package does not hold\n * the selection, because the selection is the host's to persist and to send.\n */\n onSelect(agentRef: string): void;\n /** The trigger's words. Defaults to the catalog's own. */\n label?: string | undefined;\n /** Offer the platform default agent for this mandate at the top of the list. */\n defaultMandateKey?: string | undefined;\n /** The short (h-5) trigger, for a dense composer row. */\n compact?: boolean | undefined;\n className?: string | undefined;\n}\n\n/**\n * The agent picker, for `slots.composerPickers`.\n *\n * It must be rendered inside the host's `AgentCatalogProvider` — that provider\n * IS the catalog, and it is the host's because the host owns the session the\n * list is read with. Without one the dropdown throws its own named error, which\n * is the honest outcome: a picker with no catalog is not a picker.\n */\nexport function ChatAgentPicker(props: ChatAgentPickerProps): ReactNode {\n const { notifier } = useChatPorts();\n const [module, setModule] = useState<CatalogModule | null>(null);\n const [failed, setFailed] = useState(false);\n\n useEffect(() => {\n let live = true;\n loadAgentCatalog().then(\n (loaded) => {\n if (live) setModule(loaded);\n },\n () => {\n if (!live) return;\n setFailed(true);\n notifier.notify({\n level: \"error\",\n message: AGENT_CATALOG_UNAVAILABLE,\n remedy: AGENT_CATALOG_UNAVAILABLE_REMEDY,\n });\n },\n );\n return () => {\n live = false;\n };\n }, [notifier]);\n\n // A picker that never loads SAYS SO rather than leaving a hole where the\n // reader expects to choose who answers.\n if (failed) {\n return (\n <span className=\"mx-chat-standin\" role=\"status\">\n {`${AGENT_CATALOG_UNAVAILABLE} ${AGENT_CATALOG_UNAVAILABLE_REMEDY}`}\n </span>\n );\n }\n if (module === null) return null;\n\n const Dropdown = module.AgentListDropdown;\n return (\n <Dropdown\n onSelect={props.onSelect}\n activeAgentId={props.activeAgentId ?? null}\n compact={props.compact ?? true}\n noBorder\n {...(props.label === undefined ? {} : { label: props.label })}\n {...(props.defaultMandateKey === undefined\n ? {}\n : { defaultMandateKey: props.defaultMandateKey })}\n {...(props.className === undefined ? {} : { className: props.className })}\n />\n );\n}\n\nexport interface ChatModelPickerProps {\n className?: string | undefined;\n}\n\n/**\n * The model picker, rendered by the HOST through `ports.modelPicker`.\n *\n * `null` is a real answer (\"this surface does not choose a model\"), and it\n * renders nothing at all — never a disabled control, never a stand-in sentence\n * for a capability the surface deliberately does not have.\n */\nexport function ChatModelPicker(props: ChatModelPickerProps): ReactNode {\n const { modelPicker } = useChatPorts();\n if (modelPicker === null) return null;\n return modelPicker({\n className: props.className ?? \"mx-chat-plugin-picker\",\n });\n}\n\nexport interface ChatComposerPickersProps extends ChatAgentPickerProps {\n /** Leave the agent picker out — a surface whose brain is a Mandate, not a\n * chosen agent, renders NOTHING here rather than a picker that would lie\n * about what answering it. */\n agentPicker?: boolean | undefined;\n}\n\n/**\n * Both pickers in the order a composer reads left to right: who answers, then\n * what they think with. Drop it into `slots.composerPickers`.\n */\nexport function ChatComposerPickers(props: ChatComposerPickersProps): ReactNode {\n const { agentPicker = true, ...agent } = props;\n return (\n <span className=\"mx-chat-plugin-pickers\">\n {agentPicker ? <ChatAgentPicker {...agent} /> : null}\n <ChatModelPicker />\n </span>\n );\n}\n"],"mappings":";;;;AAqNA,IAAI,YAAY;AAQT,SAAS,WAAW,QAAwB;AACjD,QAAM,YACJ,WACA;AACF,MAAI,OAAO,WAAW,eAAe,YAAY;AAC/C,WAAO,GAAG,MAAM,IAAI,UAAU,WAAW,CAAC;AAAA,EAC5C;AACA,eAAa;AACb,SAAO,GAAG,MAAM,IAAI,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,IAAI,UAAU,SAAS,EAAE,CAAC,IAAI,KAAK,OAAO,EAClF,SAAS,EAAE,EACX,MAAM,GAAG,EAAE,CAAC;AACjB;AAEO,SAAS,kBAAkB,OAMlB;AACd,SAAO;AAAA,IACL,IAAI,MAAM,MAAM,WAAW,KAAK;AAAA,IAChC,MAAM,MAAM;AAAA,IACZ,OAAO,MAAM,SAAS,CAAC;AAAA,IACvB,QAAQ,MAAM,UAAU;AAAA,IACxB,UAAU;AAAA,MACR,WAAW,MAAM,UAAU,aAAa;AAAA,MACxC,iBAAiB,MAAM,UAAU,mBAAmB;AAAA,MACpD,UAAU,MAAM,UAAU,YAAY;AAAA,MACtC,WAAW,MAAM,UAAU,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,MAC/D,KAAK,MAAM,UAAU,OAAO;AAAA,IAC9B;AAAA,EACF;AACF;AAGO,SAAS,YAAY,SAA8B;AACxD,MAAI,MAAM;AACV,aAAW,QAAQ,QAAQ,MAAO,KAAI,KAAK,SAAS,OAAQ,QAAO,KAAK;AACxE,SAAO;AACT;AAGO,SAAS,iBAAiB,SAA8B;AAC7D,MAAI,MAAM;AACV,aAAW,QAAQ,QAAQ,MAAO,KAAI,KAAK,SAAS,YAAa,QAAO,KAAK;AAC7E,SAAO;AACT;AAGO,SAAS,aAAa,SAAsC;AACjE,SAAO,QAAQ,MAAM,OAAO,CAAC,MAAyB,EAAE,SAAS,MAAM;AACzE;;;AC7PA,IAAM,OAAsC;AAAA,EAC1C,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,sBAAsB;AAAA,EACtB,oBAAoB;AAAA,EACpB,gBAAgB;AAAA,EAChB,iBAAiB;AACnB;AAEA,IAAM,WAAuC,oBAAI,IAAmB;AAAA,EAClE;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,SAAS,oBAAoB,OAA+B;AACjE,SAAO,SAAS,IAAI,KAAK;AAC3B;AAcO,SAAS,iBACd,MACA,IACqB;AACrB,MAAI,SAAS,IAAI;AACf,QAAI,SAAS,IAAI,IAAI,GAAG;AACtB,aAAO;AAAA,QACL,OAAO;AAAA,QACP,SAAS;AAAA,QACT,SAAS,eAAe,IAAI;AAAA,MAC9B;AAAA,IACF;AACA,WAAO,EAAE,OAAO,IAAI,SAAS,MAAM,SAAS,KAAK;AAAA,EACnD;AACA,MAAI,SAAS,IAAI,IAAI,GAAG;AACtB,WAAO;AAAA,MACL,OAAO;AAAA,MACP,SAAS;AAAA,MACT,SAAS,eAAe,IAAI,0CAA0C,EAAE;AAAA,IAC1E;AAAA,EACF;AACA,QAAM,WAAW,KAAK,IAAI;AAC1B,QAAM,SAAS,KAAK,EAAE;AACtB,MAAI,SAAS,UAAU;AACrB,WAAO;AAAA,MACL,OAAO;AAAA,MACP,SAAS;AAAA,MACT,SAAS,gDAAgD,IAAI,SAAS,EAAE;AAAA,IAC1E;AAAA,EACF;AACA,SAAO,EAAE,OAAO,IAAI,SAAS,MAAM,SAAS,KAAK;AACnD;AAeO,SAAS,qBACd,WACkC;AAClC,UAAQ,WAAW;AAAA,IACjB,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AAIH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAGO,IAAM,wBAA2C;AAAA,EACtD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAoCO,SAAS,iBACd,UACA,QACoB;AACpB,WAAS,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG;AAChD,UAAM,UAAU,SAAS,CAAC;AAC1B,QAAI,CAAC,QAAS;AACd,UAAM,QAAQ,QAAQ,MAAM;AAAA,MAC1B,CAACA,UAASA,MAAK,SAAS,UAAUA,MAAK,eAAe,OAAO;AAAA,IAC/D;AACA,QAAI,UAAU,GAAI;AAClB,UAAM,OAAO,QAAQ,MAAM,KAAK;AAChC,UAAM,OAAsB,OAAO,SAC/B,kBACA,OAAO,aAAa,OAClB,iBACA;AACN,UAAM,aAAa,iBAAiB,KAAK,OAAO,IAAI;AACpD,QAAI,CAAC,WAAW,SAAS;AACvB,aAAO;AAAA,QACL;AAAA,QACA,WAAW,QAAQ;AAAA,QACnB,SAAS;AAAA,QACT,SAAS,WAAW;AAAA,MACtB;AAAA,IACF;AACA,UAAM,UAAwB;AAAA,MAC5B,GAAG;AAAA,MACH,OAAO,WAAW;AAAA,MAClB,QAAQ,OAAO,WAAW,SAAY,OAAO,SAAS,KAAK;AAAA,MAC3D,WAAW,SAAS,iBAAkB,OAAO,aAAa,OAAQ;AAAA,IACpE;AACA,UAAM,QAAQ,QAAQ,MAAM,MAAM;AAClC,UAAM,KAAK,IAAI;AACf,UAAM,OAAO,SAAS,MAAM;AAC5B,SAAK,CAAC,IAAI,EAAE,GAAG,SAAS,MAAM;AAC9B,WAAO,EAAE,UAAU,MAAM,WAAW,QAAQ,IAAI,SAAS,MAAM,SAAS,KAAK;AAAA,EAC/E;AACA,SAAO;AAAA,IACL;AAAA,IACA,WAAW;AAAA,IACX,SAAS;AAAA,IACT,SAAS,gBAAgB,OAAO,UAAU;AAAA,EAC5C;AACF;;;ACvIO,IAAM,yBAAiD,OAAO,OAAO;AAAA,EAC1E,SAAS;AAAA,EACT,SAAS;AAAA,EACT,wBAAwB;AAAA,EACxB,0BAA0B;AAAA,EAC1B,YAAY;AAAA,EACZ,mBAAmB;AAAA,EACnB,cAAc;AAAA,EACd,eAAe;AAAA,EACf,2BAA2B;AAAA,EAC3B,gBAAgB;AAAA,EAChB,oBAAoB,KAAK,OAAO;AAAA,EAChC,mBAAmB;AAAA,EACnB,SAAS;AACX,CAAC;AAYM,SAAS,oBAAoB,SAA2B,CAAC,GAAiB;AAC/E,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAG,eAAe,OAAO,YAAY;AAAA,IACrC,GAAG,eAAe,OAAO,OAAO;AAAA,IAChC,GAAG,eAAe,OAAO,IAAI;AAAA,EAC/B;AACF;AAEA,SAAS,eACP,OACuB;AACvB,MAAI,CAAC,MAAO,QAAO,CAAC;AACpB,QAAM,MAA+B,CAAC;AACtC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,QAAI,UAAU,OAAW,KAAI,GAAG,IAAI;AAAA,EACtC;AACA,SAAO;AACT;AAoBO,SAAS,cACd,QACA,QACqB;AACrB,MAAI,WAAW,gBAAgB,WAAW,SAAS;AACjD,WAAO;AAAA,MACL,SAAS;AAAA,MACT,MAAM;AAAA,MACN,aACE;AAAA,MACF,QACE;AAAA,IACJ;AAAA,EACF;AACA,SAAO,EAAE,SAAS,KAAK;AACzB;;;AC7HO,IAAM,iBAAiB;AACvB,IAAM,kBAAkB;AA+C/B,IAAM,WAAW,CAAC,UAChB,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IAC9D,QACD,CAAC;AAEP,IAAM,WAAW,CAAC,UAChB,OAAO,UAAU,WAAW,QAAQ;AAEtC,IAAM,WAAW,CAAC,UAChB,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,IAAI,QAAQ;AAGhE,IAAM,iBAAsC,oBAAI,IAAI;AAAA,EAClD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA;AAAA;AAAA,EAGA;AACF,CAAC;AAEM,SAAS,wBACd,SACmB;AACnB,QAAM,WAAW,QAAQ;AACzB,QAAM,YAAY,QAAQ,aAAa,kBAAkB,EAAE,MAAM,YAAY,CAAC,EAAE;AAChF,QAAM,QAAoB,CAAC;AAC3B,QAAM,gBAAgB,oBAAI,IAA0B;AACpD,QAAM,aAAa,oBAAI,IAAiC;AAGxD,MAAI,kBAAkB;AAEtB,MAAI,iBAAiB;AAErB,QAAM,QAAuB;AAAA,IAC3B,SAAS;AAAA,IACT,gBAAgB;AAAA,IAChB,WAAW;AAAA,IACX,OAAO;AAAA,IACP,kBAAkB,EAAE,MAAM,MAAM,WAAW,KAAK;AAAA,IAChD,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,UAAU;AAAA,IACV,kBAAkB;AAAA,IAClB,mBAAmB;AAAA,IACnB,eAAe,CAAC;AAAA,EAClB;AAEA,QAAM,SAAS,CAAC,YAAmC;AACjD,YAAQ,YAAY,OAAO;AAAA,EAC7B;AAEA,QAAM,WAAW,MAA4B,MAAM,MAAM,SAAS,CAAC;AAEnE,QAAM,aAAa,CAAC,SAAuB;AACzC,UAAM,OAAO,SAAS;AACtB,QAAI,QAAQ,KAAK,SAAS,QAAQ;AAChC,WAAK,QAAQ;AACb;AAAA,IACF;AACA,UAAM,OAAqB,EAAE,MAAM,QAAQ,KAAK;AAChD,UAAM,KAAK,IAAI;AAAA,EACjB;AAEA,QAAM,kBAAkB,CAAC,SAAuB;AAC9C,UAAM,OAAO,SAAS;AACtB,QAAI,QAAQ,KAAK,SAAS,eAAe,KAAK,UAAU,aAAa;AACnE,WAAK,QAAQ;AACb;AAAA,IACF;AACA,UAAM,OAA0B,EAAE,MAAM,aAAa,MAAM,OAAO,YAAY;AAC9E,UAAM,KAAK,IAAI;AAAA,EACjB;AAEA,QAAM,iBAAiB,MAAY;AACjC,aAAS,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG;AAC7C,YAAM,OAAO,MAAM,CAAC;AACpB,UAAI,QAAQ,KAAK,SAAS,eAAe,KAAK,UAAU,aAAa;AACnE,aAAK,QAAQ;AACb;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAUA,QAAM,aAAa,CAAC,SAAuB;AACzC,QAAI,SAAS,sBAAsB,UAAU;AAC3C,iBAAW,IAAI;AACf;AAAA,IACF;AACA,QAAI,OAAO;AACX,WAAO,KAAK,SAAS,GAAG;AACtB,UAAI,gBAAgB;AAClB,cAAM,QAAQ,KAAK,QAAQ,eAAe;AAC1C,YAAI,UAAU,IAAI;AAChB,0BAAgB,IAAI;AACpB;AAAA,QACF;AACA,wBAAgB,KAAK,MAAM,GAAG,KAAK,CAAC;AACpC,uBAAe;AACf,yBAAiB;AACjB,eAAO,KAAK,MAAM,QAAQ,gBAAgB,MAAM;AAChD;AAAA,MACF;AACA,YAAM,OAAO,KAAK,QAAQ,cAAc;AACxC,UAAI,SAAS,IAAI;AACf,mBAAW,IAAI;AACf;AAAA,MACF;AACA,UAAI,OAAO,EAAG,YAAW,KAAK,MAAM,GAAG,IAAI,CAAC;AAC5C,uBAAiB;AACjB,aAAO,KAAK,MAAM,OAAO,eAAe,MAAM;AAAA,IAChD;AAAA,EACF;AAEA,QAAM,UAAU,CAAC,QAAgB,aAAmC;AAClE,UAAM,WAAW,cAAc,IAAI,MAAM;AACzC,QAAI,SAAU,QAAO;AACrB,UAAM,OAAqB;AAAA,MACzB,MAAM;AAAA,MACN,YAAY;AAAA,MACZ;AAAA,MACA,OAAO;AAAA,MACP,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,UAAU,CAAC;AAAA,MACX,WAAW;AAAA,IACb;AACA,kBAAc,IAAI,QAAQ,IAAI;AAC9B,UAAM,KAAK,IAAI;AACf,WAAO;AAAA,EACT;AAEA,QAAM,qBAAqB,CACzB,WACA,SACS;AACT,UAAM,SAAS,SAAS,KAAK,OAAO;AACpC,UAAM,WAAW,SAAS,KAAK,SAAS;AACxC,QAAI,WAAW,QAAQ,aAAa,MAAM;AACxC,aAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAS,MAAM,SAAS;AAAA,QACxB,QAAQ;AAAA,MACV,CAAC;AACD;AAAA,IACF;AACA,UAAM,OAAO,QAAQ,QAAQ,QAAQ;AACrC,UAAM,UAAU,SAAS,KAAK,IAAI;AAClC,UAAM,UAAU,SAAS,KAAK,OAAO,KAAK;AAE1C,UAAM,OAAO,qBAAqB,SAAS;AAC3C,QAAI,SAAS,WAAW;AACtB,YAAM,cAAc,KAAK,SAAS;AAClC,aAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAS,mBAAmB,SAAS;AAAA,QACrC,QAAQ;AAAA,MACV,CAAC;AACD;AAAA,IACF;AAEA,QAAI,cAAc,kBAAkB,eAAe,SAAS;AAC1D,WAAK,QAAQ,QAAQ;AAAA,IACvB;AACA,QAAI,cAAc,kBAAkB;AAClC,WAAK,YAAY;AACjB,UAAI,eAAe,QAAS,MAAK,QAAQ,QAAQ;AAAA,IACnD;AACA,QAAI,cAAc,oBAAoB,YAAY,SAAS;AACzD,WAAK,SAAS,QAAQ;AAAA,IACxB;AACA,QAAI,cAAc,cAAc;AAC9B,WAAK,YACH,SAAS,QAAQ,KAAK,MAAM,WAAW,OAAO,QAAQ;AAAA,IAC1D;AAEA,QAAI,SAAS,MAAM;AACjB,WAAK,SAAS,KAAK;AAAA,QACjB;AAAA,QACA;AAAA,QACA,SAAS,SAAS,QAAQ,OAAO;AAAA,QACjC,IAAI,SAAS,KAAK,SAAS;AAAA,MAC7B,CAAC;AACD;AAAA,IACF;AAEA,UAAM,aAAa,iBAAiB,KAAK,OAAO,IAAI;AACpD,QAAI,CAAC,WAAW,SAAS;AACvB,aAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAS,GAAG,WAAW,WAAW,SAAS,UAAU,MAAM,UAAU,QAAQ;AAAA,QAC7E,QAAQ;AAAA,MACV,CAAC;AACD;AAAA,IACF;AACA,SAAK,QAAQ,WAAW;AACxB,QAAI,SAAS,qBAAqB,KAAK,WAAW;AAChD,YAAM,UAAU;AAAA,IAClB,WACE,MAAM,YAAY,qBACjB,SAAS,sBAAsB,SAAS,iBACzC;AACA,YAAM,UAAU;AAAA,IAClB;AAAA,EACF;AAEA,QAAM,aAAa,CACjB,OACA,SACS;AACT,UAAM,cAAc,SAAS,KAAK,YAAY,KAAK;AACnD,QAAI,CAAC,YAAa;AAClB,UAAM,OAAuB;AAAA,MAC3B,MAAM;AAAA,MACN;AAAA,MACA,MAAM,SAAS,KAAK,IAAI;AAAA,MACxB;AAAA,MACA,QAAQ,SAAS,SAAS,KAAK,QAAQ,EAAE,MAAM;AAAA,IACjD;AACA,UAAM,KAAK,IAAI;AAAA,EACjB;AAEA,QAAM,QAAQ,CAACC,WAAiC;AAC9C,UAAM,YAAY,SAASA,OAAM,UAAU;AAC3C,UAAM,WAAW,SAASA,OAAM,SAAS;AACzC,UAAM,cAAc,aAAa,QAAQ,aAAa,MAAM;AAC5D,QAAI,eAAe,cAAc,QAAQ,aAAa,MAAM,kBAAkB;AAG5E;AAAA,IACF;AACA,UAAM,mBACJ,cAAc,OACV,MAAM,mBACN,cACE,KAAK,IAAI,MAAM,kBAAkB,SAAS,IAC1C;AACR,QAAI,aAAa,KAAM,OAAM,oBAAoB;AACjD,UAAM,YAAY;AAElB,UAAM,OAAO,SAASA,OAAM,IAAI;AAEhC,YAAQA,OAAM,OAAO;AAAA,MACnB,KAAK,SAAS;AACZ,cAAM,OAAO,SAAS,KAAK,IAAI;AAC/B,YAAI,SAAS,KAAM,YAAW,IAAI;AAClC;AAAA,MACF;AAAA,MACA,KAAK,mBAAmB;AACtB,cAAM,OAAO,SAAS,KAAK,IAAI;AAC/B,YAAI,SAAS,MAAM;AACjB,4BAAkB;AAClB,0BAAgB,IAAI;AAAA,QACtB;AACA;AAAA,MACF;AAAA,MACA,KAAK,aAAa;AAChB,cAAM,UAAU,KAAK,UAAU;AAC/B,0BAAkB;AAClB,YAAI,CAAC,QAAS,gBAAe;AAC7B;AAAA,MACF;AAAA,MACA,KAAK,SAAS;AACZ,cAAM,QAAQ,SAAS,KAAK,KAAK;AACjC,YAAI,UAAU,KAAM,OAAM,QAAQ;AAClC;AAAA,MACF;AAAA,MACA,KAAK,QAAQ;AACX,YAAI,KAAK,SAAS,mBAAmB;AACnC,gBAAM,iBAAiB,SAAS,KAAK,eAAe;AACpD,cAAI,mBAAmB,KAAM,OAAM,iBAAiB;AAAA,QACtD;AACA;AAAA,MACF;AAAA,MACA,KAAK,mBAAmB;AAItB,YAAI,SAAS,KAAK,KAAK,MAAM,UAAW;AACxC,cAAM,WAAW,SAAS,KAAK,SAAS;AACxC,cAAM,OAAO,SAAS,SAAS,KAAK,QAAQ,EAAE,IAAI;AAClD,YAAI,aAAa,KAAM;AACvB,YAAI,SAAS,OAAQ,OAAM,iBAAiB,OAAO;AAAA,iBAC1C,SAAS,YAAa,OAAM,iBAAiB,YAAY;AAClE;AAAA,MACF;AAAA,MACA,KAAK,cAAc;AACjB,cAAM,YAAY,SAAS,KAAK,KAAK;AACrC,YAAI,cAAc,MAAM;AACtB,iBAAO;AAAA,YACL,MAAM;AAAA,YACN,SAAS;AAAA,YACT,QAAQ;AAAA,UACV,CAAC;AACD;AAAA,QACF;AACA,2BAAmB,WAAW,IAAI;AAClC;AAAA,MACF;AAAA,MACA,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,kBAAkB;AACrB,2BAAmBA,OAAM,OAAO,IAAI;AACpC;AAAA,MACF;AAAA,MACA,KAAK,gBAAgB;AACnB,cAAM,UAAU,SAAS,KAAK,OAAO;AACrC,cAAM,aAAa,SAAS,KAAK,UAAU;AAC3C,cAAM,OAAO,SAAS,KAAK,IAAI;AAC/B,YAAI,YAAY,QAAQ,eAAe,QAAQ,SAAS,MAAM;AAC5D,iBAAO;AAAA,YACL,MAAM;AAAA,YACN,SAAS;AAAA,YACT,QAAQ;AAAA,UACV,CAAC;AACD;AAAA,QACF;AACA,cAAM,SACJ,KAAK,WAAW,cAAc,KAAK,WAAW,UACzC,KAAK,SACN;AACN,cAAM,WAAW,WAAW,IAAI,OAAO;AACvC,YAAI,UAAU;AACZ,mBAAS,SAAS;AAClB,mBAAS,UAAU,SAAS,KAAK,OAAO;AACxC,mBAAS,OAAO,KAAK;AACrB;AAAA,QACF;AACA,cAAM,OAA4B;AAAA,UAChC,MAAM;AAAA,UACN;AAAA,UACA;AAAA,UACA;AAAA,UACA,SAAS,SAAS,KAAK,OAAO;AAAA,UAC9B,MAAM,KAAK;AAAA,UACX;AAAA,QACF;AACA,mBAAW,IAAI,SAAS,IAAI;AAC5B,cAAM,KAAK,IAAI;AACf;AAAA,MACF;AAAA,MACA,KAAK,WAAW;AACd,cAAM,QAAQ,SAAS,KAAK,KAAK;AACjC;AAAA,UACE,UAAU,UAAU,UAAU,YAAY,UAAU,QAAQ,QAAQ;AAAA,UACpE;AAAA,QACF;AACA;AAAA,MACF;AAAA,MACA,KAAK,QAAQ;AAEX,YAAI,SAAS,KAAK,IAAI,MAAM,kBAAmB;AAC/C,mBAAW,QAAQ,IAAI;AACvB;AAAA,MACF;AAAA,MACA,KAAK,SAAS;AACZ,cAAM,UACJ,SAAS,KAAK,OAAO,KAAK,SAAS,KAAK,YAAY,KAAK;AAC3D,cAAM,OAAsB;AAAA,UAC1B,MAAM;AAAA,UACN,WAAW,SAAS,KAAK,UAAU;AAAA,UACnC,MAAM,SAAS,KAAK,IAAI;AAAA,UACxB;AAAA,UACA,aAAa,SAAS,KAAK,YAAY,KAAK;AAAA,UAC5C,aAAa,KAAK,gBAAgB;AAAA,UAClC,QAAQ,SAAS,SAAS,KAAK,OAAO,EAAE,MAAM;AAAA,QAChD;AACA,cAAM,KAAK,IAAI;AACf,cAAM,QAAQ;AACd,cAAM,UAAU;AAChB;AAAA,MACF;AAAA,MACA,KAAK,cAAc;AACjB,YAAI,SAAS,KAAK,SAAS,MAAM,eAAgB;AACjD,cAAM,aAAa;AACnB,cAAM,SAAS,SAAS,KAAK,MAAM;AACnC,YAAI,WAAW,UAAU;AACvB,gBAAM,UAAU;AAChB,cAAI,MAAM,UAAU,MAAM;AACxB,kBAAM,SAAS,SAAS,KAAK,MAAM;AACnC,kBAAM,UACJ,SAAS,OAAO,KAAK,KACrB,SAAS,OAAO,YAAY,KAC5B;AACF,kBAAM,OAAsB;AAAA,cAC1B,MAAM;AAAA,cACN,WAAW;AAAA,cACX,MAAM;AAAA,cACN;AAAA,cACA,aAAa;AAAA,cACb,aAAa;AAAA,cACb,QAAQ;AAAA,YACV;AACA,kBAAM,KAAK,IAAI;AACf,kBAAM,QAAQ;AAAA,UAChB;AAAA,QACF,WAAW,WAAW,aAAa;AACjC,gBAAM,UAAU;AAAA,QAClB,WAAW,MAAM,YAAY,SAAS;AACpC,gBAAM,UAAU;AAAA,QAClB;AACA;AAAA,MACF;AAAA,MACA,KAAK,OAAO;AACV,YAAI,MAAM,YAAY,eAAe,MAAM,YAAY,kBAAkB;AACvE,gBAAM,UAAU;AAAA,QAClB;AACA,YAAI,kBAAkB,iBAAiB;AACrC,yBAAe;AACf,2BAAiB;AACjB,4BAAkB;AAAA,QACpB;AACA;AAAA,MACF;AAAA,MACA,SAAS;AACP,YAAI,eAAe,IAAIA,OAAM,KAAK,EAAG;AACrC,cAAM,cAAc,KAAKA,OAAM,KAAK;AACpC,eAAO;AAAA,UACL,MAAM;AAAA,UACN,SAAS,iBAAiBA,OAAM,KAAK;AAAA,UACrC,QAAQA,OAAM;AAAA,QAChB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WAAW,MAAmB;AAClC,UAAM,SACJ,MAAM,YAAY,UACd,UACA,MAAM,YAAY,cAChB,cACA,MAAM,YAAY,aAChB,aACA;AACV,UAAM,aAAa,MAAM;AACzB,WAAO;AAAA,MACL,IAAI;AAAA,MACJ,MAAM;AAAA;AAAA;AAAA,MAGN,OAAO,MAAM,IAAI,CAAC,UAAU,EAAE,GAAG,KAAK,EAAc;AAAA,MACpD;AAAA,MACA,UAAU;AAAA,QACR,WAAW,MAAM;AAAA,QACjB,iBAAiB,MAAM,iBAAiB;AAAA,QACxC,UAAU,aAAa,gBAAgB,UAAU,IAAI;AAAA,QACrD,YAAW,oBAAI,KAAK,CAAC,GAAE,YAAY;AAAA,QACnC,KAAK;AAAA,MACP;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,IAAI,QAAQ;AACV,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAQO,SAAS,gBACd,YACe;AACf,QAAM,SAAS,SAAS,WAAW,MAAM;AACzC,QAAM,QAAQ,SAAS,OAAO,WAAW;AACzC,QAAM,UAAU,SAAS,MAAM,QAAQ;AACvC,QAAM,QAAQ,OAAO,KAAK,OAAO;AACjC,SAAO,MAAM,SAAS,IAAK,MAAM,MAAM,SAAS,CAAC,KAAK,OAAQ;AAChE;;;ACraO,SAAS,yBAA4C;AAC1D,SAAO;AAAA,IACL,QAAQ,OAAO;AACb,cAAQ,MAAM,mBAAmB,MAAM,IAAI,KAAK,MAAM,OAAO,IAAI,MAAM,MAAM;AAAA,IAC/E;AAAA,EACF;AACF;AAOO,SAAS,wBAA0C;AACxD,SAAO;AAAA,IACL,OAAO,OAAO;AACZ,YAAM,OAAO,MAAM,SACf,mBAAmB,MAAM,OAAO,WAAM,MAAM,MAAM,KAClD,mBAAmB,MAAM,OAAO;AACpC,UAAI,MAAM,UAAU,QAAS,SAAQ,MAAM,IAAI;AAAA,eACtC,MAAM,UAAU,UAAW,SAAQ,KAAK,IAAI;AAAA,UAChD,SAAQ,KAAK,IAAI;AAAA,IACxB;AAAA,EACF;AACF;AAOO,SAAS,kBACd,WAA6B,sBAAsB,GACpC;AACf,QAAM,MAAM,oBAAI,IAAoB;AACpC,MAAI,YAAY;AAChB,QAAM,WAAW,MAAY;AAC3B,QAAI,UAAW;AACf,gBAAY;AACZ,aAAS,OAAO;AAAA,MACd,OAAO;AAAA,MACP,SACE;AAAA,MACF,QACE;AAAA,IACJ,CAAC;AAAA,EACH;AACA,SAAO;AAAA,IACL,KAAK,CAAC,QAAQ,IAAI,IAAI,GAAG,KAAK;AAAA,IAC9B,KAAK,CAAC,KAAK,UAAU;AACnB,eAAS;AACT,UAAI,IAAI,KAAK,KAAK;AAAA,IACpB;AAAA,IACA,QAAQ,CAAC,QAAQ;AACf,UAAI,OAAO,GAAG;AAAA,IAChB;AAAA,EACF;AACF;AAQO,SAAS,eACd,WAA6B,sBAAsB,GACpC;AACf,QAAM,WAAW,kBAAkB,QAAQ;AAC3C,MAAI,QAAwB;AAC5B,MAAI;AACF,YAAS,WAA0C,gBAAgB;AACnE,QAAI,OAAO;AACT,YAAM,QAAQ;AACd,YAAM,QAAQ,OAAO,GAAG;AACxB,YAAM,WAAW,KAAK;AAAA,IACxB;AAAA,EACF,QAAQ;AACN,YAAQ;AAAA,EACV;AACA,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,OAAO;AACb,SAAO;AAAA,IACL,KAAK,CAAC,QAAQ;AAKZ,UAAI;AACF,cAAM,SAAS,KAAK,QAAQ,GAAG;AAC/B,YAAI,WAAW,KAAM,QAAO;AAAA,MAC9B,QAAQ;AAAA,MAER;AACA,aAAO,SAAS,IAAI,GAAG;AAAA,IACzB;AAAA,IACA,KAAK,CAAC,KAAK,UAAU;AACnB,UAAI;AACF,aAAK,QAAQ,KAAK,KAAK;AAAA,MACzB,QAAQ;AACN,iBAAS,IAAI,KAAK,KAAK;AAAA,MACzB;AAAA,IACF;AAAA,IACA,QAAQ,CAAC,QAAQ;AACf,UAAI;AACF,aAAK,WAAW,GAAG;AAAA,MACrB,QAAQ;AAAA,MAER;AACA,eAAS,OAAO,GAAG;AAAA,IACrB;AAAA,EACF;AACF;AAGO,IAAM,aAAkC;AAAA,EAC7C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;ACjJA,SAAS,gBAAgB,YAAkD;AACzE,MAAI,OAAO,eAAe,UAAU;AAClC,WAAO,CAAC,QAAQ;AACd,YAAM,SAAS,WAAW,KAAK,UAAU;AACzC,aAAO,MAAM,aAAa,MAAM;AAAA,IAClC;AAAA,EACF;AACA,QAAM,MACJ,WAIA;AACF,MAAI,OAAO,QAAQ,YAAY;AAC7B,UAAM,SAAU,WACb;AACH,WAAO,CAAC,QAAQ;AACd,YAAM,SAAS,IAAI,GAAG;AACtB,aAAO,MAAM,SAAS,MAAM;AAAA,IAC9B;AAAA,EACF;AACA,SAAO,CAAC,QAAQ;AACd,QAAI,YAAY;AAChB,mBAAe,MAAM;AACnB,UAAI,CAAC,UAAW,KAAI;AAAA,IACtB,CAAC;AACD,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF;AACF;AAEA,IAAM,mBACJ;AACF,IAAM,mBACJ;AAEK,SAAS,iBAAiB,QAA2C;AAC1E,QAAM,WAAW,oBAAoB,OAAO,QAAQ;AACpD,QAAM,YAAY,OAAO,aAAa,uBAAuB;AAC7D,QAAM,WAAW,OAAO,YAAY,sBAAsB;AAC1D,QAAM,WAAW,gBAAgB,OAAO,UAAU;AAElD,MAAI,WAA0B,CAAC;AAC/B,MAAI,SAA2B;AAC/B,MAAI,iBAAgC,OAAO,kBAAkB;AAC7D,MAAI,YAA2B;AAC/B,MAAI,QAAuB;AAC3B,MAAI,QAA8B;AAClC,MAAI,WAAW;AACf,MAAI,WAA+B,cAAc;AACjD,MAAI,QAAgC;AACpC,MAAI,WAAW;AACf,MAAI,qBAAqB;AAezB,MAAI,QAAQ;AAEZ,QAAM,kBAAkB,oBAAI,IAAgB;AAC5C,QAAM,mBAAmB,oBAAI,IAA6B;AAC1D,QAAM,gBAAgB,oBAAI,IAAY;AACtC,MAAI,kBAAuC;AAE3C,WAAS,gBAAoC;AAC3C,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW,OAAO,gBAAgB;AAAA,MAClC,iBACE,OAAO,gBAAgB,SACnB,GAAG,gBAAgB,IAAI,gBAAgB,KACvC;AAAA,MACN;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,WAAS,SAAe;AACtB,gBAAY;AACZ,eAAW,cAAc;AACzB,UAAM,QAAQ,CAAC,GAAG,aAAa;AAC/B,kBAAc,MAAM;AACpB,eAAW,MAAM,OAAO;AACtB,YAAM,YAAY,iBAAiB,IAAI,EAAE;AACzC,UAAI,CAAC,UAAW;AAChB,iBAAW,YAAY,UAAW,UAAS;AAAA,IAC7C;AACA,eAAW,YAAY,gBAAiB,UAAS;AAAA,EACnD;AAEA,WAAS,UAAU,WAAgC;AACjD,QAAI,cAAc,KAAM,eAAc,IAAI,SAAS;AACnD,QAAI,oBAAoB,KAAM;AAC9B,sBAAkB,SAAS,MAAM;AAC/B,wBAAkB;AAClB,UAAI,CAAC,SAAU,QAAO;AAAA,IACxB,CAAC;AAAA,EACH;AAGA,WAAS,UAAU,WAAgC;AACjD,QAAI,cAAc,KAAM,eAAc,IAAI,SAAS;AACnD,sBAAkB;AAClB,sBAAkB;AAClB,WAAO;AAAA,EACT;AAEA,WAAS,wBAA8B;AACrC,QAAI,sBAAsB,OAAO,gBAAgB,OAAW;AAC5D,yBAAqB;AACrB,aAAS,OAAO;AAAA,MACd,OAAO;AAAA,MACP,SAAS;AAAA,MACT,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAEA,WAAS,eAAe,MAAyB;AAC/C,UAAM,QAAQ,SAAS,UAAU,CAAC,MAAM,EAAE,OAAO,KAAK,EAAE;AACxD,QAAI,UAAU,IAAI;AAChB,iBAAW,CAAC,GAAG,UAAU,IAAI;AAC7B;AAAA,IACF;AACA,UAAM,OAAO,SAAS,MAAM;AAC5B,SAAK,KAAK,IAAI;AACd,eAAW;AAAA,EACb;AAEA,WAAS,WAAW,MAAqC;AACvD,YAAQ;AACR,aAAS;AACT,cAAU,QAAQ,EAAE,MAAM,KAAK,QAAQ,qBAAqB,SAAS,KAAK,QAAQ,CAAC;AACnF,aAAS,OAAO;AAAA,MACd,OAAO;AAAA,MACP,SAAS,KAAK;AAAA,MACd,GAAI,KAAK,WAAW,OAAO,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO;AAAA,IACxD,CAAC;AACD,cAAU,IAAI;AACd,WAAO,EAAE,UAAU,OAAO,SAAS,KAAK;AAAA,EAC1C;AAEA,iBAAe,KAAK,OAA+C;AACjE,QAAI,UAAU;AACZ,aAAO,WAAW;AAAA,QAChB,MAAM;AAAA,QACN,WAAW;AAAA,QACX,MAAM;AAAA,QACN,SAAS;AAAA,QACT,aAAa;AAAA,QACb,aAAa;AAAA,QACb,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AACA,QAAI,WAAW,eAAe,WAAW,aAAa;AACpD,aAAO,WAAW;AAAA,QAChB,MAAM;AAAA,QACN,WAAW;AAAA,QACX,MAAM;AAAA,QACN,SAAS;AAAA,QACT,aAAa;AAAA,QACb,aAAa;AAAA,QACb,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAGA,UAAM,UAAU,cAAc,SAAS,SAAS,OAAO,UAAU,QAAQ;AACzE,QAAI,CAAC,QAAQ,SAAS;AACpB,aAAO,WAAW;AAAA,QAChB,MAAM;AAAA,QACN,WAAW;AAAA,QACX,MAAM,QAAQ;AAAA,QACd,SAAS,QAAQ;AAAA,QACjB,aAAa,QAAQ;AAAA,QACrB,aAAa;AAAA,QACb,QAAQ,QAAQ;AAAA,MAClB,CAAC;AAAA,IACH;AAEA,0BAAsB;AAEtB,UAAM,UAAU,MAAM,WAAW,CAAC;AAClC,UAAM,cAAc,kBAAkB;AAAA,MACpC,MAAM;AAAA,MACN,OAAO,CAAC,EAAE,MAAM,QAAQ,MAAM,MAAM,KAAK,CAAC;AAAA,MAC1C,QAAQ;AAAA,IACV,CAAC;AACD,UAAM,cAAc,WAAW,KAAK;AACpC,UAAM,uBAAuB,kBAAkB;AAAA,MAC7C,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,OAAO,CAAC;AAAA,MACR,QAAQ;AAAA,IACV,CAAC;AACD,eAAW,CAAC,GAAG,UAAU,aAAa,oBAAoB;AAC1D,YAAQ;AACR,aAAS;AACT,cAAU,WAAW;AAErB,UAAM,YAAY;AAElB,UAAM,QAAQ,MAAe,YAAY,cAAc;AAEvD,UAAM,aAAa,IAAI,gBAAgB;AACvC,YAAQ;AAER,UAAM,YAAY,wBAAwB;AAAA,MACxC;AAAA,MACA,WAAW;AAAA,MACX,WAAW,CAAC,YACV,UAAU,QAAQ;AAAA,QAChB,MAAM,QAAQ;AAAA,QACd,SAAS,QAAQ;AAAA,QACjB,QAAQ,QAAQ;AAAA,MAClB,CAAC;AAAA,IACL,CAAC;AAED,QAAI;AACF,YAAM,SAAS,MAAM,OAAO,UAAU,MAAM;AAAA,QAC1C;AAAA,QACA,mBAAmB,mBAAmB;AAAA,QACtC,OAAO,OAAO,gBAAgB;AAAA,QAC9B,MAAM,MAAM;AAAA,QACZ;AAAA,QACA,QAAQ,WAAW;AAAA,MACrB,CAAC;AACD,UAAI,MAAM,EAAG,QAAO,EAAE,UAAU,MAAM,SAAS,KAAK;AACpD,kBAAY,OAAO;AACnB,UAAI,OAAO,mBAAmB,KAAM,kBAAiB,OAAO;AAC5D,eAAS;AACT,gBAAU,WAAW;AAErB,UAAI,MAAM,EAAG,QAAO,EAAE,UAAU,MAAM,SAAS,KAAK;AACpD,uBAAiBC,UAAS,OAAO,QAAQ;AAGvC,YAAI,MAAM,EAAG,QAAO,EAAE,UAAU,MAAM,SAAS,KAAK;AACpD,kBAAU,MAAMA,MAAK;AACrB,YAAI,UAAU,MAAM,mBAAmB,MAAM;AAC3C,2BAAiB,UAAU,MAAM;AAAA,QACnC;AACA,YAAI,UAAU,MAAM,UAAU,KAAM,SAAQ,UAAU,MAAM;AAC5D,cAAM,OAAO,UAAU,SAAS;AAChC,uBAAe;AAAA,UACb,GAAG;AAAA,UACH,UAAU;AAAA,YACR,GAAG,KAAK;AAAA,YACR;AAAA,YACA,WAAW,qBAAqB,SAAS;AAAA,UAC3C;AAAA,QACF,CAAC;AACD,iBACE,UAAU,MAAM,YAAY,mBACxB,sBACA;AACN,kBAAU,WAAW;AAAA,MACvB;AAEA,UAAI,MAAM,EAAG,QAAO,EAAE,UAAU,MAAM,SAAS,KAAK;AACpD,YAAM,eAAe,UAAU,MAAM,iBAAiB;AACtD,UAAI,iBAAiB,MAAM;AACzB,uBAAe;AAAA,UACb,GAAG;AAAA,UACH,UAAU,EAAE,GAAG,YAAY,UAAU,iBAAiB,aAAa;AAAA,QACrE,CAAC;AAAA,MACH;AACA,YAAM,QAAQ,UAAU,SAAS;AACjC,qBAAe;AAAA,QACb,GAAG;AAAA,QACH,UAAU;AAAA,UACR,GAAG,MAAM;AAAA,UACT;AAAA,UACA,WAAW,qBAAqB,SAAS;AAAA,QAC3C;AAAA,MACF,CAAC;AACD,cAAQ,UAAU,MAAM;AACxB,eAAS,UAAU,MAAM,YAAY,UAAU,UAAU;AACzD,gBAAU,WAAW;AACrB,aAAO,EAAE,UAAU,MAAM,SAAS,KAAK;AAAA,IACzC,SAAS,OAAO;AACd,UAAI,MAAM,EAAG,QAAO,EAAE,UAAU,MAAM,SAAS,KAAK;AACpD,YAAM,UACJ,WAAW,OAAO,WACjB,iBAAiB,SAAS,MAAM,SAAS;AAC5C,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,YAAM,OAAsB,UACxB;AAAA,QACE,MAAM;AAAA,QACN,WAAW;AAAA,QACX,MAAM;AAAA,QACN,SAAS;AAAA,QACT,aAAa;AAAA,QACb,aAAa;AAAA,QACb,QAAQ;AAAA,MACV,IACA;AAAA,QACE,MAAM;AAAA,QACN,WAAW;AAAA,QACX,MAAM;AAAA,QACN;AAAA,QACA,aAAa;AAAA,QACb,aAAa;AAAA,QACb,QAAQ;AAAA,MACV;AACJ,YAAM,UAAU,UAAU,SAAS;AACnC,qBAAe;AAAA,QACb,GAAG;AAAA;AAAA,QAEH,OAAO,CAAC,GAAG,QAAQ,OAAO,IAAI;AAAA,QAC9B,QAAQ,UAAU,cAAc;AAAA,QAChC,UAAU;AAAA,UACR,GAAG,QAAQ;AAAA,UACX;AAAA,UACA,WAAW,qBAAqB,SAAS;AAAA,QAC3C;AAAA,MACF,CAAC;AACD,UAAI,CAAC,SAAS;AACZ,gBAAQ;AACR,iBAAS;AACT,kBAAU,QAAQ,EAAE,MAAM,KAAK,QAAQ,oBAAoB,QAAQ,CAAC;AAAA,MACtE,OAAO;AACL,iBAAS;AAAA,MACX;AACA,gBAAU,WAAW;AACrB,aAAO,EAAE,UAAU,MAAM,SAAS,KAAK;AAAA,IACzC,UAAE;AACA,UAAI,UAAU,WAAY,SAAQ;AAAA,IACpC;AAAA,EACF;AAEA,iBAAe,SAAwB;AACrC,UAAM,UAAU;AAChB,WAAO,MAAM;AACb,QAAI,YAAY,KAAM;AACtB,QAAI;AACF,YAAM,OAAO,UAAU,OAAO,OAAO;AAAA,IACvC,SAAS,OAAO;AACd,gBAAU,QAAQ;AAAA,QAChB,MAAM;AAAA,QACN,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,MAChE,CAAC;AACD,eAAS,OAAO;AAAA,QACd,OAAO;AAAA,QACP,SAAS;AAAA,QACT,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAAA,EACF;AAEA,iBAAe,iBAAiB,IAA2B;AACzD,UAAM,cAAc,OAAO;AAC3B,QAAI,gBAAgB,QAAW;AAC7B,YAAM,OAAsB;AAAA,QAC1B,MAAM;AAAA,QACN,WAAW;AAAA,QACX,MAAM;AAAA,QACN,SAAS;AAAA,QACT,aAAa;AAAA,QACb,aAAa;AAAA,QACb,QAAQ;AAAA,MACV;AACA,iBAAW,IAAI;AACf;AAAA,IACF;AACA,aAAS;AACT,UAAM,SAAS,MAAM,YAAY,WAAW,EAAE;AAC9C,qBAAiB;AACjB,eAAW,OAAO,MAAM;AACxB,aAAS;AACT,YAAQ;AACR,cAAU,IAAI;AAAA,EAChB;AAUA,iBAAe,uBAAsC;AACnD,QAAI,SAAU;AACd,aAAS;AACT,QAAI,WAAW,eAAe,WAAW,YAAa,OAAM,OAAO;AACnE,YAAQ;AACR,qBAAiB;AACjB,gBAAY;AACZ,YAAQ;AACR,eAAW,CAAC;AACZ,YAAQ;AACR,aAAS;AACT,cAAU,IAAI;AAAA,EAChB;AAEA,SAAO;AAAA,IACL,aAAa,MAAM;AAAA,IACnB,UAAU,UAAU;AAClB,sBAAgB,IAAI,QAAQ;AAC5B,aAAO,MAAM;AACX,wBAAgB,OAAO,QAAQ;AAAA,MACjC;AAAA,IACF;AAAA,IACA,mBAAmB,WAAW,UAAU;AACtC,YAAM,WAAW,iBAAiB,IAAI,SAAS,KAAK,oBAAI,IAAgB;AACxE,eAAS,IAAI,QAAQ;AACrB,uBAAiB,IAAI,WAAW,QAAQ;AACxC,aAAO,MAAM;AACX,iBAAS,OAAO,QAAQ;AACxB,YAAI,SAAS,SAAS,EAAG,kBAAiB,OAAO,SAAS;AAAA,MAC5D;AAAA,IACF;AAAA;AAAA;AAAA,IAGA,oBAAoB,CAAC,cACnB,SAAS,SAAS,KAAK,CAAC,MAAM,EAAE,OAAO,SAAS;AAAA,IAClD;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY,MAAM;AAChB,iBAAW,KAAK,MAAM;AACtB,gBAAU,IAAI;AAAA,IAChB;AAAA,IACA,eAAe,QAAQ;AACrB,YAAM,aAAa,iBAAiB,UAAU,MAAM;AACpD,UAAI,CAAC,WAAW,SAAS;AACvB,YAAI,WAAW,YAAY,MAAM;AAC/B,oBAAU,QAAQ;AAAA,YAChB,MAAM;AAAA,YACN,SAAS,WAAW;AAAA,YACpB,QAAQ;AAAA,UACV,CAAC;AAAA,QACH;AACA,eAAO;AAAA,MACT;AACA,iBAAW,WAAW,SAAS,MAAM;AACrC,gBAAU,WAAW,SAAS;AAC9B,aAAO;AAAA,IACT;AAAA,IACA;AAAA,IACA,yBAAyB;AAAA,IACzB;AAAA,IACA,UAAU;AACR,iBAAW;AACX,wBAAkB;AAClB,wBAAkB;AAClB,aAAO,MAAM;AACb,sBAAgB,MAAM;AACtB,uBAAiB,MAAM;AAAA,IACzB;AAAA,EACF;AACF;;;AC5jBA;AAAA,EACE;AAAA,EACA;AAAA,EACA,aAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;;;ACPP,SAAS,iBAAiC;AAC1C,SAAS,oBAAoB,8BAA8B;;;ACM3D,SAAS,gBAAgC;AACzC,SAAS,cAAc,UAAU,eAAe,yBAAyB;AA+FxD,cAmVP,YAnVO;AAvFjB,IAAM,cAAc;AAEpB,IAAM,kBAAkB;AAEjB,SAAS,SAAS,KAA4B;AACnD,QAAM,OAAO,IAAI,KAAK;AACtB,MAAI,SAAS,GAAI,QAAO;AACxB,MAAI,YAAY,KAAK,IAAI,EAAG,QAAO;AAEnC,MAAI,4BAA4B,KAAK,IAAI,EAAG,QAAO;AACnD,MAAI,gBAAgB,KAAK,IAAI,EAAG,QAAO;AACvC,SAAO;AACT;AAGA,SAAS,gBAAgB,KAA4B;AACnD,QAAM,SAAS,IAAI,KAAK;AACxB,SAAO,gBAAgB,KAAK,MAAM,IAAI,SAAS;AACjD;AAwBA,IAAM,kBAAkB;AAWxB,IAAM,gBAAgB;AAAA,EACpB;AAAA;AAAA,EACE;AAAA;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAKA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AACJ,EAAE,KAAK,GAAG;AAEH,SAAS,aACd,MACA,WACA,QAAQ,GACK;AAIb,MAAI,QAAQ,KAAK,KAAK,SAAS,kBAAkB,GAAI,QAAO,CAAC,IAAI;AACjE,QAAM,QAAqB,CAAC;AAC5B,QAAM,UAAU,IAAI,OAAO,eAAe,GAAG;AAC7C,MAAI,SAAS;AACb,MAAI,QAAQ;AACZ,MAAI,QAAQ,QAAQ,KAAK,IAAI;AAC7B,SAAO,UAAU,MAAM;AACrB,QAAI,MAAM,QAAQ,OAAQ,OAAM,KAAK,KAAK,MAAM,QAAQ,MAAM,KAAK,CAAC;AACpE,UAAM,MAAM,GAAG,SAAS,IAAI,KAAK;AACjC,aAAS;AAET,QAAI,MAAM,CAAC,MAAM,QAAW;AAC1B,YAAM,KAAK,oBAAC,UAAgB,gBAAM,CAAC,KAAK,MAAlB,GAAqB,CAAO;AAAA,IACpD,WAAW,MAAM,CAAC,MAAM,QAAW;AACjC,YAAM,SAAS,gBAAgB,MAAM,CAAC,CAAC;AACvC,YAAM,MAAM,MAAM,CAAC,KAAK;AACxB,YAAM;AAAA,QACJ,WAAW,OACT,oBAAC,YAAoB,kBAAQ,KAAK,MAAM,CAAC,IAAI,OAA9B,GAAkC,IAEjD;AAAA,UAAC;AAAA;AAAA,YAEC,WAAU;AAAA,YACV,KAAK;AAAA,YACL;AAAA,YACA,SAAQ;AAAA,YACR,gBAAe;AAAA;AAAA,UALV;AAAA,QAMP;AAAA,MAEJ;AAAA,IACF,WAAW,MAAM,CAAC,MAAM,QAAW;AACjC,YAAM,OAAO,SAAS,MAAM,CAAC,CAAC;AAC9B,YAAM,QAAQ,MAAM,CAAC,KAAK;AAC1B,YAAM;AAAA,QACJ,SAAS;AAAA;AAAA,UAEP,oBAAC,YAAoB,mBAAN,GAAY;AAAA,YAE3B,oBAAC,OAAY,MAAY,QAAO,UAAS,KAAI,uBAC1C,uBAAa,OAAO,GAAG,GAAG,KAAK,QAAQ,CAAC,KADnC,GAER;AAAA,MAEJ;AAAA,IACF,WAAW,MAAM,CAAC,MAAM,QAAW;AACjC,YAAM,KAAK,oBAAC,YAAkB,uBAAa,MAAM,CAAC,GAAG,GAAG,GAAG,KAAK,QAAQ,CAAC,KAAjD,GAAmD,CAAS;AAAA,IACtF,WAAW,MAAM,EAAE,MAAM,QAAW;AAClC,YAAM,KAAK,oBAAC,QAAc,uBAAa,MAAM,EAAE,GAAG,GAAG,GAAG,KAAK,QAAQ,CAAC,KAAlD,GAAoD,CAAK;AAAA,IAC/E,WAAW,MAAM,EAAE,MAAM,QAAW;AAClC,YAAM,KAAK,oBAAC,SAAe,uBAAa,MAAM,EAAE,GAAG,GAAG,GAAG,KAAK,QAAQ,CAAC,KAAlD,GAAoD,CAAM;AAAA,IACjF,WAAW,MAAM,EAAE,MAAM,QAAW;AAClC,YAAM;AAAA,QACJ,oBAAC,OAAY,MAAM,MAAM,EAAE,GAAG,QAAO,UAAS,KAAI,uBAC/C,gBAAM,EAAE,KADH,GAER;AAAA,MACF;AAAA,IACF;AACA,aAAS,MAAM,QAAQ,MAAM,CAAC,EAAE;AAChC,YAAQ,QAAQ,KAAK,IAAI;AAAA,EAC3B;AACA,MAAI,SAAS,KAAK,OAAQ,OAAM,KAAK,KAAK,MAAM,MAAM,CAAC;AACvD,SAAO;AACT;AAaA,IAAM,aAAa;AACnB,IAAM,eAAe;AACrB,IAAM,cAAc;AACpB,IAAM,eAAe;AACrB,IAAM,aAAa;AACnB,IAAM,YAAY;AAElB,SAAS,WAAW,MAAwB;AAC1C,SAAO,SAAS,IAAI,EAAE,IAAI,iBAAiB;AAC7C;AAOO,SAAS,YAAY,QAK1B;AACA,QAAM,QAAQ,OAAO,MAAM,IAAI;AAI/B,QAAM,YAAsB,CAAC;AAC7B;AACE,QAAI,KAAK;AACT,eAAW,QAAQ,OAAO;AACxB,gBAAU,KAAK,EAAE;AACjB,YAAM,KAAK,SAAS;AAAA,IACtB;AAAA,EACF;AACA,QAAM,SAA0B,CAAC;AACjC,MAAI,QAAQ;AACZ,MAAI,YAAY;AAEhB,SAAO,QAAQ,MAAM,QAAQ;AAC3B,UAAM,OAAO,MAAM,KAAK,KAAK;AAE7B,QAAI,KAAK,KAAK,MAAM,IAAI;AACtB,eAAS;AACT;AAAA,IACF;AAIA,gBAAY;AAEZ,UAAM,QAAQ,WAAW,KAAK,IAAI;AAClC,QAAI,UAAU,MAAM;AAClB,YAAM,SAAS,MAAM,CAAC,KAAK;AAC3B,YAAM,WAAW,MAAM,CAAC,KAAK;AAC7B,YAAM,OAAiB,CAAC;AACxB,eAAS;AACT,UAAI,SAAS;AACb,aAAO,QAAQ,MAAM,QAAQ;AAC3B,cAAM,UAAU,MAAM,KAAK,KAAK;AAChC,YAAI,QAAQ,UAAU,EAAE,WAAW,OAAO,CAAC,KAAK,GAAG,KAAK,QAAQ,KAAK,EAAE,UAAU,GAAG;AAClF,gBAAM,QAAQ,WAAW,KAAK,OAAO;AACrC,cAAI,UAAU,SAAS,MAAM,CAAC,KAAK,IAAI,UAAU,OAAO,QAAQ;AAC9D,qBAAS;AACT,qBAAS;AACT;AAAA,UACF;AAAA,QACF;AACA,aAAK,KAAK,OAAO;AACjB,iBAAS;AAAA,MACX;AAIA,aAAO,KAAK,EAAE,MAAM,QAAQ,UAAU,MAAM,KAAK,KAAK,IAAI,GAAG,OAAO,CAAC;AACrE;AAAA,IACF;AAEA,UAAM,UAAU,aAAa,KAAK,IAAI;AACtC,QAAI,YAAY,MAAM;AACpB,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,QAAQ,QAAQ,CAAC,KAAK,KAAK;AAAA,QAC3B,MAAM,QAAQ,CAAC,KAAK;AAAA,MACtB,CAAC;AACD,eAAS;AACT;AAAA,IACF;AAEA,QAAI,UAAU,KAAK,IAAI,GAAG;AACxB,aAAO,KAAK,EAAE,MAAM,OAAO,CAAC;AAC5B,eAAS;AACT;AAAA,IACF;AAEA,QAAI,WAAW,KAAK,IAAI,GAAG;AACzB,YAAM,OAAiB,CAAC;AACxB,aAAO,QAAQ,MAAM,QAAQ;AAC3B,cAAM,QAAQ,WAAW,KAAK,MAAM,KAAK,KAAK,EAAE;AAChD,YAAI,UAAU,KAAM;AACpB,aAAK,KAAK,MAAM,CAAC,KAAK,EAAE;AACxB,iBAAS;AAAA,MACX;AACA,aAAO,KAAK,EAAE,MAAM,SAAS,MAAM,KAAK,KAAK,IAAI,EAAE,CAAC;AACpD;AAAA,IACF;AAEA,UAAM,SAAS,YAAY,KAAK,IAAI;AACpC,UAAM,UAAU,aAAa,KAAK,IAAI;AACtC,QAAI,WAAW,QAAQ,YAAY,MAAM;AACvC,YAAM,YAAY,YAAY;AAC9B,YAAM,QAAkB,CAAC;AACzB,aAAO,QAAQ,MAAM,QAAQ;AAC3B,cAAM,UAAU,MAAM,KAAK,KAAK;AAChC,cAAM,QAAQ,YACV,aAAa,KAAK,OAAO,IACzB,YAAY,KAAK,OAAO;AAC5B,YAAI,UAAU,KAAM;AACpB,cAAM,KAAK,MAAM,CAAC,KAAK,EAAE;AACzB,iBAAS;AAAA,MACX;AACA,aAAO,KAAK,EAAE,MAAM,QAAQ,SAAS,WAAW,MAAM,CAAC;AACvD;AAAA,IACF;AAIA,QAAI,cAAc,OAAO,KAAK,GAAG;AAC/B,YAAM,SAAS,WAAW,IAAI;AAC9B,YAAM,MAAM,aAAa,OAAO,KAAK;AACrC,eAAS;AACT,YAAM,OAAmB,CAAC;AAC1B,aAAO,QAAQ,KAAK;AAClB,cAAM,QAAQ,WAAW,MAAM,KAAK,KAAK,EAAE;AAC3C,aAAK,KAAK,OAAO,IAAI,CAAC,IAAI,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;AAC/C,iBAAS;AAAA,MACX;AACA,aAAO,KAAK,EAAE,MAAM,SAAS,QAAQ,KAAK,CAAC;AAC3C;AAAA,IACF;AAEA,UAAM,YAAsB,CAAC;AAC7B,WAAO,QAAQ,MAAM,QAAQ;AAC3B,YAAM,UAAU,MAAM,KAAK,KAAK;AAChC,UACE,QAAQ,KAAK,MAAM,MACnB,WAAW,KAAK,OAAO,KACvB,aAAa,KAAK,OAAO,KACzB,YAAY,KAAK,OAAO,KACxB,aAAa,KAAK,OAAO,KACzB,WAAW,KAAK,OAAO,KACvB,UAAU,KAAK,OAAO,GACtB;AACA;AAAA,MACF;AACA,gBAAU,KAAK,OAAO;AACtB,eAAS;AAAA,IACX;AACA,WAAO,KAAK,EAAE,MAAM,aAAa,MAAM,UAAU,KAAK,IAAI,EAAE,CAAC;AAAA,EAC/D;AAEA,SAAO,EAAE,QAAQ,UAAU,UAAU,SAAS,KAAK,EAAE;AACvD;AAmBO,SAAS,mBAAuC;AASrD,QAAM,UAAU,oBAAI,IAAmB;AACvC,MAAI,SAAS;AAEb,QAAM,OAAO,CAAC,KAAa,SAAkC;AAC3D,cAAU;AACV,UAAM,SAAS,YAAY,IAAI;AAC/B,YAAQ,IAAI,KAAK;AAAA,MACf;AAAA,MACA,QAAQ,OAAO;AAAA,MACf,QAAQ,OAAO,OAAO,MAAM,GAAG,KAAK,IAAI,GAAG,OAAO,OAAO,SAAS,CAAC,CAAC;AAAA,MACpE,WAAW,OAAO;AAAA,IACpB,CAAC;AACD,WAAO,OAAO;AAAA,EAChB;AAEA,SAAO;AAAA,IACL,IAAI,SAAS;AACX,aAAO;AAAA,IACT;AAAA,IACA,OAAO,KAAK;AACV,cAAQ,OAAO,GAAG;AAAA,IACpB;AAAA,IACA,MAAM,KAAK,MAAM;AACf,YAAM,WAAW,QAAQ,IAAI,GAAG;AAChC,UAAI,aAAa,OAAW,QAAO,KAAK,KAAK,IAAI;AACjD,UAAI,SAAS,SAAS,KAAM,QAAO,SAAS;AAK5C,UAAI,CAAC,KAAK,WAAW,SAAS,IAAI,EAAG,QAAO,KAAK,KAAK,IAAI;AAE1D,gBAAU;AACV,YAAM,OAAO,YAAY,KAAK,MAAM,SAAS,SAAS,CAAC;AACvD,YAAM,SAAS,CAAC,GAAG,SAAS,QAAQ,GAAG,KAAK,MAAM;AAClD,cAAQ,IAAI,KAAK;AAAA,QACf;AAAA,QACA;AAAA,QACA,QAAQ,OAAO,MAAM,GAAG,KAAK,IAAI,GAAG,OAAO,SAAS,CAAC,CAAC;AAAA,QACtD,WAAW,SAAS,YAAY,KAAK;AAAA,MACvC,CAAC;AACD,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAIA,SAAS,YAAY,OAAsB,KAAwB;AACjE,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK;AACH,aACE,oBAAC,SAAc,WAAU,mBAAkB,iBAAe,MAAM,YAAY,QAC1E,8BAAC,UAAM,gBAAM,MAAK,KADV,GAEV;AAAA,IAEJ,KAAK,WAAW;AACd,YAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,MAAM,KAAK,CAAC;AAClD,YAAM,MAAM,IAAI,KAAK;AACrB,aACE,oBAAC,OAAc,WAAU,uBAAsB,cAAY,OACxD,uBAAa,MAAM,MAAM,GAAG,GAAG,GAAG,KAD3B,GAEV;AAAA,IAEJ;AAAA,IACA,KAAK;AACH,aACE,oBAAC,gBAAqB,WAAU,qBAC7B,uBAAa,MAAM,MAAM,GAAG,GAAG,GAAG,KADpB,GAEjB;AAAA,IAEJ,KAAK;AACH,aAAO,MAAM,UACX,oBAAC,QAAa,WAAU,oBACrB,gBAAM,MAAM,IAAI,CAAC,MAAM,MACtB,oBAAC,QAAwB,uBAAa,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,KAAjD,GAAG,GAAG,IAAI,CAAC,EAAwC,CAC7D,KAHM,GAIT,IAEA,oBAAC,QAAa,WAAU,oBACrB,gBAAM,MAAM,IAAI,CAAC,MAAM,MACtB,oBAAC,QAAwB,uBAAa,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,KAAjD,GAAG,GAAG,IAAI,CAAC,EAAwC,CAC7D,KAHM,GAIT;AAAA,IAEJ,KAAK;AACH,aACE,oBAAC,SAAc,WAAU,4BACvB,+BAAC,WAAM,WAAU,qBACf;AAAA,4BAAC,WACC,8BAAC,QACE,gBAAM,OAAO,IAAI,CAAC,MAAM,MACvB,oBAAC,QAAyB,uBAAa,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,KAAnD,GAAG,GAAG,KAAK,CAAC,EAAyC,CAC/D,GACH,GACF;AAAA,QACA,oBAAC,WACE,gBAAM,KAAK,IAAI,CAAC,KAAK,MACpB,oBAAC,QACE,cAAI,IAAI,CAAC,MAAM,MACd,oBAAC,QAA8B,uBAAa,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,KAA7D,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,EAA8C,CACzE,KAHM,GAAG,GAAG,KAAK,CAAC,EAIrB,CACD,GACH;AAAA,SACF,KAlBQ,GAmBV;AAAA,IAEJ,KAAK;AACH,aAAO,oBAAC,QAAa,WAAU,sBAAf,GAAkC;AAAA,IACpD,KAAK;AACH,aACE,oBAAC,OAAY,WAAU,yBACpB,uBAAa,MAAM,MAAM,GAAG,GAAG,GAAG,KAD7B,GAER;AAAA,EAEN;AACF;AASO,SAAS,aAAa,OAAqC;AAChE,QAAM,SAAS,MAAM,MAAM,MAAM,MAAM,UAAU,MAAM,IAAI;AAC3D,SACE,oBAAC,SAAI,WAAU,cACZ,iBAAO,IAAI,CAAC,OAAO,UAAU,YAAY,OAAO,IAAI,KAAK,EAAE,CAAC,GAC/D;AAEJ;;;AD3cM,SACE,OAAAC,MADF,QAAAC,aAAA;AAnBC,IAAM,qBACX;AACK,IAAM,4BACX;AAEF,SAAS,YAAY,OAKP;AACZ,QAAM,OAAO,uBAAuB;AACpC,QAAM,WAAW,MAAM;AACvB,YAAU,MAAM;AACd,QAAI,SAAS,KAAM,UAAS;AAAA,EAC9B,GAAG,CAAC,MAAM,QAAQ,CAAC;AAEnB,MAAI,SAAS,MAAM;AACjB,WACE,gBAAAA,MAAC,SAAI,WAAU,mBAAkB,wBAAqB,iBACpD;AAAA,sBAAAD,KAAC,UAAK,WAAU,mBAAmB,8BAAmB;AAAA,MACtD,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,MAAM,QAAQ;AAAA,UACpB,UAAU,GAAG,MAAM,QAAQ,SAAS,IAAI,MAAM,QAAQ,IAAI;AAAA,UAC1D,OAAO,MAAM;AAAA;AAAA,MACf;AAAA,OACF;AAAA,EAEJ;AAEA,SACE,gBAAAA,KAAC,SAAI,WAAU,mBAAkB,wBAAqB,SAAQ,aAAW,MAAM,QAAQ,MACrF,0BAAAA;AAAA,IAAC;AAAA;AAAA,MACC,MAAM,MAAM,QAAQ;AAAA,MACpB,OAAO,MAAM,QAAQ,QAAQ,MAAM,QAAQ;AAAA,MAC3C,SAAQ;AAAA;AAAA,EACV,GACF;AAEJ;AAaO,SAAS,0BACd,UAAsC,CAAC,GAChB;AACvB,QAAM,WAAW,QAAQ,YAAY,sBAAsB;AAC3D,QAAM,QAAQ,QAAQ,SAAS,iBAAiB;AAGhD,MAAI,YAAY;AAChB,QAAM,WAAW,MAAY;AAC3B,QAAI,UAAW;AACf,gBAAY;AACZ,aAAS,OAAO;AAAA,MACd,OAAO;AAAA,MACP,SAAS;AAAA,MACT,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAEA,SAAO,SAAS,cAAc,SAAwC;AACpE,QAAI,QAAQ,SAAS,cAAc,QAAQ,SAAS,QAAQ;AAC1D,aACE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA;AAAA,MACF;AAAA,IAEJ;AACA,QAAI,QAAQ,SAAS,GAAI,QAAO;AAChC,WACE,gBAAAA,KAAC,SAAI,WAAU,mBAAkB,wBAAqB,YACpD,0BAAAA,KAAC,gBAAa,MAAM,QAAQ,MAAM,UAAU,QAAQ,WAAW,OAAc,GAC/E;AAAA,EAEJ;AACF;;;AE3GA,SAAS,sBAAsB;AAqCxB,IAAM,0BACX;AACK,IAAM,iCACX;AAMK,SAAS,2BACd,UACe;AACf,MAAI,YAAY;AAChB,SAAO;AAAA,IACL,WAAW;AAAA,IACX,SAAoC;AAClC,UAAI,CAAC,WAAW;AACd,oBAAY;AACZ,iBAAS,OAAO;AAAA,UACd,OAAO;AAAA,UACP,SAAS;AAAA,UACT,QAAQ;AAAA,QACV,CAAC;AAAA,MACH;AACA,aAAO,QAAQ,OAAO,IAAI,MAAM,uBAAuB,CAAC;AAAA,IAC1D;AAAA,EACF;AACF;AAGO,SAAS,iBAAiB,MAAc,YAA4B;AACzE,SAAO,GAAG,IAAI,uBAAuB,eAAe,UAAU,CAAC;AACjE;;;AC1DA,SAAS,0BAA0B;AAuF5B,SAAS,iBAAiB,UAA0B;AACzD,SAAO,mBAAmB,QAAQ,KAAK;AACzC;;;AJ6FS,gBAAAE,YAAA;AAlJF,SAAS,mBAAmB,UAA0C;AAC3E,QAAM,YAAY,oBAAI,IAA4B;AAClD,SAAO;AAAA,IACL,QAAQ,MAAM;AACZ,UAAI,UAAU,SAAS,GAAG;AACxB,iBAAS,OAAO;AAAA,UACd,OAAO;AAAA,UACP,SACE;AAAA,UACF,QACE;AAAA,QACJ,CAAC;AACD,eAAO;AAAA,MACT;AACA,iBAAW,YAAY,UAAW,UAAS,IAAI;AAC/C,aAAO;AAAA,IACT;AAAA,IACA,UAAU,UAAU;AAClB,gBAAU,IAAI,QAAQ;AACtB,aAAO,MAAM;AACX,kBAAU,OAAO,QAAQ;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AACF;AASA,IAAM,cAAc,cAAuC,IAAI;AAcxD,SAAS,aAAa,OAAqC;AAChE,QAAM,EAAE,QAAQ,OAAO,OAAO,SAAS,IAAI;AAO3C,QAAM,CAAC,QAAQ,IAAI,SAAS,MAAM;AAChC,UAAMC,YAAW,sBAAsB;AACvC,WAAO,EAAE,UAAAA,WAAU,OAAO,eAAeA,SAAQ,EAAE;AAAA,EACrD,CAAC;AAED,QAAM,WAAW,OAAO,YAAY,SAAS;AAO7C,QAAM,WAAW,OAKP,IAAI;AACd,MAAI,SAAS,YAAY,QAAQ,SAAS,QAAQ,aAAa,UAAU;AACvE,aAAS,UAAU;AAAA,MACjB;AAAA,MACA,KAAK,mBAAmB,QAAQ;AAAA,MAChC,eAAe,0BAA0B,EAAE,SAAS,CAAC;AAAA;AAAA;AAAA,MAGrD,OAAO,2BAA2B,QAAQ;AAAA,IAC5C;AAAA,EACF;AACA,QAAM,QAAQ,SAAS,QAAQ;AAC/B,QAAM,uBAAuB,SAAS,QAAQ;AAC9C,QAAM,eAAe,SAAS,QAAQ;AAEtC,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,MACL;AAAA,MACA,OAAO;AAAA,QACL,eAAe,OAAO,iBAAiB;AAAA,QACvC;AAAA,QACA,OAAO,OAAO,SAAS,SAAS;AAAA,QAChC,OAAO,OAAO,SAAS;AAAA;AAAA;AAAA,QAGvB,OAAO,OAAO,SAAS;AAAA,QACvB,WAAW,OAAO,aAAa;AAAA;AAAA;AAAA;AAAA,QAI/B,aAAa,OAAO,eAAe;AAAA;AAAA;AAAA,QAGnC,UAAU,OAAO,YAAY;AAAA,MAC/B;AAAA,MACA,OAAO,SAAS,CAAC;AAAA,MACjB;AAAA,IACF;AAAA,IACA;AAAA,MACE;AAAA,MACA,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS;AAAA,IACX;AAAA,EACF;AAMA,QAAM,WAAW,OAAO,MAAM;AAC9B,EAAAC,WAAU,MAAM;AACd,QAAI,SAAS,YAAY,OAAQ;AACjC,UAAM,SAAS,SAAS,QAAQ,YAAY,EAAE;AAC9C,QAAI,WAAW,eAAe,WAAW,aAAa;AACpD,eAAS,OAAO;AAAA,QACd,OAAO;AAAA,QACP,SAAS;AAAA,QACT,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AACA,aAAS,UAAU;AAAA,EACrB,GAAG,CAAC,QAAQ,QAAQ,CAAC;AAErB,SAAO,gBAAAF,KAAC,YAAY,UAAZ,EAAqB,OAAe,UAAS;AACvD;AAOO,SAAS,iBAAmC;AACjD,QAAM,QAAQ,WAAW,WAAW;AACpC,MAAI,UAAU,MAAM;AAClB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;;;AKjNA;AAAA,EACE;AAAA,EACA,aAAAG;AAAA,EACA,WAAAC;AAAA,EACA,UAAAC;AAAA,EACA,YAAAC;AAAA,EACA;AAAA,OAEK;AAyBA,SAAS,gBAAqC;AACnD,QAAM,EAAE,OAAO,IAAI,eAAe;AAClC,QAAM,WAAW;AAAA,IACf,OAAO;AAAA,IACP,OAAO;AAAA,IACP,OAAO;AAAA,EACT;AAEA,QAAM,OAAO;AAAA,IACX,CAAC,MAAc,YACb,OAAO,KAAK,YAAY,SAAY,EAAE,KAAK,IAAI,EAAE,MAAM,QAAQ,CAAC;AAAA,IAClE,CAAC,MAAM;AAAA,EACT;AACA,QAAM,SAAS,YAAY,MAAM,OAAO,OAAO,GAAG,CAAC,MAAM,CAAC;AAE1D,SAAOC;AAAA,IACL,OAAO;AAAA,MACL;AAAA,MACA,UAAU,SAAS;AAAA,MACnB,QAAQ,SAAS;AAAA,MACjB,QAAQ,SAAS,WAAW,eAAe,SAAS,WAAW;AAAA,MAC/D,gBAAgB,SAAS;AAAA,MACzB,WAAW,SAAS;AAAA,MACpB,iBAAiB,SAAS;AAAA,MAC1B,OAAO,SAAS;AAAA,MAChB;AAAA,MACA;AAAA,IACF;AAAA,IACA,CAAC,UAAU,MAAM,MAAM;AAAA,EACzB;AACF;AAQO,SAAS,eAAe,WAA4C;AACzE,QAAM,EAAE,OAAO,IAAI,eAAe;AAElC,QAAM,YAAY;AAAA,IAChB,CAAC,aAAyB,OAAO,mBAAmB,WAAW,QAAQ;AAAA,IACvE,CAAC,QAAQ,SAAS;AAAA,EACpB;AACA,QAAM,cAAc;AAAA,IAClB,MAAM,OAAO,mBAAmB,SAAS;AAAA,IACzC,CAAC,QAAQ,SAAS;AAAA,EACpB;AAEA,SAAO,qBAAqB,WAAW,aAAa,WAAW;AACjE;AAGO,SAAS,gBAA8B;AAC5C,SAAO,eAAe,EAAE,OAAO;AACjC;AAGO,SAAS,eAA+B;AAC7C,SAAO,eAAe,EAAE;AAC1B;AAGO,SAAS,eAA0B;AACxC,SAAO,eAAe,EAAE;AAC1B;AAsBA,IAAM,eAAe;AAed,SAAS,uBAAmD;AACjE,QAAM,EAAE,QAAQ,OAAO,MAAM,IAAI,eAAe;AAChD,QAAM,WAAW,OAAO;AACxB,QAAM,WAAW;AAAA,IACf,OAAO;AAAA,IACP,OAAO;AAAA,IACP,OAAO;AAAA,EACT;AACA,QAAM,iBAAiB,SAAS;AAChC,QAAM,SAAS,SAAS,WAAW,eAAe,SAAS,WAAW;AAEtE,QAAM,WAAW,GAAG,YAAY,GAAG,kBAAkB,KAAK;AAC1D,QAAM,CAAC,MAAM,YAAY,IAAIC;AAAA,IAAiB,MAC5C,SAAS,eAAgB,MAAM,MAAM,IAAI,QAAQ,KAAK,KAAM;AAAA,EAC9D;AACA,QAAM,CAAC,qBAAqB,WAAW,IAAIA,UAAS,KAAK;AAMzD,QAAM,SAASC,QAAO,QAAQ;AAC9B,EAAAC,WAAU,MAAM;AACd,QAAI,OAAO,YAAY,SAAU;AACjC,UAAM,cAAc,OAAO;AAC3B,WAAO,UAAU;AACjB,QAAI,CAAC,SAAS,aAAc;AAC5B,UAAM,UAAU,MAAM,MAAM,IAAI,WAAW;AAC3C,QAAI,YAAY,QAAQ,YAAY,IAAI;AACtC,YAAM,MAAM,IAAI,UAAU,OAAO;AACjC,YAAM,MAAM,OAAO,WAAW;AAC9B,mBAAa,OAAO;AACpB;AAAA,IACF;AACA,iBAAa,MAAM,MAAM,IAAI,QAAQ,KAAK,EAAE;AAAA,EAC9C,GAAG,CAAC,UAAU,SAAS,cAAc,MAAM,KAAK,CAAC;AAEjD,QAAM,UAAU;AAAA,IACd,CAAC,SAAiB;AAChB,mBAAa,IAAI;AACjB,kBAAY,KAAK;AACjB,UAAI,CAAC,SAAS,aAAc;AAC5B,UAAI,SAAS,GAAI,OAAM,MAAM,OAAO,OAAO,OAAO;AAAA,UAC7C,OAAM,MAAM,IAAI,OAAO,SAAS,IAAI;AAAA,IAC3C;AAAA,IACA,CAAC,SAAS,cAAc,MAAM,KAAK;AAAA,EACrC;AAIA,EAAAA;AAAA,IACE,MACE,MAAM,UAAU,CAAC,aAAa;AAC5B,mBAAa,QAAQ;AACrB,kBAAY,IAAI;AAChB,UAAI,SAAS,aAAc,OAAM,MAAM,IAAI,OAAO,SAAS,QAAQ;AAAA,IACrE,CAAC;AAAA,IACH,CAAC,OAAO,SAAS,cAAc,MAAM,KAAK;AAAA,EAC5C;AAEA,QAAM,OAAO,YAAY,YAA4C;AACnE,UAAM,OAAO,KAAK,KAAK;AACvB,QAAI,SAAS,MAAO,UAAU,CAAC,SAAS,cAAgB,QAAO;AAG/D,iBAAa,EAAE;AACf,gBAAY,KAAK;AACjB,QAAI,SAAS,aAAc,OAAM,MAAM,OAAO,OAAO,OAAO;AAC5D,UAAM,SAAS,MAAM,OAAO,KAAK,EAAE,MAAM,KAAK,CAAC;AAC/C,QAAI,CAAC,OAAO,UAAU;AACpB,mBAAa,IAAI;AACjB,UAAI,SAAS,aAAc,OAAM,MAAM,IAAI,OAAO,SAAS,IAAI;AAAA,IACjE;AACA,WAAO;AAAA,EACT,GAAG,CAAC,MAAM,QAAQ,QAAQ,SAAS,cAAc,SAAS,eAAe,MAAM,KAAK,CAAC;AAErF,QAAM,OAAO,YAAY,MAAM,OAAO,OAAO,GAAG,CAAC,MAAM,CAAC;AAExD,QAAM,YAAY;AAAA,IAChB,CAAC,UAA8B;AAC7B,UAAI,MAAM,QAAQ,UAAU;AAC1B,YAAI,CAAC,OAAQ;AACb,cAAM,eAAe;AACrB,aAAK,OAAO,OAAO;AACnB;AAAA,MACF;AACA,UAAI,MAAM,QAAQ,QAAS;AAC3B,YAAM,eAAe,MAAM,WAAW,MAAM;AAC5C,YAAM,QACJ,SAAS,YAAY,UACjB,CAAC,MAAM,YAAY,CAAC,eACpB;AACN,UAAI,CAAC,MAAO;AACZ,YAAM,eAAe;AACrB,WAAK,KAAK;AAAA,IACZ;AAAA,IACA,CAAC,SAAS,SAAS,QAAQ,MAAM,MAAM;AAAA,EACzC;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,SAAS,KAAK,KAAK,MAAM,OAAO,CAAC,UAAU,SAAS;AAAA,IACpD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAOO,SAAS,kBACd,UACA,WACQ;AACR,QAAM,QAAQ,SAAS,UAAU,CAAC,YAAY,QAAQ,OAAO,SAAS;AACtE,MAAI,SAAS,EAAG,QAAO;AACvB,WAAS,SAAS,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG;AACrD,UAAM,YAAY,SAAS,MAAM;AACjC,QAAI,aAAa,UAAU,SAAS,OAAQ,QAAO,YAAY,SAAS;AAAA,EAC1E;AACA,SAAO;AACT;;;AC3PA,SAAS,sBAAsB;AAuChB,gBAAAC,MAmCP,QAAAC,aAnCO;AArCf,IAAM,QAAQ;AAOd,IAAM,sBAAsB;AAAA,EAC1B;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA,EACA,EAAE,MAAM,CAAC,SAAiB,eAAe,KAAK,MAAM,IAAI,CAAC,MAAM,GAAG;AAAA;AACpE;AAEA,SAAS,kBAAkB,MAAiD;AAC1E,MAAI,CAAC,MAAM,KAAK,IAAI,EAAG,QAAO,CAAC,EAAE,MAAM,OAAO,MAAM,KAAK,CAAC;AAC1D,QAAM,WAA8C,CAAC;AAKrD,QAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,WAAS,QAAQ,GAAG,QAAQ,OAAO,QAAQ,SAAS,GAAG;AACrD,UAAM,OAAO,OAAO,KAAK,KAAK;AAC9B,QAAI,SAAS,GAAI;AACjB,aAAS,KAAK,EAAE,MAAM,QAAQ,MAAM,GAAG,KAAK,CAAC;AAAA,EAC/C;AACA,SAAO;AACT;AAEA,SAAS,YAAY,MAAc,WAAgC;AACjE,QAAM,aAAa,KAAK,MAAM,QAAQ;AACtC,QAAM,QAAqB,CAAC;AAC5B,WAAS,QAAQ,GAAG,QAAQ,WAAW,QAAQ,SAAS,GAAG;AACzD,UAAM,YAAY,WAAW,KAAK,KAAK;AACvC,QAAI,UAAU,KAAK,MAAM,GAAI;AAC7B,UAAM,KAAK,gBAAAD,KAAC,OAAkC,uBAA3B,GAAG,SAAS,KAAK,KAAK,EAAe,CAAI;AAAA,EAC9D;AACA,SAAO;AACT;AAOO,SAAS,+BACd,WAA6B,sBAAsB,GAC5B;AACvB,MAAI,YAAY;AAEhB,QAAM,eAAe,MAAY;AAC/B,QAAI,UAAW;AACf,gBAAY;AACZ,aAAS,OAAO;AAAA,MACd,OAAO;AAAA,MACP,SACE;AAAA,MACF,QACE;AAAA,IACJ,CAAC;AAAA,EACH;AAEA,SAAO,SAAS,cAAc,SAAwC;AACpE,UAAM,OAAO,QAAQ;AACrB,QAAI,SAAS,GAAI,QAAO;AAIxB,QAAI,QAAQ,SAAS,cAAc,QAAQ,SAAS,QAAQ;AAC1D,aACE,gBAAAC,MAAC,SAAI,WAAU,mBAAkB,wBAAqB,SACpD;AAAA,wBAAAD,KAAC,UAAK,WAAU,mBACb,oCAA0B,QAAQ,IAAI,2EACzC;AAAA,QACC,YAAY,MAAM,OAAO;AAAA,SAC5B;AAAA,IAEJ;AAEA,eAAW,WAAW,qBAAqB;AACzC,UAAI,QAAQ,KAAK,IAAI,GAAG;AACtB,qBAAa;AACb;AAAA,MACF;AAAA,IACF;AAEA,UAAM,WAAW,kBAAkB,IAAI;AACvC,WACE,gBAAAA,KAAC,SAAI,WAAU,mBAAkB,wBAAqB,SACnD,mBAAS;AAAA,MAAI,CAAC,SAAS,UACtB,QAAQ,OACN,gBAAAA,KAAC,SACC,0BAAAA,KAAC,UAAM,kBAAQ,KAAK,QAAQ,aAAa,EAAE,GAAE,KADrC,IAAI,KAAK,EAEnB,IAEA,YAAY,QAAQ,MAAM,IAAI,KAAK,EAAE;AAAA,IAEzC,GACF;AAAA,EAEJ;AACF;;;ACzGM,gBAAAE,YAAA;AAfC,SAAS,oBAA+B;AAC7C,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,OAAM;AAAA,MACN,QAAO;AAAA,MACP,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,QAAO;AAAA,MACP,aAAY;AAAA,MACZ,eAAc;AAAA,MACd,gBAAe;AAAA,MACf,eAAY;AAAA,MACZ,WAAU;AAAA,MAEV,0BAAAA,KAAC,UAAK,GAAE,wBAAuB;AAAA;AAAA,EACjC;AAEJ;AAEO,SAAS,iBAA4B;AAC1C,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN,QAAO;AAAA,MACP,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,QAAO;AAAA,MACP,aAAY;AAAA,MACZ,eAAc;AAAA,MACd,gBAAe;AAAA,MACf,eAAY;AAAA,MACZ,WAAU;AAAA,MAEV,0BAAAA,KAAC,UAAK,GAAE,gCAA+B;AAAA;AAAA,EACzC;AAEJ;;;ACnBA,SAAS,aAAAC,YAAW,WAAAC,UAAS,UAAAC,SAAQ,YAAAC,iBAAgC;AAErE,SAAS,uBAAuB;;;ACbhC,SAAS,eAAAC,cAAa,aAAAC,YAAW,UAAAC,SAAQ,YAAAC,iBAAgB;AAGlD,IAAM,qBAAqB;AAe3B,SAAS,iBAAiB,SAAiC;AAChE,QAAM,aAAaD,QAA2B,IAAI;AAClD,QAAM,YAAYA,QAAO,IAAI;AAC7B,QAAM,kBAAkBA,QAAO,KAAK;AACpC,QAAM,CAAC,QAAQ,SAAS,IAAIC,UAAS,IAAI;AACzC,QAAM,CAAC,gBAAgB,YAAY,IAAIA,UAAS,KAAK;AAErD,QAAM,aAAaH,aAAY,CAAC,SAA+B;AAC7D,UAAM,WAAW,KAAK,eAAe,KAAK,YAAY,KAAK;AAC3D,WAAO,YAAY;AAAA,EACrB,GAAG,CAAC,CAAC;AAEL,QAAM,iBAAiBA,aAAY,CAAC,SAA4B;AAG9D,oBAAgB,UAAU;AAC1B,SAAK,YAAY,KAAK;AAAA,EACxB,GAAG,CAAC,CAAC;AAEL,QAAM,eAAeA,aAAY,MAAY;AAC3C,UAAM,OAAO,WAAW;AACxB,QAAI,SAAS,KAAM;AACnB,QAAI,gBAAgB,SAAS;AAC3B,sBAAgB,UAAU;AAC1B;AAAA,IACF;AACA,UAAM,SAAS,WAAW,IAAI;AAC9B,cAAU,UAAU;AACpB,cAAU,MAAM;AAChB,QAAI,OAAQ,cAAa,KAAK;AAAA,EAChC,GAAG,CAAC,UAAU,CAAC;AAEf,QAAM,MAAMA;AAAA,IACV,CAAC,SAA6B;AAC5B,YAAM,WAAW,WAAW;AAC5B,UAAI,aAAa,KAAM,UAAS,oBAAoB,UAAU,YAAY;AAC1E,iBAAW,UAAU;AACrB,UAAI,SAAS,KAAM,MAAK,iBAAiB,UAAU,cAAc,EAAE,SAAS,KAAK,CAAC;AAAA,IACpF;AAAA,IACA,CAAC,YAAY;AAAA,EACf;AAEA,EAAAC;AAAA,IACE,MAAM,MAAM;AACV,YAAM,OAAO,WAAW;AACxB,UAAI,SAAS,KAAM,MAAK,oBAAoB,UAAU,YAAY;AAAA,IACpE;AAAA,IACA,CAAC,YAAY;AAAA,EACf;AAEA,QAAM,kBAAkBD,aAAY,MAAY;AAC9C,UAAM,OAAO,WAAW;AACxB,QAAI,SAAS,KAAM;AACnB,QAAI,CAAC,QAAS;AACd,QAAI,UAAU,SAAS;AACrB,qBAAe,IAAI;AACnB;AAAA,IACF;AAEA,iBAAa,IAAI;AAAA,EACnB,GAAG,CAAC,SAAS,cAAc,CAAC;AAE5B,QAAM,eAAeA,aAAY,MAAY;AAC3C,UAAM,OAAO,WAAW;AACxB,QAAI,SAAS,KAAM;AACnB,mBAAe,IAAI;AACnB,cAAU,UAAU;AACpB,cAAU,IAAI;AACd,iBAAa,KAAK;AAAA,EACpB,GAAG,CAAC,cAAc,CAAC;AAEnB,SAAO,EAAE,KAAK,QAAQ,gBAAgB,cAAc,gBAAgB;AACtE;;;ACnGA,SAAS,YAA4B;;;ACsBrC,SAAS,gBAAgB;AACzB,SAAS,eAAAI,cAAa,aAAAC,YAAW,UAAAC,SAAQ,YAAAC,iBAAgC;;;ACtBzE,SAAS,sBAAsB;AAYxB,SAAS,cAAc,MAAsB;AAMlD,QAAM,OAAiB,CAAC;AACxB,MAAI,SAAS;AACb,MAAI,KAAK;AACT,aAAW,KAAK,eAAe,IAAI,GAAG;AACpC,QAAI,EAAE,QAAQ,GAAI;AAClB,cAAU,KAAK,MAAM,IAAI,EAAE,KAAK;AAChC,UAAM,MAAM,KAAK,MAAM,EAAE,OAAO,EAAE,GAAG;AACrC,SAAK,KAAK,EAAE,SAAS,UAAU,iBAAiB,UAAU,GAAG,CAAC;AAC9D,cAAU,SAAS,KAAK,SAAS,CAAC;AAClC,SAAK,EAAE;AAAA,EACT;AACA,YAAU,KAAK,MAAM,EAAE;AACvB,SAAO,OACJ,QAAQ,2BAA2B,IAAI,EACvC,QAAQ,0BAA0B,IAAI,EACtC,QAAQ,uBAAuB,EAAE,EACjC,QAAQ,kBAAkB,EAAE,EAC5B,QAAQ,8BAA8B,EAAE,EACxC,QAAQ,wBAAwB,IAAI,EACpC,QAAQ,qBAAqB,IAAI,EACjC,QAAQ,kBAAkB,IAAI,EAC9B,QAAQ,cAAc,IAAI,EAC1B,QAAQ,6BAA6B,EAAE,EACvC,QAAQ,sBAAsB,CAAC,IAAI,MAAc,KAAK,OAAO,CAAC,CAAC,KAAK,EAAE,EACtE,QAAQ,cAAc,GAAG;AAC9B;AAGA,SAAS,UAAU,KAAqB;AACtC,QAAM,QAAQ,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,UAAU;AAC7C,QAAM,QAAQ,IAAI,MAAM,OAAO,IAAI,SAAS,KAAK;AACjD,SAAO,UAAU,KAAK,KAAK,KAAK,MAAM,KAAK,IAAI,MAAM,MAAM,GAAG,EAAE,IAAI;AACtE;;;AD6CI,SAOI,OAAAC,MAPJ,QAAAC,aAAA;AAjEG,IAAM,oBAAoB;AAC1B,IAAM,2BACX;AAGF,IAAM,YAAY;AAaX,SAAS,mBAAmB,OAA2C;AAC5E,QAAM,EAAE,QAAQ,IAAI;AACpB,QAAM,EAAE,SAAS,IAAI,aAAa;AAClC,QAAM,QAAQ,aAAa;AAC3B,QAAM,EAAE,QAAQ,MAAM,IAAI,eAAe;AACzC,QAAM,CAAC,QAAQ,SAAS,IAAIC,UAAqC,IAAI;AACrE,QAAM,QAAQC,QAA6C,IAAI;AAE/D,EAAAC;AAAA,IACE,MAAM,MAAM;AACV,UAAI,MAAM,YAAY,KAAM,cAAa,MAAM,OAAO;AAAA,IACxD;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,OAAOC;AAAA,IACX,CAAC,OAAe,UAA+B;AAG7C,WAAK,SAAS,OAAO;AAAA,QACnB,gBAAgB;AAAA,QAChB,QAAQ,CAACC,UAAS,SAAS;AACzB,cAAI,SAAS,QAAS,UAAS,OAAO,EAAE,OAAO,WAAW,SAAAA,UAAS,QAAQ,yBAAyB,CAAC;AAAA,QACvG;AAAA,MACF,CAAC,EAAE,KAAK,CAAC,aAAa;AACpB,YAAI,CAAC,SAAU;AACf,kBAAU,KAAK;AACf,YAAI,MAAM,YAAY,KAAM,cAAa,MAAM,OAAO;AACtD,cAAM,UAAU,WAAW,MAAM,UAAU,IAAI,GAAG,SAAS;AAAA,MAC7D,CAAC;AAAA,IACH;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AAIA,MAAI,QAAQ,WAAW,eAAe,QAAQ,WAAW,UAAW,QAAO;AAE3E,QAAM,MAAM,YAAY,OAAO;AAC/B,QAAM,OAAO,MAAM,iBAAiB,OAAO;AAC3C,QAAM,SAAS,QAAQ,SAAS;AAEhC,QAAM,QAAQ,SAAS,KAAK,kBAAkB,OAAO,YAAY,EAAE,UAAU,QAAQ,EAAE;AAEvF,MAAI,QAAQ,MAAM,SAAS,OAAW,QAAO;AAE7C,SACE,gBAAAL;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,MAAK;AAAA,MACL,cAAY,SAAS,6BAA6B;AAAA,MAClD,mBAAiB,QAAQ;AAAA,MAExB;AAAA,gBAAQ,KAAK,OACZ,gBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,WAAU;AAAA,YACV,eAAY;AAAA,YACZ,SAAS,MAAM,KAAK,cAAc,GAAG,GAAG,MAAM;AAAA,YAE7C,qBAAW,SAAS,WAAW;AAAA;AAAA,QAClC;AAAA,QAOD,QAAQ,MAAM,SAAS,OACtB,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,WAAU;AAAA,YACV,eAAY;AAAA,YACZ,SAAS,MAAM,KAAK,KAAK,UAAU;AAAA,YAElC,qBAAW,aAAa,WAAW;AAAA;AAAA,QACtC;AAAA,QAGD,UAAU,KAAK,OACd,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,WAAU;AAAA,YACV,eAAY;AAAA,YACZ,SAAS,MAAM,KAAK,OAAO,KAAK,EAAE,MAAM,MAAM,CAAC;AAAA,YAChD;AAAA;AAAA,QAED;AAAA,QAGD,UAAU,QAAQ,KACjB,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,WAAU;AAAA,YACV,eAAY;AAAA,YACZ,SAAS,MAAM,MAAM,QAAQ,GAAG;AAAA,YACjC;AAAA;AAAA,QAED,IACE;AAAA,QAEH;AAAA,QAOA,WAAW,OAAO,OACjB,gBAAAA,KAAC,UAAK,WAAU,oBAAmB,MAAK,UAAS,aAAU,UAAS,qCAEpE;AAAA;AAAA;AAAA,EAEJ;AAEJ;;;AErKA,SAAS,YAAAO,iBAAgC;AAEzC,SAAS,QAAQ,gBAAgB;;;ACejC,SAAS,YAAAC,iBAAgC;AA8EnC,SAwBM,YAAAC,WAfF,OAAAC,MATJ,QAAAC,aAAA;AAnEC,IAAM,wBAAuD;AAAA,EAClE,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,sBAAsB;AAAA,EACtB,oBAAoB;AAAA,EACpB,gBAAgB;AAAA,EAChB,iBAAiB;AACnB;AAEA,SAAS,cAAc,OAAwB;AAC7C,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI;AACF,WAAO,KAAK,UAAU,OAAO,MAAM,CAAC;AAAA,EACtC,QAAQ;AAEN,WAAO,OAAO,KAAK;AAAA,EACrB;AACF;AAkBO,SAAS,aAAa,OAAqC;AAChE,QAAM,EAAE,MAAM,iBAAiB,KAAK,IAAI;AACxC,QAAM,CAAC,UAAU,WAAW,IAAIC,UAAS,eAAe;AAExD,QAAM,EAAE,UAAU,IAAI,aAAa;AACnC,QAAM,SAAS,KAAK,SAAS,KAAK,SAAS,SAAS,CAAC;AACrD,QAAM,QAAQ,cAAc,KAAK,KAAK;AACtC,QAAM,SAAS,cAAc,KAAK,MAAM;AACxC,QAAM,aAAa,sBAAsB,KAAK,KAAK;AAKnD,QAAM,OACJ,WAAW,UACX,OAAO,YAAY,MACnB,OAAO,QAAQ,KAAK,EAAE,YAAY,MAAM,WAAW,YAAY,IAC3D,OAAO,UACP;AACN,QAAM,UACJ,UAAU,MAAM,WAAW,MAAM,KAAK,cAAc,QAAQ,KAAK,SAAS,SAAS;AAErF,SACE,gBAAAD;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,cAAY,KAAK;AAAA,MACjB,aAAW,KAAK;AAAA,MACf,GAAI,MAAM,gBAAgB,SACvB,CAAC,IACD,EAAE,8BAA8B,MAAM,YAAY;AAAA,MAEtD;AAAA,wBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,WAAU;AAAA,YACV,iBAAe;AAAA,YAGf,SAAS,MAAM,YAAY,CAAC,SAAS,CAAC,IAAI;AAAA,YAE1C;AAAA,8BAAAD,KAAC,UAAK,aAAW,UAAU,WAAU,wBACnC,0BAAAA,KAAC,qBAAkB,GACrB;AAAA,cAGA,gBAAAA,KAAC,UAAK,WAAU,sBAAsB,oBAAU,KAAK,QAAQ,GAAE;AAAA,cAC9D,SAAS,OAAO,OAAO,gBAAAA,KAAC,UAAK,WAAU,sBAAsB,gBAAK;AAAA,cACnE,gBAAAA,KAAC,UAAK,WAAU,uBAAuB,sBAAW;AAAA;AAAA;AAAA,QACpD;AAAA,QAEC,WACC,SAAS,SACP,OAEF,gBAAAC,MAAC,SAAI,WAAU,sBACZ;AAAA,oBAAU,KACT,gBAAAA,MAAAF,WAAA,EACE;AAAA,4BAAAC,KAAC,UAAK,WAAU,uBAAsB,+BAAiB;AAAA,YACvD,gBAAAA,KAAC,SAAI,WAAU,yBAAyB,iBAAM;AAAA,aAChD,IACE;AAAA,UAEH,KAAK,SAAS,SAAS,IACtB,gBAAAC,MAAAF,WAAA,EACE;AAAA,4BAAAC,KAAC,UAAK,WAAU,uBAAsB,sBAAQ;AAAA,YAC9C,gBAAAA,KAAC,QAAG,WAAU,0BACX,eAAK,SAAS,IAAI,CAACG,OAAM,UACxB,gBAAAF,MAAC,QACC;AAAA,8BAAAD,KAAC,UAAK,kBAAgBG,MAAK,UAAW,UAAAA,MAAK,UAAS;AAAA,cACnDA,MAAK,YAAY,KAAK,OAAO,WAAMA,MAAK,OAAO;AAAA,cAC/CA,MAAK,YAAY,OAAO,OAAO,KAAK,KAAK,MAAMA,MAAK,OAAO,CAAC;AAAA,iBAHtD,GAAGA,MAAK,QAAQ,IAAI,KAAK,EAIlC,CACD,GACH;AAAA,aACF,IACE;AAAA,UAEH,KAAK,cAAc,OAClB,gBAAAF,MAAAF,WAAA,EACE;AAAA,4BAAAC,KAAC,UAAK,WAAU,uBAAsB,6BAAe;AAAA,YACrD,gBAAAA,KAAC,OAAE,WAAU,6BAA6B,eAAK,WAAU;AAAA,aAC3D,IACE;AAAA,UAEH,WAAW,KACV,gBAAAC,MAAAF,WAAA,EACE;AAAA,4BAAAC,KAAC,UAAK,WAAU,uBAAsB,8BAAgB;AAAA,YACtD,gBAAAA,KAAC,SAAI,WAAU,yBAAyB,kBAAO;AAAA,aACjD,IACE;AAAA,UAEH,UAAU,OACT,gBAAAA,KAAC,UAAK,WAAU,mBACb,eAAK,UAAU,oBACZ,iDACA,gDACN;AAAA,WAEJ,IAEE;AAAA;AAAA;AAAA,EACN;AAEJ;;;ADvII,qBAAAI,WAAA,OAAAC,MAsCE,QAAAC,aAtCF;AAFG,SAAS,iBAAiB,OAAyC;AACxE,SACE,gBAAAD,KAAAD,WAAA,EACG,gBAAM,cAAc;AAAA,IACnB,MAAM;AAAA,IACN,MAAM,MAAM,KAAK;AAAA,IACjB,WAAW,MAAM;AAAA,IACjB,MAAM,MAAM;AAAA,IACZ,WAAW,MAAM;AAAA,EACnB,CAAC,GACH;AAEJ;AAoBO,SAAS,mBAAmB,OAA2C;AAC5E,QAAM,EAAE,MAAM,YAAY,IAAI;AAC9B,QAAM,CAAC,MAAM,OAAO,IAAIG,UAAS,WAAW;AAC5C,QAAM,YAAY,KAAK,UAAU;AACjC,QAAM,QAAQ,YAAY,mBAAc;AAExC,SACE,gBAAAD,MAAC,SAAI,WAAU,qBAAoB,kBAAgB,WACjD;AAAA,oBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,WAAU;AAAA,QACV,iBAAe;AAAA,QACf,SAAS,MAAM,QAAQ,CAAC,UAAU,CAAC,KAAK;AAAA,QAExC;AAAA,0BAAAD,KAAC,UAAK,aAAW,MAAM,WAAU,wBAC/B,0BAAAA,KAAC,qBAAkB,GACrB;AAAA,UACC;AAAA;AAAA;AAAA,IACH;AAAA,IACC,OACC,gBAAAA,KAAC,SAAI,WAAU,2BAA0B,0BAAuB,QAC7D,eAAK,MACR,IACE;AAAA,KACN;AAEJ;AAWO,SAAS,mBAAmB,OAA4C;AAC7E,QAAM,EAAE,KAAK,IAAI;AACjB,SACE,gBAAAC,MAAC,SAAI,WAAU,kBAAiB,cAAY,KAAK,OAAO,MAAK,QAC3D;AAAA,oBAAAD,KAAC,UAAM,eAAK,aAAY;AAAA,IACvB,KAAK,WAAW,OAAO,OACtB,gBAAAA,KAAC,UAAK,WAAU,0BAA0B,eAAK,QAAO;AAAA,KAE1D;AAEJ;AAmBO,SAAS,oBAAoB,OAA4C;AAC9E,QAAM,EAAE,MAAM,WAAW,QAAQ,IAAI;AACrC,SACE,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC,IAAG;AAAA,MACH,WAAU;AAAA,MACV,aAAW,KAAK,QAAQ;AAAA,MACxB,SAAS,KAAK;AAAA,MACd,OAAO;AAAA,MACP,WAAU;AAAA,MACV,cAAc,cAAc,KAAK,SAAY,EAAE,SAAS,UAAU;AAAA,MAClE,QAAO;AAAA,MAEP;AAAA,wBAAAD,KAAC,OAAE,WAAU,6BAA6B,eAAK,aAAY;AAAA,QAC1D,KAAK,WAAW,OAAO,OACtB,gBAAAA,KAAC,OAAE,WAAU,2BAA2B,eAAK,QAAO;AAAA,QAErD,cAAc,KAAK,OAClB,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,MAAK;AAAA,YACL,SAAQ;AAAA,YACR,WAAU;AAAA,YACV,SAAS,MAAM,QAAQ,SAAS;AAAA,YACjC;AAAA;AAAA,QAED;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAIO,SAAS,iBAAiB,OAGnB;AACZ,SAAO,gBAAAA,KAAC,gBAAa,MAAM,MAAM,MAAM,iBAAiB,MAAM,iBAAiB;AACjF;AAIO,SAAS,wBAAwB,OAK1B;AACZ,QAAM,EAAE,KAAK,IAAI;AACjB,SACE,gBAAAA,KAAC,SAAI,sBAAoB,KAAK,MAAM,eAAa,KAAK,QACnD,gBAAM,cAAc;AAAA,IACnB,MAAM,KAAK;AAAA,IACX,MAAM,KAAK,WAAW;AAAA,IACtB,MAAM,KAAK;AAAA,IACX,WAAW,KAAK,WAAW;AAAA,IAC3B,MAAM,MAAM;AAAA,IACZ,WAAW,MAAM;AAAA,EACnB,CAAC,GACH;AAEJ;AAMO,SAAS,iBAAiB,OAA0C;AACzE,QAAM,OAAO,MAAM,KAAK,QAAQ,MAAM,KAAK;AAC3C,SACE,gBAAAA,KAAC,UAAK,WAAU,mBAAkB,wBAAqB,QACpD,4BAAkB,IAAI,iFACzB;AAEJ;AAEO,SAAS,mBAAmB,OAA4C;AAC7E,QAAM,EAAE,KAAK,IAAI;AACjB,QAAM,QAAQ,KAAK,SAAS,KAAK,OAAO,KAAK;AAC7C,SACE,gBAAAA,KAAC,UAAK,WAAU,mBAAkB,wBAAqB,UACpD,qBAAW,KAAK,IACnB;AAEJ;AAEO,SAAS,oBAAoB,OAA6C;AAC/E,SACE,gBAAAA,KAAC,UAAK,WAAU,mBAAkB,wBAAqB,YACpD,gBAAM,KAAK,WACR,yBAAyB,MAAM,KAAK,MAAM,KAC1C,6CAA6C,MAAM,KAAK,MAAM,wHACpE;AAEJ;;;AH/KM,SAOE,OAAAG,MAPF,QAAAC,aAAA;AAhBC,IAAM,kBAAkB,KAAK,SAASC,iBAC3C,OACW;AACX,QAAM,UAAU,eAAe,MAAM,SAAS;AAC9C,QAAM,WAAW,cAAc;AAC/B,QAAM,EAAE,cAAc,IAAI,aAAa;AACvC,QAAM,QAAQ,aAAa;AAC3B,QAAM,EAAE,QAAQ,MAAM,IAAI,eAAe;AAEzC,MAAI,YAAY,OAAW,QAAO;AAElC,QAAM,YAAY,QAAQ,WAAW,eAAe,QAAQ,WAAW;AACvE,QAAM,OAAO,QAAQ;AAErB,MAAI,SAAS,QAAQ;AACnB,WACE,gBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,aAAU;AAAA,QACT,GAAI,MAAM,YAAY,EAAE,kBAAkB,OAAO,IAAI,CAAC;AAAA,QACvD,mBAAiB,QAAQ;AAAA,QACzB,cAAW;AAAA,QAEX;AAAA,0BAAAD,KAAC,SAAI,WAAU,wBACZ,wBAAc;AAAA,YACb,MAAM;AAAA,YACN,MAAM,YAAY,OAAO;AAAA,YACzB,WAAW;AAAA,YACX;AAAA,YACA,WAAW,QAAQ;AAAA,UACrB,CAAC,GACH;AAAA,UACC,WAAW,SAAS,MAAM,cAAc;AAAA,UACxC,SAAS,iBAAiB,gBAAAA,KAAC,sBAAmB,SAAkB,IAAK;AAAA;AAAA;AAAA,IACxE;AAAA,EAEJ;AAEA,QAAM,YAAY,kBAAkB,OAAO,YAAY,EAAE,UAAU,QAAQ,EAAE;AAE7E,SACE,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,aAAW;AAAA,MACV,GAAI,MAAM,YAAY,EAAE,kBAAkB,OAAO,IAAI,CAAC;AAAA,MACvD,eAAa,QAAQ;AAAA,MACrB,mBAAiB,QAAQ;AAAA,MACzB,cAAW;AAAA,MACX,aAAW,aAAa;AAAA,MAExB;AAAA,wBAAAA,MAAC,SAAI,WAAU,sBACZ;AAAA,kBAAQ,MAAM,IAAI,CAAC,MAAM,UAAU;AAClC,kBAAM,WAAW,MAAM,aAAa,MAAM,EAAE,SAAS,OAAO,UAAU,CAAC;AACvE,gBAAI,aAAa,QAAW;AAC1B,qBAAO,gBAAAD,KAAC,SAA+B,sBAAtB,OAAO,MAAM,KAAK,CAAa;AAAA,YAClD;AACA,mBACE,gBAAAA,KAAC,SACE,qBAAW,MAAM;AAAA,cAChB;AAAA,cACA;AAAA,cACA;AAAA,cACA,aAAa,SAAS;AAAA,cACtB,eAAe,SAAS;AAAA,cACxB;AAAA,cACA,SAAS,CAAC,SAAiB,MAAM,QAAQ,IAAI;AAAA,YAC/C,CAAC,KATO,OAAO,MAAM,KAAK,CAU5B;AAAA,UAEJ,CAAC;AAAA,UAKA,aAAa,QAAQ,MAAM,WAAW,IACrC,gBAAAA,KAAC,UAAK,WAAU,iBAAgB,gBAAa,WAAU,eAAY,QAAO,IACxE;AAAA,UASH,kBAAkB,OAAO,IACxB,gBAAAA,KAAC,OAAE,WAAU,yBAAwB,MAAK,QAAO,qBAEjD,IACE;AAAA,WACN;AAAA,QACC,WAAW,SAAS,MAAM,cAAc;AAAA,QACxC,SAAS,iBAAiB,gBAAAA,KAAC,sBAAmB,SAAkB,IAAK;AAAA;AAAA;AAAA,EACxE;AAEJ,CAAC;AAED,SAAS,kBAAkB,SAA+B;AACxD,MAAI,QAAQ,WAAW,YAAa,QAAO;AAC3C,SAAO,CAAC,QAAQ,MAAM;AAAA,IACpB,CAAC,SAAS,KAAK,SAAS,WAAW,KAAK,cAAc;AAAA,EACxD;AACF;AAEA,SAAS,OAAO,MAAgB,OAAuB;AACrD,MAAI,KAAK,SAAS,OAAQ,QAAO,QAAQ,KAAK,UAAU;AACxD,MAAI,KAAK,SAAS,eAAgB,QAAO,SAAS,KAAK,OAAO;AAC9D,SAAO,GAAG,KAAK,IAAI,IAAI,KAAK;AAC9B;AAEA,SAAS,WACP,SACA,MACW;AACX,QAAM,WAAW,OAAO,OAAO;AAC/B,MAAI,aAAa,OAAW,QAAO;AAInC,MAAI,QAAQ,SAAS,aAAa,KAAM,QAAO;AAC/C,SAAO,gBAAAA,KAAC,SAAI,WAAU,sBAAsB,kBAAQ,SAAS,UAAS;AACxE;AAYA,SAAS,WAAW,MAAgB,SAAuC;AACzE,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK;AACH,aACE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,eAAe,QAAQ;AAAA,UACvB,WAAW,QAAQ;AAAA,UACnB,MAAM,QAAQ,QAAQ;AAAA,UACtB,WAAW,QAAQ,QAAQ;AAAA;AAAA,MAC7B;AAAA,IAEJ,KAAK;AACH,aAAO,gBAAAA,KAAC,sBAAmB,MAAY,aAAa,QAAQ,eAAe;AAAA,IAC7E,KAAK;AACH,aAAO,gBAAAA,KAAC,oBAAiB,MAAY,iBAAiB,QAAQ,aAAa;AAAA,IAC7E,KAAK;AACH,aAAO,gBAAAA,KAAC,sBAAmB,MAAY;AAAA,IACzC,KAAK;AACH,aACE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,WAAW,QAAQ;AAAA,UACnB,SAAS,QAAQ;AAAA;AAAA,MACnB;AAAA,IAEJ,KAAK;AACH,aACE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,eAAe,QAAQ;AAAA,UACvB,WAAW,QAAQ,QAAQ;AAAA,UAC3B,MAAM,QAAQ,QAAQ;AAAA;AAAA,MACxB;AAAA,IAEJ,KAAK;AACH,aAAO,gBAAAA,KAAC,oBAAiB,MAAY;AAAA,IACvC,KAAK;AACH,aAAO,gBAAAA,KAAC,sBAAmB,MAAY;AAAA,IACzC,KAAK;AACH,aAAO,gBAAAA,KAAC,uBAAoB,MAAY;AAAA,IAC1C,SAAS;AAIP,YAAM,cAAqB;AAC3B,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AFvIQ,gBAAAG,OAoBE,QAAAC,aApBF;AApCR,IAAM,iBAAiB;AACvB,IAAM,eAAe;AAEd,SAAS,gBAAgB,OAAwC;AACtE,QAAM,kBAAkB,MAAM,mBAAmB;AACjD,QAAM,WAAW,MAAM,YAAY;AACnC,QAAM,QAAQ,MAAM,SAAS;AAE7B,QAAM,SAAS,cAAc;AAC7B,QAAM,WAAW,cAAc;AAC/B,QAAM,QAAQ,aAAa;AAC3B,QAAM,WAAW,OAAO;AAExB,QAAM,CAAC,UAAU,WAAW,IAAIC,UAAS,eAAe;AACxD,QAAM,UAAUC,SAAgC,MAAM;AACpD,QAAI,SAAS,UAAU,SAAU,QAAO;AACxC,WAAO,SAAS,MAAM,SAAS,SAAS,QAAQ;AAAA,EAClD,GAAG,CAAC,UAAU,QAAQ,CAAC;AACvB,QAAM,gBAAgB,SAAS,SAAS,QAAQ;AAEhD,QAAM,QAAQ,iBAAiB,SAAS,UAAU;AAClD,QAAM,EAAE,gBAAgB,IAAI;AAK5B,QAAM,WAAW,OAAO,SAAS;AACjC,EAAAC,WAAU,MAAM;AACd,oBAAgB;AAAA,EAClB,GAAG,CAAC,UAAU,eAAe,CAAC;AAE9B,QAAM,eAAe,wBAAwB,UAAU,OAAO,cAAc;AAE5E,SACE,gBAAAH,MAAC,SAAI,WAAW,UAAU,qBAAqB,MAAM,SAAS,GAC3D;AAAA,WAAO,aAAa,OAAO,oBAAoB,OAC9C,gBAAAD,MAAC,OAAE,WAAU,sBAAqB,MAAK,QACpC,iBAAO,iBACV,IACE;AAAA,IAEJ,gBAAAC;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,KAAK,MAAM;AAAA,QACX,MAAK;AAAA,QACL,aAAU;AAAA,QACV,iBAAc;AAAA,QACd,cAAY;AAAA,QACZ,eAAa,MAAM;AAAA,QACnB,UAAU;AAAA,QAET;AAAA,gBAAM,oBAAoB;AAAA,UAE1B,SAAS,WAAW,IACnB,YAAY,MAAM,KAAK,IAEvB,gBAAAA,MAAC,QAAG,WAAU,qBACX;AAAA,4BAAgB,IACf,gBAAAD,MAAC,QAAG,WAAU,oBACZ,0BAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,SAAS,MAAM,YAAY,CAAC,UAAU,QAAQ,QAAQ;AAAA,gBAErD,oCAA0B,aAAa;AAAA;AAAA,YAC1C,GACF,IACE;AAAA,YACH,QAAQ,IAAI,CAAC,SAAS,UAAU;AAU/B,oBAAM,YACJ,QAAQ,KAAK,QAAQ,QAAQ,CAAC,GAAG,SAAS,QAAQ;AACpD,qBACE,gBAAAA,MAAC,QAAqB,GAAI,YAAY,EAAE,kBAAkB,OAAO,IAAI,CAAC,GACpE,0BAAAA,MAAC,mBAAgB,WAAW,QAAQ,IAAI,WAAsB,KADvD,QAAQ,EAEjB;AAAA,YAEJ,CAAC;AAAA,aACH;AAAA,UAGD,MAAM,oBAAoB;AAAA;AAAA;AAAA,IAC7B;AAAA,IAIA,gBAAAA,MAAC,OAAE,WAAU,oBAAmB,MAAK,UAAS,aAAU,UACrD,wBACH;AAAA,IAEC,MAAM,iBACL,gBAAAA,MAAC,SAAI,WAAU,iBACb,0BAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,gBAAAA,MAAC,kBAAe;AAAA,QACtB,WAAU;AAAA,QACV,SAAS;AAAA,QACT,SAAS,MAAM;AAAA;AAAA,IACjB,GACF,IACE;AAAA,KACN;AAEJ;AAEA,SAAS,UAAU,MAAc,OAAmC;AAClE,SAAO,UAAU,UAAa,UAAU,KAAK,OAAO,GAAG,IAAI,IAAI,KAAK;AACtE;AAEA,SAAS,YAAY,MAA4D;AAC/E,MAAI,OAAO,SAAS,WAAY,QAAO,KAAK;AAC5C,MAAI,SAAS,OAAW,QAAO;AAC/B,SAAO,gBAAAA,MAAC,kBAAe;AACzB;AAGO,SAAS,eAAe,OAGjB;AACZ,SACE,gBAAAC,MAAC,SAAI,WAAU,iBACb;AAAA,oBAAAD,MAAC,UAAK,WAAU,wBAAwB,gBAAM,SAAS,0BAAyB;AAAA,IAChF,gBAAAA,MAAC,UAAM,gBAAM,QAAQ,kEAAiE;AAAA,KACxF;AAEJ;AAeA,IAAM,cAAc;AAab,SAAS,wBACd,UACA,gBACQ;AACR,QAAM,CAAC,WAAW,YAAY,IAAIK,UAAS,EAAE;AAC7C,QAAM,eAAeC,QAAO,CAAC;AAE7B,QAAM,OAAO,SAAS,SAAS,SAAS,CAAC;AACzC,QAAM,cAAc,SAAS,UAAa,KAAK,SAAS;AACxD,QAAM,OAAO,cAAc,cAAc,YAAY,IAAI,CAAC,IAAI;AAC9D,QAAM,OAAO,eAAe,KAAK,WAAW,eAAe,KAAK,WAAW;AAC3E,QAAM,YAAY,MAAM,MAAM;AAE9B,QAAM,QAAQA,QAAO,SAAS;AAC9B,MAAI,MAAM,YAAY,WAAW;AAC/B,UAAM,UAAU;AAChB,iBAAa,UAAU;AAAA,EACzB;AAEA,QAAM,kBAAkBA,QAAO,cAAc;AAC7C,MAAI,gBAAgB,YAAY,gBAAgB;AAI9C,UAAM,WAAW,gBAAgB,YAAY;AAC7C,oBAAgB,UAAU;AAC1B,QAAI,UAAU;AACZ,YAAM,UAAU;AAChB,mBAAa,UAAU,KAAK;AAC5B,UAAI,cAAc,GAAI,cAAa,EAAE;AAAA,IACvC;AAAA,EACF;AAEA,EAAAC,WAAU,MAAM;AACd,QAAI,CAAC,YAAa;AAClB,gBAAY,YAAY;AACxB,QAAI,WAAW;AACf,QAAI,QAAgC,YAAY,KAAK,IAAI;AACzD,WAAO,UAAU,MAAM;AACrB,iBAAW,MAAM,QAAQ,MAAM,CAAC,EAAE;AAClC,cAAQ,YAAY,KAAK,IAAI;AAAA,IAC/B;AACA,UAAM,OAAO,OAAO,KAAK,SAAS;AAClC,QAAI,QAAQ,aAAa,QAAS;AAClC,UAAM,OAAO,KAAK,MAAM,aAAa,SAAS,IAAI,EAAE,KAAK;AACzD,iBAAa,UAAU;AACvB,QAAI,SAAS,GAAI,cAAa,IAAI;AAAA,EACpC,GAAG,CAAC,MAAM,MAAM,WAAW,CAAC;AAE5B,SAAO;AACT;;;AOvOA;AAAA,EACE,eAAAC;AAAA,EACA,aAAAC;AAAA,EACA;AAAA,EACA,UAAAC;AAAA,EACA,YAAAC;AAAA,OAKK;;;ACtBP,SAAS,kBAAAC,uBAAsB;;;ACP3B,gBAAAC,aAAA;AAFJ,SAAS,MAAM,UAAgC;AAC7C,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN,QAAO;AAAA,MACP,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,QAAO;AAAA,MACP,aAAY;AAAA,MACZ,eAAc;AAAA,MACd,gBAAe;AAAA,MACf,eAAY;AAAA,MACZ,WAAU;AAAA,MAET;AAAA;AAAA,EACH;AAEJ;AAEO,SAAS,YAAuB;AACrC,SAAO,MAAM,gBAAAA,MAAC,UAAK,GAAE,uCAAsC,CAAE;AAC/D;AAEO,SAAS,YAAuB;AACrC,SAAO,MAAM,gBAAAA,MAAC,UAAK,GAAE,QAAO,GAAE,QAAO,OAAM,OAAM,QAAO,OAAM,IAAG,OAAM,MAAK,gBAAe,QAAO,QAAO,CAAE;AAC7G;AAEO,SAAS,cAAyB;AACvC,SAAO;AAAA,IACL,gBAAAA,MAAC,UAAK,GAAE,uGAAsG;AAAA,EAChH;AACF;AAEO,SAAS,cAAyB;AACvC,SAAO,MAAM,gBAAAA,MAAC,UAAK,GAAE,8BAA6B,CAAE;AACtD;;;ADZQ,SAKE,OAAAC,OALF,QAAAC,aAAA;AAJD,SAAS,oBAAoB,OAA4C;AAC9E,SACE,gBAAAD,MAAC,QAAG,WAAU,uBAAsB,cAAW,+BAC5C,gBAAM,YAAY,IAAI,CAAC,eACtB,gBAAAC;AAAA,IAAC;AAAA;AAAA,MAEC,WAAU;AAAA,MACV,eAAa,WAAW;AAAA,MAExB;AAAA,wBAAAD,MAAC,UAAK,WAAU,4BAA2B,OAAO,WAAW,MAC1D,qBAAW,MACd;AAAA,QACA,gBAAAA,MAAC,UAAK,WAAU,4BACb,qBAAW,WAAW,cACnB,WAAW,aAAa,OACtB,oBACA,aAAa,KAAK,MAAM,WAAW,WAAW,GAAG,CAAC,MACpD,WAAW,WAAW,WACpB,iBACAE,gBAAe,WAAW,SAAS,GAC3C;AAAA,QAEC,WAAW,WAAW,cACrB,gBAAAF;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,MAAK;AAAA,YACL,cAAY,aAAa,WAAW,IAAI;AAAA,YACvC,GAAI,WAAW,aAAa,OACzB,CAAC,IACD;AAAA,cACE,iBAAiB,KAAK,MAAM,WAAW,WAAW,GAAG;AAAA,cACrD,iBAAiB;AAAA,cACjB,iBAAiB;AAAA,YACnB;AAAA,YACJ,sBAAoB,WAAW,aAAa,QAAQ;AAAA,YAEpD,0BAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,WAAU;AAAA,gBACV,OACE,WAAW,aAAa,OACpB,SACA,EAAE,OAAO,GAAG,KAAK,MAAM,WAAW,WAAW,GAAG,CAAC,IAAI;AAAA;AAAA,YAE7D;AAAA;AAAA,QACF,IACE;AAAA,QAEH,WAAW,YAAY,OAAO,OAC7B,gBAAAA,MAAC,UAAK,WAAU,+BAA+B,qBAAW,SAAQ;AAAA,QAGnE,WAAW,WAAW,YAAY,WAAW,YAC5C,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,WAAU;AAAA,YACV,SAAS,MAAM,MAAM,QAAQ,WAAW,GAAG;AAAA,YAC5C;AAAA;AAAA,QAED,IACE;AAAA,QAEJ,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,WAAU;AAAA,YACV,cAAY,UAAU,WAAW,IAAI;AAAA,YACrC,OAAO,UAAU,WAAW,IAAI;AAAA,YAChC,SAAS,MAAM,MAAM,SAAS,WAAW,GAAG;AAAA,YAE5C,0BAAAA,MAAC,eAAY;AAAA;AAAA,QACf;AAAA;AAAA;AAAA,IAhEK,WAAW;AAAA,EAiElB,CACD,GACH;AAEJ;;;AE1CI,SASQ,OAAAG,OATR,QAAAC,cAAA;AAnBG,SAAS,gBAAgB,OAAwC;AACtE,QAAM,QAAQ,aAAa;AAC3B,QAAM,EAAE,SAAS,IAAI,aAAa;AAClC,QAAM,EAAE,OAAO,IAAI,eAAe;AAElC,QAAM,WAAW,OAAO,YAAY;AACpC,QAAM,QACJ,aAAa,QAAQ,MAAM,QAAQ,MAAM,WACrC,CAAC,IACD,SAAS,MAAM;AAAA,IACb,gBAAgB,SAAS;AAAA,IACzB,MAAM,MAAM;AAAA,IACZ,OAAO,SAAS,SAAS,WAAW;AAAA,EACtC,CAAC;AAEP,QAAM,WAAW,MAAM;AACvB,MAAI,aAAa,UAAa,MAAM,WAAW,EAAG,QAAO;AAEzD,SACE,gBAAAA,OAAC,SAAI,WAAU,wBACZ;AAAA;AAAA,IACA,MAAM,WAAW,IAAI,OACpB,gBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,MAAK;AAAA,QACL,cAAW;AAAA,QAEV,gBAAM,IAAI,CAAC,SACV,gBAAAA;AAAA,UAAC;AAAA;AAAA,YAEC,MAAK;AAAA,YACL,WAAU;AAAA,YACV,iBAAe,KAAK;AAAA,YACpB,SAAS,MAAM,MAAM,OAAO,KAAK,IAAI;AAAA,YAEpC,eAAK;AAAA;AAAA,UAND,KAAK;AAAA,QAOZ,CACD;AAAA;AAAA,IACH;AAAA,KAEJ;AAEJ;;;AC7DA,SAAS,iBAAAE,gBAAe,cAAAC,aAAY,WAAAC,gBAA+B;AAkC/D,gBAAAC,aAAA;AAtBJ,IAAM,uBAAuBC,eAAwC,IAAI;AAQlE,SAAS,qBAAqB,OAA6C;AAIhF,QAAM,YAAY,qBAAqB;AACvC,QAAM,EAAE,OAAO,MAAM,IAAI;AACzB,QAAM,QAAQC;AAAA,IACZ,OAAO;AAAA,MACL,OAAO,SAAS,UAAU;AAAA,MAC1B,OAAO,SAAS,UAAU;AAAA,IAC5B;AAAA,IACA,CAAC,OAAO,OAAO,UAAU,OAAO,UAAU,KAAK;AAAA,EACjD;AACA,SACE,gBAAAF,MAAC,qBAAqB,UAArB,EAA8B,OAC5B,gBAAM,UACT;AAEJ;AAQO,SAAS,uBAA0C;AACxD,QAAM,SAASG,YAAW,oBAAoB;AAC9C,QAAM,QAAQ,aAAa;AAC3B,SAAOD;AAAA,IACL,MAAM,UAAU,EAAE,OAAO,MAAM,OAAO,OAAO,MAAM,MAAM;AAAA,IACzD,CAAC,QAAQ,MAAM,OAAO,MAAM,KAAK;AAAA,EACnC;AACF;;;ACzCO,SAAS,mBACd,OACA,SACe;AACf,MAAI,MAAM,YAAa,QAAO;AAC9B,MAAI,MAAM,QAAQ,SAAU,QAAO,MAAM,SAAS,SAAS;AAC3D,MAAI,MAAM,QAAQ,QAAS,QAAO;AAClC,QAAM,eAAe,MAAM,WAAW,MAAM;AAC5C,QAAM,QACJ,YAAY,UAAU,CAAC,MAAM,YAAY,CAAC,eAAe;AAC3D,SAAO,QAAQ,SAAS;AAC1B;;;AChBA;AAAA,EACE,eAAAE;AAAA,EACA;AAAA,EACA,UAAAC;AAAA,OAMK;AAuFH,gBAAAC,aAAA;AApFG,SAAS,eACd,eACA,YACA,QACQ;AACR,MAAI,CAAC,OAAO,SAAS,UAAU,KAAK,cAAc,EAAG,QAAO;AAC5D,QAAM,aAAa,OAAO,SAAS,MAAM,KAAK,SAAS,IAAI,SAAS;AACpE,QAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,OAAO,gBAAgB,cAAc,UAAU,CAAC;AAC/E,SAAO,QAAQ,aAAa;AAC9B;AAkBO,SAAS,kBAAkB,OAA0C;AAC1E,QAAM,WAAWD,QAAmC,IAAI;AAExD,QAAM,SAASD,aAAY,MAAM;AAC/B,UAAM,UAAU,SAAS;AACzB,QAAI,YAAY,KAAM;AAEtB,UAAM,cAAc,QAAQ;AAC5B,UAAM,YAAY,QAAQ;AAC1B,UAAM,WAAW,iBAAiB,OAAO;AACzC,UAAM,aAAa,OAAO,WAAW,SAAS,UAAU;AACxD,UAAM,SACJ,OAAO,WAAW,SAAS,cAAc,GAAG,IAC5C,OAAO,WAAW,SAAS,iBAAiB,GAAG,IAC/C,OAAO,WAAW,SAAS,kBAAkB,GAAG,IAChD,OAAO,WAAW,SAAS,qBAAqB,GAAG;AAIrD,YAAQ,MAAM,qBAAqB;AACnC,YAAQ,MAAM,SAAS;AACvB,UAAM,gBAAgB,QAAQ;AAC9B,YAAQ,MAAM,SAAS,GAAG,WAAW;AAErC,SAAK,QAAQ;AACb,YAAQ,MAAM,qBAAqB;AACnC,YAAQ,YAAY;AAEpB,UAAM,UAAU,eAAe,eAAe,YAAY,MAAM;AAChE,UAAM,UAAU,KAAK;AAAA,MACnB,MAAM;AAAA,MACN,KAAK,IAAI,SAAS,MAAM,WAAW;AAAA,IACrC;AAEA,QAAI,YAAY,YAAa;AAC7B,YAAQ,MAAM,SAAS,GAAG,OAAO;AAAA,EACnC,GAAG,CAAC,MAAM,aAAa,MAAM,WAAW,CAAC;AAEzC,kBAAgB,MAAM;AACpB,WAAO;AAAA,EACT,GAAG,CAAC,QAAQ,MAAM,KAAK,CAAC;AAExB,QAAM,SAASA;AAAA,IACb,CAAC,SAAqC;AACpC,eAAS,UAAU;AACnB,YAAM,YAAY,MAAM;AACxB,UAAI,OAAO,cAAc,WAAY,WAAU,IAAI;AAAA,eAC1C,cAAc,QAAQ,cAAc,QAAW;AACtD,QAAC,UAAsD,UAAU;AAAA,MACnE;AAAA,IACF;AAAA,IACA,CAAC,MAAM,QAAQ;AAAA,EACjB;AAEA,QAAM,QAAuB,EAAE,WAAW,MAAM,YAAY;AAE5D,SACE,gBAAAE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,WAAU;AAAA,MACV,OAAO,MAAM;AAAA,MACb,MAAM;AAAA,MACN;AAAA,MACA,aAAa,MAAM;AAAA,MACnB,cAAY,MAAM;AAAA,MACjB,GAAI,MAAM,gBAAgB,SAAY,CAAC,IAAI,EAAE,oBAAoB,MAAM,YAAY;AAAA,MACpF,UAAU,MAAM;AAAA,MAChB,WAAW,MAAM,cAAc;AAAA,MAC/B,UAAU,CAAC,UAAU,MAAM,cAAc,MAAM,OAAO,KAAK;AAAA,MAC3D,WAAW,MAAM;AAAA,MAChB,GAAI,MAAM,YAAY,SAAY,CAAC,IAAI,EAAE,SAAS,MAAM,QAAQ;AAAA;AAAA,EACnE;AAEJ;;;AC5HA,SAAS,eAAAC,cAAa,aAAAC,YAAW,UAAAC,SAAQ,YAAAC,iBAAgB;AAgDlD,SAAS,mBAAmB,SAIN;AAC3B,QAAM,EAAE,OAAO,UAAU,SAAS,IAAI;AACtC,QAAM,CAAC,aAAa,cAAc,IAAIC,UAA2B,CAAC,CAAC;AACnE,QAAM,UAAUC,QAAO,oBAAI,IAAqB,CAAC;AAIjD,QAAM,gBAAgBA,QAAO,oBAAI,IAAkB,CAAC;AACpD,QAAM,UAAUA,QAAO,IAAI;AAE3B,EAAAC,WAAU,MAAM;AACd,YAAQ,UAAU;AAClB,WAAO,MAAM;AACX,cAAQ,UAAU;AAGlB,iBAAW,SAAS,QAAQ,QAAQ,OAAO,EAAG,OAAM,WAAW,MAAM;AACrE,cAAQ,QAAQ,MAAM;AAAA,IACxB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,QAAQC,aAAY,CAAC,KAAa,SAAkC;AACxE,QAAI,CAAC,QAAQ,QAAS;AACtB;AAAA,MAAe,CAAC,YACd,QAAQ,IAAI,CAAC,SAAU,KAAK,QAAQ,MAAM,EAAE,GAAG,MAAM,GAAG,KAAK,IAAI,IAAK;AAAA,IACxE;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,MAAMA;AAAA,IACV,CAAC,KAAa,SAAe;AAC3B,YAAM,aAAa,IAAI,gBAAgB;AACvC,cAAQ,QAAQ,IAAI,KAAK,EAAE,MAAM,WAAW,CAAC;AAC7C,oBAAc,QAAQ,IAAI,KAAK,IAAI;AACnC,YAAM,KAAK,EAAE,QAAQ,aAAa,UAAU,MAAM,SAAS,KAAK,CAAC;AAEjE,YACG,OAAO,MAAM;AAAA,QACZ,QAAQ,WAAW;AAAA,QACnB,YAAY,CAAC,aAAa;AACxB,cAAI,WAAW,OAAO,QAAS;AAC/B,gBAAM,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,QAAQ,CAAC,EAAE,CAAC;AAAA,QAC7D;AAAA,MACF,CAAC,EACA,KAAK,CAAC,aAA+B;AACpC,gBAAQ,QAAQ,OAAO,GAAG;AAC1B,YAAI,WAAW,OAAO,QAAS;AAC/B,cAAM,KAAK;AAAA,UACT,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,QAAQ,SAAS;AAAA,UACjB,YAAY,SAAS,cAAc;AAAA,UACnC,WAAW,SAAS;AAAA,UACpB,SAAS;AAAA,QACX,CAAC;AAAA,MACH,CAAC,EACA,MAAM,CAAC,UAAmB;AACzB,gBAAQ,QAAQ,OAAO,GAAG;AAC1B,YAAI,WAAW,OAAO,QAAS;AAC/B,cAAM,KAAK;AAAA,UACT,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,SACE,iBAAiB,SAAS,MAAM,YAAY,KACxC,MAAM,UACN;AAAA,UACN,WAAW;AAAA,QACb,CAAC;AAAA,MACH,CAAC;AAAA,IACL;AAAA,IACA,CAAC,OAAO,KAAK;AAAA,EACf;AAEA,QAAM,MAAMA;AAAA,IACV,CAAC,aAA8B;AAC7B,UAAI,SAAS,WAAW,EAAG;AAC3B,YAAM,UAA4B,CAAC;AACnC,YAAM,YAA2C,CAAC;AAElD,iBAAW,QAAQ,UAAU;AAC3B,cAAM,MAAM,WAAW,KAAK;AAC5B,YAAI,KAAK,OAAO,UAAU;AAGxB,gBAAM,WAAW,iBAAiB,KAAK,MAAM,QAAQ;AACrD,kBAAQ,KAAK;AAAA,YACX;AAAA,YACA,MAAM,KAAK;AAAA,YACX,WAAW,KAAK;AAAA,YAChB,WAAW,KAAK,SAAS,KAAK,OAAO,KAAK;AAAA,YAC1C,QAAQ;AAAA,YACR,UAAU;AAAA,YACV,QAAQ;AAAA,YACR,YAAY;AAAA,YACZ,SAAS;AAAA,YACT,WAAW;AAAA,UACb,CAAC;AACD,mBAAS,OAAO;AAAA,YACd,OAAO;AAAA,YACP,SAAS;AAAA,YACT,QAAQ;AAAA,UACV,CAAC;AACD;AAAA,QACF;AACA,gBAAQ,KAAK;AAAA,UACX;AAAA,UACA,MAAM,KAAK;AAAA,UACX,WAAW,KAAK;AAAA,UAChB,WAAW,KAAK,SAAS,KAAK,OAAO,KAAK;AAAA,UAC1C,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,QAAQ;AAAA,UACR,YAAY;AAAA,UACZ,SAAS;AAAA,UACT,WAAW;AAAA,QACb,CAAC;AACD,kBAAU,KAAK,EAAE,KAAK,KAAK,CAAC;AAAA,MAC9B;AAEA,qBAAe,CAAC,YAAY,CAAC,GAAG,SAAS,GAAG,OAAO,CAAC;AACpD,iBAAW,SAAS,UAAW,KAAI,MAAM,KAAK,MAAM,IAAI;AAAA,IAC1D;AAAA,IACA,CAAC,UAAU,UAAU,GAAG;AAAA,EAC1B;AAEA,QAAM,SAASA,aAAY,CAAC,QAAgB;AAC1C,UAAM,QAAQ,QAAQ,QAAQ,IAAI,GAAG;AACrC,QAAI,UAAU,QAAW;AACvB,YAAM,WAAW,MAAM;AACvB,cAAQ,QAAQ,OAAO,GAAG;AAAA,IAC5B;AACA,kBAAc,QAAQ,OAAO,GAAG;AAChC,mBAAe,CAAC,YAAY,QAAQ,OAAO,CAAC,SAAS,KAAK,QAAQ,GAAG,CAAC;AAAA,EACxE,GAAG,CAAC,CAAC;AAEL,QAAM,QAAQA;AAAA,IACZ,CAAC,QAAgB;AACf,YAAM,OAAO,cAAc,QAAQ,IAAI,GAAG;AAC1C,UAAI,SAAS,OAAW;AACxB,UAAI,KAAK,IAAI;AAAA,IACf;AAAA,IACA,CAAC,GAAG;AAAA,EACN;AAEA,QAAM,QAAQA,aAAY,MAAM;AAC9B,eAAW,SAAS,QAAQ,QAAQ,OAAO,EAAG,OAAM,WAAW,MAAM;AACrE,YAAQ,QAAQ,MAAM;AACtB,kBAAc,QAAQ,MAAM;AAC5B,mBAAe,CAAC,CAAC;AAAA,EACnB,GAAG,CAAC,CAAC;AAEL,SAAO;AAAA,IACL;AAAA,IACA,cAAc,YACX,OAAO,CAAC,SAAS,KAAK,WAAW,WAAW,KAAK,WAAW,IAAI,EAChE,IAAI,CAAC,SAAS,KAAK,MAAgB;AAAA,IACtC,WAAW,YAAY,KAAK,CAAC,SAAS,KAAK,WAAW,WAAW;AAAA,IACjE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AP2EM,gBAAAC,OAoCQ,QAAAC,cApCR;AA1PN,IAAM,sBAAsB;AAC5B,IAAM,sBAAsB;AAmB5B,SAAS,aAAa,OAAgC;AACpD,QAAM,QAAQ,MAAM,cAAc;AAClC,MAAI,UAAU,OAAW,QAAO;AAChC,SAAO,MAAM,KAAK,KAAK,EAAE,SAAS,OAAO;AAC3C;AAEO,SAAS,aAAa,OAAqC;AAChE,QAAM,WAAW,qBAAqB;AACtC,QAAM,WAAW,cAAc;AAC/B,QAAM,EAAE,SAAS,IAAI,aAAa;AAClC,QAAM,QAAQ,aAAa;AAC3B,QAAM,EAAE,OAAO,MAAM,IAAI,qBAAqB;AAI9C,QAAM,EAAE,OAAO,IAAI,eAAe;AAClC,QAAM,WAAWC,QAAmC,IAAI;AACxD,QAAM,WAAW,MAAM;AACvB,QAAM,CAAC,WAAW,YAAY,IAAIC,UAAS,KAAK;AAChD,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAS,KAAK;AAC9C,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,KAAK;AACpD,QAAM,YAAYD,QAAO,CAAC;AAG1B,QAAM,UAAUA,QAA4B,YAAY,MAAS;AAEjE,QAAM,cAAc,mBAAmB;AAAA,IACrC;AAAA,IACA,UAAU,SAAS;AAAA,IACnB;AAAA,EACF,CAAC;AAGD,QAAM,WAAWA,QAAsB,IAAI;AAC3C,EAAAE,WAAU,MAAM;AACd,UAAM,OAAO,MAAM;AACnB,QAAI,SAAS,QAAQ,SAAS,UAAa,SAAS,GAAI;AACxD,QAAI,SAAS,YAAY,KAAM;AAC/B,aAAS,UAAU;AACnB,QAAI,SAAS,KAAK,KAAK,MAAM,GAAI;AACjC,aAAS,QAAQ,IAAI;AAAA,EAIvB,GAAG,CAAC,MAAM,SAAS,CAAC;AAGpB,QAAM,YAAYC;AAAA,IAChB,CAAC,aAA8B;AAC7B,UAAI,SAAS,WAAW,EAAG;AAC3B,UAAI,CAAC,MAAM,WAAW;AAGpB,aAAK,MACF,OAAO,SAAS,CAAC,GAAW;AAAA,UAC3B,QAAQ,IAAI,gBAAgB,EAAE;AAAA,UAC9B,YAAY,MAAM;AAAA,QACpB,CAAC,EACA,MAAM,MAAM,MAAS;AACxB,uBAAe,IAAI;AACnB;AAAA,MACF;AACA,kBAAY,IAAI,QAAQ;AAAA,IAC1B;AAAA,IACA,CAAC,aAAa,KAAK;AAAA,EACrB;AAEA,QAAM,UAAUA;AAAA,IACd,CAAC,UAAoD;AACnD,YAAM,QAAQ,MAAM,eAAe;AACnC,UAAI,UAAU,UAAa,MAAM,WAAW,EAAG;AAG/C,YAAM,eAAe;AACrB,gBAAU,MAAM,KAAK,KAAK,CAAC;AAAA,IAC7B;AAAA,IACA,CAAC,SAAS;AAAA,EACZ;AAEA,QAAM,SAASA;AAAA,IACb,CAAC,UAA2C;AAC1C,UAAI,CAAC,aAAa,KAAK,EAAG;AAC1B,YAAM,eAAe;AACrB,gBAAU,UAAU;AACpB,kBAAY,KAAK;AACjB,gBAAU,MAAM,KAAK,MAAM,aAAa,KAAK,CAAC;AAAA,IAChD;AAAA,IACA,CAAC,SAAS;AAAA,EACZ;AAGA,QAAM,YAAYA;AAAA,IAChB,CAAC,UAAmD;AAKlD,YAAM,YAAY,MAAM,YAAY;AAEpC,UACE,CAAC,aACD,MAAM,QAAQ,aACd,SAAS,SAAS,MAClB,CAAC,MAAM,YACP,CAAC,MAAM,WACP,CAAC,MAAM,SACP;AACA,cAAM,WAAW,OAAO,YAAY,EAAE;AACtC,iBAAS,SAAS,SAAS,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG;AAC/D,gBAAM,YAAY,SAAS,MAAM;AACjC,cAAI,cAAc,UAAa,UAAU,SAAS,OAAQ;AAC1D,gBAAM,OAAO,YAAY,SAAS;AAClC,cAAI,SAAS,GAAI;AACjB,gBAAM,eAAe;AACrB,mBAAS,QAAQ,IAAI;AACrB;AAAA,QACF;AACA;AAAA,MACF;AAEA,YAAM,SAAS;AAAA,QACb;AAAA,UACE,KAAK,MAAM;AAAA,UACX,UAAU,MAAM;AAAA,UAChB,SAAS,MAAM;AAAA,UACf,SAAS,MAAM;AAAA,UACf,aAAa;AAAA,UACb,QAAQ,SAAS;AAAA,QACnB;AAAA,QACA,SAAS;AAAA,MACX;AACA,UAAI,WAAW,QAAQ;AACrB,cAAM,eAAe;AACrB,aAAK,QAAQ,QAAQ;AACrB;AAAA,MACF;AACA,UAAI,WAAW,QAAQ;AACrB,cAAM,eAAe;AACrB,aAAK,SAAS,KAAK;AAAA,MACrB;AAAA,IACF;AAAA,IACA,CAAC,UAAU,SAAS,SAAS,MAAM;AAAA,EACrC;AAGA,QAAM,OAAO,MAAM,QAAQ;AAC3B,QAAM,gBACJ,SAAS,OACL,KAAK,SACL,YACE,2CACA,YAAY,YACV,oCACA;AAEV,QAAM,UAAU,SAAS,WAAW,kBAAkB;AAEtD,QAAM,SAASA,aAAY,YAAY;AACrC,QAAI,CAAC,QAAS;AACd,UAAM,OAAO,SAAS,KAAK,KAAK;AAChC,UAAM,MAAM,YAAY;AACxB,QAAI,IAAI,WAAW,GAAG;AACpB,YAAM,SAAS,KAAK;AAAA,IACtB,OAAO;AACL,eAAS,QAAQ,EAAE;AACnB,YAAM,SAAS,MAAM,OAAO,KAAK,EAAE,MAAM,MAAM,SAAS,IAAI,CAAC;AAC7D,UAAI,CAAC,OAAO,SAAU,UAAS,QAAQ,IAAI;AAAA,UACtC,aAAY,MAAM;AAAA,IACzB;AAGA,aAAS,SAAS,MAAM;AAAA,EAC1B,GAAG,CAAC,aAAa,SAAS,UAAU,MAAM,CAAC;AAC3C,UAAQ,UAAU;AAElB,QAAM,WAAWA;AAAA,IACf,CAAC,UAAsC;AACrC,YAAM,eAAe;AACrB,WAAK,OAAO;AAAA,IACd;AAAA,IACA,CAAC,MAAM;AAAA,EACT;AAEA,QAAM,YAAY,SAAS,SACvB,oBACA,kBAAkB,OAChB,gBACA;AAGN,QAAM,WAAW,SAAS,UAAU,SAAS,iBAAiB,SAAS,KAAK,KAAK,MAAM;AACvF,QAAM,aACJ,kBAAkB,OAAO,gBAAgB;AAE3C,QAAM,eAAe;AAAA,IACnB,OAAO,SAAS;AAAA,IAChB,eAAe,SAAS;AAAA,IACxB,MAAM,SAAS;AAAA,IACf,cAAc;AAAA,IACd,WAAW;AAAA,EACb;AAEA,SACE,gBAAAJ;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,mBAAmB,MAAM,cAAc,SAAY,KAAK,IAAI,MAAM,SAAS,EAAE;AAAA,MACxF,iBAAe,YAAY;AAAA,MAC3B,aAAW,SAAS,UAAU;AAAA,MAC9B;AAAA,MACA,aAAa,CAAC,UAAU;AACtB,YAAI,CAAC,aAAa,KAAK,EAAG;AAC1B,kBAAU,WAAW;AACrB,oBAAY,IAAI;AAAA,MAClB;AAAA,MACA,YAAY,CAAC,UAAU;AACrB,YAAI,aAAa,KAAK,EAAG,OAAM,eAAe;AAAA,MAChD;AAAA,MACA,aAAa,CAAC,UAAU;AACtB,YAAI,CAAC,aAAa,KAAK,EAAG;AAG1B,kBAAU,UAAU,KAAK,IAAI,GAAG,UAAU,UAAU,CAAC;AACrD,YAAI,UAAU,YAAY,EAAG,aAAY,KAAK;AAAA,MAChD;AAAA,MACA;AAAA,MAEC;AAAA,cAAM;AAAA,QAKP,gBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,QAAQ,SAAS;AAAA,YACjB,MAAM,SAAS;AAAA,YACf,UAAU,SAAS,KAAK,KAAK,MAAM;AAAA;AAAA,QACrC;AAAA,QAGA,gBAAAC,OAAC,SAAI,WAAU,2BACZ;AAAA,sBAAY,YAAY,SAAS,IAChC,gBAAAD;AAAA,YAAC;AAAA;AAAA,cACC,aAAa,YAAY;AAAA,cACzB,UAAU,YAAY;AAAA,cACtB,SAAS,YAAY;AAAA;AAAA,UACvB,IACE;AAAA,UAEJ,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,SAAS;AAAA,cAChB,eAAe,SAAS;AAAA,cACxB;AAAA,cACA;AAAA,cACA,aAAa,MAAM,eAAe;AAAA,cAClC,aAAa;AAAA,cACb,aAAa;AAAA,cACb,UAAU,SAAS;AAAA,cACnB,OAAM;AAAA,cACN,aAAa;AAAA,cACb,UAAU;AAAA,cACT,GAAI,MAAM,cAAc,SAAY,CAAC,IAAI,EAAE,WAAW,MAAM,UAAU;AAAA;AAAA,UACzE;AAAA,UAEA,gBAAAC,OAAC,SAAI,WAAU,yBACb;AAAA,4BAAAA,OAAC,SAAI,WAAU,0BAGZ;AAAA,oBAAM,YACL,gBAAAA,OAAC,WAAM,WAAU,mDACf;AAAA,gCAAAD,MAAC,eAAY;AAAA,gBACb,gBAAAA,MAAC,UAAK,WAAU,oBAAmB,0BAAY;AAAA,gBAC/C,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,UAAQ;AAAA,oBACR,cAAW;AAAA,oBACV,GAAI,MAAM,WAAW,SAAY,CAAC,IAAI,EAAE,QAAQ,MAAM,OAAO;AAAA,oBAC9D,UAAU,CAAC,UAAU;AACnB,4BAAM,OAAO,MAAM,OAAO;AAC1B,0BAAI,SAAS,KAAM,WAAU,MAAM,KAAK,IAAI,CAAC;AAC7C,4BAAM,OAAO,QAAQ;AAAA,oBACvB;AAAA;AAAA,gBACF;AAAA,iBACF,IACE;AAAA,cAEH,UAAU,OAAO,OAAO,MAAM,aAAa,YAAY;AAAA,cACvD,MAAM;AAAA,cACN,MAAM;AAAA,eACT;AAAA,YAEA,gBAAAC,OAAC,SAAI,WAAU,2BACZ;AAAA,oBAAM;AAAA,cAGN,WACC,gBAAAD;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,WAAU;AAAA,kBACV,aAAU;AAAA,kBACV,cAAY;AAAA,kBACZ,OAAO;AAAA,kBACP,UAAU,CAAC;AAAA,kBAEX,0BAAAA,MAAC,aAAU;AAAA;AAAA,cACb,IACE;AAAA,cACH,SAAS,SACR,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,WAAU;AAAA,kBACV,aAAU;AAAA,kBACV,cAAY;AAAA,kBACZ,OAAO;AAAA,kBACP,SAAS,MAAM,KAAK,SAAS,KAAK;AAAA,kBAElC,0BAAAA,MAAC,aAAU;AAAA;AAAA,cACb,IAEA,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,WAAU;AAAA,kBACV,aAAU;AAAA,kBACV,cAAY;AAAA,kBACZ,OAAO;AAAA,kBACP,UAAU,CAAC;AAAA,kBAEX,0BAAAA,MAAC,aAAU;AAAA;AAAA,cACb;AAAA,eAEJ;AAAA,aACF;AAAA,WACF;AAAA,QAKA,gBAAAC,OAAC,SAAI,WAAU,4BAA2B,IAAI,UAAU,MAAK,UAAS,aAAU,UAC7E;AAAA,mBAAS,OACR,gBAAAD,MAAC,UAAK,WAAU,mBAAmB,eAAK,QAAO,IAC7C,cACF,gBAAAA,MAAC,UAAK,WAAU,mBACb,aAAG,uBAAuB,IAAI,8BAA8B,IAC/D,IACE,kBAAkB,QAAQ,SAAS,KAAK,KAAK,MAAM,KACrD,gBAAAA,MAAC,UAAK,WAAU,mBAAmB,yBAAc,IAC/C,SAAS,sBACX,gBAAAA,MAAC,UAAK,WAAU,mBAAkB,gFAElC,IACE;AAAA,UACH,OAAO,iBAAiB,YAAY,KAAK;AAAA,WAC5C;AAAA,QAEC,MAAM;AAAA,QAEN,WACC,gBAAAA,MAAC,SAAI,WAAU,0BAAyB,aAAU,UAC/C,gBAAM,YACH,8CACA,yBACN,IACE;AAAA;AAAA;AAAA,EACN;AAEJ;;;AQ1ZA,SAAS,aAAAM,YAAW,YAAAC,kBAAoD;AAqE/D,gBAAAC,aAAA;AApCT,IAAI,cAA2C;AAC/C,SAAS,mBAAyC;AAChD,kBAAgB,OAAO,+BAA+B;AACtD,SAAO;AACT;AAOA,SAAS,UAAU,OAIL;AACZ,QAAM,CAAC,QAAQ,SAAS,IAAID,WAA6B,IAAI;AAC7D,QAAM,EAAE,QAAQ,IAAI;AACpB,EAAAD,WAAU,MAAM;AACd,QAAI,OAAO;AACX,qBAAiB,EAAE;AAAA,MACjB,CAAC,WAAW;AACV,YAAI,KAAM,WAAU,MAAM;AAAA,MAC5B;AAAA,MACA,CAAC,UAAmB;AAClB,YAAI,CAAC,KAAM;AACX,gBAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,MAChE;AAAA,IACF;AACA,WAAO,MAAM;AACX,aAAO;AAAA,IACT;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AAEZ,MAAI,WAAW,KAAM,QAAO;AAC5B,QAAM,YAAY,MAAM,KAAK,MAAM;AACnC,SAAO,gBAAAE,MAAC,aAAW,GAAG,MAAM,gBAAgB;AAC9C;AASO,SAAS,4BACd,QACA,UAAwC,CAAC,GAC1B;AACf,QAAM,UACJ,QAAQ,gBACP,CAAC,YAAoB;AACpB,YAAQ;AAAA,MACN,0LAAqL,OAAO;AAAA,IAC9L;AAAA,EACF;AAEF,SAAO;AAAA,IACL,aAAa,SAAS;AACpB,aACE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,CAAC,WAAW,OAAO;AAAA,UACzB;AAAA,UACA,gBAAgB;AAAA,YACd;AAAA,YACA,OAAO,QAAQ;AAAA,YACf,eAAe,QAAQ;AAAA,YACvB,cAAc,QAAQ;AAAA,YACtB,WAAW,QAAQ;AAAA,YACnB,GAAI,QAAQ,UAAU,SAAY,CAAC,IAAI,EAAE,OAAO,QAAQ,MAAM;AAAA,UAChE;AAAA;AAAA,MACF;AAAA,IAEJ;AAAA,IACA,eAAe,SAAS;AAGtB,aACE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,CAAC,WAAW,OAAO;AAAA,UACzB;AAAA,UACA,gBAAgB;AAAA,YACd;AAAA,YACA,OAAO,QAAQ;AAAA,YACf,eAAe,QAAQ;AAAA,YACvB,WAAW;AAAA,UACb;AAAA;AAAA,MACF;AAAA,IAEJ;AAAA,EACF;AACF;;;AChIA;AAAA,EACE;AAAA,OAEK;AAkEP,IAAM,gBAAgB;AACtB,IAAM,gBAAgB;AAYf,SAAS,oCACd,aACwB;AACxB,QAAM,SAAiC;AAAA,IACrC,IAAI;AAAA;AAAA;AAAA,IAGJ,oBAAoB;AAAA,IAEpB,MAAM,KAAK,OAAO;AAChB,YAAM,QAAQ,MAAM,SAAS;AAC7B,YAAM,UAAU,MAAM,WAAW;AACjC,YAAM,kBAAkB,YAAY;AAEpC,YAAM,OAAkC,CAAC;AACzC,UAAI,SAAS;AACb,UAAI,SAAS,MAAM,UAAU;AAC7B,UAAI,aAA4B;AAEhC,eAAS,OAAO,GAAG,OAAO,eAAe,QAAQ,GAAG;AAClD,cAAM,SAAS,MAAM,YAAY,kBAAkB;AAAA,UACjD;AAAA,UACA;AAAA,UACA,GAAI,WAAW,UAAa,WAAW,OAAO,CAAC,IAAI,EAAE,OAAO;AAAA,UAC5D,GAAI,MAAM,cAAc,SAAY,CAAC,IAAI,EAAE,WAAW,MAAM,UAAU;AAAA,UACtE,GAAI,MAAM,kBAAkB,SACxB,CAAC,IACD,EAAE,eAAe,MAAM,cAAc;AAAA,QAC3C,CAAC;AAGD,kBAAU,OAAO;AAEjB,mBAAW,gBAAgB,OAAO,eAAe;AAC/C,cAAI,YAAY,cAAc,CAAC,aAAa,UAAU;AACpD,sBAAU;AACV;AAAA,UACF;AACA,cAAI,KAAK,WAAW,MAAO;AAC3B,eAAK,KAAK,YAAY;AAAA,QACxB;AAEA,qBAAa,OAAO;AACpB,YAAI,KAAK,UAAU,SAAS,OAAO,eAAe,KAAM;AACxD,iBAAS,OAAO;AAAA,MAClB;AAEA,aAAO,EAAE,eAAe,MAAM,gBAAgB,QAAQ,WAAW;AAAA,IACnE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,OAAO,gBAAgB,OAAO;AAC5B,aAAO,YAAY,OAAO,gBAAgB,KAAK;AAAA,IACjD;AAAA,IAEA,YAAY,gBAAgB,UAAU;AACpC,aAAO,YAAY,YAAY,gBAAgB,QAAQ;AAAA,IACzD;AAAA,EACF;AACA,SAAO;AACT;;;AC1IO,IAAM,4BACX;AACK,IAAM,4BACX;AAyBF,eAAsB,uBACpB,OACuC;AACvC,QAAM,EAAE,QAAQ,UAAU,gBAAgB,OAAO,SAAS,IAAI;AAC9D,QAAM,QAAQ,mBAAmB;AAEjC,QAAM,UAAU,QAAQ,EAAE,gBAAgB,MAAM,CAAC,KAAK;AACtD,MAAI,YAAY,MAAM;AACpB,aAAS,OAAO,EAAE,OAAO,QAAQ,SAAS,QAAQ,CAAC;AACnD,WAAO,EAAE,UAAU,OAAO,SAAS,aAAa,MAAM;AAAA,EACxD;AAEA,QAAM,SAAS,OAAO,YAAY,EAAE;AACpC,QAAM,OAAO,WAAW,eAAe,WAAW;AAClD,MAAI,MAAM;AACR,UAAM,OAAO,OAAO;AACpB,aAAS,OAAO;AAAA,MACd,OAAO;AAAA,MACP,SAAS;AAAA,MACT,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAEA,MAAI,aAAa,QAAW;AAC1B,UAAM,SAAS,cAAc;AAC7B,WAAO,EAAE,UAAU,MAAM,SAAS,MAAM,aAAa,KAAK;AAAA,EAC5D;AAOA,MAAI,mBAAmB,MAAM;AAC3B,QAAI,OAAO,yBAAyB;AAClC,YAAM,OAAO,qBAAqB;AAClC,aAAO,EAAE,UAAU,MAAM,SAAS,MAAM,aAAa,KAAK;AAAA,IAC5D;AACA,aAAS,OAAO;AAAA,MACd,OAAO;AAAA,MACP,SAAS;AAAA,MACT,QACE;AAAA,IACJ,CAAC;AACD,WAAO,EAAE,UAAU,OAAO,SAAS,MAAM,aAAa,KAAK;AAAA,EAC7D;AAEA,QAAM,OAAO,iBAAiB,cAAc;AAC5C,SAAO,EAAE,UAAU,MAAM,SAAS,MAAM,aAAa,KAAK;AAC5D;;;AC/FA;AAAA,EACE,eAAAC;AAAA,EACA,aAAAC;AAAA,EACA,WAAAC;AAAA,EACA,UAAAC;AAAA,EACA,YAAAC;AAAA,EACA,wBAAAC;AAAA,OACK;AAEP;AAAA,EACE,0BAAAC;AAAA,OAEK;AAuBA,SAAS,aAAa,gBAAuC;AAClE,SAAO,uBAAuB,kBAAkB,KAAK;AACvD;AA6DA,IAAM,oBAAoB;AAEnB,SAAS,wBACd,SAC+B;AAC/B,QAAM,EAAE,QAAQ,OAAO,UAAU,kBAAkB,IAAI;AACvD,QAAM,EAAE,OAAO,IAAI,eAAe;AAClC,QAAM,QAAQ,aAAa;AAC3B,QAAM,WAAW,cAAc;AAC/B,QAAM,WAAW,QAAQ,YAAY;AAErC,QAAM,CAAC,SAAS,eAAe,IAAIC;AAAA,IAA6B,MAC9D,SAAS,4BAA4BC,0BAAyB;AAAA,EAChE;AACA,QAAM,CAAC,QAAQ,SAAS,IAAID,WAAS,EAAE;AACvC,QAAM,CAAC,eAAe,gBAAgB,IAAIA,WAAoC,CAAC,CAAC;AAChF,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,WAAS,CAAC;AACtD,QAAM,CAAC,YAAY,aAAa,IAAIA,WAAwB,IAAI;AAChE,QAAM,CAAC,SAAS,UAAU,IAAIA,WAAS,IAAI;AAC3C,QAAM,CAAC,OAAO,QAAQ,IAAIA,WAAwB,IAAI;AACtD,QAAM,CAAC,SAAS,UAAU,IAAIA,WAAwB,IAAI;AAC1D,QAAM,CAAC,OAAO,QAAQ,IAAIA,WAAS,CAAC;AACpC,QAAM,CAAC,SAAS,UAAU,IAAIA,WAAwB,IAAI;AAI1D,QAAM,WAAWE,QAAO,CAAC;AAczB,QAAM,YAAYA,QAAO,MAAM;AAC/B,YAAU,UAAU;AACpB,QAAM,WAAWA,QAAO,KAAK;AAC7B,WAAS,UAAU;AACnB,QAAM,cAAcA,QAAO,QAAQ;AACnC,cAAY,UAAU;AACtB,QAAM,WAAWA,QAAO,iBAAiB;AACzC,WAAS,UAAU;AAEnB,QAAM,OAAOC;AAAA,IACX,OAAO,WAA0B;AAC/B,YAAM,SAAU,SAAS,WAAW;AACpC,iBAAW,IAAI;AACf,UAAI;AACF,cAAM,OAAmC,MAAM,UAAU,QAAQ,KAAK;AAAA,UACpE;AAAA,UACA,OAAO;AAAA,UACP,GAAI,WAAW,KAAK,CAAC,IAAI,EAAE,OAAO;AAAA,UAClC,GAAI,WAAW,OAAO,CAAC,IAAI,EAAE,OAAO;AAAA,UACpC,GAAI,QAAQ,cAAc,SAAY,CAAC,IAAI,EAAE,WAAW,QAAQ,UAAU;AAAA,UAC1E,GAAI,QAAQ,kBAAkB,SAC1B,CAAC,IACD,EAAE,eAAe,QAAQ,cAAc;AAAA,QAC7C,CAAC;AACD,YAAI,WAAW,SAAS,QAAS;AACjC;AAAA,UAAiB,CAAC,aAChB,WAAW,OACP,CAAC,GAAG,KAAK,aAAa,IACtB,CAAC,GAAG,UAAU,GAAG,KAAK,aAAa;AAAA,QACzC;AACA;AAAA,UAAkB,CAAC,aACjB,WAAW,OAAO,KAAK,iBAAiB,WAAW,KAAK;AAAA,QAC1D;AACA,sBAAc,KAAK,UAAU;AAC7B,iBAAS,IAAI;AAAA,MACf,SAAS,OAAO;AACd,YAAI,WAAW,SAAS,QAAS;AACjC,cAAM,UACJ,iBAAiB,SAAS,MAAM,YAAY,KACxC,MAAM,UACN;AACN,iBAAS,OAAO;AAChB,cAAM,SAAS,OAAO;AAAA,UACpB,OAAO;AAAA,UACP,SAAS;AAAA,UACT,QAAQ;AAAA,QACV,CAAC;AAAA,MACH,UAAE;AACA,YAAI,WAAW,SAAS,QAAS,YAAW,KAAK;AAAA,MACnD;AAAA,IACF;AAAA,IACA,CAAC,SAAS,QAAQ,UAAU,QAAQ,WAAW,QAAQ,eAAe,MAAM,QAAQ;AAAA,EACtF;AAEA,EAAAC,YAAU,MAAM;AACd,SAAK,KAAK,IAAI;AAAA,EAChB,GAAG,CAAC,MAAM,KAAK,CAAC;AAEhB,QAAM,UAAUD,aAAY,MAAM,SAAS,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;AAC5D,QAAM,WAAWA,aAAY,MAAM;AACjC,QAAI,eAAe,KAAM;AACzB,SAAK,KAAK,UAAU;AAAA,EACtB,GAAG,CAAC,MAAM,UAAU,CAAC;AAErB,QAAM,aAAaA,aAAY,CAAC,SAA6B;AAC3D,oBAAgB,IAAI;AACpB,qBAAiB,CAAC,CAAC;AACnB,sBAAkB,CAAC;AACnB,kBAAc,IAAI;AAAA,EACpB,GAAG,CAAC,CAAC;AAML,QAAM,qBAAqBA;AAAA,IACzB,MAAM,OAAO,YAAY,EAAE;AAAA,IAC3B,CAAC,MAAM;AAAA,EACT;AACA,QAAM,uBAAuBE;AAAA,IAC3B,OAAO;AAAA,IACP;AAAA,IACA;AAAA,EACF;AACA,QAAM,uBACJ,QAAQ,yBAAyB,SAC7B,uBACA,QAAQ;AAEd,QAAM,SAAS,OAAO,KAAK,EAAE,YAAY;AACzC,QAAM,UAAUC,SAAQ,MAAM;AAC5B,QAAI,WAAW,MAAM,OAAO,mBAAoB,QAAO;AACvD,WAAO,cAAc;AAAA,MAAO,CAAC,kBAC1B,aAAa,SAAS,IAAI,YAAY,EAAE,SAAS,MAAM;AAAA,IAC1D;AAAA,EACF,GAAG,CAAC,eAAe,QAAQ,OAAO,kBAAkB,CAAC;AAErD,QAAM,OAAOA;AAAA,IACX,MACE,QAAQ,IAAI,CAAC,iBAAiB;AAC5B,YAAM,QAAQ,MAAM,MAAM,IAAI,aAAa,aAAa,EAAE,CAAC;AAC3D,aAAO;AAAA,QACL;AAAA,QACA,SAAS,aAAa,OAAO;AAAA,QAC7B,OAAO,UAAU,QAAQ,UAAU,KAAK,OAAO;AAAA,MACjD;AAAA,IACF,CAAC;AAAA;AAAA;AAAA;AAAA,IAIH,CAAC,SAAS,sBAAsB,MAAM,OAAO,OAAO;AAAA,EACtD;AAEA,QAAM,WAAWH;AAAA,IACf,OAAO,mBAAoD;AACzD,iBAAW,cAAc;AACzB,UAAI;AACF,cAAM,SAAS,MAAM,uBAAuB;AAAA,UAC1C;AAAA,UACA,UAAU,MAAM;AAAA,UAChB;AAAA,UACA,GAAI,SAAS,YAAY,SAAY,CAAC,IAAI,EAAE,OAAO,SAAS,QAAQ;AAAA,UACpE,GAAI,YAAY,YAAY,SACxB,CAAC,IACD,EAAE,UAAU,YAAY,QAAQ;AAAA,QACtC,CAAC;AACD,mBAAW,OAAO,OAAO;AACzB,eAAO,OAAO;AAAA,MAChB,UAAE;AACA,mBAAW,IAAI;AAAA,MACjB;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,MAAM,QAAQ;AAAA,EACzB;AAUA,QAAM,cACJ,OAAO,aAAa,UACpB,sBAAsB,UACtB,OAAO;AAET,QAAM,WAAWA,aAAY,YAA8B;AACzD,UAAM,UAAU,UAAU;AAC1B,UAAM,QAAQ,SAAS;AACvB,QAAI,QAAQ,aAAa,QAAW;AAClC,YAAM,QAAQ,MAAM,QAAQ,SAAS;AACrC,YAAM,WAAW,MAAM,SAAS,KAAK;AACrC,UAAI,SAAU,SAAQ;AACtB,aAAO;AAAA,IACT;AACA,QAAI,UAAU,QAAW;AAGvB,YAAM,WAAW,MAAM,SAAS,IAAI;AACpC,UAAI,SAAU,SAAQ;AACtB,aAAO;AAAA,IACT;AACA,UAAM,UAAU,SAAS,UAAU,EAAE,gBAAgB,MAAM,OAAO,KAAK,CAAC,KAAK;AAC7E,QAAI,YAAY,MAAM;AACpB,iBAAW,OAAO;AAClB,YAAM,SAAS,OAAO,EAAE,OAAO,QAAQ,SAAS,QAAQ,CAAC;AACzD,aAAO;AAAA,IACT;AACA,UAAM,SAAS,OAAO,YAAY,EAAE;AACpC,QAAI,WAAW,eAAe,WAAW,YAAa,OAAM,OAAO,OAAO;AAC1E,eAAW,IAAI;AACf,UAAM,MAAM;AACZ,YAAQ;AACR,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,MAAM,UAAU,QAAQ,OAAO,CAAC;AAE9C,QAAM,OAAOA;AAAA,IACX,OAAO,mBAA2B,SAAS,cAAc;AAAA,IACzD,CAAC,QAAQ;AAAA,EACX;AAEA,QAAM,SAASA;AAAA,IACb,OAAO,gBAAwB,UAAoC;AACjE,YAAM,UAAU,MAAM,KAAK;AAC3B,YAAM,UAAU,UAAU,QAAQ;AAClC,UAAI,YAAY,UAAa,YAAY,GAAI,QAAO;AACpD,UAAI;AACF,cAAM,QAAQ,gBAAgB,OAAO;AAAA,MACvC,QAAQ;AACN,cAAM,SAAS,OAAO;AAAA,UACpB,OAAO;AAAA,UACP,SAAS;AAAA,UACT,QAAQ;AAAA,QACV,CAAC;AACD,eAAO;AAAA,MACT;AACA;AAAA,QAAiB,CAAC,aAChB,SAAS;AAAA,UAAI,CAAC,iBACZ,aAAa,OAAO,iBAChB,EAAE,GAAG,cAAc,OAAO,QAAQ,IAClC;AAAA,QACN;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,MAAM,QAAQ;AAAA,EACjB;AAEA,QAAM,cAAcA;AAAA,IAClB,OAAO,gBAAwB,aAAwC;AACrE,YAAM,eAAe,UAAU,QAAQ;AACvC,UAAI,iBAAiB,OAAW,QAAO;AACvC,UAAI;AACF,cAAM,aAAa,gBAAgB,QAAQ;AAAA,MAC7C,QAAQ;AACN,cAAM,SAAS,OAAO;AAAA,UACpB,OAAO;AAAA,UACP,SAAS,WACL,6CACA;AAAA,UACJ,QAAQ;AAAA,QACV,CAAC;AACD,eAAO;AAAA,MACT;AAIA,cAAQ;AACR,aAAO;AAAA,IACT;AAAA,IACA,CAAC,MAAM,UAAU,OAAO;AAAA,EAC1B;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,YAAY,YAAY,aAAa,WAAW;AAAA,IAChD;AAAA,IACA;AAAA,IACA,SAAS,eAAe;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,oBAAoB,OAAO;AAAA,IAC3B,cAAc,WAAW,KAAK,IAAI,cAAc,SAAS,QAAQ;AAAA,IACjE;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,OAAO,WAAW;AAAA,IAC7B,YAAY,OAAO,gBAAgB;AAAA,IACnC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,gBAAgBA,aAAY,MAAM,WAAW,IAAI,GAAG,CAAC,CAAC;AAAA,EACxD;AACF;;;AC3YA,SAAS,aAAAI,aAAW,UAAAC,UAAQ,YAAAC,kBAAgC;AAE5D,SAAS,eAAe,YAAAC,iBAAgB;AACxC,SAAS,0BAA0B;AAmD7B,SAGI,OAAAC,OAHJ,QAAAC,cAAA;AAnCC,SAAS,qBACd,cACA,KACQ;AAGR,QAAM,OAAO,aAAa,YACtB,mBAAmB,aAAa,WAAW,EAAE,OAAO,QAAQ,KAAK,UAAU,GAAG,CAAC,KAAK,OACpF;AACJ,QAAM,QAAQ,aAAa;AAC3B,QAAM,OACJ,SAAS,IAAI,qBAAqB,UAAU,IAAI,cAAc,GAAG,KAAK;AACxE,SAAO,SAAS,OAAO,OAAO,GAAG,IAAI,SAAM,IAAI;AACjD;AAGO,IAAM,wBAAwB;AAW9B,SAAS,qBAAqB,OAA6C;AAChF,QAAM,EAAE,KAAK,IAAI;AACjB,QAAM,QAAQ,aAAa;AAC3B,QAAM,QAAQ,MAAM,SAAS;AAC7B,QAAM,aAAa,MAAM,cAAc;AAEvC,SACE,gBAAAA,OAAC,SAAI,WAAWC,WAAU,mBAAmB,MAAM,SAAS,GAC1D;AAAA,oBAAAD,OAAC,SAAI,WAAU,2BACZ;AAAA,YAAM,0BAA0B;AAAA,MAChC,KAAK,cACJ,gBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAU;AAAA,UACV,SAAS,MAAM,KAAK,KAAK,SAAS;AAAA,UACnC;AAAA;AAAA,MAED,IACE;AAAA,MACH,aACC,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAU;AAAA,UACV,OAAO,KAAK;AAAA,UACZ,aAAY;AAAA,UACZ,cAAW;AAAA,UACX,UAAU,CAAC,UAAU,KAAK,UAAU,MAAM,OAAO,KAAK;AAAA;AAAA,MACxD,IACE;AAAA,MAOH,KAAK,aACJ,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,KAAK;AAAA,UACZ,eAAe,KAAK;AAAA,UACpB,cAAW;AAAA;AAAA,MACb,IACE;AAAA,OACN;AAAA,IAEC,KAAK,YAAY,OAChB,gBAAAC,OAAC,OAAE,WAAU,4BAA2B,MAAK,UAC1C;AAAA,WAAK;AAAA,MACN,gBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAU;AAAA,UACV,SAAS,KAAK;AAAA,UACf;AAAA;AAAA,MAED;AAAA,OACF,IACE;AAAA,IAEH,KAAK,UAAU,OACd,gBAAAC,OAACE,WAAA,EAAS,WAAU,0BAAyB,OAAO,KAAK,OAAO,WAAU,8BAA6B,QAAO,yBAAwB;AAAA;AAAA,MAEpI,gBAAAH;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAU;AAAA,UACV,SAAS,KAAK;AAAA,UACf;AAAA;AAAA,MAED;AAAA,OACF,IACE;AAAA,IAEJ,gBAAAA,MAAC,QAAG,WAAU,yBAAwB,cAAY,OAAO,aAAW,KAAK,SACtE,eAAK,KAAK,IAAI,CAAC,QACd,gBAAAA,MAAC,mBAA0C,KAAU,QAA/B,IAAI,aAAa,EAA0B,CAClE,GACH;AAAA,IAEC,KAAK,KAAK,WAAW,KAAK,CAAC,KAAK,WAAW,KAAK,UAAU,OACzD,gBAAAA,MAAC,OAAE,WAAU,0BACV,eAAK,OAAO,KAAK,MAAM,KACpB,+BACA,0CACN,IACE;AAAA,IAEH,KAAK,UACJ,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,WAAU;AAAA,QACV,SAAS,KAAK;AAAA,QACd,UAAU,KAAK;AAAA,QAEd,eAAK,UAAU,kBAAa;AAAA;AAAA,IAC/B,IACE;AAAA,IAEJ,gBAAAA,MAAC,0BAAuB,MAAY;AAAA,IACnC,MAAM,0BAA0B;AAAA,KACnC;AAEJ;AAUO,SAAS,uBAAuB;AAAA,EACrC;AACF,GAEc;AACZ,QAAM,QAAqB,CAAC;AAE5B,MAAI,KAAK,iBAAiB,GAAG;AAC3B,UAAM,OAAO,KAAK;AAClB,UAAM,QAAQ,KAAK;AACnB,UAAM;AAAA,MACJ,gBAAAC,OAAC,UACE;AAAA,kBAAU,IACP,KAAK,IAAI,6BACT,GAAG,KAAK,IAAI,IAAI;AAAA,QAA8B;AAAA,QAClD,gBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,WAAU;AAAA,YACV,SAAS,MAAM,KAAK,WAAW,KAAK;AAAA,YACrC;AAAA;AAAA,QAED;AAAA,WAVQ,SAWV;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,KAAK,sBAAsB,KAAK,OAAO,KAAK,MAAM,IAAI;AACzD,UAAM;AAAA,MACJ,gBAAAC,OAAC,UAAkB;AAAA;AAAA,QAEhB,KAAK,UAAU,kCAAkC;AAAA,WAF1C,QAGV;AAAA,IACF;AAAA,EACF;AAEA,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,SACE,gBAAAD,MAAC,OAAE,WAAU,2BAA0B,MAAK,QACzC,iBACH;AAEJ;AAEA,SAAS,gBAAgB;AAAA,EACvB;AAAA,EACA;AACF,GAGc;AACZ,QAAM,QAAQ,aAAa;AAC3B,QAAM,CAAC,UAAU,WAAW,IAAII,WAAS,KAAK;AAC9C,QAAM,eAAe,IAAI;AACzB,QAAM,QAAQ,aAAa,SAAS;AAEpC,QAAM,WAAW,MAAM,kBAAkB,GAAG;AAC5C,MAAI,aAAa,OAAW,QAAO,gBAAAJ,MAAC,QAAG,WAAU,wBAAwB,oBAAS;AAElF,SACE,gBAAAC,OAAC,QAAG,WAAU,wBAAuB,gBAAc,IAAI,SACpD;AAAA,gBAAY,KAAK,YAChB,gBAAAD;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,UAAU,MAAM,YAAY,KAAK;AAAA,QACjC,UAAU,OAAO,SAAS;AACxB,gBAAM,KAAK,OAAO,aAAa,IAAI,IAAI;AACvC,sBAAY,KAAK;AAAA,QACnB;AAAA;AAAA,IACF,IAEA,gBAAAC;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,WAAU;AAAA,QACV,gBAAc,IAAI,UAAU,SAAS;AAAA,QACrC,SAAS,MAAM,KAAK,KAAK,KAAK,aAAa,EAAE;AAAA,QAE7C;AAAA,0BAAAD,MAAC,UAAK,WAAU,0BAA0B,iBAAM;AAAA,UAChD,gBAAAA,MAAC,UAAK,WAAU,yBACb,+BAAqB,YAAY,GACpC;AAAA,UACC,IAAI,UAAU,OACb,gBAAAC,OAAC,UAAK,WAAU,0BAAyB;AAAA;AAAA,YAC9B,QAAQ,IAAI,KAAK;AAAA,aAC5B,IACE;AAAA;AAAA;AAAA,IACN;AAAA,IAGF,gBAAAA,OAAC,UAAK,WAAU,4BACb;AAAA,WAAK,aAAa,CAAC,WAClB,gBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAU;AAAA,UACV,SAAS,MAAM,YAAY,IAAI;AAAA,UAChC;AAAA;AAAA,MAED,IACE;AAAA,MACH,KAAK,aACJ,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,WAAU;AAAA,UACV,SAAS,MAAM,KAAK,KAAK,YAAY,aAAa,IAAI,CAAC,aAAa,QAAQ;AAAA,UAE3E,uBAAa,WAAW,eAAe;AAAA;AAAA,MAC1C,IACE;AAAA,OACN;AAAA,KACF;AAEJ;AAEA,SAAS,YAAY;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AACF,GAIc;AACZ,QAAM,CAAC,OAAO,QAAQ,IAAII,WAAS,KAAK;AACxC,QAAM,MAAMC,SAAyB,IAAI;AACzC,EAAAC,YAAU,MAAM,IAAI,SAAS,OAAO,GAAG,CAAC,CAAC;AACzC,SACE,gBAAAN;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,WAAU;AAAA,MACV;AAAA,MACA,cAAY,UAAU,KAAK;AAAA,MAC3B,UAAU,CAAC,UAAU,SAAS,MAAM,OAAO,KAAK;AAAA,MAChD,QAAQ;AAAA,MACR,WAAW,CAAC,UAAU;AACpB,YAAI,MAAM,QAAQ,SAAS;AACzB,gBAAM,eAAe;AACrB,eAAK,SAAS,KAAK;AAAA,QACrB;AACA,YAAI,MAAM,QAAQ,UAAU;AAC1B,gBAAM,eAAe;AACrB,mBAAS;AAAA,QACX;AAAA,MACF;AAAA;AAAA,EACF;AAEJ;AAEA,IAAM,gBAAgB;AAEtB,SAAS,QAAQ,MAAsB;AACrC,QAAM,OAAO,KAAK,QAAQ,QAAQ,GAAG,EAAE,KAAK;AAC5C,SAAO,KAAK,UAAU,gBAAgB,OAAO,GAAG,KAAK,MAAM,GAAG,gBAAgB,CAAC,CAAC;AAClF;AAEA,SAASE,cAAa,OAAuC;AAC3D,SAAO,MAAM,OAAO,CAAC,SAAS,SAAS,UAAa,SAAS,EAAE,EAAE,KAAK,GAAG;AAC3E;;;ACpSI,SAEE,OAAAK,OAFF,QAAAC,cAAA;AAHG,SAAS,YAAY,OAAoC;AAC9D,QAAM,QAAQ,aAAa;AAC3B,SACE,gBAAAA,OAAC,SAAI,WAAWC,WAAU,2BAA2B,MAAM,SAAS,GACjE;AAAA,UAAM,UAAU,MAAM,iBAAiB;AAAA,IACxC,gBAAAF,MAAC,mBAAiB,GAAI,MAAM,cAAc,CAAC,GAAI;AAAA,IAC/C,gBAAAA,MAAC,SAAI,WAAU,6BAA6B,gBAAM,UAAS;AAAA,KAC7D;AAEJ;AAEO,SAASE,cAAa,OAA+C;AAC1E,SAAO,MAAM,OAAO,CAAC,SAAyB,OAAO,SAAS,YAAY,SAAS,EAAE,EAAE,KAAK,GAAG;AACjG;;;ACFM,SACE,OAAAC,OADF,QAAAC,cAAA;AALC,SAAS,gBAAgB,OAAwC;AACtE,QAAM,eAAe,MAAM,gBAAgB;AAC3C,QAAM,iBAAiB,MAAM,aAAa;AAC1C,QAAM,UACJ,MAAM,YAAY,OAChB,gBAAAA,OAAC,UAAK,WAAU,0BAAyB,MAAK,UAC5C;AAAA,oBAAAD,MAAC,UAAK,WAAU,wBAAuB,eAAY,QAAO;AAAA,IACzD;AAAA,KACH,IACE;AACN,SACE,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,gBAAgB,yBAAyB,MAAM,SAAS;AAAA,MACnE,kBAAgB,MAAM,oBAAoB;AAAA,MACzC,GAAI,MAAM,mBAAmB,CAAC;AAAA,MAE9B;AAAA,cAAM,SAAS,SAAY,OAC1B,gBAAAD,MAAC,UAAK,WAAU,uBAAuB,gBAAM,MAAK;AAAA,QAEpD,gBAAAC,OAAC,SAAI,WAAU,yBACZ;AAAA,gBAAM,UAAU,SAAY,OAC3B,gBAAAA,OAAC,SAAI,WAAU,wBACZ;AAAA,kBAAM;AAAA,YACN,iBAAiB,OAChB,gBAAAD,MAAC,UAAK,WAAU,iCAAiC,mBAAQ;AAAA,aAE7D;AAAA,UAED,iBACC,gBAAAA,MAAC,SAAI,WAAU,2BACZ,qBAAW,MAAM,UACpB,IACE;AAAA,WACN;AAAA,QACC,MAAM,YAAY,SAAY,OAC7B,gBAAAA,MAAC,UAAK,WAAU,0BAA0B,gBAAM,SAAQ;AAAA;AAAA;AAAA,EAE5D;AAEJ;AAEA,SAAS,mBAAmB,OAAuC;AACjE,SAAO,MAAM,OAAO,CAAC,SAAS,SAAS,UAAa,SAAS,EAAE,EAAE,KAAK,GAAG;AAC3E;;;ACnEA;AAAA,EACE,eAAAE;AAAA,EACA,aAAAC;AAAA,EACA,mBAAAC;AAAA,EACA,UAAAC;AAAA,EACA,YAAAC;AAAA,OAGK;AAqCP,IAAM,mBAAmB;AAclB,SAAS,kBACd,SACsB;AACtB,QAAM,EAAE,OAAO,KAAK,KAAK,MAAM,cAAc,IAAI;AACjD,QAAM,WAAWD,SAA2B,IAAI;AAChD,QAAM,CAAC,UAAU,WAAW,IAAIC,WAAS,KAAK;AAC9C,QAAM,WAAWD,SAAsB,IAAI;AAC3C,QAAM,UAAUA;AAAA,IACd;AAAA,EACF;AACA,QAAM,aAAaA,SAAO,KAAK;AAE/B,QAAM,QAAQH;AAAA,IACZ,CAAC,SAAiB,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,MAAM,IAAI,CAAC,CAAC;AAAA,IAC/D,CAAC,KAAK,GAAG;AAAA,EACX;AAEA,QAAM,SAASA;AAAA,IACb,CAAC,SAAiB;AAChB,eAAS,SAAS,MAAM,eAAe,OAAO;AAC9C,sBAAgB,MAAM,IAAI,CAAC;AAAA,IAC7B;AAAA,IACA,CAAC,OAAO,aAAa;AAAA,EACvB;AAEA,QAAM,MAAMA,aAAY,MAAM;AAC5B,UAAM,OAAO,QAAQ;AACrB,QAAI,SAAS,YAAY,MAAM;AAC7B,iBAAW,uBAAuB,SAAS,OAAO;AAClD,eAAS,UAAU;AAAA,IACrB;AACA,YAAQ,UAAU;AAClB,gBAAY,KAAK;AACjB,QAAI,SAAS,KAAM,QAAO,KAAK,MAAM;AAAA,EACvC,GAAG,CAAC,MAAM,CAAC;AAEX,EAAAC;AAAA,IACE,MAAM,MAAM;AACV,UAAI,SAAS,YAAY,KAAM,YAAW,uBAAuB,SAAS,OAAO;AAAA,IACnF;AAAA,IACA,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,aAAa;AAAA,MACX,MAAM;AAAA,MACN,UAAU;AAAA,MACV,oBAAoB;AAAA,MACpB,iBAAiB;AAAA,MACjB,iBAAiB;AAAA,MACjB,iBAAiB;AAAA,MACjB,cAAc,OAAO;AACnB,cAAM,eAAe;AACrB,gBAAQ,UAAU,EAAE,QAAQ,MAAM,SAAS,YAAY,OAAO,QAAQ,MAAM;AAC5E,oBAAY,IAAI;AAChB,QAAC,MAAM,OAAuB,oBAAoB,MAAM,SAAS;AAAA,MACnE;AAAA,MACA,cAAc,OAAO;AACnB,cAAM,OAAO,QAAQ;AACrB,YAAI,SAAS,KAAM;AACnB,cAAM,QACJ,SAAS,QAAQ,MAAM,UAAU,KAAK,SAAS,KAAK,SAAS,MAAM;AACrE,aAAK,SAAS,MAAM,KAAK,aAAa,KAAK;AAC3C,YAAI,SAAS,YAAY,KAAM;AAC/B,cAAM,WAAW,WAAW;AAC5B,cAAM,QAAQ,MAAY;AACxB,mBAAS,UAAU;AACnB,gBAAM,SAAS,QAAQ;AACvB,cAAI,WAAW,QAAQ,SAAS,YAAY,KAAM;AAClD,mBAAS,QAAQ,MAAM,QAAQ,GAAG,OAAO,MAAM;AAAA,QACjD;AACA,YAAI,OAAO,aAAa,WAAY,UAAS,UAAU,SAAS,KAAK;AAAA,YAChE,OAAM;AAAA,MACb;AAAA,MACA,aAAa;AAAA,MACb,iBAAiB;AAAA,MACjB,UAAU,OAAO;AACf,cAAM,OAAO,SAAS,QAAQ,eAAe;AAC7C,cAAM,SAAS,SAAS,QAAQ,cAAc;AAC9C,YAAI,MAAM,QAAQ,MAAM;AACtB,gBAAM,eAAe;AACrB,iBAAO,QAAQ,gBAAgB;AAC/B;AAAA,QACF;AACA,YAAI,MAAM,QAAQ,QAAQ;AACxB,gBAAM,eAAe;AACrB,iBAAO,QAAQ,gBAAgB;AAAA,QACjC;AAAA,MACF;AAAA,MACA,gBAAgB;AACd,eAAO,WAAW,OAAO;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AACF;AAyBA,IAAM,uBAAuB;AAEtB,SAAS,kBACd,SACsB;AACtB,QAAM,cAAc,QAAQ,iBAAiB;AAC7C,QAAM,CAAC,UAAU,WAAW,IAAIG,WAAiC,QAAQ,OAAO;AAChF,QAAM,CAAC,UAAU,WAAW,IAAIA,WAAS,KAAK;AAC9C,QAAM,WAAWD,SAA2B,IAAI;AAChD,QAAM,WAAWA,SAAsB,IAAI;AAC3C,QAAM,cAAcA,SAAO,KAAK;AAChC,QAAM,UAAUA,SAKN,IAAI;AAEd,QAAM,QAAQH;AAAA,IACZ,CAAC,SAAyD;AACxD,YAAM,QAAQ,WAAW,cAAc;AACvC,YAAM,SAAS,WAAW,eAAe;AACzC,UAAI,UAAU,KAAK,WAAW,EAAG,QAAO;AACxC,aAAO;AAAA,QACL,GAAG,KAAK,IAAI,GAAG,KAAK,IAAI,QAAQ,aAAa,KAAK,CAAC,CAAC;AAAA,QACpD,GAAG,KAAK,IAAI,GAAG,KAAK,IAAI,SAAS,aAAa,KAAK,CAAC,CAAC;AAAA,MACvD;AAAA,IACF;AAAA,IACA,CAAC,WAAW;AAAA,EACd;AAEA,QAAM,gBAAgBA;AAAA,IACpB,CAAC,UAA6B;AAE5B,UAAK,MAAM,OAAuB,QAAQ,gCAAgC,EAAG;AAC7E,YAAM,eAAe;AACrB,cAAQ,UAAU;AAAA,QAChB,IAAI,MAAM,UAAU,SAAS;AAAA,QAC7B,IAAI,MAAM,UAAU,SAAS;AAAA,QAC7B,QAAQ;AAAA,QACR,QAAQ;AAAA,MACV;AACA,kBAAY,IAAI;AAChB,MAAC,MAAM,OAAuB,oBAAoB,MAAM,SAAS;AAAA,IACnE;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AAEA,QAAM,gBAAgBA;AAAA,IACpB,CAAC,UAA6B;AAC5B,YAAM,OAAO,QAAQ;AACrB,UAAI,SAAS,KAAM;AACnB,WAAK,SAAS,MAAM,EAAE,GAAG,MAAM,UAAU,KAAK,IAAI,GAAG,MAAM,UAAU,KAAK,GAAG,CAAC;AAC9E,UAAI,SAAS,YAAY,KAAM;AAC/B,YAAM,WAAW,WAAW;AAC5B,YAAM,QAAQ,MAAY;AACxB,iBAAS,UAAU;AACnB,cAAM,SAAS,QAAQ;AACvB,cAAM,UAAU,SAAS;AACzB,YAAI,WAAW,QAAQ,YAAY,KAAM;AACzC,gBAAQ,MAAM,YAAY,sBAAsB,GAAG,OAAO,OAAO,IAAI,OAAO,OAAO,CAAC,IAAI;AACxF,gBAAQ,MAAM,YAAY,sBAAsB,GAAG,OAAO,OAAO,IAAI,OAAO,OAAO,CAAC,IAAI;AAAA,MAC1F;AACA,UAAI,OAAO,aAAa,WAAY,UAAS,UAAU,SAAS,KAAK;AAAA,UAChE,OAAM;AAAA,IACb;AAAA,IACA,CAAC,KAAK;AAAA,EACR;AAEA,QAAM,MAAMA,aAAY,MAAM;AAC5B,UAAM,OAAO,QAAQ;AACrB,QAAI,SAAS,YAAY,MAAM;AAC7B,iBAAW,uBAAuB,SAAS,OAAO;AAClD,eAAS,UAAU;AAAA,IACrB;AACA,YAAQ,UAAU;AAClB,gBAAY,KAAK;AACjB,QAAI,SAAS,KAAM;AACnB,gBAAY,UAAU;AACtB,gBAAY,KAAK,MAAM;AACvB,YAAQ,WAAW,KAAK,MAAM;AAAA,EAChC,GAAG,CAAC,OAAO,CAAC;AAEZ,EAAAE,iBAAgB,MAAM;AACpB,QAAI,CAAC,YAAY,QAAS;AAC1B,gBAAY,UAAU;AACtB,aAAS,SAAS,MAAM,eAAe,oBAAoB;AAC3D,aAAS,SAAS,MAAM,eAAe,oBAAoB;AAAA,EAC7D,CAAC;AAED,EAAAD;AAAA,IACE,MAAM,MAAM;AACV,UAAI,SAAS,YAAY,KAAM,YAAW,uBAAuB,SAAS,OAAO;AAAA,IACnF;AAAA,IACA,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,iBAAiB;AAAA,MACf;AAAA,MACA;AAAA,MACA,aAAa;AAAA,MACb,iBAAiB;AAAA,IACnB;AAAA,EACF;AACF;;;ACnSA,SAAS,YAAAI,kBAAgC;AA0BJ,gBAAAC,OAK7B,QAAAC,cAL6B;AAX9B,SAAS,SAAS,OAAiC;AACxD,QAAM,EAAE,SAAS,QAAQ,OAAO,WAAW,GAAG,QAAQ,IAAI;AAC1D,QAAM,CAAC,UAAU,WAAW,IAAIC,WAAS,KAAK;AAE9C,SACE,gBAAAD;AAAA,IAAC;AAAA;AAAA,MACC,WAAWE,WAAU,qCAAqC,SAAS;AAAA,MACnE,cAAW;AAAA,MACX,MAAK;AAAA,MACL,cAAY,SAAS;AAAA,MAEpB;AAAA,mBAAW,SAAY,OAAO,gBAAAH,MAAC,mBAAiB,GAAG,QAAQ;AAAA,QAC5D,gBAAAC,OAAC,SAAI,WAAU,sBACb;AAAA,0BAAAD,MAAC,WAAM,WAAU,0BAAyB,cAAW,sBACnD,0BAAAA,MAAC,wBAAqB,MAAM,SAAS,GACvC;AAAA,UACA,gBAAAC,OAAC,SAAI,WAAU,iCACb;AAAA,4BAAAD;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,WAAU;AAAA,gBACV,iBAAe;AAAA,gBACf,SAAS,MAAM,YAAY,CAAC,SAAS,CAAC,IAAI;AAAA,gBAEzC,qBAAW,uBAAuB;AAAA;AAAA,YACrC;AAAA,YACC,WAAW,gBAAAA,MAAC,wBAAqB,MAAM,SAAS,IAAK;AAAA,aACxD;AAAA,UACA,gBAAAA,MAAC,eAAa,GAAG,SAAS,WAAU,uBAAsB;AAAA,WAC5D;AAAA;AAAA;AAAA,EACF;AAEJ;;;ACrCA,SAAS,SAAAI,QAAO,YAAAC,kBAAgC;AAqD1C,SAkCI,YAAAC,WAlCJ,OAAAC,OAkCI,QAAAC,cAlCJ;AA7CC,IAAM,sBAAsB;AAC5B,IAAM,sBAAsB;AAkB5B,SAAS,SAAS,OAAiC;AACxD,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP,QAAQ;AAAA,IACR;AAAA,IACA,WAAW;AAAA,IACX,WAAW;AAAA,IACX;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,IAAI;AAEJ,QAAM,CAAC,aAAa,cAAc,IAAIC,WAAS,KAAK;AACpD,QAAM,YAAYC,OAAM;AACxB,QAAM,SAAS,kBAAkB;AAAA,IAC/B;AAAA,IACA,KAAK;AAAA,IACL,KAAK;AAAA,IACL,MAAM,SAAS,UAAU,QAAQ;AAAA,IACjC,GAAI,kBAAkB,SAAY,CAAC,IAAI,EAAE,cAAc;AAAA,EACzD,CAAC;AAED,QAAM,gBACJ,YAAY,SAAY,OACtB,gBAAAH;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,WAAU;AAAA,MACV,iBAAe;AAAA,MACf,iBAAe;AAAA,MACf,SAAS,MAAM,eAAe,CAAC,SAAS,CAAC,IAAI;AAAA,MAC9C;AAAA;AAAA,EAED;AAGJ,SACE,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC,KAAK,OAAO;AAAA,MACZ,WAAWG,WAAU,qCAAqC,SAAS;AAAA,MACnE,cAAW;AAAA,MACX,aAAW;AAAA,MACX,iBAAe,OAAO;AAAA,MACtB,OAAO,EAAE,MAAM;AAAA,MACf,MAAK;AAAA,MACL,cAAY,SAAS;AAAA,MAEpB;AAAA,0BAAkB,SAAY;AAAA;AAAA;AAAA,UAG7B,gBAAAJ;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,cAAW;AAAA,cACV,GAAG,OAAO;AAAA;AAAA,UACb;AAAA;AAAA,QAEF,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACE,GAAI,UAAU,CAAC;AAAA,YAChB,SACE,gBAAAC,OAAAF,WAAA,EACG;AAAA;AAAA,cACA,QAAQ,WAAW;AAAA,eACtB;AAAA;AAAA,QAEJ;AAAA,QACC,YAAY,UAAa,cACxB,gBAAAC,MAAC,SAAI,WAAU,0BAAyB,IAAI,WAC1C,0BAAAA,MAAC,wBAAqB,MAAM,SAAS,YAAY,OAAO,GAC1D,IACE;AAAA,QACJ,gBAAAA,MAAC,eAAa,GAAG,SAAS,WAAU,uBAAsB;AAAA;AAAA;AAAA,EAC5D;AAEJ;;;AC1GA,SAAS,SAAAK,QAAO,YAAAC,kBAAgC;AAoD1C,SAgCI,YAAAC,WAhCJ,OAAAC,OAgCI,QAAAC,cAhCJ;AA5CC,IAAM,oBAAoB;AAuB1B,SAAS,WAAW,OAAmC;AAC5D,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,IAAI;AAEJ,QAAM,CAAC,QAAQ,IAAIC,WAAiC,OAAO;AAAA,IACzD,GAAG,KAAK,IAAI,IAAI,WAAW,cAAc,QAAQ,MAAM,QAAQ,EAAE;AAAA,IACjE,GAAG;AAAA,EACL,EAAE;AAEF,QAAM,CAAC,aAAa,cAAc,IAAIA,WAAS,KAAK;AACpD,QAAM,YAAYC,OAAM;AACxB,QAAM,gBACJ,YAAY,SAAY,OACtB,gBAAAH;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,WAAU;AAAA,MACV,iBAAe;AAAA,MACf,iBAAe;AAAA,MAEf,eAAe,CAAC,UAAU,MAAM,gBAAgB;AAAA,MAChD,SAAS,MAAM,eAAe,CAAC,SAAS,CAAC,IAAI;AAAA,MAC9C;AAAA;AAAA,EAED;AAGJ,QAAM,OAAO,kBAAkB;AAAA,IAC7B,SAAS,mBAAmB;AAAA,IAC5B,GAAI,qBAAqB,SAAY,CAAC,IAAI,EAAE,UAAU,iBAAiB;AAAA,EACzE,CAAC;AAED,SACE,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC,KAAK,KAAK;AAAA,MACV,WAAWG,WAAU,uCAAuC,SAAS;AAAA,MACrE,cAAW;AAAA,MACX,iBAAe,KAAK;AAAA,MACpB,MAAK;AAAA,MACL,cAAY,SAAS;AAAA,MACrB,OAAO,EAAE,MAAM,KAAK,SAAS,GAAG,KAAK,KAAK,SAAS,GAAG,MAAM;AAAA,MAE5D;AAAA,wBAAAJ;AAAA,UAAC;AAAA;AAAA,YACE,GAAI,UAAU,CAAC;AAAA,YAChB,iBAAiB,KAAK;AAAA,YACtB,SACE,gBAAAC,OAAAF,WAAA,EACG;AAAA;AAAA,cACA,QAAQ,WAAW;AAAA,eACtB;AAAA;AAAA,QAEJ;AAAA,QACC,YAAY,UAAa,cACxB,gBAAAC,MAAC,SAAI,WAAU,0BAAyB,IAAI,WAC1C,0BAAAA,MAAC,wBAAqB,MAAM,SAAS,YAAY,OAAO,GAC1D,IACE;AAAA,QACJ,gBAAAA,MAAC,eAAa,GAAG,SAAS,WAAU,uBAAsB;AAAA;AAAA;AAAA,EAC5D;AAEJ;;;AClGA,SAAS,eAAAK,cAAa,aAAAC,mBAAiC;AAqD7C,qBAAAC,WAEI,OAAAC,OAFJ,QAAAC,cAAA;AArCH,SAAS,WAAW,OAAmC;AAC5D,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,IAAI;AAEJ,QAAM,QAAQC;AAAA,IACZ,MAAM,sBAAsB,KAAK;AAAA,IACjC,CAAC,mBAAmB;AAAA,EACtB;AAEA,EAAAC,YAAU,MAAM;AACd,QAAI,CAAC,YAAa;AAClB,UAAM,QAAQ,CAAC,UAA+B;AAC5C,UAAI,MAAM,QAAQ,SAAU,OAAM;AAAA,IACpC;AACA,eAAW,mBAAmB,WAAW,KAAK;AAC9C,WAAO,MAAM,WAAW,sBAAsB,WAAW,KAAK;AAAA,EAChE,GAAG,CAAC,aAAa,KAAK,CAAC;AAEvB,QAAM,iBAAiB,YAAY,UAAa,wBAAwB;AAExE,SACE,gBAAAF;AAAA,IAAC;AAAA;AAAA,MACC,WAAWG,WAAU,uCAAuC,SAAS;AAAA,MACrE,cAAW;AAAA,MACX,MAAK;AAAA,MACL,cAAY,SAAS;AAAA,MAErB;AAAA,wBAAAJ;AAAA,UAAC;AAAA;AAAA,YACE,GAAI,UAAU,CAAC;AAAA,YAChB,SACE,gBAAAC,OAAAF,WAAA,EACG;AAAA,+BACC,gBAAAC;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,WAAU;AAAA,kBACV,iBAAe;AAAA,kBACf,SAAS,MAAM,sBAAsB,CAAC,WAAW;AAAA,kBAClD;AAAA;AAAA,cAED,IACE;AAAA,cACH,QAAQ,WAAW;AAAA,eACtB;AAAA;AAAA,QAEJ;AAAA,QACA,gBAAAA,MAAC,eAAa,GAAG,SAAS,WAAU,uBAAsB;AAAA,QAEzD,kBAAkB,cACjB,gBAAAC,OAAAF,WAAA,EACE;AAAA,0BAAAC;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,SAAS;AAAA,cACT,eAAY;AAAA;AAAA,UACd;AAAA,UACA,gBAAAC;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,MAAK;AAAA,cACL,cAAW;AAAA,cACX,cAAW;AAAA,cAEX;AAAA,gCAAAD,MAAC,SAAI,WAAU,6BAA4B,eAAY,QAAO;AAAA,gBAC9D,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,WAAU;AAAA,oBACV,SAAS;AAAA,oBACV;AAAA;AAAA,gBAED;AAAA,gBACA,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAM,kBAAkB,SAAS,KAAK;AAAA,oBACtC,OAAM;AAAA;AAAA,gBACR;AAAA;AAAA;AAAA,UACF;AAAA,WACF,IACE;AAAA;AAAA;AAAA,EACN;AAEJ;AAQA,SAAS,kBACP,MACA,OAC+B;AAC/B,SAAO;AAAA,IACL,GAAG;AAAA,IACH,MAAM,KAAK,gBAAwB;AACjC,YAAM,WAAW,MAAM,KAAK,KAAK,cAAc;AAC/C,UAAI,SAAU,OAAM;AACpB,aAAO;AAAA,IACT;AAAA,IACA,MAAM,WAAW;AACf,YAAM,UAAU,MAAM,KAAK,SAAS;AACpC,UAAI,QAAS,OAAM;AACnB,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AClHA;AAAA,EACE,aAAAK;AAAA,EACA,YAAAC;AAAA,OAGK;AA4FD,gBAAAC,OAwDF,QAAAC,cAxDE;AAnFN,IAAI,gBAA+C;AACnD,SAAS,mBAA2C;AAClD,oBAAkB,OAAO,gCAAgC;AACzD,SAAO;AACT;AAeO,IAAM,4BACX;AACK,IAAM,mCACX;AA+BK,SAAS,gBAAgB,OAAwC;AACtE,QAAM,EAAE,SAAS,IAAI,aAAa;AAClC,QAAM,CAAC,QAAQ,SAAS,IAAIC,WAA+B,IAAI;AAC/D,QAAM,CAAC,QAAQ,SAAS,IAAIA,WAAS,KAAK;AAE1C,EAAAC,YAAU,MAAM;AACd,QAAI,OAAO;AACX,qBAAiB,EAAE;AAAA,MACjB,CAAC,WAAW;AACV,YAAI,KAAM,WAAU,MAAM;AAAA,MAC5B;AAAA,MACA,MAAM;AACJ,YAAI,CAAC,KAAM;AACX,kBAAU,IAAI;AACd,iBAAS,OAAO;AAAA,UACd,OAAO;AAAA,UACP,SAAS;AAAA,UACT,QAAQ;AAAA,QACV,CAAC;AAAA,MACH;AAAA,IACF;AACA,WAAO,MAAM;AACX,aAAO;AAAA,IACT;AAAA,EACF,GAAG,CAAC,QAAQ,CAAC;AAIb,MAAI,QAAQ;AACV,WACE,gBAAAC,MAAC,UAAK,WAAU,mBAAkB,MAAK,UACpC,aAAG,yBAAyB,IAAI,gCAAgC,IACnE;AAAA,EAEJ;AACA,MAAI,WAAW,KAAM,QAAO;AAE5B,QAAM,WAAW,OAAO;AACxB,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,UAAU,MAAM;AAAA,MAChB,eAAe,MAAM,iBAAiB;AAAA,MACtC,SAAS,MAAM,WAAW;AAAA,MAC1B,UAAQ;AAAA,MACP,GAAI,MAAM,UAAU,SAAY,CAAC,IAAI,EAAE,OAAO,MAAM,MAAM;AAAA,MAC1D,GAAI,MAAM,sBAAsB,SAC7B,CAAC,IACD,EAAE,mBAAmB,MAAM,kBAAkB;AAAA,MAChD,GAAI,MAAM,cAAc,SAAY,CAAC,IAAI,EAAE,WAAW,MAAM,UAAU;AAAA;AAAA,EACzE;AAEJ;AAaO,SAAS,gBAAgB,OAAwC;AACtE,QAAM,EAAE,YAAY,IAAI,aAAa;AACrC,MAAI,gBAAgB,KAAM,QAAO;AACjC,SAAO,YAAY;AAAA,IACjB,WAAW,MAAM,aAAa;AAAA,EAChC,CAAC;AACH;AAaO,SAAS,oBAAoB,OAA4C;AAC9E,QAAM,EAAE,cAAc,MAAM,GAAG,MAAM,IAAI;AACzC,SACE,gBAAAC,OAAC,UAAK,WAAU,0BACb;AAAA,kBAAc,gBAAAD,MAAC,mBAAiB,GAAG,OAAO,IAAK;AAAA,IAChD,gBAAAA,MAAC,mBAAgB;AAAA,KACnB;AAEJ;","names":["part","frame","frame","useEffect","jsx","jsxs","jsx","notifier","useEffect","useEffect","useMemo","useRef","useState","useMemo","useState","useRef","useEffect","jsx","jsxs","jsx","useEffect","useMemo","useRef","useState","useCallback","useEffect","useRef","useState","useCallback","useEffect","useRef","useState","jsx","jsxs","useState","useRef","useEffect","useCallback","message","useState","useState","Fragment","jsx","jsxs","useState","beat","Fragment","jsx","jsxs","useState","jsx","jsxs","ChatMessageTurn","jsx","jsxs","useState","useMemo","useEffect","useState","useRef","useEffect","useCallback","useEffect","useRef","useState","formatFileSize","jsx","jsx","jsxs","formatFileSize","jsx","jsxs","createContext","useContext","useMemo","jsx","createContext","useMemo","useContext","useCallback","useRef","jsx","useCallback","useEffect","useRef","useState","useState","useRef","useEffect","useCallback","jsx","jsxs","useRef","useState","useEffect","useCallback","useEffect","useState","jsx","useCallback","useEffect","useMemo","useRef","useState","useSyncExternalStore","DEFAULT_ARCHIVE_FILTER","useState","DEFAULT_ARCHIVE_FILTER","useRef","useCallback","useEffect","useSyncExternalStore","useMemo","useEffect","useRef","useState","ErrorBox","jsx","jsxs","joinClass","ErrorBox","useState","useRef","useEffect","jsx","jsxs","joinClass","jsx","jsxs","useCallback","useEffect","useLayoutEffect","useRef","useState","useState","jsx","jsxs","useState","joinClass","useId","useState","Fragment","jsx","jsxs","useState","useId","joinClass","useId","useState","Fragment","jsx","jsxs","useState","useId","joinClass","useCallback","useEffect","Fragment","jsx","jsxs","useCallback","useEffect","joinClass","useEffect","useState","jsx","jsxs","useState","useEffect","jsx","jsxs"]}
|