@tangle-network/agent-app 0.45.63 → 0.45.65

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/web-react/index.tsx","../src/web-react/smooth-text.ts","../src/web-react/motion.ts","../src/web-react/brand-mark.tsx","../src/web-react/durable-plan-card.tsx","../src/web-react/interaction-question-card.tsx","../src/web-react/interaction-card-support.ts","../src/web-react/interaction-plan-card.tsx","../src/web-react/durable-chat-cards.tsx","../src/web-react/message-attachments.tsx","../src/web-react/chat-stream.ts","../src/web-react/chat-composer.tsx","../src/web-react/use-dictation.ts","../src/web-react/durable-plan-flow.ts","../src/web-react/durable-interaction-submit.ts","../src/web-react/use-chat-interactions.ts","../src/web-react/use-file-mentions.ts","../src/web-react/chat-mentions.ts","../src/web-react/mission-activity.tsx","../src/web-react/provenance.tsx","../src/web-react/provenance-model.ts","../src/web-react/seat-paywall.tsx","../src/web-react/session-history.tsx","../src/web-react/record-grid.tsx","../src/web-react/record-grid-model.ts","../src/web-react/command-palette.tsx","../src/web-react/class-names.ts","../src/web-react/sparkline.tsx","../src/web-react/insight-card.tsx"],"sourcesContent":["/**\n * `@tangle-network/agent-app/web-react` — the shared chat-shell components\n * every agent app's web UI hand-rolls: a model picker over the runtime's\n * model catalogue, a reasoning-effort selector, and a message thread with\n * User/Agent identity, per-message model + cost + tokens/sec metrics,\n * canonical tool rows, and a collapsible thinking section.\n *\n * Works for BOTH chat shapes: router-backed copilots (LoopEvents from\n * `runtime/openai-stream`) and sandbox-backed chats — the thread renders\n * `ChatUiMessage`s; how they're produced is the app's business.\n *\n * Styling contract: Tailwind classes against the shared design tokens\n * (`bg-card`, `border-border`, `text-muted-foreground`, `bg-primary`, …) that\n * Tangle app shells define. No icon library of its own — the few local glyphs\n * are inline SVGs. Markdown and provider logos are injected (`renderMarkdown`,\n * `renderProviderBadge`).\n *\n * Tool rows compose the canonical run-row grammar from `@tangle-network/ui`\n * (`InlineToolItem` over `RunRowShell`): `chatToolCallPart` adapts each\n * `ChatToolCallInfo` to ui's `ToolPart` (the same adapter pattern ui's own\n * `ToolCallStep` uses), so the chat surface and every other Tangle run view\n * share one row implementation instead of drifting. That makes\n * `@tangle-network/ui` a peer of this subpath.\n */\n\nimport { useEffect, useId, useMemo, useRef, useState, memo, type ReactNode } from 'react'\nimport { InlineToolItem, RunRowShell } from '@tangle-network/ui/run'\nimport type { ToolPart } from '@tangle-network/ui/types'\nimport { useSmoothText } from './smooth-text'\nimport { useArrivalStyle } from './motion'\nimport { BrainGlyph, ChevronDown, OVERLAY_SHADOW, POPOVER_OPTION_FOCUS, usePending } from './controls'\nimport { BrandMark } from './brand-mark'\nimport { DurableChatCards, type DurableChatCardsProps } from './durable-chat-cards'\nimport { attachmentPartsFromMessageParts, type ChatAttachmentPart } from './chat-attachments'\nimport { MessageAttachments } from './message-attachments'\nimport { WorkProductCard, workProductPartsFromMessageParts } from './work-product'\nimport type { WorkProductPersistedPart } from '../work-product/types'\n\nexport * from './chat-stream'\nexport * from './chat-interactions'\nexport * from './chat-composer'\nexport * from './composer-file-accept'\nexport * from './interaction-card-support'\nexport * from './interaction-question-card'\nexport * from './interaction-plan-card'\nexport * from './durable-plan-flow'\nexport * from './durable-plan-card'\nexport * from './durable-chat-cards'\nexport * from './durable-interaction-submit'\nexport * from './use-chat-interactions'\nexport * from './use-file-mentions'\nexport * from './chat-mentions'\nexport * from './chat-attachments'\nexport * from './message-attachments'\nexport * from './use-composer-attachments'\nexport * from './provider-logo'\nexport * from './harness-glyphs'\nexport * from './smooth-text'\nexport * from './mission-activity'\nexport * from './work-product'\nexport * from './provenance'\nexport * from './sandbox-terminal'\nexport * from './seat-paywall'\nexport * from './session-history'\nexport * from './record-grid'\nexport * from './command-palette'\nexport * from './sparkline'\nexport * from './insight-card'\nexport * from './use-dictation'\nexport {\n usePopover,\n usePending,\n PopoverSurface,\n POPOVER_SURFACE_ATTR,\n ModelPicker,\n EffortPicker,\n EffortMeter,\n effortMeterFill,\n effortLevelLabel,\n effortLevelsFromIds,\n reconcileEffortLevels,\n DEFAULT_EFFORT_LEVELS,\n EFFORT_METER_SEGMENTS,\n OVERLAY_SHADOW,\n type ModelPickerProps,\n type EffortPickerProps,\n type EffortLevel,\n type PopoverSurfaceProps,\n} from './controls'\nexport {\n AgentSessionControls,\n type AgentSessionControlsProps,\n} from './agent-session-controls'\nimport type { CatalogModel } from '../runtime/model-catalog'\n// Re-export the model type the chat components consume, so a web-react consumer\n// imports it from here rather than the package root.\nexport type { CatalogModel } from '../runtime/model-catalog'\n\n// ── metrics helpers ───────────────────────────────────────────────────────\n\n/** Describe metrics related to a chat message including model, token counts, and duration */\nexport interface ChatMessageMetrics {\n modelUsed?: string\n promptTokens?: number\n completionTokens?: number\n durationMs?: number\n}\n\n/** \"$0.0042\" from token counts × catalogue per-token pricing; null when unknown. */\nexport function formatModelCost(msg: ChatMessageMetrics, models: CatalogModel[]): string | null {\n if (msg.promptTokens == null && msg.completionTokens == null) return null\n const pricing = models.find((m) => m.id === msg.modelUsed)?.pricing\n if (!pricing) return null\n const cost =\n (msg.promptTokens ?? 0) * Number(pricing.prompt ?? 0) +\n (msg.completionTokens ?? 0) * Number(pricing.completion ?? 0)\n if (!isFinite(cost) || cost <= 0) return null\n return cost < 0.01 ? `$${cost.toFixed(4)}` : `$${cost.toFixed(2)}`\n}\n\n/** One-line preview of a reasoning trace for the collapsed row's description\n * slot: whitespace collapsed, hard-truncated with an ellipsis. */\nfunction reasoningPreview(reasoning: string): string | undefined {\n const flat = reasoning.replace(/\\s+/g, ' ').trim()\n if (!flat) return undefined\n return flat.length > 120 ? `${flat.slice(0, 119)}…` : flat\n}\n\n/** \"38 tok/s\" from completion tokens over first-token→end duration; null when unknown. */\nexport function formatTokensPerSecond(msg: ChatMessageMetrics): string | null {\n if (msg.completionTokens == null || !msg.durationMs) return null\n return `${Math.round(msg.completionTokens / (msg.durationMs / 1000))} tok/s`\n}\n\n// ── Tool run drill-in (retained runs) ─────────────────────────────────────\n\n/** One step of a retained tool run (e.g. a sandbox command + its output). */\nexport interface ToolRunStep {\n at: string\n label: string\n detail?: string\n status?: 'ok' | 'error'\n}\n\n/** A retained tool run keyed by the parent message's toolCallId. The product\n * persists these server-side (fail-closed: only ids its own loop created)\n * and serves them to the drill-in panel. */\nexport interface ToolRunRecord {\n toolCallId: string\n toolName: string\n title: string\n status: 'running' | 'complete' | 'error'\n steps: ToolRunStep[]\n}\n\n/** Define properties required to run a drill and handle its closure event */\nexport interface RunDrillInProps {\n run: ToolRunRecord\n onClose: () => void\n}\n\n/**\n * Readonly side panel showing a retained tool run's transcript — the\n * \"drill into what the sandbox actually did\" view. Follow-ups happen in the\n * main chat, never here.\n */\nexport function RunDrillIn({ run, onClose }: RunDrillInProps) {\n return (\n <div className={`fixed inset-y-0 right-0 z-50 flex w-[480px] max-w-full flex-col border-l border-card-edge bg-popover ${OVERLAY_SHADOW}`}>\n <div className=\"flex items-center gap-2 border-b border-border px-4 py-3\">\n <span\n className={`h-2 w-2 shrink-0 rounded-full ${\n run.status === 'running' ? 'bg-warning' : run.status === 'error' ? 'bg-destructive' : 'bg-success'\n }`}\n />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-[15px] font-semibold\">{run.title}</p>\n <p className=\"truncate font-mono text-xs text-muted-foreground\">{run.toolName}</p>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n aria-label=\"Close\"\n className=\"rounded-md p-1.5 text-muted-foreground transition hover:bg-accent hover:text-foreground\"\n >\n <svg className=\"h-4 w-4\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n </button>\n </div>\n <div className=\"flex-1 space-y-3 overflow-y-auto p-4\">\n {run.steps.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">No steps recorded yet.</p>\n )}\n {run.steps.map((step, i) => (\n <div key={i} className=\"rounded-lg border border-card-edge bg-card\">\n <div className=\"flex items-baseline gap-2 border-b border-border px-3 py-1.5\">\n <span className={`font-mono text-xs ${step.status === 'error' ? 'text-destructive' : 'text-muted-foreground'}`}>\n {step.status === 'error' ? '✗' : '$'}\n </span>\n <code className=\"min-w-0 flex-1 truncate font-mono text-xs\">{step.label}</code>\n <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\">\n {new Date(step.at).toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })}\n </span>\n </div>\n {step.detail && (\n <pre className=\"max-h-48 overflow-auto whitespace-pre-wrap px-3 py-2 font-mono text-xs leading-relaxed text-muted-foreground\">\n {step.detail}\n </pre>\n )}\n </div>\n ))}\n </div>\n <p className=\"border-t border-border px-4 py-2 text-xs text-muted-foreground\">\n Read-only transcript — reply in the main chat.\n </p>\n </div>\n )\n}\n\n// ── ChatMessages ──────────────────────────────────────────────────────────\n\n/** Describe the structure and state of a tool call within a chat interaction */\nexport interface ChatToolCallInfo {\n id: string\n name: string\n status: 'running' | 'done' | 'error'\n /** The call arguments, captured from the tool_call event — shown in the\n * expanded card so users see exactly what the agent invoked. */\n args?: Record<string, unknown>\n /** The tool outcome (`{ok, result}` shape). When `result.status` is\n * 'queued_for_approval' the card renders the approval state. */\n result?: unknown\n}\n\n/** Extract `{proposalId, status}` from a tool outcome when it is a proposal\n * awaiting human approval; null otherwise. */\nexport function pendingApprovalOf(call: ChatToolCallInfo): { proposalId: string } | null {\n const outcome = call.result as { ok?: boolean; result?: { status?: string; proposalId?: string } } | undefined\n if (!outcome?.ok || outcome.result?.status !== 'queued_for_approval' || !outcome.result.proposalId) return null\n return { proposalId: outcome.result.proposalId }\n}\n\n/** One ordered piece of an assistant turn: a run of answer text, or a tool\n * call, in the sequence the agent emitted them. A message carrying `segments`\n * is rendered in order — interleaving text and tool rows — so the agent's\n * pre- and post-tool reasoning reads chronologically instead of as one text\n * blob with the tool rows collected after it. */\nexport type ChatMessageSegment =\n | { kind: 'text'; content: string }\n | { kind: 'tool'; call: ChatToolCallInfo }\n\n/** Describe the structure and properties of a chat message with roles, content, and optional metadata */\nexport interface ChatUiMessage extends ChatMessageMetrics {\n id: string\n role: 'user' | 'assistant' | 'system'\n content: string\n reasoning?: string\n toolCalls?: ChatToolCallInfo[]\n /** Ordered text/tool sequence for true chronological interleaving. When\n * present and non-empty it is rendered in place of `content` + `toolCalls`;\n * both remain the fallback for producers that don't segment a turn. */\n segments?: ChatMessageSegment[]\n /** Persisted assistant parts. When `ChatMessages.durableCards` is supplied,\n * shared plan/question cards render directly from these projections. */\n parts?: Array<Record<string, unknown>>\n}\n\n/** Define properties for rendering chat messages with optional models, markdown, extras, and durable cards */\nexport interface ChatMessagesProps {\n messages: ChatUiMessage[]\n /** Shared reading scale for both user and assistant prose. Defaults to 16px\n * at a 1.6 line height; `large` uses 17px at the same leading without\n * enlarging labels, tool chrome, or metadata. */\n messageSize?: 'default' | 'large'\n /** Transcript chrome. `labeled` (default) keeps the always-on role label +\n * model/tok-s/cost meta line and the primary-tinted user bubble. `quiet`\n * drops the label row into a fixed-height meta lane at each row's bottom\n * (copy + the demoted meta, revealed on hover/focus, always visible on\n * touch) and renders user bubbles neutral with a symmetric radius.\n * Everything else — tool rows, reasoning, streaming — is identical. */\n chrome?: 'labeled' | 'quiet'\n /** Catalogue models, for per-message cost from pricing. Pass [] to skip cost. */\n models?: CatalogModel[]\n /** Markdown renderer for assistant content; default renders pre-wrapped text. */\n renderMarkdown?: (content: string) => ReactNode\n /** Extra per-message content (artifacts, custom panels) appended after the body. */\n renderExtras?: (message: ChatUiMessage) => ReactNode\n /** Canonical durable plan/question card wiring. Apps inject only transport,\n * access, and optional visual callbacks; card selection/dedupe stays shared. */\n durableCards?: Omit<DurableChatCardsProps, 'parts' | 'renderMarkdown'>\n userLabel?: string\n agentLabel?: string\n /** Render the trailing \"agent is thinking\" row. */\n loading?: boolean\n /** Approve/Reject handlers for proposals awaiting approval. When omitted the\n * card still shows \"awaiting approval\" but without action buttons. */\n approval?: ProposalApprovalHandlers\n /** Open a full-transcript view (e.g. {@link RunDrillIn}) from a tool row's\n * actions slot. */\n onToolCallClick?: (call: ChatToolCallInfo, message: ChatUiMessage) => void\n /** Per-tool custom detail renderers for the expanded tool row body. */\n toolRenderers?: ToolDetailRenderers\n /** Stream-error affordance: when the turn failed (a thrown transport error or\n * a loop-level `onErrorEvent`), pass the message here to render an error row.\n * A failed turn otherwise just stops with no UI signal. */\n error?: string | null\n /** Retry control shown on the error row; omit to render the error without a\n * retry button (e.g. when the product retries automatically). */\n onRetry?: () => void\n /** Zero-state renderer, shown when there are no messages and the turn is\n * neither loading nor errored. When omitted, a branded first-run state is\n * shown ({@link ChatEmptyState}); pass `() => null` to render nothing. */\n renderEmpty?: () => ReactNode\n /** First-run state config used when `renderEmpty` is not supplied. Lets a\n * product set the headline and the \"doors\" (e.g. start from a template, ask\n * the agent) without replacing the whole zero-state. */\n emptyState?: ChatEmptyStateProps\n /** Optional branded header slot rendered above the thread. Off by default to\n * preserve the current layout; pass `{ title }` (or your own node via\n * `header`) to show the Tangle mark + product title in the chat shell. */\n header?: ReactNode\n /** Resolve a raw-bytes download URL for one attachment part. When set, any\n * message carrying attachment parts (`file`/`image` parts with a `path`,\n * see `attachmentPartsFromMessageParts`) renders them as a `MessageAttachments`\n * row next to the bubble — thumbnails for images, download chips for files.\n * Absent → today's rendering, byte-identical (no attachment row). */\n resolveAttachmentUrl?: (part: ChatAttachmentPart) => string\n /** Render persisted `type:'work_product'` anchor parts as `WorkProductCard`\n * rows under the message body — the chat card that keeps chat the driver\n * surface for review. `onOpen` opens the product's queue/detail surface.\n * Absent → today's rendering, byte-identical (no card row). */\n workProductCards?: { onOpen?: (part: WorkProductPersistedPart) => void }\n}\n\n/** One starting \"door\" in the chat first-run state — a concrete, labeled action\n * (start from a template, do it by hand, ask the agent), not a placeholder. */\nexport interface ChatEmptyDoor {\n label: string\n description?: string\n onSelect: () => void\n /** Optional glyph rendered left of the label. */\n icon?: ReactNode\n}\n\n/** Define properties for rendering the chat empty state with customizable text and starting doors */\nexport interface ChatEmptyStateProps {\n /** Product name shown next to the Tangle mark. Default \"Agent\". */\n productName?: string\n /** Headline. Default frames delegation, not messaging. */\n headline?: string\n /** Subline under the headline. */\n subline?: string\n /** Up to three concrete starting doors. Omit for a mark-and-prompt-only state. */\n doors?: ChatEmptyDoor[]\n}\n\n/**\n * Branded chat first-run state: the Tangle mark, a delegation-framed prompt, and\n * up to three concrete doors. Replaces the blank thread that read as \"empty or\n * broken\". Concrete + actionable — never a \"coming soon\" placeholder.\n */\nexport function ChatEmptyState({\n productName = 'Agent',\n headline = 'Ask the agent to do something',\n subline = 'Describe the outcome you want. The agent works through it step by step, and pauses for your approval before anything irreversible.',\n doors,\n}: ChatEmptyStateProps) {\n // Column count follows the doors actually offered: one door centers on a\n // narrow measure, two split evenly, three take the full row — a lone door\n // stretched across three tracks read as a mistake, not a choice.\n const doorCount = Math.min(doors?.length ?? 0, 3)\n const doorsGridClass =\n doorCount === 1\n ? 'mx-auto max-w-sm sm:grid-cols-1'\n : doorCount === 2\n ? 'sm:grid-cols-2'\n : 'sm:grid-cols-3'\n return (\n <div className=\"mx-auto flex w-full max-w-2xl flex-col items-center px-6 py-12 text-center sm:py-20\">\n <span className=\"mb-5 inline-flex h-14 w-14 items-center justify-center rounded-2xl bg-primary/10 ring-1 ring-primary/15\">\n <BrandMark size={32} className=\"shrink-0\" />\n </span>\n <p className=\"text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground\">{productName}</p>\n <h2 className=\"mt-1.5 text-balance text-2xl font-semibold leading-tight text-foreground\">\n {headline}\n </h2>\n {subline && <p className=\"mt-3 max-w-md text-[15px] leading-relaxed text-muted-foreground\">{subline}</p>}\n {doors && doors.length > 0 && (\n <div className={`mt-7 grid w-full gap-2.5 ${doorsGridClass}`}>\n {doors.slice(0, 3).map((door, i) => (\n <button\n key={i}\n type=\"button\"\n onClick={door.onSelect}\n className=\"group flex min-h-[44px] flex-col items-start rounded-xl border border-border bg-card px-4 py-3 text-left transition hover:border-primary/40 hover:bg-accent\"\n >\n <span className=\"flex items-center gap-2 text-sm font-semibold text-foreground\">\n {door.icon}\n {door.label}\n </span>\n {door.description && (\n <span className=\"mt-0.5 text-[12px] leading-snug text-muted-foreground\">{door.description}</span>\n )}\n </button>\n ))}\n </div>\n )}\n </div>\n )\n}\n\n/** Handle approval and rejection actions for proposals with asynchronous support */\nexport interface ProposalApprovalHandlers {\n onApprove: (proposalId: string, toolCallId: string) => void | Promise<void>\n onReject: (proposalId: string, toolCallId: string) => void | Promise<void>\n}\n\n/** Per-tool custom detail renderers for the expanded card body — keyed by\n * tool name. Return null to fall back to the generic detail view. */\nexport type ToolDetailRenderers = Record<\n string,\n (call: ChatToolCallInfo, message: ChatUiMessage) => ReactNode\n>\n\nfunction ToolGlyph({ name, className }: { name: string; className?: string }) {\n if (name.startsWith('sandbox_')) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <polyline points=\"4 17 10 11 4 5\" />\n <line x1=\"12\" y1=\"19\" x2=\"20\" y2=\"19\" />\n </svg>\n )\n }\n if (name === 'submit_proposal') {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\" />\n <path d=\"M14 2v6h6M9 15l2 2 4-4\" />\n </svg>\n )\n }\n if (name === 'schedule_followup') {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <circle cx=\"12\" cy=\"12\" r=\"9\" />\n <path d=\"M12 7v5l3 3\" />\n </svg>\n )\n }\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M12 3v3m0 12v3M3 12h3m12 0h3\" />\n <circle cx=\"12\" cy=\"12\" r=\"4\" />\n </svg>\n )\n}\n\nfunction toolOutcomeOf(call: ChatToolCallInfo): { ok?: boolean; result?: Record<string, unknown>; message?: string } | undefined {\n return call.result as { ok?: boolean; result?: Record<string, unknown>; message?: string } | undefined\n}\n\n/** A call that failed — by status OR by an `ok:false` outcome envelope. Shared\n * by the row adapter, the collapse guard, and the card itself so the three can\n * never disagree about what \"failed\" means. */\nfunction toolCallFailed(call: ChatToolCallInfo): boolean {\n return call.status === 'error' || toolOutcomeOf(call)?.ok === false\n}\n\n/**\n * Adapt a chat tool call to the canonical `@tangle-network/ui` `ToolPart`, so\n * the row renders through ui's `InlineToolItem` — one run-row grammar shared\n * with every other Tangle run view (the same adapter pattern ui's own\n * `ToolCallStep` uses for its flat props). `sandbox_run_command` maps to ui's\n * canonical `bash` name so the row takes the command category (terminal icon);\n * every other tool keeps its real name. agent-app carries no per-call timings,\n * so `state.time` stays unset and the row shows no duration.\n */\nexport function chatToolCallPart(call: ChatToolCallInfo): ToolPart {\n const failed = toolCallFailed(call)\n return {\n type: 'tool',\n id: call.id,\n tool: call.name === 'sandbox_run_command' ? 'bash' : call.name,\n state: {\n status: call.status === 'running' ? 'running' : failed ? 'error' : 'completed',\n input: call.args,\n output: call.result,\n error: failed ? (toolOutcomeOf(call)?.message ?? 'Tool failed') : undefined,\n },\n }\n}\n\n/** The four visual kinds a tool call presents as. They are *different kinds of\n * thing* (audit chat finding #3/#4) and must read differently: a command is a\n * past-tense action, a proposal is a pending decision, a follow-up is a\n * scheduled intent, everything else is a generic tool step. Derived from the\n * tool name + outcome, never from baked domain values. */\ntype BlockKind = 'command' | 'proposal' | 'followup' | 'generic'\n\nfunction blockKindOf(call: ChatToolCallInfo): BlockKind {\n if (call.name === 'submit_proposal') return 'proposal'\n if (call.name === 'schedule_followup') return 'followup'\n if (call.name.startsWith('sandbox_')) return 'command'\n return 'generic'\n}\n\n/** Humanize an otherwise-unmapped tool name for display: `get_credit_balance`\n * → \"Get credit balance\". Splits on separators and camelCase, then sentence-\n * cases — domain-agnostic, so a host's tool reads as a label without this\n * shared renderer knowing that host's tool taxonomy. Falls back to the raw name\n * when there's nothing to humanize. */\nfunction humanizeToolName(name: string): string {\n const words = name\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .replace(/[_-]+/g, ' ')\n .trim()\n if (!words) return name\n return words.charAt(0).toUpperCase() + words.slice(1)\n}\n\n/** Human title for a call, derived from its real arguments. Proposals lead with\n * the decision verb (docs/product-surfaces.md) rather than the internal tool\n * taxonomy, so the user reads \"Approve: publish …?\" not \"submit_proposal\". An\n * unmapped tool falls back to its humanized name rather than the raw slug. */\nfunction friendlyToolTitle(call: ChatToolCallInfo): string {\n const a = call.args ?? {}\n switch (call.name) {\n case 'submit_proposal':\n return a.title ? `Approve: ${String(a.title)}?` : 'Approve this action?'\n case 'sandbox_create':\n return `Created sandbox (${String(a.environment ?? 'universal')})`\n case 'sandbox_run_command':\n return `Ran ${String(a.command ?? 'command')}`\n case 'sandbox_destroy':\n return `Destroyed sandbox ${String(a.sandbox_id ?? '')}`\n case 'schedule_followup':\n return `Scheduled: ${String(a.title ?? 'follow-up')}`\n case 'render_ui':\n return `Rendered view · ${String(a.title ?? '')}`\n case 'add_citation':\n return `Cited ${String(a.path ?? '')}`\n default:\n return humanizeToolName(call.name)\n }\n}\n\n/** A one-line, plain-English preview of WHAT a proposal will do, assembled from\n * the proposal's real arguments (audit chat finding #2 — \"approving a black box\n * is the fastest way to lose trust\"). Domain stays a parameter: we only read\n * conventional fields (destinations/targets/channels, cost, reach) when present\n * — nothing here is baked to a specific product's proposal type. When the agent\n * wrote no summary, the preview is the proposal's TYPE slug rendered mono —\n * never a \"type: title\" derivation, which restated the title the card header\n * already carries. Returns nulls when there's nothing meaningful to preview. */\nfunction proposalPreview(call: ChatToolCallInfo): { summary: string | null; meta: string[]; typeSlug: string | null } {\n const a = (call.args ?? {}) as Record<string, unknown>\n const asString = (v: unknown): string | null =>\n typeof v === 'string' && v.trim() ? v.trim() : null\n const asList = (v: unknown): string[] =>\n Array.isArray(v)\n ? v.map((x) => (typeof x === 'string' ? x : null)).filter((x): x is string => !!x)\n : asString(v)\n ? [asString(v) as string]\n : []\n\n // The verb: only a free-form summary the agent actually wrote.\n const verbPhrase = asString(a.summary) ?? asString(a.description) ?? null\n\n const destinations = [\n ...asList(a.destinations),\n ...asList(a.channels),\n ...asList(a.targets),\n ...asList(a.platforms),\n ]\n const dest = destinations.length ? ` to ${destinations.join(' and ')}` : ''\n const summary = verbPhrase ? `${verbPhrase}${dest}` : destinations.length ? `Publish to ${destinations.join(' and ')}` : null\n // No authored summary: the raw type slug (`asset_publish`) says what kind of\n // action this is without duplicating the header's \"Approve: {title}?\".\n const typeSlug = summary === null ? asString(a.type) : null\n\n // Cost / reach: surfaced when the data carries it, formatted lightly.\n const meta: string[] = []\n const cost = a.cost ?? a.price ?? a.estimatedCost\n if (typeof cost === 'number' && cost > 0) meta.push(`~$${cost < 0.01 ? cost.toFixed(4) : cost.toFixed(2)}`)\n else if (asString(cost)) meta.push(asString(cost) as string)\n const reach = a.reach ?? a.audience ?? a.estimatedReach\n if (typeof reach === 'number' && reach > 0) meta.push(`reaches ~${reach.toLocaleString()}`)\n else if (asString(reach)) meta.push(asString(reach) as string)\n\n return { summary, meta, typeSlug }\n}\n\nfunction truncate(v: unknown, max = 240): string {\n const s = typeof v === 'string' ? v : JSON.stringify(v)\n // JSON.stringify returns undefined (not a string) for undefined / functions /\n // symbols — a non-envelope tool output (a bare string, or a result with no\n // `.result`) lands here. Coerce to '' so we never read `.length` off undefined\n // and crash the whole chat surface.\n if (typeof s !== 'string') return ''\n return s.length > max ? `${s.slice(0, max)}…` : s\n}\n\nfunction KvRows({ data }: { data: Record<string, unknown> }) {\n const entries = Object.entries(data).filter(([, v]) => v !== undefined && v !== null && v !== '')\n if (!entries.length) return null\n return (\n <dl className=\"grid grid-cols-[auto_1fr] gap-x-3 gap-y-1\">\n {entries.map(([k, v]) => (\n <div key={k} className=\"contents\">\n <dt className=\"font-mono text-xs text-muted-foreground\">{k}</dt>\n <dd className=\"min-w-0 whitespace-pre-wrap break-words font-mono text-xs text-muted-foreground\">\n {truncate(v)}\n </dd>\n </div>\n ))}\n </dl>\n )\n}\n\n/** Terminal-styled rendering for shell executions. */\nfunction ShellDetail({ call }: { call: ChatToolCallInfo }) {\n const outcome = toolOutcomeOf(call)\n const r = (outcome?.result ?? {}) as { stdout?: string; stderr?: string; exitCode?: number }\n return (\n <div className=\"overflow-hidden rounded-md bg-zinc-900 font-mono text-xs leading-relaxed\">\n <div className=\"flex items-center gap-2 px-3 pt-2 text-zinc-400\">\n <span className=\"select-none text-zinc-500\">$</span>\n <span className=\"min-w-0 flex-1 truncate text-zinc-200\">{String(call.args?.command ?? '')}</span>\n {r.exitCode != null && (\n <span className={r.exitCode === 0 ? 'text-success' : 'text-destructive'}>exit {r.exitCode}</span>\n )}\n </div>\n <pre className=\"max-h-56 overflow-auto whitespace-pre-wrap px-3 pb-2.5 pt-1.5 text-zinc-300\">\n {outcome?.ok === false ? (outcome.message ?? 'failed') : [r.stdout, r.stderr].filter(Boolean).join('\\n') || '(no output)'}\n </pre>\n </div>\n )\n}\n\n/** Generic expanded detail: what was called, and what actually happened. */\nfunction DefaultToolDetail({ call }: { call: ChatToolCallInfo }) {\n // A tool result is the `{ ok, result }` proposal envelope ONLY when it is an\n // object. bash/skill/python outputs arrive as a bare string (or nothing at\n // all), so reading `.ok`/`.result` off them is meaningless — those render as\n // their raw value, not through the envelope branch.\n const result: unknown = call.result\n const envelope =\n typeof result === 'object' && result !== null\n ? (result as { ok?: boolean; result?: unknown; message?: string })\n : null\n return (\n <div className=\"space-y-2\">\n {call.args && Object.keys(call.args).length > 0 && (\n <div>\n <p className=\"mb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground\">Called with</p>\n <KvRows data={call.args} />\n </div>\n )}\n {envelope ? (\n <div>\n <p className=\"mb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground\">\n {envelope.ok === false ? 'Failed' : 'Result'}\n </p>\n {envelope.ok === false ? (\n <p className=\"text-xs text-destructive\">{envelope.message ?? 'Tool failed'}</p>\n ) : envelope.result && typeof envelope.result === 'object' ? (\n <KvRows data={envelope.result as Record<string, unknown>} />\n ) : envelope.result != null ? (\n <p className=\"font-mono text-xs text-muted-foreground\">{truncate(envelope.result)}</p>\n ) : null}\n </div>\n ) : result != null ? (\n <div>\n <p className=\"mb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground\">Result</p>\n <p className=\"font-mono text-xs text-muted-foreground\">{truncate(result)}</p>\n </div>\n ) : null}\n </div>\n )\n}\n\n/** The pending-decision card. The single highest-leverage surface in the repo\n * (audit chat finding #1, critical): Approve is the affirmative path — filled,\n * brand-colored, primary — and Reject is quiet/outline, so a user never reads\n * both labels twice to know the safe action. Carries a plain-English preview of\n * WHAT it will do (#2). `onApprove`/`onReject` are unchanged. */\nfunction ProposalCard({\n call,\n message,\n pending,\n approval,\n renderers,\n}: {\n call: ChatToolCallInfo\n message: ChatUiMessage\n pending: { proposalId: string }\n approval?: ProposalApprovalHandlers\n renderers?: ToolDetailRenderers\n}) {\n const [expanded, setExpanded] = useState(false)\n const { summary, meta, typeSlug } = proposalPreview(call)\n const custom = renderers?.[call.name]?.(call, message)\n const { pending: deciding, run: decide } = usePending()\n\n return (\n <div className=\"w-full max-w-full rounded-xl border border-warning/50 bg-warning/[0.06] text-sm shadow-sm ring-1 ring-warning/10\">\n <div className=\"flex items-start gap-2.5 px-4 pt-3.5\">\n <span className=\"mt-0.5 inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-warning/15 text-warning\">\n <ToolGlyph name={call.name} className=\"h-3.5 w-3.5\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n {/* Without handlers the card is read-only: the eyebrow reports the\n state (\"Awaiting approval\") instead of demanding an action the\n viewer cannot take, and the footer adds no second note. */}\n <p className=\"text-xs font-semibold uppercase tracking-[0.05em] text-warning-strong\">\n {approval ? 'Needs your approval' : 'Awaiting approval'}\n </p>\n <p className=\"mt-0.5 text-[15px] font-semibold leading-snug text-foreground\">{friendlyToolTitle(call)}</p>\n {summary && <p className=\"mt-1 text-xs leading-relaxed text-muted-foreground\">{summary}</p>}\n {typeSlug && <p className=\"mt-1 font-mono text-xs text-muted-foreground\">{typeSlug}</p>}\n {meta.length > 0 && (\n <div className=\"mt-1.5 flex flex-wrap items-center gap-1.5\">\n {meta.map((m, i) => (\n <span key={i} className=\"rounded-full bg-secondary px-2 py-0.5 text-xs font-medium text-muted-foreground\">\n {m}\n </span>\n ))}\n </div>\n )}\n </div>\n </div>\n <div className=\"flex flex-wrap items-center gap-2 px-4 pb-3.5 pt-3\">\n {approval && (\n <>\n <button\n type=\"button\"\n disabled={deciding}\n onClick={() => decide(() => approval.onApprove(pending.proposalId, call.id))}\n className=\"inline-flex min-h-[40px] flex-1 items-center justify-center rounded-lg bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-sm transition hover:bg-primary/90 disabled:cursor-not-allowed disabled:opacity-60 sm:flex-none sm:min-w-[160px]\"\n >\n Approve &amp; run\n </button>\n <button\n type=\"button\"\n disabled={deciding}\n onClick={() => decide(() => approval.onReject(pending.proposalId, call.id))}\n className=\"inline-flex min-h-[40px] items-center justify-center rounded-lg border border-border bg-transparent px-4 py-2 text-sm font-medium text-muted-foreground transition hover:bg-accent hover:text-foreground disabled:cursor-not-allowed disabled:opacity-60\"\n >\n Reject\n </button>\n </>\n )}\n <button\n type=\"button\"\n onClick={() => setExpanded((v) => !v)}\n aria-expanded={expanded}\n className=\"ml-auto inline-flex items-center gap-1 rounded text-[12px] font-medium text-muted-foreground transition hover:text-foreground\"\n >\n {expanded ? 'Hide details' : 'View details'}\n <ChevronDown className={`h-3 w-3 transition-transform ${expanded ? 'rotate-180' : ''}`} />\n </button>\n </div>\n {expanded && (\n <div className=\"border-t border-warning/20 px-4 py-3 text-xs\">\n {custom ?? <DefaultToolDetail call={call} />}\n </div>\n )}\n </div>\n )\n}\n\n/** \"Mon, Jun 22 · 9:00 AM\" for an ISO timestamp; anything unparseable renders\n * as written — a producer's \"Tomorrow 9am\" is already human copy. The raw\n * value stays on the row's `title` for the exact instant. */\nfunction formatFollowupWhen(when: string): string {\n const date = new Date(when)\n if (Number.isNaN(date.getTime())) return when\n const day = date.toLocaleDateString([], { weekday: 'short', month: 'short', day: 'numeric' })\n const time = date.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })\n return `${day} · ${time}`\n}\n\n/** A scheduled follow-up — a pending, time-based intent, not a decision and not\n * a completed action (audit finding #5). Re-skinned onto the shared run-row\n * geometry (full width, 24px icon chip, xs title, `when` in the description\n * slot) so it reads as a sibling of the canonical tool rows; it is not a tool\n * invocation, so the row does not expand. It carries the same trailing status\n * cue its siblings do (spinner while the schedule call is in flight, a settled\n * dot after), and a FAILED schedule keeps the follow-up identity — the clock —\n * with the failure reported in place instead of falling back to a generic\n * tool row. */\nfunction FollowupCard({ call }: { call: ChatToolCallInfo }) {\n const a = (call.args ?? {}) as Record<string, unknown>\n const when = typeof a.when === 'string' ? a.when : typeof a.at === 'string' ? a.at : typeof a.schedule === 'string' ? a.schedule : null\n const failed = toolCallFailed(call)\n const errorText = failed ? (toolOutcomeOf(call)?.message ?? 'Scheduling failed') : null\n return (\n <div className=\"flex items-start gap-2\">\n <div className=\"min-w-0 flex-1 overflow-hidden rounded-[var(--radius-lg)] border border-[var(--border-subtle)] bg-[var(--md3-surface-container)]\">\n <div className=\"flex w-full items-center gap-2.5 px-3 py-2\">\n <span className=\"flex h-6 w-6 shrink-0 items-center justify-center rounded-[var(--radius-sm)] border border-border bg-muted text-muted-foreground\">\n <ToolGlyph name={call.name} className=\"h-3.5 w-3.5\" />\n </span>\n <span className=\"shrink-0 whitespace-nowrap text-xs font-medium text-foreground\">{friendlyToolTitle(call)}</span>\n {when && (\n <span title={when} className=\"hidden min-w-0 flex-1 truncate font-mono text-xs tabular-nums text-muted-foreground sm:inline\">\n {formatFollowupWhen(when)}\n </span>\n )}\n <span className=\"ml-auto flex shrink-0 items-center gap-1.5\">\n {call.status === 'running' ? (\n <svg className=\"h-3 w-3 shrink-0 animate-spin text-[var(--accent-text)]\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" aria-hidden>\n <path d=\"M21 12a9 9 0 1 1-6.219-8.56\" strokeLinecap=\"round\" />\n </svg>\n ) : (\n <span className={`h-1.5 w-1.5 shrink-0 rounded-full ${failed ? 'bg-[var(--surface-danger-text)]' : 'bg-[var(--surface-success-text)]'}`} />\n )}\n </span>\n </div>\n {errorText && (\n <div className=\"border-t border-border px-3 py-2 text-xs text-[var(--surface-danger-text)]\">{errorText}</div>\n )}\n </div>\n </div>\n )\n}\n\n/** Row title: short and past-tense. A command's text moves to the canonical\n * mono description slot instead of filling the title. */\nfunction toolRowTitle(call: ChatToolCallInfo): string {\n if (call.name === 'sandbox_run_command') return 'Ran command'\n return friendlyToolTitle(call)\n}\n\n/** The collapsed row's inline description — the canonical row's mono slot. Only\n * a command has a natural one-liner; every other tool's title already carries\n * its identifying argument. */\nfunction toolRowDescription(call: ChatToolCallInfo): string | undefined {\n if (call.name !== 'sandbox_run_command') return undefined\n const command = call.args?.command\n return typeof command === 'string' && command ? command : undefined\n}\n\nfunction ToolCallCard({\n call,\n message,\n approval,\n onOpenRun,\n renderers,\n staggerIndex,\n}: {\n call: ChatToolCallInfo\n message: ChatUiMessage\n approval?: ProposalApprovalHandlers\n onOpenRun?: (call: ChatToolCallInfo, message: ChatUiMessage) => void\n renderers?: ToolDetailRenderers\n /** Position in the surrounding run, so a group of rows arrives as a\n * sequence. Frozen at mount by `useArrivalStyle`. */\n staggerIndex?: number\n}) {\n const arrival = useArrivalStyle(staggerIndex ?? 0)\n const pending = call.status === 'done' ? pendingApprovalOf(call) : null\n const kind = blockKindOf(call)\n\n // One wrapper for all three row shapes, because `InlineToolItem` takes a\n // `className` but no `style`, and the stagger index has to ride an element\n // this package controls. The row arrives ONCE: a call going running → done →\n // failed re-renders this same node (its key is the call id, never its\n // status), and a CSS animation does not replay on a re-render.\n const arrive = (row: ReactNode) => (\n <div className=\"agent-arrive\" style={arrival}>\n {row}\n </div>\n )\n\n // A proposal awaiting approval is a pending DECISION, not a tool row — it\n // keeps its own prominent card with primary Approve / quiet Reject.\n if (pending) {\n return arrive(\n <ProposalCard\n call={call}\n message={message}\n pending={pending}\n approval={approval}\n renderers={renderers}\n />,\n )\n }\n // A scheduled follow-up is a time-based intent — its own quiet row, distinct\n // from a tool invocation. A FAILED schedule keeps the row too (destructive\n // dot + error subline) rather than falling through to the generic tool row\n // and losing the clock identity.\n if (kind === 'followup') {\n return arrive(<FollowupCard call={call} />)\n }\n\n // Command and generic tool calls render through the canonical InlineToolItem\n // row (category icon chip, title, mono description, status, hairline expand).\n // The expanded body stays agent-app's — the host's custom renderer, the\n // terminal ShellDetail for commands, or the generic args/result view — via\n // ui's `renderToolDetail` seam, and `onOpenRun` maps to the row's actions\n // slot, so no agent-app capability is lost to the shared chrome.\n const custom = renderers?.[call.name]?.(call, message)\n return arrive(\n <InlineToolItem\n part={chatToolCallPart(call)}\n title={toolRowTitle(call)}\n description={toolRowDescription(call)}\n renderToolDetail={() =>\n custom ??\n (call.name === 'sandbox_run_command' ? <ShellDetail call={call} /> : <DefaultToolDetail call={call} />)\n }\n actions={\n onOpenRun && call.name.startsWith('sandbox_') ? (\n <button\n type=\"button\"\n onClick={() => onOpenRun(call, message)}\n aria-label=\"Open full transcript\"\n title=\"Open full transcript\"\n className=\"rounded-md p-1.5 text-muted-foreground transition hover:bg-accent hover:text-foreground\"\n >\n <svg className=\"h-3.5 w-3.5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M7 17 17 7\" />\n <path d=\"M7 7h10v10\" />\n </svg>\n </button>\n ) : undefined\n }\n />,\n )\n}\n\n/** The blinking insertion caret shown at the end of streaming answer text.\n * Shared by the segmented and legacy branches so their streaming cue can't\n * visually diverge. */\nfunction StreamingCaret() {\n return (\n <span\n // `animate-pulse` is a 2s ease-in-out opacity fade — a breathing\n // placeholder, not a caret. A caret is a hard 1s step blink, which is\n // what every text surface the user has ever typed into does.\n className=\"ml-0.5 inline-block h-[1.1em] w-[3px] translate-y-[2px] animate-[agent-caret_1s_step-end_infinite] rounded-sm bg-foreground/70\"\n data-motion=\"essential\"\n aria-hidden\n />\n )\n}\n\n/** One text run inside a segmented turn. Smooths its own text so only the\n * actively-streaming trailing run types out; finalized runs render at once.\n * A child component (not an inline map) so its `useSmoothText` state is stable\n * across the parent's per-frame stream re-renders. */\nfunction SegmentText({\n content,\n streaming,\n showCaret,\n renderBody,\n messageClassName,\n}: {\n content: string\n streaming: boolean\n showCaret: boolean\n renderBody: (content: string) => ReactNode\n messageClassName: string\n}) {\n const text = useSmoothText(content, streaming)\n const body = useMemo(() => renderBody(text), [renderBody, text])\n // An empty / whitespace-only run paints a blank line-height gap — render\n // nothing, UNLESS it's the live trailing run (showCaret), where it still\n // carries the caret so the turn doesn't look frozen. (Hooks run first, so\n // this stays rules-of-hooks safe.)\n if (!content.trim() && !showCaret) return null\n return (\n // A settled run arrives from a short blur; the LIVE run does not, because\n // its text is already being revealed character by character and animating\n // the container on top of that makes the paragraph shimmer while it types.\n // The distinction is what separates \"the answer materialised\" from \"the\n // log was appended to\".\n <div className={`${messageClassName}${streaming ? '' : ' agent-stream-in'}`}>\n {body}\n {/* Gate on showCaret (not the smoothed `text`, which is '' on the first\n frame) so the caret is steady from the start instead of flickering. */}\n {showCaret && <StreamingCaret />}\n </div>\n )\n}\n\n/** A settled run of at least this many consecutive tool calls collapses into a\n * single \"Worked through N steps\" disclosure so a long multi-step turn does not\n * flood the transcript. Below it, tool rows render inline as before. */\nconst COLLAPSE_TOOL_RUN_AT = 3\n\n/** A tool call the user must not miss even in a settled run — a failure (by\n * status OR by an `ok:false` outcome, the shared `toolCallFailed` predicate),\n * a card awaiting their approval, or a tool still `running` after the turn has\n * settled (i.e. stuck / timed out). A run containing one is NEVER collapsed, so\n * a failed, blocked, or stuck turn can't hide behind a \"Worked through N steps\"\n * summary and read as successful. */\nfunction isImportantTool(call: ChatToolCallInfo): boolean {\n return (\n call.status === 'running' ||\n toolCallFailed(call) ||\n pendingApprovalOf(call) !== null\n )\n}\n\n/** Renders a turn's ordered text/tool segments interleaved. The trailing text\n * run carries the streaming caret; if the last segment is instead a tool, a\n * trailing caret keeps the gap before the next run from looking frozen. Any\n * `toolCalls` not represented in `segments` (a partially-migrated producer that\n * set both) still render, so a tool row is never silently dropped. A settled\n * run of many tool calls collapses into one disclosure — see below. */\nfunction SegmentedBody({\n segments,\n msg,\n streaming,\n renderBody,\n approval,\n onToolCallClick,\n toolRenderers,\n messageClassName,\n}: {\n segments: ChatMessageSegment[]\n msg: ChatUiMessage\n streaming: boolean\n renderBody: (content: string) => ReactNode\n approval?: ProposalApprovalHandlers\n onToolCallClick?: (call: ChatToolCallInfo, message: ChatUiMessage) => void\n toolRenderers?: ToolDetailRenderers\n messageClassName: string\n}) {\n const lastIndex = segments.length - 1\n const segmentToolIds = new Set(\n segments.flatMap((s) => (s.kind === 'tool' ? [s.call.id] : [])),\n )\n const leftoverToolCalls = (msg.toolCalls ?? []).filter(\n (tc) => !segmentToolIds.has(tc.id),\n )\n // `index` is the row's position WITHIN its group, so a run of six steps\n // cascades once instead of every group in the turn sharing one clock. The\n // index is stable to take from the map because segments only ever APPEND: a\n // row already on screen keeps the position it arrived at.\n const renderToolCard = (call: ChatToolCallInfo, index: number) => (\n <ToolCallCard\n key={`tool-${call.id}`}\n call={call}\n message={msg}\n approval={approval}\n onOpenRun={onToolCallClick}\n renderers={toolRenderers}\n staggerIndex={index}\n />\n )\n // Group consecutive tool segments so a SETTLED run of many tool calls (a\n // multi-step turn, e.g. workflow authoring) collapses into one disclosure\n // instead of flooding the transcript with a card per step. While the turn is\n // streaming nothing is grouped — live tool activity is exactly what the user\n // wants to watch; the run collapses once the turn settles. Text runs (the\n // actual answer) always render in full.\n const groups: Array<\n | { kind: 'text'; index: number; content: string }\n | { kind: 'tools'; index: number; calls: ChatToolCallInfo[] }\n > = []\n for (let i = 0; i < segments.length; i++) {\n const seg = segments[i]\n if (!seg) continue\n if (seg.kind === 'text') {\n groups.push({ kind: 'text', index: i, content: seg.content })\n } else {\n const last = groups[groups.length - 1]\n if (last && last.kind === 'tools') last.calls.push(seg.call)\n else groups.push({ kind: 'tools', index: i, calls: [seg.call] })\n }\n }\n\n // ONE flat child list rather than a wrapper element per group, because React\n // identity is per PARENT: a call the producer reports in `toolCalls` before\n // the segment carrying it arrives starts life in `leftoverToolCalls` and later\n // lands in a `tool` segment, and a change of parent is an unmount plus a mount\n // — the row replays its entrance although the reader has been watching it for\n // seconds. Flat, every card is a sibling keyed on its call id, so that\n // migration is a re-order at worst and usually not even that (segments append,\n // and a leftover sits at the end, which is exactly where its segment lands).\n // The outer `gap-2` is what the removed per-group wrapper supplied, so the\n // layout is byte-for-byte what it was.\n const children: ReactNode[] = []\n for (const g of groups) {\n if (g.kind === 'text') {\n children.push(\n <SegmentText\n // Segments only ever append within a turn, so the index is a stable\n // key — a finalized run keeps its slot as later runs/tools are added,\n // so its smooth-text state isn't reset.\n key={`text-${g.index}`}\n content={g.content}\n // Only the trailing run of the live turn types out + shows the caret.\n streaming={streaming && g.index === lastIndex}\n showCaret={streaming && g.index === lastIndex}\n renderBody={renderBody}\n messageClassName={messageClassName}\n />,\n )\n continue\n }\n if (\n !streaming &&\n g.calls.length >= COLLAPSE_TOOL_RUN_AT &&\n !g.calls.some(isImportantTool)\n ) {\n // The fold is a quiet disclosure line, not a filled box — the canonical\n // rows inside it carry the row chrome.\n //\n // Deliberately still a `<details>` and NOT `.agent-disclose`: a closed\n // `<details>` gives its children no box at all, so their `.agent-arrive`\n // has not run yet and the run genuinely cascades on the click that\n // reveals it. `.agent-disclose` keeps the subtree laid out and merely\n // clipped, which would spend the arrival behind a zero height and open\n // onto rows that were already there. The reasoning box is the opposite\n // case — it is open while the model thinks, so what has to animate there\n // is the HEIGHT.\n //\n // The key names the FOLD, not the group: folded and unfolded are two\n // different elements for one group, and one key over two element types is\n // how React is told to tear a subtree down and rebuild it — replaying the\n // entrance of every row in it.\n children.push(\n <details key={`tools-fold-${g.index}`}>\n {/* No horizontal padding: the fold line hangs on the same left edge\n as the rows it reveals, so opening it does not shift the column. */}\n <summary className=\"cursor-pointer select-none rounded-md py-0.5 text-xs font-medium text-muted-foreground [transition:color_var(--motion-control)] hover:text-foreground\">\n Worked through {g.calls.length} steps\n </summary>\n <div className=\"mt-1.5 flex flex-col gap-1.5\">\n {g.calls.map(renderToolCard)}\n </div>\n </details>,\n )\n continue\n }\n g.calls.forEach((call, index) => children.push(renderToolCard(call, index)))\n }\n leftoverToolCalls.forEach((call, index) => children.push(renderToolCard(call, index)))\n if (streaming && segments[lastIndex]?.kind === 'tool') {\n children.push(<StreamingCaret key=\"streaming-caret\" />)\n }\n\n return <div className=\"flex flex-col gap-2\">{children}</div>\n}\n\n// ── Quiet chrome ────────────────────────────────────────────────────────────\n\n/** The quiet chrome's per-row meta lane: a fixed ~18px strip that always\n * reserves its height (so the reveal is pure opacity — zero layout shift) and\n * fades in on row hover/focus-within, staying visible on touch via\n * `@media (hover: none)`. The fade is reduced-motion-guarded. Tabular figures\n * keep the tok/s and cost columns from jittering as they change; no letter\n * tracking — a meta lane is data, not an eyebrow. */\nconst QUIET_META_LANE_CLASS =\n 'mt-1 flex h-[18px] items-center gap-2 text-xs tabular-nums text-muted-foreground opacity-0 transition-opacity duration-150 group-hover:opacity-100 group-focus-within:opacity-100 motion-reduce:transition-none [@media(hover:none)]:opacity-100'\n\n/** The text a copy of the message should carry: the ordered text runs when the\n * turn is segmented (they render in place of `content`), else `content`. */\nfunction copyTextOf(msg: ChatUiMessage): string {\n const textRuns = msg.segments?.filter((s) => s.kind === 'text') ?? []\n if (textRuns.length > 0) return textRuns.map((s) => s.content).join('\\n\\n')\n return msg.content\n}\n\n/** Copies the message's plain text; swaps to a check for a beat on success.\n * Quiet chrome only — labeled mode's meta line is information, not action. */\nfunction CopyMessageButton({ text }: { text: string }) {\n const [copied, setCopied] = useState(false)\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)\n useEffect(\n () => () => {\n if (timerRef.current !== null) clearTimeout(timerRef.current)\n },\n [],\n )\n const copy = () => {\n const clipboard = navigator.clipboard\n if (!clipboard) return\n void clipboard.writeText(text).then(\n () => {\n setCopied(true)\n if (timerRef.current !== null) clearTimeout(timerRef.current)\n timerRef.current = setTimeout(() => setCopied(false), 1200)\n },\n () => {},\n )\n }\n return (\n <button\n type=\"button\"\n onClick={copy}\n aria-label=\"Copy message\"\n title=\"Copy message\"\n className=\"rounded p-0.5 text-muted-foreground transition hover:bg-accent hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n {copied ? (\n <svg className=\"h-3.5 w-3.5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n ) : (\n <svg className=\"h-3.5 w-3.5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" />\n <path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\" />\n </svg>\n )}\n </button>\n )\n}\n\nfunction AssistantMessageImpl({\n msg,\n streaming,\n models,\n agentLabel,\n renderBody,\n approval,\n onToolCallClick,\n toolRenderers,\n renderExtras,\n durableCards,\n resolveAttachmentUrl,\n workProductCards,\n messageClassName,\n chrome,\n}: {\n msg: ChatUiMessage\n streaming: boolean\n models: CatalogModel[]\n agentLabel: string\n renderBody: (content: string) => ReactNode\n approval?: ProposalApprovalHandlers\n onToolCallClick?: (call: ChatToolCallInfo, message: ChatUiMessage) => void\n toolRenderers?: ToolDetailRenderers\n renderExtras?: (message: ChatUiMessage) => ReactNode\n durableCards?: Omit<DurableChatCardsProps, 'parts' | 'renderMarkdown'>\n resolveAttachmentUrl?: (part: ChatAttachmentPart) => string\n workProductCards?: { onOpen?: (part: WorkProductPersistedPart) => void }\n messageClassName: string\n chrome: 'labeled' | 'quiet'\n}) {\n // Smooth reveal: chunky network slabs (model bursts, flush windows, replay\n // polls) paint as a continuous typewriter. Reasoning often arrives as one\n // burst right before the answer — smoothing makes it visibly type out in\n // the open thinking box instead of popping in and collapsing.\n const content = useSmoothText(msg.content, streaming)\n const reasoning = useSmoothText(msg.reasoning ?? '', streaming)\n // The smooth reveal re-renders on every rAF frame while streaming, but the\n // FLOORED visible length only advances every few frames — re-parsing markdown\n // each frame is wasted work on the hot path. Memo on (renderBody, content) so\n // the parse runs only when the visible text actually changes.\n const body = useMemo(() => renderBody(content), [renderBody, content])\n // When a turn is segmented, render the ordered text/tool runs interleaved;\n // otherwise fall back to the single content body + trailing tool group.\n const segments = msg.segments\n // \"Has the answer started?\" — true once any answer text exists, whether the\n // producer puts it in `content` (legacy) or in a text `segment`. Drives the\n // reasoning box (open while still thinking, the thinking timer, the summary\n // label), so a segmented message with `content: ''` doesn't read as\n // perpetually \"Thinking…\" after its answer segments are visible.\n const hasAnswerText =\n content !== '' ||\n (segments?.some((s) => s.kind === 'text' && s.content.trim() !== '') ??\n false)\n const reasoningScrollRef = useRef<HTMLDivElement>(null)\n // Measure visible thinking time: first reasoning reveal → first answer text.\n const thinkStartRef = useRef<number | null>(null)\n const thinkMsRef = useRef<number | null>(null)\n if (streaming && reasoning && !hasAnswerText && thinkStartRef.current === null) {\n thinkStartRef.current = performance.now()\n }\n if (\n hasAnswerText &&\n thinkStartRef.current !== null &&\n thinkMsRef.current === null\n ) {\n thinkMsRef.current = performance.now() - thinkStartRef.current\n }\n useEffect(() => {\n const el = reasoningScrollRef.current\n if (el && streaming && !hasAnswerText) el.scrollTop = el.scrollHeight\n }, [reasoning, streaming, hasAnswerText])\n // Live seconds while the model is reasoning before its answer starts, so a\n // long thinking gap shows progress rather than a static \"Thinking…\".\n const thinkingSeconds = useThinkingSeconds(\n streaming && !!reasoning && !hasAnswerText,\n )\n // Open while the model is still thinking, closed once the answer starts —\n // and a click outranks that default from then on. `<details open={…}>` could\n // not express the second half: React re-asserts the attribute on every\n // render, so a reader who opened the box mid-stream had it shut again by the\n // next frame of tokens.\n const [reasoningToggled, setReasoningToggled] = useState<boolean | null>(null)\n const reasoningOpen = reasoningToggled ?? !hasAnswerText\n\n const quiet = chrome === 'quiet'\n return (\n <div className={`mx-auto w-full max-w-3xl px-6 ${quiet ? 'group pb-1 pt-3' : 'py-3'}`}>\n {!quiet && (\n <div className=\"mb-1 flex items-baseline gap-2 text-xs tabular-nums text-muted-foreground\">\n <span className=\"font-semibold uppercase tracking-[0.05em]\">{agentLabel}</span>\n {msg.modelUsed && <span className=\"font-mono normal-case\">{msg.modelUsed}</span>}\n {formatTokensPerSecond(msg) && <span>{formatTokensPerSecond(msg)}</span>}\n {formatModelCost(msg, models) && <span>{formatModelCost(msg, models)}</span>}\n </div>\n )}\n {reasoning && (\n // The canonical run-row grammar (RunRowShell — the same shell the tool\n // rows compose): one family of rows instead of a bespoke disclosure per\n // kind. The shimmer title is a NODE (ui widened `title` to ReactNode for\n // exactly this): the sweep through the glyphs is the working-vs-stuck\n // signal. Open while thinking, auto-collapse on the first answer token,\n // and a click outranks the default from then on — the contract the old\n // hand-rolled disclosure had, now enforced through the shell's\n // controlled `open`.\n <RunRowShell\n className=\"mb-2\"\n icon={<BrainGlyph className=\"h-3.5 w-3.5\" />}\n title={\n !hasAnswerText ? (\n <span className=\"agent-shimmer\" data-motion=\"essential\">\n Thinking{thinkingSeconds >= 1 ? ` · ${thinkingSeconds}s` : '…'}\n </span>\n ) : thinkMsRef.current != null ? (\n // Words, not the abbreviated unit — \"Thought for 4 seconds\" reads\n // like a sentence; \"4s\" reads like a log line.\n `Thought for ${(() => { const s = Math.max(1, Math.round(thinkMsRef.current! / 1000)); return `${s} second${s === 1 ? '' : 's'}` })()}`\n ) : (\n 'Thought process'\n )\n }\n // A collapsed trace previews its own first line (the inline truncated\n // description slot), so the closed row tells you what was considered,\n // not just that thinking happened.\n description={hasAnswerText ? reasoningPreview(reasoning) : undefined}\n status={hasAnswerText ? 'idle' : 'running'}\n open={reasoningOpen}\n onOpenChange={(next) => setReasoningToggled(next)}\n >\n <div\n ref={reasoningScrollRef}\n className=\"max-h-48 overflow-y-auto whitespace-pre-wrap px-3 py-2.5 text-sm leading-relaxed text-muted-foreground\"\n >\n {reasoning}\n </div>\n </RunRowShell>\n )}\n {segments && segments.length > 0 ? (\n <SegmentedBody\n segments={segments}\n msg={msg}\n streaming={streaming}\n renderBody={renderBody}\n approval={approval}\n onToolCallClick={onToolCallClick}\n toolRenderers={toolRenderers}\n messageClassName={messageClassName}\n />\n ) : (\n <>\n <div className={messageClassName}>\n {body}\n {streaming && content && !msg.toolCalls?.length && <StreamingCaret />}\n </div>\n {msg.toolCalls && msg.toolCalls.length > 0 && (\n <div className=\"mt-2 flex flex-col gap-1.5\">\n {msg.toolCalls.map((tc, index) => (\n <ToolCallCard\n key={tc.id}\n call={tc}\n message={msg}\n approval={approval}\n onOpenRun={onToolCallClick}\n renderers={toolRenderers}\n staggerIndex={index}\n />\n ))}\n </div>\n )}\n </>\n )}\n {durableCards && msg.parts && (\n <DurableChatCards\n {...durableCards}\n parts={msg.parts}\n renderMarkdown={renderBody}\n className=\"mt-3\"\n />\n )}\n {workProductCards &&\n workProductPartsFromMessageParts(msg.parts).map((part) => (\n <WorkProductCard\n key={`${part.ref.id}:${part.ref.version}`}\n part={part}\n onOpen={workProductCards.onOpen}\n className=\"mt-3\"\n />\n ))}\n {renderExtras?.(msg)}\n {resolveAttachmentUrl && attachmentPartsFromMessageParts(msg.parts).length > 0 && (\n <div className=\"mt-2\">\n <MessageAttachments\n parts={attachmentPartsFromMessageParts(msg.parts)}\n resolveFileUrl={resolveAttachmentUrl}\n justify=\"start\"\n />\n </div>\n )}\n {quiet && (\n <div data-testid=\"message-meta-lane\" className={QUIET_META_LANE_CLASS}>\n <CopyMessageButton text={copyTextOf(msg)} />\n {msg.modelUsed && <span className=\"font-mono\">{msg.modelUsed}</span>}\n {formatTokensPerSecond(msg) && <span>{formatTokensPerSecond(msg)}</span>}\n {formatModelCost(msg, models) && <span>{formatModelCost(msg, models)}</span>}\n </div>\n )}\n </div>\n )\n}\n\n/**\n * Only the actively-streaming message changes per frame; historical messages\n * are referentially stable. `memo` keeps a stable `AssistantMessage` from\n * re-rendering (and re-running its markdown parse) when a sibling streams —\n * default shallow-equal prop comparison is exactly right here since every prop\n * is referentially stable except the one being streamed.\n */\nconst AssistantMessage = memo(AssistantMessageImpl)\n\n/** Whole seconds elapsed while `active`, ticking once a second. Powers the live\n * \"thinking\" timers (the pre-first-token row and the reasoning box) so a long\n * thinking gap shows progress instead of a frozen label. Counts from when\n * `active` first turns true; freezes when it clears. */\nexport function useThinkingSeconds(active: boolean): number {\n const [seconds, setSeconds] = useState(0)\n useEffect(() => {\n if (!active) return\n // Reset on each (re)activation so a reused component resuming \"thinking\"\n // counts from 0 rather than showing the prior phase's stale elapsed time.\n setSeconds(0)\n const id = setInterval(() => setSeconds((s) => s + 1), 1000)\n return () => clearInterval(id)\n }, [active])\n return seconds\n}\n\nfunction ThinkingRow({ agentLabel, chrome = 'labeled' }: { agentLabel: string; chrome?: 'labeled' | 'quiet' }) {\n const seconds = useThinkingSeconds(true)\n return (\n <div className=\"mx-auto w-full max-w-3xl px-6 py-3\">\n {chrome !== 'quiet' && (\n <p className=\"mb-1 text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground\">{agentLabel}</p>\n )}\n <div className=\"flex items-center gap-2 text-[15px] text-muted-foreground\">\n <svg className=\"h-4 w-4 animate-spin\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" aria-hidden>\n <path d=\"M21 12a9 9 0 1 1-6.219-8.56\" strokeLinecap=\"round\" />\n </svg>\n Thinking{seconds >= 3 ? ` · ${seconds}s` : '...'}\n </div>\n </div>\n )\n}\n\n/** Top-level turn-failure row with an optional Retry — the affordance a failed\n * stream otherwise lacks (the turn just stopped). */\nfunction StreamErrorRow({ message, onRetry }: { message: string; onRetry?: () => void }) {\n return (\n <div className=\"mx-auto w-full max-w-3xl px-6 py-3\">\n <div role=\"alert\" className=\"flex items-start gap-2.5 rounded-lg border border-destructive/40 bg-destructive/5 px-3 py-2.5 text-sm text-destructive\">\n <svg className=\"mt-0.5 h-4 w-4 shrink-0\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <circle cx=\"12\" cy=\"12\" r=\"9\" />\n <path d=\"M12 8v4m0 4h.01\" />\n </svg>\n <span className=\"min-w-0 flex-1 break-words\">{message}</span>\n {onRetry && (\n <button\n type=\"button\"\n onClick={onRetry}\n className={`shrink-0 rounded border border-destructive/40 bg-card px-2 py-0.5 text-xs font-medium text-destructive transition hover:bg-destructive/10 ${POPOVER_OPTION_FOCUS}`}\n >\n Retry\n </button>\n )}\n </div>\n </div>\n )\n}\n\n/**\n * The message thread: one centered column; user messages are right-aligned\n * bubbles with a User label; agent messages carry an Agent meta line with\n * model id, tokens/sec, and cost, plus a collapsible thinking section and\n * tool rows. `chrome=\"quiet\"` opts into the label-free variant: the\n * label/meta row becomes a hover-revealed meta lane under each row.\n */\nexport function ChatMessages({\n messages,\n messageSize = 'default',\n chrome = 'labeled',\n models = [],\n renderMarkdown,\n renderExtras,\n durableCards,\n userLabel = 'User',\n agentLabel = 'Agent',\n loading,\n approval,\n onToolCallClick,\n toolRenderers,\n error,\n onRetry,\n renderEmpty,\n emptyState,\n header,\n resolveAttachmentUrl,\n workProductCards,\n}: ChatMessagesProps) {\n const messageClassName =\n messageSize === 'large'\n ? 'agent-app-message-copy text-[17px] leading-[1.6]'\n : 'agent-app-message-copy text-base leading-[1.6]'\n // Stabilize the fallback renderer's identity so it doesn't change every\n // render — otherwise the memoized `AssistantMessage` (and its per-frame body\n // memo) would invalidate on every parent render when no `renderMarkdown` is\n // supplied.\n const renderBody = useMemo(\n () => renderMarkdown ?? ((content: string) => <p className=\"whitespace-pre-wrap\">{content}</p>),\n [renderMarkdown],\n )\n const lastIsUser = messages[messages.length - 1]?.role === 'user'\n const quiet = chrome === 'quiet'\n if (messages.length === 0 && !loading && !error) {\n // Explicit renderEmpty wins (incl. `() => null` to opt out); otherwise show\n // the branded first-run state instead of a blank thread.\n const empty = renderEmpty ? renderEmpty() : <ChatEmptyState {...emptyState} />\n return (\n <>\n {header}\n {empty}\n </>\n )\n }\n return (\n <>\n {header}\n {messages.map((msg) =>\n msg.role === 'user' ? (\n <div key={msg.id} className={`mx-auto w-full max-w-3xl px-6 ${quiet ? 'group pb-1 pt-3' : 'py-3'}`}>\n <div className={`ml-auto w-fit ${quiet ? 'max-w-[72%]' : 'max-w-[85%]'}`}>\n {!quiet && (\n <p className=\"mb-1 text-right text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground\">\n {userLabel}\n </p>\n )}\n <div\n className={\n quiet\n ? `rounded-2xl bg-[color-mix(in_srgb,hsl(var(--secondary))_65%,hsl(var(--background)))] px-4 py-2.5 ${messageClassName}`\n : `rounded-2xl rounded-tr-md bg-primary/10 px-4 py-2.5 ${messageClassName}`\n }\n >\n <p className=\"whitespace-pre-wrap\">{msg.content}</p>\n </div>\n {resolveAttachmentUrl && attachmentPartsFromMessageParts(msg.parts).length > 0 && (\n <div className=\"mt-1.5\">\n <MessageAttachments\n parts={attachmentPartsFromMessageParts(msg.parts)}\n resolveFileUrl={resolveAttachmentUrl}\n justify=\"end\"\n />\n </div>\n )}\n </div>\n {quiet && (\n <div data-testid=\"message-meta-lane\" className={`${QUIET_META_LANE_CLASS} justify-end`}>\n <CopyMessageButton text={msg.content} />\n </div>\n )}\n </div>\n ) : (\n <AssistantMessage\n key={msg.id}\n msg={msg}\n streaming={!!loading && msg.id === messages[messages.length - 1]?.id}\n models={models}\n agentLabel={agentLabel}\n renderBody={renderBody}\n approval={approval}\n onToolCallClick={onToolCallClick}\n toolRenderers={toolRenderers}\n renderExtras={renderExtras}\n durableCards={durableCards}\n resolveAttachmentUrl={resolveAttachmentUrl}\n workProductCards={workProductCards}\n messageClassName={messageClassName}\n chrome={chrome}\n />\n ),\n )}\n {loading && lastIsUser && <ThinkingRow agentLabel={agentLabel} chrome={chrome} />}\n {error && !loading && <StreamErrorRow message={error} onRetry={onRetry} />}\n </>\n )\n}\n","/**\n * Smooth text reveal — turns chunky network deltas into a continuous\n * typewriter paint. Streamed turns arrive in 100-500ms slabs (model burst,\n * flush windows, replay polls); revealing characters at an adaptive rate\n * makes the same bytes read as top-tier streaming. The rate scales with the\n * backlog so the reveal never falls behind the stream — it crawls when caught\n * up and sprints when a burst lands (e.g. a reasoning summary arriving all at\n * once still *types out* instead of popping in).\n */\n\nimport { useEffect, useRef, useState } from 'react'\n\n/** Define configuration options for controlling smooth text reveal animation rates */\nexport interface SmoothRevealOptions {\n /** Baseline reveal rate when nearly caught up. Default 90 chars/s. */\n baseCharsPerSecond?: number\n /** Extra chars/s per backlog character — the catch-up pressure. Default 5. */\n catchUpPerChar?: number\n /** Hard ceiling so giant bursts still animate. Default 2400 chars/s. */\n maxCharsPerSecond?: number\n}\n\n/** Pure reveal step: how many characters should be visible after `dtMs`.\n * Exposed for tests; the hook is a thin rAF wrapper around it. */\nexport function nextRevealCount(\n shown: number,\n targetLength: number,\n dtMs: number,\n opts: SmoothRevealOptions = {},\n): number {\n if (shown >= targetLength) return targetLength\n const base = opts.baseCharsPerSecond ?? 90\n const catchUp = opts.catchUpPerChar ?? 5\n const max = opts.maxCharsPerSecond ?? 2400\n const backlog = targetLength - shown\n const rate = Math.min(max, base + backlog * catchUp)\n return Math.min(targetLength, shown + (rate * dtMs) / 1000)\n}\n\n/**\n * Animate `target` text into view. While `enabled`, the returned string grows\n * smoothly toward `target` (which may itself keep growing); when `enabled` is\n * false the full text returns immediately (history, completed turns). A\n * target that is not an extension of the revealed prefix (new message) resets\n * the reveal.\n */\nexport function useSmoothText(target: string, enabled: boolean, opts?: SmoothRevealOptions): string {\n const [, force] = useState(0)\n const shownRef = useRef(0)\n const lastTargetRef = useRef('')\n\n // New message / rewritten prefix → restart the reveal from zero.\n if (!target.startsWith(lastTargetRef.current.slice(0, Math.floor(shownRef.current)))) {\n shownRef.current = 0\n }\n lastTargetRef.current = target\n if (!enabled) shownRef.current = target.length\n\n useEffect(() => {\n if (!enabled) return\n let raf = 0\n let last: number | null = null\n const tick = (t: number) => {\n const dt = last == null ? 16 : Math.min(t - last, 100)\n last = t\n const targetLen = lastTargetRef.current.length\n if (shownRef.current < targetLen) {\n shownRef.current = nextRevealCount(shownRef.current, targetLen, dt, opts)\n force((n) => n + 1)\n // Keep painting while there is still backlog to reveal.\n raf = requestAnimationFrame(tick)\n }\n // Caught up: stop the loop. A later `target` growth re-renders this hook\n // (target is read fresh below), and the next render's effect — re-run\n // because `target` is a dep — restarts the loop. Idle messages spawn no\n // rAF, so a full thread of completed turns is dormant.\n }\n raf = requestAnimationFrame(tick)\n return () => cancelAnimationFrame(raf)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [enabled, target])\n\n return target.slice(0, Math.floor(shownRef.current))\n}\n","/**\n * The React half of the motion primitives that ship in `src/theme/tokens.css`.\n *\n * The stylesheet owns every keyframe, duration and easing, and that is the\n * whole point: a component that writes its own timing is invisible to the\n * reduced-motion collapse at `:root`, so \"it writes its own timing\" and \"it\n * ignores reduced motion\" are the same defect. The one thing CSS cannot supply\n * is WHERE in a group a row sits, which is per-element data — hence these two\n * helpers and nothing else.\n */\n\nimport { useState, type CSSProperties } from 'react'\n\n/**\n * Position in a staggered `.agent-arrive` group. `--stagger-index` is a custom\n * property and `CSSProperties` has no index signature for one, so the cast is\n * the only way to hand React a value tokens.css already defines (it declares\n * the property with a `0` default and caps the delay at 8 steps internally, so\n * a long list never opens with a multi-second cascade).\n *\n * Use it directly only where the group's order is FIXED once rendered — an\n * ask's option rows, a turn's append-only tool segments. A list that can\n * re-sort wants {@link useArrivalStyle}.\n *\n * `base` merges a caller's own style, because a row that carries layout of its\n * own would otherwise have to spread the two itself at every call site, and the\n * spread order matters: the index must win, or a stale `--stagger-index` on the\n * base silently overrides the position being asked for.\n */\nexport function staggerStyle(index: number, base?: CSSProperties): CSSProperties {\n return { ...base, '--stagger-index': index } as CSSProperties\n}\n\n/**\n * The same style, frozen at the row's first render.\n *\n * A settled `.agent-arrive` is finished, not inert: `animation-delay` is part\n * of its timing, so handing an already-arrived row a LARGER delay pushes it\n * back into the animation's before-phase and it plays again. Any list that\n * re-sorts does exactly that — `mergeActivityPages` sorts newest-first, so one\n * newer run landing on a refresh shifts every row's index by one and the whole\n * panel would re-animate because nothing about it changed. Freezing the index\n * at mount makes the arrival a property of ARRIVING rather than of the row's\n * current position, which is the difference between choreography and flicker.\n *\n * Rules of hooks: a row that needs this is a component, not a `.map` body.\n */\nexport function useArrivalStyle(index: number): CSSProperties {\n const [frozen] = useState(index)\n return staggerStyle(frozen)\n}\n","/**\n * Load the canonical Tangle knot only when a web surface renders it.\n * `/brand` imports the optional `@tangle-network/brand` peer, so a failed load\n * renders a fixed-size spacer instead of crashing the chat shell.\n */\n\nimport { lazy, Suspense } from 'react'\nimport type { ComponentType } from 'react'\n\ninterface BrandMarkProps {\n size?: number\n className?: string\n}\n\n/** Preserve the mark's footprint while its optional package loads or is absent. */\nfunction MarkSpacer({ size = 24, className }: BrandMarkProps) {\n return <span aria-hidden style={{ display: 'inline-block', width: size, height: size }} className={className} />\n}\n\nconst LazyKnot = lazy(async () => {\n try {\n const mod = await import('../brand')\n return { default: mod.TangleKnot as ComponentType<BrandMarkProps> }\n } catch {\n return { default: MarkSpacer as ComponentType<BrandMarkProps> }\n }\n})\n\nexport function BrandMark({ size = 24, className }: BrandMarkProps) {\n return (\n <Suspense fallback={<MarkSpacer size={size} className={className} />}>\n <LazyKnot size={size} className={className} />\n </Suspense>\n )\n}\n","import { useEffect, useLayoutEffect, useRef, useState, type ReactNode } from 'react'\nimport type { ChatPlan } from '../plans/index'\nimport type {\n DurablePlanDecision,\n DurablePlanDecisionResult,\n} from './durable-plan-flow'\nimport { InteractionActionButton, InteractionBadge } from './interaction-question-card'\n\nexport interface DurablePlanCardProps {\n plan: ChatPlan\n canWrite: boolean\n decide: (decision: DurablePlanDecision, feedback?: string) => Promise<DurablePlanDecisionResult | null>\n deciding?: DurablePlanDecision | null\n error?: string | null\n renderMarkdown?: (markdown: string) => ReactNode\n className?: string\n}\n\nfunction statusLabel(plan: ChatPlan): string {\n switch (plan.status) {\n case 'pending': return 'Waiting for your decision'\n case 'approved': return 'Approved'\n case 'rejected': return 'Changes requested'\n case 'superseded': return 'Superseded'\n case 'withdrawn': return 'Withdrawn'\n default: return 'Preparing'\n }\n}\n\n/** Body height (px) beyond which the plan collapses behind a \"Show full plan\"\n * toggle — same cap as the interaction plan card. */\nconst COLLAPSED_MAX_HEIGHT = 320\n\nexport function DurablePlanCard({\n plan,\n canWrite,\n decide,\n deciding = null,\n error,\n renderMarkdown,\n className,\n}: DurablePlanCardProps) {\n const [feedback, setFeedback] = useState('')\n const [expanded, setExpanded] = useState(false)\n const [localError, setLocalError] = useState<string | null>(null)\n useEffect(() => setLocalError(null), [plan.planId, plan.revision, plan.status])\n\n const actionable = plan.status === 'pending'\n const disabled = !canWrite || !actionable || deciding !== null\n\n // The collapse UI appears only when the body actually overflows the cap —\n // measured, so a short plan shows neither the fade nor the toggle.\n const bodyRef = useRef<HTMLDivElement>(null)\n const [overflows, setOverflows] = useState(false)\n useLayoutEffect(() => {\n const el = bodyRef.current\n if (el) setOverflows(el.scrollHeight > COLLAPSED_MAX_HEIGHT)\n }, [plan.body, renderMarkdown])\n\n async function submit(decision: DurablePlanDecision) {\n const trimmed = feedback.trim()\n if (decision === 'rejected' && !trimmed) {\n setLocalError('Describe what you want changed before requesting a revision.')\n return\n }\n setLocalError(null)\n await decide(decision, decision === 'rejected' ? trimmed : undefined)\n }\n\n return (\n <div className={`rounded-xl border border-primary/40 bg-card p-4 ${className ?? ''}`}>\n <div className=\"mb-3 flex flex-wrap items-center justify-between gap-2\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <InteractionBadge variant=\"outline\">Plan decision</InteractionBadge>\n <InteractionBadge variant={plan.status === 'approved' ? 'default' : plan.status === 'rejected' || plan.status === 'withdrawn' ? 'destructive' : 'outline'}>\n {statusLabel(plan)}\n </InteractionBadge>\n </div>\n <span className=\"text-xs text-muted-foreground\">Revision {plan.revision}</span>\n </div>\n {plan.title && <p className=\"mb-3 text-[15px] font-semibold leading-snug text-foreground\">{plan.title}</p>}\n <div className=\"relative\">\n <div\n ref={bodyRef}\n className=\"overflow-hidden text-sm\"\n style={expanded || !overflows ? undefined : { maxHeight: COLLAPSED_MAX_HEIGHT }}\n >\n {renderMarkdown ? renderMarkdown(plan.body) : <p className=\"whitespace-pre-wrap leading-5\">{plan.body}</p>}\n </div>\n {overflows && !expanded && <div className=\"pointer-events-none absolute inset-x-0 bottom-0 h-12 bg-gradient-to-t from-card to-transparent\" />}\n {overflows && (\n <button\n type=\"button\"\n onClick={() => setExpanded((value) => !value)}\n className=\"relative z-10 mt-1 text-xs text-muted-foreground hover:text-foreground\"\n >\n {expanded ? 'Collapse plan' : 'Show full plan'}\n </button>\n )}\n </div>\n {actionable && (\n <div className=\"mt-3 space-y-2\">\n <label className=\"block text-sm font-medium leading-5 text-foreground\" htmlFor={`durable-plan-feedback-${plan.planId}-${plan.revision}`}>\n Feedback for requested changes\n </label>\n <textarea\n id={`durable-plan-feedback-${plan.planId}-${plan.revision}`}\n value={feedback}\n disabled={disabled}\n onChange={(event) => setFeedback(event.target.value)}\n rows={2}\n placeholder=\"Describe what you want changed in the plan\"\n className=\"w-full rounded-lg border border-border bg-background px-3 py-2 text-sm focus:border-primary disabled:opacity-50\"\n />\n </div>\n )}\n {(localError ?? error) && <p className=\"mt-3 text-xs text-destructive\">{localError ?? error}</p>}\n {actionable && (\n <div className=\"mt-4 flex items-center justify-end gap-2\">\n <InteractionActionButton variant=\"outline\" onClick={() => void submit('rejected')} disabled={disabled}>\n {deciding === 'rejected' ? 'Sending…' : 'Request changes'}\n </InteractionActionButton>\n <InteractionActionButton onClick={() => void submit('approved')} disabled={disabled}>\n {deciding === 'approved' ? 'Approving…' : 'Approve plan'}\n </InteractionActionButton>\n </div>\n )}\n </div>\n )\n}\n","/**\n * InteractionQuestionCard — the agent-ask card every sandbox-backed chat UI\n * forked (~1,000 lines each in gtm/legal/tax). Renders the answerSpec\n * verbatim: selects (radio/checkbox by `multi`, write-in row only when the\n * sidecar granted `allowCustom`), free text, and minimal number/boolean/secret\n * inputs for open kinds.\n *\n * Behavior lifted from the gtm-agent fork (the most fix-absorbed):\n * - a terminal stream status always wins over local optimistic state,\n * - a 410 from the answer route flips the card to the same dead state a\n * cancel event produces (never a raw error),\n * - expired/withdrawn asks stay answerable: the answer is delivered as a NEW\n * chat turn via `onLateAnswer` (secret-bearing asks are blocked from that\n * path),\n * - one submit in flight at a time; a failed/timed-out submit stays\n * retryable.\n *\n * Pure data + callbacks: no fetch inside the component. Products bind the wire\n * via `createInteractionAnswerSubmitter` (or any `SubmitInteractionAnswer`).\n */\n\nimport { type ReactNode, useEffect, useMemo, useRef, useState } from 'react'\nimport type {\n ChatInteraction,\n ChatInteractionField,\n ChatInteractionStatus,\n ChatSelectField,\n ChatFreeTextField,\n} from './chat-interactions'\nimport { isTerminalInteractionStatus } from './chat-interactions'\nimport { staggerStyle } from './motion'\nimport {\n buildAnswerData,\n fieldValuesFromAnswers,\n hasSecretField,\n interactionStatusLabels,\n interactionTerminalNotes,\n isLateAnswerableStatus,\n lateAnswerMessage,\n settleInteractionSubmit,\n type FieldValues,\n type SubmitInteractionAnswer,\n} from './interaction-card-support'\n\n// ── glyphs + primitives (no icon-library / UI-kit dependency) ───────────────\n\nfunction CheckGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n )\n}\n\nexport type InteractionBadgeVariant = 'outline' | 'default' | 'destructive'\n\nconst BADGE_VARIANT_CLASSES: Record<InteractionBadgeVariant, string> = {\n outline: 'border-border text-foreground',\n default: 'border-transparent bg-primary text-primary-foreground',\n destructive: 'border-transparent bg-destructive/15 text-destructive',\n}\n\nexport function InteractionBadge({ variant, children }: { variant: InteractionBadgeVariant; children: string }) {\n return (\n <span className={`inline-flex items-center rounded-md border px-2 py-0.5 text-xs font-medium ${BADGE_VARIANT_CLASSES[variant]}`}>\n {children}\n </span>\n )\n}\n\nexport function InteractionActionButton({\n variant = 'primary',\n onClick,\n disabled,\n children,\n}: {\n variant?: 'primary' | 'outline'\n onClick: () => void\n disabled?: boolean\n children: string\n}) {\n const variantClasses = variant === 'primary'\n ? 'bg-primary text-primary-foreground shadow-sm hover:bg-primary/90'\n : 'border border-border bg-transparent text-foreground hover:bg-accent'\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n className={`inline-flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm font-medium transition disabled:cursor-not-allowed disabled:opacity-40 ${variantClasses}`}\n >\n {children}\n </button>\n )\n}\n\nconst FIELD_INPUT_CLASSES =\n 'w-full rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground focus:border-primary disabled:opacity-50'\n\n// ── option rows ─────────────────────────────────────────────────────────────\n\nexport interface QuestionOptionListProps {\n /** Radio/checkbox group name — unique per field so selection is isolated. */\n groupName: string\n /** Stable prefix for per-option input ids (label htmlFor pairing). */\n idPrefix: string\n options: ChatSelectField['options']\n /** Checkbox (multi-select) vs radio (single). */\n multi: boolean\n selectedValues: string[]\n disabled: boolean\n onToggle: (value: string) => void\n /** Terminal answered state: the selected rows highlight (primary edge, tint,\n * trailing check) so the card shows WHAT was answered, not just that it was. */\n answered?: boolean\n}\n\n/** The radio/checkbox option rows for a select field. Renders a fragment of\n * option `<label>` rows so a card keeps its own wrapping layout and appends\n * its own write-in input.\n *\n * The rows arrive as a SEQUENCE (`.agent-arrive` + `--stagger-index`), which\n * is the difference between reading a list and being shown one: the eye is\n * told there are N choices and in what order before it has read any of them.\n * The index is safe to take straight from the map because an option list does\n * not re-sort under a mounted card — a different set of options is a\n * different `key`, and a different ask resets the card wholesale. */\nexport function QuestionOptionList({\n groupName,\n idPrefix,\n options,\n multi,\n selectedValues,\n disabled,\n onToggle,\n answered = false,\n}: QuestionOptionListProps) {\n return (\n <>\n {options.map((option, optionIndex) => {\n const inputId = `${idPrefix}-${optionIndex}`\n const checked = selectedValues.includes(option.value)\n const highlighted = answered && checked\n // The whole row is a wrapping <label> for click target, but the input's\n // accessible NAME must be the option label alone — the description is\n // linked as aria-describedby, not folded into the name.\n return (\n <label\n key={`${option.value}-${optionIndex}`}\n htmlFor={inputId}\n style={staggerStyle(optionIndex)}\n className={`agent-arrive flex gap-2 rounded-lg border p-3 transition-colors ${\n highlighted ? 'border-primary bg-primary/5' : 'border-strong'\n } ${disabled ? 'cursor-default' : 'cursor-pointer hover:bg-accent'}`}\n >\n <input\n id={inputId}\n type={multi ? 'checkbox' : 'radio'}\n name={groupName}\n value={option.value}\n checked={checked}\n disabled={disabled}\n onChange={() => onToggle(option.value)}\n aria-labelledby={`${inputId}-label`}\n aria-describedby={option.description ? `${inputId}-description` : undefined}\n className=\"mt-0.5 h-4 w-4 shrink-0 accent-primary\"\n />\n <span className=\"min-w-0 flex-1\">\n <span id={`${inputId}-label`} className=\"block text-sm font-medium leading-5 text-foreground\">{option.label}</span>\n {option.description && <span id={`${inputId}-description`} className=\"mt-0.5 block text-xs leading-5 text-muted-foreground\">{option.description}</span>}\n </span>\n {highlighted && <CheckGlyph className=\"mt-0.5 h-4 w-4 shrink-0 text-primary\" />}\n </label>\n )\n })}\n </>\n )\n}\n\n// ── card ────────────────────────────────────────────────────────────────────\n\nexport interface InteractionQuestionCardProps {\n interaction: ChatInteraction\n /** Viewer-vs-editor gate: false renders everything read-only. */\n canWrite: boolean\n /** POST one resolution to the product's answer route (see\n * `createInteractionAnswerSubmitter`). Never called for late answers. */\n submitAnswer: SubmitInteractionAnswer\n /** Fired when this card resolves locally (answered, or discovered expired\n * via a 410) so the stream/route state stays in sync. */\n onResolved?: (\n id: string,\n status: Exclude<ChatInteractionStatus, 'pending'>,\n answers?: ChatInteraction['answers'],\n ) => void\n /** Delivers a late answer (the ask expired/was withdrawn) as a fresh chat\n * turn. Return/resolve `false` when the send was rejected so the card stays\n * retryable. Omit to hide the late-answer affordance entirely. */\n onLateAnswer?: (message: string) => boolean | void | Promise<boolean | void>\n /** Overrides the kind badge (\"Question\"). */\n kindLabel?: string\n /** What happens if nobody answers, rendered beside the submit action.\n *\n * The caller owns both the clock and the copy: this card holds no timer, so a\n * deadline that counts down re-renders on the caller's cadence rather than\n * driving one of its own — and the consequence of silence (\"the default is\n * taken\", \"the run fails\") is the host's policy to state, not this card's to\n * infer. */\n timeoutNote?: ReactNode\n /** Renders `body` as markdown. Omitted, `body` renders as plain text — so a\n * host that passes authored markdown without this shows its syntax raw.\n *\n * `body` ONLY. `title` and every `field.label` stay plain strings: a label is\n * also the input's accessible name (`aria-label`), which has to be text, and\n * rendering one as nodes would either break that or silently disagree with\n * what a screen reader announces. Put prose in `body`.\n *\n * `interaction.body` is untrusted: it arrives off the wire, written by an\n * agent or whoever authored the ask. This card never injects HTML, but a\n * renderer that does is an XSS sink — so return React elements, and sanitize\n * (DOMPurify or equivalent) if you must produce HTML. */\n renderMarkdown?: (markdown: string) => ReactNode\n className?: string\n}\n\nfunction selectField(field: ChatInteractionField): ChatSelectField | null {\n return field.type === 'select' ? (field as ChatSelectField) : null\n}\n\n/** The free-text fields, which are the only ones that can carry a length cap. */\nfunction freeTextField(field: ChatInteractionField): ChatFreeTextField | null {\n return field.type === 'text' || field.type === 'secret' ? (field as ChatFreeTextField) : null\n}\n\n/** The cap to stop typing at, or `undefined` for an uncapped field. A\n * non-positive or fractional cap is treated as absent rather than clamping the\n * field to zero characters — an unanswerable field is worse than an uncapped\n * one, and the answer route still enforces the real bound. */\nfunction textFieldMaxLength(field: ChatFreeTextField): number | undefined {\n const max = field.maxLength\n return typeof max === 'number' && Number.isInteger(max) && max > 0 ? max : undefined\n}\n\nfunction valuesWithSelected(values: FieldValues, field: ChatSelectField, optionValue: string): FieldValues {\n const current = values[field.name]?.selected ?? []\n let selected = [optionValue]\n if (field.multi === true) {\n selected = current.includes(optionValue)\n ? current.filter((item) => item !== optionValue)\n : [...current, optionValue]\n }\n return { ...values, [field.name]: { ...values[field.name], selected } }\n}\n\nconst STATUS_LABELS = interactionStatusLabels({\n pending: 'Waiting for your answer',\n answered: 'Answered',\n declined: 'Declined',\n})\n\nconst TERMINAL_NOTES = interactionTerminalNotes('question', {\n expired: 'The original run ended. Answer now to send a new message with this context.',\n cancelled: 'The agent withdrew this question. Answer now to send a new message with this context.',\n})\n\nexport function InteractionQuestionCard({\n interaction,\n canWrite,\n submitAnswer,\n onResolved,\n onLateAnswer,\n kindLabel,\n timeoutNote,\n renderMarkdown,\n className,\n}: InteractionQuestionCardProps) {\n const [values, setValues] = useState<FieldValues>(() =>\n fieldValuesFromAnswers(interaction.fields, interaction.answers))\n const [submitting, setSubmitting] = useState(false)\n // Terminal state this card learned locally (submit success / 410) before the\n // stream part catches up. A terminal stream status always wins.\n const [localStatus, setLocalStatus] = useState<Exclude<ChatInteractionStatus, 'pending'> | null>(null)\n const [lateAnswerSent, setLateAnswerSent] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const submitInFlightRef = useRef(false)\n // The ask this card's state currently belongs to. State, never a ref: it is\n // compared and written during render, and a ref would not be transactional\n // with the resets below. React may abandon a render — the discarded pass's\n // `setValues` would be thrown away while a ref mutation survived it, leaving\n // this guard claiming the reset had happened over the previous ask's answers.\n const [askId, setAskId] = useState(interaction.id)\n\n // Every answer-bearing piece of state belongs to ONE ask, so being handed the\n // NEXT one starts over. A host can resolve one question and be handed another\n // on the same card instance (a run that re-parks, a queue that advances); left\n // alone, the new question would render with the previous answer already\n // filled in and its terminal chrome still showing — one click from submitting\n // an answer to a question the user never read.\n //\n // Keyed on `id` because `id` IS the ask's identity: a different id is a\n // different question. Re-issuing the same id with different fields is\n // therefore NOT a new ask and deliberately does not reset — an answer already\n // typed against those fields survives. A host that changes what it is asking\n // must change the id; what it may re-send under one id is the answer, which\n // arrives as `answers` and resyncs through the effect below.\n //\n // During render, not in an effect — React's documented \"adjusting state when\n // a prop changes\" pattern: it re-runs this component immediately with the new\n // state, before committing and before rendering children, so nothing escapes\n // the render phase. An effect would instead commit one frame of the previous\n // answer under the new question before clearing it. `submitting` and\n // `submitInFlightRef` are deliberately NOT reset — they are owned by the\n // in-flight request's `finally`, and clearing them here would let a second\n // submit start while the first is still outstanding.\n //\n // The visible cost: an id that changes WHILE a submit is outstanding leaves\n // the new question reading \"Submitting…\" and disabled until that request\n // settles. Bounded, because `settleInteractionSubmit` holds the submitter to\n // this card's own deadline rather than trusting it to have one. Preferred to\n // the alternative, since the only way to free the button early is to drop the\n // in-flight guard, and a second submit racing the first is a real bug where a\n // stale label is only a confusing one.\n if (askId !== interaction.id) {\n setAskId(interaction.id)\n setValues(fieldValuesFromAnswers(interaction.fields, interaction.answers))\n setLocalStatus(null)\n setLateAnswerSent(false)\n setError(null)\n }\n\n useEffect(() => {\n if (!interaction.answers) return\n setValues(fieldValuesFromAnswers(interaction.fields, interaction.answers))\n }, [interaction.answers, interaction.fields])\n\n const status: ChatInteractionStatus = isTerminalInteractionStatus(interaction.status)\n ? interaction.status\n : localStatus ?? interaction.status\n const answered = status === 'answered'\n const lateAnswerable = isLateAnswerableStatus(status) && onLateAnswer !== undefined\n const secretLateAnswerBlocked = lateAnswerable && hasSecretField(interaction.fields)\n const canLateAnswer = canWrite && lateAnswerable && !lateAnswerSent && !secretLateAnswerBlocked\n const disabled = !canWrite || (status !== 'pending' && !canLateAnswer) || submitting\n const answerData = useMemo(() => buildAnswerData(interaction.fields, values), [interaction.fields, values])\n\n const setFieldValue = (name: string, patch: FieldValues[string]) => {\n setValues((prev) => ({ ...prev, [name]: { ...prev[name], ...patch } }))\n }\n\n const toggleSelected = (field: ChatSelectField, optionValue: string) => {\n setValues((prev) => valuesWithSelected(prev, field, optionValue))\n }\n\n async function submitLateAnswer() {\n if (submitInFlightRef.current || !canLateAnswer || !onLateAnswer) return\n const data = buildAnswerData(interaction.fields, values)\n if (!data) return\n submitInFlightRef.current = true\n setSubmitting(true)\n setError(null)\n let accepted: boolean | void\n try {\n accepted = await onLateAnswer(lateAnswerMessage(interaction, data))\n } catch {\n accepted = false\n } finally {\n submitInFlightRef.current = false\n setSubmitting(false)\n }\n if (accepted === false) {\n setError('The new message was not sent. Try again from this card.')\n return\n }\n setLateAnswerSent(true)\n }\n\n async function submit() {\n if (lateAnswerable) {\n await submitLateAnswer()\n return\n }\n if (submitInFlightRef.current || disabled || !answerData) return\n submitInFlightRef.current = true\n setSubmitting(true)\n setError(null)\n try {\n const result = await settleInteractionSubmit(() =>\n submitAnswer({ id: interaction.id, outcome: 'accepted', data: answerData }),\n )\n if (result.ok) {\n setLocalStatus('answered')\n onResolved?.(interaction.id, 'answered', answerData)\n return\n }\n if (result.expired) {\n // The ask is gone (answered elsewhere, timed out, or the session moved\n // on) — flip to the same dead state a cancel event produces.\n setLocalStatus('expired')\n onResolved?.(interaction.id, 'expired')\n return\n }\n setError(result.message)\n } finally {\n submitInFlightRef.current = false\n setSubmitting(false)\n }\n }\n\n const terminalNote = secretLateAnswerBlocked\n ? 'This question asked for a secret, so it cannot be sent as a new chat message. Ask the agent to request it again.'\n : TERMINAL_NOTES[status]\n const showSubmitButton = status === 'pending' || (canWrite && lateAnswerable && !lateAnswerSent)\n // Only while the ask is still open. The note says what happens if nobody\n // answers, which stops being true the moment somebody has — and a resolved\n // card still offering a countdown reads as though the answer did not land.\n // Shown to read-only viewers too: an ask about to settle itself is worth\n // knowing whether or not you are the one who can answer it.\n const showTimeoutNote = timeoutNote != null && status === 'pending'\n let submitLabel = 'Submit answer'\n if (lateAnswerable) {\n submitLabel = submitting ? 'Sending…' : 'Send as new message'\n } else if (submitting) {\n submitLabel = 'Submitting…'\n }\n\n return (\n // The card LANDS. This is the moment the run stopped and handed the turn\n // back — a surface that blinks into place reads as chrome that was always\n // there, which is exactly the wrong reading for the one thing on screen\n // waiting on the reader. `.agent-arrive` runs once on mount; a status\n // change (answered, expired, a failed submit) re-renders the same DOM node\n // and therefore does NOT replay it.\n //\n // `dark:[color-scheme:dark]` keeps the native radios/checkboxes on the\n // dark control scheme — without it they paint light-scheme white on the\n // dark card.\n <div className={`agent-arrive rounded-xl border border-card-edge bg-card p-4 dark:[color-scheme:dark] ${className ?? ''}`}>\n <div className=\"mb-3 flex flex-wrap items-center gap-2\">\n <InteractionBadge variant=\"outline\">{kindLabel ?? 'Question'}</InteractionBadge>\n <InteractionBadge variant={answered ? 'default' : status === 'expired' || status === 'declined' ? 'destructive' : 'outline'}>\n {STATUS_LABELS[status]}\n </InteractionBadge>\n </div>\n\n {interaction.title.trim() && interaction.fields.every((field) => field.label !== interaction.title) && (\n <p className=\"mb-3 text-[15px] font-semibold leading-snug text-foreground\">{interaction.title}</p>\n )}\n {interaction.body && (renderMarkdown\n ? <div className=\"mb-3 text-sm leading-5 text-muted-foreground\">{renderMarkdown(interaction.body)}</div>\n : <p className=\"mb-3 text-sm leading-5 text-muted-foreground\">{interaction.body}</p>)}\n\n <div className=\"space-y-4\">\n {interaction.fields.map((field) => {\n const value = values[field.name] ?? {}\n const select = selectField(field)\n const freeText = freeTextField(field)\n return (\n <fieldset key={field.name} className=\"space-y-2\">\n <p className=\"text-sm font-medium leading-5 text-foreground\">{field.label}</p>\n {select ? (\n <div className=\"space-y-2\">\n <QuestionOptionList\n groupName={`${interaction.id}-${field.name}`}\n idPrefix={`${interaction.id}-${field.name}`}\n options={select.options}\n multi={select.multi === true}\n selectedValues={value.selected ?? []}\n disabled={disabled}\n onToggle={(optionValue) => toggleSelected(select, optionValue)}\n answered={answered}\n />\n {select.allowCustom === true && (\n <input\n type=\"text\"\n value={value.custom ?? ''}\n disabled={disabled}\n onChange={(event) => setFieldValue(field.name, { custom: event.target.value })}\n placeholder=\"Other — type your own answer\"\n aria-label={`Custom answer for ${field.label}`}\n className={FIELD_INPUT_CLASSES}\n />\n )}\n </div>\n ) : field.type === 'boolean' ? (\n <div className=\"flex gap-4\">\n {(['true', 'false'] as const).map((boolValue) => (\n <label key={boolValue} className=\"flex cursor-pointer items-center gap-2 text-sm text-foreground\">\n <input\n type=\"radio\"\n name={`${interaction.id}-${field.name}`}\n value={boolValue}\n checked={(value.selected ?? [])[0] === boolValue}\n disabled={disabled}\n onChange={() => setFieldValue(field.name, { selected: [boolValue] })}\n className=\"h-4 w-4 accent-primary\"\n />\n {boolValue === 'true' ? 'Yes' : 'No'}\n </label>\n ))}\n </div>\n ) : field.type === 'number' ? (\n <input\n type=\"number\"\n value={value.text ?? ''}\n disabled={disabled}\n aria-label={field.label}\n onChange={(event) => setFieldValue(field.name, { text: event.target.value })}\n className={FIELD_INPUT_CLASSES}\n />\n ) : field.type === 'secret' ? (\n <input\n type=\"password\"\n value={value.text ?? ''}\n disabled={disabled}\n aria-label={field.label}\n onChange={(event) => setFieldValue(field.name, { text: event.target.value })}\n placeholder={field.placeholder}\n maxLength={freeText ? textFieldMaxLength(freeText) : undefined}\n className={FIELD_INPUT_CLASSES}\n />\n ) : (\n <textarea\n value={value.text ?? ''}\n disabled={disabled}\n aria-label={field.label}\n onChange={(event) => setFieldValue(field.name, { text: event.target.value })}\n rows={3}\n maxLength={freeText ? textFieldMaxLength(freeText) : undefined}\n placeholder={field.type === 'text' ? field.placeholder : undefined}\n className={FIELD_INPUT_CLASSES}\n />\n )}\n </fieldset>\n )\n })}\n </div>\n\n {/* Announced, not just shown: a submit that failed is the one thing on this\n card that changes without the reader having moved focus. */}\n {error && <p role=\"alert\" className=\"mt-3 text-xs text-destructive\">{error}</p>}\n {terminalNote && <p className=\"mt-3 text-xs text-muted-foreground\">{terminalNote}</p>}\n\n {(showSubmitButton || showTimeoutNote) && (\n <div className=\"mt-4 flex flex-wrap items-center justify-end gap-2\">\n {showTimeoutNote && (\n <div className=\"mr-auto text-xs text-muted-foreground\">{timeoutNote}</div>\n )}\n {showSubmitButton && (\n <InteractionActionButton onClick={() => void submit()} disabled={disabled || !answerData}>\n {submitLabel}\n </InteractionActionButton>\n )}\n </div>\n )}\n {answered && (\n <div className=\"mt-4 flex items-center justify-end\">\n <span className=\"inline-flex items-center gap-1 text-xs text-muted-foreground\"><CheckGlyph className=\"h-3 w-3\" />Answered</span>\n </div>\n )}\n {lateAnswerSent && (\n <div className=\"mt-4 flex items-center justify-end\">\n <span className=\"inline-flex items-center gap-1 text-xs text-muted-foreground\"><CheckGlyph className=\"h-3 w-3\" />Sent as new message</span>\n </div>\n )}\n </div>\n )\n}\n","/**\n * Shared answer-building + submit plumbing for the interaction cards\n * (question, plan). Client-safe, no React: cards own their state, this owns\n * the wire. Lifted from the gtm-agent fork (the most fix-absorbed of the three\n * product copies), including the 30s submit timeout that keeps a dead route\n * from wedging a card in \"Submitting…\".\n */\n\nimport type {\n ChatInteraction,\n ChatInteractionField,\n ChatInteractionStatus,\n ChatSelectField,\n InteractionAnswers,\n InteractionData,\n} from './chat-interactions'\n\n// ---------------------------------------------------------------------------\n// Card copy helpers\n\n/** Status-badge labels for an interaction card. `cancelled`/`expired` read the\n * same across cards; each card supplies its own verbs for the other states\n * (a question is answered/declined; a plan is approved/rejected). */\nexport function interactionStatusLabels(\n labels: { pending: string; answered: string; declined: string },\n): Record<ChatInteractionStatus, string> {\n return { cancelled: 'Withdrawn', expired: 'Expired', ...labels }\n}\n\n/** Terminal-state notes for an interaction card. The expiry/withdrawal lines\n * share one shape around the card's noun (\"question\"/\"plan\"); any extra notes\n * (e.g. a plan's `declined` revision line) merge on top. */\nexport function interactionTerminalNotes(\n noun: string,\n extra?: Partial<Record<ChatInteractionStatus, string>>,\n): Partial<Record<ChatInteractionStatus, string>> {\n return {\n expired: `This ${noun} expired — send a new message to continue.`,\n cancelled: `The agent withdrew this ${noun}.`,\n ...extra,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Answer building\n\n/** Define a record mapping field names to objects with optional selected, text, and custom string arrays or values */\nexport type FieldValues = Record<string, { selected?: string[]; text?: string; custom?: string }>\n\n/** Converts acknowledged, persisted answers back into the local field state\n * consumed by the shared cards. Persisted values are authoritative: this is\n * intentionally used only when an interaction carries `answers`, never to\n * guess an answer from the absence of an outstanding sidecar ask. */\nexport function fieldValuesFromAnswers(\n fields: ChatInteractionField[],\n answers: InteractionAnswers | undefined,\n): FieldValues {\n if (!answers) return {}\n const values: FieldValues = {}\n for (const field of fields) {\n const answer = answers[field.name]\n if (answer === undefined) continue\n if (field.type === 'select') {\n values[field.name] = { selected: Array.isArray(answer) ? [...answer] : [String(answer)] }\n } else if (field.type === 'boolean') {\n values[field.name] = { selected: [String(answer)] }\n } else {\n values[field.name] = { text: String(answer) }\n }\n }\n return values\n}\n\n/** The submitted value for one field, or null when it has no answer yet.\n *\n * Returns `InteractionAnswers[string]`, not the wider `InteractionData[string]`:\n * a card reads its value out of a rendered control, so every branch below\n * yields a plain scalar or string array. `InteractionData` also admits a\n * one-use `secret_handle` reference, which no control here can produce and\n * which `onResolved` must never receive — that path persists into the visible\n * transcript. Declaring the narrow type keeps the handle out by construction\n * rather than by review. */\nexport function fieldAnswer(field: ChatInteractionField, values: FieldValues): InteractionAnswers[string] | null {\n const value = values[field.name] ?? {}\n if (field.type === 'select') {\n const custom = (field as ChatSelectField).allowCustom === true ? value.custom?.trim() : undefined\n const chosen = [...(value.selected ?? []), ...(custom ? [custom] : [])]\n if (field.multi !== true && custom) return [custom]\n return chosen.length > 0 ? chosen : null\n }\n if (field.type === 'number') {\n const parsed = Number(value.text)\n return value.text?.trim() && Number.isFinite(parsed) ? parsed : null\n }\n if (field.type === 'boolean') return value.selected ? value.selected[0] === 'true' : null\n const text = value.text?.trim()\n return text ? text : null\n}\n\n/** All required fields answered → the respond payload; else null (not\n * submittable yet). Optional unanswered fields are omitted. */\nexport function buildAnswerData(fields: ChatInteractionField[], values: FieldValues): InteractionAnswers | null {\n const data: InteractionAnswers = {}\n for (const field of fields) {\n const answer = fieldAnswer(field, values)\n if (answer === null) {\n if (field.required === false) continue\n return null\n }\n data[field.name] = answer\n }\n return data\n}\n\n// ---------------------------------------------------------------------------\n// Late answers (question card): an expired/withdrawn ask can still be sent as\n// a NEW chat turn carrying the question context, so the user's typed answer is\n// never dropped on the floor.\n\n/** Determine if a status is late answerable by checking if it is expired or cancelled */\nexport function isLateAnswerableStatus(status: ChatInteractionStatus): boolean {\n return status === 'expired' || status === 'cancelled'\n}\n\n/** Secrets must never leave the sidecar answer channel for the visible chat\n * transcript, so a secret-bearing ask cannot be late-answered. */\nexport function hasSecretField(fields: ChatInteractionField[]): boolean {\n return fields.some((field) => field.type === 'secret')\n}\n\nfunction optionLabel(field: ChatSelectField, value: string): string {\n return field.options.find((option) => option.value === value)?.label ?? value\n}\n\nfunction answerText(field: ChatInteractionField, answer: InteractionData[string]): string {\n if (field.type === 'select' && Array.isArray(answer)) {\n return answer.map((value) => optionLabel(field as ChatSelectField, value)).join(', ')\n }\n if (field.type === 'boolean') return answer === true ? 'Yes' : 'No'\n if (field.type === 'secret') return '[secret omitted]'\n return String(answer)\n}\n\n/** Renders the late answer as a self-contained chat message: the original\n * question, its context, and the user's answer(s). */\nexport function lateAnswerMessage(interaction: ChatInteraction, data: InteractionData): string {\n const title = interaction.title.trim() || 'the earlier question'\n const body = interaction.body?.trim()\n const answers = interaction.fields\n .map((field) => {\n const answer = data[field.name]\n if (answer === undefined) return null\n return { label: field.label.trim(), text: answerText(field, answer).trim() }\n })\n .filter((item): item is { label: string; text: string } => !!item && item.text.length > 0)\n\n const only = answers.length === 1 ? answers[0] : undefined\n const answerSummary = only\n ? only.text\n : answers.map((item) => `${item.label || 'Answer'}: ${item.text}`).join('\\n')\n\n return [\n `Regarding your earlier question: \"${title}\"`,\n body ? `Context: ${body}` : null,\n `My answer: ${answerSummary}`,\n ].filter((line): line is string => !!line).join('\\n')\n}\n\n// ---------------------------------------------------------------------------\n// Submit plumbing\n\n/** Define the timeout duration in milliseconds for submitting an interaction */\nexport const INTERACTION_SUBMIT_TIMEOUT_MS = 30_000\n/** Provide the timeout message displayed when the agent cannot be reached during interaction submission */\nexport const INTERACTION_SUBMIT_TIMEOUT_MESSAGE = 'Could not reach the agent. Try again.'\n\n/** One card submission: which ask, resolved how, with what answers. */\nexport interface InteractionAnswerSubmission {\n id: string\n outcome: 'accepted' | 'declined'\n data?: InteractionData\n}\n\n/** Resolve the result of an interaction submission indicating success or failure with details */\nexport type InteractionSubmitResult =\n | { ok: true }\n | { ok: false; expired: boolean; message: string }\n\n/** The cards' only side-effect seam: POST one resolution, report the normalized\n * outcome. Products bind their route URL + routing fields (workspaceId,\n * threadId, session path param) via `createInteractionAnswerSubmitter` or a\n * hand-rolled implementation. */\nexport type SubmitInteractionAnswer = (submission: InteractionAnswerSubmission) => Promise<InteractionSubmitResult>\n\n/** Extracts the most specific error message a route returned. */\nexport async function responseErrorMessage(res: Response): Promise<{ code?: string; message: string }> {\n const text = await res.text().catch(() => '')\n if (text) {\n try {\n const parsed = JSON.parse(text) as { code?: unknown; error?: unknown; message?: unknown }\n const message = typeof parsed.error === 'string' && parsed.error.trim() ? parsed.error\n : typeof parsed.message === 'string' && parsed.message.trim() ? parsed.message\n : null\n if (message) return { ...(typeof parsed.code === 'string' ? { code: parsed.code } : {}), message }\n } catch { /* non-JSON body falls through */ }\n }\n return { message: `Answer failed (${res.status})` }\n}\n\n/** Define options for submitting interaction answers including URL, body, timeout, and fetch implementation */\nexport interface InteractionAnswerSubmitterOptions {\n /** The product's answer route (the POST half of `createInteractionAnswerRoute`).\n * A function when the URL carries the session (e.g. `/api/sessions/${id}/interactions`). */\n url: string | ((submission: InteractionAnswerSubmission) => string)\n /** Extra routing fields merged into the POST body (e.g. workspaceId, threadId). */\n body?: Record<string, unknown> | ((submission: InteractionAnswerSubmission) => Record<string, unknown>)\n timeoutMs?: number\n fetchImpl?: typeof fetch\n}\n\n/**\n * Runs a host-supplied submitter under the CARD's own deadline, and always\n * resolves.\n *\n * `createInteractionAnswerSubmitter` aborts its own fetch, but a product may\n * pass any `SubmitInteractionAnswer` — commonly one wrapping an untimed\n * `fetch`. The deadline cannot live only in the submitter, because what gets\n * stuck is the card: its in-flight guard is cleared by the awaited promise\n * settling, so a submitter that never settles leaves that guard set for the\n * life of the instance — \"Submitting…\" forever, and no answer can be sent\n * again. A submitter with its own shorter timeout simply wins the race.\n *\n * Rejection is normalized too: a submitter that throws would otherwise escape\n * the click handler as an unhandled rejection, leaving the user with a card\n * that silently did nothing. It becomes a visible, retryable message instead.\n */\nexport function settleInteractionSubmit(\n run: () => Promise<InteractionSubmitResult>,\n timeoutMs: number = INTERACTION_SUBMIT_TIMEOUT_MS,\n): Promise<InteractionSubmitResult> {\n return new Promise((resolve) => {\n const timer = setTimeout(\n () => resolve({ ok: false, expired: false, message: INTERACTION_SUBMIT_TIMEOUT_MESSAGE }),\n timeoutMs,\n )\n const settle = (result: InteractionSubmitResult) => {\n clearTimeout(timer)\n // A late result after the deadline resolves nothing — this promise is\n // already settled — so the card keeps the timeout it already reported.\n resolve(result)\n }\n // `Promise.resolve().then(run)` so a submitter that throws SYNCHRONOUSLY is\n // caught here rather than at the call site, where it would bypass this\n // whole guard.\n Promise.resolve()\n .then(run)\n .then(settle, (err: unknown) =>\n settle({\n ok: false,\n expired: false,\n message: err instanceof Error ? err.message : 'Failed to submit the answer',\n }),\n )\n })\n}\n\n/**\n * Builds the `SubmitInteractionAnswer` the cards consume: POSTs\n * `{ ...routingFields, id, outcome, data? }` with an abortable timeout and\n * normalizes the outcome. `expired` is the 410 path — the ask is gone\n * (answered elsewhere, timed out, or the session moved on) and the card must\n * flip to the same dead state a cancel event produces.\n */\nexport function createInteractionAnswerSubmitter(options: InteractionAnswerSubmitterOptions): SubmitInteractionAnswer {\n const timeoutMs = options.timeoutMs ?? INTERACTION_SUBMIT_TIMEOUT_MS\n return async (submission) => {\n const doFetch = options.fetchImpl ?? fetch\n const url = typeof options.url === 'function' ? options.url(submission) : options.url\n const extra = typeof options.body === 'function' ? options.body(submission) : options.body ?? {}\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(INTERACTION_SUBMIT_TIMEOUT_MESSAGE), timeoutMs)\n try {\n const res = await doFetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n signal: controller.signal,\n body: JSON.stringify({\n ...extra,\n id: submission.id,\n outcome: submission.outcome,\n ...(submission.data ? { data: submission.data } : {}),\n }),\n })\n if (res.ok) return { ok: true }\n const failure = await responseErrorMessage(res)\n return { ok: false, expired: res.status === 410, message: failure.message }\n } catch (err) {\n if (controller.signal.aborted) {\n return { ok: false, expired: false, message: INTERACTION_SUBMIT_TIMEOUT_MESSAGE }\n }\n return { ok: false, expired: false, message: err instanceof Error ? err.message : 'Failed to submit the answer' }\n } finally {\n clearTimeout(timer)\n }\n }\n}\n","/**\n * InteractionPlanCard — the plan-approval round-trip card (kind:\"plan\",\n * claude-code plan mode). The plan itself arrives as markdown in\n * `interaction.body`; the answerSpec is producer-defined, so fields render\n * generically — a free-text field doubles as the rejection-feedback input.\n * Approve POSTs outcome:\"accepted\", Request changes POSTs outcome:\"declined\"\n * with any typed feedback.\n *\n * Markdown is injected (`renderMarkdown`, matching the rest of `web-react`);\n * without it the plan body falls back to pre-wrapped plain text. Pure data +\n * callbacks: no fetch inside the component.\n */\n\nimport { useLayoutEffect, useMemo, useRef, useState, type ReactNode } from 'react'\nimport type { ChatInteraction, ChatInteractionField, ChatInteractionStatus, InteractionData } from './chat-interactions'\nimport { fieldAcceptsFreeText, isTerminalInteractionStatus } from './chat-interactions'\nimport {\n buildAnswerData,\n fieldAnswer,\n fieldValuesFromAnswers,\n interactionStatusLabels,\n interactionTerminalNotes,\n isLateAnswerableStatus,\n type FieldValues,\n type SubmitInteractionAnswer,\n} from './interaction-card-support'\nimport { InteractionActionButton, InteractionBadge } from './interaction-question-card'\n\nfunction CheckGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n )\n}\n\nfunction ChevronDownGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n )\n}\n\nexport interface InteractionPlanCardProps {\n interaction: ChatInteraction\n /** Viewer-vs-editor gate: false renders everything read-only. */\n canWrite: boolean\n /** POST one resolution to the product's answer route (see\n * `createInteractionAnswerSubmitter`). */\n submitAnswer: SubmitInteractionAnswer\n /** Fired when this card resolves locally (approved/rejected, or discovered\n * expired via a 410) so the stream/route state stays in sync. */\n onResolved?: (id: string, status: Exclude<ChatInteractionStatus, 'pending'>) => void\n /** Fired when the user asks the agent to re-submit an expired/withdrawn plan\n * as a new chat turn. Receives the interaction so a callback shared across\n * cards (e.g. via DurableChatCards) knows which plan fired. Return/resolve\n * `false` (or throw) to report the send failed and keep the affordance\n * retryable. Omit to hide it entirely. */\n onReRequest?: (interaction: ChatInteraction) => boolean | void | Promise<boolean | void>\n /** Overrides the default re-request button label\n * (\"Ask agent to re-submit the plan\" — gtm's exact current copy). */\n reRequestLabel?: string\n /** Renders the plan body (markdown). Falls back to pre-wrapped plain text. */\n renderMarkdown?: (markdown: string) => ReactNode\n className?: string\n}\n\nconst STATUS_LABELS = interactionStatusLabels({\n pending: 'Waiting for your approval',\n answered: 'Approved',\n declined: 'Rejected',\n})\n\nconst TERMINAL_NOTES = interactionTerminalNotes('plan', {\n declined: 'The agent was asked to revise the plan.',\n})\n\nconst DEFAULT_RE_REQUEST_LABEL = 'Ask agent to re-submit the plan'\n\n/** Body height (px) beyond which the plan collapses behind a \"Show full plan\"\n * control so a long plan doesn't dominate the transcript. */\nconst COLLAPSED_MAX_HEIGHT = 320\n\n/** The submitted answer for one field as display text (select values resolve\n * to their option labels), or null when the field went unanswered. Used for\n * the terminal readout, where the dead disabled inputs used to sit. */\nfunction submittedFieldText(field: ChatInteractionField, values: FieldValues): string | null {\n const answer = fieldAnswer(field, values)\n if (answer === null) return null\n if (Array.isArray(answer)) {\n const options = field.type === 'select' ? field.options : undefined\n return answer.map((value) => options?.find((option) => option.value === value)?.label ?? value).join(', ')\n }\n return String(answer)\n}\n\n/** True when the plan body is taller than its collapsed cap. Measured (ref +\n * scrollHeight) rather than assumed from text length, so a short plan shows\n * no fade and no toggle — the collapse UI used to paint over every plan,\n * overflowing or not. */\nfunction useBodyOverflows(body: string | undefined, renderMarkdown: ((markdown: string) => ReactNode) | undefined) {\n const bodyRef = useRef<HTMLDivElement>(null)\n const [overflows, setOverflows] = useState(false)\n useLayoutEffect(() => {\n const el = bodyRef.current\n if (el) setOverflows(el.scrollHeight > COLLAPSED_MAX_HEIGHT)\n }, [body, renderMarkdown])\n return { bodyRef, overflows }\n}\n\nexport function InteractionPlanCard({\n interaction,\n canWrite,\n submitAnswer,\n onResolved,\n onReRequest,\n reRequestLabel,\n renderMarkdown,\n className,\n}: InteractionPlanCardProps) {\n const [values, setValues] = useState<FieldValues>({})\n const [expanded, setExpanded] = useState(false)\n const [submitting, setSubmitting] = useState<'approve' | 'reject' | 'requesting' | null>(null)\n // Terminal state this card learned locally (resolved / 410) before the\n // stream part catches up. A terminal stream status always wins.\n const [localStatus, setLocalStatus] = useState<Exclude<ChatInteractionStatus, 'pending'> | null>(null)\n const [reRequested, setReRequested] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const submitInFlightRef = useRef(false)\n const { bodyRef, overflows } = useBodyOverflows(interaction.body, renderMarkdown)\n\n const status: ChatInteractionStatus = isTerminalInteractionStatus(interaction.status)\n ? interaction.status\n : localStatus ?? interaction.status\n const reRequestable = isLateAnswerableStatus(status) && onReRequest !== undefined\n const canReRequest = canWrite && reRequestable && !reRequested\n const disabled = !canWrite || status !== 'pending' || submitting !== null\n\n // Approve sends whatever the producer's answerSpec requires; an empty or\n // all-optional spec still approves with `data: {}` (the sidecar validates\n // fail-closed either way).\n const approveData = useMemo(() => buildAnswerData(interaction.fields, values), [interaction.fields, values])\n // Reject carries only the values actually typed/picked — feedback is\n // optional, so unanswered fields are simply omitted.\n const rejectData = useMemo(() => {\n const data: InteractionData = {}\n for (const field of interaction.fields) {\n const answer = fieldAnswer(field, values)\n if (answer !== null) data[field.name] = answer\n }\n return data\n }, [interaction.fields, values])\n\n async function submit(outcome: 'accepted' | 'declined') {\n const data = outcome === 'accepted' ? approveData : rejectData\n if (submitInFlightRef.current || disabled || data === null) return\n submitInFlightRef.current = true\n setSubmitting(outcome === 'accepted' ? 'approve' : 'reject')\n setError(null)\n try {\n const result = await submitAnswer({ id: interaction.id, outcome, data })\n if (result.ok) {\n const resolved = outcome === 'accepted' ? 'answered' : 'declined'\n setLocalStatus(resolved)\n onResolved?.(interaction.id, resolved)\n return\n }\n if (result.expired) {\n setLocalStatus('expired')\n onResolved?.(interaction.id, 'expired')\n return\n }\n setError(result.message)\n } finally {\n submitInFlightRef.current = false\n setSubmitting(null)\n }\n }\n\n async function requestReSubmission() {\n if (submitInFlightRef.current || !canReRequest || !onReRequest) return\n submitInFlightRef.current = true\n setSubmitting('requesting')\n setError(null)\n let accepted: boolean | void\n try {\n accepted = await onReRequest(interaction)\n } catch {\n accepted = false\n } finally {\n submitInFlightRef.current = false\n setSubmitting(null)\n }\n if (accepted === false) {\n setError('The re-request was not sent. Try again.')\n return\n }\n setReRequested(true)\n }\n\n const terminalNote = TERMINAL_NOTES[status]\n const approved = status === 'answered'\n // The terminal readout shows what was actually submitted: persisted answers\n // win; a card resolved locally this session falls back to its own values.\n const submittedValues = interaction.answers\n ? fieldValuesFromAnswers(interaction.fields, interaction.answers)\n : values\n\n return (\n // Same arrival as the question card: an approval is the run stopping, and\n // the card that carries it should land rather than appear. Once on screen\n // it never re-animates — approving, rejecting or a 410 changes state on the\n // same DOM node, and a CSS animation does not replay on a re-render.\n <div className={`agent-arrive rounded-xl border border-card-edge bg-card p-4 ${className ?? ''}`}>\n <div className=\"mb-3 flex flex-wrap items-center gap-2\">\n <InteractionBadge variant=\"outline\">Plan</InteractionBadge>\n <InteractionBadge variant={approved ? 'default' : status === 'expired' || status === 'declined' ? 'destructive' : 'outline'}>\n {STATUS_LABELS[status]}\n </InteractionBadge>\n </div>\n\n {interaction.title.trim() && (\n <p className=\"mb-3 text-[15px] font-semibold leading-snug text-foreground\">{interaction.title}</p>\n )}\n\n {interaction.body && (\n <div className=\"relative\">\n <div\n ref={bodyRef}\n className=\"overflow-hidden text-sm text-foreground\"\n style={expanded || !overflows ? undefined : { maxHeight: COLLAPSED_MAX_HEIGHT }}\n >\n {renderMarkdown\n ? renderMarkdown(interaction.body)\n : <p className=\"whitespace-pre-wrap leading-5\">{interaction.body}</p>}\n </div>\n {overflows && !expanded && (\n <div className=\"pointer-events-none absolute inset-x-0 bottom-0 h-12 bg-gradient-to-t from-card to-transparent\" />\n )}\n {overflows && (\n <button\n type=\"button\"\n onClick={() => setExpanded((prev) => !prev)}\n className=\"relative z-10 mt-1 inline-flex items-center gap-1 text-xs text-muted-foreground transition hover:text-foreground\"\n >\n <ChevronDownGlyph className={`h-3 w-3 transition-transform ${expanded ? 'rotate-180' : ''}`} />\n {expanded ? 'Collapse plan' : 'Show full plan'}\n </button>\n )}\n </div>\n )}\n\n {interaction.fields.length > 0 && status === 'pending' && (\n // The fields do NOT carry their own `.agent-arrive`. One level per\n // surface: the card is the thing that was not there a moment ago, and a\n // second entrance nested inside a travelling parent composes two\n // translations and two opacity ramps over the same pixels — the card\n // lands while its contents are still arriving into it, which reads as\n // instability rather than as sequence.\n //\n // The card level is the one that survives, because a stagger is a claim\n // that these appeared one after another and inside a landing card that\n // claim is false — they all appeared with it. The question card keeps\n // its option rows staggered for the opposite reason: those are the\n // CHOICES being offered, and telling the eye there are three of them\n // before it has read any is information about the decision. A form's\n // fields carry no such count to announce.\n <div className=\"mt-3 space-y-4\">\n {interaction.fields.map((field) => (\n <fieldset key={field.name} className=\"space-y-2\">\n <p className=\"text-sm font-medium leading-5 text-foreground\">{field.label}</p>\n {fieldAcceptsFreeText(field) ? (\n <textarea\n value={values[field.name]?.text ?? ''}\n disabled={disabled}\n aria-label={field.label}\n onChange={(event) =>\n setValues((prev) => ({ ...prev, [field.name]: { ...prev[field.name], text: event.target.value } }))}\n rows={2}\n placeholder={field.type === 'text' ? field.placeholder ?? 'Optional feedback for the agent' : undefined}\n className=\"w-full rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground focus:border-primary disabled:opacity-50\"\n />\n ) : (\n <input\n type=\"text\"\n value={values[field.name]?.text ?? ''}\n disabled={disabled}\n aria-label={field.label}\n onChange={(event) =>\n setValues((prev) => ({ ...prev, [field.name]: { ...prev[field.name], text: event.target.value } }))}\n className=\"w-full rounded-lg border border-border bg-background px-3 py-2 text-sm text-foreground focus:border-primary disabled:opacity-50\"\n />\n )}\n </fieldset>\n ))}\n </div>\n )}\n\n {interaction.fields.length > 0 && status !== 'pending' && (\n <div className=\"mt-3 space-y-2\">\n {interaction.fields.map((field) => {\n const text = submittedFieldText(field, submittedValues)\n if (!text) return null\n return (\n <div key={field.name}>\n <p className=\"text-xs font-medium text-muted-foreground\">{field.label}</p>\n <p className=\"mt-0.5 whitespace-pre-wrap text-sm text-foreground\">{text}</p>\n </div>\n )\n })}\n </div>\n )}\n\n {error && <p className=\"mt-3 text-xs text-destructive\">{error}</p>}\n {terminalNote && <p className=\"mt-3 text-xs text-muted-foreground\">{terminalNote}</p>}\n\n {canReRequest && (\n <div className=\"mt-4 flex items-center justify-end\">\n <InteractionActionButton variant=\"outline\" onClick={() => void requestReSubmission()} disabled={submitting !== null}>\n {submitting === 'requesting' ? 'Asking…' : reRequestLabel ?? DEFAULT_RE_REQUEST_LABEL}\n </InteractionActionButton>\n </div>\n )}\n {reRequested && (\n <div className=\"mt-4 flex items-center justify-end\">\n <span className=\"inline-flex items-center gap-1 text-xs text-muted-foreground\"><CheckGlyph className=\"h-3 w-3\" />Re-submission requested</span>\n </div>\n )}\n\n {status === 'pending' && (\n <div className=\"mt-4 flex items-center justify-end gap-2\">\n <InteractionActionButton variant=\"outline\" onClick={() => void submit('declined')} disabled={disabled}>\n {submitting === 'reject' ? 'Sending…' : 'Request changes'}\n </InteractionActionButton>\n <InteractionActionButton onClick={() => void submit('accepted')} disabled={disabled || approveData === null}>\n {submitting === 'approve' ? 'Approving…' : 'Approve plan'}\n </InteractionActionButton>\n </div>\n )}\n {approved && (\n <div className=\"mt-4 flex items-center justify-end\">\n <span className=\"inline-flex items-center gap-1 text-xs text-muted-foreground\"><CheckGlyph className=\"h-3 w-3\" />Approved</span>\n </div>\n )}\n </div>\n )\n}\n","import type { ReactNode } from 'react'\nimport { persistedPartToInteraction, type ChatInteraction, type InteractionAnswers } from './chat-interactions'\nimport { persistedPartToPlan, type ChatPlan } from '../plans/index'\nimport type { DurablePlanDecision, DurablePlanDecisionResult } from './durable-plan-flow'\nimport { DurablePlanCard } from './durable-plan-card'\nimport { InteractionPlanCard } from './interaction-plan-card'\nimport { InteractionQuestionCard } from './interaction-question-card'\nimport type { SubmitInteractionAnswer } from './interaction-card-support'\n\nexport type DurableChatCard =\n | { kind: 'plan'; key: string; plan: ChatPlan }\n | { kind: 'interaction'; key: string; interaction: ChatInteraction }\n\nfunction planIdentity(planId: string, revision: number): string {\n return `${planId}:${revision}`\n}\n\n/** Converts persisted/live parts to canonical cards. Legacy interaction-plan\n * cards are suppressed only when their raw part carries an explicit planId and\n * revision matching a durable plan. Identical markdown alone is never proof. */\nexport function durableChatCardsFromParts(parts: Array<Record<string, unknown>>): DurableChatCard[] {\n const durablePlans = new Set<string>()\n for (const part of parts) {\n const plan = persistedPartToPlan(part)\n if (plan) durablePlans.add(planIdentity(plan.planId, plan.revision))\n }\n const cards: DurableChatCard[] = []\n for (const part of parts) {\n const plan = persistedPartToPlan(part)\n if (plan) {\n cards.push({ kind: 'plan', key: `plan:${planIdentity(plan.planId, plan.revision)}`, plan })\n continue\n }\n const interaction = persistedPartToInteraction(part)\n if (!interaction) continue\n const correlatedPlan = interaction.kind === 'plan' && typeof part.planId === 'string' &&\n typeof part.revision === 'number' && durablePlans.has(planIdentity(part.planId, part.revision))\n if (correlatedPlan) continue\n cards.push({ kind: 'interaction', key: `interaction:${interaction.id}`, interaction })\n }\n return cards\n}\n\nexport interface DurableChatCardsProps {\n parts: Array<Record<string, unknown>>\n canWrite: boolean\n submitInteraction: SubmitInteractionAnswer\n decidePlan: (plan: ChatPlan, decision: DurablePlanDecision, feedback?: string) => Promise<DurablePlanDecisionResult | null>\n decidingPlan?: (plan: ChatPlan) => DurablePlanDecision | null\n planError?: (plan: ChatPlan) => string | null\n onInteractionResolved?: (id: string, status: Exclude<ChatInteraction['status'], 'pending'>, answers?: InteractionAnswers) => void\n onLateAnswer?: (message: string) => boolean | void | Promise<boolean | void>\n /** Fired when the user asks the agent to re-submit an expired/withdrawn\n * plan card as a new chat turn; receives that card's interaction. Omit to\n * hide the affordance entirely. */\n onReRequest?: (interaction: ChatInteraction) => boolean | void | Promise<boolean | void>\n /** Overrides the default re-request button label. */\n reRequestLabel?: string\n renderMarkdown?: (markdown: string) => ReactNode\n className?: string\n}\n\n/** Ready-to-embed canonical question/plan card lane for persisted assistant\n * parts. Apps inject transport and styling callbacks instead of rebuilding the\n * lifecycle/render switch. */\nexport function DurableChatCards({\n parts,\n canWrite,\n submitInteraction,\n decidePlan,\n decidingPlan,\n planError,\n onInteractionResolved,\n onLateAnswer,\n onReRequest,\n reRequestLabel,\n renderMarkdown,\n className,\n}: DurableChatCardsProps) {\n const cards = durableChatCardsFromParts(parts)\n if (cards.length === 0) return null\n return (\n <div className={`space-y-3 ${className ?? ''}`}>\n {cards.map((card) => {\n if (card.kind === 'plan') {\n return (\n <DurablePlanCard\n key={card.key}\n plan={card.plan}\n canWrite={canWrite}\n decide={(decision, feedback) => decidePlan(card.plan, decision, feedback)}\n deciding={decidingPlan?.(card.plan)}\n error={planError?.(card.plan)}\n renderMarkdown={renderMarkdown}\n />\n )\n }\n if (card.interaction.kind === 'plan') {\n return (\n <InteractionPlanCard\n key={card.key}\n interaction={card.interaction}\n canWrite={canWrite}\n submitAnswer={submitInteraction}\n onResolved={onInteractionResolved}\n onReRequest={onReRequest}\n reRequestLabel={reRequestLabel}\n renderMarkdown={renderMarkdown}\n />\n )\n }\n return (\n <InteractionQuestionCard\n key={card.key}\n interaction={card.interaction}\n canWrite={canWrite}\n submitAnswer={submitInteraction}\n onResolved={onInteractionResolved}\n onLateAnswer={onLateAnswer}\n />\n )\n })}\n </div>\n )\n}\n","/**\n * Renders a message's attachment parts (images + files) as thumbnails and\n * download chips — the transcript-side counterpart to `ChatComposer`'s\n * staged-upload chips. Ported from gtm-agent's `chat-attachment-parts.tsx`\n * onto agent-app's RAW-BYTES download contract: the host supplies\n * `resolveFileUrl(part)`, a URL that serves the attachment's raw bytes\n * directly, so this module never parses a JSON `{file:{blobUrl,body}}`\n * envelope or decodes a `[base64]` marker the way gtm's vault route did.\n *\n * No icon-library or primitives dependency (`ChatComposer`'s house style):\n * the loading skeleton is an inline `animate-pulse` span and the few glyphs\n * are inline SVGs.\n */\n\nimport { useCallback, useEffect, useState, type ReactNode } from 'react'\nimport type { ChatAttachmentPart } from './chat-attachments'\n\n// ── glyphs (no icon-library dependency) ───────────────────────────────────\n\nfunction FileGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z\" />\n <path d=\"M14 2v6h6\" />\n </svg>\n )\n}\n\nfunction ImageGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" />\n <circle cx=\"9\" cy=\"9\" r=\"2\" />\n <path d=\"m21 15-5-5L5 21\" />\n </svg>\n )\n}\n\nfunction WarningGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M12 9v4m0 4h.01M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0Z\" />\n </svg>\n )\n}\n\nfunction iconForMediaType(mediaType: string | undefined): (props: { className?: string }) => ReactNode {\n return mediaType?.startsWith('image/') ? ImageGlyph : FileGlyph\n}\n\n/** Display byte size for a chip: \"1.2 MB\" / \"47 KB\" — one rounded figure with a\n * spaced unit. Deliberately NOT the wire formatter (`formatBytes` in\n * chat-routes/wire), whose exact \"1MB 152KB\" decomposition serves limit-error\n * messages; a chip wants a glanceable approximation. */\nfunction formatDisplayBytes(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`\n if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`\n const mb = bytes / (1024 * 1024)\n return `${mb >= 10 ? Math.round(mb) : mb.toFixed(1)} MB`\n}\n\n// ── display name ────────────────────────────────────────────────────────\n\n/** Resolve a display name for an attachment part. `isChatAttachmentPart`\n * only checks `type`/`path` (by design — a structural guard, not a\n * provenance proof), so a stored part can carry `path` with no `name` if it\n * was written before a normalization fix landed. Rather than rendering a\n * blank label or downloading a file named `undefined`, fall back to the\n * storage path's own basename; only when even that is unusable (e.g. a\n * path ending in `/`) does a caller need the explicit unavailable state. */\nfunction attachmentDisplayName(part: ChatAttachmentPart): string | null {\n if (typeof part.name === 'string' && part.name.trim().length > 0) return part.name\n const base = part.path.split('/').pop() ?? ''\n const trimmed = base.trim()\n return trimmed.length > 0 ? trimmed : null\n}\n\n// ── file loading + cache ───────────────────────────────────────────────────\n\n/** Typed outcome of fetching one attachment's raw bytes. Callers must check\n * `ok` before touching `blob` — a failed fetch never produces a blank\n * render, it produces a visible error state. */\nexport type AttachmentFileResult = { ok: true; blob: Blob } | { ok: false; message: string }\n\n/** Module-level cache so mounting several rows that reference the same URL\n * (e.g. a thumbnail re-rendered across reloads within one session) issues\n * exactly one fetch. Keyed on the RESOLVED url — `resolveFileUrl`'s output —\n * since that is what actually identifies the byte stream to the host. */\nconst attachmentFileCache = new Map<string, Promise<AttachmentFileResult>>()\n\nexport function __resetAttachmentFileCacheForTests(): void {\n attachmentFileCache.clear()\n}\n\nasync function defaultFetchFile(url: string): Promise<Response> {\n return fetch(url, { credentials: 'same-origin' })\n}\n\nasync function fetchAttachmentFile(\n url: string,\n fetchFile: (url: string) => Promise<Response>,\n): Promise<AttachmentFileResult> {\n try {\n const res = await fetchFile(url)\n if (!res.ok) return { ok: false, message: `Failed to load attachment (${res.status})` }\n return { ok: true, blob: await res.blob() }\n } catch (err) {\n return { ok: false, message: err instanceof Error && err.message ? err.message : 'Network error loading attachment' }\n }\n}\n\n/** Fetches (and caches) the raw bytes behind one attachment url. Concurrent\n * callers for the SAME url dedupe to one in-flight fetch. Only a successful\n * settlement stays cached — evicting failures means a remount or click-retry\n * after a transient error issues a fresh fetch. */\nexport function loadAttachmentFile(\n url: string,\n fetchFile: (url: string) => Promise<Response> = defaultFetchFile,\n): Promise<AttachmentFileResult> {\n const cached = attachmentFileCache.get(url)\n if (cached) return cached\n const promise = fetchAttachmentFile(url, fetchFile)\n attachmentFileCache.set(url, promise)\n void promise.then((result) => {\n if (!result.ok && attachmentFileCache.get(url) === promise) attachmentFileCache.delete(url)\n })\n return promise\n}\n\n/** Drives an anchor-click download from an already-resolved blob. Returns a\n * typed outcome rather than throwing — a chip that fails to synthesize the\n * download must show the failure, not silently no-op. */\nexport function triggerAttachmentDownload(name: string, blob: Blob): { ok: true } | { ok: false; message: string } {\n try {\n const url = URL.createObjectURL(blob)\n const link = document.createElement('a')\n link.href = url\n link.download = name\n document.body.appendChild(link)\n link.click()\n link.remove()\n URL.revokeObjectURL(url)\n return { ok: true }\n } catch (err) {\n return { ok: false, message: err instanceof Error && err.message ? err.message : 'Failed to download attachment' }\n }\n}\n\n/** Object URL for a resolved blob, created once per blob and revoked on\n * unmount (or when the blob it was built from changes). */\nfunction useAttachmentObjectUrl(blob: Blob | undefined): string | null {\n const [url, setUrl] = useState<string | null>(null)\n useEffect(() => {\n if (!blob) {\n setUrl(null)\n return\n }\n const objectUrl = URL.createObjectURL(blob)\n setUrl(objectUrl)\n return () => URL.revokeObjectURL(objectUrl)\n }, [blob])\n return url\n}\n\n// ── thumbnail (images) ──────────────────────────────────────────────────────\n\nfunction AttachmentThumbnailError({ name }: { name: string }) {\n return (\n <span className=\"inline-flex h-16 w-16 shrink-0 flex-col items-center justify-center gap-1 rounded-md border border-destructive/40 bg-destructive/10 px-1 text-center text-destructive\">\n <WarningGlyph className=\"h-4 w-4 shrink-0\" />\n <span className=\"line-clamp-2 text-xs leading-tight\">{name}</span>\n </span>\n )\n}\n\n/** Non-clickable placeholder for a stored part with no usable name at all\n * (no `name`, and no basename could be derived from `path`) — never a blank\n * clickable control, and never a download named `undefined`. */\nfunction AttachmentUnavailable({ shape }: { shape: 'thumbnail' | 'chip' }) {\n if (shape === 'thumbnail') {\n return (\n <span\n aria-disabled=\"true\"\n className=\"inline-flex h-16 w-16 shrink-0 flex-col items-center justify-center gap-1 rounded-md border border-border bg-muted px-1 text-center text-muted-foreground\"\n >\n <WarningGlyph className=\"h-4 w-4 shrink-0\" />\n <span className=\"line-clamp-2 text-xs leading-tight\">Attachment unavailable</span>\n </span>\n )\n }\n return (\n <span\n aria-disabled=\"true\"\n className=\"inline-flex items-center gap-1 rounded-md border border-border bg-muted px-2 py-0.5 text-xs text-muted-foreground\"\n >\n <WarningGlyph className=\"h-3 w-3 shrink-0\" />\n Attachment unavailable\n </span>\n )\n}\n\ninterface AttachmentPartProps {\n part: ChatAttachmentPart\n resolveFileUrl: (part: ChatAttachmentPart) => string\n fetchFile?: (url: string) => Promise<Response>\n}\n\nfunction AttachmentThumbnail({ part, resolveFileUrl, fetchFile }: AttachmentPartProps) {\n const url = resolveFileUrl(part)\n const displayName = attachmentDisplayName(part)\n const [result, setResult] = useState<AttachmentFileResult | null>(null)\n\n // Images fetch eagerly on mount — unlike the chip, which fetches only on\n // click — so the transcript shows a real thumbnail rather than a\n // placeholder icon. Skipped entirely for a part with no usable display\n // name — there is nothing meaningful to label the fetched bytes with.\n useEffect(() => {\n if (!displayName) return\n let cancelled = false\n setResult(null)\n loadAttachmentFile(url, fetchFile).then((next) => {\n if (!cancelled) setResult(next)\n })\n return () => {\n cancelled = true\n }\n }, [url, fetchFile, displayName])\n\n const objectUrl = useAttachmentObjectUrl(result?.ok ? result.blob : undefined)\n\n const handleClick = useCallback(() => {\n if (!objectUrl) return\n window.open(objectUrl, '_blank', 'noopener')\n }, [objectUrl])\n\n if (!displayName) {\n return <AttachmentUnavailable shape=\"thumbnail\" />\n }\n if (!result) {\n // The shimmer stays hidden and UNROLED. Giving each placeholder a role\n // would put a dozen of them in the accessibility tree of one transcript\n // message and, worse, make a not-yet-loaded attachment answer to the same\n // role as a loaded one — the announcement belongs to the group instead.\n return <span aria-hidden=\"true\" className=\"inline-block h-16 w-16 shrink-0 animate-pulse rounded-md bg-muted\" />\n }\n if (!result.ok || !objectUrl) {\n return <AttachmentThumbnailError name={displayName} />\n }\n\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n aria-label={`Open ${displayName}`}\n className=\"h-16 w-16 shrink-0 overflow-hidden rounded-md border border-border\"\n >\n <img src={objectUrl} alt={displayName} className=\"h-16 w-16 object-cover\" />\n </button>\n )\n}\n\n// ── chip (files) ─────────────────────────────────────────────────────────\n\nfunction AttachmentChip({ part, resolveFileUrl, fetchFile }: AttachmentPartProps) {\n const displayName = attachmentDisplayName(part)\n const [status, setStatus] = useState<'idle' | 'loading' | 'error'>('idle')\n const [errorMessage, setErrorMessage] = useState<string | null>(null)\n\n // Files fetch ONLY on click — an unopened chip never issues a network\n // request, unlike an eagerly-fetched thumbnail.\n const handleClick = useCallback(() => {\n if (!displayName || status === 'loading') return\n setStatus('loading')\n setErrorMessage(null)\n const url = resolveFileUrl(part)\n void loadAttachmentFile(url, fetchFile).then((result) => {\n if (!result.ok) {\n setStatus('error')\n setErrorMessage(result.message)\n return\n }\n const download = triggerAttachmentDownload(displayName, result.blob)\n if (!download.ok) {\n setStatus('error')\n setErrorMessage(download.message)\n return\n }\n setStatus('idle')\n })\n }, [displayName, status, resolveFileUrl, part, fetchFile])\n\n if (!displayName) {\n return <AttachmentUnavailable shape=\"chip\" />\n }\n\n const Icon = status === 'error' ? WarningGlyph : iconForMediaType(part.mediaType)\n const className = [\n 'inline-flex items-center gap-1 rounded-md border px-2 py-0.5 text-xs',\n status === 'error'\n ? 'border-destructive/40 bg-destructive/10 text-destructive'\n : 'border-border bg-secondary text-muted-foreground',\n ].join(' ')\n\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n title={status === 'error' ? errorMessage ?? undefined : undefined}\n className={className}\n >\n <Icon className=\"h-3 w-3 shrink-0\" />\n {displayName}\n {typeof part.size === 'number' && <span className=\"text-muted-foreground/70\">· {formatDisplayBytes(part.size)}</span>}\n </button>\n )\n}\n\n// ── row ──────────────────────────────────────────────────────────────────\n\nexport interface MessageAttachmentsProps {\n parts: ChatAttachmentPart[]\n /** URL serving the attachment's RAW bytes. */\n resolveFileUrl: (part: ChatAttachmentPart) => string\n /** Row alignment — a user-bubble attachment row is right-aligned by\n * default; pass `\"start\"` for an assistant-turn attachment, which sits\n * inline with the rest of the transcript. */\n justify?: 'start' | 'end'\n /** Override the fetch used to load an attachment's bytes. Default:\n * `fetch(url, { credentials: 'same-origin' })`. */\n fetchFile?: (url: string) => Promise<Response>\n}\n\n/** Renders a message's attachment parts as a row of image thumbnails and file\n * chips. `null` when there are none, so callers can render unconditionally\n * without an extra length check. */\nexport function MessageAttachments({ parts, resolveFileUrl, justify = 'end', fetchFile }: MessageAttachmentsProps): ReactNode {\n if (parts.length === 0) return null\n return (\n <div className={`flex flex-wrap gap-1.5 ${justify === 'start' ? 'justify-start' : 'justify-end'}`}>\n {parts.map((part) =>\n part.type === 'image' ? (\n <AttachmentThumbnail key={`${part.path}:${part.name}`} part={part} resolveFileUrl={resolveFileUrl} fetchFile={fetchFile} />\n ) : (\n <AttachmentChip key={`${part.path}:${part.name}`} part={part} resolveFileUrl={resolveFileUrl} fetchFile={fetchFile} />\n ),\n )}\n </div>\n )\n}\n","/**\n * Client-side chat-stream consumption — the NDJSON parse loop every agent\n * app's chat UI hand-rolls (and breaks). Normalizes the three line shapes the\n * agent-app chat routes emit:\n *\n * {kind:'event', event:{type:'text'|'reasoning'|'tool_call'|'usage'|'notice'|'error', ...}}\n * {kind:'tool_result', toolCallId, toolName, label, outcome}\n * {type:'turn'|'metadata'|'error'|'turn_status', ...} (route-level)\n * {type:'interaction', data:{request}} (sidecar ask)\n *\n * Replayed lines carry an extra `seq` — transparently ignored. Works for\n * router-backed and sandbox-backed chats alike: anything producing these\n * lines (live pump, queued follow, resume replay) feeds the same callbacks.\n */\n\nimport {\n interactionFromWireRequest,\n parseInteractionCancel,\n parseInteractionRequest,\n type ChatInteraction,\n type InteractionCancelData,\n} from './chat-interactions'\nimport {\n parsePlanSubmittedEvent,\n persistedPartToPlan,\n type ChatPlan,\n} from '../plans/index'\n\n// The `/chat-routes` wire contract, re-exported for turn-body construction —\n// `./chat-routes/wire` and `./chat-routes/file-index`'s response types are\n// import-free and browser-safe by design.\nexport {\n chatTurnRequestInit,\n type ChatTurnFilePartInput,\n type ChatTurnPartInput,\n type ChatTurnRequestPayload,\n type ProducerTextEvent,\n type ProducerReasoningEvent,\n type ProducerToolCallEvent,\n type ProducerToolResultEvent,\n type ProducerUsageEvent,\n type ProducerNoticeEvent,\n type ProducerErrorEvent,\n type ProducerPassthroughEventType,\n type ProducerPassthroughEvent,\n type ProducerWireEvent,\n type FileMention,\n fileMentionsToParts,\n buildMentionPromptBlock,\n mediaTypeForMentionPath,\n mentionKindForPath,\n type ChatAttachmentKind,\n type ChatAttachmentInput,\n DISPATCH_REQUEST_MAX_BYTES,\n DISPATCH_STRUCTURAL_RESERVE_BYTES,\n DISPATCH_MAX_PARTS,\n DISPATCH_MAX_MEDIA_PARTS,\n base64WireLen,\n} from '../chat-routes/wire'\nexport {\n type FileIndexResponse,\n type FileIndexReadyResponse,\n type FileIndexWarmingResponse,\n} from '../chat-routes/file-index'\n\n/** Define the structure for a chat tool call including optional ID, name, and arguments object */\nexport interface ChatStreamToolCall {\n toolCallId?: string\n toolName: string\n args: Record<string, unknown>\n}\n\n/** Describe the result of a chat stream tool including its outcome and optional metadata fields */\nexport interface ChatStreamToolResult {\n toolCallId?: string\n toolName?: string\n label?: string\n outcome: { ok: boolean; result?: unknown; code?: string; message?: string }\n}\n\n/** Define callbacks to handle events and data during a chat streaming session */\nexport interface ChatStreamCallbacks {\n onTurnId?: (turnId: string) => void\n onText?: (delta: string) => void\n onReasoning?: (delta: string) => void\n onToolCall?: (call: ChatStreamToolCall) => void\n onToolResult?: (result: ChatStreamToolResult) => void\n onUsage?: (usage: { promptTokens: number; completionTokens: number }) => void\n onNotice?: (notice: { id: string; noticeKind: 'warning' | 'auto-declined'; text: string }) => void\n onMetadata?: (data: Record<string, unknown>) => void\n /** Structured detail from a loop-level error event. Fired alongside the\n * legacy string-only `onErrorEvent` callback. */\n onErrorEventDetail?: (detail: {\n message: string\n code?: string\n details?: Record<string, unknown>\n }) => void\n /** A loop-level error event (the turn failed server-side). Optional, but the\n * error never vanishes: when omitted, the message is synthesized into the\n * transcript via `onText` (rendered by ChatMessages as a text segment) and\n * logged with `console.error`. */\n onErrorEvent?: (message: string) => void\n /** A sidecar interaction ask (kind: \"question\"/\"plan\"/…). The run is BLOCKED\n * in the broker until the user answers; a pending ask is \"waiting on the\n * user\", not \"model working\". Optional — a consumer that doesn't wire it\n * parses the same stream unchanged. */\n onInteraction?: (interaction: ChatInteraction) => void\n /** A terminal withdrawal/timeout for a previously emitted interaction. */\n onInteractionCancel?: (cancel: InteractionCancelData) => void\n /** A durable-plan snapshot from any plan lifecycle event. */\n onPlan?: (plan: ChatPlan) => void\n}\n\n/** Represent the result of consuming a chat stream including turn ID and content reception status */\nexport interface ConsumeChatStreamResult {\n turnId: string | null\n /** True when any text/reasoning/tool activity was received. */\n receivedContent: boolean\n}\n\n/** Parse one NDJSON line into the callbacks. Exposed for tests. */\nexport function dispatchChatStreamLine(line: string, cb: ChatStreamCallbacks): {\n turnId?: string\n receivedContent: boolean\n} {\n let receivedContent = false\n let turnId: string | undefined\n if (!line.trim()) return { receivedContent }\n let parsed: Record<string, unknown>\n try {\n parsed = JSON.parse(line) as Record<string, unknown>\n } catch {\n return { receivedContent } // tolerate a torn line\n }\n\n if (parsed.kind === 'tool_result') {\n cb.onToolResult?.({\n toolCallId: parsed.toolCallId as string | undefined,\n toolName: parsed.toolName as string | undefined,\n label: parsed.label as string | undefined,\n outcome: (parsed.outcome ?? parsed.result) as ChatStreamToolResult['outcome'],\n })\n return { receivedContent: true }\n }\n\n const evt = (parsed.kind === 'event' ? parsed.event : parsed) as Record<string, unknown>\n if (!evt || typeof evt !== 'object') return { receivedContent }\n\n switch (evt.type) {\n case 'turn':\n if (typeof evt.turnId === 'string') turnId = evt.turnId\n break\n case 'text':\n if (typeof evt.text === 'string') {\n cb.onText?.(evt.text)\n receivedContent = true\n }\n break\n case 'reasoning':\n if (typeof evt.text === 'string') {\n cb.onReasoning?.(evt.text)\n receivedContent = true\n }\n break\n case 'tool_call': {\n const call = (evt.call ?? evt) as Record<string, unknown>\n cb.onToolCall?.({\n toolCallId: (call.toolCallId ?? call.id) as string | undefined,\n toolName: String(call.toolName ?? call.name ?? 'unknown'),\n args: (call.args ?? {}) as Record<string, unknown>,\n })\n receivedContent = true\n break\n }\n case 'tool_result':\n cb.onToolResult?.({\n toolCallId: evt.toolCallId as string | undefined,\n toolName: evt.toolName as string | undefined,\n label: evt.label as string | undefined,\n outcome: (evt.outcome ?? evt.result) as ChatStreamToolResult['outcome'],\n })\n receivedContent = true\n break\n case 'usage': {\n const u = evt.usage as { promptTokens?: number; completionTokens?: number } | undefined\n if (u) cb.onUsage?.({ promptTokens: u.promptTokens ?? 0, completionTokens: u.completionTokens ?? 0 })\n break\n }\n case 'notice': {\n if (\n typeof evt.id === 'string' &&\n (evt.noticeKind === 'warning' || evt.noticeKind === 'auto-declined') &&\n typeof evt.text === 'string'\n ) {\n cb.onNotice?.({ id: evt.id, noticeKind: evt.noticeKind, text: evt.text })\n receivedContent = true\n }\n break\n }\n case 'metadata':\n cb.onMetadata?.((evt.data ?? {}) as Record<string, unknown>)\n break\n case 'interaction': {\n // The run is now BLOCKED in the sidecar broker until this ask is\n // answered, withdrawn, or times out. Validate the shape and surface the\n // parsed ChatInteraction; a malformed ask is logged and skipped rather\n // than half-surfaced.\n const parsed = parseInteractionRequest(evt.data as Record<string, unknown> | undefined)\n if (parsed.succeeded) {\n cb.onInteraction?.(interactionFromWireRequest(parsed.value))\n receivedContent = true\n } else {\n console.error('[chat-stream] dropping malformed interaction line:', parsed.error)\n }\n break\n }\n case 'interaction.cancel': {\n const cancelled = parseInteractionCancel(evt.data as Record<string, unknown> | undefined)\n if (cancelled.succeeded) {\n cb.onInteractionCancel?.(cancelled.value)\n receivedContent = true\n } else {\n console.error('[chat-stream] dropping malformed interaction.cancel line:', cancelled.error)\n }\n break\n }\n case 'error': {\n // The sandbox lane sends the reason as `{ type: 'error', data: { message } }`\n // (mirrored by `session.run.failed`); older/edge lanes use a top-level\n // `details`/`error`. Read `data.message` FIRST so a real failure surfaces\n // to the operator instead of the useless \"Unknown stream error\".\n const data = evt.data as {\n message?: string\n code?: string\n details?: Record<string, unknown>\n } | undefined\n const message = String(data?.message ?? evt.details ?? evt.error ?? 'Unknown stream error')\n cb.onErrorEventDetail?.({\n message,\n ...(typeof data?.code === 'string' ? { code: data.code } : {}),\n ...(data?.details && typeof data.details === 'object' ? { details: data.details } : {}),\n })\n if (cb.onErrorEvent) {\n cb.onErrorEvent(message)\n } else {\n // Fail loud when the consumer wired no onErrorEvent: a turn that fails\n // server-side must not end as a silent empty answer. Synthesize the\n // error into the transcript through the text channel (it lands as a\n // text segment ChatMessages renders) and log it — the app that forgot\n // the callback still shows the user an error row.\n console.error('[chat-stream] unhandled stream error event:', message)\n cb.onText?.(`\\n\\nThe agent hit an error and this turn stopped: ${message}`)\n receivedContent = true\n }\n break\n }\n default: {\n if (typeof evt.type === 'string' && evt.type.startsWith('plan.')) {\n const submitted = parsePlanSubmittedEvent(evt)\n const planRecord = (\n (evt.data as Record<string, unknown> | undefined)?.plan ??\n (evt.properties as Record<string, unknown> | undefined)?.plan\n ) as Record<string, unknown> | undefined\n const plan = submitted.succeeded\n ? submitted.value\n : planRecord ? persistedPartToPlan({ type: 'plan', ...planRecord }) : null\n if (plan) {\n cb.onPlan?.(plan)\n receivedContent = true\n } else {\n console.error('[chat-stream] dropping malformed durable plan line:', evt.type)\n }\n }\n break // turn_status and unknown line types are non-content\n }\n }\n return { turnId, receivedContent }\n}\n\n/** Drain one NDJSON body into the callbacks. Throws on transport failure\n * (caller decides whether to resume). */\nexport async function consumeChatStream(\n body: ReadableStream<Uint8Array>,\n cb: ChatStreamCallbacks,\n): Promise<ConsumeChatStreamResult> {\n const reader = body.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n let turnId: string | null = null\n let receivedContent = false\n\n const handle = (line: string) => {\n const r = dispatchChatStreamLine(line, cb)\n if (r.turnId) {\n turnId = r.turnId\n cb.onTurnId?.(r.turnId)\n }\n if (r.receivedContent) receivedContent = true\n }\n\n for (;;) {\n const { done, value } = await reader.read()\n if (done) {\n if (buffer.trim()) handle(buffer)\n break\n }\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? ''\n for (const line of lines) handle(line)\n }\n return { turnId, receivedContent }\n}\n\n/** Define options for managing and resuming streaming chat interactions with callbacks */\nexport interface StreamChatOptions {\n /** Start the turn (POST the chat request); must return a streaming Response. */\n start: () => Promise<Response>\n /** Re-attach to a turn after a transport drop (GET the resume route). */\n resume?: (turnId: string, fromSeq: number) => Promise<Response>\n callbacks: ChatStreamCallbacks\n /** Called before a resume replays from 0 so the UI can reset accumulated\n * turn state (text, reasoning, tool chips). */\n onResetForResume?: () => void\n}\n\n/**\n * Run one chat turn with automatic single-shot resume: if the transport drops\n * mid-turn and the server announced a turnId, reset and replay the buffered\n * turn. Server-side the turn keeps running either way (queued runner).\n */\nexport async function streamChatTurn(opts: StreamChatOptions): Promise<ConsumeChatStreamResult> {\n const res = await opts.start()\n if (!res.ok || !res.body) {\n const err = await res.json().catch(() => ({ error: `HTTP ${res.status}` })) as { error?: string }\n throw new Error(err.error ?? `HTTP ${res.status}`)\n }\n let turnId: string | null = null\n const cb: ChatStreamCallbacks = {\n ...opts.callbacks,\n onTurnId: (id) => {\n turnId = id\n opts.callbacks.onTurnId?.(id)\n },\n }\n try {\n return await consumeChatStream(res.body, cb)\n } catch (transportErr) {\n if (!turnId || !opts.resume) throw transportErr\n opts.onResetForResume?.()\n const resumed = await opts.resume(turnId, 0)\n if (!resumed.ok || !resumed.body) throw transportErr\n return await consumeChatStream(resumed.body, cb)\n }\n}\n","/**\n * ChatComposer — the shared message input every agent app used to hand-roll:\n * an auto-resizing textarea (Enter sends, Shift+Enter inserts a newline), an\n * opt-in attach + drag-and-drop + clipboard-paste surface with pending-file\n * chips, a streaming Stop/Send toggle, a slot for inline controls (model\n * picker, reasoning effort), and a Cmd/Ctrl+L focus shortcut.\n *\n * Files arrive by three routes — the picker dialog, a drop, and a paste — and\n * all three funnel through `accept` (`./composer-file-accept`) before they\n * reach `onAttach`, so a type the picker will not offer cannot get in by\n * another route. What `accept` refuses goes to `onRejectFiles` with a reason;\n * without that prop a refusal is silent, which is what the native picker also\n * does. Size and count limits stay the host's job — `useComposerAttachments`\n * owns them, because they depend on what is already staged.\n *\n * A REJECTED send never destroys the draft. The input clears optimistically —\n * the composer stays editable while a turn streams precisely so the next\n * message can be typed against a live answer, and holding the sent text in the\n * box until the server confirms would put the clear on a collision course with\n * that typing. So the clear happens immediately and the draft is held until the\n * send is known to have landed: a handler that throws, rejects, or returns\n * `{ ok: false }` puts the exact bytes back with the caret where it was, names\n * the reason, and reports `onSendFailed` so the host can restore the\n * attachments it consumed. If the user has already typed a replacement, the\n * unsent text is shown in the notice with its own Retry instead of overwriting\n * what they typed — neither draft is ever destroyed.\n *\n * Styling contract matches the rest of `web-react`: Tailwind over the shared\n * design tokens (`bg-card`, `border-border`, `text-foreground`, `bg-primary`, …)\n * and inline-SVG glyphs. It defines NO `--chat-*` / `--brand-*` custom\n * properties, so it themes correctly in any shell that provides the standard\n * tokens — the input renders on-palette instead of collapsing to unstyled\n * fallbacks when a host hasn't defined a private chat-token set.\n */\n\nimport {\n useCallback,\n useEffect,\n useMemo,\n useId,\n useRef,\n useState,\n type ChangeEvent,\n type ClipboardEvent,\n type DragEvent,\n type KeyboardEvent,\n type ReactNode,\n} from 'react'\n\nimport {\n filterAcceptedFiles,\n renamePastedImages,\n type ComposerFileRejection,\n} from './composer-file-accept'\nimport { filterCommandPaletteItems, type CommandPaletteItem } from '../session-shell/index'\nimport { OVERLAY_SHADOW, POPOVER_OPTION_FOCUS, PopoverSurface } from './controls'\nimport { formatDictationElapsed, useDictation, type DictationAudio } from './use-dictation'\n\n// ── glyphs (no icon-library dependency) ───────────────────────────────────\n\n/** The focus-shortcut hint names the platform's modifier: Cmd on Apple,\n * Ctrl everywhere else (the handler itself listens for both). SSR-safe —\n * defaults to Ctrl when there's no navigator to ask. */\nconst IS_APPLE_PLATFORM =\n typeof navigator !== 'undefined' && /Mac|iPhone|iPad|iPod/i.test(navigator.platform)\n\nfunction SendGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M22 2 11 13M22 2l-7 20-4-9-9-4 20-7z\" />\n </svg>\n )\n}\n\nfunction StopGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden>\n <rect x=\"6\" y=\"6\" width=\"12\" height=\"12\" rx=\"2\" />\n </svg>\n )\n}\n\nfunction ArrowUpGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M12 19V5M5 12l7-7 7 7\" />\n </svg>\n )\n}\n\nfunction PaperclipGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48\" />\n </svg>\n )\n}\n\nfunction FolderGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M4 20h16a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.93a2 2 0 0 1-1.66-.9l-.82-1.2A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13c0 1.1.9 2 2 2Z\" />\n <path d=\"M12 10v6m-3-3h6\" />\n </svg>\n )\n}\n\nfunction CloseGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" aria-hidden>\n <path d=\"M18 6 6 18M6 6l12 12\" />\n </svg>\n )\n}\n\nfunction RetryGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\" />\n <path d=\"M3 3v5h5\" />\n </svg>\n )\n}\n\nfunction UploadGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M17 8l-5-5-5 5M12 3v12\" />\n </svg>\n )\n}\n\nfunction MicGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <rect x=\"9\" y=\"2\" width=\"6\" height=\"12\" rx=\"3\" />\n <path d=\"M5 10v1a7 7 0 0 0 14 0v-1M12 18v4\" />\n </svg>\n )\n}\n\n// ── component ──────────────────────────────────────────────────────────────\n\n/** Prompt-part descriptor an uploaded file carries (the upload route's\n * `UploadedChatFile.part`), echoed back in the turn body on send. Mirrors\n * `/chat-routes`' wire shape structurally — no server import here. */\nexport interface ComposerFilePart {\n type: 'image' | 'file'\n filename?: string\n mediaType?: string\n url?: string\n path?: string\n content?: string\n}\n\nexport interface ComposerFile {\n id: string\n name: string\n size?: number\n kind: 'file' | 'folder'\n /** Number of files inside, for a folder chip. */\n fileCount?: number\n status: 'pending' | 'uploading' | 'ready' | 'error'\n /** Uploaded part descriptor; set once the upload route returns. Only\n * `status: 'ready'` files with a part travel on a parts-aware send. */\n part?: ComposerFilePart\n /** Object URL for an image thumbnail on the chip. The host owns the URL's\n * whole life — `URL.createObjectURL` when the file is staged,\n * `URL.revokeObjectURL` when it leaves — and the composer only reads it.\n * `useComposerAttachments` already does both. */\n previewUrl?: string\n /** Why this file failed, shown on the chip while `status: 'error'`. Without\n * it an error chip is red and mute, which tells the user nothing. */\n errorMessage?: string\n}\n\n/** A piece of context the agent will see beside the next message — an open\n * file, a selected record, a pinned document. Rendered as its own chip row,\n * separate from staged attachments: context is what the turn already carries,\n * an attachment is what the user is adding to it. */\nexport interface ComposerContextItem {\n id: string\n label: string\n icon?: ReactNode\n /** Omit for a chip the user cannot dismiss. */\n onRemove?: () => void\n}\n\n/** A send the host refused. `error` is shown verbatim in the composer's notice;\n * omit it for the generic copy. */\nexport interface ComposerSendRejected {\n ok: false\n error?: string\n}\n\n/**\n * What a send handler reports back. `void` — what every handler returned before\n * this existed — reads as accepted, so wiring stays unchanged; a thrown error, a\n * rejected promise, or `{ ok: false }` is the rejection that restores the draft.\n * A handler that resolves only when the whole turn finishes still reports\n * correctly: the input already cleared on dispatch, so the answer only decides\n * whether the draft comes back.\n */\nexport type ComposerSendOutcome = void | { ok: true } | ComposerSendRejected\nexport type ComposerSendResult = ComposerSendOutcome | Promise<ComposerSendOutcome>\n\n/**\n * A send handler, typed as a UNION with the legacy `=> void` signature rather\n * than as `(…) => ComposerSendResult` alone.\n *\n * TypeScript's return-type-`void` rule accepts a function returning ANYTHING\n * where a `=> void` is expected, and that rule fires only when the target's\n * return type is exactly `void` — not when it is a union that contains `void`.\n * So narrowing this prop to `ComposerSendResult` would reject handler shapes\n * that compiled against the shipped `onSend?: (message: string) => void`:\n * `onSend={(m) => rows.push(m)}` (returns `number`) and\n * `onSend={(m) => append({ role: 'user', content: m })}` (an ai-sdk append\n * returns `Promise<string | null | undefined>`) both stop compiling, on a\n * package whose pinned consumers must never need a source edit to take a minor.\n *\n * The union keeps both: a legacy handler lands on the first member, and a\n * handler that reports an outcome lands on the second. A call through it\n * resolves to `void | ComposerSendResult`, which IS `ComposerSendResult`, so\n * the composer reads the outcome exactly as before.\n */\nexport type ComposerSendHandler =\n | ((message: string) => void)\n | ((message: string) => ComposerSendResult)\n\n/** @see ComposerSendHandler — the parts-aware arity, same union for the same reason. */\nexport type ComposerSendPartsHandler =\n | ((message: string, parts: ComposerFilePart[]) => void)\n | ((message: string, parts: ComposerFilePart[]) => ComposerSendResult)\n\n/** The rejected send, handed to `onSendFailed` so the host can undo whatever it\n * cleared optimistically — most importantly the staged attachments, which the\n * composer does not own (`pendingFiles` is a prop). */\nexport interface ComposerSendFailure {\n /** The reason as the composer renders it. */\n message: string\n /** The user's exact draft, untrimmed. */\n text: string\n /** The parts the rejected send carried. */\n parts: ComposerFilePart[]\n /** Whatever the handler threw / rejected with, or the `{ ok: false }` value. */\n error: unknown\n /** True when the draft was put back in the textarea (the box was empty).\n * False means the user had typed a replacement, so the unsent text is held in\n * the notice instead. */\n restored: boolean\n}\n\n/**\n * One `/` command the composer offers. Typing `/` at position 0 opens the\n * command menu; the rest of the token filters it (the same prefix > substring\n * > token-order ranking as the command palette). Picking a command CLEARS the\n * token from the draft and calls `run` — what the command does (a route, a\n * dialog, a draft transformation) is the product's business.\n */\nexport interface SlashCommand {\n /** Command name without the leading slash: `model`, `clear`. */\n name: string\n /** One line of what it does, rendered beside the name. */\n description: string\n run: () => void\n}\n\nexport interface ChatComposerProps {\n /** Send the trimmed, non-empty message. Attached files travel separately via\n * `onAttach` + `pendingFiles` (the host consumes and clears them on send).\n * Optional when `onSendParts` is wired.\n *\n * Report a refused send by throwing, rejecting, or returning `{ ok: false }`\n * — the composer restores the draft rather than losing it. */\n onSend?: ComposerSendHandler\n /** Parts-aware send: receives the trimmed message plus the `part`\n * descriptors of every `ready` pending file. Takes precedence over\n * `onSend`; enables file-only sends (empty text, ≥1 ready part).\n *\n * Same rejection contract as `onSend`. */\n onSendParts?: ComposerSendPartsHandler\n /** Notified when a send is rejected, after the composer has restored what it\n * owns. The host uses it to put back the `pendingFiles` it consumed. */\n onSendFailed?: (failure: ComposerSendFailure) => void\n /** Notice copy when the handler names no reason of its own. */\n sendFailureMessage?: string\n /** Stop the in-flight turn; shown in place of Send while `isStreaming`. */\n onCancel?: () => void\n isStreaming?: boolean\n /** Block input + send (e.g. while restoring). Distinct from `isStreaming`,\n * which keeps the textarea editable so the next turn can be composed. */\n disabled?: boolean\n placeholder?: string\n\n /** Controlled value. Omit for self-managed internal state (cleared on send). */\n value?: string\n onValueChange?: (value: string) => void\n /** Initial text in uncontrolled mode; ignored when `value` is provided. */\n initialValue?: string\n\n /** One-shot external prefill: when this becomes a non-null string the\n * composer adopts it as the draft (replacing any current draft), focuses the\n * input with the caret at the end, and reports consumption via\n * `onSeedApplied` so the host can clear its seed state. */\n seed?: string | null\n onSeedApplied?: () => void\n\n /** Inline controls (e.g. `<ModelPicker/>` + `<EffortPicker/>` or\n * `<AgentSessionControls/>`). */\n controls?: ReactNode\n /**\n * Where {@link controls} sit. `inline` (default) puts them on the card's own\n * action row, beside attach and Send — the model a turn will use reads as\n * part of the input rather than as a separate widget floating above it.\n * `above` keeps them outside the card, for a host that wants the input to be\n * nothing but the input.\n */\n controlsPlacement?: 'above' | 'inline'\n\n /** Attachments are opt-in: pass `onAttach` to show the attach button, accept\n * drag-and-drop and clipboard paste onto the input, and render\n * `pendingFiles` chips. */\n onAttach?: (files: FileList) => void\n onAttachFolder?: (files: FileList) => void\n pendingFiles?: ComposerFile[]\n onRemoveFile?: (id: string) => void\n /** Pass it and a chip with `status: 'error'` gains a retry button. */\n onRetryFile?: (id: string) => void\n /**\n * File types the composer takes, in the native `<input accept>` grammar.\n * Enforced on every ingress route — the picker dialog (which the user can\n * override with \"All Files\"), drag-and-drop, and clipboard paste — so a type\n * the picker will not offer cannot arrive by another route. A non-matching\n * file goes to `onRejectFiles` and never reaches `onAttach`. Folder attach is\n * exempt: directory selection has no native accept semantics.\n */\n accept?: string\n /** Called with the files `accept` removed from a pick, drop, or paste, each\n * with a reason. Without it a refusal is silent — the same feedback the\n * native picker gives for a type it will not offer. */\n onRejectFiles?: (rejections: ComposerFileRejection[]) => void\n dropTitle?: string\n dropDescription?: string\n\n /** Context the agent will see beside the next message, as its own chip row\n * above the input. */\n contextItems?: ReadonlyArray<ComposerContextItem>\n\n /**\n * Let a staged file stand in for message text, so the send control stays live\n * while an upload is in flight instead of going dead with nothing to explain\n * it. Default false, where an empty message needs a `ready` file.\n *\n * It does NOT make an unfinished file sendable. A turn whose only content is a\n * file that is still uploading or has failed never reaches the send handler —\n * it would arrive empty and the attachment would be lost. The composer\n * refuses it and names the reason in its notice\n * ({@link attachmentsNotReadyMessage}). So the flag decides whether the\n * control is live, and the composer keeps the integrity gate rather than\n * leaving each host to re-derive it.\n */\n canSubmitAttachmentsOnly?: boolean\n /** Notice copy when a send is refused because no staged file is ready yet.\n * Defaults to wording chosen from whether a file failed or is still\n * uploading. */\n attachmentsNotReadyMessage?: string\n /**\n * Let Enter and Send keep firing while `isStreaming`, for a surface that\n * queues the next turn rather than blocking on the current one. Default\n * false. The button still flips to Stop while a turn streams, so this opens\n * the keyboard path, not a second button.\n */\n canSubmitWhileBusy?: boolean\n\n /** Focus the input on mount — for a surface whose whole job is the input\n * (an entry/hero composer), never for one docked under a transcript. */\n autoFocus?: boolean\n /** Rows the input shows before it grows. Default 2. */\n minRows?: number\n /** Pixel height the input grows to before it scrolls. Default 168. */\n maxHeight?: number\n /** Content between the controls slot and Send — a token meter, a cost, a\n * status line. It sits outside the controls slot and never shrinks, so a\n * wrapping picker set cannot push it away. */\n trailing?: ReactNode\n /** `/` commands offered when the draft is exactly a leading slash token.\n * Omit (or pass []) and `/` types as ordinary text. */\n slashCommands?: SlashCommand[]\n /** Dictation is opt-in: pass `onDictate` and the action row gains a mic\n * button (browsers without `MediaRecorder`/`getUserMedia` render none).\n * Click starts the capture; the button flips to a stop control with the\n * running elapsed seconds; stop hands the recorded audio blob here. The\n * composer owns capture only — turning the audio into text (e.g. the\n * Whisper provider from `sequences-react`) is the host's. */\n onDictate?: (audio: DictationAudio) => void\n /** Capture failures (a denied mic prompt, no device), after the composer has\n * shown its own dismissible notice. For hosts that log or track. */\n onDictateError?: (message: string) => void\n\n /** Cmd/Ctrl+L focuses the input and shows the hint. Default true. */\n focusShortcut?: boolean\n /** Float the card on a soft two-layer foreground-tinted shadow (opt-in).\n * Elevation only — radius, ring, and control layout are unchanged. */\n floating?: boolean\n /** Send button label. Default \"Send\". */\n sendLabel?: string\n /** Send control shape. `pill` (default) is the labeled button; `icon` is the\n * 34px circular inverted arrow (streaming: circular outlined stop) — the\n * grammar sandbox-ui's legacy AgentComposer used and the current agent-app\n * canon for new surfaces. */\n sendVariant?: 'pill' | 'icon'\n className?: string\n}\n\nconst DEFAULT_MAX_HEIGHT = 168\n\n/** The input's own `leading-6` line box and its `py-1` padding, in pixels. The\n * `minRows` floor is computed from them so the CSS floor and the `rows`\n * attribute cannot drift apart at a row count other than the default. */\nconst LINE_HEIGHT = 24\nconst TEXTAREA_PADDING_Y = 8\n\nconst DEFAULT_SEND_FAILURE = \"Message not sent. Your draft is still here — try again.\"\n\n/** A rejection the handler reported by value rather than by throwing. */\nfunction isRejectedOutcome(outcome: ComposerSendOutcome): outcome is ComposerSendRejected {\n return typeof outcome === 'object' && outcome !== null && outcome.ok === false\n}\n\nfunction isPromise(value: ComposerSendResult): value is Promise<ComposerSendOutcome> {\n return typeof (value as Promise<ComposerSendOutcome> | undefined)?.then === 'function'\n}\n\n/** The reason to show. A rejection's own `error` string wins; then an Error's\n * message; else the caller's copy — never an empty notice. */\nfunction sendFailureText(error: unknown, fallback: string): string {\n if (typeof error === 'object' && error !== null && 'ok' in error) {\n const named = (error as ComposerSendRejected).error\n if (typeof named === 'string' && named.trim() !== '') return named\n return fallback\n }\n if (typeof error === 'string' && error.trim() !== '') return error\n if (error instanceof Error && error.message.trim() !== '') return error.message\n return fallback\n}\n\ninterface FailedSend {\n message: string\n /** The user's exact draft, untrimmed — what a restore puts back. */\n text: string\n /** The trimmed form the handler was called with, so Retry sends the same\n * bytes the rejected attempt did. */\n trimmed: string\n parts: ComposerFilePart[]\n restored: boolean\n}\n\nexport function ChatComposer({\n onSend,\n onSendParts,\n onSendFailed,\n sendFailureMessage = DEFAULT_SEND_FAILURE,\n onCancel,\n isStreaming = false,\n disabled = false,\n placeholder = 'Message the agent…',\n value,\n onValueChange,\n initialValue,\n seed,\n onSeedApplied,\n controls,\n controlsPlacement = 'inline',\n onAttach,\n onAttachFolder,\n pendingFiles = [],\n onRemoveFile,\n onRetryFile,\n accept,\n onRejectFiles,\n dropTitle = 'Drop files to add context',\n dropDescription = 'They attach to your next message.',\n contextItems = [],\n canSubmitAttachmentsOnly = false,\n attachmentsNotReadyMessage,\n canSubmitWhileBusy = false,\n autoFocus,\n minRows = 2,\n maxHeight = DEFAULT_MAX_HEIGHT,\n trailing,\n slashCommands,\n onDictate,\n onDictateError,\n\n focusShortcut = true,\n floating = false,\n sendLabel = 'Send',\n sendVariant = 'pill',\n className,\n}: ChatComposerProps) {\n const isControlled = value !== undefined\n const [internal, setInternal] = useState(initialValue ?? '')\n const text = isControlled ? value : internal\n // A send outcome arrives after the render that dispatched it, so the restore\n // decision must read the LIVE draft, not the one captured in that closure.\n const textRef = useRef(text)\n textRef.current = text\n\n const textareaRef = useRef<HTMLTextAreaElement>(null)\n const fileInputRef = useRef<HTMLInputElement>(null)\n const folderInputRef = useRef<HTMLInputElement>(null)\n const [dragOver, setDragOver] = useState(false)\n const dragDepth = useRef(0)\n // Counts every clipboard image this composer has renamed, so two pastes of\n // the same bitmap do not both arrive as `image.png`.\n const pastedImageCount = useRef(0)\n\n const setText = useCallback(\n (next: string) => {\n if (!isControlled) setInternal(next)\n onValueChange?.(next)\n },\n [isControlled, onValueChange],\n )\n\n // Dictation: capture only. The hook reports every failure in words; the\n // composer shows them in its own dismissible notice (the same shape as a\n // rejected send) AND forwards them for hosts that log.\n const [dictateError, setDictateError] = useState<string | null>(null)\n const handleDictated = useCallback(\n (audio: DictationAudio) => {\n setDictateError(null)\n onDictate?.(audio)\n },\n [onDictate],\n )\n const handleDictateError = useCallback(\n (message: string) => {\n setDictateError(message)\n onDictateError?.(message)\n },\n [onDictateError],\n )\n const dictation = useDictation({ onDictate: handleDictated, onError: handleDictateError })\n\n // Keep the textarea height in sync with the content for BOTH typed and\n // external (controlled) value changes — one effect covers both paths. It also\n // reruns when the bounds move: `rows` is what `scrollHeight` resolves the\n // measurement against, so a changed `minRows` that did not re-measure would\n // strand the previous inline height.\n useEffect(() => {\n const el = textareaRef.current\n if (!el) return\n el.style.height = 'auto'\n el.style.height = `${Math.min(el.scrollHeight, maxHeight)}px`\n }, [text, maxHeight, minRows])\n\n // Adopt a one-shot seed. Applies only when the `seed` PROP transitions to a\n // new string (host sets it → consumed here → host clears it via\n // onSeedApplied), so an unstable callback identity re-running this effect\n // can never re-apply a still-set seed over the user's typing. Like\n // `initialValue`, the seed is honored ONLY in uncontrolled mode — a\n // controlled host drives its own `value` (which would shadow `setText`), so\n // it seeds by updating that state itself.\n const prevSeedRef = useRef<string | null>(null)\n const pendingCaretRef = useRef<string | null>(null)\n useEffect(() => {\n const prev = prevSeedRef.current\n prevSeedRef.current = seed ?? null\n if (seed == null || seed === prev || isControlled) return\n setText(seed)\n onSeedApplied?.()\n const el = textareaRef.current\n if (el && el.value === seed) {\n // The DOM already shows the seed — setText was a no-op (the user had\n // typed the exact string), so no re-render is coming and the [text]\n // effect below won't fire. Position the caret now instead of leaving a\n // stranded pendingCaretRef.\n el.focus()\n el.setSelectionRange(seed.length, seed.length)\n } else {\n // Defer caret positioning until the seeded value renders (see below).\n pendingCaretRef.current = seed\n }\n }, [seed, setText, onSeedApplied, isControlled])\n\n // Focus + caret-to-end AFTER the seeded value has rendered into the DOM —\n // setSelectionRange in the applying effect would run against the pre-render\n // value and clamp the caret to the old text's length.\n useEffect(() => {\n if (pendingCaretRef.current == null || pendingCaretRef.current !== text)\n return\n pendingCaretRef.current = null\n const el = textareaRef.current\n if (!el) return\n el.focus()\n el.setSelectionRange(text.length, text.length)\n }, [text])\n\n // A restored draft gets the caret back where the user left it — same\n // post-render rule as the seed above, but to the recorded offsets rather than\n // to the end, so a failed send returns the user to the word they were on.\n const restoreCaretRef = useRef<{ text: string; start: number; end: number } | null>(null)\n useEffect(() => {\n const pending = restoreCaretRef.current\n if (!pending || pending.text !== text) return\n restoreCaretRef.current = null\n const el = textareaRef.current\n if (!el) return\n el.focus()\n const start = Math.min(pending.start, text.length)\n const end = Math.min(pending.end, text.length)\n el.setSelectionRange(start, end)\n }, [text])\n\n // Cmd/Ctrl+L focuses the composer from anywhere — the shortcut the hint\n // advertises. Scoped to when the shortcut is enabled and not disabled.\n useEffect(() => {\n if (!focusShortcut || disabled) return\n function onKeyDown(e: globalThis.KeyboardEvent) {\n if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'l') {\n e.preventDefault()\n textareaRef.current?.focus()\n }\n }\n document.addEventListener('keydown', onKeyDown)\n return () => document.removeEventListener('keydown', onKeyDown)\n }, [focusShortcut, disabled])\n\n // A ready file counts as sendable content even without a `part`: store-backed\n // attachments (`useComposerAttachments`) carry no prompt part — their\n // references ride the turn body's `attachments` field — but a file-only\n // message must still be sendable. `canSubmitAttachmentsOnly` widens that to a\n // file of ANY status, so an in-flight upload leaves the control live and the\n // host's handler decides what to do about it.\n const sendableFiles = canSubmitAttachmentsOnly\n ? pendingFiles\n : pendingFiles.filter((f) => f.status === 'ready')\n const hasSendable = text.trim().length > 0 || sendableFiles.length > 0\n // Streaming blocks a send unless the host queues turns. The button still\n // shows Stop while streaming, so `canSubmitWhileBusy` opens Enter, not a\n // second visible control.\n const sendBlockedByStream = isStreaming && !canSubmitWhileBusy\n const canSend = hasSendable && !sendBlockedByStream && !disabled\n\n const [failedSend, setFailedSend] = useState<FailedSend | null>(null)\n\n // The draft comes back only when the box is still empty. If the user typed a\n // replacement while the send was in flight, overwriting it would trade one\n // lost message for another — the unsent text is held in the notice instead,\n // where Retry can send it without touching what they typed.\n const failSend = useCallback(\n (error: unknown, draft: string, trimmed: string, parts: ComposerFilePart[], caret: { start: number; end: number }) => {\n const message = sendFailureText(error, sendFailureMessage)\n const restored = textRef.current === ''\n if (restored) {\n const el = textareaRef.current\n setText(draft)\n if (el && el.value === draft) {\n // A handler that rejected SYNCHRONOUSLY did so inside the same event\n // as the clear, so React collapses clear+restore into no state change\n // at all — no re-render is coming and the effect below will never\n // fire. Place the caret now rather than stranding the pending ref.\n el.focus()\n el.setSelectionRange(Math.min(caret.start, draft.length), Math.min(caret.end, draft.length))\n } else {\n restoreCaretRef.current = { text: draft, start: caret.start, end: caret.end }\n }\n }\n setFailedSend({ message, text: draft, trimmed, parts, restored })\n onSendFailed?.({ message, text: draft, parts, error, restored })\n },\n [onSendFailed, sendFailureMessage, setText],\n )\n\n // Hand the message to the host and watch the outcome. The input has already\n // been cleared by the caller — this only decides whether it comes back.\n const dispatchSend = useCallback(\n (draft: string, trimmed: string, parts: ComposerFilePart[], caret: { start: number; end: number }) => {\n let outcome: ComposerSendResult\n try {\n outcome = onSendParts ? onSendParts(trimmed, parts) : onSend?.(trimmed)\n } catch (error) {\n failSend(error, draft, trimmed, parts, caret)\n return\n }\n if (isPromise(outcome)) {\n void outcome.then(\n (settled) => {\n if (isRejectedOutcome(settled)) failSend(settled, draft, trimmed, parts, caret)\n },\n (error: unknown) => failSend(error, draft, trimmed, parts, caret),\n )\n return\n }\n if (isRejectedOutcome(outcome)) failSend(outcome, draft, trimmed, parts, caret)\n },\n [onSend, onSendParts, failSend],\n )\n\n const send = useCallback(() => {\n const trimmed = text.trim()\n if (sendBlockedByStream || disabled) return\n const readyFiles = pendingFiles.filter((f) => f.status === 'ready')\n const sendable = canSubmitAttachmentsOnly ? pendingFiles : readyFiles\n if (!trimmed && sendable.length === 0) return\n // `canSubmitAttachmentsOnly` keeps the control live while a file is staged,\n // but a turn carrying no text and no file the host can deliver must not go\n // out: it would arrive empty and the attachment would be lost. Refuse it\n // here and say why, rather than dispatching and trusting every host to\n // re-derive the same check.\n if (!trimmed && readyFiles.length === 0) {\n const message =\n attachmentsNotReadyMessage ??\n (pendingFiles.some((f) => f.status === 'error')\n ? 'Retry or remove the failed attachment before sending.'\n : 'Wait for the attachment to finish uploading.')\n setFailedSend({ message, text: '', trimmed: '', parts: [], restored: true })\n return\n }\n // Only a parts-aware send carries parts; `onSend`'s files travel through the\n // host's own `pendingFiles`, so its failure payload names none. Parts come\n // from READY files whatever `canSubmitAttachmentsOnly` says — an unfinished\n // upload has no part to send.\n const parts = onSendParts\n ? readyFiles.filter((f) => f.part).map((f) => f.part as ComposerFilePart)\n : []\n const el = textareaRef.current\n const caret = { start: el?.selectionStart ?? text.length, end: el?.selectionEnd ?? text.length }\n setFailedSend(null)\n setText('')\n textRef.current = ''\n dispatchSend(text, trimmed, parts, caret)\n }, [\n text,\n sendBlockedByStream,\n disabled,\n canSubmitAttachmentsOnly,\n attachmentsNotReadyMessage,\n onSendParts,\n pendingFiles,\n setText,\n dispatchSend,\n ])\n\n // Re-send the message the notice is holding. Reached only when the draft was\n // NOT restored (the restored path leaves the text in the box, where Send is\n // the affordance), so it never competes with the primary control.\n const retryFailedSend = useCallback(() => {\n const failure = failedSend\n if (!failure || sendBlockedByStream || disabled) return\n setFailedSend(null)\n const caret = { start: failure.text.length, end: failure.text.length }\n dispatchSend(failure.text, failure.trimmed, failure.parts, caret)\n }, [failedSend, sendBlockedByStream, disabled, dispatchSend])\n\n // ── '/' commands ─────────────────────────────────────────────────────────\n // The menu exists only while the WHOLE draft is one leading slash token\n // (`/`, `/mod`). The first space ends it — arguments are ordinary text. Esc\n // or an outside click dismisses for the CURRENT token only, so continued\n // typing reopens the menu instead of leaving it permanently suppressed.\n const slashPanelRef = useRef<HTMLDivElement>(null)\n const cardRef = useRef<HTMLDivElement>(null)\n const slashListId = useId()\n const [slashActive, setSlashActive] = useState(0)\n const [slashDismissedFor, setSlashDismissedFor] = useState<string | null>(null)\n const slashToken =\n slashCommands && slashCommands.length > 0 ? /^\\/(\\S*)$/.exec(text)?.[1] : undefined\n const slashOpen = slashToken !== undefined && text !== slashDismissedFor\n const slashItems = useMemo<CommandPaletteItem[]>(\n () =>\n (slashCommands ?? []).map((command) => ({\n id: command.name,\n group: 'Commands',\n label: `/${command.name}`,\n description: command.description,\n keywords: [command.name, command.description],\n })),\n [slashCommands],\n )\n const slashFiltered = useMemo(\n () => (slashToken === undefined ? [] : filterCommandPaletteItems(slashItems, slashToken)),\n [slashItems, slashToken],\n )\n const slashActiveIndex = slashFiltered.length === 0 ? 0 : Math.min(slashActive, slashFiltered.length - 1)\n\n useEffect(() => {\n setSlashActive(0)\n }, [slashToken])\n\n useEffect(() => {\n if (!slashOpen) return\n document\n .getElementById(`${slashListId}-${slashActiveIndex}`)\n ?.scrollIntoView?.({ block: 'nearest' })\n }, [slashOpen, slashActiveIndex, slashListId])\n\n useEffect(() => {\n if (!slashOpen) return\n function onMouseDown(e: MouseEvent) {\n const target = e.target as Node\n if (cardRef.current?.contains(target)) return\n if (slashPanelRef.current?.contains(target)) return\n setSlashDismissedFor(textRef.current)\n }\n document.addEventListener('mousedown', onMouseDown)\n return () => document.removeEventListener('mousedown', onMouseDown)\n }, [slashOpen])\n\n const pickSlash = useCallback(\n (name: string) => {\n const command = slashCommands?.find((c) => c.name === name)\n // The draft IS the slash token (the menu only opens while it is), so the\n // pick consumes it: clear the box, then run.\n setText('')\n setSlashDismissedFor(null)\n command?.run()\n },\n [slashCommands, setText],\n )\n\n const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {\n // Respect IME composition — Enter commits the candidate, it doesn't send.\n if (e.nativeEvent.isComposing) return\n if (slashOpen) {\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n if (slashFiltered.length > 0) setSlashActive((slashActiveIndex + 1) % slashFiltered.length)\n return\n }\n if (e.key === 'ArrowUp') {\n e.preventDefault()\n if (slashFiltered.length > 0)\n setSlashActive((slashActiveIndex - 1 + slashFiltered.length) % slashFiltered.length)\n return\n }\n if ((e.key === 'Enter' && !e.shiftKey) || e.key === 'Tab') {\n const item = slashFiltered[slashActiveIndex]\n if (item) {\n e.preventDefault()\n pickSlash(item.id)\n return\n }\n // No command matched — fall through and let Enter send the raw text.\n }\n if (e.key === 'Escape') {\n e.preventDefault()\n setSlashDismissedFor(text)\n return\n }\n }\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n send()\n }\n }\n\n // Every route a file can arrive by ends here: apply `accept`, report what it\n // removed, and hand `onAttach` only what passed. A batch the filter left\n // untouched is forwarded as the browser's own `FileList`; one it changed is\n // rebuilt, since `onAttach` takes a `FileList` and only a `DataTransfer` can\n // produce one.\n const deliverFiles = useCallback(\n (files: File[], original: FileList) => {\n if (!onAttach || files.length === 0) return\n const { accepted, rejected } = filterAcceptedFiles(files, accept)\n if (rejected.length > 0) onRejectFiles?.(rejected)\n if (accepted.length === 0) return\n const unchanged =\n accepted.length === original.length && accepted.every((file, i) => file === original[i])\n if (unchanged) {\n onAttach(original)\n return\n }\n const transfer = new DataTransfer()\n for (const file of accepted) transfer.items.add(file)\n onAttach(transfer.files)\n },\n [onAttach, onRejectFiles, accept],\n )\n\n const handleFileChange = (e: ChangeEvent<HTMLInputElement>) => {\n // Re-filter: a picker dialog lets the user override `accept` with\n // \"All Files\", so the attribute alone does not hold the gate.\n if (e.target.files?.length) deliverFiles(Array.from(e.target.files), e.target.files)\n e.target.value = ''\n }\n\n const handlePaste = (e: ClipboardEvent<HTMLTextAreaElement>) => {\n if (!onAttach) return\n const clipboardFiles = e.clipboardData?.files\n if (!clipboardFiles || clipboardFiles.length === 0) return\n // Files are the payload, so suppress the default text paste even when every\n // one of them is refused — a rejection must not half-paste stray text.\n e.preventDefault()\n // The staged names go in alongside the count: the queue is the host's and\n // can outlive this mount, so the count alone could hand the next paste a\n // name the queue already holds.\n const { files, nextIndex } = renamePastedImages(\n Array.from(clipboardFiles),\n pastedImageCount.current,\n pendingFiles.map((f) => f.name),\n )\n pastedImageCount.current = nextIndex\n deliverFiles(files, clipboardFiles)\n }\n\n const handleFolderChange = (e: ChangeEvent<HTMLInputElement>) => {\n if (e.target.files?.length) (onAttachFolder ?? onAttach)?.(e.target.files)\n e.target.value = ''\n }\n\n const handleDragEnter = useCallback((e: DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n dragDepth.current++\n if (e.dataTransfer?.types.includes('Files')) setDragOver(true)\n }, [])\n\n const handleDragLeave = useCallback((e: DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n dragDepth.current--\n if (dragDepth.current <= 0) {\n dragDepth.current = 0\n setDragOver(false)\n }\n }, [])\n\n const handleDragOver = useCallback((e: DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n if (e.dataTransfer) e.dataTransfer.dropEffect = 'copy'\n }, [])\n\n const handleDrop = useCallback(\n (e: DragEvent) => {\n e.preventDefault()\n e.stopPropagation()\n dragDepth.current = 0\n setDragOver(false)\n const files = e.dataTransfer?.files\n if (files?.length) deliverFiles(Array.from(files), files)\n },\n [deliverFiles],\n )\n\n const folderChips = pendingFiles.filter((f) => f.kind === 'folder')\n const fileChips = pendingFiles.filter((f) => f.kind !== 'folder')\n // `above` is the only placement that takes controls OUT of the card, so it is\n // the only one matched exactly; everything else falls to inline. That keeps a\n // retired value (this prop used to accept `footer` for the same placement) or a\n // typo rendering the controls somewhere rather than nowhere — dropping them\n // silently is the one outcome with no recovery for the reader.\n const showAbove = controls != null && controlsPlacement === 'above'\n const showInline = controls != null && !showAbove\n\n return (\n <div\n className={`relative ${className ?? ''}`}\n onDragEnter={onAttach ? handleDragEnter : undefined}\n onDragLeave={onAttach ? handleDragLeave : undefined}\n onDragOver={onAttach ? handleDragOver : undefined}\n onDrop={onAttach ? handleDrop : undefined}\n >\n {dragOver && (\n <div className=\"pointer-events-none absolute inset-0 z-10 flex items-center justify-center rounded-2xl border-2 border-dashed border-primary/50 bg-card\">\n <div className=\"text-center\">\n <span className=\"mx-auto mb-2 flex h-11 w-11 items-center justify-center rounded-xl bg-primary/10 text-primary\">\n <UploadGlyph className=\"h-5 w-5\" />\n </span>\n <p className=\"text-sm font-semibold text-foreground\">{dropTitle}</p>\n <p className=\"mt-0.5 text-xs text-muted-foreground\">{dropDescription}</p>\n </div>\n </div>\n )}\n\n {showAbove && <div className=\"mb-1.5 flex flex-wrap items-center gap-1.5 px-1\">{controls}</div>}\n\n {dictateError && (\n <div\n role=\"alert\"\n data-testid=\"composer-dictate-error\"\n className=\"mb-2 flex items-start gap-2 rounded-xl border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive\"\n >\n <span className=\"min-w-0 flex-1\">{dictateError}</span>\n <button\n type=\"button\"\n aria-label=\"Dismiss dictation error\"\n onClick={() => setDictateError(null)}\n className=\"shrink-0 font-medium underline-offset-2 hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-destructive/50\"\n >\n Dismiss\n </button>\n </div>\n )}\n\n {failedSend && (\n <div\n role=\"alert\"\n data-testid=\"composer-send-error\"\n className=\"mb-2 rounded-xl border border-destructive/40 bg-destructive/10 px-3 py-2 text-xs text-destructive\"\n >\n <div className=\"flex items-start gap-2\">\n <span className=\"min-w-0 flex-1\">{failedSend.message}</span>\n <button\n type=\"button\"\n aria-label=\"Dismiss send error\"\n onClick={() => setFailedSend(null)}\n className=\"shrink-0 font-medium underline-offset-2 hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-destructive/50\"\n >\n Dismiss\n </button>\n </div>\n {/* The draft is only held here when it could NOT go back in the box —\n the user typed a replacement. Showing the bytes is what makes the\n message recoverable by hand even if Retry keeps failing. */}\n {!failedSend.restored && (\n <div className=\"mt-1.5\">\n <p\n data-testid=\"composer-unsent-draft\"\n className=\"max-h-20 overflow-y-auto whitespace-pre-wrap rounded-lg border border-destructive/30 bg-card px-2 py-1 text-foreground\"\n >\n {failedSend.text}\n </p>\n <button\n type=\"button\"\n aria-label=\"Retry sending the unsent message\"\n onClick={retryFailedSend}\n disabled={sendBlockedByStream || disabled}\n className=\"mt-1.5 font-medium underline-offset-2 hover:underline disabled:cursor-not-allowed disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-destructive/50\"\n >\n Retry\n </button>\n </div>\n )}\n </div>\n )}\n\n {contextItems.length > 0 && (\n <div aria-label=\"Message context\" className=\"mb-2 flex min-w-0 flex-wrap gap-1.5\">\n {contextItems.map((item) => (\n <span\n key={item.id}\n className=\"inline-flex min-w-0 max-w-full items-center gap-1.5 rounded-md border border-primary/30 bg-primary/10 px-2.5 py-1 text-xs text-primary\"\n >\n {item.icon && (\n <span className=\"shrink-0\" aria-hidden>\n {item.icon}\n </span>\n )}\n <span className=\"min-w-0 truncate\">{item.label}</span>\n {item.onRemove && (\n <button\n type=\"button\"\n aria-label={`Remove context ${item.label}`}\n onClick={item.onRemove}\n className=\"shrink-0 rounded p-0.5 text-primary/70 transition hover:text-primary focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <CloseGlyph className=\"h-3 w-3\" />\n </button>\n )}\n </span>\n ))}\n </div>\n )}\n\n {pendingFiles.length > 0 && (\n <div className=\"mb-2 flex flex-wrap gap-1.5\">\n {[...folderChips, ...fileChips].map((f) => {\n const isError = f.status === 'error'\n return (\n <span\n key={f.id}\n title={isError ? f.errorMessage : undefined}\n className={`inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs ${\n isError\n ? 'border-destructive/40 text-destructive'\n : 'border-border bg-secondary text-foreground'\n } ${f.status === 'pending' ? 'opacity-60' : ''}`}\n >\n {/* A thumbnail identifies a pasted screenshot that the\n auto-generated name cannot. Folders never have one. */}\n {f.kind !== 'folder' && f.previewUrl ? (\n <img src={f.previewUrl} alt=\"\" className=\"h-8 w-8 shrink-0 rounded object-cover\" />\n ) : f.kind === 'folder' ? (\n <FolderGlyph className=\"h-3 w-3 shrink-0\" />\n ) : (\n <PaperclipGlyph className=\"h-3 w-3 shrink-0\" />\n )}\n <span className=\"max-w-[150px] truncate\">{f.name}</span>\n {f.fileCount !== undefined && <span className=\"text-muted-foreground\">({f.fileCount})</span>}\n {f.status === 'uploading' && (\n <span className=\"h-3 w-3 animate-spin rounded-full border-2 border-primary border-t-transparent\" />\n )}\n {isError && f.errorMessage && (\n <span className=\"max-w-[150px] truncate text-destructive/80\">{f.errorMessage}</span>\n )}\n {isError && onRetryFile && (\n <button\n type=\"button\"\n aria-label={`Retry upload ${f.name}`}\n onClick={() => onRetryFile(f.id)}\n className=\"rounded p-0.5 text-muted-foreground transition hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <RetryGlyph className=\"h-3 w-3\" />\n </button>\n )}\n {onRemoveFile && (\n <button\n type=\"button\"\n aria-label={`Remove ${f.name}`}\n onClick={() => onRemoveFile(f.id)}\n className=\"rounded p-0.5 text-muted-foreground transition hover:text-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <CloseGlyph className=\"h-3 w-3\" />\n </button>\n )}\n </span>\n )\n })}\n </div>\n )}\n\n {/* Two rows inside one card: the message gets the full width, and every\n affordance that acts on it — attach, the controls slot, send — sits on\n its own row beneath. A single row would make the textarea share its\n line with the buttons, which is what squeezed the input and pushed the\n controls out of the card in the first place. */}\n <div\n ref={cardRef}\n data-testid=\"composer-card\"\n className={`flex flex-col gap-1.5 rounded-2xl border border-card-edge bg-card px-3 py-2.5 transition focus-within:border-primary/40 focus-within:ring-2 focus-within:ring-primary/15 ${\n floating ? 'shadow-raised' : ''\n }`}\n >\n <textarea\n ref={textareaRef}\n value={text}\n onChange={(e) => setText(e.target.value)}\n onKeyDown={handleKeyDown}\n onPaste={onAttach ? handlePaste : undefined}\n placeholder={placeholder}\n disabled={disabled}\n autoFocus={autoFocus}\n // `minRows` lines before it grows. `rows` is what actually holds the\n // floor: the autosize measures `scrollHeight` against `height: auto`,\n // which a textarea resolves through `rows`, so the measurement cannot\n // come back shorter. The paired `minHeight` is those same lines in CSS\n // (`box-sizing: border-box` puts the padding inside it), computed from\n // the same row count so the two cannot disagree. It sits exactly AT\n // the natural height on purpose: a floor is meant to be inert until\n // something tries to go under it, which here means an inline height\n // arriving from anywhere but the autosize. Setting it higher would buy\n // no protection and cost permanent dead space under the caret.\n rows={minRows}\n style={{ minHeight: minRows * LINE_HEIGHT + TEXTAREA_PADDING_Y, maxHeight }}\n aria-label=\"Message input\"\n className=\"w-full resize-none bg-transparent px-1.5 py-1 text-base leading-6 text-foreground outline-none placeholder:text-muted-foreground disabled:opacity-50\"\n />\n\n <div className=\"flex items-end gap-2\">\n {onAttach && (\n <>\n <button\n type=\"button\"\n onClick={() => fileInputRef.current?.click()}\n disabled={disabled}\n aria-label=\"Attach files\"\n title=\"Attach files\"\n className=\"shrink-0 rounded-lg p-2 text-muted-foreground transition hover:bg-accent hover:text-foreground disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <PaperclipGlyph className=\"h-4 w-4\" />\n </button>\n <input ref={fileInputRef} type=\"file\" multiple className=\"hidden\" accept={accept} onChange={handleFileChange} />\n </>\n )}\n {onAttachFolder && (\n <>\n <button\n type=\"button\"\n onClick={() => folderInputRef.current?.click()}\n disabled={disabled}\n aria-label=\"Attach folder\"\n title=\"Attach folder\"\n className=\"shrink-0 rounded-lg p-2 text-muted-foreground transition hover:bg-accent hover:text-foreground disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <FolderGlyph className=\"h-4 w-4\" />\n </button>\n {/* webkitdirectory is non-standard but widely supported for folder picks. */}\n <input\n ref={folderInputRef}\n type=\"file\"\n multiple\n className=\"hidden\"\n onChange={handleFolderChange}\n {...({ webkitdirectory: '' } as Record<string, string>)}\n />\n </>\n )}\n\n {/* The controls take the row's slack and wrap onto a second line when\n a long picker set outgrows it. This slot must never establish an\n overflow box: a control owns its popover (ModelPicker, EffortPicker)\n and anchors it absolutely to itself, so a scroll/clip box here traps\n a 400px-tall list inside a ~34px row — the list renders and is never\n visible — and the scroll offset that comes with it cuts the trigger's\n own left edge. Growing a second line is the cost of controls that\n stay operable. Rendered even when empty so Send stays right-aligned. */}\n <div\n data-testid=\"composer-controls\"\n className=\"flex min-w-0 flex-1 flex-wrap items-center gap-1.5\"\n >\n {showInline && controls}\n </div>\n\n {/* Trailing content is the controls slot's SIBLING, not its content:\n the slot is where a picker set is allowed to wrap and shrink, and\n a meter or a status line put inside it would be pushed onto the\n second line by the very pickers it reports on. */}\n {trailing && (\n <div data-testid=\"composer-trailing\" className=\"flex shrink-0 items-center gap-1.5\">\n {trailing}\n </div>\n )}\n {/* Dictation sits beside Send: it produces input, like typing. The\n button renders only when the host takes audio AND the browser can\n record — a dead mic is worse than no mic. While recording, the\n elapsed seconds (not the pulsing dot, which reduced motion\n collapses) are the signal, and the stop control is never\n disabled: a `disabled` flip mid-capture must not strand the mic. */}\n {onDictate && dictation.supported ? (\n dictation.recording ? (\n <div className=\"flex shrink-0 items-center gap-1.5\">\n <span aria-hidden=\"true\" className=\"h-2 w-2 animate-pulse rounded-full bg-destructive\" />\n <span\n aria-hidden=\"true\"\n data-testid=\"composer-dictate-elapsed\"\n className=\"text-xs tabular-nums text-muted-foreground\"\n >\n {formatDictationElapsed(dictation.elapsedSeconds)}\n </span>\n <span role=\"status\" className=\"sr-only\">\n Recording\n </span>\n <button\n type=\"button\"\n onClick={dictation.stop}\n aria-label=\"Stop dictation\"\n title=\"Stop dictation\"\n className=\"shrink-0 rounded-lg p-2 text-destructive transition hover:bg-accent focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <StopGlyph className=\"h-4 w-4\" />\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={dictation.start}\n disabled={disabled}\n aria-label=\"Dictate message\"\n title=\"Dictate message\"\n className=\"shrink-0 rounded-lg p-2 text-muted-foreground transition hover:bg-accent hover:text-foreground disabled:opacity-50 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <MicGlyph className=\"h-4 w-4\" />\n </button>\n )\n ) : null}\n\n {isStreaming ? (\n sendVariant === 'icon' ? (\n <button\n type=\"button\"\n onClick={onCancel}\n aria-label=\"Stop response\"\n title=\"Stop\"\n className=\"inline-flex h-[34px] w-[34px] shrink-0 items-center justify-center rounded-full border border-border bg-transparent text-foreground transition hover:bg-accent focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <StopGlyph className=\"h-3 w-3\" />\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={onCancel}\n aria-label=\"Stop response\"\n className=\"inline-flex shrink-0 items-center gap-1.5 rounded-full bg-destructive/15 px-3.5 py-2 text-sm font-medium text-destructive transition hover:bg-destructive/25 focus:outline-none focus-visible:ring-2 focus-visible:ring-destructive/50\"\n >\n <StopGlyph className=\"h-3.5 w-3.5\" />\n <span>Stop</span>\n </button>\n )\n ) : sendVariant === 'icon' ? (\n <button\n type=\"button\"\n onClick={send}\n disabled={!canSend}\n aria-label={sendLabel}\n title={sendLabel}\n className=\"inline-flex h-[34px] w-[34px] shrink-0 items-center justify-center rounded-full bg-foreground text-background transition hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-40 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-card\"\n >\n <ArrowUpGlyph className=\"h-4 w-4\" />\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={send}\n disabled={!canSend}\n aria-label={sendLabel}\n className=\"inline-flex shrink-0 items-center gap-1.5 rounded-full bg-primary px-3.5 py-2 text-sm font-medium text-primary-foreground shadow-sm transition hover:bg-primary/90 disabled:cursor-not-allowed disabled:opacity-40 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-card\"\n >\n <SendGlyph className=\"h-3.5 w-3.5\" />\n <span>{sendLabel}</span>\n </button>\n )}\n </div>\n </div>\n\n {/* The slash menu ports through PopoverSurface like every canonical\n popover: the composer docks inside horizontally scrolling rails, and\n an in-place panel there is a panel the host clips away. It anchors\n to the textarea and opens above. Focus never leaves the input —\n rows are mousedown-swallowed so a click can't blur it. */}\n <PopoverSurface\n open={slashOpen}\n id={slashListId}\n role=\"listbox\"\n triggerRef={textareaRef}\n panelRef={slashPanelRef}\n className={`w-80 overflow-y-auto rounded-xl border border-card-edge bg-popover p-1 ${OVERLAY_SHADOW}`}\n >\n {slashFiltered.length === 0 && (\n <div className=\"px-3 py-4 text-center text-sm text-muted-foreground\">No matching commands</div>\n )}\n {slashFiltered.map((item, index) => (\n <button\n key={item.id}\n type=\"button\"\n role=\"option\"\n aria-selected={index === slashActiveIndex}\n id={`${slashListId}-${index}`}\n onMouseDown={(e) => e.preventDefault()}\n onMouseMove={() => setSlashActive(index)}\n onClick={() => pickSlash(item.id)}\n className={`flex w-full items-center gap-2.5 rounded-md px-3 py-2.5 text-left text-sm transition ${POPOVER_OPTION_FOCUS} ${\n index === slashActiveIndex ? 'bg-accent' : 'hover:bg-accent'\n }`}\n >\n <span className=\"shrink-0 font-medium text-foreground\">{item.label}</span>\n <span className=\"truncate text-xs text-muted-foreground\">{item.description}</span>\n </button>\n ))}\n </PopoverSurface>\n\n {focusShortcut && (\n <div className=\"mt-1.5 flex justify-end px-1\">\n <span className=\"text-xs text-muted-foreground\">\n <kbd className=\"rounded border border-border bg-background px-1 py-0.5 text-xs\">{IS_APPLE_PLATFORM ? 'Cmd' : 'Ctrl'}</kbd>\n <kbd className=\"ml-0.5 rounded border border-border bg-background px-1 py-0.5 text-xs\">L</kbd>\n <span className=\"ml-1\">to focus</span>\n </span>\n </div>\n )}\n </div>\n )\n}\n","/**\n * `useDictation` — the capture half of composer dictation.\n *\n * Dictation splits at a clean seam: the browser owns capture (`getUserMedia` +\n * `MediaRecorder`), the host owns what the audio MEANS (transcription —\n * `sequences-react`'s Whisper provider — or a straight upload). This hook is\n * the capture half and nothing else: it asks for the mic, records, ticks whole\n * seconds while it does, and hands the assembled `Blob` to the host's\n * `onDictate`. A hook rather than composer-private code, because a host whose\n * composer is fully composed (hotkey, push-to-talk) needs the same capture\n * without re-deriving it.\n *\n * The rules the implementation exists to hold:\n *\n * - **Unsupported is a render signal, not an exception.** A browser without\n * `MediaRecorder`/`getUserMedia` gets `supported: false`, and the composer\n * renders no dead button. `start()` stays a no-op rather than throwing, so\n * a host that wired it to a hotkey cannot crash on such a browser.\n * - **The mic is released the moment recording ends.** Tracks are stopped in\n * every exit — stop, error, cancel-during-prompt, unmount. A red dot the\n * browser keeps showing after the composer says \"idle\" is the failure this\n * is written against.\n * - **A denied prompt is a message, not a crash.** `NotAllowedError` and a\n * missing device are reported through `onError` as words the composer can\n * show; the hook returns to idle.\n * - **Unmount discards.** A composer that unmounts mid-recording delivers\n * nothing: the host it would have called has moved on, and an arriving\n * transcript would land in a conversation the user left.\n * - **Duration is measured, not counted.** `durationSeconds` comes off the\n * clock at stop; the one-second ticker drives only the visible elapsed\n * display, so a throttled timer never falsifies the delivered figure.\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react'\n\n/** The audio a finished recording hands to the host. */\nexport interface DictationAudio {\n /** The assembled recording, typed with the MIME the recorder actually used. */\n readonly blob: Blob\n /** `blob.type`, surfaced so a host can switch on it without touching the blob. */\n readonly mimeType: string\n /** Clock-measured whole seconds between start and stop. */\n readonly durationSeconds: number\n}\n\nexport interface UseDictationOptions {\n /** The host callback: receive the recording. Transcription is the host's. */\n onDictate: (audio: DictationAudio) => void\n /** Capture failures in words (\"Microphone access was denied…\"). Optional —\n * the composer shows its own notice either way; this is for hosts that log. */\n onError?: (message: string) => void\n}\n\nexport interface DictationControls {\n /** Whether this browser can record at all. When false, render no affordance. */\n readonly supported: boolean\n readonly recording: boolean\n /** Whole seconds since the current recording started; drives the indicator. */\n readonly elapsedSeconds: number\n /** Ask for the mic and start. A no-op while a recording or a prompt is open. */\n readonly start: () => void\n /** Stop and deliver. Cancels a still-pending permission prompt instead. */\n readonly stop: () => void\n}\n\n/** Preference order: opus-in-webm first, Safari's mp4 last, UA default if none. */\nconst PREFERRED_MIME_TYPES = ['audio/webm;codecs=opus', 'audio/webm', 'audio/mp4'] as const\n\n/** The mime to ask the recorder for, or `undefined` to take the UA default. */\nexport function pickDictationMimeType(): string | undefined {\n if (typeof MediaRecorder === 'undefined' || typeof MediaRecorder.isTypeSupported !== 'function') {\n return undefined\n }\n for (const type of PREFERRED_MIME_TYPES) {\n if (MediaRecorder.isTypeSupported(type)) return type\n }\n return undefined\n}\n\n/** Capture support is a property of the browser, so it is read once per mount. */\nfunction detectDictationSupport(): boolean {\n return (\n typeof navigator !== 'undefined' &&\n typeof navigator.mediaDevices?.getUserMedia === 'function' &&\n typeof MediaRecorder !== 'undefined'\n )\n}\n\n/** The failure as a sentence. The denied prompt is the common case and the one\n * whose generic name (\"NotAllowedError\") says nothing to a reader. */\nexport function dictationErrorMessage(error: unknown): string {\n if (error instanceof DOMException) {\n if (error.name === 'NotAllowedError') return 'Microphone access was denied — allow it in the browser to dictate.'\n if (error.name === 'NotFoundError') return 'No microphone found on this device.'\n }\n return 'Could not start recording.'\n}\n\n/** `0:00`, `0:09`, `1:05`, `60:00` — minutes unbounded, seconds always two digits. */\nexport function formatDictationElapsed(totalSeconds: number): string {\n const safe = Number.isFinite(totalSeconds) && totalSeconds > 0 ? Math.floor(totalSeconds) : 0\n const minutes = Math.floor(safe / 60)\n const seconds = safe % 60\n return `${minutes}:${String(seconds).padStart(2, '0')}`\n}\n\n/** One capture's mutable internals, kept in a ref: they move with recorder\n * events, not with renders. */\ninterface DictationSession {\n readonly stream: MediaStream\n readonly recorder: MediaRecorder\n readonly chunks: Blob[]\n readonly mimeType: string\n readonly startedAt: number\n /** Set when the capture must deliver nothing (unmount, recorder failure). */\n cancelled: boolean\n}\n\n/** Release the mic. Idempotent — every exit path ends here. */\nfunction releaseStream(stream: MediaStream): void {\n for (const track of stream.getTracks()) track.stop()\n}\n\nexport function useDictation({ onDictate, onError }: UseDictationOptions): DictationControls {\n const [supported] = useState(detectDictationSupport)\n const [recording, setRecording] = useState(false)\n const [elapsedSeconds, setElapsedSeconds] = useState(0)\n\n const sessionRef = useRef<DictationSession | null>(null)\n /** Cancels a start whose getUserMedia has not resolved yet. */\n const cancelPendingStartRef = useRef<(() => void) | null>(null)\n // The recorder's event handlers fire outside React's render, so they read the\n // LATEST callbacks — a re-rendered host must not have its audio delivered to\n // the props the recording started with.\n const callbacksRef = useRef({ onDictate, onError })\n callbacksRef.current = { onDictate, onError }\n\n // The visible elapsed ticker. Follows `recording`; reset on each start so a\n // reused composer never opens at the previous capture's stale count.\n useEffect(() => {\n if (!recording) return\n setElapsedSeconds(0)\n const id = setInterval(() => setElapsedSeconds((s) => s + 1), 1000)\n return () => clearInterval(id)\n }, [recording])\n\n /** End the session: release the mic, reset state. Delivery is onstop's job. */\n const teardown = useCallback((cancelled: boolean) => {\n const session = sessionRef.current\n if (session === null) return\n session.cancelled = session.cancelled || cancelled\n sessionRef.current = null\n releaseStream(session.stream)\n setRecording(false)\n }, [])\n\n const stop = useCallback(() => {\n // A stop while the permission prompt is still open cancels the start: when\n // the stream arrives it is released unused, and nothing ever records.\n cancelPendingStartRef.current?.()\n cancelPendingStartRef.current = null\n const session = sessionRef.current\n if (session === null || session.cancelled) return\n // stop() flushes the buffered chunk (dataavailable) and THEN fires stop —\n // the blob is assembled in onstop, so a stop mid-chunk loses nothing.\n if (session.recorder.state !== 'inactive') session.recorder.stop()\n }, [])\n\n const start = useCallback(() => {\n if (!supported) return\n if (sessionRef.current !== null || cancelPendingStartRef.current !== null) return\n\n let pendingCancelled = false\n cancelPendingStartRef.current = () => {\n pendingCancelled = true\n }\n\n navigator.mediaDevices.getUserMedia({ audio: true }).then(\n (stream) => {\n cancelPendingStartRef.current = null\n if (pendingCancelled) {\n releaseStream(stream)\n return\n }\n const mimeType = pickDictationMimeType()\n const recorder = new MediaRecorder(stream, mimeType === undefined ? undefined : { mimeType })\n const session: DictationSession = {\n stream,\n recorder,\n chunks: [],\n mimeType: recorder.mimeType || mimeType || '',\n startedAt: Date.now(),\n cancelled: false,\n }\n sessionRef.current = session\n\n recorder.ondataavailable = (event) => {\n if (event.data.size > 0) session.chunks.push(event.data)\n }\n\n recorder.onstop = () => {\n teardown(session.cancelled)\n if (session.cancelled) return\n const blob = new Blob(session.chunks, { type: session.mimeType })\n if (blob.size === 0) {\n // A tap on/off can produce no bytes at all. Handing the host a\n // 0-byte blob reads as a recording that happened; it did not.\n callbacksRef.current.onError?.('Nothing was recorded.')\n return\n }\n const durationSeconds = Math.max(0, Math.round((Date.now() - session.startedAt) / 1000))\n callbacksRef.current.onDictate({ blob, mimeType: session.mimeType, durationSeconds })\n }\n\n recorder.onerror = () => {\n // The capture is dead; what matters is that the mic is released and\n // the hook is not wedged — the next start builds a fresh session.\n teardown(true)\n callbacksRef.current.onError?.('Recording stopped unexpectedly.')\n }\n\n recorder.start()\n setRecording(true)\n },\n (error: unknown) => {\n cancelPendingStartRef.current = null\n if (pendingCancelled) return\n callbacksRef.current.onError?.(dictationErrorMessage(error))\n },\n )\n }, [supported, teardown])\n\n // Unmount mid-recording discards the capture: mark it cancelled so onstop\n // delivers nothing, then stop the recorder to flush its events, and release\n // the mic whether or not those events ever fire.\n useEffect(\n () => () => {\n cancelPendingStartRef.current?.()\n cancelPendingStartRef.current = null\n const session = sessionRef.current\n if (session === null) return\n session.cancelled = true\n sessionRef.current = null\n try {\n if (session.recorder.state !== 'inactive') session.recorder.stop()\n } finally {\n releaseStream(session.stream)\n }\n },\n [],\n )\n\n return { supported, recording, elapsedSeconds, start, stop }\n}\n","import { useCallback, useEffect, useRef, useState } from 'react'\nimport type { ChatPlan } from '../plans/index'\n\n/** Represent durable plan decisions as either approved or rejected */\nexport type DurablePlanDecision = 'approved' | 'rejected'\n\n/** Stable authority receipt for the follow-up turn dispatched by a plan\n * decision. Consumers must make `attachFollowUp` idempotent by `receiptId`;\n * reload and retry deliberately invoke it again. */\nexport interface DurablePlanFollowUpReceipt {\n receiptId: string\n planId: string\n revision: number\n turnId: string\n state: string\n}\n\n/** Describe the result of a durable plan decision including plan details and pending statuses */\nexport interface DurablePlanDecisionResult {\n plan: ChatPlan\n followUp?: DurablePlanFollowUpReceipt\n idempotent: boolean\n projectionPending?: boolean\n effectPending?: boolean\n}\n\n/** Define input parameters for making a durable plan decision including optional feedback */\nexport interface DurablePlanDecisionInput {\n planId: string\n revision: number\n decision: DurablePlanDecision\n feedback?: string\n}\n\n/** Define input parameters for retrieving the current durable plan including optional revision number */\nexport interface DurablePlanCurrentInput {\n planId: string\n revision?: number\n}\n\n/** Define methods to obtain and decide durable plan decisions asynchronously */\nexport interface DurablePlanDecisionClient {\n current: (input: DurablePlanCurrentInput) => Promise<DurablePlanDecisionResult>\n decide: (input: DurablePlanDecisionInput) => Promise<DurablePlanDecisionResult>\n}\n\n/** Represent errors from DurablePlanClient operations including status, code, and current plan details */\nexport class DurablePlanClientError extends Error {\n constructor(\n message: string,\n readonly status: number,\n readonly code?: string,\n readonly currentPlan?: ChatPlan,\n ) {\n super(message)\n this.name = 'DurablePlanClientError'\n }\n}\n\n/** Define configuration options for creating a durable plan decision client */\nexport interface DurablePlanDecisionClientOptions {\n url: string | ((input: DurablePlanCurrentInput | DurablePlanDecisionInput) => string)\n body?: Record<string, unknown> | ((input: DurablePlanDecisionInput) => Record<string, unknown>)\n fetchImpl?: typeof fetch\n}\n\nfunction recordOf(value: unknown): Record<string, unknown> | null {\n return value && typeof value === 'object' && !Array.isArray(value)\n ? value as Record<string, unknown>\n : null\n}\n\nfunction readPlan(value: unknown): ChatPlan | null {\n const plan = recordOf(value)\n if (!plan) return null\n const planId = typeof plan.planId === 'string' ? plan.planId : typeof plan.id === 'string' ? plan.id : null\n if (!planId || typeof plan.revision !== 'number' || typeof plan.body !== 'string' ||\n typeof plan.submittedAt !== 'string' || typeof plan.status !== 'string') return null\n return { ...plan, planId } as ChatPlan\n}\n\nfunction receiptIdentity(plan: ChatPlan, followUp: Record<string, unknown>): string {\n if (typeof followUp.receiptId === 'string' && followUp.receiptId) return followUp.receiptId\n const turnId = typeof followUp.turnId === 'string' ? followUp.turnId : ''\n return `${plan.planId}:${plan.revision}:${turnId}`\n}\n\nfunction parseDecisionResult(value: unknown): DurablePlanDecisionResult | null {\n const body = recordOf(value)\n const plan = readPlan(body?.plan)\n if (!body || !plan) return null\n const rawFollowUp = recordOf(body.followUp) ?? recordOf(body.receipt)\n const followUp = rawFollowUp && typeof rawFollowUp.turnId === 'string'\n ? {\n receiptId: receiptIdentity(plan, rawFollowUp),\n planId: plan.planId,\n revision: plan.revision,\n turnId: rawFollowUp.turnId,\n state: typeof rawFollowUp.state === 'string' ? rawFollowUp.state : 'unknown',\n }\n : undefined\n return {\n plan,\n ...(followUp ? { followUp } : {}),\n idempotent: body.idempotent === true || body.replayed === true,\n ...(body.projectionPending === true ? { projectionPending: true } : {}),\n ...(body.effectPending === true ? { effectPending: true } : {}),\n }\n}\n\nasync function responseBody(response: Response): Promise<Record<string, unknown>> {\n return recordOf(await response.json().catch(() => null)) ?? {}\n}\n\n/** Browser client for the shared durable-plan route. The route URL and all\n * product routing fields are injected; workspace/session identity is still\n * resolved and authorized on the server. */\nexport function createDurablePlanDecisionClient(\n options: DurablePlanDecisionClientOptions,\n): DurablePlanDecisionClient {\n const fetchImpl = options.fetchImpl ?? fetch\n const urlFor = (input: DurablePlanCurrentInput | DurablePlanDecisionInput) =>\n typeof options.url === 'function' ? options.url(input) : options.url\n\n const read = async (response: Response): Promise<DurablePlanDecisionResult> => {\n const body = await responseBody(response)\n const result = parseDecisionResult(body)\n if (response.ok && result) return result\n const currentPlan = readPlan(body.plan) ?? undefined\n const message = typeof body.error === 'string'\n ? body.error\n : typeof body.message === 'string' ? body.message : `Plan request failed (${response.status})`\n throw new DurablePlanClientError(\n message,\n response.status,\n typeof body.code === 'string' ? body.code : undefined,\n currentPlan,\n )\n }\n\n return {\n async current(input) {\n const rawUrl = urlFor(input)\n const url = new URL(rawUrl, globalThis.location?.origin ?? 'http://localhost')\n url.searchParams.set('planId', input.planId)\n if (input.revision !== undefined) url.searchParams.set('revision', String(input.revision))\n const target = /^https?:/.test(rawUrl)\n ? url.toString()\n : `${url.pathname}${url.search}`\n return read(await fetchImpl(target, { method: 'GET' }))\n },\n async decide(input) {\n const extra = typeof options.body === 'function' ? options.body(input) : options.body ?? {}\n return read(await fetchImpl(urlFor(input), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ ...extra, ...input }),\n }))\n },\n }\n}\n\n/** Define options to configure durable plan flow with plan, client, and optional callbacks */\nexport interface UseDurablePlanFlowOptions {\n plan: ChatPlan\n client: DurablePlanDecisionClient\n /** Must be idempotent by receipt.receiptId. */\n attachFollowUp?: (receipt: DurablePlanFollowUpReceipt) => Promise<void> | void\n onUpdated?: (plan: ChatPlan) => void\n}\n\n/** Define the result and actions for managing a durable plan flow including decisions, restoration, and error handling */\nexport interface UseDurablePlanFlowResult {\n plan: ChatPlan\n deciding: DurablePlanDecision | null\n restoring: boolean\n error: string | null\n decide: (decision: DurablePlanDecision, feedback?: string) => Promise<DurablePlanDecisionResult | null>\n restore: () => Promise<DurablePlanDecisionResult | null>\n clearError: () => void\n}\n\n/** Shared plan decision controller. It coalesces only concurrent attachment\n * attempts; a later retry/restore calls the consumer's idempotent transport\n * again so a lost response cannot strand an already-dispatched follow-up. */\nexport function useDurablePlanFlow(options: UseDurablePlanFlowOptions): UseDurablePlanFlowResult {\n const [plan, setPlan] = useState(options.plan)\n const [deciding, setDeciding] = useState<DurablePlanDecision | null>(null)\n const [restoring, setRestoring] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const attachments = useRef(new Map<string, Promise<void>>())\n const decisionInFlight = useRef(false)\n\n useEffect(() => setPlan(options.plan), [options.plan])\n\n const apply = useCallback(async (result: DurablePlanDecisionResult) => {\n setPlan(result.plan)\n options.onUpdated?.(result.plan)\n const receipt = result.followUp\n if (!receipt || !options.attachFollowUp) return\n let pending = attachments.current.get(receipt.receiptId)\n if (!pending) {\n pending = Promise.resolve(options.attachFollowUp(receipt))\n attachments.current.set(receipt.receiptId, pending)\n void pending.finally(() => attachments.current.delete(receipt.receiptId))\n }\n await pending\n }, [options.attachFollowUp, options.onUpdated])\n\n const decide = useCallback(async (decision: DurablePlanDecision, feedback?: string) => {\n if (decisionInFlight.current) return null\n decisionInFlight.current = true\n setDeciding(decision)\n setError(null)\n try {\n const result = await options.client.decide({\n planId: plan.planId,\n revision: plan.revision,\n decision,\n ...(feedback?.trim() ? { feedback: feedback.trim() } : {}),\n })\n await apply(result)\n return result\n } catch (cause) {\n if (cause instanceof DurablePlanClientError && cause.currentPlan) {\n setPlan(cause.currentPlan)\n options.onUpdated?.(cause.currentPlan)\n }\n setError(cause instanceof Error ? cause.message : 'Could not decide the plan.')\n return null\n } finally {\n decisionInFlight.current = false\n setDeciding(null)\n }\n }, [apply, options.client, options.onUpdated, plan.planId, plan.revision])\n\n const restore = useCallback(async () => {\n setRestoring(true)\n setError(null)\n try {\n const result = await options.client.current({ planId: plan.planId, revision: plan.revision })\n await apply(result)\n return result\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : 'Could not restore the plan.')\n return null\n } finally {\n setRestoring(false)\n }\n }, [apply, options.client, plan.planId, plan.revision])\n\n return { plan, deciding, restoring, error, decide, restore, clearError: () => setError(null) }\n}\n","import {\n INTERACTION_SUBMIT_TIMEOUT_MESSAGE,\n INTERACTION_SUBMIT_TIMEOUT_MS,\n responseErrorMessage,\n type InteractionAnswerSubmission,\n type InteractionAnswerSubmitterOptions,\n type SubmitInteractionAnswer,\n} from './interaction-card-support'\n\n/** Manage storage and retrieval of interaction attempt keys by interaction and submission identifiers */\nexport interface InteractionAttemptStore {\n get(interactionId: string, submissionSignature: string): string | null\n set(interactionId: string, submissionSignature: string, attemptKey: string): void\n delete(interactionId: string, submissionSignature: string): void\n}\n\nfunction attemptStorageKey(namespace: string, interactionId: string): string {\n return `${namespace}:${encodeURIComponent(interactionId)}`\n}\n\nfunction storedAttempts(storage: Pick<Storage, 'getItem'>, key: string): Record<string, string> {\n try {\n const value = JSON.parse(storage.getItem(key) ?? '{}') as unknown\n return value && typeof value === 'object' && !Array.isArray(value)\n ? value as Record<string, string>\n : {}\n } catch {\n return {}\n }\n}\n\n/** Create a session-based store to manage interaction attempts using provided storage and optional namespace */\nexport function createSessionInteractionAttemptStore(\n storage: Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>,\n namespace = 'agent-app:interaction-attempt',\n): InteractionAttemptStore {\n return {\n get(id, signature) {\n return storedAttempts(storage, attemptStorageKey(namespace, id))[signature] ?? null\n },\n set(id, signature, attemptKey) {\n const key = attemptStorageKey(namespace, id)\n storage.setItem(key, JSON.stringify({ ...storedAttempts(storage, key), [signature]: attemptKey }))\n },\n delete(id, signature) {\n const key = attemptStorageKey(namespace, id)\n const attempts = storedAttempts(storage, key)\n delete attempts[signature]\n if (Object.keys(attempts).length === 0) storage.removeItem(key)\n else storage.setItem(key, JSON.stringify(attempts))\n },\n }\n}\n\n/** Create an in-memory store to manage interaction attempts keyed by ID and signature */\nexport function createMemoryInteractionAttemptStore(): InteractionAttemptStore {\n const attempts = new Map<string, string>()\n const key = (id: string, signature: string) => `${id}\\u0000${signature}`\n return {\n get: (id, signature) => attempts.get(key(id, signature)) ?? null,\n set: (id, signature, attemptKey) => attempts.set(key(id, signature), attemptKey),\n delete: (id, signature) => { attempts.delete(key(id, signature)) },\n }\n}\n\nfunction stableValue(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(stableValue)\n if (!value || typeof value !== 'object') return value\n return Object.fromEntries(Object.entries(value as Record<string, unknown>)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([key, nested]) => [key, stableValue(nested)]))\n}\n\n/** Generate a stable string signature from an interaction answer submission */\nexport function interactionSubmissionSignature(submission: InteractionAnswerSubmission): string {\n return JSON.stringify(stableValue(submission))\n}\n\n/** Define options for submitting durable interaction answers with attempt tracking and optional key creation */\nexport interface DurableInteractionAnswerSubmitterOptions extends InteractionAnswerSubmitterOptions {\n attempts: InteractionAttemptStore\n createAttemptKey?: () => string\n}\n\nfunction defaultAttemptKey(): string {\n if (globalThis.crypto?.randomUUID) return globalThis.crypto.randomUUID()\n return `attempt-${Date.now()}-${Math.random().toString(36).slice(2)}`\n}\n\n/** Answer submitter for a durable interaction route. One opaque attempt key is\n * retained for an ambiguous transport/5xx result and reused after reload. A\n * changed answer has a different signature and therefore a new attempt. */\nexport function createDurableInteractionAnswerSubmitter(\n options: DurableInteractionAnswerSubmitterOptions,\n): SubmitInteractionAnswer {\n const timeoutMs = options.timeoutMs ?? INTERACTION_SUBMIT_TIMEOUT_MS\n const fetchImpl = options.fetchImpl ?? fetch\n return async (submission) => {\n const signature = interactionSubmissionSignature(submission)\n let attemptKey: string\n try {\n attemptKey = options.attempts.get(submission.id, signature) ?? ''\n if (!attemptKey) {\n attemptKey = (options.createAttemptKey ?? defaultAttemptKey)()\n options.attempts.set(submission.id, signature, attemptKey)\n }\n } catch (cause) {\n return {\n ok: false,\n expired: false,\n message: cause instanceof Error ? cause.message : 'Failed to submit the answer',\n }\n }\n const url = typeof options.url === 'function' ? options.url(submission) : options.url\n const extra = typeof options.body === 'function' ? options.body(submission) : options.body ?? {}\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(INTERACTION_SUBMIT_TIMEOUT_MESSAGE), timeoutMs)\n try {\n const response = await fetchImpl(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n signal: controller.signal,\n body: JSON.stringify({\n ...extra,\n id: submission.id,\n outcome: submission.outcome,\n attemptKey,\n ...(submission.data ? { data: submission.data } : {}),\n }),\n })\n if (response.ok) {\n options.attempts.delete(submission.id, signature)\n return { ok: true }\n }\n const failure = await responseErrorMessage(response)\n if (response.status < 500) options.attempts.delete(submission.id, signature)\n return { ok: false, expired: response.status === 410, message: failure.message }\n } catch (cause) {\n if (controller.signal.aborted) {\n return { ok: false, expired: false, message: INTERACTION_SUBMIT_TIMEOUT_MESSAGE }\n }\n return {\n ok: false,\n expired: false,\n message: cause instanceof Error ? cause.message : 'Failed to submit the answer',\n }\n } finally {\n clearTimeout(timer)\n }\n }\n}\n","/**\n * useChatInteractions — the interaction-state wiring every consumer of\n * `ChatStreamCallbacks.onInteraction` re-implements: an id-keyed,\n * insertion-ordered list with\n *\n * - forward-only status transitions (a replayed/stale `pending` never\n * resurrects a resolved card),\n * - pending-question content dedupe (a re-emitted duplicate ask never renders\n * a second card),\n * - cancel-event application (`interaction.cancel` → cancelled/expired),\n * - local resolution marking (the card's `onResolved`),\n * - reload restore from the answer route's GET list (sidecar registry is the\n * source of truth after a reload),\n * - turn-end settling (client mirror of the server's finalize pass: a turn\n * that completed without a cancel was answered; a failed turn can make no\n * such claim).\n *\n * The reducer functions are pure and exported for non-React consumers/tests;\n * the hook is a thin `useState` shell over them.\n */\n\nimport { useCallback, useMemo, useState } from 'react'\nimport {\n cancelStatusFor,\n interactionFromWireRequest,\n isTerminalInteractionStatus,\n questionInteractionContentSignature,\n type ChatInteraction,\n type ChatInteractionStatus,\n type InteractionAnswers,\n type InteractionCancelData,\n type InteractionRequestWire,\n} from './chat-interactions'\n\nfunction hasPendingContentDuplicate(list: ChatInteraction[], interaction: ChatInteraction): boolean {\n if (interaction.status !== 'pending') return false\n const signature = questionInteractionContentSignature(interaction)\n if (!signature) return false\n return list.some((item) =>\n item.id !== interaction.id &&\n item.status === 'pending' &&\n questionInteractionContentSignature(item) === signature)\n}\n\n/** Insert or update one interaction. A terminal existing entry wins over any\n * incoming state for the same id; a new pending ask that duplicates another\n * pending ask's content is dropped. Returns the same array when unchanged. */\nexport function upsertChatInteraction(list: ChatInteraction[], interaction: ChatInteraction): ChatInteraction[] {\n const index = list.findIndex((item) => item.id === interaction.id)\n if (index === -1) {\n if (hasPendingContentDuplicate(list, interaction)) return list\n return [...list, interaction]\n }\n const existing = list[index]\n if (!existing) return list\n if (isTerminalInteractionStatus(existing.status)) {\n if (\n existing.status === interaction.status &&\n (!existing.answers && interaction.answers || !existing.cancelReason && interaction.cancelReason)\n ) {\n const next = [...list]\n next[index] = { ...existing, ...interaction }\n return next\n }\n return list\n }\n const next = [...list]\n next[index] = interaction\n return next\n}\n\n/** Applies an `interaction.cancel` event: only a pending ask moves, to\n * `expired` (reason:\"timeout\") or `cancelled`. */\nexport function cancelChatInteraction(list: ChatInteraction[], cancel: InteractionCancelData): ChatInteraction[] {\n const index = list.findIndex((item) => item.id === cancel.id)\n const existing = list[index]\n if (!existing || existing.status !== 'pending') return list\n const next = [...list]\n next[index] = {\n ...existing,\n status: cancelStatusFor(cancel.reason),\n ...(cancel.reason ? { cancelReason: cancel.reason } : {}),\n }\n return next\n}\n\n/** Marks one ask resolved locally (the card's `onResolved`). Forward-only. */\nexport function resolveChatInteraction(\n list: ChatInteraction[],\n id: string,\n status: Exclude<ChatInteractionStatus, 'pending'>,\n answers?: InteractionAnswers,\n): ChatInteraction[] {\n const index = list.findIndex((item) => item.id === id)\n const existing = list[index]\n if (!existing || existing.status !== 'pending') return list\n const next = [...list]\n next[index] = { ...existing, status, ...(answers ? { answers } : {}) }\n return next\n}\n\n/** Settles every still-pending ask when the turn ends: `answered` for a turn\n * that completed cleanly, `expired` for one that failed. */\nexport function terminalizePendingChatInteractions(\n list: ChatInteraction[],\n status: Extract<ChatInteractionStatus, 'answered' | 'expired'>,\n): ChatInteraction[] {\n if (!list.some((item) => item.status === 'pending')) return list\n return list.map((item) => (item.status === 'pending' ? { ...item, status } : item))\n}\n\n/** Define modes for restoring chat interactions with legacy or durable strategies */\nexport type ChatInteractionRestoreMode = 'legacy' | 'durable'\n\n/** Define options to control how chat interactions are restored during the restore process */\nexport interface RestoreChatInteractionsOptions {\n /**\n * `legacy` settles pending asks absent from the sidecar list as answered,\n * preserving the pre-durable restore contract. `durable` leaves them\n * pending because absence is ambiguous until `hydrateChatInteractions`\n * applies the durable projection.\n */\n mode?: ChatInteractionRestoreMode\n}\n\n/** Reload restore from the answer route's GET list. Legacy consumers retain\n * the historical absence→answered behavior; durable consumers opt into the\n * ambiguity-preserving mode and apply terminal parts through `hydrate`. */\nexport function restoreChatInteractions(\n list: ChatInteraction[],\n outstanding: InteractionRequestWire[],\n options: RestoreChatInteractionsOptions = {},\n): ChatInteraction[] {\n let next = list\n for (const request of outstanding) {\n const interaction = interactionFromWireRequest(request)\n const exact = next.findIndex((item) => item.id === interaction.id)\n if (exact !== -1) {\n next = upsertChatInteraction(next, interaction)\n continue\n }\n const signature = questionInteractionContentSignature(interaction)\n const obsolete = signature\n ? next.findIndex((item) => item.status === 'pending' && questionInteractionContentSignature(item) === signature)\n : -1\n if (obsolete === -1) {\n next = [...next, interaction]\n continue\n }\n next = [...next]\n next[obsolete] = interaction\n }\n if (options.mode !== 'durable') {\n const outstandingIds = new Set(outstanding.map((request) => request.id))\n next = next.map((item) =>\n item.status === 'pending' && !outstandingIds.has(item.id)\n ? { ...item, status: 'answered' as const }\n : item)\n }\n return next\n}\n\n/** Applies transcript/state-store projections after reload. Terminal state and\n * acknowledged answer values enrich an existing pending card without relying\n * on the sidecar's outstanding-list absence. */\nexport function hydrateChatInteractions(\n list: ChatInteraction[],\n persisted: ChatInteraction[],\n): ChatInteraction[] {\n return persisted.reduce(upsertChatInteraction, list)\n}\n\n/** Resolve and manage chat interactions with methods to update, cancel, mark resolved, and restore state */\nexport interface UseChatInteractionsResult {\n /** All known interactions, insertion-ordered. */\n interactions: ChatInteraction[]\n /** The asks currently blocking the run (waiting on the user). */\n pending: ChatInteraction[]\n /** Wire to `ChatStreamCallbacks.onInteraction` (and persisted-part replay). */\n upsert: (interaction: ChatInteraction) => void\n /** Wire to `interaction.cancel` events. */\n applyCancel: (cancel: InteractionCancelData) => void\n /** Wire to the cards' `onResolved`. */\n markResolved: (id: string, status: Exclude<ChatInteractionStatus, 'pending'>, answers?: InteractionAnswers) => void\n /** Wire to the answer route's GET list after a reload/reconnect. */\n restore: (outstanding: InteractionRequestWire[], options?: RestoreChatInteractionsOptions) => void\n /** Apply durable transcript/state projections after a reload. */\n hydrate: (persisted: ChatInteraction[]) => void\n /** Settle still-pending asks when the turn ends. */\n terminalizePending: (status: Extract<ChatInteractionStatus, 'answered' | 'expired'>) => void\n /** Drop everything (thread switch). */\n reset: () => void\n}\n\n/** Resolve options for restoring chat interactions from previous sessions */\nexport type UseChatInteractionsOptions = RestoreChatInteractionsOptions\n\n/** Manage chat interactions state with upsert, cancel, resolve, and restore capabilities */\nexport function useChatInteractions(options: UseChatInteractionsOptions = {}): UseChatInteractionsResult {\n const [interactions, setInteractions] = useState<ChatInteraction[]>([])\n\n const upsert = useCallback((interaction: ChatInteraction) => {\n setInteractions((prev) => upsertChatInteraction(prev, interaction))\n }, [])\n const applyCancel = useCallback((cancel: InteractionCancelData) => {\n setInteractions((prev) => cancelChatInteraction(prev, cancel))\n }, [])\n const markResolved = useCallback((id: string, status: Exclude<ChatInteractionStatus, 'pending'>, answers?: InteractionAnswers) => {\n setInteractions((prev) => resolveChatInteraction(prev, id, status, answers))\n }, [])\n const restore = useCallback((outstanding: InteractionRequestWire[], restoreOptions?: RestoreChatInteractionsOptions) => {\n setInteractions((prev) => restoreChatInteractions(prev, outstanding, {\n mode: restoreOptions?.mode ?? options.mode,\n }))\n }, [options.mode])\n const hydrate = useCallback((persisted: ChatInteraction[]) => {\n setInteractions((prev) => hydrateChatInteractions(prev, persisted))\n }, [])\n const terminalizePending = useCallback((status: Extract<ChatInteractionStatus, 'answered' | 'expired'>) => {\n setInteractions((prev) => terminalizePendingChatInteractions(prev, status))\n }, [])\n const reset = useCallback(() => setInteractions([]), [])\n\n const pending = useMemo(() => interactions.filter((item) => item.status === 'pending'), [interactions])\n\n return { interactions, pending, upsert, applyCancel, markResolved, restore, hydrate, terminalizePending, reset }\n}\n","/**\n * `useFileMentions` — the glue a host passes straight into `AgentComposer`'s\n * `mention` prop (`@tangle-network/sandbox-ui#184`) to wire up `@`-file\n * mentions against `createSandboxFileIndexRoute` (`/chat-routes`).\n *\n * Fetches the index once per session from `indexUrl`, refreshes it in the\n * background whenever the popover opens (a `fetchItems` call) if the cached\n * copy has aged past `refreshAfterMs`, and answers every keystroke from an\n * in-memory fuzzy filter — no per-keystroke network round trip. The returned\n * `refresh()` lets a caller force a re-fetch immediately instead of waiting\n * on `refreshAfterMs` — e.g. right after the agent creates a file mid-session.\n *\n * `MentionItem`/the `mention` prop shape mirror the FROZEN contract from\n * sandbox-ui#184 structurally (no import: `/web-react` stays dependency-free\n * beyond React, and `@tangle-network/sandbox-ui` is an optional peer).\n */\n\nimport { useCallback, useMemo, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport type { FileIndexResponse, FileIndexReadyResponse } from '../chat-routes/file-index'\nimport type { FileMention } from '../chat-routes/wire'\n\n/** Mirrors sandbox-ui#184's `MentionItem` — the atomic pill's payload. For a\n * file mention, `id` is the workspace-relative path (the pill's stable\n * identity and the `@<id>` serialization sandbox-ui uses to round-trip\n * `value`), `label` is the display name, and `detail` carries the full path\n * for the popover row's secondary line. */\nexport interface MentionItem {\n id: string\n label: string\n detail?: string\n kind?: string\n}\n\n/** Mirrors sandbox-ui#184's `AgentComposerProps['mention']` shape — plug the\n * hook's `mention` return value straight into that prop. */\nexport interface ComposerMentionProp {\n trigger?: string\n fetchItems(query: string): Promise<MentionItem[]>\n onMentionsChange?(mentions: MentionItem[]): void\n renderItem?(item: MentionItem): ReactNode\n emptyText?: string\n}\n\nconst FILE_MENTION_KIND = 'file'\n\nfunction toMentionItem(file: FileMention): MentionItem {\n return { id: file.path, label: file.name, detail: file.path, kind: FILE_MENTION_KIND }\n}\n\nfunction toFileMention(item: MentionItem): FileMention {\n return { path: item.id, name: item.label }\n}\n\n/**\n * Ranks `files` against `query` (case-insensitive), capped to `limit`:\n * name-prefix matches first, then name-substring, then path-substring.\n * Within a tier, shorter names sort first (the more specific match), then\n * alphabetically by path for a stable order. An empty query returns the\n * first `limit` entries unranked — the popover's default list before typing.\n * Pure and dependency-free (no fuzzy-match library) so it's cheap enough to\n * re-run on every keystroke against a 10k-entry index.\n */\nexport function rankFileMentions(\n files: readonly FileMention[],\n query: string,\n limit: number,\n): FileMention[] {\n const q = query.trim().toLowerCase()\n if (!q) return files.slice(0, limit)\n const scored: Array<{ file: FileMention; tier: 0 | 1 | 2 }> = []\n for (const file of files) {\n const name = file.name.toLowerCase()\n if (name.startsWith(q)) {\n scored.push({ file, tier: 0 })\n continue\n }\n if (name.includes(q)) {\n scored.push({ file, tier: 1 })\n continue\n }\n if (file.path.toLowerCase().includes(q)) {\n scored.push({ file, tier: 2 })\n }\n }\n scored.sort((a, b) => {\n if (a.tier !== b.tier) return a.tier - b.tier\n if (a.file.name.length !== b.file.name.length) return a.file.name.length - b.file.name.length\n return a.file.path.localeCompare(b.file.path)\n })\n return scored.slice(0, limit).map((s) => s.file)\n}\n\ntype IndexState =\n | { kind: 'idle' }\n | { kind: 'loading' }\n | { kind: 'ready'; files: FileMention[]; truncated: boolean; fetchedAt: number }\n | { kind: 'warming'; attemptedAt: number }\n | { kind: 'error'; message: string; attemptedAt: number }\n\n/** Minimum spacing between automatic retries while the box is warming or the\n * last attempt errored — a query per keystroke would otherwise hammer the\n * index endpoint the whole time the box is cold. */\nconst RETRY_AFTER_MS = 3000\n\n/** Max popover results per query — enough to show a useful spread of matches\n * without pushing the fuzzy-filtered list past what a popover can usefully\n * render in one screen. */\nexport const DEFAULT_MENTION_LIMIT = 20\n\n/** How long a `ready` index is served before a background refetch — long\n * enough that a full session's worth of popover opens don't repeatedly hit\n * the index endpoint, short enough that a stale listing doesn't linger too\n * far past workspace file changes. Callers who need the index current right\n * now (e.g. just after the agent creates a file) call `refresh()` instead of\n * waiting on this window. */\nexport const INDEX_REFRESH_AFTER_MS = 5 * 60 * 1000\n\n/** Popover empty-state copy for a `ready` index whose query matched nothing.\n * Loading/warming/error states have their own copy — see `emptyTextFor`. */\nexport const DEFAULT_MENTION_EMPTY_TEXT = 'No matching files'\n\n/** Define options for configuring file mention fetching, caching, and display behavior */\nexport interface UseFileMentionsOptions {\n /** GET endpoint returning `FileIndexResponse` (a `createSandboxFileIndexRoute`). */\n indexUrl: string\n /** Max popover results per query. Default {@link DEFAULT_MENTION_LIMIT}. */\n limit?: number\n /** How long a `ready` index is served without a background refetch.\n * Default {@link INDEX_REFRESH_AFTER_MS}. */\n refreshAfterMs?: number\n /** `fetch` override for tests / non-global-fetch hosts. Default `fetch`. */\n fetchImpl?: typeof fetch\n /** Text shown in the popover's empty state once the index is loaded and\n * the query matched nothing. Default {@link DEFAULT_MENTION_EMPTY_TEXT}. */\n emptyText?: string\n}\n\n/** Provide properties and methods to manage and refresh file mentions in a composer interface */\nexport interface UseFileMentionsResult {\n /** Spread straight into `AgentComposer`'s `mention` prop. */\n mention: ComposerMentionProp\n /** The files currently referenced by mentions in the composer's value —\n * the send-body list (map through `fileMentionsToParts`). */\n mentions: FileMention[]\n /** Drop all currently-referenced mentions (e.g. after a successful send). */\n clearMentions: () => void\n /** Force a re-fetch of the index right now, ignoring `refreshAfterMs` — for\n * example right after the agent creates a file mid-session, so the next\n * popover open sees it. Dedupes against an already-in-flight load rather\n * than firing a second request. */\n refresh: () => Promise<void>\n}\n\n/** Never blocks — a `warming` or `error` index answers `fetchItems` with an\n * empty list plus an explanatory `emptyText`, since the frozen composer\n * contract has no separate loading/warming slot. */\nfunction emptyTextFor(state: IndexState, fallback: string): string {\n switch (state.kind) {\n case 'idle':\n case 'loading':\n return 'Loading files…'\n case 'warming':\n return 'Sandbox is starting — try again in a moment'\n case 'error':\n return `Couldn't load files: ${state.message}`\n case 'ready':\n return fallback\n }\n}\n\n/** Resolve and manage file mention data with configurable fetching and state handling */\nexport function useFileMentions(options: UseFileMentionsOptions): UseFileMentionsResult {\n const {\n indexUrl,\n limit = DEFAULT_MENTION_LIMIT,\n refreshAfterMs = INDEX_REFRESH_AFTER_MS,\n emptyText = DEFAULT_MENTION_EMPTY_TEXT,\n } = options\n const fetchImpl = options.fetchImpl ?? fetch\n\n const [state, setState] = useState<IndexState>({ kind: 'idle' })\n // `fetchItems` needs the settled result of a fetch it just awaited, but a\n // `setState` call doesn't synchronously update anything a plain callback\n // can read — the re-render (and this ref's refresh) lands on a later tick.\n // `load()` returns its resolved `IndexState` directly (and mirrors it onto\n // this ref) so `fetchItems` never depends on render timing for its answer;\n // the ref separately lets `fetchItems` read the CURRENT state up front\n // without subscribing to it (which would break `mention`'s referential\n // stability on every keystroke).\n const stateRef = useRef(state)\n stateRef.current = state\n const inFlightRef = useRef<Promise<IndexState> | null>(null)\n const [mentions, setMentions] = useState<FileMention[]>([])\n\n const load = useCallback((): Promise<IndexState> => {\n if (inFlightRef.current) return inFlightRef.current\n if (stateRef.current.kind === 'idle') {\n stateRef.current = { kind: 'loading' }\n setState(stateRef.current)\n }\n const attempt = (async (): Promise<IndexState> => {\n let next: IndexState\n try {\n const res = await fetchImpl(indexUrl)\n if (!res.ok) {\n next = { kind: 'error', message: `HTTP ${res.status}`, attemptedAt: Date.now() }\n } else {\n const body = (await res.json()) as FileIndexResponse\n next =\n body.status === 'warming'\n ? { kind: 'warming', attemptedAt: Date.now() }\n : {\n kind: 'ready',\n files: (body as FileIndexReadyResponse).files,\n truncated: (body as FileIndexReadyResponse).truncated,\n fetchedAt: Date.now(),\n }\n }\n } catch (err) {\n next = { kind: 'error', message: err instanceof Error ? err.message : String(err), attemptedAt: Date.now() }\n }\n stateRef.current = next\n setState(next)\n inFlightRef.current = null\n return next\n })()\n inFlightRef.current = attempt\n return attempt\n }, [fetchImpl, indexUrl])\n\n // `load()` itself never consults `refreshAfterMs` — that gate lives in\n // `fetchItems` — so calling it directly here already bypasses the TTL. The\n // in-flight dedup inside `load()` covers the case where a background\n // refresh from `fetchItems` is already running.\n const refresh = useCallback(async (): Promise<void> => {\n await load()\n }, [load])\n\n const fetchItems = useCallback(\n async (query: string): Promise<MentionItem[]> => {\n let current = stateRef.current\n // First open: block on the fetch so the popover's first result set\n // reflects it. Once `ready`, background-refresh a stale cache without\n // blocking this query's answer. A warming/errored index retries at\n // most every RETRY_AFTER_MS — not on every keystroke.\n if (current.kind === 'idle' || current.kind === 'loading') {\n current = await load()\n } else if (current.kind === 'ready') {\n if (Date.now() - current.fetchedAt > refreshAfterMs) void load()\n } else if (Date.now() - current.attemptedAt > RETRY_AFTER_MS) {\n void load()\n }\n if (current.kind !== 'ready') return []\n return rankFileMentions(current.files, query, limit).map(toMentionItem)\n },\n [load, limit, refreshAfterMs],\n )\n\n const onMentionsChange = useCallback((items: MentionItem[]) => {\n setMentions(items.filter((item) => item.kind === undefined || item.kind === FILE_MENTION_KIND).map(toFileMention))\n }, [])\n\n const clearMentions = useCallback(() => setMentions([]), [])\n\n const mention = useMemo<ComposerMentionProp>(\n () => ({\n fetchItems,\n onMentionsChange,\n emptyText: emptyTextFor(state, emptyText),\n }),\n [fetchItems, onMentionsChange, state, emptyText],\n )\n\n return { mention, mentions, clearMentions, refresh }\n}\n","/**\n * Transcript-side counterpart to the composer's `@`-mention primitive\n * (sandbox-ui#184). The composer serializes a picked file into the message\n * text as `@<path>`; this module is the exact inverse — it finds those tokens\n * again in a PERSISTED message and splits the text so a renderer can draw a\n * pill where the user typed one and leave the rest as prose.\n *\n * Pure and product-agnostic: no React, no fetch, no DOM. The only input beyond\n * the text is the message's OWN mention parts, so one message can never render\n * a pill for a path another message mentioned.\n *\n * `ChatMentionPart` and the runtime helpers `mentionInputToPart` /\n * `mentionPartsFromMessageParts` are re-exported here from `../chat-store/parts`\n * directly (not the `/chat-store` barrel), so a browser bundle gets the mention\n * vocabulary and its converters without importing `/chat-store`, whose barrel\n * pulls the drizzle peer.\n */\n\nimport { mentionInputToPart, mentionPartsFromMessageParts, type ChatMentionKind, type ChatMentionPart } from '../chat-store/parts'\n\nexport type { ChatMentionKind, ChatMentionPart }\nexport { mentionInputToPart, mentionPartsFromMessageParts }\n\n/** One run of a segmented message: literal prose, or a matched mention with\n * the part that produced it. `text` for a mention segment is the token as it\n * appears in the message (`@<path>`), so a renderer that ignores `part` still\n * reproduces the original string exactly. */\nexport interface MentionTextSegment {\n type: 'text' | 'mention'\n text: string\n part?: ChatMentionPart\n}\n\n/** A character that could plausibly continue the SAME path/filename past a\n * matched token. Without this lookahead a mention of `@a/b.md` would match\n * inside the unrelated `@a/b.md.bak` and split it mid-filename.\n *\n * Unicode-aware because the wire validator (`validateSandboxMentionPath`)\n * deliberately ALLOWS non-ASCII paths — in-box filenames are arbitrary. An\n * ASCII-only class here would accept input the segmenter then mangles. */\nconst PATH_CONTINUATION_CHAR = /[\\p{L}\\p{N}._\\-/]/u\n/** A character that, immediately BEFORE an `@`, means the `@` is part of a\n * longer token (an email local part, a handle) rather than a mention start.\n * Unicode-aware for the same reason. */\nconst WORD_CHAR = /[\\p{L}\\p{N}]/u\n\n/**\n * Split a message's text into plain-text and mention segments by matching\n * `@<path>` runs against that message's own mention parts.\n *\n * Only a part whose exact `@<path>` token appears in `content`, at a token\n * boundary on both sides, counts as a match; everything else — including\n * unrelated `@` text — passes through as plain text untouched. When two parts'\n * tokens both match at the same position (one path a prefix of another), the\n * LONGEST token wins, so nested-looking paths split at the right boundary.\n *\n * Returns the matched parts alongside the segments: a caller that also renders\n * a fallback chip row can drop the chip for anything now shown inline and keep\n * it only for mentions the text does not actually contain (a restored draft, a\n * queued message whose text was edited).\n */\nexport function segmentMentionContent(\n content: string,\n parts: ReadonlyArray<ChatMentionPart>,\n): { segments: MentionTextSegment[]; matched: Set<ChatMentionPart> } {\n const matched = new Set<ChatMentionPart>()\n if (!content) return { segments: [], matched }\n if (parts.length === 0) return { segments: [{ type: 'text', text: content }], matched }\n\n const candidates = parts\n .map((part) => ({ part, token: `@${part.path}` }))\n .sort((a, b) => b.token.length - a.token.length)\n\n const segments: MentionTextSegment[] = []\n let cursor = 0\n let textStart = 0\n while (cursor < content.length) {\n if (content[cursor] !== '@') {\n cursor += 1\n continue\n }\n const prevChar = cursor > 0 ? content[cursor - 1] : undefined\n if (prevChar && WORD_CHAR.test(prevChar)) {\n cursor += 1\n continue\n }\n const candidate = candidates.find(({ token }) => content.startsWith(token, cursor))\n if (!candidate) {\n cursor += 1\n continue\n }\n const endIdx = cursor + candidate.token.length\n const nextChar = endIdx < content.length ? content[endIdx] : undefined\n if (nextChar && PATH_CONTINUATION_CHAR.test(nextChar)) {\n cursor += 1\n continue\n }\n\n if (cursor > textStart) segments.push({ type: 'text', text: content.slice(textStart, cursor) })\n segments.push({ type: 'mention', text: candidate.token, part: candidate.part })\n matched.add(candidate.part)\n cursor = endIdx\n textStart = cursor\n }\n if (textStart < content.length) segments.push({ type: 'text', text: content.slice(textStart) })\n\n return { segments, matched }\n}\n","/**\n * Mission + delegation observability surfaces — different nouns, one trace\n * tree:\n *\n * - {@link MissionActivityLane}: the collapsed sub-rows under a mission step\n * (what the step's agent is actually doing), expanding to a compact web\n * waterfall rendered from the `/trace` converters.\n * - {@link AgentActivityPanel}: the standalone cross-context surface — every\n * delegation a workspace ran, regardless of which mission (if any) spawned\n * it — behind a `fetchActivity` data port with cursor + refresh.\n * - {@link FlowWaterfall}: the web counterpart of `/trace`'s ASCII\n * `renderWaterfall` (which stays CLI) — proportional bars over a FlowTrace.\n *\n * Same styling contract as the rest of `/web-react`: Tailwind classes against\n * the shared design tokens, inline SVG glyphs, no icon library. The pure\n * layout/merge/format helpers are exported for tests and reuse.\n */\n\nimport { useCallback, useEffect, useState, type ReactNode } from 'react'\n\nimport type { StepAgentActivity } from '../missions/agent-activity'\nimport type { FlowTrace } from '../trace/index'\nimport { stepActivityFlowTrace } from '../trace/mission-flow'\nimport { useArrivalStyle } from './motion'\n\n// ── pure helpers ──────────────────────────────────────────────────────────\n\nexport type ActivityTone = 'live' | 'ok' | 'error' | 'neutral'\n\nconst LIVE_STATUSES = new Set(['pending', 'running'])\nconst OK_STATUSES = new Set(['completed', 'done', 'succeeded'])\nconst ERROR_STATUSES = new Set(['failed', 'error', 'cancelled', 'aborted'])\n\n/** Map a delegation status (free-form string on the wire) to a render tone. */\nexport function activityTone(status: string): ActivityTone {\n const s = status.toLowerCase()\n if (LIVE_STATUSES.has(s)) return 'live'\n if (OK_STATUSES.has(s)) return 'ok'\n if (ERROR_STATUSES.has(s)) return 'error'\n return 'neutral'\n}\n\n/** \"$0.4000\" under a cent shows 4 decimals; null when unknown/zero. */\nexport function formatActivityCost(costUsd?: number): string | null {\n if (costUsd === undefined || !isFinite(costUsd) || costUsd <= 0) return null\n return costUsd < 0.01 ? `$${costUsd.toFixed(4)}` : `$${costUsd.toFixed(2)}`\n}\n\n/** \"8s\" / \"2m 05s\" / \"1h 12m\"; null when unknown. */\nexport function formatActivityDuration(durationMs?: number): string | null {\n if (durationMs === undefined || !isFinite(durationMs) || durationMs < 0) return null\n const totalSeconds = Math.round(durationMs / 1000)\n if (totalSeconds < 60) return `${totalSeconds}s`\n const minutes = Math.floor(totalSeconds / 60)\n const seconds = totalSeconds % 60\n if (minutes < 60) return `${minutes}m ${String(seconds).padStart(2, '0')}s`\n return `${Math.floor(minutes / 60)}h ${String(minutes % 60).padStart(2, '0')}m`\n}\n\n/** A delegation record on the cross-context surface; `missionRef` links a\n * promoted delegation back to the mission/step that spawned it. */\nexport interface AgentActivityRecord extends StepAgentActivity {\n missionRef?: { missionId: string; stepId?: string; label?: string }\n}\n\nexport interface AgentActivityPage {\n items: AgentActivityRecord[]\n /** Opaque continuation token; absent ⇒ no further pages. */\n nextCursor?: string\n}\n\n/**\n * Fold a fetched page into the held rows: dedupe by `taskId` with the\n * incoming row winning (a refresh re-fetches the head page, so newer\n * snapshots of in-flight runs replace stale ones), newest `startedAt` first.\n */\nexport function mergeActivityPages(\n existing: AgentActivityRecord[],\n incoming: AgentActivityRecord[],\n): AgentActivityRecord[] {\n const byTask = new Map<string, AgentActivityRecord>()\n for (const row of existing) byTask.set(row.taskId, row)\n for (const row of incoming) byTask.set(row.taskId, row)\n return [...byTask.values()].sort((a, b) => Date.parse(b.startedAt) - Date.parse(a.startedAt))\n}\n\nexport interface WaterfallRow {\n name: string\n kind: 'pipeline' | 'model' | 'tool'\n /** Bar geometry as percentages of the trace's total span. */\n offsetPct: number\n widthPct: number\n durationLabel: string\n approx: boolean\n /** False only when the span's meta carries an explicit failure. */\n ok: boolean\n}\n\n/** Project a FlowTrace into proportional bar geometry for {@link FlowWaterfall}. */\nexport function waterfallLayout(trace: FlowTrace): WaterfallRow[] {\n const total = trace.totalMs > 0 ? trace.totalMs : 1\n return [...trace.spans]\n .sort((a, b) => a.startMs - b.startMs)\n .map((span) => {\n const meta = span.meta ?? {}\n const failed =\n meta.ok === false || (typeof meta.status === 'string' && activityTone(meta.status) === 'error')\n return {\n name: span.name,\n kind: span.kind,\n offsetPct: Math.max(0, Math.min(100, (span.startMs / total) * 100)),\n widthPct: Math.max(0.5, Math.min(100, ((span.endMs - span.startMs) / total) * 100)),\n durationLabel: `${((span.endMs - span.startMs) / 1000).toFixed(1)}s${span.approx ? '~' : ''}`,\n approx: span.approx === true,\n ok: !failed,\n }\n })\n}\n\n// ── glyphs ────────────────────────────────────────────────────────────────\n\nfunction ChevronGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n )\n}\n\nfunction RefreshGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M21 12a9 9 0 1 1-2.64-6.36M21 3v6h-6\" />\n </svg>\n )\n}\n\nfunction CopyGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" />\n <path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\" />\n </svg>\n )\n}\n\n/** Copy a trace id to the clipboard — the drill-in's actionable handle instead\n * of a bare, dead-end string. Falls back silently when the Clipboard API is\n * unavailable (insecure context / older browser). */\nfunction TraceIdCopy({ traceId }: { traceId: string }) {\n const [copied, setCopied] = useState(false)\n const copy = useCallback(() => {\n void navigator.clipboard?.writeText(traceId).then(\n () => {\n setCopied(true)\n setTimeout(() => setCopied(false), 1200)\n },\n () => {},\n )\n }, [traceId])\n return (\n <button\n type=\"button\"\n onClick={copy}\n title=\"Copy trace id\"\n aria-label=\"Copy trace id\"\n className=\"inline-flex min-w-0 items-center gap-1.5 rounded text-left font-mono text-muted-foreground transition hover:text-foreground\"\n >\n <span className=\"truncate\">{traceId}</span>\n <CopyGlyph className=\"h-3 w-3 shrink-0\" />\n {copied && <span className=\"shrink-0 not-italic text-success\">copied</span>}\n </button>\n )\n}\n\n/**\n * The tone dot. A live run's dot no longer pulses: `animate-pulse` is the same\n * 2s fade a loading skeleton uses, so one animation was carrying two unrelated\n * meanings (\"no data yet\" and \"work in progress\") and a reader could not learn\n * either. The dot keeps the tone — hue plus the screen-reader word — and the\n * \"in flight\" signal moves to the one thing on the row that can say it in the\n * shipped vocabulary: the run's own label, sweeping (see {@link RunLabel}).\n */\nfunction StatusDot({ tone }: { tone: ActivityTone }) {\n return (\n <span className=\"inline-flex items-center\">\n <span\n aria-hidden\n className={`h-2 w-2 shrink-0 rounded-full ${\n tone === 'live' ? 'bg-warning' : tone === 'ok' ? 'bg-success' : tone === 'error' ? 'bg-destructive' : 'bg-muted-foreground/40'\n }`}\n />\n <span className=\"sr-only\">{tone}</span>\n </span>\n )\n}\n\n/**\n * `tool — detail` for one run. While the run is live the tool name sweeps: the\n * same cue the chat surface's waiting label uses, and the only signal on the\n * row separating an agent that is working from one that is stuck, which is why\n * it declares `data-motion=\"essential\"`. A settled run is plain text — nothing\n * is in flight, so nothing moves.\n *\n * `essential` exempts the label from the blanket reduced-motion collapse; it\n * does NOT keep the sweep running at a reader who asked for less motion. Under\n * `prefers-reduced-motion` tokens.css stops the animation and leaves a dotted\n * rule under the live tool name that its settled siblings on this lane do not\n * carry. That rule is the WHOLE distinction: measured in Chromium, a live and a\n * settled tool name on this lane compute the same color and the same weight\n * (rgb(12,12,21) light / rgb(236,236,241) dark, 500) and differ only in\n * `text-decoration`. The distinction survives; only the movement goes.\n */\nfunction RunLabel({ tool, detail, live }: { tool: string; detail: string; live: boolean }) {\n return (\n <span className=\"min-w-0 flex-1 truncate\">\n <span className={live ? 'agent-shimmer font-medium' : 'font-medium'} data-motion={live ? 'essential' : undefined}>\n {tool}\n </span>\n <span className=\"text-muted-foreground\"> — {detail}</span>\n </span>\n )\n}\n\n// ── FlowWaterfall ─────────────────────────────────────────────────────────\n\nconst BAR_CLASS: Record<WaterfallRow['kind'], string> = {\n pipeline: 'bg-muted-foreground/30',\n model: 'bg-primary/60',\n tool: 'bg-primary',\n}\n\nexport interface FlowWaterfallProps {\n trace: FlowTrace\n}\n\n/** Compact proportional waterfall over a FlowTrace — span name, bar, duration\n * per row; total + cost in the footer. */\nexport function FlowWaterfall({ trace }: FlowWaterfallProps) {\n const rows = waterfallLayout(trace)\n if (rows.length === 0) return null\n const cost = formatActivityCost(trace.costUsd)\n return (\n <div className=\"space-y-1\">\n {rows.map((row, i) => (\n <div key={i} className=\"grid grid-cols-[minmax(0,2fr)_minmax(0,3fr)_auto] items-center gap-2\">\n <span className=\"truncate font-mono text-xs text-muted-foreground\" title={row.name}>\n {row.name}\n </span>\n <div className=\"relative h-2 rounded-sm bg-secondary\">\n <div\n className={`absolute inset-y-0 rounded-sm ${row.ok ? BAR_CLASS[row.kind] : 'bg-destructive/80'} ${row.approx ? 'opacity-70' : ''}`}\n style={{ left: `${row.offsetPct}%`, width: `${row.widthPct}%` }}\n />\n </div>\n <span className=\"shrink-0 font-mono text-xs tabular-nums text-muted-foreground/70\">{row.durationLabel}</span>\n </div>\n ))}\n <p className=\"pt-0.5 text-right font-mono text-xs tabular-nums text-muted-foreground/60\">\n {(trace.totalMs / 1000).toFixed(1)}s{cost ? ` · ${cost}` : ''}\n </p>\n </div>\n )\n}\n\n// ── MissionActivityLane ───────────────────────────────────────────────────\n\nexport interface MissionActivityLaneProps {\n /** The step's delegated-run snapshot (`MissionStepState.agentActivity`). */\n activity: StepAgentActivity[]\n /** Epoch ms origin for the expanded waterfall — usually the step start. */\n startedAt?: number\n /** Wall clock for extending in-flight runs on the waterfall. */\n nowMs?: number\n}\n\n/**\n * One delegated run in the lane. A component rather than a `.map` body so the\n * arrival can be frozen at mount — a row that is already on screen and merely\n * changes status must not re-animate, and a stagger index recomputed from a\n * live array is exactly how that happens.\n */\nfunction LaneRow({ run, staggerIndex }: { run: StepAgentActivity; staggerIndex: number }) {\n const arrival = useArrivalStyle(staggerIndex)\n const tone = activityTone(run.status)\n const cost = formatActivityCost(run.costUsd)\n const duration = formatActivityDuration(run.durationMs)\n return (\n <div className=\"agent-arrive flex items-center gap-2 py-1 text-xs\" style={arrival}>\n <StatusDot tone={tone} />\n <RunLabel tool={run.tool} detail={run.detail} live={tone === 'live'} />\n {tone === 'live' && (run.iteration !== undefined || run.phase !== undefined) && (\n <span className=\"shrink-0 rounded-full bg-warning/10 px-1.5 py-0.5 font-mono text-xs text-warning\">\n {[run.iteration !== undefined ? `iter ${run.iteration}` : null, run.phase ?? null]\n .filter(Boolean)\n .join(' · ')}\n </span>\n )}\n <span className=\"flex shrink-0 items-center gap-1.5 font-mono text-xs tabular-nums text-muted-foreground/70\">\n {tone !== 'live' && tone !== 'ok' && <span>{run.status}</span>}\n {cost && <span>{cost}</span>}\n {duration && <span>{duration}</span>}\n </span>\n </div>\n )\n}\n\n/**\n * Collapsed sub-rows under a mission step — one row per delegated run —\n * expanding to the step's waterfall. Renders nothing for an empty lane.\n *\n * A sub-row appears because a delegated run STARTED or FINISHED, which is the\n * one kind of list change worth choreographing: it arrives, and the group\n * arrives as a sequence. Keying on `taskId` is what keeps the rest still — the\n * snapshot re-renders every poll, and a row whose status merely advanced holds\n * the DOM node it already had.\n */\nexport function MissionActivityLane({ activity, startedAt, nowMs }: MissionActivityLaneProps) {\n const [expanded, setExpanded] = useState(false)\n if (activity.length === 0) return null\n\n return (\n <div className=\"mt-1 border-l border-border pl-3\">\n {activity.map((run, index) => (\n <LaneRow key={run.taskId} run={run} staggerIndex={index} />\n ))}\n <button\n type=\"button\"\n onClick={() => setExpanded((v) => !v)}\n className=\"flex items-center gap-1 py-0.5 text-xs font-medium text-muted-foreground/70 transition hover:text-foreground\"\n >\n <ChevronGlyph className={`h-3 w-3 transition-transform ${expanded ? 'rotate-180' : ''}`} />\n timeline\n </button>\n {expanded && (\n <div className=\"rounded-md border border-border bg-secondary p-2\">\n <FlowWaterfall\n trace={stepActivityFlowTrace(activity, {\n ...(startedAt !== undefined ? { startedAt } : {}),\n ...(nowMs !== undefined ? { nowMs } : {}),\n })}\n />\n </div>\n )}\n </div>\n )\n}\n\n// ── AgentActivityPanel ────────────────────────────────────────────────────\n\nexport interface AgentActivityPanelProps {\n /** Data port — page through the product's delegation records. Called with\n * no cursor on mount/refresh, with `nextCursor` for older pages. */\n fetchActivity: (cursor?: string) => Promise<AgentActivityPage>\n /** Render the mission link for a promoted delegation (chip, anchor, router\n * Link — the product's routing, not ours). */\n renderMissionRef?: (ref: NonNullable<AgentActivityRecord['missionRef']>, record: AgentActivityRecord) => ReactNode\n title?: string\n emptyLabel?: string\n}\n\nfunction ActivityRow({\n record,\n renderMissionRef,\n staggerIndex,\n}: {\n record: AgentActivityRecord\n renderMissionRef?: AgentActivityPanelProps['renderMissionRef']\n /** Position in the page as it was FIRST rendered — see `useArrivalStyle`. */\n staggerIndex: number\n}) {\n const arrival = useArrivalStyle(staggerIndex)\n const [open, setOpen] = useState(false)\n const tone = activityTone(record.status)\n const cost = formatActivityCost(record.costUsd)\n const duration = formatActivityDuration(record.durationMs)\n\n return (\n <div className=\"agent-arrive rounded-lg border border-card-edge bg-card\" style={arrival}>\n <button type=\"button\" onClick={() => setOpen((v) => !v)} className=\"flex w-full items-center gap-2.5 px-3 py-2 text-left text-sm\">\n <StatusDot tone={tone} />\n <RunLabel tool={record.tool} detail={record.detail} live={tone === 'live'} />\n {tone === 'live' && (record.iteration !== undefined || record.phase !== undefined) && (\n <span className=\"shrink-0 rounded-full bg-warning/10 px-2 py-0.5 font-mono text-xs text-warning\">\n {[record.iteration !== undefined ? `iter ${record.iteration}` : null, record.phase ?? null]\n .filter(Boolean)\n .join(' · ')}\n </span>\n )}\n <span\n className={`shrink-0 rounded-full px-2 py-0.5 text-xs font-medium ${\n tone === 'ok'\n ? 'bg-success/10 text-success'\n : tone === 'error'\n ? 'bg-destructive/10 text-destructive'\n : tone === 'live'\n ? 'bg-warning/10 text-warning'\n : 'bg-secondary text-muted-foreground'\n }`}\n >\n {record.status}\n </span>\n {cost && <span className=\"shrink-0 font-mono text-xs tabular-nums text-muted-foreground\">{cost}</span>}\n <ChevronGlyph className={`h-3 w-3 shrink-0 text-muted-foreground transition-transform ${open ? 'rotate-180' : ''}`} />\n </button>\n {open && (\n <div className=\"space-y-2.5 border-t border-border px-3 py-2.5\">\n {record.durationMs !== undefined && (\n <div className=\"rounded-md border border-border bg-secondary p-2\">\n <FlowWaterfall trace={stepActivityFlowTrace([record])} />\n </div>\n )}\n <dl className=\"grid grid-cols-[auto_1fr] gap-x-3 gap-y-1 font-mono text-xs\">\n <dt className=\"text-muted-foreground/60\">task</dt>\n <dd className=\"truncate text-muted-foreground\">{record.taskId}</dd>\n <dt className=\"text-muted-foreground/60\">started</dt>\n <dd className=\"text-muted-foreground\">{new Date(record.startedAt).toLocaleString()}</dd>\n {duration && (\n <>\n <dt className=\"text-muted-foreground/60\">duration</dt>\n <dd className=\"text-muted-foreground\">{duration}</dd>\n </>\n )}\n {record.traceId && (\n <>\n <dt className=\"text-muted-foreground/60\">trace</dt>\n <dd className=\"min-w-0\">\n <TraceIdCopy traceId={record.traceId} />\n </dd>\n </>\n )}\n </dl>\n {record.missionRef && renderMissionRef?.(record.missionRef, record)}\n </div>\n )}\n </div>\n )\n}\n\n/**\n * The standalone cross-context delegation surface: every agent run the\n * product journaled, mission-spawned or not, with status, cost, drill-in, and\n * a mission link slot for promoted delegations.\n */\n/**\n * `loading`/`error` collapsed onto one status rather than two independent\n * booleans, so \"loading and errored at once\" is unrepresentable instead of\n * merely avoided by careful set-ordering — the same discipline\n * `web-react/async`'s `AsyncResourceState` enforces for a single fetch.\n * `rows`/`cursor` stay separate state because they ACCUMULATE across pages,\n * which a single-resolution status can't express — this is a cursor-paged\n * panel (`useSessionHistory`'s shape), not a one-shot resource.\n */\ntype AgentActivityStatus = 'loading' | 'error' | 'ready'\n\nexport function AgentActivityPanel({ fetchActivity, renderMissionRef, title = 'Agent activity', emptyLabel = 'No agent runs yet.' }: AgentActivityPanelProps) {\n const [rows, setRows] = useState<AgentActivityRecord[]>([])\n const [cursor, setCursor] = useState<string | undefined>(undefined)\n const [status, setStatus] = useState<AgentActivityStatus>('loading')\n const [error, setError] = useState<string | null>(null)\n\n const load = useCallback(\n async (from?: string) => {\n setStatus('loading')\n setError(null)\n try {\n const page = await fetchActivity(from)\n setRows((prev) => mergeActivityPages(from === undefined ? [] : prev, page.items))\n setCursor(page.nextCursor)\n setStatus('ready')\n } catch (e) {\n setError(e instanceof Error ? e.message : String(e))\n setStatus('error')\n }\n },\n [fetchActivity],\n )\n\n useEffect(() => {\n void load()\n }, [load])\n\n const loading = status === 'loading'\n\n return (\n <div className=\"space-y-2\">\n <div className=\"flex items-center gap-2\">\n <h2 className=\"flex-1 text-sm font-semibold\">{title}</h2>\n <button\n type=\"button\"\n onClick={() => void load()}\n disabled={loading}\n aria-label=\"Refresh\"\n className=\"rounded-md p-1.5 text-muted-foreground transition hover:bg-accent hover:text-foreground disabled:opacity-50\"\n >\n <RefreshGlyph className={`h-3.5 w-3.5 ${loading ? 'animate-spin' : ''}`} />\n </button>\n </div>\n {status === 'error' && (\n <p role=\"alert\" className=\"rounded-md border border-destructive/40 bg-destructive/5 px-3 py-2 text-xs text-destructive\">\n {error}\n </p>\n )}\n {status === 'ready' && rows.length === 0 && <p className=\"px-1 text-sm text-muted-foreground\">{emptyLabel}</p>}\n {/* While `loading` the list is empty AND the empty copy is suppressed, so\n without this the panel is silent to a screen reader from first paint\n until rows land. The region stays MOUNTED and its text changes, because\n a live region that is inserted already carrying its message is not\n reliably announced — and emptying it on arrival is what reports the\n wait ending. */}\n <span role=\"status\" aria-live=\"polite\" aria-busy={loading} className=\"sr-only\">\n {loading ? 'Loading activity…' : ''}\n </span>\n <div className=\"space-y-1.5\" aria-busy={loading}>\n {rows.map((record, index) => (\n <ActivityRow key={record.taskId} record={record} renderMissionRef={renderMissionRef} staggerIndex={index} />\n ))}\n </div>\n {cursor && (\n <button\n type=\"button\"\n onClick={() => void load(cursor)}\n disabled={loading}\n className=\"w-full rounded-md border border-border bg-card px-3 py-1.5 text-xs font-medium text-muted-foreground transition hover:bg-accent disabled:opacity-50\"\n >\n Older runs\n </button>\n )}\n </div>\n )\n}\n","/**\n * `ProvenanceValue` — one value rendered so its origin is discoverable without\n * navigating away, and openable when there is something to open.\n *\n * This is the affordance three verticals each rebuilt: tax renders a source\n * quote under an expanded return line, legal renders authorities under a\n * finding, the record grid renders a per-cell marker. Same product promise,\n * three vocabularies, three sets of gaps.\n *\n * What the primitive holds that a hand-rolled caption does not:\n *\n * 1. The four bases render differently — a person's entry and a model's\n * assertion are never the same pixels. Each carries its own glyph AND its\n * own words, so the distinction survives greyscale and a screen reader.\n * 2. It COMPOSES. A computed value's provenance is its inputs, and each input\n * is itself a `ProvenanceValue` with its own disclosure.\n * 3. Confidence renders as the next move (\"Check the source\"), never as a\n * percentage a reader cannot act on.\n * 4. The disclosure is a real button — keyboard reachable, `aria-expanded`,\n * Escape closes and returns focus, a click outside dismisses, and opening\n * one trail closes the one the reader left. Nothing here discloses on hover,\n * and no fact lives only in a `title` attribute.\n * 5. A source that is loading, unopenable, or absent SAYS so. There is no path\n * through this component that renders a bare number.\n *\n * Layout: an inline-block block-level element, so put it in a table cell, a\n * list item, or a card — not inside a `<p>`. Sandbox-ui-free, like the rest of\n * `/web-react`; the model in `./provenance-model` is React-free.\n */\n\nimport { useCallback, useEffect, useId, useRef, useState, type KeyboardEvent, type ReactNode } from 'react'\n\nimport {\n describeProvenance,\n describeProvenanceSourceStatus,\n loadingProvenanceSources,\n provenanceBasisMeta,\n provenanceGaps,\n provenanceNextMove,\n provenanceStandingMeta,\n provenanceTriggerLabel,\n rollUpProvenanceStanding,\n type ProvenanceBasis,\n type ProvenanceConfidencePolicy,\n type ProvenanceRecord,\n type ProvenanceSource,\n type ProvenanceStanding,\n} from './provenance-model'\n\nexport * from './provenance-model'\n\n// ── marker vocabulary ─────────────────────────────────────────────────────\n\n// Tone AND glyph differ per basis. Colour alone fails the reader who cannot\n// see it and the one whose theme flattens it; the words in\n// `provenanceBasisMeta` carry the same distinction a third time.\nconst BASIS_TONES: Record<ProvenanceBasis, string> = {\n extracted: 'border-primary/30 bg-primary/10 text-primary',\n entered: 'border-success/30 bg-success/10 text-success',\n computed: 'border-border bg-secondary text-foreground',\n asserted: 'border-warning/40 bg-warning/10 text-warning',\n}\n\nconst STANDING_TONES: Record<ProvenanceStanding, string> = {\n settled: 'text-muted-foreground',\n check: 'text-warning',\n confirm: 'text-destructive',\n}\n\nfunction BasisGlyph({ basis, className }: { basis: ProvenanceBasis; className?: string }) {\n const shared = {\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 2,\n strokeLinecap: 'round' as const,\n strokeLinejoin: 'round' as const,\n 'aria-hidden': true,\n className,\n }\n switch (basis) {\n case 'extracted':\n return (\n <svg {...shared}>\n <path d=\"M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z\" />\n <polyline points=\"14 3 14 8 19 8\" />\n <line x1=\"9\" y1=\"13\" x2=\"15\" y2=\"13\" />\n </svg>\n )\n case 'entered':\n return (\n <svg {...shared}>\n <path d=\"M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2\" />\n <circle cx=\"12\" cy=\"7\" r=\"4\" />\n </svg>\n )\n case 'computed':\n return (\n <svg {...shared}>\n <line x1=\"4\" y1=\"9\" x2=\"20\" y2=\"9\" />\n <line x1=\"4\" y1=\"15\" x2=\"20\" y2=\"15\" />\n <line x1=\"10\" y1=\"3\" x2=\"8\" y2=\"21\" />\n <line x1=\"16\" y1=\"3\" x2=\"14\" y2=\"21\" />\n </svg>\n )\n case 'asserted':\n return (\n <svg {...shared}>\n <path d=\"M12 3v3M12 18v3M3 12h3M18 12h3M5.6 5.6l2.1 2.1M16.3 16.3l2.1 2.1M18.4 5.6l-2.1 2.1M7.7 16.3l-2.1 2.1\" />\n <circle cx=\"12\" cy=\"12\" r=\"3.2\" />\n </svg>\n )\n }\n}\n\n// ── open panels ───────────────────────────────────────────────────────────\n\ninterface OpenPanel {\n root: HTMLElement\n close: () => void\n}\n\n// Two trails open beside each other leave the reader matching a quote to a\n// number by position, which is the failure a marker adjacent to its own value\n// exists to remove.\nconst openPanels = new Set<OpenPanel>()\n\n/**\n * Close every open trail this one does not sit INSIDE. A composed input's panel\n * is nested in its parent's, so closing \"everything else\" would close the row\n * the reader just drilled into — an ancestor stays open.\n *\n * Only a reader opening a trail closes another one. A `defaultOpen` surface has\n * deliberately asked for several trails at once, and mount order is not a\n * decision anyone made.\n */\nfunction closeTrailsOutside(root: HTMLElement | null): void {\n for (const other of Array.from(openPanels)) {\n if (root === null || !other.root.contains(root)) other.close()\n }\n}\n\n// ── props ─────────────────────────────────────────────────────────────────\n\n/** Properties for one provenanced value and its disclosure. */\nexport interface ProvenanceValueProps {\n /** The value and where it came from. */\n record: ProvenanceRecord\n /**\n * Open one source in the product's own way (a document pane at the right\n * page, a drawer, a route). Takes precedence over `href` when both are\n * present, because a product that routes wants its router — a source with\n * neither is named but not openable, which is a legitimate state and is\n * rendered as such.\n */\n onOpenSource?: (source: ProvenanceSource, record: ProvenanceRecord) => void\n /** Retry resolving an `unavailable` source. Absent → the failure is stated\n * without a retry control, never swallowed. */\n onRetrySource?: (source: ProvenanceSource, record: ProvenanceRecord) => void\n /** Where this product draws its confidence lines. */\n confidencePolicy?: ProvenanceConfidencePolicy\n /**\n * How many levels of composed inputs stay expandable. Past it an input still\n * renders its value, its basis and its origin sentence — it just stops\n * carrying its own disclosure, so a deep tree cannot run away and a record\n * that reaches itself cannot recurse forever. Default 2.\n */\n maxDepth?: number\n /** Open the disclosure on first render — for a review surface where the\n * trail is the point. Several of these coexist: only a reader opening a\n * trail closes another one. */\n defaultOpen?: boolean\n /** What an empty `display` renders as. A blank cell is the defect this\n * component exists to remove. */\n missingValueLabel?: string\n className?: string\n}\n\nconst DEFAULT_MISSING_VALUE_LABEL = 'No value recorded'\n\n// ── source rows ───────────────────────────────────────────────────────────\n\nfunction SourceRow({\n source,\n record,\n onOpenSource,\n onRetrySource,\n}: {\n source: ProvenanceSource\n record: ProvenanceRecord\n onOpenSource?: (source: ProvenanceSource, record: ProvenanceRecord) => void\n onRetrySource?: (source: ProvenanceSource, record: ProvenanceRecord) => void\n}) {\n const status = source.status ?? 'ready'\n const statusLine = describeProvenanceSourceStatus(source)\n const openable = status === 'ready' && (onOpenSource !== undefined || source.href !== undefined)\n\n return (\n <li className=\"rounded-md border border-card-edge bg-card px-2.5 py-2\">\n <div className=\"flex flex-wrap items-baseline gap-x-2 gap-y-1\">\n <span className=\"text-sm font-medium text-foreground\">{source.label}</span>\n {source.locator && <span className=\"text-xs text-muted-foreground\">{source.locator}</span>}\n {openable &&\n (onOpenSource ? (\n <button\n type=\"button\"\n onClick={() => onOpenSource(source, record)}\n className=\"rounded px-1 text-xs font-medium text-primary underline-offset-2 hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n Open {source.label}\n </button>\n ) : (\n <a\n href={source.href}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"rounded px-1 text-xs font-medium text-primary underline-offset-2 hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n Open {source.label}\n </a>\n ))}\n </div>\n {source.quote && (\n <blockquote className=\"mt-1 border-l-2 border-primary/50 pl-2 text-[12px] italic leading-snug text-foreground\">\n “{source.quote}”\n </blockquote>\n )}\n {statusLine && (\n // `role=\"status\"` and not `alert`: the reader opened this panel, so the\n // update is theirs to read, not an interruption. Either way it is TEXT\n // — a spinner alone and a greyed row alone both render as \"nothing\n // here\".\n <p\n role=\"status\"\n className={`mt-1 text-xs ${status === 'unavailable' ? 'text-destructive' : 'text-muted-foreground'}`}\n >\n {statusLine}\n {status === 'unavailable' && onRetrySource && (\n <button\n type=\"button\"\n onClick={() => onRetrySource(source, record)}\n className=\"ml-2 rounded border border-border px-1.5 py-0.5 text-xs font-medium text-foreground hover:bg-accent focus:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n Try again\n </button>\n )}\n </p>\n )}\n </li>\n )\n}\n\n// ── the value ─────────────────────────────────────────────────────────────\n\n/**\n * A value, its origin marker, and the disclosure that shows where it came\n * from. The marker states the basis in words and, whenever there is something\n * to do about the value, the next move — so the standing is legible at rest\n * and does not depend on anyone opening the panel.\n */\nexport function ProvenanceValue({\n record,\n onOpenSource,\n onRetrySource,\n confidencePolicy,\n maxDepth = 2,\n defaultOpen = false,\n missingValueLabel = DEFAULT_MISSING_VALUE_LABEL,\n className,\n}: ProvenanceValueProps) {\n const [open, setOpen] = useState(defaultOpen)\n const triggerRef = useRef<HTMLButtonElement | null>(null)\n const rootRef = useRef<HTMLDivElement | null>(null)\n const panelId = useId()\n\n const standing = rollUpProvenanceStanding(record, confidencePolicy)\n const basisMeta = provenanceBasisMeta(record.basis)\n const standingMeta = provenanceStandingMeta(standing)\n const gaps = provenanceGaps(record)\n const loading = loadingProvenanceSources(record)\n const sources = record.sources ?? []\n const inputs = record.inputs ?? []\n const hasValue = record.display.trim() !== ''\n\n // Escape closes the value the focus is inside and hands focus back to its own\n // trigger; `stopPropagation` keeps a nested input from also closing the\n // parent that contains it.\n const onKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.key !== 'Escape' || !open) return\n event.stopPropagation()\n setOpen(false)\n triggerRef.current?.focus()\n },\n [open],\n )\n\n // Opening is a reader's decision, so it is the one moment another trail is\n // closed — see `closeTrailsOutside`.\n const onToggle = useCallback(() => {\n if (!open) closeTrailsOutside(rootRef.current)\n setOpen(!open)\n }, [open])\n\n // A click landing anywhere else dismisses the trail. Registered in the\n // capture phase so a product's own click handler under the pointer cannot\n // swallow it, and paired with the registry above so the two dismissals agree.\n useEffect(() => {\n const root = rootRef.current\n if (!open || root === null) return\n\n const entry: OpenPanel = { root, close: () => setOpen(false) }\n openPanels.add(entry)\n\n const onPointerDown = (event: Event) => {\n const target = event.target\n if (target instanceof Node && root.contains(target)) return\n setOpen(false)\n }\n document.addEventListener('mousedown', onPointerDown, true)\n document.addEventListener('touchstart', onPointerDown, true)\n\n return () => {\n openPanels.delete(entry)\n document.removeEventListener('mousedown', onPointerDown, true)\n document.removeEventListener('touchstart', onPointerDown, true)\n }\n }, [open])\n\n const summary: ReactNode = (\n <span className=\"inline-flex flex-wrap items-baseline gap-x-1.5\">\n {record.label && <span className=\"text-xs text-muted-foreground\">{record.label}</span>}\n <span className={hasValue ? 'text-sm text-foreground' : 'text-sm italic text-muted-foreground'}>\n {hasValue ? record.display : missingValueLabel}\n </span>\n </span>\n )\n\n // Depth exhausted: still the value, still the basis, still the origin\n // sentence — only the disclosure goes. A truncated tree must not silently\n // become a bare number.\n if (maxDepth <= 0) {\n return (\n <div\n className={`inline-block max-w-full ${className ?? ''}`}\n data-provenance-basis={record.basis}\n data-provenance-standing={standing}\n >\n {summary}\n <span\n className={`ml-1.5 inline-flex items-center gap-1 rounded-full border px-1.5 py-0.5 text-xs font-medium ${BASIS_TONES[record.basis]}`}\n >\n <BasisGlyph basis={record.basis} className=\"h-3 w-3\" />\n {basisMeta.label}\n </span>\n <span className=\"mt-0.5 block text-xs text-muted-foreground\">{describeProvenance(record)}</span>\n </div>\n )\n }\n\n return (\n <div\n ref={rootRef}\n className={`inline-block max-w-full ${className ?? ''}`}\n onKeyDown={onKeyDown}\n data-provenance-basis={record.basis}\n data-provenance-standing={standing}\n >\n <span className=\"inline-flex flex-wrap items-baseline gap-1.5\">\n {summary}\n <button\n ref={triggerRef}\n type=\"button\"\n onClick={onToggle}\n aria-expanded={open}\n aria-controls={panelId}\n aria-label={provenanceTriggerLabel(record, standing)}\n className={`inline-flex items-center gap-1 rounded-full border px-1.5 py-0.5 text-xs font-medium transition hover:brightness-105 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring ${BASIS_TONES[record.basis]}`}\n >\n <BasisGlyph basis={record.basis} className=\"h-3 w-3\" />\n <span aria-hidden>{basisMeta.label}</span>\n </button>\n {standing !== 'settled' && (\n // Legible at rest: what to do about the value does not wait for\n // someone to open the panel. `aria-hidden` because the trigger's\n // accessible name already carries it — this is the visual half.\n <span aria-hidden className={`text-xs font-medium ${STANDING_TONES[standing]}`}>\n {standingMeta.label}\n </span>\n )}\n </span>\n\n {open && (\n <div\n id={panelId}\n role=\"group\"\n aria-label={provenanceTriggerLabel(record, standing)}\n className=\"mt-1.5 w-full min-w-0 space-y-2 rounded-lg border border-border bg-card px-3 py-2.5 text-left\"\n >\n <div>\n <p className=\"text-[12px] leading-snug text-foreground\">{describeProvenance(record)}</p>\n <p className={`mt-0.5 text-xs leading-snug ${STANDING_TONES[standing]}`}>\n {standingMeta.label} — {provenanceNextMove(record, standing, confidencePolicy)}\n </p>\n {basisMeta.checkableAgainst === null && (\n <p className=\"mt-0.5 text-xs leading-snug text-muted-foreground\">\n {basisMeta.meaning} There is nothing outside the model to check it against.\n </p>\n )}\n </div>\n\n {sources.length > 0 && (\n <ul className=\"space-y-1.5\">\n {sources.map((source, index) => (\n <SourceRow\n key={`${source.label}-${source.locator ?? ''}-${index}`}\n source={source}\n record={record}\n onOpenSource={onOpenSource}\n onRetrySource={onRetrySource}\n />\n ))}\n </ul>\n )}\n\n {gaps\n .filter((gap) => gap.kind !== 'unavailable-source')\n .map((gap) => (\n // An unavailable source already states itself on its own row; a\n // structural gap has no row to state it, so it gets one here.\n <p key={gap.kind} className=\"rounded-md bg-destructive/10 px-2 py-1.5 text-xs leading-snug text-destructive\">\n {gap.message}\n </p>\n ))}\n\n {loading.length > 0 && sources.length === 0 && (\n <p role=\"status\" className=\"text-xs text-muted-foreground\">\n Looking up where this came from…\n </p>\n )}\n\n {inputs.length > 0 && (\n <div className=\"border-t border-border pt-2\">\n <p className=\"text-xs font-medium uppercase tracking-[0.05em] text-muted-foreground\">\n {record.derivation ? `Computed from ${record.derivation}` : 'Computed from'}\n </p>\n <ul className=\"mt-1.5 space-y-1.5\">\n {inputs.map((input, index) => (\n <li key={`${input.label ?? input.display}-${index}`}>\n <ProvenanceValue\n record={input}\n onOpenSource={onOpenSource}\n onRetrySource={onRetrySource}\n confidencePolicy={confidencePolicy}\n maxDepth={maxDepth - 1}\n missingValueLabel={missingValueLabel}\n />\n </li>\n ))}\n </ul>\n </div>\n )}\n </div>\n )}\n </div>\n )\n}\n\n/** Properties for the basis legend. */\nexport interface ProvenanceLegendProps {\n /** Only the bases present on screen. Passing all four when only two appear\n * teaches distinctions the reader cannot use. */\n bases: readonly ProvenanceBasis[]\n className?: string\n}\n\n/** The marker key for a surface that renders several bases at once — a review\n * pane, a grid, a return. Each row is the same glyph, tone and words the\n * markers use, plus what the basis MEANS. */\nexport function ProvenanceLegend({ bases, className }: ProvenanceLegendProps) {\n if (bases.length === 0) return null\n return (\n <ul className={`flex flex-wrap items-center gap-x-3 gap-y-1.5 ${className ?? ''}`}>\n {bases.map((basis) => {\n const meta = provenanceBasisMeta(basis)\n return (\n <li key={basis} className=\"flex items-center gap-1.5 text-xs text-muted-foreground\">\n <span\n className={`inline-flex items-center gap-1 rounded-full border px-1.5 py-0.5 text-xs font-medium ${BASIS_TONES[basis]}`}\n >\n <BasisGlyph basis={basis} className=\"h-3 w-3\" />\n {meta.label}\n </span>\n <span>{meta.meaning}</span>\n </li>\n )\n })}\n </ul>\n )\n}\n","/**\n * The pure half of the provenance affordance: what kind of claim a value is,\n * what a reader should DO about it, and what has to be SAID when its origin\n * cannot be shown.\n *\n * Zero React, zero DOM — a loader or a worker can decide a value's standing\n * before it reaches a screen, and `./provenance`'s `ProvenanceValue` renders\n * exactly what these functions decide.\n *\n * The distinction the module exists to hold: a person typing a number, a\n * document carrying it, a formula producing it, and a model claiming it are\n * four different kinds of evidence. Rendered as one grey caption they are\n * indistinguishable, which is how an unverified model assertion reads to a\n * reviewer as a transcribed fact.\n *\n * Every domain word is a caller parameter: no field names, no document kinds,\n * and no confidence policy beyond a default the product overrides.\n */\n\n/**\n * How a value came to exist. Four kinds, never interchangeable:\n *\n * - `extracted` — read out of a document or message the product can open.\n * - `entered` — a person typed or confirmed it.\n * - `computed` — produced from other values, each carrying its own provenance.\n * - `asserted` — the agent stated it, with nothing outside the model behind it.\n */\nexport type ProvenanceBasis = 'extracted' | 'entered' | 'computed' | 'asserted'\n\n/** Every basis, in the order a legend should list them. */\nexport const PROVENANCE_BASES: readonly ProvenanceBasis[] = ['extracted', 'entered', 'computed', 'asserted']\n\n/** The words one basis is rendered and announced with. */\nexport interface ProvenanceBasisMeta {\n /** Marker text next to the value — short, and never only a colour. */\n label: string\n /** One plain sentence naming what kind of claim this is. */\n meaning: string\n /** What a reader can hold the value against, or `null` when nothing outside\n * the model can. `null` is what makes an `asserted` value uncertifiable at\n * any confidence. */\n checkableAgainst: string | null\n}\n\nconst BASIS_META: Record<ProvenanceBasis, ProvenanceBasisMeta> = {\n extracted: {\n label: 'From document',\n meaning: 'Read out of a source document.',\n checkableAgainst: 'the document it was read from',\n },\n entered: {\n label: 'Entered by a person',\n meaning: 'A person typed or confirmed this value.',\n checkableAgainst: 'the person who entered it',\n },\n computed: {\n label: 'Computed',\n meaning: 'Produced from other values.',\n checkableAgainst: 'the values it was computed from',\n },\n asserted: {\n label: 'Agent, unverified',\n meaning: 'The agent stated this. No source was recorded behind it.',\n checkableAgainst: null,\n },\n}\n\n/** Words for one basis. */\nexport function provenanceBasisMeta(basis: ProvenanceBasis): ProvenanceBasisMeta {\n return BASIS_META[basis]\n}\n\n/** Whether a source could be resolved. `ready` is the default for a source that\n * says nothing. */\nexport type ProvenanceSourceStatus = 'ready' | 'loading' | 'unavailable'\n\n/** One thing a value came from. A `label` is mandatory because an unnamed\n * source is the same as no source. */\nexport interface ProvenanceSource {\n /** What the source IS, in the reader's words: \"Form W-2 (Acme Corp)\",\n * \"Dana Whitfield\", \"Engagement letter\". */\n label: string\n /** The text in the source that carries the value. */\n quote?: string\n /** Position inside the source — a page, a line, a span, a timestamp. The\n * caller's words; nothing here parses it. */\n locator?: string\n /** Click-through target. Absent → the source is named but not openable. */\n href?: string\n /** Defaults to `ready`. */\n status?: ProvenanceSourceStatus\n /** Why an `unavailable` source cannot be opened, in one sentence. */\n unavailableReason?: string\n}\n\n/**\n * What the reader should DO about a value — the only form confidence takes on\n * screen. \"89% confidence\" names no next move; these three do.\n *\n * - `settled` — nothing to do.\n * - `check` — open the source and confirm before relying on it.\n * - `confirm` — a person has to confirm the value before it is used.\n */\nexport type ProvenanceStanding = 'settled' | 'check' | 'confirm'\n\n/** The words one standing is rendered and announced with. */\nexport interface ProvenanceStandingMeta {\n /** Short state label. */\n label: string\n /** The next move, as a sentence a person can follow. */\n action: string\n}\n\nconst STANDING_META: Record<ProvenanceStanding, ProvenanceStandingMeta> = {\n settled: { label: 'Traced', action: 'Nothing to check — this value can be traced to where it came from.' },\n check: { label: 'Check the source', action: 'Open the source and confirm this value before you rely on it.' },\n confirm: { label: 'Needs a person', action: 'Someone has to confirm this value before it is used.' },\n}\n\n/** Words for one standing. */\nexport function provenanceStandingMeta(standing: ProvenanceStanding): ProvenanceStandingMeta {\n return STANDING_META[standing]\n}\n\nconst STANDING_SEVERITY: Record<ProvenanceStanding, number> = { settled: 0, check: 1, confirm: 2 }\n\n/** The weaker of two standings — `confirm` beats `check` beats `settled`. */\nexport function weakerProvenanceStanding(a: ProvenanceStanding, b: ProvenanceStanding): ProvenanceStanding {\n return STANDING_SEVERITY[a] >= STANDING_SEVERITY[b] ? a : b\n}\n\n/**\n * Where a product draws its confidence lines. These are a POLICY, not a truth:\n * a number a model reports about itself means different things per surface, so\n * the thresholds are a parameter and the number itself never reaches the screen.\n */\nexport interface ProvenanceConfidencePolicy {\n /** At or above this, a value is `settled`. */\n settledAtOrAbove: number\n /** At or above this (and below `settledAtOrAbove`), a value is `check`.\n * Below it, `confirm`. */\n checkAtOrAbove: number\n}\n\n/** The starting policy. Products with a different tolerance pass their own. */\nexport const DEFAULT_PROVENANCE_CONFIDENCE_POLICY: ProvenanceConfidencePolicy = {\n settledAtOrAbove: 0.9,\n checkAtOrAbove: 0.6,\n}\n\n/** A value, where it came from, and — when it was computed — the provenanced\n * values it came from. The `inputs` field is what makes the shape compose:\n * a computed value's provenance IS its inputs. */\nexport interface ProvenanceRecord {\n /** The value as the reader should see it, already formatted. An empty string\n * is a missing value and renders as one, never as blank space. */\n display: string\n /** What the value IS (\"Wages\", \"Filing deadline\"). Optional at the top level,\n * where the surface around it usually says; rendered for every composed\n * input, where nothing else names them. */\n label?: string\n basis: ProvenanceBasis\n /** Where it came from. An `extracted` value without one is a gap, not a\n * detail. */\n sources?: readonly ProvenanceSource[]\n /** The provenanced values a `computed` value was produced from. */\n inputs?: readonly ProvenanceRecord[]\n /** How the inputs combine, in the caller's words (\"wages + interest\"). */\n derivation?: string\n /** 0–1. Never rendered as a number — it selects a standing. */\n confidence?: number\n /** Overrides the standing confidence and basis would produce — a reviewer\n * approved it, a gate failed. It can only make a value WEAKER: the\n * structural floors below still apply, so a product cannot mark a value\n * settled that has no origin on file. */\n standing?: ProvenanceStanding\n}\n\n/** The standing a bare confidence maps to under a policy. */\nexport function standingFromConfidence(\n confidence: number,\n policy: ProvenanceConfidencePolicy = DEFAULT_PROVENANCE_CONFIDENCE_POLICY,\n): ProvenanceStanding {\n if (confidence >= policy.settledAtOrAbove) return 'settled'\n if (confidence >= policy.checkAtOrAbove) return 'check'\n return 'confirm'\n}\n\n/** The standing a basis implies before any confidence or gap is considered. */\nfunction standingFromBasis(basis: ProvenanceBasis): ProvenanceStanding {\n // Only `asserted` starts weak: the other three name something outside the\n // model — a document, a person, a computation — that a reader can go check.\n return basis === 'asserted' ? 'check' : 'settled'\n}\n\n/** Something missing that the reader has to be TOLD about, because the value\n * renders either way and a bare number reads as a fact. */\nexport type ProvenanceGapKind = 'no-source' | 'no-inputs' | 'unavailable-source'\n\n/** One stated gap. `message` is rendered verbatim. */\nexport interface ProvenanceGap {\n kind: ProvenanceGapKind\n message: string\n /** The source that could not be resolved (`unavailable-source` only). */\n source?: ProvenanceSource\n}\n\n/** The sentence for a source that is not `ready`, or `null` when it is. One\n * source of this copy, so the row and the gap list never disagree. */\nexport function describeProvenanceSourceStatus(source: ProvenanceSource): string | null {\n if (source.status === 'loading') return `Looking up ${source.label}…`\n if (source.status === 'unavailable') {\n return source.unavailableReason\n ? `${source.label} could not be opened — ${source.unavailableReason}`\n : `${source.label} could not be opened.`\n }\n return null\n}\n\n/**\n * What this record cannot show, at its own level. Inputs are not walked: every\n * composed input renders its own gaps next to its own value, where a reader can\n * act on them.\n */\nexport function provenanceGaps(record: ProvenanceRecord): ProvenanceGap[] {\n const gaps: ProvenanceGap[] = []\n const sources = record.sources ?? []\n if (record.basis === 'extracted' && sources.length === 0) {\n gaps.push({\n kind: 'no-source',\n message: 'Read from a document, but no document is on file — nothing here shows where this came from.',\n })\n }\n if (record.basis === 'computed' && (record.inputs ?? []).length === 0) {\n gaps.push({\n kind: 'no-inputs',\n message: 'Computed, but the values it was computed from are not recorded.',\n })\n }\n for (const source of sources) {\n if (source.status !== 'unavailable') continue\n gaps.push({\n kind: 'unavailable-source',\n message: describeProvenanceSourceStatus(source) ?? `${source.label} could not be opened.`,\n source,\n })\n }\n return gaps\n}\n\n/** The sources still resolving — rendered as their own state, never as an\n * absence. A load in flight is not a missing source. */\nexport function loadingProvenanceSources(record: ProvenanceRecord): ProvenanceSource[] {\n return (record.sources ?? []).filter((source) => source.status === 'loading')\n}\n\n/**\n * This record's own standing, ignoring its inputs.\n *\n * The order is: start from the explicit standing, else from confidence, else\n * from the basis — then apply every structural floor, taking the WEAKEST. The\n * floors are what a caller cannot talk its way out of:\n *\n * - an `asserted` value never reaches `settled` (a model's own confidence\n * cannot certify the model's claim — there is nothing to check it against),\n * - a `no-source` / `no-inputs` gap forces `confirm` (a value dressed as\n * evidence with no evidence behind it is worse than an open guess),\n * - an unopenable source forces `check` (the value may be right; the reader\n * just cannot confirm it).\n */\nexport function resolveProvenanceStanding(\n record: ProvenanceRecord,\n policy: ProvenanceConfidencePolicy = DEFAULT_PROVENANCE_CONFIDENCE_POLICY,\n): ProvenanceStanding {\n let standing =\n record.standing ??\n (record.confidence === undefined ? standingFromBasis(record.basis) : standingFromConfidence(record.confidence, policy))\n\n if (record.basis === 'asserted') standing = weakerProvenanceStanding(standing, 'check')\n for (const gap of provenanceGaps(record)) {\n standing = weakerProvenanceStanding(standing, gap.kind === 'unavailable-source' ? 'check' : 'confirm')\n }\n return standing\n}\n\n/**\n * The standing a reader should see: this record's own, weakened by every value\n * it was computed from, however deep.\n *\n * A total is only as trustworthy as the weakest number in it. Rendering the\n * parent's own standing instead is how an exact sum of one document figure and\n * one model guess presents as traced.\n *\n * Cycle-safe: a record reachable from itself is counted once.\n */\nexport function rollUpProvenanceStanding(\n record: ProvenanceRecord,\n policy: ProvenanceConfidencePolicy = DEFAULT_PROVENANCE_CONFIDENCE_POLICY,\n seen: Set<ProvenanceRecord> = new Set(),\n): ProvenanceStanding {\n if (seen.has(record)) return 'settled'\n seen.add(record)\n let standing = resolveProvenanceStanding(record, policy)\n for (const input of record.inputs ?? []) {\n standing = weakerProvenanceStanding(standing, rollUpProvenanceStanding(input, policy, seen))\n }\n return standing\n}\n\nfunction sourcePhrase(source: ProvenanceSource): string {\n return source.locator ? `${source.label}, ${source.locator}` : source.label\n}\n\nfunction inputPhrase(input: ProvenanceRecord): string {\n return input.label ?? input.display\n}\n\n/**\n * One plain sentence naming where the value came from — the panel's first line\n * and part of what a screen reader announces. It states the absence when there\n * is one, so no code path produces silence.\n */\nexport function describeProvenance(record: ProvenanceRecord): string {\n const sources = record.sources ?? []\n const first = sources[0]\n const more = sources.length > 1 ? ` and ${sources.length - 1} more source${sources.length > 2 ? 's' : ''}` : ''\n\n switch (record.basis) {\n case 'extracted':\n return first ? `Read from ${sourcePhrase(first)}${more}.` : 'Read from a document, but no document is on file.'\n case 'entered':\n return first ? `Entered by ${sourcePhrase(first)}.` : 'Entered by a person.'\n case 'computed': {\n const inputs = record.inputs ?? []\n if (record.derivation) return `Computed from ${record.derivation}.`\n if (inputs.length === 0) return 'Computed, but the values it was computed from are not recorded.'\n return `Computed from ${inputs.map(inputPhrase).join(', ')}.`\n }\n case 'asserted':\n return first\n ? `Stated by the agent, pointing at ${sourcePhrase(first)} — not verified against it.`\n : 'Stated by the agent, with no source to check it against.'\n }\n}\n\n/**\n * The move THIS value's reader can actually make.\n *\n * `provenanceStandingMeta().action` is the generic sentence for a standing;\n * this is the one that accounts for what is on file. \"Open the source and\n * confirm it\" is a dead instruction on a value that has no source — an action\n * a reader cannot perform is the same defect as no action at all.\n */\nexport function provenanceNextMove(\n record: ProvenanceRecord,\n standing: ProvenanceStanding,\n policy: ProvenanceConfidencePolicy = DEFAULT_PROVENANCE_CONFIDENCE_POLICY,\n): string {\n const generic = provenanceStandingMeta(standing).action\n if (standing === 'settled') return generic\n\n const gaps = provenanceGaps(record)\n if (gaps.some((gap) => gap.kind === 'no-source')) {\n return 'Nobody recorded where this came from. Confirm the value and record its source before it is used.'\n }\n if (gaps.some((gap) => gap.kind === 'no-inputs')) {\n return 'Nobody recorded what this was computed from. Confirm the value and record its inputs before it is used.'\n }\n if (gaps.some((gap) => gap.kind === 'unavailable-source')) {\n return 'The source could not be opened. Try again, or confirm this value another way before you rely on it.'\n }\n if (record.basis === 'asserted' && (record.sources ?? []).length === 0) {\n return 'The agent gave no source. Check this against the real document before you rely on it.'\n }\n // Weakened only by what it was computed from: the move is one level down,\n // not on this row.\n if (record.basis === 'computed' && (record.inputs ?? []).length > 0 && resolveProvenanceStanding(record, policy) === 'settled') {\n return 'One of the values this was computed from still needs checking — open the marked ones below.'\n }\n return generic\n}\n\n/**\n * The accessible name of the disclosure control: the value, how it came to\n * exist, and — unless there is nothing to do — the next move. This is the\n * whole affordance for someone who never sees the colour.\n */\nexport function provenanceTriggerLabel(record: ProvenanceRecord, standing: ProvenanceStanding): string {\n const value = record.display.trim() === '' ? 'this missing value' : `“${record.display}”`\n const named = record.label ? `${record.label} ${value}` : value\n const basis = provenanceBasisMeta(record.basis).label\n const next = standing === 'settled' ? '' : ` ${provenanceStandingMeta(standing).label}.`\n return `Where ${named} came from — ${basis}.${next}`\n}\n","/**\n * `SeatPaywall` — the shared \"unlock this product\" screen every agent app\n * shows when a user has no active seat and has spent past the free tier.\n *\n * Copy contract (design §6.8): the included monthly AI usage is framed as a\n * BENEFIT the buyer receives — never the ratio, never the word \"margin\", never\n * \"we debit 50%\". Surface the allowance, hide the economics.\n *\n * Styling contract matches the rest of `web-react`: Tailwind classes over the\n * shared design tokens (`bg-card`, `border-border`, `text-muted-foreground`,\n * `bg-primary`, …); glyphs are inline SVGs; no icon or UI library.\n */\n\nimport type { ReactNode } from 'react'\n\nimport type { ProductSeatOffer } from '../platform/billing'\nimport { usePending } from './controls'\n\nexport interface SeatPaywallProps {\n /** Human product name shown in the headline, e.g. \"Creative\". */\n product: string\n /** Fired when the user clicks the unlock CTA — route them to checkout. When\n * it returns a promise the button shows a pending state and ignores repeat\n * clicks until it settles (no double-charge on a slow checkout open). */\n onCheckout: () => void | Promise<void>\n /** Monthly seat price in whole dollars. Default 100. */\n priceUsd?: number\n /** Included monthly AI usage in whole dollars. Default 50. */\n includedUsageUsd?: number\n /** Platform catalog terms. When present, these override the legacy dollar\n * props and show any introductory period without product-local price copy. */\n offer?: ProductSeatOffer\n /** Optional one-line value prop under the headline. */\n tagline?: string\n /** CTA label. Default \"Continue to checkout\". */\n ctaLabel?: string\n /** Value-prop bullets. Default = product/usage-derived only; pass your own to\n * supply product-specific value props (the shell bakes no GTM copy). */\n benefits?: ReactNode[]\n /** Optional fine print under the CTA (e.g. \"Cancel anytime.\"). Omitted by default. */\n footnote?: ReactNode\n}\n\nfunction usd(cents: number): string {\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: 'USD',\n minimumFractionDigits: cents % 100 === 0 ? 0 : 2,\n maximumFractionDigits: cents % 100 === 0 ? 0 : 2,\n }).format(cents / 100)\n}\n\nfunction CheckGlyph(): ReactNode {\n return (\n <svg\n className=\"h-4 w-4 shrink-0 text-primary\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n >\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n )\n}\n\nfunction Benefit({ children }: { children: ReactNode }): ReactNode {\n return (\n <li className=\"flex items-start gap-2.5 text-sm text-foreground\">\n <span className=\"mt-0.5\">\n <CheckGlyph />\n </span>\n <span>{children}</span>\n </li>\n )\n}\n\n/**\n * Centered card paywall. The price line reads\n * \"$100/mo · includes $50/mo of AI usage\" so the included allowance anchors the\n * value without ever exposing the ratio — and says it ONCE: the default\n * benefits don't restate the usage line the subline already carries, and the\n * CTA is the next step (\"Continue to checkout\"), not a third repeat of the\n * eyebrow + headline's \"Unlock {product}\".\n */\nexport function SeatPaywall({\n product,\n onCheckout,\n priceUsd = 100,\n includedUsageUsd = 50,\n offer,\n tagline,\n ctaLabel,\n benefits,\n footnote,\n}: SeatPaywallProps): ReactNode {\n const { pending, run } = usePending()\n const recurringPrice = offer ? usd(offer.recurring.priceCents) : `$${priceUsd}`\n const recurringUsage = offer\n ? usd(offer.recurring.includedCreditsCents)\n : `$${includedUsageUsd}`\n const introductory = offer?.introductory ?? null\n return (\n <div className=\"flex min-h-[60vh] w-full items-center justify-center p-6\">\n <div className=\"w-full max-w-md rounded-2xl border border-card-edge bg-card p-8\">\n <p className=\"text-xs font-semibold uppercase tracking-[0.05em] text-muted-foreground\">\n {product}\n </p>\n <h1 className=\"mt-2 text-2xl font-semibold tracking-tight text-foreground\">\n Unlock {product}\n </h1>\n {tagline && <p className=\"mt-2 text-sm text-muted-foreground\">{tagline}</p>}\n\n {introductory ? (\n <>\n <div className=\"mt-6 flex items-baseline gap-1.5\">\n <span className=\"text-3xl font-semibold text-foreground\">\n {usd(introductory.priceCents)}\n </span>\n <span className=\"text-sm text-muted-foreground\">first month</span>\n </div>\n <p className=\"mt-1 text-sm text-muted-foreground\">\n Includes {usd(introductory.includedCreditsCents)} of AI usage in your first month\n </p>\n <p className=\"mt-1 text-sm text-muted-foreground\">\n Then {recurringPrice}/mo · includes {recurringUsage}/mo of AI usage\n </p>\n </>\n ) : (\n <>\n <div className=\"mt-6 flex items-baseline gap-1.5\">\n <span className=\"text-3xl font-semibold text-foreground\">{recurringPrice}</span>\n <span className=\"text-sm text-muted-foreground\">/mo</span>\n </div>\n <p className=\"mt-1 text-sm text-muted-foreground\">\n Includes {recurringUsage}/mo of AI usage\n </p>\n </>\n )}\n\n <ul className=\"mt-6 space-y-2.5\">\n {/* Default benefits leave the usage allowance to the price subline,\n which always states it — listing it again here read the same\n number three times on one card. */}\n {(benefits ?? [\n `Full access to ${product}`,\n ]).map((benefit, i) => (\n <Benefit key={i}>{benefit}</Benefit>\n ))}\n </ul>\n\n <button\n type=\"button\"\n disabled={pending}\n onClick={() => run(onCheckout)}\n className=\"mt-7 inline-flex w-full items-center justify-center rounded-xl bg-primary px-4 py-2.5 text-sm font-medium text-primary-foreground transition hover:bg-primary/90 disabled:cursor-not-allowed disabled:opacity-70\"\n >\n {pending ? 'Opening checkout…' : ctaLabel ?? 'Continue to checkout'}\n </button>\n {footnote && (\n <p className=\"mt-3 text-center text-xs text-muted-foreground/70\">\n {footnote}\n </p>\n )}\n </div>\n </div>\n )\n}\n","/**\n * The rendered half of the session shell: the history view behind the rail's\n * session list, and the rename/delete dialogs both surfaces drive.\n *\n * Storage is a seam, not a dependency. Every product keeps sessions somewhere\n * different (gtm threads, tax sessions, legal matters), so this takes a\n * `fetchPage` data port and injected mutations — the same shape\n * `AgentActivityPanel` (`fetchActivity`) and `ReviewQueuePanel` (`fetchQueue`)\n * already use, rather than a fifth pattern.\n *\n * sandbox-ui free on purpose: `/web-react` must not force the optional peer, so\n * these render on the shared design tokens like the rest of the subpath. The\n * pure logic (nav items, routing, cookies, merging) lives in `/session-shell`,\n * which a server loader can import without pulling React.\n */\n\nimport {\n type ReactNode,\n type RefObject,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react'\n\nimport {\n type SessionPage,\n type SessionRailAction,\n type SessionSort,\n type SessionSummary,\n mergeSessionPages,\n sessionLabel,\n UNTITLED_SESSION_LABEL,\n} from '../session-shell/index'\nimport { OVERLAY_SHADOW, PopoverSurface, usePopover } from './controls'\n\n// ---------------------------------------------------------------------------\n// useInfiniteScroll\n// ---------------------------------------------------------------------------\n\nexport interface UseInfiniteScrollOptions {\n /** Only fire `onLoadMore` while true (a next page exists, none in flight). */\n enabled: boolean\n /** Scroll container the sentinel lives in. Defaults to the viewport. */\n root?: RefObject<HTMLElement | null>\n /** Prefetch distance before the sentinel is actually reached. */\n rootMargin?: string\n}\n\nfunction rethrowAsync(error: unknown) {\n queueMicrotask(() => {\n throw error\n })\n}\n\n/**\n * Fires `onLoadMore` when a sentinel element scrolls into view. Returns a ref\n * callback for that sentinel (typically the last element in a list).\n *\n * The observer is re-created whenever `enabled` flips, so a short first page\n * that leaves the sentinel on-screen keeps loading: when a load finishes and\n * `enabled` returns to true, the fresh observer re-reads the current\n * intersection state and fires again until the sentinel is pushed off-screen.\n */\nexport function useInfiniteScroll(\n onLoadMore: () => void,\n { enabled, root, rootMargin = '300px' }: UseInfiniteScrollOptions,\n): (node: HTMLElement | null) => void {\n const [sentinel, setSentinel] = useState<HTMLElement | null>(null)\n const sentinelRef = useCallback((node: HTMLElement | null) => setSentinel(node), [])\n const onLoadMoreRef = useRef(onLoadMore)\n\n useEffect(() => {\n onLoadMoreRef.current = onLoadMore\n }, [onLoadMore])\n\n useEffect(() => {\n if (!sentinel || !enabled) return\n if (typeof IntersectionObserver === 'undefined') return\n const observer = new IntersectionObserver(\n (entries) => {\n if (!entries.some((entry) => entry.isIntersecting)) return\n try {\n onLoadMoreRef.current()\n } catch (error) {\n rethrowAsync(error)\n }\n },\n { root: root?.current ?? null, rootMargin },\n )\n observer.observe(sentinel)\n return () => observer.disconnect()\n }, [sentinel, enabled, root, rootMargin])\n\n return sentinelRef\n}\n\n// ---------------------------------------------------------------------------\n// useSessionHistory — cursor-paged data over an injected port\n// ---------------------------------------------------------------------------\n\nexport interface SessionPageQuery {\n /** Trimmed search term; empty string means no filter. */\n q: string\n sort: SessionSort\n /** `null` for the first page. */\n cursor: string | null\n /** Aborted when the view changes or the component unmounts. */\n signal: AbortSignal\n}\n\n/** Data port — one page of sessions for the current view. */\nexport type FetchSessionPage = (query: SessionPageQuery) => Promise<SessionPage>\n\nexport interface UseSessionHistoryOptions {\n fetchPage: FetchSessionPage\n /** Trimmed search term driving the fetch. */\n q: string\n sort: SessionSort\n /** SSR page 1 of the default view, so the first paint costs no request. */\n initialPage: SessionPage\n /** The sort `initialPage` was rendered for. Default `'newest'`. */\n defaultSort?: SessionSort\n}\n\nexport interface SessionHistoryState {\n items: SessionSummary[]\n hasMore: boolean\n isLoadingFirst: boolean\n isLoadingMore: boolean\n isError: boolean\n loadMore: () => void\n /** Re-run whichever load failed. */\n retry: () => void\n /** Refetch page 1 — call after a client-side mutation (e.g. a delete). */\n reload: () => void\n}\n\n/** Cheap content signature so a consumer inlining `initialPage={{items}}` on\n * every render does not reseed (and re-render) forever. Identity alone — what\n * the per-product versions keyed on — makes that an infinite loop. */\nfunction seedSignature(page: SessionPage): string {\n return `${page.nextCursor ?? ''}|${page.items.map((item) => item.id).join(',')}`\n}\n\n/**\n * Infinite-scroll data source for the history view. Seeds from `initialPage`\n * for the default view (no fetch) and otherwise fetches page 1 for the current\n * search/sort; `loadMore` appends the next cursor page.\n *\n * Raw promises + `AbortController` rather than a router fetcher, so a filter\n * change cancels in-flight requests, pages accumulate, and a late response from\n * a superseded view is dropped by the monotonic `seq` guard.\n */\nexport function useSessionHistory({\n fetchPage,\n q,\n sort,\n initialPage,\n defaultSort = 'newest',\n}: UseSessionHistoryOptions): SessionHistoryState {\n const [items, setItems] = useState<SessionSummary[]>(initialPage.items)\n const [nextCursor, setNextCursor] = useState<string | null>(initialPage.nextCursor ?? null)\n const [phase, setPhase] = useState<'idle' | 'loadingFirst' | 'loadingMore' | 'error'>('idle')\n const [reloadKey, setReloadKey] = useState(0)\n\n const seqRef = useRef(0)\n const resetAbortRef = useRef<AbortController | null>(null)\n const loadMoreAbortRef = useRef<AbortController | null>(null)\n const loadingMoreRef = useRef(false)\n const lastOpRef = useRef<'first' | 'more'>('first')\n\n // Live values read inside the stable `loadMore` callback.\n const nextCursorRef = useRef(nextCursor)\n nextCursorRef.current = nextCursor\n const viewRef = useRef({ q, sort, fetchPage })\n viewRef.current = { q, sort, fetchPage }\n const seedRef = useRef(initialPage)\n seedRef.current = initialPage\n\n const isDefaultView = q === '' && sort === defaultSort\n const seedKey = useMemo(() => seedSignature(initialPage), [initialPage])\n\n // Reset on view change (q/sort), on a new SSR seed (loader revalidation), or\n // on an explicit retry/reload. The default view comes straight from SSR;\n // explicit reloads fetch page 1 so a client-only mutation refreshes this list\n // without waiting on a route loader.\n useEffect(() => {\n resetAbortRef.current?.abort()\n loadMoreAbortRef.current?.abort()\n loadingMoreRef.current = false\n const seq = ++seqRef.current\n\n if (isDefaultView && reloadKey === 0) {\n setItems(seedRef.current.items)\n setNextCursor(seedRef.current.nextCursor ?? null)\n setPhase('idle')\n return\n }\n\n const controller = new AbortController()\n resetAbortRef.current = controller\n lastOpRef.current = 'first'\n setItems([])\n setNextCursor(null)\n setPhase('loadingFirst')\n\n void (async () => {\n try {\n const page = await viewRef.current.fetchPage({ q, sort, cursor: null, signal: controller.signal })\n if (seq !== seqRef.current) return\n setItems(page.items)\n setNextCursor(page.nextCursor ?? null)\n setPhase('idle')\n } catch {\n if (controller.signal.aborted || seq !== seqRef.current) return\n setPhase('error')\n }\n })()\n\n return () => controller.abort()\n }, [q, sort, seedKey, isDefaultView, reloadKey])\n\n const loadMore = useCallback(() => {\n const cursor = nextCursorRef.current\n if (!cursor || loadingMoreRef.current) return\n\n const { q: currentQ, sort: currentSort, fetchPage: currentFetch } = viewRef.current\n const seq = seqRef.current\n loadingMoreRef.current = true\n lastOpRef.current = 'more'\n const controller = new AbortController()\n loadMoreAbortRef.current = controller\n setPhase('loadingMore')\n\n void (async () => {\n try {\n const page = await currentFetch({ q: currentQ, sort: currentSort, cursor, signal: controller.signal })\n if (seq !== seqRef.current) return\n setItems((prev) => mergeSessionPages(prev, page.items))\n setNextCursor(page.nextCursor ?? null)\n setPhase('idle')\n } catch {\n if (controller.signal.aborted || seq !== seqRef.current) return\n setPhase('error')\n } finally {\n if (seq === seqRef.current) loadingMoreRef.current = false\n }\n })()\n }, [])\n\n const retry = useCallback(() => {\n if (lastOpRef.current === 'more') loadMore()\n else setReloadKey((key) => key + 1)\n }, [loadMore])\n\n const reload = useCallback(() => {\n setReloadKey((key) => key + 1)\n }, [])\n\n useEffect(\n () => () => {\n resetAbortRef.current?.abort()\n loadMoreAbortRef.current?.abort()\n },\n [],\n )\n\n return {\n items,\n hasMore: nextCursor !== null,\n isLoadingFirst: phase === 'loadingFirst',\n isLoadingMore: phase === 'loadingMore',\n isError: phase === 'error',\n loadMore,\n retry,\n reload,\n }\n}\n\n// ---------------------------------------------------------------------------\n// useSessionActions — rename / delete over injected mutations\n// ---------------------------------------------------------------------------\n\nexport interface SessionActionsOptions {\n /** Persist a new title. Reject to surface the error in the dialog. */\n renameSession: (sessionId: string, title: string) => Promise<void>\n deleteSession: (sessionId: string) => Promise<void>\n /** Called after a successful rename/delete — revalidate the rail here. */\n onChanged?: () => void\n /** Called after deleting the session the user is currently viewing, so the\n * product can navigate away from a route that no longer resolves. */\n onDeletedCurrent?: () => void\n /** The open session, compared against the delete target. */\n currentSessionId?: string | null\n /** Product toast/log seam. Errors also render inside the dialog. */\n notify?: (level: 'success' | 'error', message: string) => void\n labels?: Partial<SessionActionLabels>\n}\n\nexport interface SessionActionLabels {\n renameTitle: string\n renameField: string\n renameSubmit: string\n deleteTitle: string\n deleteBody: (title: string) => string\n deleteSubmit: string\n cancel: string\n renamed: string\n deleted: string\n renameFailed: string\n deleteFailed: string\n}\n\nconst DEFAULT_LABELS: SessionActionLabels = {\n renameTitle: 'Rename session',\n renameField: 'Title',\n renameSubmit: 'Save',\n deleteTitle: 'Delete session?',\n deleteBody: (title) => `This will permanently delete “${title}” and its messages. This cannot be undone.`,\n deleteSubmit: 'Delete',\n cancel: 'Cancel',\n renamed: 'Session renamed',\n deleted: 'Session deleted',\n renameFailed: 'Failed to rename session',\n deleteFailed: 'Failed to delete session',\n}\n\nexport interface SessionActions {\n openRename: (session: SessionSummary) => void\n openDelete: (session: SessionSummary) => void\n /** Render once, anywhere that survives navigation (the layout). */\n dialogs: ReactNode\n busy: boolean\n}\n\n/**\n * Rename + delete for one session, shared by the rail kebab and the history\n * row menu so both drive the same dialogs and the same product mutations.\n *\n * Dialogs are owned here rather than returned as raw state: two surfaces\n * needing the same confirm step is exactly how a product ends up with two\n * subtly different delete confirmations.\n */\nexport function useSessionActions({\n renameSession,\n deleteSession,\n onChanged,\n onDeletedCurrent,\n currentSessionId,\n notify,\n labels,\n}: SessionActionsOptions): SessionActions {\n const text = { ...DEFAULT_LABELS, ...labels }\n const [renameTarget, setRenameTarget] = useState<SessionSummary | null>(null)\n const [renameValue, setRenameValue] = useState('')\n const [deleteTarget, setDeleteTarget] = useState<SessionSummary | null>(null)\n const [busy, setBusy] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n const openRename = useCallback((session: SessionSummary) => {\n setError(null)\n setRenameTarget(session)\n setRenameValue(session.title ?? '')\n }, [])\n\n const openDelete = useCallback((session: SessionSummary) => {\n setError(null)\n setDeleteTarget(session)\n }, [])\n\n const submitRename = useCallback(async () => {\n if (!renameTarget) return\n const title = renameValue.trim()\n // A no-op rename closes rather than writing — otherwise every accidental\n // open costs a request and a revalidation.\n if (!title || title === renameTarget.title) {\n setRenameTarget(null)\n return\n }\n setBusy(true)\n setError(null)\n try {\n await renameSession(renameTarget.id, title)\n setRenameTarget(null)\n notify?.('success', text.renamed)\n onChanged?.()\n } catch (e) {\n const message = e instanceof Error ? e.message : text.renameFailed\n setError(message)\n notify?.('error', message)\n } finally {\n setBusy(false)\n }\n }, [renameTarget, renameValue, renameSession, notify, onChanged, text.renamed, text.renameFailed])\n\n const confirmDelete = useCallback(async () => {\n if (!deleteTarget) return\n const deletingCurrent = currentSessionId != null && deleteTarget.id === currentSessionId\n setBusy(true)\n setError(null)\n try {\n await deleteSession(deleteTarget.id)\n setDeleteTarget(null)\n notify?.('success', text.deleted)\n onChanged?.()\n if (deletingCurrent) onDeletedCurrent?.()\n } catch (e) {\n const message = e instanceof Error ? e.message : text.deleteFailed\n setError(message)\n notify?.('error', message)\n } finally {\n setBusy(false)\n }\n }, [deleteTarget, currentSessionId, deleteSession, notify, onChanged, onDeletedCurrent, text.deleted, text.deleteFailed])\n\n const dialogs = (\n <>\n {renameTarget && (\n <SessionDialog\n title={text.renameTitle}\n onClose={() => setRenameTarget(null)}\n busy={busy}\n error={error}\n footer={\n <>\n <DialogButton onClick={() => setRenameTarget(null)} disabled={busy} variant=\"ghost\">\n {text.cancel}\n </DialogButton>\n <DialogButton onClick={() => void submitRename()} disabled={busy || !renameValue.trim()}>\n {text.renameSubmit}\n </DialogButton>\n </>\n }\n >\n <label htmlFor=\"agent-app-rename-session\" className=\"text-xs text-muted-foreground\">\n {text.renameField}\n </label>\n <input\n id=\"agent-app-rename-session\"\n value={renameValue}\n autoFocus\n onChange={(e) => setRenameValue(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && !busy) {\n e.preventDefault()\n void submitRename()\n }\n }}\n className=\"mt-1.5 h-9 w-full rounded-md border border-strong bg-background px-3 text-sm text-foreground\"\n />\n </SessionDialog>\n )}\n\n {deleteTarget && (\n <SessionDialog\n title={text.deleteTitle}\n onClose={() => setDeleteTarget(null)}\n busy={busy}\n error={error}\n footer={\n <>\n <DialogButton onClick={() => setDeleteTarget(null)} disabled={busy} variant=\"ghost\">\n {text.cancel}\n </DialogButton>\n <DialogButton onClick={() => void confirmDelete()} disabled={busy} variant=\"destructive\">\n {text.deleteSubmit}\n </DialogButton>\n </>\n }\n >\n <p className=\"text-sm text-muted-foreground\">{text.deleteBody(sessionLabel(deleteTarget))}</p>\n </SessionDialog>\n )}\n </>\n )\n\n return { openRename, openDelete, dialogs, busy }\n}\n\nfunction DialogButton({\n children,\n onClick,\n disabled,\n variant = 'primary',\n}: {\n children: ReactNode\n onClick: () => void\n disabled?: boolean\n variant?: 'primary' | 'ghost' | 'destructive'\n}) {\n const tone =\n variant === 'ghost'\n ? 'text-muted-foreground hover:bg-accent hover:text-foreground'\n : variant === 'destructive'\n ? 'bg-destructive text-destructive-foreground hover:opacity-90'\n : 'bg-primary text-primary-foreground hover:opacity-90'\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n className={`h-9 rounded-md px-3 text-sm font-medium transition disabled:opacity-50 ${tone}`}\n >\n {children}\n </button>\n )\n}\n\nfunction SessionDialog({\n title,\n children,\n footer,\n onClose,\n busy,\n error,\n}: {\n title: string\n children: ReactNode\n footer: ReactNode\n onClose: () => void\n busy: boolean\n error: string | null\n}) {\n // Escape closes unless a mutation is in flight — closing mid-write would hide\n // the error the user needs to see.\n useEffect(() => {\n const onKey = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && !busy) onClose()\n }\n document.addEventListener('keydown', onKey)\n return () => document.removeEventListener('keydown', onKey)\n }, [busy, onClose])\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center p-4\">\n <div\n className=\"absolute inset-0 bg-black/50\"\n onClick={() => {\n if (!busy) onClose()\n }}\n aria-hidden\n />\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={title}\n className={`relative w-full max-w-sm rounded-xl border border-card-edge bg-popover p-5 ${OVERLAY_SHADOW}`}\n >\n <h2 className=\"text-sm font-semibold text-foreground\">{title}</h2>\n <div className=\"mt-3\">{children}</div>\n {error && (\n <p role=\"alert\" className=\"mt-3 rounded-md bg-destructive/10 px-3 py-2 text-xs text-destructive\">\n {error}\n </p>\n )}\n <div className=\"mt-5 flex justify-end gap-2\">{footer}</div>\n </div>\n </div>\n )\n}\n\n// ---------------------------------------------------------------------------\n// SessionHistoryPanel\n// ---------------------------------------------------------------------------\n\nexport interface SessionHistoryPanelProps {\n history: SessionHistoryState\n /** Whether the workspace has any sessions at all — decided by the SSR page,\n * independent of the active search, so filtering to zero shows \"no matches\"\n * rather than the first-run empty state. */\n hasAnySessions: boolean\n query: string\n onQueryChange: (value: string) => void\n sort: SessionSort\n onSortChange: (value: SessionSort) => void\n /** Product route for one session row. */\n hrefForSession: (sessionId: string) => string\n /** Rendered as the row link. Defaults to an `<a>`; pass a router Link to keep\n * client-side navigation. */\n linkComponent?: LinkLikeComponent\n /** Ids currently mid-turn — renders the responding treatment. */\n respondingSessionIds?: ReadonlySet<string>\n onRename?: (session: SessionSummary) => void\n onDelete?: (session: SessionSummary) => void\n /** Product-owned mutation for selected rows or a workspace-wide age range. */\n onBulkAction?: (action: SessionBulkAction) => Promise<void>\n /** Menu wording, so this surface and the rail name the same act the same way\n * — a product whose delete is really an archive says so in both places. */\n renameLabel?: string\n deleteLabel?: string\n /**\n * Row actions this shell has no opinion about — pin, categorise, share.\n * Same seam and same ordering as the rail's `SessionRowActions.extraActions`:\n * evaluated per session, placed between rename and delete. This menu is\n * text-only, so `icon` is ignored here and honoured on the rail.\n */\n extraActions?: (session: SessionSummary) => SessionRailAction[]\n /** New-session destination for the header action. Omitted ⇒ no button. */\n newSessionHref?: string\n title?: string\n untitledLabel?: string\n emptyTitle?: string\n emptyDescription?: string\n /** Absolute → relative timestamp. Defaults to a compact built-in. */\n formatTimestamp?: (isoDate: string | null) => string\n /** Max width of the reading column. `'full'` opts out for a product whose\n * surface really is a wide table. Default keeps title and timestamp inside\n * one scannable line rather than at opposite edges of a 1440px viewport. */\n contentWidth?: 'reading' | 'full'\n className?: string\n}\n\nexport type SessionBulkAction =\n | { kind: 'selected'; ids: string[] }\n | { kind: 'older-than'; days: number }\n | { kind: 'newer-than'; days: number }\n\nexport interface LinkLikeProps {\n to: string\n className?: string\n children?: ReactNode\n}\n\nexport type LinkLikeComponent = (props: LinkLikeProps) => ReactNode\n\nfunction AnchorLink({ to, className, children }: LinkLikeProps) {\n return (\n <a href={to} className={className}>\n {children}\n </a>\n )\n}\n\nconst MINUTE = 60_000\nconst HOUR = 60 * MINUTE\nconst DAY = 24 * HOUR\n\n/** Compact relative time. Overridable — a product with its own i18n passes\n * `formatTimestamp` rather than this being the only option. */\nexport function formatSessionTimestamp(isoDate: string | null): string {\n if (!isoDate) return ''\n const at = Date.parse(isoDate)\n if (Number.isNaN(at)) return ''\n const delta = Date.now() - at\n if (delta < MINUTE) return 'just now'\n if (delta < HOUR) return `${Math.floor(delta / MINUTE)}m ago`\n if (delta < DAY) return `${Math.floor(delta / HOUR)}h ago`\n if (delta < 7 * DAY) return `${Math.floor(delta / DAY)}d ago`\n return new Date(at).toLocaleDateString(undefined, { month: 'short', day: 'numeric' })\n}\n\nfunction SkeletonRows() {\n // The shimmer rows stay hidden from assistive tech — they say nothing a\n // reader can use — but the wait itself has to be announced, or the panel is\n // silent from the first paint until the rows arrive. The live region is a\n // sibling so the rows' own flex layout is untouched.\n return (\n <>\n <span role=\"status\" aria-live=\"polite\" aria-busy={true} className=\"sr-only\">\n Loading sessions…\n </span>\n <div className=\"flex flex-col gap-0.5\" aria-hidden>\n {Array.from({ length: 8 }).map((_, i) => (\n <div key={i} className=\"flex items-center gap-3 px-3 py-2.5\">\n <div className=\"h-4 w-4 animate-pulse rounded bg-muted\" />\n <div className=\"h-4 w-1/2 animate-pulse rounded bg-muted\" />\n <div className=\"ml-auto h-3 w-12 animate-pulse rounded bg-muted\" />\n </div>\n ))}\n </div>\n </>\n )\n}\n\nfunction MessageIcon({ className }: { className?: string }) {\n return (\n <svg viewBox=\"0 0 24 24\" className={className} fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <path d=\"M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z\" />\n </svg>\n )\n}\n\n/**\n * The full session history: search, sort, cursor-paged rows with per-row\n * actions, and the states in between (first-run empty, loading, no matches,\n * error + retry).\n *\n * This is the surface the rail's capped list overflows into — the reason the\n * rail can stay short without hiding the user's work.\n */\nexport function SessionHistoryPanel({\n history,\n hasAnySessions,\n query,\n onQueryChange,\n sort,\n onSortChange,\n hrefForSession,\n linkComponent: Link = AnchorLink,\n respondingSessionIds,\n onRename,\n onDelete,\n onBulkAction,\n renameLabel = 'Rename',\n deleteLabel = 'Delete',\n extraActions,\n newSessionHref,\n title = 'History',\n untitledLabel = UNTITLED_SESSION_LABEL,\n emptyTitle = 'No sessions yet',\n emptyDescription = 'Your chat sessions will show up here once you start one.',\n formatTimestamp = formatSessionTimestamp,\n contentWidth = 'reading',\n className,\n}: SessionHistoryPanelProps) {\n const scrollRef = useRef<HTMLDivElement | null>(null)\n const sentinelRef = useInfiniteScroll(history.loadMore, {\n enabled: history.hasMore && !history.isLoadingMore && !history.isError,\n root: scrollRef,\n rootMargin: '300px',\n })\n const searchTerm = query.trim()\n const isSearching = searchTerm.length > 0\n const column = contentWidth === 'full' ? 'w-full' : 'mx-auto w-full max-w-4xl'\n const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set())\n const [ageDays, setAgeDays] = useState('30')\n const [bulkTarget, setBulkTarget] = useState<{\n action: SessionBulkAction\n title: string\n body: string\n } | null>(null)\n const [bulkBusy, setBulkBusy] = useState(false)\n const [bulkError, setBulkError] = useState<string | null>(null)\n\n useEffect(() => {\n setSelectedIds(new Set())\n }, [searchTerm, sort])\n\n useEffect(() => {\n const visible = new Set(history.items.map((item) => item.id))\n setSelectedIds((current) => {\n const next = new Set([...current].filter((id) => visible.has(id)))\n return next.size === current.size ? current : next\n })\n }, [history.items])\n\n const selectedCount = selectedIds.size\n const allVisibleSelected = history.items.length > 0 && history.items.every((item) => selectedIds.has(item.id))\n const parsedAgeDays = Number(ageDays)\n const validAgeDays = Number.isInteger(parsedAgeDays) && parsedAgeDays >= 1 && parsedAgeDays <= 36_500\n\n const openBulkAction = useCallback((action: SessionBulkAction) => {\n const verb = deleteLabel.toLowerCase()\n if (action.kind === 'selected') {\n setBulkTarget({\n action,\n title: `${deleteLabel} selected sessions?`,\n body: `${verb === 'delete' ? 'This permanently removes' : `This ${verb}s`} ${action.ids.length} selected session${action.ids.length === 1 ? '' : 's'} and its messages.`,\n })\n return\n }\n const range = action.kind === 'older-than' ? `older than ${action.days} days` : `from the last ${action.days} days`\n setBulkTarget({\n action,\n title: `${deleteLabel} sessions ${range}?`,\n body: 'This applies to every matching session in this workspace, including sessions not currently loaded in this list.',\n })\n }, [deleteLabel])\n\n const confirmBulkAction = useCallback(async () => {\n if (!bulkTarget || !onBulkAction) return\n setBulkBusy(true)\n setBulkError(null)\n try {\n await onBulkAction(bulkTarget.action)\n setBulkTarget(null)\n setSelectedIds(new Set())\n history.reload()\n } catch (error) {\n setBulkError(error instanceof Error ? error.message : `Could not ${deleteLabel.toLowerCase()} sessions`)\n } finally {\n setBulkBusy(false)\n }\n }, [bulkTarget, deleteLabel, history, onBulkAction])\n\n return (\n <div className={`flex min-h-0 min-w-0 flex-1 flex-col ${className ?? ''}`}>\n {/* 56px, matching the rail header the fleet aligned on. */}\n <header className=\"flex h-14 shrink-0 items-center border-b border-border px-4 sm:px-6\">\n <div className={`flex items-center gap-3 px-3 ${column}`}>\n <h1 className=\"flex-1 truncate text-sm font-semibold text-foreground\">{title}</h1>\n {newSessionHref && (\n <Link\n to={newSessionHref}\n className=\"inline-flex h-8 shrink-0 items-center gap-1.5 rounded-md bg-primary px-3 text-xs font-medium text-primary-foreground transition hover:opacity-90\"\n >\n <span aria-hidden className=\"text-sm leading-none\">+</span>\n New chat\n </Link>\n )}\n </div>\n </header>\n\n <div ref={scrollRef} className=\"min-h-0 flex-1 overflow-y-auto\">\n {hasAnySessions && (\n <div className=\"sticky top-0 z-10 bg-background px-4 sm:px-6\">\n <div className={`flex flex-col gap-2 px-3 pb-3 pt-4 sm:flex-row sm:items-center sm:gap-3 ${column}`}>\n <input\n type=\"search\"\n value={query}\n onChange={(e) => onQueryChange(e.target.value)}\n placeholder=\"Search your sessions…\"\n aria-label=\"Search sessions\"\n className=\"h-9 min-w-0 appearance-none rounded-md border border-strong bg-card px-3 text-sm text-foreground placeholder:text-muted-foreground sm:flex-1 [&::-webkit-search-cancel-button]:appearance-none\"\n />\n <select\n value={sort}\n onChange={(e) => onSortChange(e.target.value as SessionSort)}\n aria-label=\"Sort sessions\"\n className=\"h-9 shrink-0 appearance-none rounded-md border border-strong bg-card px-2 text-sm text-foreground sm:w-[132px]\"\n >\n <option value=\"newest\">Newest</option>\n <option value=\"oldest\">Oldest</option>\n </select>\n </div>\n {onBulkAction && (\n <div className={`flex flex-col gap-2 border-t border-border px-3 py-3 ${column}`}>\n <div className=\"flex flex-wrap items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => setSelectedIds(new Set(history.items.map((item) => item.id)))}\n disabled={allVisibleSelected || history.items.length === 0}\n className=\"h-8 rounded-md border border-border px-2.5 text-xs font-medium text-foreground transition hover:bg-accent disabled:opacity-50\"\n >\n Select all\n </button>\n <button\n type=\"button\"\n onClick={() => setSelectedIds(new Set())}\n disabled={selectedCount === 0}\n className=\"h-8 rounded-md border border-border px-2.5 text-xs font-medium text-foreground transition hover:bg-accent disabled:opacity-50\"\n >\n Deselect all\n </button>\n <span className=\"text-xs text-muted-foreground\" aria-live=\"polite\">\n {selectedCount} selected\n </span>\n {selectedCount > 0 && (\n <button\n type=\"button\"\n onClick={() => openBulkAction({ kind: 'selected', ids: [...selectedIds] })}\n className=\"h-8 rounded-md bg-destructive px-2.5 text-xs font-medium text-destructive-foreground transition hover:opacity-90\"\n >\n {deleteLabel} selected\n </button>\n )}\n </div>\n <div className=\"flex flex-wrap items-center gap-2\">\n <label htmlFor=\"agent-app-session-age\" className=\"text-xs text-muted-foreground\">\n Session age\n </label>\n <input\n id=\"agent-app-session-age\"\n type=\"number\"\n min={1}\n max={36_500}\n value={ageDays}\n onChange={(event) => setAgeDays(event.target.value)}\n aria-invalid={ageDays.length > 0 && !validAgeDays}\n className=\"h-8 w-20 rounded-md border border-strong bg-card px-2 text-xs tabular-nums text-foreground\"\n />\n <span className=\"text-xs text-muted-foreground\">days</span>\n <button\n type=\"button\"\n onClick={() => openBulkAction({ kind: 'older-than', days: parsedAgeDays })}\n disabled={!validAgeDays}\n className=\"h-8 rounded-md border border-border px-2.5 text-xs font-medium text-foreground transition hover:bg-accent disabled:opacity-50\"\n >\n {deleteLabel} older\n </button>\n <button\n type=\"button\"\n onClick={() => openBulkAction({ kind: 'newer-than', days: parsedAgeDays })}\n disabled={!validAgeDays}\n className=\"h-8 rounded-md border border-border px-2.5 text-xs font-medium text-foreground transition hover:bg-accent disabled:opacity-50\"\n >\n {deleteLabel} recent\n </button>\n </div>\n </div>\n )}\n </div>\n )}\n\n <div className={`px-4 pb-8 pt-1 sm:px-6 ${column}`}>\n {!hasAnySessions ? (\n <div className=\"flex min-h-[50vh] flex-col items-center justify-center gap-2 text-center\">\n <p className=\"text-sm font-medium text-foreground\">{emptyTitle}</p>\n <p className=\"max-w-xs text-xs text-muted-foreground\">{emptyDescription}</p>\n </div>\n ) : history.isLoadingFirst ? (\n <SkeletonRows />\n ) : history.items.length === 0 ? (\n history.isError ? (\n <ErrorBlock onRetry={history.retry} message=\"Couldn’t load your sessions.\" />\n ) : isSearching ? (\n <p className=\"py-16 text-center text-sm text-muted-foreground\">\n No sessions match “{searchTerm}”.\n </p>\n ) : (\n <p className=\"py-16 text-center text-sm text-muted-foreground\">No sessions remain.</p>\n )\n ) : (\n <div className=\"flex flex-col gap-0.5\">\n {history.items.map((session) => (\n <SessionRow\n key={session.id}\n session={session}\n href={hrefForSession(session.id)}\n Link={Link}\n responding={respondingSessionIds?.has(session.id) ?? false}\n untitledLabel={untitledLabel}\n timestamp={formatTimestamp(session.updatedAt)}\n onRename={onRename}\n onDelete={onDelete}\n selectable={Boolean(onBulkAction)}\n selected={selectedIds.has(session.id)}\n onSelectedChange={(selected) => {\n setSelectedIds((current) => {\n const next = new Set(current)\n if (selected) next.add(session.id)\n else next.delete(session.id)\n return next\n })\n }}\n renameLabel={renameLabel}\n deleteLabel={deleteLabel}\n extraActions={extraActions}\n />\n ))}\n\n {history.isError ? (\n <ErrorBlock onRetry={history.retry} message=\"Couldn’t load more sessions.\" inline />\n ) : history.hasMore ? (\n <div ref={sentinelRef} className=\"flex items-center justify-center py-6\">\n {history.isLoadingMore && (\n <span role=\"status\" aria-live=\"polite\" aria-busy={true} className=\"text-xs text-muted-foreground\">\n Loading…\n </span>\n )}\n </div>\n ) : null}\n </div>\n )}\n </div>\n </div>\n {bulkTarget && (\n <SessionDialog\n title={bulkTarget.title}\n onClose={() => {\n if (!bulkBusy) {\n setBulkTarget(null)\n setBulkError(null)\n }\n }}\n busy={bulkBusy}\n error={bulkError}\n footer={\n <>\n <DialogButton\n onClick={() => {\n setBulkTarget(null)\n setBulkError(null)\n }}\n disabled={bulkBusy}\n variant=\"ghost\"\n >\n Cancel\n </DialogButton>\n <DialogButton onClick={() => void confirmBulkAction()} disabled={bulkBusy} variant=\"destructive\">\n {bulkBusy ? 'Working…' : deleteLabel}\n </DialogButton>\n </>\n }\n >\n <p className=\"text-sm text-muted-foreground\">{bulkTarget.body}</p>\n </SessionDialog>\n )}\n </div>\n )\n}\n\nfunction ErrorBlock({ message, onRetry, inline }: { message: string; onRetry: () => void; inline?: boolean }) {\n return (\n <div\n className={\n inline\n ? 'flex items-center justify-center gap-3 py-6 text-sm text-muted-foreground'\n : 'flex min-h-[40vh] flex-col items-center justify-center gap-3 text-center'\n }\n >\n <span className=\"text-sm text-muted-foreground\">{message}</span>\n <button\n type=\"button\"\n onClick={onRetry}\n className=\"h-8 rounded-md border border-border px-3 text-xs font-medium text-foreground transition hover:bg-accent\"\n >\n Retry\n </button>\n </div>\n )\n}\n\nfunction SessionRow({\n session,\n href,\n Link,\n responding,\n untitledLabel,\n timestamp,\n onRename,\n onDelete,\n renameLabel,\n deleteLabel,\n extraActions,\n selectable,\n selected,\n onSelectedChange,\n}: {\n session: SessionSummary\n href: string\n Link: LinkLikeComponent\n responding: boolean\n untitledLabel: string\n timestamp: string\n onRename?: (session: SessionSummary) => void\n onDelete?: (session: SessionSummary) => void\n renameLabel: string\n deleteLabel: string\n extraActions?: (session: SessionSummary) => SessionRailAction[]\n selectable: boolean\n selected: boolean\n onSelectedChange: (selected: boolean) => void\n}) {\n const [menuOpen, setMenuOpen] = useState(false)\n const panelId = useId()\n const { containerRef, triggerRef, panelRef, triggerProps } = usePopover(menuOpen, setMenuOpen)\n const extras = extraActions?.(session) ?? []\n const hasMenu = Boolean(onRename) || Boolean(onDelete) || extras.length > 0\n // An unread dot next to a live responding indicator is two signals for one\n // state; the working indicator wins while the turn runs.\n const showUnread = Boolean(session.unread) && !responding\n\n return (\n <div className={`group relative flex items-center gap-2 rounded-lg px-3 py-2.5 transition-colors hover:bg-accent ${selected ? 'bg-primary/10' : ''}`}>\n {selectable && (\n <input\n type=\"checkbox\"\n checked={selected}\n onChange={(event) => onSelectedChange(event.target.checked)}\n aria-label={`Select ${sessionLabel(session, untitledLabel)}`}\n className=\"h-4 w-4 shrink-0 rounded border-border accent-primary\"\n />\n )}\n <Link to={href} className=\"flex min-w-0 flex-1 items-center gap-3\">\n {showUnread && <span className=\"h-1.5 w-1.5 shrink-0 rounded-full bg-primary\" aria-hidden />}\n <MessageIcon className=\"h-4 w-4 shrink-0 text-muted-foreground\" />\n <span\n className={`truncate text-sm ${responding ? 'text-muted-foreground' : 'text-foreground'} ${showUnread ? 'font-semibold' : ''}`}\n {...(responding ? { role: 'status', 'aria-label': 'Agent responding' } : {})}\n >\n {sessionLabel(session, untitledLabel)}\n </span>\n </Link>\n <span className=\"shrink-0 text-xs tabular-nums text-muted-foreground\">{timestamp}</span>\n {hasMenu && (\n <div ref={containerRef} className=\"relative shrink-0\">\n <button\n type=\"button\"\n {...triggerProps}\n aria-label=\"Session actions\"\n aria-controls={menuOpen ? panelId : undefined}\n onClick={() => setMenuOpen((open) => !open)}\n // Visible by default and hover-revealed only from `sm:` up: a\n // touch device has no hover, so an opacity-0 kebab is an action\n // the user can never reach.\n className=\"flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition hover:bg-muted hover:text-foreground focus-visible:opacity-100 sm:opacity-0 sm:group-hover:opacity-100 aria-expanded:opacity-100\"\n >\n <span aria-hidden className=\"text-base leading-none\">⋯</span>\n </button>\n <PopoverSurface\n open={menuOpen}\n id={panelId}\n role=\"menu\"\n triggerRef={triggerRef}\n panelRef={panelRef}\n className={`w-36 overflow-hidden rounded-md border border-card-edge bg-popover py-1 ${OVERLAY_SHADOW}`}\n >\n {onRename && (\n <button\n type=\"button\"\n role=\"menuitem\"\n onClick={() => {\n setMenuOpen(false)\n onRename(session)\n }}\n className=\"block w-full px-3 py-1.5 text-left text-xs text-foreground transition hover:bg-accent\"\n >\n {renameLabel}\n </button>\n )}\n {extras.map((action) => (\n <button\n key={action.id}\n type=\"button\"\n role=\"menuitem\"\n onClick={() => {\n setMenuOpen(false)\n action.onSelect()\n }}\n className={`block w-full px-3 py-1.5 text-left text-xs transition ${\n action.destructive\n ? 'text-destructive hover:bg-destructive/10'\n : 'text-foreground hover:bg-accent'\n }`}\n >\n {action.label}\n </button>\n ))}\n {onDelete && (\n <button\n type=\"button\"\n role=\"menuitem\"\n onClick={() => {\n setMenuOpen(false)\n onDelete(session)\n }}\n className=\"block w-full px-3 py-1.5 text-left text-xs text-destructive transition hover:bg-destructive/10\"\n >\n {deleteLabel}\n </button>\n )}\n </PopoverSurface>\n </div>\n )}\n </div>\n )\n}\n","/**\n * `RecordGrid` — the editable record table four verticals each hand-rolled: a\n * cap table, two relationship record pages, a content board, and an entities\n * panel. Every copy re-derived the same mechanism and each one lost a\n * different part of it.\n *\n * What this owns:\n *\n * - **Typed columns with correctable errors.** A cell is text / number /\n * currency / date / select / boolean (`./record-grid-model`), so a rejected\n * edit explains itself next to the control instead of storing a coerced\n * value.\n * - **Optimistic write with a real rollback.** The edit lands instantly, the\n * caller's writer returns a typed outcome, and a failure puts the previous\n * value back AND says why. A grid that keeps a value the server refused is\n * the defect this replaces.\n * - **Provenance per cell.** An optional quote + link + basis, so a\n * record-backed grid shows where a value came from without the product\n * building a second surface for it.\n * - **Review of a proposed change set.** Hand the grid a `proposed` patch\n * (`./record-grid-model`'s `diffRecordGridProposal`) and it becomes the\n * red/green row-diff surface a tax/legal review needs: changed cells render\n * the struck live value against the proposed one, added/removed rows are\n * marked, and every diffed row carries accept/reject — per row and for the\n * whole set. What accepting MEANS stays the caller's (a record-store review\n * write); the grid reports decisions, it does not persist them.\n * - **Three distinct data states, on `web-react/async`'s own contract.**\n * `state: AsyncResourceState<Row[]>` and `empty: AsyncEmptySpec` are the\n * same types every other screen fetches through — loading, error-with-\n * retry and empty are different renders, a failed fetch never looks like\n * \"no data yet\", and the empty state carries the CALLER's next action.\n * The optimistic overlay is layered on top of whichever `ready`/`empty`\n * value the caller last supplied, so a row created while the caller's own\n * status is still `empty` renders immediately rather than waiting for a\n * refetch.\n * - **Keyboard-navigable, labelled controls.** Arrow keys move between cells,\n * Enter edits, Escape cancels, and no destructive control is an unlabelled\n * icon.\n *\n * Presentation is Tailwind against the shared design tokens, with no icon\n * library and no sandbox-ui — the same contract as the rest of `/web-react`.\n */\n\nimport {\n Fragment,\n isValidElement,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type KeyboardEvent,\n type ReactNode,\n} from 'react'\n\nimport { type AsyncEmptyAction, type AsyncEmptySpec, type AsyncResourceState } from './async'\nimport { OVERLAY_SHADOW, PopoverSurface, usePopover } from './controls'\nimport {\n EMPTY_RECORD_GRID_OVERLAY,\n diffRecordGridProposal,\n formatRecordGridValue,\n isRecordGridCellApplicable,\n projectRecordGridRows,\n pruneRecordGridOverlay,\n readRecordGridCell,\n recordGridEditorText,\n recordGridRowLabel,\n sameRecordGridValue,\n validateRecordGridRow,\n withRecordGridCreated,\n withRecordGridRemoved,\n withRecordGridServerRow,\n withRecordGridUpdate,\n withoutRecordGridCreated,\n withoutRecordGridRemoved,\n withoutRecordGridUpdate,\n type RecordGridCellDiff,\n type RecordGridCellSource,\n type RecordGridColumn,\n type RecordGridOverlay,\n type RecordGridProposal,\n type RecordGridRow,\n type RecordGridRowDiff,\n type RecordGridSourceBasis,\n type RecordGridValue,\n} from './record-grid-model'\n\nexport * from './record-grid-model'\n\n/** One committed cell edit, handed to `onUpdate`. */\nexport interface RecordGridCellChange {\n /** The row as it was BEFORE the edit — what rollback restores. */\n row: RecordGridRow\n columnId: string\n value: RecordGridValue\n /** The full value bag after the edit: what a whole-row write would send. */\n values: Readonly<Record<string, RecordGridValue>>\n}\n\n/** Outcome of an update or a delete. `value` optionally carries the server's\n * canonical row, which replaces the optimistic one. */\nexport type RecordGridWriteOutcome =\n | { succeeded: true; value?: RecordGridRow }\n | { succeeded: false; error: string }\n\n/** Outcome of a create. The row is REQUIRED on success: a create that does not\n * name the row it wrote leaves the grid unable to address it. */\nexport type RecordGridCreateOutcome =\n | { succeeded: true; value: RecordGridRow }\n | { succeeded: false; error: string }\n\n/** Properties for the editable, provenance-aware record grid. */\nexport interface RecordGridProps {\n /** Column definitions, in render order. */\n columns: readonly RecordGridColumn[]\n /** Fetch state over the caller's rows — `web-react/async`'s\n * `AsyncResourceState`, the same three-state contract every other screen\n * in the shell fetches through. `ready`/`empty`'s value is the base rows;\n * optimistic edits are layered over it and dropped as a later value\n * catches up. `error` always carries `retry` — there is no way to render a\n * failed fetch with no recovery action, by construction. */\n state: AsyncResourceState<readonly RecordGridRow[]>\n /** Accessible name for the grid. Required — an unnamed grid is unusable with\n * a screen reader. */\n caption: string\n /** What the empty state says, and what it offers next — `web-react/async`'s\n * `AsyncEmptySpec`, so an empty grid reads in the same words as an empty\n * list or panel elsewhere in the product. */\n empty: AsyncEmptySpec\n /** Persist one created row. Absent → no add affordance. */\n onCreate?: (values: Readonly<Record<string, RecordGridValue>>) => Promise<RecordGridCreateOutcome>\n /** Persist one cell edit. Absent → every cell renders read-only. */\n onUpdate?: (change: RecordGridCellChange) => Promise<RecordGridWriteOutcome>\n /** Delete one row. Absent → no delete affordance. */\n onDelete?: (row: RecordGridRow) => Promise<RecordGridWriteOutcome>\n /** A proposed change set to review against the live rows (see\n * `diffRecordGridProposal`). While a non-empty diff is on the table the grid\n * is a REVIEW surface, not an editor: cell editing, row add, and row delete\n * are inert; changed cells render the struck live value against the\n * proposed one; added/removed rows are marked; each diffed row carries\n * accept/reject controls. A proposal that diffs to nothing renders the grid\n * unchanged — there is nothing to review. */\n proposed?: RecordGridProposal\n /** Accept one diffed row — write its proposed values, adopt the addition, or\n * confirm the removal. The caller owns what accepting MEANS (a record-store\n * review write); the grid reports the decision and the caller moves the row\n * out of `proposed`. */\n onAcceptRow?: (rowId: string) => void\n /** Reject one diffed row — the live row stands. */\n onRejectRow?: (rowId: string) => void\n /** Accept every remaining diffed row at once. */\n onAcceptAll?: () => void\n /** Reject every remaining diffed row at once. */\n onRejectAll?: () => void\n /** Starting values for the add form. */\n newRowDefaults?: Readonly<Record<string, RecordGridValue>>\n /** Label of the add control and of the add form. Defaults to `Add row`. */\n addLabel?: string\n /** BCP-47 locale for number, currency, and date display. */\n locale?: string\n /** Rendered above the grid — filters, counts, a product action row. */\n toolbar?: ReactNode\n /** Skeleton rows in the loading state. Defaults to 3. */\n loadingRowCount?: number\n className?: string\n}\n\n/** Stable empty base for `idle`/`loading`/`error` — a fresh `[]` every render\n * would retrigger the overlay-pruning effect for no reason. */\nconst EMPTY_RECORD_GRID_ROWS: readonly RecordGridRow[] = []\n\n/** Internal map key. NUL cannot occur in a column or row id a product would\n * write, and this key never reaches the DOM. */\nconst CELL_KEY_SEPARATOR = '\\u0000'\n\nfunction cellKey(rowId: string, columnId: string): string {\n return `${rowId}${CELL_KEY_SEPARATOR}${columnId}`\n}\n\nconst NAVIGATION_KEYS = new Set(['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Home', 'End'])\n\nfunction clamp(value: number, max: number): number {\n if (value < 0) return 0\n if (value > max) return max\n return value\n}\n\nfunction alignmentClass(column: RecordGridColumn): string {\n const align = column.align ?? (column.kind === 'number' || column.kind === 'currency' ? 'right' : 'left')\n return align === 'right' ? 'text-right' : 'text-left'\n}\n\n/** Columns bucketed by their `group`, preserving first-appearance order. Each\n * bucket becomes one labelled sub-form in the add form — how a nested\n * schedule stays a set of flat, individually-typed columns. */\nfunction groupColumns(\n columns: readonly RecordGridColumn[],\n): Array<{ label: string | null; columns: RecordGridColumn[] }> {\n const groups: Array<{ label: string | null; columns: RecordGridColumn[] }> = []\n for (const column of columns) {\n const label = column.group ?? null\n const existing = groups.find((group) => group.label === label)\n if (existing) existing.columns.push(column)\n else groups.push({ label, columns: [column] })\n }\n return groups\n}\n\nconst INPUT_CLASS =\n 'w-full rounded-md border border-strong bg-background px-2 py-1 text-sm text-foreground outline-none focus:border-primary/60 focus:ring-1 focus:ring-primary/40'\n\nconst BASIS_TONES: Record<RecordGridSourceBasis, string> = {\n extracted: 'border-primary/60 text-primary',\n entered: 'border-success/60 text-success',\n computed: 'border-border text-muted-foreground',\n asserted: 'border-warning/60 text-warning',\n}\n\nconst BASIS_TITLES: Record<RecordGridSourceBasis, string> = {\n extracted: 'Extracted from a source document',\n entered: 'Confirmed by a person',\n computed: 'Computed from other values',\n asserted: 'Agent, unverified — no source recorded',\n}\n\n/**\n * The shared editable record table. A row is a flat value bag keyed by column\n * id, so a record store's fold output maps straight on: one cell per entry,\n * its quote and link in `sources`.\n */\nexport function RecordGrid({\n columns,\n caption,\n state,\n empty,\n onCreate,\n onUpdate,\n onDelete,\n proposed,\n onAcceptRow,\n onRejectRow,\n onAcceptAll,\n onRejectAll,\n newRowDefaults,\n addLabel = 'Add row',\n locale,\n toolbar,\n loadingRowCount = 3,\n className,\n}: RecordGridProps) {\n const fieldPrefix = useId()\n const [overlay, setOverlay] = useState<RecordGridOverlay>(EMPTY_RECORD_GRID_OVERLAY)\n const [editing, setEditingState] = useState<{ rowId: string; columnId: string; text: string } | null>(null)\n const [cellErrors, setCellErrors] = useState<Readonly<Record<string, string>>>({})\n const [rowErrors, setRowErrors] = useState<Readonly<Record<string, string>>>({})\n const [pendingRows, setPendingRows] = useState<Readonly<Record<string, true>>>({})\n const [focus, setFocus] = useState<{ rowId: string; columnId: string } | null>(null)\n const [openSource, setOpenSource] = useState<string | null>(null)\n const [confirmDelete, setConfirmDelete] = useState<string | null>(null)\n const [adding, setAdding] = useState(false)\n const [draft, setDraft] = useState<Record<string, RecordGridValue>>({ ...(newRowDefaults ?? {}) })\n const [draftErrors, setDraftErrors] = useState<Readonly<Record<string, string>>>({})\n const [draftError, setDraftError] = useState<string | null>(null)\n const [creating, setCreating] = useState(false)\n\n const cellRefs = useRef(new Map<string, HTMLElement | null>())\n const settling = useRef(false)\n const draftCounter = useRef(0)\n // Mirrors `editing` synchronously: a blur that arrives after the editor\n // already closed must not re-commit the value it was holding.\n const editingRef = useRef<{ rowId: string; columnId: string; text: string } | null>(null)\n\n const setEditing = useCallback((next: { rowId: string; columnId: string; text: string } | null) => {\n editingRef.current = next\n setEditingState(next)\n }, [])\n\n // `ready` and `empty` are the only variants carrying rows to project; the\n // other three short-circuit below before `visibleRows` is ever read, so an\n // empty base here is inert rather than wrong.\n const callerRows = state.status === 'ready' || state.status === 'empty' ? state.value : EMPTY_RECORD_GRID_ROWS\n\n // Overlay entries the caller's own rows have caught up with are dropped, so\n // a later refresh of the same cell is never masked by a settled edit.\n useEffect(() => {\n setOverlay((current) => pruneRecordGridOverlay(callerRows, current))\n }, [callerRows])\n\n const visibleRows = useMemo(() => projectRecordGridRows(callerRows, overlay), [callerRows, overlay])\n\n // The diff is computed against the rows ON SCREEN, so a struck \"before\"\n // value is always the value the reader sees the proposal replace.\n const diffs = useMemo(\n () => (proposed === undefined ? null : diffRecordGridProposal(visibleRows, proposed)),\n [proposed, visibleRows],\n )\n const reviewing = diffs !== null && diffs.length > 0\n const diffByRow = useMemo(() => new Map((diffs ?? []).map((diff) => [diff.rowId, diff])), [diffs])\n const diffCellByKey = useMemo(() => {\n const map = new Map<string, RecordGridCellDiff>()\n for (const diff of diffs ?? []) {\n for (const cell of diff.cells) map.set(cellKey(diff.rowId, cell.columnId), cell)\n }\n return map\n }, [diffs])\n const addedRows = useMemo(\n () => (diffs ?? []).filter((diff) => diff.kind === 'added').map((diff) => diff.row),\n [diffs],\n )\n\n const activeFocus = useMemo(() => {\n if (focus === null) return null\n if (!visibleRows.some((row) => row.id === focus.rowId)) return null\n if (!columns.some((column) => column.id === focus.columnId)) return null\n return focus\n }, [columns, focus, visibleRows])\n\n const setCellError = useCallback((key: string, message: string | null) => {\n setCellErrors((current) => {\n if (message === null) {\n if (!(key in current)) return current\n const next = { ...current }\n delete next[key]\n return next\n }\n if (current[key] === message) return current\n return { ...current, [key]: message }\n })\n }, [])\n\n const setRowError = useCallback((rowId: string, message: string | null) => {\n setRowErrors((current) => {\n if (message === null) {\n if (!(rowId in current)) return current\n const next = { ...current }\n delete next[rowId]\n return next\n }\n return { ...current, [rowId]: message }\n })\n }, [])\n\n const setRowPending = useCallback((rowId: string, pending: boolean) => {\n setPendingRows((current) => {\n if (pending) return rowId in current ? current : { ...current, [rowId]: true }\n if (!(rowId in current)) return current\n const next = { ...current }\n delete next[rowId]\n return next\n })\n }, [])\n\n const focusCell = useCallback((rowId: string, columnId: string) => {\n setFocus({ rowId, columnId })\n cellRefs.current.get(cellKey(rowId, columnId))?.focus()\n }, [])\n\n const beginEdit = useCallback(\n (row: RecordGridRow, column: RecordGridColumn) => {\n // An editor always opens on the STORED value, so the message from a\n // previous rejected attempt must not survive into it.\n setCellError(cellKey(row.id, column.id), null)\n setEditing({\n rowId: row.id,\n columnId: column.id,\n text: recordGridEditorText(column, row.values[column.id] ?? null),\n })\n },\n [setCellError, setEditing],\n )\n\n /** Apply one cell value optimistically, write it, and on failure put the\n * previous value back with the reason attached to the row. */\n const applyCellWrite = useCallback(\n async (row: RecordGridRow, column: RecordGridColumn, value: RecordGridValue) => {\n if (!onUpdate) return\n const values = { ...row.values, [column.id]: value }\n setOverlay((current) => withRecordGridUpdate(current, row.id, column.id, value))\n setRowError(row.id, null)\n setRowPending(row.id, true)\n\n let outcome: RecordGridWriteOutcome\n try {\n outcome = await onUpdate({ row, columnId: column.id, value, values })\n } catch (cause) {\n outcome = { succeeded: false, error: cause instanceof Error ? cause.message : String(cause) }\n }\n\n setRowPending(row.id, false)\n if (outcome.succeeded) {\n const canonical = outcome.value\n if (canonical) setOverlay((current) => withRecordGridServerRow(current, row.id, canonical))\n return\n }\n setOverlay((current) => withoutRecordGridUpdate(current, row.id, column.id))\n const rejected = formatRecordGridValue(column, value, locale)\n setRowError(\n row.id,\n rejected === ''\n ? `Could not save ${column.header}: ${outcome.error}`\n : `Could not save ${column.header} as ${rejected}: ${outcome.error}`,\n )\n },\n [locale, onUpdate, setRowError, setRowPending],\n )\n\n const commitEdit = useCallback(\n async (row: RecordGridRow, column: RecordGridColumn, text: string) => {\n const open = editingRef.current\n if (open === null || open.rowId !== row.id || open.columnId !== column.id) return\n if (settling.current) return\n settling.current = true\n try {\n const key = cellKey(row.id, column.id)\n const parsed = readRecordGridCell(column, text)\n if (!parsed.succeeded) {\n // The editor stays open holding the rejected text: the point of a\n // typed cell is that a person can correct it in place.\n setCellError(key, parsed.error)\n setEditing({ rowId: row.id, columnId: column.id, text })\n return\n }\n setCellError(key, null)\n setEditing(null)\n if (sameRecordGridValue(row.values[column.id], parsed.value)) return\n await applyCellWrite(row, column, parsed.value)\n } finally {\n settling.current = false\n }\n },\n [applyCellWrite, setCellError, setEditing],\n )\n\n const cancelEdit = useCallback(\n (row: RecordGridRow, column: RecordGridColumn) => {\n setCellError(cellKey(row.id, column.id), null)\n setEditing(null)\n focusCell(row.id, column.id)\n },\n [focusCell, setCellError, setEditing],\n )\n\n const performDelete = useCallback(\n async (row: RecordGridRow) => {\n if (!onDelete) return\n setConfirmDelete(null)\n setRowError(row.id, null)\n setOverlay((current) => withRecordGridRemoved(current, row.id))\n\n let outcome: RecordGridWriteOutcome\n try {\n outcome = await onDelete(row)\n } catch (cause) {\n outcome = { succeeded: false, error: cause instanceof Error ? cause.message : String(cause) }\n }\n if (outcome.succeeded) return\n setOverlay((current) => withoutRecordGridRemoved(current, row.id))\n setRowError(row.id, `Could not delete ${recordGridRowLabel(columns, row)}: ${outcome.error}`)\n },\n [columns, onDelete, setRowError],\n )\n\n const resetDraft = useCallback(() => {\n setDraft({ ...(newRowDefaults ?? {}) })\n setDraftErrors({})\n setDraftError(null)\n }, [newRowDefaults])\n\n const openAdd = useCallback(() => {\n resetDraft()\n setAdding(true)\n }, [resetDraft])\n\n const submitDraft = useCallback(async () => {\n if (!onCreate) return\n const validated = validateRecordGridRow(columns, draft)\n if (!validated.succeeded) {\n setDraftErrors(validated.cellErrors)\n setDraftError(validated.error)\n return\n }\n setDraftErrors({})\n setDraftError(null)\n\n draftCounter.current += 1\n const draftId = `${fieldPrefix}-draft-${draftCounter.current}`\n setOverlay((current) => withRecordGridCreated(current, { id: draftId, values: validated.value }))\n setCreating(true)\n\n let outcome: RecordGridCreateOutcome\n try {\n outcome = await onCreate(validated.value)\n } catch (cause) {\n outcome = { succeeded: false, error: cause instanceof Error ? cause.message : String(cause) }\n }\n\n setCreating(false)\n if (outcome.succeeded) {\n setOverlay((current) => withRecordGridServerRow(current, draftId, outcome.value))\n setAdding(false)\n resetDraft()\n return\n }\n // The optimistic row goes away and the form keeps what was typed: a\n // rejected create is correctable, never silently lost.\n setOverlay((current) => withoutRecordGridCreated(current, draftId))\n setDraftError(outcome.error)\n }, [columns, draft, fieldPrefix, onCreate, resetDraft])\n\n const handleGridKeyDown = useCallback(\n (event: KeyboardEvent<HTMLTableElement>) => {\n if (editing !== null) return\n const target = event.target as HTMLElement\n const rowId = target.dataset?.recordGridRow\n const columnId = target.dataset?.recordGridColumn\n if (rowId === undefined || columnId === undefined) return\n const rowIndex = visibleRows.findIndex((row) => row.id === rowId)\n const columnIndex = columns.findIndex((column) => column.id === columnId)\n if (rowIndex < 0 || columnIndex < 0) return\n\n if (event.key === 'Enter') {\n const row = visibleRows[rowIndex]\n const column = columns[columnIndex]\n if (!row || !column) return\n if (reviewing || !onUpdate || column.editable === false || row.readOnly === true) return\n if (column.kind === 'boolean') return\n if (!isRecordGridCellApplicable(column, row.values)) return\n event.preventDefault()\n beginEdit(row, column)\n return\n }\n if (!NAVIGATION_KEYS.has(event.key)) return\n event.preventDefault()\n\n let nextRow = rowIndex\n let nextColumn = columnIndex\n if (event.key === 'ArrowUp') nextRow = clamp(rowIndex - 1, visibleRows.length - 1)\n if (event.key === 'ArrowDown') nextRow = clamp(rowIndex + 1, visibleRows.length - 1)\n if (event.key === 'ArrowLeft') nextColumn = clamp(columnIndex - 1, columns.length - 1)\n if (event.key === 'ArrowRight') nextColumn = clamp(columnIndex + 1, columns.length - 1)\n if (event.key === 'Home') nextColumn = 0\n if (event.key === 'End') nextColumn = columns.length - 1\n\n const destinationRow = visibleRows[nextRow]\n const destinationColumn = columns[nextColumn]\n if (!destinationRow || !destinationColumn) return\n focusCell(destinationRow.id, destinationColumn.id)\n },\n [beginEdit, columns, editing, focusCell, onUpdate, reviewing, visibleRows],\n )\n\n // `idle` and `loading` render the same busy block — from the reader's\n // side, \"not started\" and \"in flight\" are the same wait\n // (`web-react/async`'s own rule for the same two variants).\n if (state.status === 'idle' || state.status === 'loading') {\n return (\n <div className={`space-y-3 ${className ?? ''}`}>\n {toolbar}\n <div\n role=\"status\"\n aria-busy=\"true\"\n aria-live=\"polite\"\n className=\"space-y-2 rounded-xl border border-card-edge bg-card p-4\"\n >\n <span className=\"sr-only\">Loading {caption}</span>\n {Array.from({ length: Math.max(1, loadingRowCount) }, (_, index) => (\n <div key={index} className=\"h-8 animate-pulse rounded-md bg-secondary\" aria-hidden />\n ))}\n </div>\n </div>\n )\n }\n\n if (state.status === 'error') {\n return (\n <div className={`space-y-3 ${className ?? ''}`}>\n {toolbar}\n <div role=\"alert\" className=\"rounded-xl border border-destructive/40 bg-destructive/10 px-4 py-4\">\n <p className=\"text-sm font-medium text-destructive\">{state.message}</p>\n <button\n type=\"button\"\n onClick={state.retry}\n className=\"mt-3 rounded-md border border-destructive/40 px-3 py-1.5 text-xs font-medium text-destructive transition hover:bg-destructive/10\"\n >\n Try again\n </button>\n </div>\n </div>\n )\n }\n\n const addForm =\n adding && onCreate && !reviewing ? (\n <AddRecordForm\n columns={columns}\n draft={draft}\n setDraft={setDraft}\n errors={draftErrors}\n formError={draftError}\n busy={creating}\n label={addLabel}\n fieldPrefix={fieldPrefix}\n onSubmit={() => void submitDraft()}\n onCancel={() => {\n setAdding(false)\n resetDraft()\n }}\n />\n ) : null\n\n // A review whose only diff is additions has no live rows — the empty state\n // would hide the very rows up for review, so the table renders instead.\n if (visibleRows.length === 0 && !reviewing) {\n return (\n <div className={`space-y-3 ${className ?? ''}`}>\n {toolbar}\n {addForm ?? (\n <div className=\"rounded-xl border border-dashed border-border px-6 py-10 text-center\">\n <p className=\"text-sm font-medium text-foreground\">{empty.title}</p>\n {empty.description && (\n <p className=\"mx-auto mt-1 max-w-md text-sm text-muted-foreground\">{empty.description}</p>\n )}\n <div className=\"mt-4 flex flex-wrap items-center justify-center gap-2\">\n {empty.action &&\n (isValidElement(empty.action) ? (\n empty.action\n ) : (\n <button\n type=\"button\"\n onClick={(empty.action as AsyncEmptyAction).onClick}\n className=\"rounded-md border border-border px-3 py-1.5 text-sm font-medium text-foreground transition hover:bg-accent\"\n >\n {(empty.action as AsyncEmptyAction).label}\n </button>\n ))}\n {onCreate && (\n <button\n type=\"button\"\n onClick={openAdd}\n className=\"rounded-md border border-border px-3 py-1.5 text-sm font-medium text-foreground transition hover:bg-accent\"\n >\n {addLabel}\n </button>\n )}\n </div>\n </div>\n )}\n </div>\n )\n }\n\n const hasFooter = columns.some((column) => column.footerValue !== undefined)\n const showActionsColumn = reviewing || onDelete !== undefined\n const columnSpan = columns.length + (showActionsColumn ? 1 : 0)\n\n const changedCount = (diffs ?? []).filter((diff) => diff.kind === 'changed').length\n const addedCount = addedRows.length\n const removedCount = (diffs ?? []).filter((diff) => diff.kind === 'removed').length\n const changedCellCount = (diffs ?? []).reduce((total, diff) => total + diff.cells.length, 0)\n const reviewSummary = [\n changedCount > 0 ? `${changedCount} changed (${changedCellCount} ${changedCellCount === 1 ? 'cell' : 'cells'})` : null,\n addedCount > 0 ? `${addedCount} added` : null,\n removedCount > 0 ? `${removedCount} removed` : null,\n ]\n .filter((part) => part !== null)\n .join(' · ')\n\n const reviewBar = reviewing ? (\n <div\n data-record-grid-review=\"\"\n className=\"flex flex-wrap items-center justify-between gap-3 rounded-xl border border-card-edge bg-card px-4 py-2.5\"\n >\n <div className=\"min-w-0\">\n <p className=\"text-[11px] font-semibold uppercase tracking-[0.05em] text-muted-foreground\">\n Proposed changes\n </p>\n <p className=\"mt-0.5 text-xs tabular-nums text-muted-foreground\">{reviewSummary}</p>\n </div>\n {(onAcceptAll || onRejectAll) && (\n <div className=\"flex items-center gap-2\">\n {onRejectAll && (\n <button\n type=\"button\"\n aria-label={`Reject all proposed changes to ${caption}`}\n onClick={onRejectAll}\n className=\"rounded-md border border-border px-3 py-1.5 text-xs font-medium text-muted-foreground transition hover:bg-accent\"\n >\n Reject all\n </button>\n )}\n {onAcceptAll && (\n <button\n type=\"button\"\n aria-label={`Accept all proposed changes to ${caption}`}\n onClick={onAcceptAll}\n className=\"rounded-md bg-success/10 px-3 py-1.5 text-xs font-medium text-success transition hover:bg-success/20\"\n >\n Accept all\n </button>\n )}\n </div>\n )}\n </div>\n ) : null\n\n return (\n <div className={`space-y-3 ${className ?? ''}`}>\n {toolbar}\n {reviewBar}\n <div className=\"overflow-x-auto rounded-xl border border-card-edge bg-card\">\n <table\n role=\"grid\"\n aria-label={caption}\n className=\"w-full border-collapse text-left text-sm\"\n onKeyDown={handleGridKeyDown}\n >\n <thead>\n <tr role=\"row\" className=\"border-b border-border text-xs uppercase tracking-[0.05em] text-muted-foreground\">\n {columns.map((column) => (\n <th\n key={column.id}\n role=\"columnheader\"\n scope=\"col\"\n className={`px-3 py-2 font-medium ${alignmentClass(column)}`}\n >\n {column.header}\n </th>\n ))}\n {showActionsColumn && (\n <th role=\"columnheader\" scope=\"col\" className=\"w-px px-3 py-2 font-medium\">\n <span className=\"sr-only\">{reviewing ? 'Review' : 'Row actions'}</span>\n </th>\n )}\n </tr>\n </thead>\n <tbody>\n {visibleRows.map((row) => {\n const rowLabel = recordGridRowLabel(columns, row)\n const pending = row.id in pendingRows\n const rowError = rowErrors[row.id]\n const rowDiff = reviewing ? diffByRow.get(row.id) : undefined\n const removedRow = rowDiff?.kind === 'removed'\n return (\n <Fragment key={row.id}>\n <tr\n role=\"row\"\n aria-busy={pending}\n data-record-grid-diff={rowDiff?.kind}\n className={`border-b border-border ${pending ? 'opacity-60' : ''} ${removedRow ? 'bg-destructive/[0.06]' : ''}`}\n >\n {columns.map((column) => {\n const key = cellKey(row.id, column.id)\n const applicable = isRecordGridCellApplicable(column, row.values)\n const editable =\n !reviewing &&\n onUpdate !== undefined &&\n column.editable !== false &&\n row.readOnly !== true &&\n applicable\n const value = row.values[column.id] ?? null\n const isEditing = editing?.rowId === row.id && editing.columnId === column.id\n const cellError = cellErrors[key]\n const active =\n activeFocus === null\n ? row.id === visibleRows[0]?.id && column.id === columns[0]?.id\n : activeFocus.rowId === row.id && activeFocus.columnId === column.id\n const source = row.sources?.[column.id]\n const errorId = `${fieldPrefix}-cell-error-${row.id}-${column.id}`\n const cellDiff = rowDiff?.kind === 'changed' ? diffCellByKey.get(key) : undefined\n\n if (isEditing && editable) {\n return (\n <td key={column.id} role=\"gridcell\" className={`px-3 py-1.5 ${alignmentClass(column)}`}>\n <CellEditor\n column={column}\n rowLabel={rowLabel}\n text={editing.text}\n invalid={cellError !== undefined}\n describedBy={cellError === undefined ? undefined : errorId}\n onText={(text) => setEditing({ rowId: row.id, columnId: column.id, text })}\n onCommit={(text) => void commitEdit(row, column, text)}\n onCancel={() => cancelEdit(row, column)}\n />\n {cellError !== undefined && (\n <p id={errorId} role=\"alert\" className=\"mt-1 text-xs leading-snug text-destructive\">\n {cellError}\n </p>\n )}\n </td>\n )\n }\n\n if (column.kind === 'boolean' && editable) {\n return (\n <td key={column.id} role=\"gridcell\" className={`px-3 py-2 ${alignmentClass(column)}`}>\n <input\n type=\"checkbox\"\n checked={value === true}\n aria-label={`${column.header}, ${rowLabel}`}\n data-record-grid-row={row.id}\n data-record-grid-column={column.id}\n tabIndex={active ? 0 : -1}\n ref={(node) => {\n cellRefs.current.set(key, node)\n }}\n onFocus={() => setFocus({ rowId: row.id, columnId: column.id })}\n onChange={(event) => void applyCellWrite(row, column, event.target.checked)}\n className=\"h-4 w-4 rounded border-border accent-primary\"\n />\n </td>\n )\n }\n\n const display = applicable ? formatRecordGridValue(column, value, locale) : ''\n return (\n <td\n key={column.id}\n role=\"gridcell\"\n aria-readonly={editable ? undefined : true}\n data-record-grid-row={row.id}\n data-record-grid-column={column.id}\n tabIndex={active ? 0 : -1}\n ref={(node) => {\n cellRefs.current.set(key, node)\n }}\n onFocus={() => setFocus({ rowId: row.id, columnId: column.id })}\n onClick={() => {\n if (editable) beginEdit(row, column)\n }}\n className={`px-3 py-2 outline-none focus:ring-2 focus:ring-inset focus:ring-primary/50 ${alignmentClass(\n column,\n )} ${editable ? 'cursor-text' : ''}`}\n >\n <span className=\"inline-flex max-w-full items-center gap-1.5\">\n {column.id === columns[0]?.id && removedRow && (\n <span className=\"inline-flex shrink-0 rounded border border-destructive/60 px-1 py-px text-[11px] font-semibold uppercase tracking-[0.05em] text-destructive\">\n Remove\n </span>\n )}\n {cellDiff ? (\n <span className=\"inline-flex max-w-full flex-wrap items-baseline gap-x-1.5\">\n <span className=\"tabular-nums text-destructive line-through decoration-destructive/60\">\n {formatRecordGridValue(column, cellDiff.before, locale) || '—'}\n </span>\n <span aria-hidden=\"true\" className=\"text-muted-foreground\">\n →\n </span>\n <span className=\"tabular-nums font-medium text-success\">\n {formatRecordGridValue(column, cellDiff.after, locale) || '—'}\n </span>\n </span>\n ) : (\n <span\n className={\n removedRow\n ? 'truncate text-destructive line-through decoration-destructive/60'\n : display === ''\n ? 'text-muted-foreground'\n : 'truncate text-foreground'\n }\n >\n {display === '' ? (applicable ? '—' : 'n/a') : display}\n </span>\n )}\n {source && (\n <SourceMarker\n panelId={`${fieldPrefix}-source-${row.id}-${column.id}`}\n columnHeader={column.header}\n rowLabel={rowLabel}\n source={source}\n open={openSource === key}\n onToggle={() => setOpenSource((current) => (current === key ? null : key))}\n />\n )}\n </span>\n </td>\n )\n })}\n {showActionsColumn && (\n <td role=\"gridcell\" className=\"px-3 py-2 text-right\">\n {reviewing ? (\n rowDiff && (\n <ReviewActions\n rowId={row.id}\n kind={rowDiff.kind}\n rowLabel={rowLabel}\n onAccept={onAcceptRow}\n onReject={onRejectRow}\n />\n )\n ) : row.readOnly === true ? null : confirmDelete === row.id ? (\n <span className=\"inline-flex items-center gap-1.5\">\n <button\n type=\"button\"\n aria-label={`Confirm delete ${rowLabel}`}\n onClick={() => void performDelete(row)}\n className=\"rounded-md bg-destructive/10 px-2 py-1 text-xs font-medium text-destructive transition hover:bg-destructive/20\"\n >\n Delete\n </button>\n <button\n type=\"button\"\n aria-label={`Keep ${rowLabel}`}\n onClick={() => setConfirmDelete(null)}\n className=\"rounded-md border border-border px-2 py-1 text-xs font-medium text-muted-foreground transition hover:bg-accent\"\n >\n Cancel\n </button>\n </span>\n ) : (\n <button\n type=\"button\"\n aria-label={`Delete ${rowLabel}`}\n onClick={() => setConfirmDelete(row.id)}\n className=\"rounded-md p-1.5 text-muted-foreground transition hover:bg-destructive/10 hover:text-destructive\"\n >\n <svg\n viewBox=\"0 0 24 24\"\n className=\"h-3.5 w-3.5\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n >\n <polyline points=\"3 6 5 6 21 6\" />\n <path d=\"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2\" />\n </svg>\n </button>\n )}\n </td>\n )}\n </tr>\n {rowError !== undefined && (\n <tr role=\"row\" className=\"border-b border-border\">\n <td role=\"gridcell\" colSpan={columnSpan} className=\"px-3 pb-2\">\n <p role=\"alert\" className=\"rounded-md bg-destructive/10 px-2.5 py-1.5 text-xs text-destructive\">\n {rowError}\n </p>\n </td>\n </tr>\n )}\n </Fragment>\n )\n })}\n {reviewing &&\n addedRows.map((row) => {\n const rowLabel = recordGridRowLabel(columns, row)\n return (\n <tr\n key={row.id}\n role=\"row\"\n data-record-grid-diff=\"added\"\n className=\"border-b border-border bg-success/[0.06]\"\n >\n {columns.map((column, columnIndex) => {\n const applicable = isRecordGridCellApplicable(column, row.values)\n const value = row.values[column.id] ?? null\n const display = applicable ? formatRecordGridValue(column, value, locale) : ''\n return (\n <td key={column.id} role=\"gridcell\" className={`px-3 py-2 ${alignmentClass(column)}`}>\n <span className=\"inline-flex max-w-full items-center gap-1.5\">\n {columnIndex === 0 && (\n <span className=\"inline-flex shrink-0 rounded border border-success/60 px-1 py-px text-[11px] font-semibold uppercase tracking-[0.05em] text-success\">\n New\n </span>\n )}\n <span\n className={`tabular-nums ${display === '' ? 'text-muted-foreground' : 'truncate text-foreground'}`}\n >\n {display === '' ? (applicable ? '—' : 'n/a') : display}\n </span>\n </span>\n </td>\n )\n })}\n <td role=\"gridcell\" className=\"px-3 py-2 text-right\">\n <ReviewActions\n rowId={row.id}\n kind=\"added\"\n rowLabel={rowLabel}\n onAccept={onAcceptRow}\n onReject={onRejectRow}\n />\n </td>\n </tr>\n )\n })}\n </tbody>\n {hasFooter && (\n <tfoot>\n <tr role=\"row\" className=\"border-t-2 border-border\">\n {columns.map((column) => (\n <td\n key={column.id}\n role=\"gridcell\"\n className={`px-3 py-2 text-sm font-semibold text-foreground ${alignmentClass(column)}`}\n >\n {column.footerValue ? formatRecordGridValue(column, column.footerValue(visibleRows), locale) : ''}\n </td>\n ))}\n {showActionsColumn && <td role=\"gridcell\" />}\n </tr>\n </tfoot>\n )}\n </table>\n </div>\n\n {onCreate &&\n !reviewing &&\n (addForm ?? (\n <button\n type=\"button\"\n onClick={openAdd}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border px-3 py-1.5 text-sm font-medium text-foreground transition hover:bg-accent\"\n >\n <svg\n viewBox=\"0 0 24 24\"\n className=\"h-3.5 w-3.5\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden\n >\n <line x1=\"12\" y1=\"5\" x2=\"12\" y2=\"19\" />\n <line x1=\"5\" y1=\"12\" x2=\"19\" y2=\"12\" />\n </svg>\n {addLabel}\n </button>\n ))}\n </div>\n )\n}\n\ninterface ReviewActionsProps {\n rowId: string\n kind: RecordGridRowDiff['kind']\n rowLabel: string\n onAccept?: (rowId: string) => void\n onReject?: (rowId: string) => void\n}\n\n/** Per-row accept/reject in review mode. The verbs name what accepting DOES:\n * a changed row is written, an added row is adopted, a removed row is\n * deleted. */\nfunction ReviewActions({ rowId, kind, rowLabel, onAccept, onReject }: ReviewActionsProps) {\n const noun = kind === 'changed' ? `proposed change to ${rowLabel}` : kind === 'added' ? `new row ${rowLabel}` : `removal of ${rowLabel}`\n return (\n <span className=\"inline-flex items-center gap-1.5\">\n {onReject && (\n <button\n type=\"button\"\n aria-label={`Reject ${noun}`}\n onClick={() => onReject(rowId)}\n className=\"rounded-md border border-border px-2 py-1 text-xs font-medium text-muted-foreground transition hover:bg-accent\"\n >\n Reject\n </button>\n )}\n {onAccept && (\n <button\n type=\"button\"\n aria-label={`Accept ${noun}`}\n onClick={() => onAccept(rowId)}\n className=\"rounded-md bg-success/10 px-2 py-1 text-xs font-medium text-success transition hover:bg-success/20\"\n >\n Accept\n </button>\n )}\n </span>\n )\n}\n\ninterface CellEditorProps {\n column: RecordGridColumn\n rowLabel: string\n text: string\n invalid: boolean\n describedBy?: string\n onText: (text: string) => void\n onCommit: (text: string) => void\n onCancel: () => void\n}\n\n/** The in-cell control for one edit. Enter commits, Escape cancels, blur\n * commits — the editor never disappears without deciding. */\nfunction CellEditor({ column, rowLabel, text, invalid, describedBy, onText, onCommit, onCancel }: CellEditorProps) {\n const shared = {\n 'aria-label': `${column.header}, ${rowLabel}`,\n 'aria-invalid': invalid ? true : undefined,\n 'aria-describedby': describedBy,\n autoFocus: true,\n className: INPUT_CLASS,\n }\n\n if (column.kind === 'select') {\n return (\n <select\n {...shared}\n value={text}\n onChange={(event) => {\n onText(event.target.value)\n onCommit(event.target.value)\n }}\n onKeyDown={(event) => {\n if (event.key === 'Escape') {\n event.preventDefault()\n onCancel()\n }\n }}\n onBlur={() => onCommit(text)}\n >\n <option value=\"\">—</option>\n {column.options.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n )\n }\n\n const keyDown = (event: KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => {\n const multiline = column.kind === 'text' && column.multiline === true\n if (event.key === 'Enter' && !multiline) {\n event.preventDefault()\n onCommit(text)\n return\n }\n if (event.key === 'Escape') {\n event.preventDefault()\n onCancel()\n }\n }\n\n if (column.kind === 'text' && column.multiline === true) {\n return (\n <textarea\n {...shared}\n rows={3}\n value={text}\n onChange={(event) => onText(event.target.value)}\n onKeyDown={keyDown}\n onBlur={() => onCommit(text)}\n />\n )\n }\n\n return (\n <input\n {...shared}\n type={column.kind === 'date' ? 'date' : 'text'}\n inputMode={column.kind === 'number' || column.kind === 'currency' ? 'decimal' : undefined}\n value={text}\n onChange={(event) => onText(event.target.value)}\n onKeyDown={keyDown}\n onBlur={() => onCommit(text)}\n />\n )\n}\n\ninterface SourceMarkerProps {\n panelId: string\n columnHeader: string\n rowLabel: string\n source: RecordGridCellSource\n open: boolean\n onToggle: () => void\n}\n\n/**\n * The per-cell provenance affordance: a marker that opens the quote, the\n * source's name, and a link to it.\n *\n * Renders its panel through `PopoverSurface` (not an in-place `absolute`\n * span): the grid is a scrollable table by construction — `overflow-x-auto`/\n * `overflow-y-auto` on the scroll region is the whole reason `RecordGrid`\n * stays usable with many columns/rows — and that scroll container clips every\n * positioned descendant whose containing block sits inside it exactly the way\n * the composer's control rail clipped the model/thinking menus (see AGENTS.md\n * \"UI chrome ownership (picker canon)\"). `open` stays parent-owned (only one\n * marker across the grid may be open at a time); `usePopover` never calls\n * `setOpen(true)` itself — its listeners only run while `open` is already\n * true — so translating its `setOpen(false)` into the existing `onToggle`\n * flip is safe.\n */\nfunction SourceMarker({ panelId, columnHeader, rowLabel, source, open, onToggle }: SourceMarkerProps) {\n // An omitted basis is the DEFAULT path every first integration takes, so it\n // must resolve to the weakest claim, not the strongest. `extracted` (and\n // `source` before the rename) told the reader the figure was read out of a\n // document the caller never named — a factual claim about a document that may\n // not exist. `asserted` is the union's own \"nothing outside the model behind\n // it\", which is exactly what a caller who stated no basis has established.\n const basis: RecordGridSourceBasis = source.basis ?? 'asserted'\n const setOpen = useCallback(\n (next: boolean) => {\n if (!next) onToggle()\n },\n [onToggle],\n )\n const { containerRef, triggerRef, panelRef, triggerProps } = usePopover(open, setOpen)\n return (\n <span ref={containerRef} className=\"relative inline-flex\">\n <button\n type=\"button\"\n {...triggerProps}\n aria-label={`Source for ${columnHeader}, ${rowLabel}`}\n aria-controls={open ? panelId : undefined}\n title={BASIS_TITLES[basis]}\n onClick={(event) => {\n event.stopPropagation()\n onToggle()\n }}\n className={`inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-full border ${BASIS_TONES[basis]}`}\n >\n <svg\n viewBox=\"0 0 24 24\"\n className=\"h-2.5 w-2.5\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n aria-hidden\n >\n <path d=\"M9 8h6M9 12h6M9 16h3\" />\n </svg>\n </button>\n <PopoverSurface\n open={open}\n id={panelId}\n role=\"note\"\n triggerRef={triggerRef}\n panelRef={panelRef}\n className={`w-64 rounded-lg border border-card-edge bg-popover p-3 text-left ${OVERLAY_SHADOW}`}\n >\n {source.quote && (\n <span className=\"block border-l-2 border-primary/50 pl-2 text-xs italic leading-snug text-foreground\">\n “{source.quote}”\n </span>\n )}\n <span className=\"mt-2 block text-xs text-muted-foreground\">\n {source.label ?? 'Source'}\n {source.locator ? ` · ${source.locator}` : ''}\n {` · ${BASIS_TITLES[basis]}`}\n </span>\n {source.href && (\n <a\n href={source.href}\n target=\"_blank\"\n rel=\"noreferrer\"\n onClick={(event) => event.stopPropagation()}\n className=\"mt-1.5 inline-block text-xs font-medium text-primary underline-offset-2 hover:underline\"\n >\n Open source\n </a>\n )}\n </PopoverSurface>\n </span>\n )\n}\n\ninterface AddRecordFormProps {\n columns: readonly RecordGridColumn[]\n draft: Record<string, RecordGridValue>\n setDraft: (next: Record<string, RecordGridValue>) => void\n errors: Readonly<Record<string, string>>\n formError: string | null\n busy: boolean\n label: string\n fieldPrefix: string\n onSubmit: () => void\n onCancel: () => void\n}\n\n/** The inline add form. Every control is labelled, every rejection names its\n * field, and a column whose `dependsOn` is unsatisfied is not rendered — the\n * nested sub-form case. */\nfunction AddRecordForm({\n columns,\n draft,\n setDraft,\n errors,\n formError,\n busy,\n label,\n fieldPrefix,\n onSubmit,\n onCancel,\n}: AddRecordFormProps) {\n const groups = useMemo(() => groupColumns(columns), [columns])\n\n return (\n <form\n noValidate\n aria-label={label}\n onSubmit={(event) => {\n event.preventDefault()\n onSubmit()\n }}\n className=\"space-y-4 rounded-xl border border-card-edge bg-card p-4\"\n >\n <h3 className=\"text-sm font-semibold text-foreground\">{label}</h3>\n {formError !== null && (\n <p role=\"alert\" className=\"rounded-md bg-destructive/10 px-3 py-2 text-xs text-destructive\">\n {formError}\n </p>\n )}\n {groups.map((group) => {\n const fields = group.columns.filter((column) => isRecordGridCellApplicable(column, draft))\n if (fields.length === 0) return null\n return (\n <fieldset\n key={group.label ?? '_'}\n className={group.label === null ? 'min-w-0' : 'min-w-0 rounded-lg border border-card-edge p-3'}\n >\n {group.label !== null && (\n <legend className=\"px-1 text-xs font-medium text-muted-foreground\">{group.label}</legend>\n )}\n <div className=\"grid gap-3 sm:grid-cols-2\">\n {fields.map((column) => {\n const fieldId = `${fieldPrefix}-${column.id}`\n const errorId = `${fieldId}-error`\n const message = errors[column.id]\n return (\n <div\n key={column.id}\n className={column.kind === 'text' && column.multiline === true ? 'sm:col-span-2' : ''}\n >\n <label htmlFor={fieldId} className=\"mb-1 block text-xs font-medium text-muted-foreground\">\n {column.header}\n {column.required === true && <span className=\"ml-0.5 text-destructive\">*</span>}\n </label>\n <DraftField\n column={column}\n id={fieldId}\n value={draft[column.id] ?? null}\n invalid={message !== undefined}\n describedBy={message === undefined ? undefined : errorId}\n onValue={(next) => setDraft({ ...draft, [column.id]: next })}\n />\n {column.hint && <p className=\"mt-1 text-xs text-muted-foreground\">{column.hint}</p>}\n {message !== undefined && (\n <p id={errorId} role=\"alert\" className=\"mt-1 text-xs text-destructive\">\n {message}\n </p>\n )}\n </div>\n )\n })}\n </div>\n </fieldset>\n )\n })}\n <div className=\"flex items-center gap-2\">\n <button\n type=\"submit\"\n disabled={busy}\n className=\"rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground transition hover:bg-primary/90 disabled:opacity-50\"\n >\n {busy ? 'Saving…' : 'Save'}\n </button>\n <button\n type=\"button\"\n onClick={onCancel}\n className=\"rounded-md border border-border px-3 py-1.5 text-sm font-medium text-muted-foreground transition hover:bg-accent\"\n >\n Cancel\n </button>\n </div>\n </form>\n )\n}\n\ninterface DraftFieldProps {\n column: RecordGridColumn\n id: string\n value: RecordGridValue\n invalid: boolean\n describedBy?: string\n onValue: (value: RecordGridValue) => void\n}\n\n/** One labelled control in the add form. A half-typed number is held verbatim\n * rather than coerced mid-keystroke; submit is where it is refused with a\n * reason. */\nfunction DraftField({ column, id, value, invalid, describedBy, onValue }: DraftFieldProps) {\n const shared = {\n id,\n 'aria-invalid': invalid ? true : undefined,\n 'aria-describedby': describedBy,\n }\n\n if (column.kind === 'boolean') {\n return (\n <input\n {...shared}\n type=\"checkbox\"\n checked={value === true}\n onChange={(event) => onValue(event.target.checked)}\n className=\"h-4 w-4 rounded border-border accent-primary\"\n />\n )\n }\n\n if (column.kind === 'select') {\n return (\n <select\n {...shared}\n className={INPUT_CLASS}\n value={typeof value === 'string' ? value : ''}\n onChange={(event) => onValue(event.target.value === '' ? null : event.target.value)}\n >\n <option value=\"\">—</option>\n {column.options.map((option) => (\n <option key={option.value} value={option.value}>\n {option.label}\n </option>\n ))}\n </select>\n )\n }\n\n if (column.kind === 'text' && column.multiline === true) {\n return (\n <textarea\n {...shared}\n className={INPUT_CLASS}\n rows={3}\n value={typeof value === 'string' ? value : ''}\n onChange={(event) => onValue(event.target.value === '' ? null : event.target.value)}\n />\n )\n }\n\n if (column.kind === 'number' || column.kind === 'currency') {\n return (\n <input\n {...shared}\n className={INPUT_CLASS}\n type=\"text\"\n inputMode=\"decimal\"\n value={value === null ? '' : String(value)}\n onChange={(event) => {\n const raw = event.target.value\n if (raw.trim() === '') {\n onValue(null)\n return\n }\n const parsed = readRecordGridCell(column, raw)\n onValue(parsed.succeeded ? parsed.value : raw)\n }}\n />\n )\n }\n\n return (\n <input\n {...shared}\n className={INPUT_CLASS}\n type={column.kind === 'date' ? 'date' : 'text'}\n value={typeof value === 'string' ? value : ''}\n onChange={(event) => onValue(event.target.value === '' ? null : event.target.value)}\n />\n )\n}\n","/**\n * The pure half of the editable record grid: the typed column vocabulary, the\n * per-cell parse/validate rules, display + editor formatting, and the\n * optimistic overlay a caller's rows are projected through.\n *\n * Zero React, zero DOM — a product can validate a row on a worker before it\n * ever reaches storage, and the component in `./record-grid` renders exactly\n * what these functions decide.\n *\n * Every domain word is a caller parameter: the module knows no column names,\n * no currencies, no option sets. What it owns is the mechanism four verticals\n * each re-derived — typed cells, an error a person can act on, and an\n * optimistic edit that can be taken back.\n */\n\nimport type { ProvenanceBasis } from './provenance-model'\n\n/** The value one cell can hold. `null` is \"no value on file\". */\nexport type RecordGridValue = string | number | boolean | null\n\n/** Typed outcome for one cell. Callers MUST inspect `succeeded` before reading\n * `value`; nothing here throws. */\nexport type RecordGridCellOutcome =\n | { succeeded: true; value: RecordGridValue }\n | { succeeded: false; error: string }\n\n/** Typed outcome for a whole row of inputs (the add form). `cellErrors` is\n * keyed by column id so each control can render its own message. */\nexport type RecordGridRowOutcome =\n | { succeeded: true; value: Record<string, RecordGridValue> }\n | { succeeded: false; error: string; cellErrors: Readonly<Record<string, string>> }\n\n/** Build a cell success outcome. */\nexport function recordGridOk(value: RecordGridValue): RecordGridCellOutcome {\n return { succeeded: true, value }\n}\n\n/** Build a cell failure outcome carrying the message shown next to the cell. */\nexport function recordGridFail(error: string): RecordGridCellOutcome {\n return { succeeded: false, error }\n}\n\n/**\n * How a value came to sit in a cell. Rendered as the provenance tone.\n *\n * A type alias of `./provenance-model`'s `ProvenanceBasis`, not a lookalike:\n * two vocabularies for the same concept — a grid cell's origin — shipped one\n * day apart and would have drifted the moment either one added a value. The\n * grid gains `asserted` for free (a cell an agent claimed with nothing behind\n * it), where the caller previously had no way to say that.\n */\nexport type RecordGridSourceBasis = ProvenanceBasis\n\n/**\n * Where one cell's value came from. Optional on every row — a grid over data\n * with no lineage renders identically without it.\n */\nexport interface RecordGridCellSource {\n /** The text in the source that supports this value. */\n quote?: string\n /** Human name of the source document, message, or system. */\n label?: string\n /** Click-through to the source. */\n href?: string\n /** Position inside the source: a page, a line, a span — the caller's words. */\n locator?: string\n /** How the value got here. Absent renders as `asserted` — the weakest claim\n * in the union — because a caller that stated no basis has established\n * nothing about a document, and an omission must never read as one. */\n basis?: RecordGridSourceBasis\n}\n\n/** One option of a `select` column. */\nexport interface RecordGridSelectOption {\n value: string\n label: string\n}\n\n/** A cell is only editable, rendered, and validated when the column it depends\n * on holds `equals`. This is how a nested sub-form (a vesting schedule behind\n * a \"has vesting\" toggle) stays a set of flat, individually-typed columns. */\nexport interface RecordGridDependency {\n column: string\n equals: RecordGridValue\n}\n\n/** Fields every column kind carries. */\nexport interface RecordGridColumnBase {\n /** Key into a row's `values` bag. */\n id: string\n /** Column heading, and the accessible name of every control in the column. */\n header: string\n /** Short hint rendered under the control in the add form. */\n hint?: string\n /** An empty cell is rejected. */\n required?: boolean\n /** Cells are editable unless this is `false`. */\n editable?: boolean\n /** Cell alignment. Numeric kinds default to `right`. */\n align?: 'left' | 'right'\n /** Groups this column under a labelled sub-form in the add form. */\n group?: string\n /** Only applicable when another column holds a given value. */\n dependsOn?: RecordGridDependency\n /** Extra rule, run after the kind's own checks pass. Return the message to\n * reject with, or `null` to accept. */\n validate?: (value: RecordGridValue) => string | null\n /** Column summary rendered in the footer row. */\n footerValue?: (rows: readonly RecordGridRow[]) => RecordGridValue\n}\n\n/** Free text, optionally length- or pattern-constrained. */\nexport interface RecordGridTextColumn extends RecordGridColumnBase {\n kind: 'text'\n minLength?: number\n maxLength?: number\n pattern?: RegExp\n /** Message when `pattern` rejects. Without it the pattern source is shown. */\n patternMessage?: string\n /** Render a textarea instead of a single-line input. */\n multiline?: boolean\n}\n\n/** A plain number. */\nexport interface RecordGridNumberColumn extends RecordGridColumnBase {\n kind: 'number'\n min?: number\n max?: number\n integer?: boolean\n /** Passed through to the editor's `step`. */\n step?: number\n}\n\n/** A money amount. The currency is a caller parameter — this module bakes no\n * domain value, so a product with two currencies declares two columns. */\nexport interface RecordGridCurrencyColumn extends RecordGridColumnBase {\n kind: 'currency'\n /** ISO 4217 code, e.g. `USD`. */\n currency: string\n min?: number\n max?: number\n /** Fraction digits for display. Defaults to the currency's own. */\n fractionDigits?: number\n}\n\n/** A calendar date held as `YYYY-MM-DD`; no time, no zone. */\nexport interface RecordGridDateColumn extends RecordGridColumnBase {\n kind: 'date'\n /** Earliest accepted date, `YYYY-MM-DD`. */\n min?: string\n /** Latest accepted date, `YYYY-MM-DD`. */\n max?: string\n}\n\n/** One of a closed set of values. */\nexport interface RecordGridSelectColumn extends RecordGridColumnBase {\n kind: 'select'\n options: readonly RecordGridSelectOption[]\n}\n\n/** A checkbox. */\nexport interface RecordGridBooleanColumn extends RecordGridColumnBase {\n kind: 'boolean'\n /** Label for `true`. Defaults to `Yes`. */\n trueLabel?: string\n /** Label for `false`. Defaults to `No`. */\n falseLabel?: string\n}\n\n/** Every column shape the grid renders. */\nexport type RecordGridColumn =\n | RecordGridTextColumn\n | RecordGridNumberColumn\n | RecordGridCurrencyColumn\n | RecordGridDateColumn\n | RecordGridSelectColumn\n | RecordGridBooleanColumn\n\n/** One row: an id, a flat value bag keyed by column id, and optional per-cell\n * provenance. A record-backed product maps its fold output straight onto\n * this — one entry per cell, its quote and link in `sources`. */\nexport interface RecordGridRow {\n id: string\n values: Readonly<Record<string, RecordGridValue>>\n /** Per-cell provenance, keyed by column id. */\n sources?: Readonly<Record<string, RecordGridCellSource>>\n /** No cell in this row may be edited or deleted. */\n readOnly?: boolean\n /** Accessible name for the row's own controls. Falls back to the first text\n * or select column's value, then the row id. */\n label?: string\n}\n\n/** True when the column's dependency (if any) is satisfied by the row's other\n * values. An inapplicable cell is never required, never validated, and never\n * editable. */\nexport function isRecordGridCellApplicable(\n column: RecordGridColumn,\n values: Readonly<Record<string, RecordGridValue>>,\n): boolean {\n const dependency = column.dependsOn\n if (!dependency) return true\n const held = values[dependency.column] ?? null\n return sameRecordGridValue(held, dependency.equals)\n}\n\n/** Value equality across the grid's value union, treating `undefined` as\n * `null` so an absent key and an explicit null never read as a change. */\nexport function sameRecordGridValue(a: RecordGridValue | undefined, b: RecordGridValue | undefined): boolean {\n return Object.is(a ?? null, b ?? null)\n}\n\n// ── proposed changes (row diff) ────────────────────────────────────────────\n\n/**\n * A proposed change set, diffed against the live rows by\n * {@link diffRecordGridProposal}. The grid's review mode renders exactly what\n * this shape declares — it owns no opinion about where the proposal came from\n * (an agent's `submit_proposal` call, a record store's pending entries).\n */\nexport interface RecordGridProposal {\n /** Proposed new cell values for EXISTING rows: row id → column id → value.\n * Only cells that differ from the live value diff; an update that restates\n * the current value is not a change. An id with no live row is ignored —\n * adding a row is `additions`' job. */\n updates?: Readonly<Record<string, Readonly<Record<string, RecordGridValue>>>>\n /** Proposed new rows. An addition whose id already names a live row is\n * ignored — changing an existing row is `updates`' job. */\n additions?: readonly RecordGridRow[]\n /** Live row ids proposed for removal. Unknown ids are ignored. */\n removals?: readonly string[]\n}\n\n/** One cell whose proposed value differs from the live one. */\nexport interface RecordGridCellDiff {\n columnId: string\n /** The live value — what rejecting keeps. */\n before: RecordGridValue\n /** The proposed value — what accepting writes. */\n after: RecordGridValue\n}\n\nexport type RecordGridRowDiffKind = 'changed' | 'added' | 'removed'\n\n/** One row's verdict: what the proposal does to it. */\nexport interface RecordGridRowDiff {\n rowId: string\n kind: RecordGridRowDiffKind\n /** The differing cells. Empty for `added`/`removed` — every cell of those is\n * part of the change by definition. */\n cells: readonly RecordGridCellDiff[]\n /** The live row for `changed`/`removed`, the proposed row for `added`. */\n row: RecordGridRow\n}\n\n/**\n * Diff a proposal against the live rows. Pure and deterministic: input order\n * in, diff order out — updates follow `rows` order, removals follow `rows`\n * order, additions follow the proposal's order. A row whose update bag diffs\n * to nothing produces no entry, so a no-op proposal yields an empty diff and\n * the grid has nothing to review.\n */\nexport function diffRecordGridProposal(\n rows: readonly RecordGridRow[],\n proposal: RecordGridProposal,\n): RecordGridRowDiff[] {\n const updates = proposal.updates ?? {}\n const removals = new Set(proposal.removals ?? [])\n const additions = proposal.additions ?? []\n const liveIds = new Set(rows.map((row) => row.id))\n\n const diffs: RecordGridRowDiff[] = []\n for (const row of rows) {\n if (removals.has(row.id)) {\n diffs.push({ rowId: row.id, kind: 'removed', cells: [], row })\n continue\n }\n const patch = updates[row.id]\n if (patch === undefined) continue\n const cells: RecordGridCellDiff[] = []\n for (const [columnId, after] of Object.entries(patch)) {\n const before = row.values[columnId] ?? null\n if (!sameRecordGridValue(before, after)) cells.push({ columnId, before, after })\n }\n if (cells.length > 0) diffs.push({ rowId: row.id, kind: 'changed', cells, row })\n }\n for (const row of additions) {\n if (liveIds.has(row.id)) continue\n diffs.push({ rowId: row.id, kind: 'added', cells: [], row })\n }\n return diffs\n}\n\n// ── parsing ───────────────────────────────────────────────────────────────\n\nconst NUMERIC = /^[+-]?(?:\\d+(?:\\.\\d+)?|\\.\\d+)$/\nconst ISO_DATE = /^\\d{4}-\\d{2}-\\d{2}$/\n\n/**\n * A number as a person types it: grouping separators, a leading currency\n * symbol, and accounting parentheses for a negative are all accepted. Anything\n * else is rejected rather than coerced — `12abc` is a typo to correct, not the\n * number 12.\n */\nfunction parseNumericText(raw: string): { empty: true } | { value: number } | { invalid: true } {\n const trimmed = raw.trim()\n if (trimmed === '') return { empty: true }\n const parenthesized = /^\\(.+\\)$/.test(trimmed)\n const body = (parenthesized ? trimmed.slice(1, -1).trim() : trimmed)\n .replace(/[\\s,]/g, '')\n .replace(/^([+-]?)\\p{Sc}/u, '$1')\n if (!NUMERIC.test(body)) return { invalid: true }\n const parsed = Number(body)\n if (!Number.isFinite(parsed)) return { invalid: true }\n return { value: parenthesized ? -parsed : parsed }\n}\n\n/** True when `text` is a real calendar date in `YYYY-MM-DD`. */\nfunction isCalendarDate(text: string): boolean {\n if (!ISO_DATE.test(text)) return false\n const [year, month, day] = text.split('-').map((part) => Number(part))\n if (month === undefined || day === undefined || year === undefined) return false\n if (month < 1 || month > 12 || day < 1) return false\n const stamp = Date.UTC(year, month - 1, day)\n const date = new Date(stamp)\n return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day\n}\n\n/**\n * Turn what an editor control produced into a typed value. Syntax only —\n * range, length, and membership are {@link validateRecordGridCell}'s job.\n */\nexport function parseRecordGridInput(column: RecordGridColumn, raw: string): RecordGridCellOutcome {\n switch (column.kind) {\n case 'text': {\n const trimmed = raw.trim()\n return recordGridOk(trimmed === '' ? null : trimmed)\n }\n case 'select': {\n const trimmed = raw.trim()\n return recordGridOk(trimmed === '' ? null : trimmed)\n }\n case 'boolean': {\n const trimmed = raw.trim().toLowerCase()\n if (trimmed === '') return recordGridOk(null)\n if (trimmed === 'true') return recordGridOk(true)\n if (trimmed === 'false') return recordGridOk(false)\n return recordGridFail(`${column.header} must be true or false — got “${raw}”.`)\n }\n case 'date': {\n const trimmed = raw.trim()\n if (trimmed === '') return recordGridOk(null)\n if (!isCalendarDate(trimmed)) {\n return recordGridFail(`${column.header} must be a date in YYYY-MM-DD form — got “${raw}”.`)\n }\n return recordGridOk(trimmed)\n }\n case 'number':\n case 'currency': {\n const parsed = parseNumericText(raw)\n if ('empty' in parsed) return recordGridOk(null)\n if ('invalid' in parsed) return recordGridFail(`${column.header} must be a number — got “${raw}”.`)\n return recordGridOk(parsed.value)\n }\n }\n}\n\n// ── validation ────────────────────────────────────────────────────────────\n\nfunction describeOptions(column: RecordGridSelectColumn): string {\n return column.options.map((option) => option.label).join(', ')\n}\n\n/**\n * Check one already-typed value against its column. Returns the value the grid\n * should store (empty text normalizes to `null`) or the message a person can\n * act on.\n */\nexport function validateRecordGridCell(column: RecordGridColumn, value: RecordGridValue): RecordGridCellOutcome {\n const normalized = value === '' ? null : value\n if (normalized === null) {\n if (column.required) return recordGridFail(`${column.header} is required.`)\n const custom = column.validate?.(null)\n return custom === undefined || custom === null ? recordGridOk(null) : recordGridFail(custom)\n }\n\n switch (column.kind) {\n case 'text': {\n if (typeof normalized !== 'string') return recordGridFail(`${column.header} must be text.`)\n if (column.minLength !== undefined && normalized.length < column.minLength) {\n return recordGridFail(`${column.header} must be at least ${column.minLength} characters — got ${normalized.length}.`)\n }\n if (column.maxLength !== undefined && normalized.length > column.maxLength) {\n return recordGridFail(`${column.header} must be at most ${column.maxLength} characters — got ${normalized.length}.`)\n }\n if (column.pattern && !column.pattern.test(normalized)) {\n return recordGridFail(column.patternMessage ?? `${column.header} does not match ${column.pattern.source}.`)\n }\n break\n }\n case 'number':\n case 'currency': {\n if (typeof normalized !== 'number' || !Number.isFinite(normalized)) {\n return recordGridFail(`${column.header} must be a number.`)\n }\n if (column.kind === 'number' && column.integer === true && !Number.isInteger(normalized)) {\n return recordGridFail(`${column.header} must be a whole number — got ${normalized}.`)\n }\n if (column.min !== undefined && normalized < column.min) {\n return recordGridFail(`${column.header} must be at least ${column.min} — got ${normalized}.`)\n }\n if (column.max !== undefined && normalized > column.max) {\n return recordGridFail(`${column.header} must be at most ${column.max} — got ${normalized}.`)\n }\n break\n }\n case 'date': {\n if (typeof normalized !== 'string' || !isCalendarDate(normalized)) {\n return recordGridFail(`${column.header} must be a date in YYYY-MM-DD form.`)\n }\n if (column.min !== undefined && normalized < column.min) {\n return recordGridFail(`${column.header} must be on or after ${column.min} — got ${normalized}.`)\n }\n if (column.max !== undefined && normalized > column.max) {\n return recordGridFail(`${column.header} must be on or before ${column.max} — got ${normalized}.`)\n }\n break\n }\n case 'select': {\n if (typeof normalized !== 'string') return recordGridFail(`${column.header} must be one of: ${describeOptions(column)}.`)\n if (!column.options.some((option) => option.value === normalized)) {\n return recordGridFail(`${column.header} must be one of: ${describeOptions(column)} — got “${normalized}”.`)\n }\n break\n }\n case 'boolean': {\n if (typeof normalized !== 'boolean') return recordGridFail(`${column.header} must be true or false.`)\n break\n }\n }\n\n const custom = column.validate?.(normalized)\n if (custom !== undefined && custom !== null) return recordGridFail(custom)\n return recordGridOk(normalized)\n}\n\n/** Parse editor text and validate it in one step — what a committing cell\n * editor calls. */\nexport function readRecordGridCell(column: RecordGridColumn, raw: string): RecordGridCellOutcome {\n const parsed = parseRecordGridInput(column, raw)\n if (!parsed.succeeded) return parsed\n return validateRecordGridCell(column, parsed.value)\n}\n\n/**\n * Validate a whole value bag against the columns. Inapplicable cells (an\n * unsatisfied `dependsOn`) are forced to `null` rather than carried, so a\n * sub-form the user turned off cannot smuggle stale values into a write.\n */\nexport function validateRecordGridRow(\n columns: readonly RecordGridColumn[],\n values: Readonly<Record<string, RecordGridValue>>,\n): RecordGridRowOutcome {\n const accepted: Record<string, RecordGridValue> = {}\n const cellErrors: Record<string, string> = {}\n let firstError: string | null = null\n\n for (const column of columns) {\n if (!isRecordGridCellApplicable(column, values)) {\n accepted[column.id] = null\n continue\n }\n const outcome = validateRecordGridCell(column, values[column.id] ?? null)\n if (outcome.succeeded) {\n accepted[column.id] = outcome.value\n continue\n }\n cellErrors[column.id] = outcome.error\n if (firstError === null) firstError = outcome.error\n }\n\n if (firstError !== null) {\n const count = Object.keys(cellErrors).length\n const error = count === 1 ? firstError : `${count} fields need attention. ${firstError}`\n return { succeeded: false, error, cellErrors }\n }\n return { succeeded: true, value: accepted }\n}\n\n// ── formatting ────────────────────────────────────────────────────────────\n\n/** Display text for a cell. `null` renders as the empty string; the component\n * decides what a missing value looks like. */\nexport function formatRecordGridValue(\n column: RecordGridColumn,\n value: RecordGridValue,\n locale?: string,\n): string {\n if (value === null || value === '') return ''\n switch (column.kind) {\n case 'currency': {\n if (typeof value !== 'number') return String(value)\n return new Intl.NumberFormat(locale, {\n style: 'currency',\n currency: column.currency,\n ...(column.fractionDigits === undefined\n ? {}\n : { minimumFractionDigits: column.fractionDigits, maximumFractionDigits: column.fractionDigits }),\n }).format(value)\n }\n case 'number': {\n if (typeof value !== 'number') return String(value)\n return new Intl.NumberFormat(locale).format(value)\n }\n case 'date': {\n if (typeof value !== 'string' || !isCalendarDate(value)) return String(value)\n return new Intl.DateTimeFormat(locale, { dateStyle: 'medium', timeZone: 'UTC' }).format(\n new Date(`${value}T00:00:00Z`),\n )\n }\n case 'select': {\n const option = column.options.find((candidate) => candidate.value === value)\n return option ? option.label : String(value)\n }\n case 'boolean': {\n if (typeof value !== 'boolean') return String(value)\n return value ? (column.trueLabel ?? 'Yes') : (column.falseLabel ?? 'No')\n }\n case 'text':\n return String(value)\n }\n}\n\n/** The text an editor control starts with — the raw value, never the formatted\n * one, so committing an untouched cell is a no-op. */\nexport function recordGridEditorText(column: RecordGridColumn, value: RecordGridValue): string {\n if (value === null) return ''\n if (column.kind === 'boolean') return value === true ? 'true' : 'false'\n return String(value)\n}\n\n/** Accessible name for a row's own controls. */\nexport function recordGridRowLabel(columns: readonly RecordGridColumn[], row: RecordGridRow): string {\n if (row.label !== undefined && row.label !== '') return row.label\n for (const column of columns) {\n if (column.kind !== 'text' && column.kind !== 'select') continue\n const value = row.values[column.id]\n if (typeof value === 'string' && value !== '') return formatRecordGridValue(column, value)\n }\n return row.id\n}\n\n/** Sum a numeric column over the rows that hold a number. Rows with no value\n * are absent from the sum, not zero — a total over three of five filled cells\n * is the total of what is on file. */\nexport function sumRecordGridColumn(rows: readonly RecordGridRow[], columnId: string): number {\n let total = 0\n for (const row of rows) {\n const value = row.values[columnId]\n if (typeof value === 'number' && Number.isFinite(value)) total += value\n }\n return total\n}\n\n// ── optimistic overlay ────────────────────────────────────────────────────\n\n/**\n * Edits the grid has applied locally but the caller's `rows` prop has not yet\n * caught up with. Every field is what rollback removes: drop the entry and the\n * caller's own data shows through again.\n */\nexport interface RecordGridOverlay {\n /** rowId → columnId → optimistic value. */\n updates: Readonly<Record<string, Readonly<Record<string, RecordGridValue>>>>\n /** Rows created locally, in insertion order. */\n created: readonly RecordGridRow[]\n /** Row ids removed locally. */\n removed: readonly string[]\n}\n\n/** An overlay holding nothing. */\nexport const EMPTY_RECORD_GRID_OVERLAY: RecordGridOverlay = { updates: {}, created: [], removed: [] }\n\n/** The rows to render: caller rows minus local deletes, with local cell edits\n * applied, then locally-created rows. */\nexport function projectRecordGridRows(\n rows: readonly RecordGridRow[],\n overlay: RecordGridOverlay,\n): RecordGridRow[] {\n const removed = new Set(overlay.removed)\n const projected: RecordGridRow[] = []\n for (const row of rows) {\n if (removed.has(row.id)) continue\n const patch = overlay.updates[row.id]\n projected.push(patch === undefined ? row : { ...row, values: { ...row.values, ...patch } })\n }\n for (const row of overlay.created) {\n if (removed.has(row.id)) continue\n // Same treatment as a caller row: a draft the user edited before the\n // server adopted it must render what they typed, not the value the create\n // went out with.\n const patch = overlay.updates[row.id]\n projected.push(patch === undefined ? row : { ...row, values: { ...row.values, ...patch } })\n }\n return projected\n}\n\n/** Record one optimistic cell edit. */\nexport function withRecordGridUpdate(\n overlay: RecordGridOverlay,\n rowId: string,\n columnId: string,\n value: RecordGridValue,\n): RecordGridOverlay {\n const rowPatch = { ...(overlay.updates[rowId] ?? {}), [columnId]: value }\n return { ...overlay, updates: { ...overlay.updates, [rowId]: rowPatch } }\n}\n\n/** Take back one optimistic cell edit — the rollback path. */\nexport function withoutRecordGridUpdate(\n overlay: RecordGridOverlay,\n rowId: string,\n columnId: string,\n): RecordGridOverlay {\n const rowPatch = overlay.updates[rowId]\n if (rowPatch === undefined || !(columnId in rowPatch)) return overlay\n const nextPatch: Record<string, RecordGridValue> = {}\n for (const [key, value] of Object.entries(rowPatch)) {\n if (key !== columnId) nextPatch[key] = value\n }\n const updates: Record<string, Readonly<Record<string, RecordGridValue>>> = { ...overlay.updates }\n if (Object.keys(nextPatch).length === 0) delete updates[rowId]\n else updates[rowId] = nextPatch\n return { ...overlay, updates }\n}\n\n/** Adopt a row the writer returned as canonical: for a locally-created row it\n * replaces the draft; for an existing row it replaces the optimistic cells.\n * Either way the draft's pending cell edits go with it — the canonical row IS\n * the answer, and leaving an edit layered over it is how a grid keeps showing\n * a value the server normalized away. */\nexport function withRecordGridServerRow(\n overlay: RecordGridOverlay,\n draftId: string,\n row: RecordGridRow,\n): RecordGridOverlay {\n const createdIndex = overlay.created.findIndex((candidate) => candidate.id === draftId)\n if (createdIndex >= 0) {\n const created = [...overlay.created]\n created[createdIndex] = row\n return { ...overlay, created, updates: withoutRowUpdates(overlay.updates, draftId, row.id) }\n }\n return { ...overlay, updates: { ...overlay.updates, [row.id]: { ...row.values } } }\n}\n\n/** Drop every pending cell edit recorded against these row ids. */\nfunction withoutRowUpdates(\n updates: RecordGridOverlay['updates'],\n ...rowIds: readonly string[]\n): RecordGridOverlay['updates'] {\n const drop = new Set(rowIds)\n const next: Record<string, Readonly<Record<string, RecordGridValue>>> = {}\n let changed = false\n for (const [rowId, patch] of Object.entries(updates)) {\n if (drop.has(rowId)) changed = true\n else next[rowId] = patch\n }\n return changed ? next : updates\n}\n\n/** Record an optimistic create. */\nexport function withRecordGridCreated(overlay: RecordGridOverlay, row: RecordGridRow): RecordGridOverlay {\n return { ...overlay, created: [...overlay.created, row] }\n}\n\n/** Take back an optimistic create — the rollback path. Cell edits made against\n * the draft go with it; the row they applied to no longer exists anywhere. */\nexport function withoutRecordGridCreated(overlay: RecordGridOverlay, rowId: string): RecordGridOverlay {\n const created = overlay.created.filter((row) => row.id !== rowId)\n if (created.length === overlay.created.length) return overlay\n return { ...overlay, created, updates: withoutRowUpdates(overlay.updates, rowId) }\n}\n\n/** Record an optimistic delete. */\nexport function withRecordGridRemoved(overlay: RecordGridOverlay, rowId: string): RecordGridOverlay {\n if (overlay.removed.includes(rowId)) return overlay\n return { ...overlay, removed: [...overlay.removed, rowId] }\n}\n\n/** Take back an optimistic delete — the rollback path. */\nexport function withoutRecordGridRemoved(overlay: RecordGridOverlay, rowId: string): RecordGridOverlay {\n if (!overlay.removed.includes(rowId)) return overlay\n return { ...overlay, removed: overlay.removed.filter((candidate) => candidate !== rowId) }\n}\n\n/**\n * Drop the overlay entries the caller's own rows have caught up with: a cell\n * whose value now matches, a created row now present, a removed row now gone.\n * Without this an overlay would mask every later refresh of the same cell.\n *\n * Returns the SAME overlay object when nothing settled, so a caller can prune\n * on every render without looping.\n */\nexport function pruneRecordGridOverlay(\n rows: readonly RecordGridRow[],\n overlay: RecordGridOverlay,\n): RecordGridOverlay {\n const byId = new Map(rows.map((row) => [row.id, row]))\n const createdIds = new Set(overlay.created.map((row) => row.id))\n let changed = false\n\n const updates: Record<string, Readonly<Record<string, RecordGridValue>>> = {}\n for (const [rowId, patch] of Object.entries(overlay.updates)) {\n const row = byId.get(rowId)\n if (row === undefined) {\n // A draft the caller's rows have not adopted yet is still on screen, so\n // its edits still have a row to apply to. Anything else left the\n // caller's data entirely and the edit has nothing to apply to.\n if (createdIds.has(rowId)) {\n updates[rowId] = patch\n continue\n }\n changed = true\n continue\n }\n const kept: Record<string, RecordGridValue> = {}\n for (const [columnId, value] of Object.entries(patch)) {\n if (sameRecordGridValue(row.values[columnId], value)) changed = true\n else kept[columnId] = value\n }\n if (Object.keys(kept).length > 0) updates[rowId] = kept\n }\n\n const created = overlay.created.filter((row) => {\n const settled = byId.has(row.id)\n if (settled) changed = true\n return !settled\n })\n\n const removed = overlay.removed.filter((rowId) => {\n const settled = !byId.has(rowId)\n if (settled) changed = true\n return !settled\n })\n\n if (!changed) return overlay\n return { updates, created, removed }\n}\n","/**\n * CommandPalette — the rendered half of the Cmd/Ctrl+K surface. Selection,\n * ranking, and grouping live in `/session-shell` (`buildCommandPaletteItems`,\n * `filterCommandPaletteItems`, `groupCommandPaletteItems`); this component is\n * the overlay, the input, and the keyboard model.\n *\n * Placement follows the PopoverSurface canon (AGENTS.md \"UI chrome\n * ownership\"): the panel PORTALS to `document.body` and positions in viewport\n * coordinates (`fixed`), so no host markup — a scroll rail, a `transform`, a\n * stacking context — can clip or trap it. Unlike the pickers it is CENTERED,\n * not trigger-anchored: a palette has no trigger, so it does not reuse\n * `PopoverSurface` itself, but it carries the same grammar — `bg-popover`,\n * `border-card-edge`, `OVERLAY_SHADOW`, the stamped surface attribute.\n *\n * The keyboard model is the ARIA combobox pattern: focus stays in the input,\n * ArrowUp/ArrowDown move `aria-activedescendant` across the FLAT result list\n * (groups are presentation), Enter selects, Escape closes, and closing returns\n * focus to whatever had it before the palette opened.\n */\n\nimport {\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n type KeyboardEvent,\n} from 'react'\nimport { createPortal } from 'react-dom'\n\nimport {\n filterCommandPaletteItems,\n groupCommandPaletteItems,\n type CommandPaletteItem,\n} from '../session-shell/index'\nimport { OVERLAY_SHADOW, POPOVER_SURFACE_ATTR } from './controls'\n\n// The item type IS the palette's prop surface — a consumer builds items for\n// this component, so it imports the type from here, not a second subpath.\nexport type { CommandPaletteItem } from '../session-shell/index'\n\nfunction SearchGlyph({ className }: { className?: string }) {\n return (\n <svg className={className} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden>\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <path d=\"m21 21-4.3-4.3\" />\n </svg>\n )\n}\n\nexport interface CommandPaletteProps {\n /** The full item list, build-ordered (recent-first sessions, then actions).\n * Filtering and ranking are owned here — pass the UNFILTERED list. */\n items: CommandPaletteItem[]\n /** A row was chosen (click or Enter). The palette closes itself. */\n onSelect: (item: CommandPaletteItem) => void\n\n /** Controlled open state. Omit for self-managed state toggled by the hotkey. */\n open?: boolean\n onOpenChange?: (open: boolean) => void\n /** Register the Cmd/Ctrl+K toggle. Default true. */\n hotkey?: boolean\n\n /** Async source is still resolving — the input stays live, the list shows\n * the loading row instead of a premature empty state. */\n loading?: boolean\n /** Seed for the query (uncontrolled). */\n initialQuery?: string\n placeholder?: string\n /** Empty-state copy. Default names the query: `No results for “…”`. */\n emptyMessage?: string\n /** Accessible name for the dialog. Default \"Command palette\". */\n label?: string\n}\n\nexport function CommandPalette({\n items,\n onSelect,\n open: controlledOpen,\n onOpenChange,\n hotkey = true,\n loading = false,\n initialQuery,\n placeholder = 'Search sessions and commands…',\n emptyMessage,\n label = 'Command palette',\n}: CommandPaletteProps) {\n const [internalOpen, setInternalOpen] = useState(false)\n const open = controlledOpen ?? internalOpen\n const setOpen = useCallback(\n (next: boolean) => {\n if (controlledOpen === undefined) setInternalOpen(next)\n onOpenChange?.(next)\n },\n [controlledOpen, onOpenChange],\n )\n\n const [query, setQuery] = useState(initialQuery ?? '')\n const [active, setActive] = useState(0)\n const inputRef = useRef<HTMLInputElement>(null)\n const surfaceId = useId()\n const listId = `${surfaceId}-list`\n\n // The flat list is the keyboard model: activedescendant indexes into it.\n // Sections regroup the SAME order for rendering, so the two never disagree.\n const flat = useMemo(() => filterCommandPaletteItems(items, query), [items, query])\n const sections = useMemo(() => groupCommandPaletteItems(flat), [flat])\n const activeIndex = flat.length === 0 ? 0 : Math.min(active, flat.length - 1)\n const activeId = flat.length > 0 ? `${listId}-${activeIndex}` : undefined\n\n // Cmd/Ctrl+K toggles from anywhere — the one global chord this surface owns.\n useEffect(() => {\n if (!hotkey) return\n function onKeyDown(e: globalThis.KeyboardEvent) {\n if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') {\n e.preventDefault()\n setOpen(!open)\n }\n }\n document.addEventListener('keydown', onKeyDown)\n return () => document.removeEventListener('keydown', onKeyDown)\n }, [hotkey, open, setOpen])\n\n // Opening: remember who had focus, take it for the input. Closing: give it\n // back, reset the query, and drop the active row — a reopen starts clean.\n const restoreFocusRef = useRef<Element | null>(null)\n useEffect(() => {\n if (open) {\n restoreFocusRef.current = document.activeElement\n inputRef.current?.focus()\n return\n }\n setQuery(initialQuery ?? '')\n setActive(0)\n const restore = restoreFocusRef.current\n restoreFocusRef.current = null\n if (restore instanceof HTMLElement) restore.focus()\n // eslint-disable-next-line react-hooks/exhaustive-deps -- initialQuery is a seed, not a subscription\n }, [open])\n\n // Keep the active row on screen as the list scrolls under the keyboard.\n useEffect(() => {\n if (!open || !activeId) return\n document.getElementById(activeId)?.scrollIntoView?.({ block: 'nearest' })\n }, [open, activeId])\n\n const choose = useCallback(\n (item: CommandPaletteItem) => {\n onSelect(item)\n setOpen(false)\n },\n [onSelect, setOpen],\n )\n\n const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n if (flat.length > 0) setActive((activeIndex + 1) % flat.length)\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n if (flat.length > 0) setActive((activeIndex - 1 + flat.length) % flat.length)\n } else if (e.key === 'Enter') {\n e.preventDefault()\n const item = flat[activeIndex]\n if (item) choose(item)\n } else if (e.key === 'Escape') {\n e.preventDefault()\n setOpen(false)\n }\n }\n\n if (!open || typeof document === 'undefined') return null\n\n let rowIndex = -1\n return createPortal(\n <>\n <div\n aria-hidden\n data-testid=\"command-palette-backdrop\"\n onMouseDown={() => setOpen(false)}\n className=\"fixed inset-0 z-[999] bg-background/80\"\n />\n {/* Centering is a full-width flex wrapper, NOT a `-translate-x-1/2` on\n the panel: `.agent-pop-in` animates `transform` with fill mode\n `both`, and its settled `transform: none` would override a translate\n utility and leave the panel half a width to the right. The wrapper\n is click-transparent so the backdrop still receives outside\n mousedowns; the panel opts back in. */}\n <div className=\"pointer-events-none fixed inset-x-0 top-[15%] z-[1000] flex justify-center px-4\">\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={label}\n {...{ [POPOVER_SURFACE_ATTR]: surfaceId }}\n className={`agent-pop-in pointer-events-auto flex max-h-[70vh] w-[560px] max-w-full flex-col overflow-hidden rounded-xl border border-card-edge bg-popover ${OVERLAY_SHADOW}`}\n >\n <div className=\"flex shrink-0 items-center gap-2 border-b border-border px-3 py-2.5\">\n <SearchGlyph className=\"h-4 w-4 shrink-0 text-muted-foreground\" />\n <input\n ref={inputRef}\n type=\"text\"\n role=\"combobox\"\n aria-expanded\n aria-controls={listId}\n aria-activedescendant={activeId}\n aria-label={label}\n value={query}\n onChange={(e) => {\n setQuery(e.target.value)\n setActive(0)\n }}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n className=\"flex-1 bg-transparent text-sm text-foreground placeholder:text-muted-foreground\"\n />\n </div>\n\n {/* `min-h-0` lets the list absorb the panel's max-height instead of\n overflowing it — the same flex rule the picker panels rely on. */}\n <div role=\"listbox\" id={listId} className=\"min-h-0 flex-1 overflow-y-auto p-1 pb-2\">\n {loading && (\n <div className=\"px-3 py-4 text-center text-sm text-muted-foreground\">Loading…</div>\n )}\n {!loading && flat.length === 0 && (\n <div className=\"px-3 py-4 text-center text-sm text-muted-foreground\">\n {emptyMessage ?? (query.trim() ? `No results for “${query.trim()}”` : 'Nothing here yet')}\n </div>\n )}\n {!loading &&\n sections.map((section) => (\n <div key={section.group}>\n <div className=\"px-3 pb-1 pt-3 text-xs font-semibold uppercase tracking-wide text-muted-foreground\">\n {section.group}\n </div>\n {section.items.map((item) => {\n rowIndex += 1\n const index = rowIndex\n return (\n <div\n key={item.id}\n id={`${listId}-${index}`}\n role=\"option\"\n aria-selected={index === activeIndex}\n onMouseMove={() => setActive(index)}\n onClick={() => choose(item)}\n className={`flex w-full cursor-pointer items-center gap-2.5 rounded-md px-3 py-2.5 text-left text-sm ${\n index === activeIndex ? 'bg-accent' : ''\n }`}\n >\n <span className=\"truncate text-foreground\">{item.label}</span>\n {item.description && (\n <span className=\"truncate text-xs text-muted-foreground\">{item.description}</span>\n )}\n {item.hint && (\n <span className=\"ml-auto shrink-0 text-xs tabular-nums text-muted-foreground\">{item.hint}</span>\n )}\n </div>\n )\n })}\n </div>\n ))}\n </div>\n\n <div className=\"flex shrink-0 items-center justify-between border-t border-border px-3 py-2 text-xs text-muted-foreground\">\n <span className=\"tabular-nums\">\n {query.trim() ? `${flat.length} of ${items.length}` : `${items.length} items`}\n </span>\n <span className=\"flex items-center gap-1.5\">\n <kbd className=\"rounded border border-border bg-background px-1 py-0.5\">↑↓</kbd>\n <span>navigate</span>\n <kbd className=\"ml-1.5 rounded border border-border bg-background px-1 py-0.5\">↵</kbd>\n <span>select</span>\n <kbd className=\"ml-1.5 rounded border border-border bg-background px-1 py-0.5\">esc</kbd>\n <span>close</span>\n </span>\n </div>\n </div>\n </div>\n </>,\n document.body,\n )\n}\n","/**\n * One class-attribute join for this subpath.\n *\n * The pattern it replaces is `` `base ${className ?? ''}` ``, which emits\n * `class=\"base \"` for every caller that passes nothing: a trailing separator in\n * the DOM, in every snapshot, and in every assertion that compares the attribute\n * instead of searching it. Interpolating an absent value is the defect — the\n * fix is to never build the attribute by interpolation.\n */\nexport function joinClasses(...parts: ReadonlyArray<string | false | null | undefined>): string {\n const kept: string[] = []\n for (const part of parts) {\n if (typeof part !== 'string') continue\n const trimmed = part.trim()\n if (trimmed.length > 0) kept.push(trimmed)\n }\n return kept.join(' ')\n}\n","/**\n * `Sparkline` — the series behind a number, as inline SVG.\n *\n * `/spend`, `/missions` and the eval lanes all produce a number for today, and\n * every product renders it as text. Text cannot separate \"$41, up from $38\"\n * from \"$41, up from $4\" — the same sentence, two different situations — so the\n * reader opens a second surface to find out which one they are in. The series\n * next to the number answers it in one glance.\n *\n * No chart dependency: this subpath is react + `@tangle-network/ui` only, and a\n * polyline is not worth a bundle. What a chart library would give us here is\n * axes, ticks and a tooltip, none of which belong on a 96×24 glyph.\n *\n * The three shapes a hand-rolled sparkline gets wrong, each handled here rather\n * than left to the caller:\n *\n * - **no readings** renders an explicit empty label, never a line. A line\n * along the baseline is a claim — \"this metric sat at zero\" — and a series\n * nobody has measured yet did not sit anywhere.\n * - **one reading** renders a point. A line needs two coordinates; drawing one\n * from a single reading invents the segment before it.\n * - **equal readings** render flat at MID height. The obvious normalisation\n * divides by `max - min`, which is `0` for a perfectly stable metric, and\n * the resulting `NaN` lands in the `points` attribute — SVG drops the whole\n * polyline, so the metric that never moved is the one that disappears.\n * - **a missing reading renders as a GAP, and the accessible name says so.**\n * A `null` from a hole in a series and a `NaN` from a producer's unguarded\n * division are not smaller series — they are readings nobody has. Deleting\n * them closed the line straight across the hole and announced a count that\n * was short by the number deleted: measured on `[1, NaN, 3]`, one continuous\n * two-point line labelled \"2 readings, rising from 1 to 3\", with nothing\n * anywhere saying a reading was unreadable. The card's figure slot already\n * refuses to let a non-measurement look measured; the series one line below\n * it holds the same rule. The x axis is the SAMPLE index, so the hole keeps\n * its width, the line breaks at it, and the label carries \"N not available\".\n *\n * Accessibility: `role=\"img\"` with an `aria-label` naming the metric, its range\n * and its direction. A sparkline with no accessible name is decoration a screen\n * reader cannot report, which would leave the shape — the entire reason the\n * component exists — visible to exactly one kind of reader.\n *\n * Deliberately not animated. `docs/product-surfaces.md` Pattern 4 lists chart\n * draw-on under what this package does not animate: the shape IS the answer,\n * and easing it in taxes every read of a surface people sit in for hours. The\n * card around it arrives (`.agent-arrive`); the line does not draw itself.\n */\n\nimport type { ReactElement } from 'react'\n\nimport { joinClasses } from './class-names'\n\n/** Where a series ended relative to where it started. */\nexport type SparklineDirection = 'rising' | 'falling' | 'flat'\n\nexport interface SparklinePoint {\n readonly x: number\n readonly y: number\n}\n\nexport interface SparklineGeometry {\n /** The finite readings, in order — what was actually plotted. */\n readonly readings: readonly number[]\n /** Every plotted point, in order. Positions are on the SAMPLE axis, so a\n * missing reading leaves its width behind rather than closing up. */\n readonly points: readonly SparklinePoint[]\n /** The points split into runs of CONSECUTIVE samples. One run is one stroke:\n * a line drawn across a missing reading states a movement nobody measured. */\n readonly segments: readonly (readonly SparklinePoint[])[]\n /** Samples that carried no usable reading — a `null`, a `NaN`, an infinity.\n * Counted rather than discarded, because the accessible name has to state\n * them: a shorter series announced as a complete one is the silent loss. */\n readonly gaps: number\n readonly min: number\n readonly max: number\n readonly first: number\n readonly last: number\n readonly direction: SparklineDirection\n}\n\nexport interface SparklineGeometryOptions {\n width?: number\n height?: number\n /** Keeps the stroke and the end dot inside the viewBox instead of clipping\n * them at the extremes, where the interesting readings always are. */\n inset?: number\n}\n\nexport const DEFAULT_SPARKLINE_WIDTH = 96\nexport const DEFAULT_SPARKLINE_HEIGHT = 24\nconst DEFAULT_INSET = 2.5\nconst STROKE_WIDTH = 1.5\nconst DOT_RADIUS = 1.75\n/** Only a name, never a metric: it exists so the accessible label is never\n * empty. Every caller in this package passes the metric's own title. */\nexport const DEFAULT_SPARKLINE_LABEL = 'Trend'\nexport const DEFAULT_SPARKLINE_EMPTY_LABEL = 'No history yet'\n/** Nothing was measurable, which is not the same as nothing was measured yet —\n * and \"No history yet\" over a series that arrived full of `NaN` reads as the\n * metric being new when the producer is broken. */\nexport const DEFAULT_SPARKLINE_UNAVAILABLE_LABEL = 'No readings available'\n\nconst NUMBER_FORMAT = new Intl.NumberFormat('en-US', { maximumFractionDigits: 2 })\n\n/** The package's default number rendering, pinned to `en-US` so a card and its\n * series read the same on every host — a series formatted by the server's\n * locale and a value formatted by the browser's is a defect nobody sees until\n * the decimal separators disagree. */\nexport function formatSparklineValue(value: number): string {\n return NUMBER_FORMAT.format(value)\n}\n\nfunction isReading(value: unknown): value is number {\n return typeof value === 'number' && Number.isFinite(value)\n}\n\n/**\n * The readings that can be plotted.\n *\n * A `null` from a gap in a series, or a `NaN` from a division a producer did\n * not guard, is not plotted rather than coerced to `0`: plotting a missing\n * reading at the baseline draws a cliff that never happened.\n *\n * This returns the readings ALONE, so it cannot tell a caller how many are\n * missing. That is what {@link SparklineGeometry.gaps} is for, and what the\n * accessible name reports — dropping a sample and then announcing the shorter\n * count as the whole series is the defect, not the filter.\n */\nexport function sparklineReadings(values: readonly number[]): number[] {\n return values.filter(isReading)\n}\n\n/** Two decimals: enough for a 96px glyph, and it keeps the serialised `points`\n * attribute stable enough to assert on. */\nfunction round(value: number): number {\n return Math.round(value * 100) / 100\n}\n\n/**\n * Plots the series into the viewBox.\n *\n * Pure and exported so the cases that produce a broken chart — nothing, one\n * reading, a flat series, negatives — are unit-testable without a DOM.\n */\nexport function sparklineGeometry(\n values: readonly number[],\n { width = DEFAULT_SPARKLINE_WIDTH, height = DEFAULT_SPARKLINE_HEIGHT, inset = DEFAULT_INSET }: SparklineGeometryOptions = {},\n): SparklineGeometry {\n const samples = values.length\n // Kept WITH their sample index: the index is the x axis, so a missing reading\n // leaves a hole of the right width instead of the series closing up and\n // drawing a straight line over the sample nobody has.\n const plotted: Array<{ readonly index: number; readonly value: number }> = []\n for (let index = 0; index < samples; index += 1) {\n const value = values[index]\n if (isReading(value)) plotted.push({ index, value })\n }\n const readings = plotted.map((entry) => entry.value)\n const gaps = samples - readings.length\n if (readings.length === 0) {\n return { readings, points: [], segments: [], gaps, min: 0, max: 0, first: 0, last: 0, direction: 'flat' }\n }\n\n // Folded rather than `Math.min(...readings)`: a spread of a long series\n // overflows the argument limit, and a spend chart is exactly the caller that\n // hands over a year of daily readings.\n let min = readings[0] as number\n let max = readings[0] as number\n for (const value of readings) {\n if (value < min) min = value\n if (value > max) max = value\n }\n\n const first = readings[0] as number\n const last = readings[readings.length - 1] as number\n const span = max - min\n const top = inset\n const bottom = height - inset\n const left = inset\n const right = width - inset\n\n const points = plotted.map(({ index, value }) => ({\n // A series of ONE SAMPLE sits in the middle rather than at the left edge,\n // where it reads as the start of a line whose rest failed to render. A\n // single reading among several samples keeps its own position — that is the\n // one thing that says where in the window the reading is.\n x: round(samples <= 1 ? width / 2 : left + ((right - left) * index) / (samples - 1)),\n // `span === 0` is the stable metric. Mid-height is the honest render of it;\n // dividing by the span here is the NaN that erases the whole polyline.\n y: round(span === 0 ? height / 2 : bottom - ((bottom - top) * (value - min)) / span),\n }))\n\n // One segment per run of CONSECUTIVE samples. A run break is a gap, and a\n // stroke across it would state a movement between two readings that are not\n // next to each other.\n const segments: SparklinePoint[][] = []\n let run: SparklinePoint[] = []\n let previous = Number.NEGATIVE_INFINITY\n plotted.forEach(({ index }, position) => {\n if (index !== previous + 1 && run.length > 0) {\n segments.push(run)\n run = []\n }\n run.push(points[position] as SparklinePoint)\n previous = index\n })\n if (run.length > 0) segments.push(run)\n\n return {\n readings,\n points,\n segments,\n gaps,\n min,\n max,\n first,\n last,\n direction: last > first ? 'rising' : last < first ? 'falling' : 'flat',\n }\n}\n\n/** `\"2,14 48,3 94,21\"` — the `points` attribute of the polyline. */\nexport function sparklinePointsAttribute(points: readonly SparklinePoint[]): string {\n return points.map((point) => `${point.x},${point.y}`).join(' ')\n}\n\nexport interface SparklineLabelOptions {\n label?: string\n format?: (value: number) => string\n}\n\n/**\n * The accessible name: metric, how many readings, how many are missing, the\n * range, and the direction.\n *\n * All of it is load-bearing. The range without the direction describes a shape\n * that could have been walked in either order; the direction without the range\n * says \"rising\" about a metric that moved by a rounding error; and the count\n * without the gaps is the number of readings that SURVIVED announced as the\n * number that were taken — the shape a reader cannot see is exactly the one\n * this sentence exists to carry.\n */\nexport function sparklineLabel(\n values: readonly number[],\n { label = DEFAULT_SPARKLINE_LABEL, format = formatSparklineValue }: SparklineLabelOptions = {},\n): string {\n const { readings, gaps, min, max, first, last, direction } = sparklineGeometry(values)\n // The card's own word for a figure it does not have is \"Not available\"; a\n // series uses the same word rather than a second vocabulary for one fact.\n const missing = gaps === 0 ? '' : `, ${gaps} not available`\n // \"no readings yet\" is a claim about a NEW metric. A series that arrived and\n // was unreadable is a different state and must not borrow that sentence.\n if (readings.length === 0) return gaps === 0 ? `${label}: no readings yet` : `${label}: no readings${missing}`\n if (readings.length === 1) return `${label}: one reading${missing}, ${format(first)}`\n if (max === min) return `${label}: ${readings.length} readings${missing}, unchanged at ${format(first)}`\n // A series can cover ground and come back — the range is real, the net move\n // is not, and \"flat\" alone would hide the first while \"rising\" would invent\n // the second.\n const movement = direction === 'flat' ? 'net unchanged' : direction\n return (\n `${label}: ${readings.length} readings${missing}, range ${format(min)} to ${format(max)}, ` +\n `${movement} from ${format(first)} to ${format(last)}`\n )\n}\n\nexport interface SparklineProps {\n values: readonly number[]\n /** Names the metric in the accessible label. */\n label?: string\n /** Renders a reading in that label; defaults to the package number format. */\n format?: (value: number) => string\n width?: number\n height?: number\n /** Shown instead of a line when the metric has no history yet. */\n emptyLabel?: string\n /** Shown instead of a line when every sample arrived unreadable — a different\n * state from \"no history yet\", and one the reader has to be able to tell\n * apart, because one is a new metric and the other is a broken producer. */\n unavailableLabel?: string\n className?: string\n}\n\n/** The series glyph. Strokes in `currentColor`, so tone is the caller's. */\nexport function Sparkline({\n values,\n label = DEFAULT_SPARKLINE_LABEL,\n format = formatSparklineValue,\n width = DEFAULT_SPARKLINE_WIDTH,\n height = DEFAULT_SPARKLINE_HEIGHT,\n emptyLabel = DEFAULT_SPARKLINE_EMPTY_LABEL,\n unavailableLabel = DEFAULT_SPARKLINE_UNAVAILABLE_LABEL,\n className,\n}: SparklineProps): ReactElement {\n const geometry = sparklineGeometry(values, { width, height })\n const accessibleName = sparklineLabel(values, { label, format })\n\n if (geometry.points.length === 0) {\n // Words, not a flat line at zero. The sentence assistive tech gets is the\n // same one the chart would have carried, so a deck of cards never produces\n // an unattributed phrase — and a series that arrived unreadable says that,\n // rather than borrowing the copy for a metric with no history yet.\n return (\n <span\n data-sparkline={geometry.gaps > 0 ? 'unavailable' : 'empty'}\n className={joinClasses('text-[11px] text-muted-foreground', className)}\n >\n <span className=\"sr-only\">{accessibleName}</span>\n <span aria-hidden=\"true\">{geometry.gaps > 0 ? unavailableLabel : emptyLabel}</span>\n </span>\n )\n }\n\n const drawsLine = geometry.segments.some((segment) => segment.length > 1)\n const end = geometry.points[geometry.points.length - 1] as SparklinePoint\n\n return (\n <svg\n role=\"img\"\n aria-label={accessibleName}\n data-sparkline={drawsLine ? 'line' : 'point'}\n data-direction={geometry.direction}\n // Readable from the DOM because \"the line broke here\" is not a thing a\n // caller can measure off a `points` attribute.\n data-gaps={geometry.gaps > 0 ? geometry.gaps : undefined}\n width={width}\n height={height}\n viewBox={`0 0 ${width} ${height}`}\n className={className}\n // A decorative-by-default `focusable` keeps IE-era SVG out of the tab\n // order; the label is what carries this element, not focus.\n focusable=\"false\"\n >\n {/* One stroke per run of consecutive samples. A reading with no neighbour\n is a dot for the same reason a one-reading series is: there is nothing\n beside it to draw a line to, and drawing one anyway would invent the\n sample the gap is there to report. */}\n {geometry.segments.map((segment, index) => {\n const key = `segment-${index}`\n if (segment.length > 1) {\n return (\n <polyline\n key={key}\n points={sparklinePointsAttribute(segment)}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={STROKE_WIDTH}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n vectorEffect=\"non-scaling-stroke\"\n />\n )\n }\n const only = segment[0] as SparklinePoint\n // The latest-reading dot below already paints this one.\n if (only.x === end.x && only.y === end.y) return null\n return <circle key={key} cx={only.x} cy={only.y} r={DOT_RADIUS} fill=\"currentColor\" />\n })}\n {/* The latest reading, marked. Without it the eye has to decide which end\n of the line is \"now\", and half the readers guess wrong. */}\n <circle cx={end.x} cy={end.y} r={DOT_RADIUS} fill=\"currentColor\" />\n </svg>\n )\n}\n","/**\n * `InsightCard` + `InsightDeck` — the number that moved, and the paged deck of\n * them.\n *\n * Every product on this shell computes insights already: `/spend` knows today's\n * burn against yesterday's, `/missions` knows how many runs landed, the eval\n * lanes know a pass rate per release. All of it renders as a line of text, so\n * the reader does the comparison in their head and the series behind the number\n * never reaches the screen at all.\n *\n * Two rules this surface exists to hold:\n *\n * - **A delta needs a baseline.** `previous` absent means no delta is drawn —\n * not a green `+0%`, which is the specific fabrication a hand-rolled card\n * produces when it defaults its baseline to zero, and which reads as \"we\n * measured, nothing changed\" when the truth is \"we have nothing to compare\n * against\". {@link insightDelta} returns `null` rather than a zero.\n * - **Direction is not sentiment.** Spend going up and missions going up are\n * the same arrow and opposite news, so tone is a caller declaration\n * (`polarity`), and the default is neutral. A card that paints every rise\n * green teaches the reader to stop reading the label.\n *\n * The deck is built on `web-react/async` rather than a loading boolean, so it\n * inherits that module's invariant instead of restating it: `AsyncView` renders\n * `error` with its message and retry, and `empty` is reachable only from a load\n * that resolved — a failed fetch can never paint \"No insights yet\"\n * (`docs/async-state-module.md`).\n *\n * Motion: cards arrive with `.agent-arrive`, staggered by `--stagger-index`\n * from the deck, and a page TURN remounts them so the next page arrives as a\n * sequence instead of swapping text under cards that never moved. A REFRESH is\n * the opposite case and gets the opposite treatment — see the deck's own note.\n * Every piece of that is decoration, carries no `data-motion`, and collapses\n * under `prefers-reduced-motion` — the live label included.\n *\n * The live label does NOT opt out, and the reasoning is worth stating because\n * the opposite reads plausible. What tells the reader a figure is still being\n * computed is the WORD (`liveLabel`, \"Updating\"): it is rendered only while\n * `live`, and a settled card does not render it at all. The sweep through its\n * glyphs is emphasis on a signal that is already there, not the signal. So a\n * reader who asked for less motion still sees the word — static, in the\n * shimmer's resting gradient, still legible, and still disappearing the moment\n * the figure is final. Nothing here overrides a request the reader made.\n */\n\nimport {\n isValidElement,\n useCallback,\n useEffect,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n type ReactElement,\n type ReactNode,\n} from 'react'\n\nimport { AsyncView, type AsyncEmptySpec, type AsyncResourceState } from './async'\nimport { joinClasses } from './class-names'\nimport { staggerStyle } from './motion'\nimport { Sparkline, formatSparklineValue } from './sparkline'\n\n// ── the delta, and its honesty rules ──────────────────────────────────────\n\nexport type InsightDirection = 'up' | 'down' | 'flat'\n\n/** Which way is good news for THIS metric. `neutral` is the default because it\n * is the only answer that is true for every metric. */\nexport type InsightPolarity = 'higher-is-better' | 'lower-is-better' | 'neutral'\n\nexport type InsightTone = 'positive' | 'negative' | 'neutral'\n\nexport interface InsightDelta {\n /** The baseline the move is measured against — rendered, so the delta is\n * never a number floating free of what produced it. */\n readonly previous: number\n readonly absolute: number\n /** `null` when the baseline is `0`: a share of nothing is undefined, and\n * \"+∞%\" or a silently-dropped percentage are both worse than the absolute. */\n readonly percent: number | null\n readonly direction: InsightDirection\n}\n\n/**\n * The move, or `null` when there is no honest one to state.\n *\n * `unknown` inputs on purpose: these arrive from a fetched payload, and the\n * cases that must not produce a delta — a missing baseline, a `null` from a\n * first-ever reading, a `NaN` from a producer's division — are exactly the ones\n * a narrower signature would let through as `0`.\n */\nexport function insightDelta(value: unknown, previous: unknown): InsightDelta | null {\n if (typeof value !== 'number' || !Number.isFinite(value)) return null\n if (typeof previous !== 'number' || !Number.isFinite(previous)) return null\n const absolute = value - previous\n return {\n previous,\n absolute,\n percent: previous === 0 ? null : absolute / previous,\n direction: absolute > 0 ? 'up' : absolute < 0 ? 'down' : 'flat',\n }\n}\n\n/** Maps a direction onto good/bad news, which only the caller knows. */\nexport function insightDeltaTone(direction: InsightDirection, polarity: InsightPolarity = 'neutral'): InsightTone {\n if (direction === 'flat' || polarity === 'neutral') return 'neutral'\n const welcome: InsightDirection = polarity === 'higher-is-better' ? 'up' : 'down'\n return direction === welcome ? 'positive' : 'negative'\n}\n\n/**\n * The delta as words: direction, magnitude, and the baseline it is measured\n * against. Words rather than an arrow plus a bare number, because the arrow is\n * `aria-hidden` and a reader hearing \"12%\" learns nothing about which way.\n */\nexport function formatInsightDelta(delta: InsightDelta, format: (value: number) => string = formatSparklineValue): string {\n const from = format(delta.previous)\n if (delta.direction === 'flat') return `No change from ${from}`\n const word = delta.direction === 'up' ? 'Up' : 'Down'\n const magnitude =\n delta.percent === null ? format(Math.abs(delta.absolute)) : `${(Math.abs(delta.percent) * 100).toFixed(1)}%`\n return `${word} ${magnitude} from ${from}`\n}\n\nconst TONE_CLASS: Record<InsightTone, string> = {\n positive: 'text-success',\n negative: 'text-destructive',\n neutral: 'text-muted-foreground',\n}\n\nconst DIRECTION_GLYPH: Record<InsightDirection, string> = { up: '↑', down: '↓', flat: '→' }\n\n/**\n * What a non-finite figure renders as.\n *\n * `Intl.NumberFormat.format(NaN)` is the string `\"NaN\"` and `Infinity` is `\"∞\"`,\n * so a producer's unguarded division lands on the card as though it were a\n * reading. On a surface whose entire job is \"here is the number that moved\",\n * printing a non-number in the figure slot is the worst available failure: it\n * looks measured. A dash says the opposite, and the delta is already suppressed\n * for the same input by {@link insightDelta}.\n */\nconst INSIGHT_UNAVAILABLE_GLYPH = '—'\n/** The dash reads as nothing at all to a screen reader, so the words go beside\n * it. Not \"0\", not the metric name alone — the state IS \"no reading\". */\nconst INSIGHT_UNAVAILABLE_LABEL = 'Not available'\n\n// ── the card ──────────────────────────────────────────────────────────────\n\nexport interface InsightAction {\n label: string\n onClick: () => void\n}\n\nexport interface InsightCardProps {\n /** The lane the metric belongs to (\"Spend\", \"Missions\"), set small above the\n * title. A deck of cards from different surfaces needs the grouping word\n * before the metric's own name, not after it. */\n eyebrow?: string\n /** What was measured, in the reader's words (\"Spend today\"). */\n title: string\n /** The number that moved. A `string` renders verbatim — a total the caller\n * already formatted with its own currency — and takes no delta, because\n * there is nothing to subtract. A non-finite number is not a measurement and\n * renders as {@link INSIGHT_UNAVAILABLE_GLYPH}, never as \"NaN\" or \"∞\". */\n value: number | string\n /** \"USD\", \"runs\", \"%\" — the unit the number is in, beside it rather than\n * glued into it, so the figure stays scannable. */\n unit?: string\n /** The baseline. Absent ⇒ the card renders the value and no delta. */\n previous?: number\n polarity?: InsightPolarity\n /** One number format for the value, the delta and the series, so the three\n * cannot disagree about decimals on the same card. */\n format?: (value: number) => string\n series?: readonly number[]\n /** Names the series in its accessible label; defaults to the card's title. */\n seriesLabel?: string\n /** One line of context under the number — what the window is, what is\n * excluded. Not a restatement of the title. */\n description?: string\n /** The next action for this insight. An element renders as supplied (a link,\n * a dialog trigger); the object form renders the standard button. */\n action?: InsightAction | ReactElement\n /** The number is still being computed. The label's PRESENCE is the signal, so\n * it reads the same with motion collapsed — see the module note. */\n live?: boolean\n liveLabel?: string\n className?: string\n style?: CSSProperties\n}\n\nexport function InsightCard({\n eyebrow,\n title,\n value,\n unit,\n previous,\n polarity = 'neutral',\n format = formatSparklineValue,\n series,\n seriesLabel,\n description,\n action,\n live = false,\n liveLabel = 'Updating',\n className,\n style,\n}: InsightCardProps): ReactElement {\n const delta = insightDelta(value, previous)\n const tone = delta ? insightDeltaTone(delta.direction, polarity) : 'neutral'\n // A `NaN`/`Infinity` from a producer is not a smaller number — it is the\n // absence of a reading, and it takes the unit with it: \"∞ USD\" and\n // \"Not available USD\" are both claims about a measurement nobody made.\n const unavailable = typeof value === 'number' && !Number.isFinite(value)\n const shown = typeof value === 'number' ? format(value) : value\n\n return (\n <article\n data-insight-card=\"\"\n data-tone={tone}\n // `.agent-arrive` is the package's card entrance; the deck sets\n // `--stagger-index` through `style` so a page of them lands as a sequence.\n className={joinClasses('agent-arrive flex h-full flex-col rounded-xl border border-card-edge bg-card p-4', className)}\n style={style}\n >\n {eyebrow ? (\n <p data-insight-eyebrow=\"\" className=\"mb-0.5 text-[11px] font-semibold uppercase tracking-[0.05em] text-muted-foreground\">\n {eyebrow}\n </p>\n ) : null}\n <div className=\"flex items-baseline justify-between gap-2\">\n <h3 className=\"text-[13px] font-medium text-muted-foreground\">{title}</h3>\n {live ? (\n // No `data-motion` opt-out: the word is the signal and the sweep is\n // emphasis, so the reduced-motion floor reaches this like everything\n // else and leaves a static, legible label.\n <span className=\"agent-shimmer shrink-0 text-[11px] font-medium\" data-insight-live=\"\">\n {liveLabel}\n </span>\n ) : null}\n </div>\n\n <p className=\"mt-1 flex items-baseline gap-1\">\n {unavailable ? (\n <span data-insight-value=\"unavailable\" className=\"text-xl font-semibold text-muted-foreground\">\n <span aria-hidden=\"true\">{INSIGHT_UNAVAILABLE_GLYPH}</span>\n <span className=\"sr-only\">{INSIGHT_UNAVAILABLE_LABEL}</span>\n </span>\n ) : (\n <>\n {/* `tabular-nums`: a deck of cards whose digits change width jitters\n on every refresh, which reads as the layout being unsure. */}\n <span className=\"text-xl font-semibold tabular-nums text-foreground\">{shown}</span>\n {unit ? <span className=\"text-[11px] text-muted-foreground\">{unit}</span> : null}\n </>\n )}\n </p>\n\n {delta ? (\n <p data-insight-delta={delta.direction} className={`mt-0.5 text-[11px] font-medium ${TONE_CLASS[tone]}`}>\n <span aria-hidden=\"true\">{DIRECTION_GLYPH[delta.direction]} </span>\n {formatInsightDelta(delta, format)}\n </p>\n ) : null}\n\n {description ? <p className=\"mt-1 text-[11px] text-muted-foreground\">{description}</p> : null}\n\n {series ? (\n <div className=\"mt-2 text-muted-foreground\">\n {/* Muted, not the accent: one accent colour at rest belongs on the\n next action, and a meaningful graphic still needs to clear 3:1 —\n which the text-grade muted foreground does and a divider tint\n does not. */}\n <Sparkline values={series} label={seriesLabel ?? title} format={format} />\n </div>\n ) : null}\n\n {action ? <div className=\"mt-3\">{renderInsightAction(action)}</div> : null}\n </article>\n )\n}\n\nfunction renderInsightAction(action: InsightAction | ReactElement): ReactNode {\n if (isValidElement(action)) return action\n return (\n <button\n type=\"button\"\n onClick={action.onClick}\n className=\"h-8 rounded-md border border-border px-3 text-xs font-medium text-foreground transition hover:bg-accent\"\n >\n {action.label}\n </button>\n )\n}\n\n// ── the deck ──────────────────────────────────────────────────────────────\n\nexport interface Insight extends InsightCardProps {\n /** Stable across refreshes: it keys the card. Paired with the deck holding\n * the last loaded page across a reload, a stable id is what lets a settled\n * card keep its own DOM node — and therefore not replay its arrival — when\n * a poll returns the same insight. */\n readonly id: string\n}\n\nexport const DEFAULT_INSIGHT_PAGE_SIZE = 3\n/** Past this many pages the dots stop being scannable and become a second row\n * of controls; the counter and the arrows carry it from there. */\nconst MAX_PAGE_DOTS = 8\n\n/**\n * What is wrong with a page size — and the key the warning dedupes on.\n *\n * Dedupe by VALUE alone is the leak: the size is often computed from a measured\n * viewport, a drag-resize produces a new fractional value on every frame, and a\n * `Set<number>` that never forgets then grows once per frame while the console\n * fills with the same sentence about a different decimal. Measured: 500 distinct\n * fractional sizes, 499 retained entries, 499 lines.\n *\n * The fault is the useful unit — a caller who passed `2.5` has one mistake to\n * fix, not five hundred — so the cache is a fixed three buckets, each of which\n * names the first {@link MAX_NAMED_PAGE_SIZES} distinct values it sees and then\n * latches shut and drops what it was holding.\n */\ntype PageSizeFault = 'not-a-number' | 'below-one' | 'fractional'\n\nconst PAGE_SIZE_FAULT_REASON: Record<PageSizeFault, string> = {\n 'not-a-number': 'A page size that is not a number cannot count cards at all.',\n 'below-one': 'A page size below one card gives the deck a page nothing fits on.',\n fractional: 'A fractional page size hides cards on no page at all.',\n}\n\n/** Distinct offending values each fault names before it goes quiet. Naming the\n * first few is the developer aid; naming the five-hundredth is noise sitting on\n * top of a leak. */\nconst MAX_NAMED_PAGE_SIZES = 8\n\nconst warnedPageSizes: Record<PageSizeFault, { readonly named: Set<number>; latched: boolean }> = {\n 'not-a-number': { named: new Set<number>(), latched: false },\n 'below-one': { named: new Set<number>(), latched: false },\n fractional: { named: new Set<number>(), latched: false },\n}\n\nfunction pageSizeFault(pageSize: number): PageSizeFault {\n if (!Number.isFinite(pageSize)) return 'not-a-number'\n if (pageSize < 1) return 'below-one'\n return 'fractional'\n}\n\nfunction warnPageSize(pageSize: number): void {\n const fault = pageSizeFault(pageSize)\n const record = warnedPageSizes[fault]\n // Once per distinct value, and only while the bucket is still naming values:\n // this runs on every render of every deck, and a warning repeated sixty times\n // a second is one nobody reads.\n if (record.latched || record.named.has(pageSize)) return\n record.named.add(pageSize)\n console.warn(\n `[insight-card] pageSize must be a whole number of cards, 1 or more — received ${String(pageSize)}. ` +\n `Using ${DEFAULT_INSIGHT_PAGE_SIZE}. ${PAGE_SIZE_FAULT_REASON[fault]}`,\n )\n if (record.named.size >= MAX_NAMED_PAGE_SIZES) {\n // Nothing more will be printed for this fault, so the values it was keeping\n // in order to dedupe are dead weight — the bucket ends holding nothing.\n record.named.clear()\n record.latched = true\n console.warn(`[insight-card] further \"${fault}\" pageSize warnings are suppressed.`)\n }\n}\n\n/**\n * The page size — ONE definition, read by the count and by the slice.\n *\n * Two definitions is how a deck hides an insight with no error at all: a count\n * that divides by the raw `2.5` claims two pages of a five-card deck, a slice\n * that floors it puts two cards on each, and the fifth card is on no page the\n * reader can reach. Nothing renders wrong; a card is simply gone.\n *\n * A page size is a count of cards, so a fraction, a zero and a negative are not\n * smaller decks — they are caller mistakes, and this normalises them back to the\n * default and says so once per offending value. Normalised rather than thrown\n * because the value is often computed from a measured viewport, where the first\n * paint legitimately produces a `0`: a deck that pages in threes is a far\n * smaller failure than a dashboard that throws during render.\n */\nexport function insightPageSize(pageSize: number = DEFAULT_INSIGHT_PAGE_SIZE): number {\n if (Number.isInteger(pageSize) && pageSize >= 1) return pageSize\n warnPageSize(pageSize)\n return DEFAULT_INSIGHT_PAGE_SIZE\n}\n\n/** Always at least one page, so \"Page 1 of 0\" cannot be rendered. */\nexport function insightPageCount(total: number, pageSize: number = DEFAULT_INSIGHT_PAGE_SIZE): number {\n const size = insightPageSize(pageSize)\n // `total` reaches this as an array length from every caller in the package;\n // the clamp is for the exported surface, where \"Page 1 of NaN\" is the same\n // class of defect as the figure that renders one.\n const counted = Number.isFinite(total) && total > 0 ? total : 0\n return Math.max(1, Math.ceil(counted / size))\n}\n\n/** The items on `page`, with the page clamped into range — a deck whose list\n * shrank under the reader shows the last page that exists, never a blank one. */\nexport function insightPageSlice<T>(items: readonly T[], page: number, pageSize: number = DEFAULT_INSIGHT_PAGE_SIZE): readonly T[] {\n const size = insightPageSize(pageSize)\n const count = insightPageCount(items.length, size)\n const requested = Number.isFinite(page) ? Math.floor(page) : 0\n const safe = Math.min(Math.max(requested, 0), count - 1)\n return items.slice(safe * size, safe * size + size)\n}\n\nexport interface InsightDeckProps {\n /** The same five-state contract every other screen fetches through. */\n state: AsyncResourceState<readonly Insight[]>\n /** Required by `AsyncView`: an empty deck must say what is missing and what\n * to do about it. */\n empty: AsyncEmptySpec | ReactElement\n /** Names the region for assistive tech and titles nothing visually — the\n * cards carry their own headings. */\n label?: string\n pageSize?: number\n loadingLabel?: string\n retryLabel?: string\n className?: string\n /**\n * The page the reader is ON, whatever moved them there.\n *\n * That includes the render-time clamp: a list that shrinks under a reader\n * standing on page 3 leaves them on the last page that exists, and a parent\n * persisting this to a URL or to storage would otherwise keep writing a page\n * number nothing can reach. Reported once per effective page, never twice for\n * the same one.\n */\n onPageChange?: (page: number) => void\n}\n\n/**\n * The paged deck.\n *\n * `AsyncView` owns the non-`ready` branches, which is what makes the invariant\n * structural here: the cards are rendered from one branch of that component,\n * and no branch of this one could paint the empty copy over a failure.\n *\n * **A REFRESH DOES NOT REPLACE WHAT IS ON SCREEN.** `useAsyncResource` re-enters\n * `loading` with no value held on every reload, and handing that straight to\n * `AsyncView` swaps the ready subtree for the busy block — which destroys the\n * DOM the reader is standing in. Measured, on a real reload: `document.\n * activeElement` fell to `document.body`, so a keyboard reader mid-page lost\n * their place on every automatic poll; and every settled card was a NEW node, so\n * `.agent-arrive` replayed across the whole visible page — the exact flash this\n * surface's motion rules exist to prevent. Holding the page NUMBER above the\n * boundary fixed the counter and none of that, because the subtree under it was\n * still being torn down.\n *\n * So the deck holds the last insights it rendered and keeps handing them to the\n * SAME `AsyncView` branch while a reload is in flight: same element, same\n * position, same keys — React reuses the nodes, focus stays where the reader put\n * it, and nothing re-animates. `aria-busy` on the region is the signal that a\n * load is in flight; a per-card one is `live` on the card.\n *\n * The bridge is only ever over a WAIT. `error` and `empty` are answers about the\n * resource, so they drop what was held and render their own branch — a failed\n * fetch still cannot paint stale numbers, and the async module's invariant is\n * untouched.\n *\n * It bridges one resource, not one component: if the SUBJECT changes (a\n * different workspace, a different window), give the deck a `key` so it remounts\n * rather than showing the previous subject's numbers while the new ones load.\n */\nexport function InsightDeck({\n state,\n empty,\n label = 'Insights',\n pageSize = DEFAULT_INSIGHT_PAGE_SIZE,\n loadingLabel = 'Loading insights…',\n retryLabel,\n className,\n onPageChange,\n}: InsightDeckProps): ReactElement {\n const [page, setPage] = useState(0)\n const [held, setHeld] = useState<readonly Insight[] | null>(null)\n\n // Adjusted during render, which is what keeps the swap out of the DOM: an\n // effect runs after the commit, so the teardown this exists to prevent would\n // already have happened by the time it fired.\n const answered = state.status === 'error' || state.status === 'empty'\n const carried = state.status === 'ready' ? state.value : answered ? null : held\n if (carried !== held) setHeld(carried)\n\n const shown: AsyncResourceState<readonly Insight[]> =\n carried !== null && state.status !== 'ready' ? { status: 'ready', value: carried, retry: state.retry } : state\n const refreshing = shown !== state\n\n // The last page reported to the caller. A clamp and a navigation both settle\n // on an effective page, and the caller hears about each one exactly once.\n const reported = useRef(0)\n const settlePage = useCallback(\n (next: number) => {\n if (reported.current === next) return\n reported.current = next\n onPageChange?.(next)\n },\n [onPageChange],\n )\n\n return (\n <AsyncView\n state={shown}\n empty={empty}\n loadingLabel={loadingLabel}\n retryLabel={retryLabel}\n // The same box in every state: a deck that is 200px tall while loading\n // and 400px once loaded shoves the page under it on arrival.\n className={className}\n >\n {(insights) => (\n <InsightPages\n insights={insights}\n label={label}\n pageSize={pageSize}\n className={className}\n page={page}\n busy={refreshing}\n onSelectPage={setPage}\n onPageSettled={settlePage}\n />\n )}\n </AsyncView>\n )\n}\n\n/** Tag names whose own keyboard model owns the arrow keys. */\nconst EDITABLE_TAG = /^(INPUT|TEXTAREA|SELECT)$/\n\n/**\n * ARIA roles that own the arrow keys, checked because the TAG name cannot\n * answer the question.\n *\n * `action` takes an arbitrary element, so a card's next action is routinely a\n * composed widget: a combobox is a `<button aria-expanded>` far more often than\n * it is a `<select>`, a slider is a `<div role=\"slider\">`, a menu button opens a\n * `role=\"menu\"`. Measured on the documented object form: `<button\n * role=\"combobox\">` inside a card had its `ArrowRight` swallowed by the deck,\n * which then paged away from the control the reader was operating.\n *\n * Walked up to the deck rather than read off the event target, because focus\n * inside a composite widget lands on a descendant — the `role=\"grid\"` is the\n * ancestor of the `role=\"gridcell\"` that has focus.\n */\nconst ARROW_KEY_ROLES: ReadonlySet<string> = new Set([\n 'application',\n 'combobox',\n 'grid',\n 'gridcell',\n 'listbox',\n 'menu',\n 'menubar',\n 'menuitem',\n 'menuitemcheckbox',\n 'menuitemradio',\n 'option',\n 'radiogroup',\n 'row',\n 'scrollbar',\n 'searchbox',\n 'slider',\n 'spinbutton',\n 'tab',\n 'tablist',\n 'textbox',\n 'tree',\n 'treegrid',\n 'treeitem',\n])\n\nfunction ownsArrowKeys(target: EventTarget | null, boundary: EventTarget | null): boolean {\n let node = target instanceof Element ? target : null\n while (node !== null && node !== boundary) {\n if (EDITABLE_TAG.test(node.tagName)) return true\n if (node instanceof HTMLElement && node.isContentEditable) return true\n const role = node.getAttribute('role')\n if (role !== null && role.split(/\\s+/).some((token) => ARROW_KEY_ROLES.has(token))) return true\n node = node.parentElement\n }\n return false\n}\n\nfunction InsightPages({\n insights,\n label,\n pageSize,\n className,\n page,\n busy,\n onSelectPage,\n onPageSettled,\n}: {\n insights: readonly Insight[]\n label: string\n pageSize: number\n className?: string\n page: number\n busy: boolean\n onSelectPage: (page: number) => void\n onPageSettled: (page: number) => void\n}): ReactElement {\n const size = insightPageSize(pageSize)\n const pageCount = insightPageCount(insights.length, size)\n // Clamped at render, not only on navigation: the list can shrink between\n // renders (a retry returning fewer insights) and held state would point past\n // the end. Clamped rather than written back, so a list that grows again\n // returns the reader to where they were — and the caller is TOLD which page\n // that leaves them on, through `onPageSettled` below.\n const current = Math.min(Math.max(page, 0), pageCount - 1)\n const visible = insightPageSlice(insights, current, size)\n\n const sectionRef = useRef<HTMLElement | null>(null)\n const listRef = useRef<HTMLUListElement | null>(null)\n /** Set by a page turn that was made from INSIDE the cards, which the turn is\n * about to replace. */\n const recoverFocus = useRef(false)\n\n // One report per effective page, from either cause. Held above this component\n // so a remount cannot re-report a page the caller already has.\n useEffect(() => {\n onPageSettled(current)\n }, [current, onPageSettled])\n\n // Where focus goes on a page turn, decided rather than dropped.\n //\n // A page turn deliberately REMOUNTS the cards (the page-prefixed key — that is\n // what replays the arrival), so a reader standing on a card's action is\n // standing on a node that is about to be removed: measured, `document.\n // activeElement` became `document.body`. The deck is the answer. It owns the\n // paging, it survives the turn, it is a tab stop whenever there is a page to\n // turn to, and every paging key works from it — so the reader keeps paging\n // instead of being returned to the top of the document.\n //\n // A turn made from a PAGER control moves nothing: those buttons outlive the\n // turn, and taking focus off the \"Next insights\" the reader is clicking would\n // be the same defect pointed the other way.\n useEffect(() => {\n if (!recoverFocus.current) return\n recoverFocus.current = false\n sectionRef.current?.focus()\n }, [current])\n\n /** `true` when the page actually moved — which is what decides whether the\n * key that asked for it is consumed. */\n const goTo = useCallback(\n (next: number): boolean => {\n const clamped = Math.min(Math.max(next, 0), pageCount - 1)\n if (clamped === current) return false\n const active = typeof document === 'undefined' ? null : document.activeElement\n recoverFocus.current = active instanceof Node && (listRef.current?.contains(active) ?? false)\n onSelectPage(clamped)\n return true\n },\n [current, onSelectPage, pageCount],\n )\n\n const onKeyDown = (event: KeyboardEvent<HTMLElement>): void => {\n if (event.defaultPrevented) return\n // A card's action may be a text field, a select, or any ARIA widget whose\n // own keyboard model owns the arrows; they belong to it before they belong\n // to paging.\n if (ownsArrowKeys(event.target, event.currentTarget)) return\n let moved = false\n switch (event.key) {\n case 'ArrowRight':\n case 'PageDown':\n moved = goTo(current + 1)\n break\n case 'ArrowLeft':\n case 'PageUp':\n moved = goTo(current - 1)\n break\n case 'Home':\n moved = goTo(0)\n break\n case 'End':\n moved = goTo(pageCount - 1)\n break\n default:\n return\n }\n // Only a key that turned a page is consumed. `ArrowRight` on the last page\n // moves nothing here, and swallowing it there costs the reader the scroll\n // the browser would have done — a surface taking a key it has no use for.\n if (moved) event.preventDefault()\n }\n\n return (\n <section\n ref={sectionRef}\n aria-label={label}\n data-insight-deck=\"\"\n // Emitted in both states rather than added when the reload starts: a\n // region that only gains the attribute while busy gives assistive tech no\n // transition to report. The deck keeps its cards through the reload, so\n // this is the only thing that says one is happening.\n aria-busy={busy}\n className={joinClasses('space-y-3', className)}\n onKeyDown={onKeyDown}\n // The deck itself is the paging control, so it has to be somewhere a\n // keyboard can land: a handler on an element with no tab stop is reachable\n // only by a mouse user, who has the arrows anyway. One stop, and only when\n // there is a second page to reach — a single-page deck offers nothing to\n // page to and should not be in anyone's tab order.\n tabIndex={pageCount > 1 ? 0 : undefined}\n aria-keyshortcuts={pageCount > 1 ? 'ArrowLeft ArrowRight PageUp PageDown Home End' : undefined}\n >\n <ul ref={listRef} className=\"grid gap-3 sm:grid-cols-2 lg:grid-cols-3\">\n {visible.map(({ id, style, ...card }, index) => (\n // The page index is in the key on purpose: a page turn is an arrival,\n // and reusing the node would swap the text under a card that never\n // moved. Remounting replays `.agent-arrive` with the new stagger.\n //\n // A REFRESH is the other case and the key is why it behaves the other\n // way: the page has not changed and the id is stable, so the key\n // matches, React keeps the node, and a card that was already settled\n // does not arrive a second time. The key does BOTH jobs — but only\n // because the deck now keeps this subtree mounted across a reload\n // (see `InsightDeck`); a key is never compared across a teardown.\n <li key={`${current}:${id}`}>\n <InsightCard {...card} style={staggerStyle(index, style)} />\n </li>\n ))}\n </ul>\n\n {/* The live region is mounted whatever shape the deck is in. Held inside\n the `pageCount > 1` branch it was DESTROYED the moment a shrinking list\n collapsed the deck onto one page — measured: a reader on page 3 of 3\n watched the list drop to two insights, the counter unmounted with the\n pager, and the one change most worth announcing was the change that\n removed the thing that would have announced it. Mounted, its text goes\n from \"Page 3 of 3\" to \"Page 1 of 1\" and the reader is told. */}\n <div className={pageCount > 1 ? 'flex items-center justify-between gap-2' : undefined}>\n <p\n role=\"status\"\n aria-live=\"polite\"\n className={pageCount > 1 ? 'text-[11px] text-muted-foreground' : 'sr-only'}\n >\n Page {current + 1} of {pageCount}\n </p>\n {pageCount > 1 ? (\n <div className=\"flex items-center gap-1\">\n <PagerButton label=\"Previous insights\" glyph=\"‹\" atEnd={current === 0} onClick={() => goTo(current - 1)} />\n {pageCount <= MAX_PAGE_DOTS\n ? Array.from({ length: pageCount }, (_, index) => (\n // WCAG 2.2 SC 2.5.8 wants a 24x24 CSS px target. The dot stays\n // 8px because a 24px dot is a different control; the BUTTON\n // around it carries the target, so the padding is the hit area\n // and the span is the graphic. The Spacing exception cannot\n // rescue the bare dot — at a 12px pitch the 24px circle around\n // each centre overlaps its neighbour's.\n <button\n key={index}\n type=\"button\"\n aria-label={`Page ${index + 1} of ${pageCount}`}\n // `page`, not `true`: `true` is the generic token, and\n // `page` is the one ARIA defines for a pagination control.\n aria-current={index === current ? 'page' : undefined}\n onClick={() => goTo(index)}\n className=\"group flex h-6 w-6 shrink-0 items-center justify-center rounded-full\"\n >\n {/* `bg-muted-foreground`, NOT `bg-border`. A divider token is\n tuned to be barely there, and measured in Chromium the\n inactive dot painted rgb(204,205,211) on rgb(236,236,241)\n — 1.35:1, less than half the 3:1 WCAG 2.2 SC 1.4.11 asks\n of a meaningful graphic. This card's own sparkline note\n already says the text-grade muted foreground clears that\n bar and a divider tint does not; the dots are the same\n kind of graphic and now use the same token.\n\n The CURRENT page then needs a second channel, because\n tone alone no longer carries it: foreground against\n muted-foreground is 3.10:1 in light but 2.27:1 in dark.\n So the current page is a WIDER bar at the same 8px\n height — a difference in shape, which survives both a\n low-contrast theme and forced colours, and still sits\n inside the 24px target with the pitch unchanged.\n\n The hover belongs to the target, not to the graphic:\n `group-hover` keeps the whole 24px square reactive\n instead of only the 8px the eye is aiming at. */}\n <span\n aria-hidden=\"true\"\n className={joinClasses(\n 'block rounded-full transition',\n index === current ? 'h-2 w-4 bg-foreground' : 'h-2 w-2 bg-muted-foreground group-hover:bg-foreground',\n )}\n />\n </button>\n ))\n : null}\n <PagerButton\n label=\"Next insights\"\n glyph=\"›\"\n atEnd={current === pageCount - 1}\n onClick={() => goTo(current + 1)}\n />\n </div>\n ) : null}\n </div>\n </section>\n )\n}\n\n/**\n * `aria-disabled`, never the `disabled` attribute.\n *\n * A disabled button leaves the tab order at the moment it is pressed, so the\n * keyboard user who paged to the last card loses the focus they were paging\n * with and lands on the document body. Keeping it focusable and inert holds the\n * focus where the reader put it, and the arrow keys keep working from there.\n */\nfunction PagerButton({\n label,\n glyph,\n atEnd,\n onClick,\n}: {\n label: string\n glyph: string\n atEnd: boolean\n onClick: () => void\n}): ReactElement {\n return (\n <button\n type=\"button\"\n aria-label={label}\n aria-disabled={atEnd}\n onClick={() => {\n if (!atEnd) onClick()\n }}\n className={`flex h-6 w-6 items-center justify-center rounded-md border border-border text-xs text-muted-foreground transition ${\n atEnd ? 'opacity-40' : 'hover:bg-accent hover:text-foreground'\n }`}\n >\n <span aria-hidden=\"true\">{glyph}</span>\n </button>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,SAAS,aAAAA,aAAkB,WAAAC,UAAS,UAAAC,UAAQ,YAAAC,YAAU,YAA4B;AAClF,SAAS,gBAAgB,mBAAmB;;;AChB5C,SAAS,WAAW,QAAQ,gBAAgB;AAcrC,SAAS,gBACd,OACA,cACA,MACA,OAA4B,CAAC,GACrB;AACR,MAAI,SAAS,aAAc,QAAO;AAClC,QAAM,OAAO,KAAK,sBAAsB;AACxC,QAAM,UAAU,KAAK,kBAAkB;AACvC,QAAM,MAAM,KAAK,qBAAqB;AACtC,QAAM,UAAU,eAAe;AAC/B,QAAM,OAAO,KAAK,IAAI,KAAK,OAAO,UAAU,OAAO;AACnD,SAAO,KAAK,IAAI,cAAc,QAAS,OAAO,OAAQ,GAAI;AAC5D;AASO,SAAS,cAAc,QAAgB,SAAkB,MAAoC;AAClG,QAAM,CAAC,EAAE,KAAK,IAAI,SAAS,CAAC;AAC5B,QAAM,WAAW,OAAO,CAAC;AACzB,QAAM,gBAAgB,OAAO,EAAE;AAG/B,MAAI,CAAC,OAAO,WAAW,cAAc,QAAQ,MAAM,GAAG,KAAK,MAAM,SAAS,OAAO,CAAC,CAAC,GAAG;AACpF,aAAS,UAAU;AAAA,EACrB;AACA,gBAAc,UAAU;AACxB,MAAI,CAAC,QAAS,UAAS,UAAU,OAAO;AAExC,YAAU,MAAM;AACd,QAAI,CAAC,QAAS;AACd,QAAI,MAAM;AACV,QAAI,OAAsB;AAC1B,UAAM,OAAO,CAAC,MAAc;AAC1B,YAAM,KAAK,QAAQ,OAAO,KAAK,KAAK,IAAI,IAAI,MAAM,GAAG;AACrD,aAAO;AACP,YAAM,YAAY,cAAc,QAAQ;AACxC,UAAI,SAAS,UAAU,WAAW;AAChC,iBAAS,UAAU,gBAAgB,SAAS,SAAS,WAAW,IAAI,IAAI;AACxE,cAAM,CAAC,MAAM,IAAI,CAAC;AAElB,cAAM,sBAAsB,IAAI;AAAA,MAClC;AAAA,IAKF;AACA,UAAM,sBAAsB,IAAI;AAChC,WAAO,MAAM,qBAAqB,GAAG;AAAA,EAEvC,GAAG,CAAC,SAAS,MAAM,CAAC;AAEpB,SAAO,OAAO,MAAM,GAAG,KAAK,MAAM,SAAS,OAAO,CAAC;AACrD;;;ACxEA,SAAS,YAAAC,iBAAoC;AAkBtC,SAAS,aAAa,OAAe,MAAqC;AAC/E,SAAO,EAAE,GAAG,MAAM,mBAAmB,MAAM;AAC7C;AAgBO,SAAS,gBAAgB,OAA8B;AAC5D,QAAM,CAAC,MAAM,IAAIA,UAAS,KAAK;AAC/B,SAAO,aAAa,MAAM;AAC5B;;;AC5CA,SAAS,MAAM,gBAAgB;AAUtB;AADT,SAAS,WAAW,EAAE,OAAO,IAAI,UAAU,GAAmB;AAC5D,SAAO,oBAAC,UAAK,eAAW,MAAC,OAAO,EAAE,SAAS,gBAAgB,OAAO,MAAM,QAAQ,KAAK,GAAG,WAAsB;AAChH;AAEA,IAAM,WAAW,KAAK,YAAY;AAChC,MAAI;AACF,UAAM,MAAM,MAAM,OAAO,kBAAU;AACnC,WAAO,EAAE,SAAS,IAAI,WAA4C;AAAA,EACpE,QAAQ;AACN,WAAO,EAAE,SAAS,WAA4C;AAAA,EAChE;AACF,CAAC;AAEM,SAAS,UAAU,EAAE,OAAO,IAAI,UAAU,GAAmB;AAClE,SACE,oBAAC,YAAS,UAAU,oBAAC,cAAW,MAAY,WAAsB,GAChE,8BAAC,YAAS,MAAY,WAAsB,GAC9C;AAEJ;;;AClCA,SAAS,aAAAC,YAAW,iBAAiB,UAAAC,SAAQ,YAAAC,iBAAgC;;;ACqB7E,SAAyB,aAAAC,YAAW,SAAS,UAAAC,SAAQ,YAAAC,iBAAgB;;;ACE9D,SAAS,wBACd,QACuC;AACvC,SAAO,EAAE,WAAW,aAAa,SAAS,WAAW,GAAG,OAAO;AACjE;AAKO,SAAS,yBACd,MACA,OACgD;AAChD,SAAO;AAAA,IACL,SAAS,QAAQ,IAAI;AAAA,IACrB,WAAW,2BAA2B,IAAI;AAAA,IAC1C,GAAG;AAAA,EACL;AACF;AAYO,SAAS,uBACd,QACA,SACa;AACb,MAAI,CAAC,QAAS,QAAO,CAAC;AACtB,QAAM,SAAsB,CAAC;AAC7B,aAAW,SAAS,QAAQ;AAC1B,UAAM,SAAS,QAAQ,MAAM,IAAI;AACjC,QAAI,WAAW,OAAW;AAC1B,QAAI,MAAM,SAAS,UAAU;AAC3B,aAAO,MAAM,IAAI,IAAI,EAAE,UAAU,MAAM,QAAQ,MAAM,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,OAAO,MAAM,CAAC,EAAE;AAAA,IAC1F,WAAW,MAAM,SAAS,WAAW;AACnC,aAAO,MAAM,IAAI,IAAI,EAAE,UAAU,CAAC,OAAO,MAAM,CAAC,EAAE;AAAA,IACpD,OAAO;AACL,aAAO,MAAM,IAAI,IAAI,EAAE,MAAM,OAAO,MAAM,EAAE;AAAA,IAC9C;AAAA,EACF;AACA,SAAO;AACT;AAWO,SAAS,YAAY,OAA6B,QAAwD;AAC/G,QAAM,QAAQ,OAAO,MAAM,IAAI,KAAK,CAAC;AACrC,MAAI,MAAM,SAAS,UAAU;AAC3B,UAAM,SAAU,MAA0B,gBAAgB,OAAO,MAAM,QAAQ,KAAK,IAAI;AACxF,UAAM,SAAS,CAAC,GAAI,MAAM,YAAY,CAAC,GAAI,GAAI,SAAS,CAAC,MAAM,IAAI,CAAC,CAAE;AACtE,QAAI,MAAM,UAAU,QAAQ,OAAQ,QAAO,CAAC,MAAM;AAClD,WAAO,OAAO,SAAS,IAAI,SAAS;AAAA,EACtC;AACA,MAAI,MAAM,SAAS,UAAU;AAC3B,UAAM,SAAS,OAAO,MAAM,IAAI;AAChC,WAAO,MAAM,MAAM,KAAK,KAAK,OAAO,SAAS,MAAM,IAAI,SAAS;AAAA,EAClE;AACA,MAAI,MAAM,SAAS,UAAW,QAAO,MAAM,WAAW,MAAM,SAAS,CAAC,MAAM,SAAS;AACrF,QAAM,OAAO,MAAM,MAAM,KAAK;AAC9B,SAAO,OAAO,OAAO;AACvB;AAIO,SAAS,gBAAgB,QAAgC,QAAgD;AAC9G,QAAM,OAA2B,CAAC;AAClC,aAAW,SAAS,QAAQ;AAC1B,UAAM,SAAS,YAAY,OAAO,MAAM;AACxC,QAAI,WAAW,MAAM;AACnB,UAAI,MAAM,aAAa,MAAO;AAC9B,aAAO;AAAA,IACT;AACA,SAAK,MAAM,IAAI,IAAI;AAAA,EACrB;AACA,SAAO;AACT;AAQO,SAAS,uBAAuB,QAAwC;AAC7E,SAAO,WAAW,aAAa,WAAW;AAC5C;AAIO,SAAS,eAAe,QAAyC;AACtE,SAAO,OAAO,KAAK,CAAC,UAAU,MAAM,SAAS,QAAQ;AACvD;AAEA,SAAS,YAAY,OAAwB,OAAuB;AAClE,SAAO,MAAM,QAAQ,KAAK,CAAC,WAAW,OAAO,UAAU,KAAK,GAAG,SAAS;AAC1E;AAEA,SAAS,WAAW,OAA6B,QAAyC;AACxF,MAAI,MAAM,SAAS,YAAY,MAAM,QAAQ,MAAM,GAAG;AACpD,WAAO,OAAO,IAAI,CAAC,UAAU,YAAY,OAA0B,KAAK,CAAC,EAAE,KAAK,IAAI;AAAA,EACtF;AACA,MAAI,MAAM,SAAS,UAAW,QAAO,WAAW,OAAO,QAAQ;AAC/D,MAAI,MAAM,SAAS,SAAU,QAAO;AACpC,SAAO,OAAO,MAAM;AACtB;AAIO,SAAS,kBAAkB,aAA8B,MAA+B;AAC7F,QAAM,QAAQ,YAAY,MAAM,KAAK,KAAK;AAC1C,QAAM,OAAO,YAAY,MAAM,KAAK;AACpC,QAAM,UAAU,YAAY,OACzB,IAAI,CAAC,UAAU;AACd,UAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,QAAI,WAAW,OAAW,QAAO;AACjC,WAAO,EAAE,OAAO,MAAM,MAAM,KAAK,GAAG,MAAM,WAAW,OAAO,MAAM,EAAE,KAAK,EAAE;AAAA,EAC7E,CAAC,EACA,OAAO,CAAC,SAAkD,CAAC,CAAC,QAAQ,KAAK,KAAK,SAAS,CAAC;AAE3F,QAAM,OAAO,QAAQ,WAAW,IAAI,QAAQ,CAAC,IAAI;AACjD,QAAM,gBAAgB,OAClB,KAAK,OACL,QAAQ,IAAI,CAAC,SAAS,GAAG,KAAK,SAAS,QAAQ,KAAK,KAAK,IAAI,EAAE,EAAE,KAAK,IAAI;AAE9E,SAAO;AAAA,IACL,qCAAqC,KAAK;AAAA,IAC1C,OAAO,YAAY,IAAI,KAAK;AAAA,IAC5B,cAAc,aAAa;AAAA,EAC7B,EAAE,OAAO,CAAC,SAAyB,CAAC,CAAC,IAAI,EAAE,KAAK,IAAI;AACtD;AAMO,IAAM,gCAAgC;AAEtC,IAAM,qCAAqC;AAqBlD,eAAsB,qBAAqB,KAA4D;AACrG,QAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,MAAI,MAAM;AACR,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,YAAM,UAAU,OAAO,OAAO,UAAU,YAAY,OAAO,MAAM,KAAK,IAAI,OAAO,QAC7E,OAAO,OAAO,YAAY,YAAY,OAAO,QAAQ,KAAK,IAAI,OAAO,UACrE;AACJ,UAAI,QAAS,QAAO,EAAE,GAAI,OAAO,OAAO,SAAS,WAAW,EAAE,MAAM,OAAO,KAAK,IAAI,CAAC,GAAI,QAAQ;AAAA,IACnG,QAAQ;AAAA,IAAoC;AAAA,EAC9C;AACA,SAAO,EAAE,SAAS,kBAAkB,IAAI,MAAM,IAAI;AACpD;AA6BO,SAAS,wBACd,KACA,YAAoB,+BACc;AAClC,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,QAAQ;AAAA,MACZ,MAAM,QAAQ,EAAE,IAAI,OAAO,SAAS,OAAO,SAAS,mCAAmC,CAAC;AAAA,MACxF;AAAA,IACF;AACA,UAAM,SAAS,CAAC,WAAoC;AAClD,mBAAa,KAAK;AAGlB,cAAQ,MAAM;AAAA,IAChB;AAIA,YAAQ,QAAQ,EACb,KAAK,GAAG,EACR;AAAA,MAAK;AAAA,MAAQ,CAAC,QACb,OAAO;AAAA,QACL,IAAI;AAAA,QACJ,SAAS;AAAA,QACT,SAAS,eAAe,QAAQ,IAAI,UAAU;AAAA,MAChD,CAAC;AAAA,IACH;AAAA,EACJ,CAAC;AACH;AASO,SAAS,iCAAiC,SAAqE;AACpH,QAAM,YAAY,QAAQ,aAAa;AACvC,SAAO,OAAO,eAAe;AAC3B,UAAM,UAAU,QAAQ,aAAa;AACrC,UAAM,MAAM,OAAO,QAAQ,QAAQ,aAAa,QAAQ,IAAI,UAAU,IAAI,QAAQ;AAClF,UAAM,QAAQ,OAAO,QAAQ,SAAS,aAAa,QAAQ,KAAK,UAAU,IAAI,QAAQ,QAAQ,CAAC;AAC/F,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,kCAAkC,GAAG,SAAS;AAC9F,QAAI;AACF,YAAM,MAAM,MAAM,QAAQ,KAAK;AAAA,QAC7B,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,QAAQ,WAAW;AAAA,QACnB,MAAM,KAAK,UAAU;AAAA,UACnB,GAAG;AAAA,UACH,IAAI,WAAW;AAAA,UACf,SAAS,WAAW;AAAA,UACpB,GAAI,WAAW,OAAO,EAAE,MAAM,WAAW,KAAK,IAAI,CAAC;AAAA,QACrD,CAAC;AAAA,MACH,CAAC;AACD,UAAI,IAAI,GAAI,QAAO,EAAE,IAAI,KAAK;AAC9B,YAAM,UAAU,MAAM,qBAAqB,GAAG;AAC9C,aAAO,EAAE,IAAI,OAAO,SAAS,IAAI,WAAW,KAAK,SAAS,QAAQ,QAAQ;AAAA,IAC5E,SAAS,KAAK;AACZ,UAAI,WAAW,OAAO,SAAS;AAC7B,eAAO,EAAE,IAAI,OAAO,SAAS,OAAO,SAAS,mCAAmC;AAAA,MAClF;AACA,aAAO,EAAE,IAAI,OAAO,SAAS,OAAO,SAAS,eAAe,QAAQ,IAAI,UAAU,8BAA8B;AAAA,IAClH,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AACF;;;ADhQM,SAyFF,UAzFE,OAAAC,MAsHM,YAtHN;AAHN,SAAS,WAAW,EAAE,UAAU,GAA2B;AACzD,SACE,gBAAAA,KAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACvJ,0BAAAA,KAAC,UAAK,GAAE,mBAAkB,GAC5B;AAEJ;AAIA,IAAM,wBAAiE;AAAA,EACrE,SAAS;AAAA,EACT,SAAS;AAAA,EACT,aAAa;AACf;AAEO,SAAS,iBAAiB,EAAE,SAAS,SAAS,GAA2D;AAC9G,SACE,gBAAAA,KAAC,UAAK,WAAW,8EAA8E,sBAAsB,OAAO,CAAC,IAC1H,UACH;AAEJ;AAEO,SAAS,wBAAwB;AAAA,EACtC,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,QAAM,iBAAiB,YAAY,YAC/B,qEACA;AACJ,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA,WAAW,0IAA0I,cAAc;AAAA,MAElK;AAAA;AAAA,EACH;AAEJ;AAEA,IAAM,sBACJ;AA8BK,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AACb,GAA4B;AAC1B,SACE,gBAAAA,KAAA,YACG,kBAAQ,IAAI,CAAC,QAAQ,gBAAgB;AACpC,UAAM,UAAU,GAAG,QAAQ,IAAI,WAAW;AAC1C,UAAM,UAAU,eAAe,SAAS,OAAO,KAAK;AACpD,UAAM,cAAc,YAAY;AAIhC,WACE;AAAA,MAAC;AAAA;AAAA,QAEC,SAAS;AAAA,QACT,OAAO,aAAa,WAAW;AAAA,QAC/B,WAAW,mEACT,cAAc,gCAAgC,eAChD,IAAI,WAAW,mBAAmB,gCAAgC;AAAA,QAElE;AAAA,0BAAAA;AAAA,YAAC;AAAA;AAAA,cACC,IAAI;AAAA,cACJ,MAAM,QAAQ,aAAa;AAAA,cAC3B,MAAM;AAAA,cACN,OAAO,OAAO;AAAA,cACd;AAAA,cACA;AAAA,cACA,UAAU,MAAM,SAAS,OAAO,KAAK;AAAA,cACrC,mBAAiB,GAAG,OAAO;AAAA,cAC3B,oBAAkB,OAAO,cAAc,GAAG,OAAO,iBAAiB;AAAA,cAClE,WAAU;AAAA;AAAA,UACZ;AAAA,UACA,qBAAC,UAAK,WAAU,kBACd;AAAA,4BAAAA,KAAC,UAAK,IAAI,GAAG,OAAO,UAAU,WAAU,uDAAuD,iBAAO,OAAM;AAAA,YAC3G,OAAO,eAAe,gBAAAA,KAAC,UAAK,IAAI,GAAG,OAAO,gBAAgB,WAAU,wDAAwD,iBAAO,aAAY;AAAA,aAClJ;AAAA,UACC,eAAe,gBAAAA,KAAC,cAAW,WAAU,wCAAuC;AAAA;AAAA;AAAA,MAvBxE,GAAG,OAAO,KAAK,IAAI,WAAW;AAAA,IAwBrC;AAAA,EAEJ,CAAC,GACH;AAEJ;AAgDA,SAAS,YAAY,OAAqD;AACxE,SAAO,MAAM,SAAS,WAAY,QAA4B;AAChE;AAGA,SAAS,cAAc,OAAuD;AAC5E,SAAO,MAAM,SAAS,UAAU,MAAM,SAAS,WAAY,QAA8B;AAC3F;AAMA,SAAS,mBAAmB,OAA8C;AACxE,QAAM,MAAM,MAAM;AAClB,SAAO,OAAO,QAAQ,YAAY,OAAO,UAAU,GAAG,KAAK,MAAM,IAAI,MAAM;AAC7E;AAEA,SAAS,mBAAmB,QAAqB,OAAwB,aAAkC;AACzG,QAAM,UAAU,OAAO,MAAM,IAAI,GAAG,YAAY,CAAC;AACjD,MAAI,WAAW,CAAC,WAAW;AAC3B,MAAI,MAAM,UAAU,MAAM;AACxB,eAAW,QAAQ,SAAS,WAAW,IACnC,QAAQ,OAAO,CAAC,SAAS,SAAS,WAAW,IAC7C,CAAC,GAAG,SAAS,WAAW;AAAA,EAC9B;AACA,SAAO,EAAE,GAAG,QAAQ,CAAC,MAAM,IAAI,GAAG,EAAE,GAAG,OAAO,MAAM,IAAI,GAAG,SAAS,EAAE;AACxE;AAEA,IAAM,gBAAgB,wBAAwB;AAAA,EAC5C,SAAS;AAAA,EACT,UAAU;AAAA,EACV,UAAU;AACZ,CAAC;AAED,IAAM,iBAAiB,yBAAyB,YAAY;AAAA,EAC1D,SAAS;AAAA,EACT,WAAW;AACb,CAAC;AAEM,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAiC;AAC/B,QAAM,CAAC,QAAQ,SAAS,IAAIC,UAAsB,MAChD,uBAAuB,YAAY,QAAQ,YAAY,OAAO,CAAC;AACjE,QAAM,CAAC,YAAY,aAAa,IAAIA,UAAS,KAAK;AAGlD,QAAM,CAAC,aAAa,cAAc,IAAIA,UAA2D,IAAI;AACrG,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAAS,KAAK;AAC1D,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAwB,IAAI;AACtD,QAAM,oBAAoBC,QAAO,KAAK;AAMtC,QAAM,CAAC,OAAO,QAAQ,IAAID,UAAS,YAAY,EAAE;AAgCjD,MAAI,UAAU,YAAY,IAAI;AAC5B,aAAS,YAAY,EAAE;AACvB,cAAU,uBAAuB,YAAY,QAAQ,YAAY,OAAO,CAAC;AACzE,mBAAe,IAAI;AACnB,sBAAkB,KAAK;AACvB,aAAS,IAAI;AAAA,EACf;AAEA,EAAAE,WAAU,MAAM;AACd,QAAI,CAAC,YAAY,QAAS;AAC1B,cAAU,uBAAuB,YAAY,QAAQ,YAAY,OAAO,CAAC;AAAA,EAC3E,GAAG,CAAC,YAAY,SAAS,YAAY,MAAM,CAAC;AAE5C,QAAM,SAAgC,4BAA4B,YAAY,MAAM,IAChF,YAAY,SACZ,eAAe,YAAY;AAC/B,QAAM,WAAW,WAAW;AAC5B,QAAM,iBAAiB,uBAAuB,MAAM,KAAK,iBAAiB;AAC1E,QAAM,0BAA0B,kBAAkB,eAAe,YAAY,MAAM;AACnF,QAAM,gBAAgB,YAAY,kBAAkB,CAAC,kBAAkB,CAAC;AACxE,QAAM,WAAW,CAAC,YAAa,WAAW,aAAa,CAAC,iBAAkB;AAC1E,QAAM,aAAa,QAAQ,MAAM,gBAAgB,YAAY,QAAQ,MAAM,GAAG,CAAC,YAAY,QAAQ,MAAM,CAAC;AAE1G,QAAM,gBAAgB,CAAC,MAAc,UAA+B;AAClE,cAAU,CAAC,UAAU,EAAE,GAAG,MAAM,CAAC,IAAI,GAAG,EAAE,GAAG,KAAK,IAAI,GAAG,GAAG,MAAM,EAAE,EAAE;AAAA,EACxE;AAEA,QAAM,iBAAiB,CAAC,OAAwB,gBAAwB;AACtE,cAAU,CAAC,SAAS,mBAAmB,MAAM,OAAO,WAAW,CAAC;AAAA,EAClE;AAEA,iBAAe,mBAAmB;AAChC,QAAI,kBAAkB,WAAW,CAAC,iBAAiB,CAAC,aAAc;AAClE,UAAM,OAAO,gBAAgB,YAAY,QAAQ,MAAM;AACvD,QAAI,CAAC,KAAM;AACX,sBAAkB,UAAU;AAC5B,kBAAc,IAAI;AAClB,aAAS,IAAI;AACb,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,aAAa,kBAAkB,aAAa,IAAI,CAAC;AAAA,IACpE,QAAQ;AACN,iBAAW;AAAA,IACb,UAAE;AACA,wBAAkB,UAAU;AAC5B,oBAAc,KAAK;AAAA,IACrB;AACA,QAAI,aAAa,OAAO;AACtB,eAAS,yDAAyD;AAClE;AAAA,IACF;AACA,sBAAkB,IAAI;AAAA,EACxB;AAEA,iBAAe,SAAS;AACtB,QAAI,gBAAgB;AAClB,YAAM,iBAAiB;AACvB;AAAA,IACF;AACA,QAAI,kBAAkB,WAAW,YAAY,CAAC,WAAY;AAC1D,sBAAkB,UAAU;AAC5B,kBAAc,IAAI;AAClB,aAAS,IAAI;AACb,QAAI;AACF,YAAM,SAAS,MAAM;AAAA,QAAwB,MAC3C,aAAa,EAAE,IAAI,YAAY,IAAI,SAAS,YAAY,MAAM,WAAW,CAAC;AAAA,MAC5E;AACA,UAAI,OAAO,IAAI;AACb,uBAAe,UAAU;AACzB,qBAAa,YAAY,IAAI,YAAY,UAAU;AACnD;AAAA,MACF;AACA,UAAI,OAAO,SAAS;AAGlB,uBAAe,SAAS;AACxB,qBAAa,YAAY,IAAI,SAAS;AACtC;AAAA,MACF;AACA,eAAS,OAAO,OAAO;AAAA,IACzB,UAAE;AACA,wBAAkB,UAAU;AAC5B,oBAAc,KAAK;AAAA,IACrB;AAAA,EACF;AAEA,QAAM,eAAe,0BACjB,qHACA,eAAe,MAAM;AACzB,QAAM,mBAAmB,WAAW,aAAc,YAAY,kBAAkB,CAAC;AAMjF,QAAM,kBAAkB,eAAe,QAAQ,WAAW;AAC1D,MAAI,cAAc;AAClB,MAAI,gBAAgB;AAClB,kBAAc,aAAa,kBAAa;AAAA,EAC1C,WAAW,YAAY;AACrB,kBAAc;AAAA,EAChB;AAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAWE,qBAAC,SAAI,WAAW,wFAAwF,aAAa,EAAE,IACrH;AAAA,2BAAC,SAAI,WAAU,0CACb;AAAA,wBAAAH,KAAC,oBAAiB,SAAQ,WAAW,uBAAa,YAAW;AAAA,QAC7D,gBAAAA,KAAC,oBAAiB,SAAS,WAAW,YAAY,WAAW,aAAa,WAAW,aAAa,gBAAgB,WAC/G,wBAAc,MAAM,GACvB;AAAA,SACF;AAAA,MAEC,YAAY,MAAM,KAAK,KAAK,YAAY,OAAO,MAAM,CAAC,UAAU,MAAM,UAAU,YAAY,KAAK,KAChG,gBAAAA,KAAC,OAAE,WAAU,+DAA+D,sBAAY,OAAM;AAAA,MAE/F,YAAY,SAAS,iBAClB,gBAAAA,KAAC,SAAI,WAAU,gDAAgD,yBAAe,YAAY,IAAI,GAAE,IAChG,gBAAAA,KAAC,OAAE,WAAU,gDAAgD,sBAAY,MAAK;AAAA,MAElF,gBAAAA,KAAC,SAAI,WAAU,aACZ,sBAAY,OAAO,IAAI,CAAC,UAAU;AACjC,cAAM,QAAQ,OAAO,MAAM,IAAI,KAAK,CAAC;AACrC,cAAM,SAAS,YAAY,KAAK;AAChC,cAAM,WAAW,cAAc,KAAK;AACpC,eACE,qBAAC,cAA0B,WAAU,aACnC;AAAA,0BAAAA,KAAC,OAAE,WAAU,iDAAiD,gBAAM,OAAM;AAAA,UACzE,SACC,qBAAC,SAAI,WAAU,aACb;AAAA,4BAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,WAAW,GAAG,YAAY,EAAE,IAAI,MAAM,IAAI;AAAA,gBAC1C,UAAU,GAAG,YAAY,EAAE,IAAI,MAAM,IAAI;AAAA,gBACzC,SAAS,OAAO;AAAA,gBAChB,OAAO,OAAO,UAAU;AAAA,gBACxB,gBAAgB,MAAM,YAAY,CAAC;AAAA,gBACnC;AAAA,gBACA,UAAU,CAAC,gBAAgB,eAAe,QAAQ,WAAW;AAAA,gBAC7D;AAAA;AAAA,YACF;AAAA,YACC,OAAO,gBAAgB,QACtB,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,OAAO,MAAM,UAAU;AAAA,gBACvB;AAAA,gBACA,UAAU,CAAC,UAAU,cAAc,MAAM,MAAM,EAAE,QAAQ,MAAM,OAAO,MAAM,CAAC;AAAA,gBAC7E,aAAY;AAAA,gBACZ,cAAY,qBAAqB,MAAM,KAAK;AAAA,gBAC5C,WAAW;AAAA;AAAA,YACb;AAAA,aAEJ,IACE,MAAM,SAAS,YACjB,gBAAAA,KAAC,SAAI,WAAU,cACX,WAAC,QAAQ,OAAO,EAAY,IAAI,CAAC,cACjC,qBAAC,WAAsB,WAAU,kEAC/B;AAAA,4BAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,MAAM,GAAG,YAAY,EAAE,IAAI,MAAM,IAAI;AAAA,gBACrC,OAAO;AAAA,gBACP,UAAU,MAAM,YAAY,CAAC,GAAG,CAAC,MAAM;AAAA,gBACvC;AAAA,gBACA,UAAU,MAAM,cAAc,MAAM,MAAM,EAAE,UAAU,CAAC,SAAS,EAAE,CAAC;AAAA,gBACnE,WAAU;AAAA;AAAA,YACZ;AAAA,YACC,cAAc,SAAS,QAAQ;AAAA,eAVtB,SAWZ,CACD,GACH,IACE,MAAM,SAAS,WACjB,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,OAAO,MAAM,QAAQ;AAAA,cACrB;AAAA,cACA,cAAY,MAAM;AAAA,cAClB,UAAU,CAAC,UAAU,cAAc,MAAM,MAAM,EAAE,MAAM,MAAM,OAAO,MAAM,CAAC;AAAA,cAC3E,WAAW;AAAA;AAAA,UACb,IACE,MAAM,SAAS,WACjB,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,OAAO,MAAM,QAAQ;AAAA,cACrB;AAAA,cACA,cAAY,MAAM;AAAA,cAClB,UAAU,CAAC,UAAU,cAAc,MAAM,MAAM,EAAE,MAAM,MAAM,OAAO,MAAM,CAAC;AAAA,cAC3E,aAAa,MAAM;AAAA,cACnB,WAAW,WAAW,mBAAmB,QAAQ,IAAI;AAAA,cACrD,WAAW;AAAA;AAAA,UACb,IAEA,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,MAAM,QAAQ;AAAA,cACrB;AAAA,cACA,cAAY,MAAM;AAAA,cAClB,UAAU,CAAC,UAAU,cAAc,MAAM,MAAM,EAAE,MAAM,MAAM,OAAO,MAAM,CAAC;AAAA,cAC3E,MAAM;AAAA,cACN,WAAW,WAAW,mBAAmB,QAAQ,IAAI;AAAA,cACrD,aAAa,MAAM,SAAS,SAAS,MAAM,cAAc;AAAA,cACzD,WAAW;AAAA;AAAA,UACb;AAAA,aAzEW,MAAM,IA2ErB;AAAA,MAEJ,CAAC,GACH;AAAA,MAIC,SAAS,gBAAAA,KAAC,OAAE,MAAK,SAAQ,WAAU,iCAAiC,iBAAM;AAAA,MAC1E,gBAAgB,gBAAAA,KAAC,OAAE,WAAU,sCAAsC,wBAAa;AAAA,OAE/E,oBAAoB,oBACpB,qBAAC,SAAI,WAAU,sDACZ;AAAA,2BACC,gBAAAA,KAAC,SAAI,WAAU,yCAAyC,uBAAY;AAAA,QAErE,oBACC,gBAAAA,KAAC,2BAAwB,SAAS,MAAM,KAAK,OAAO,GAAG,UAAU,YAAY,CAAC,YAC3E,uBACH;AAAA,SAEJ;AAAA,MAED,YACC,gBAAAA,KAAC,SAAI,WAAU,sCACb,+BAAC,UAAK,WAAU,gEAA+D;AAAA,wBAAAA,KAAC,cAAW,WAAU,WAAU;AAAA,QAAE;AAAA,SAAQ,GAC3H;AAAA,MAED,kBACC,gBAAAA,KAAC,SAAI,WAAU,sCACb,+BAAC,UAAK,WAAU,gEAA+D;AAAA,wBAAAA,KAAC,cAAW,WAAU,WAAU;AAAA,QAAE;AAAA,SAAmB,GACtI;AAAA,OAEJ;AAAA;AAEJ;;;AD9eQ,SACE,OAAAI,MADF,QAAAC,aAAA;AAtDR,SAAS,YAAY,MAAwB;AAC3C,UAAQ,KAAK,QAAQ;AAAA,IACnB,KAAK;AAAW,aAAO;AAAA,IACvB,KAAK;AAAY,aAAO;AAAA,IACxB,KAAK;AAAY,aAAO;AAAA,IACxB,KAAK;AAAc,aAAO;AAAA,IAC1B,KAAK;AAAa,aAAO;AAAA,IACzB;AAAS,aAAO;AAAA,EAClB;AACF;AAIA,IAAM,uBAAuB;AAEtB,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AACF,GAAyB;AACvB,QAAM,CAAC,UAAU,WAAW,IAAIC,UAAS,EAAE;AAC3C,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAS,KAAK;AAC9C,QAAM,CAAC,YAAY,aAAa,IAAIA,UAAwB,IAAI;AAChE,EAAAC,WAAU,MAAM,cAAc,IAAI,GAAG,CAAC,KAAK,QAAQ,KAAK,UAAU,KAAK,MAAM,CAAC;AAE9E,QAAM,aAAa,KAAK,WAAW;AACnC,QAAM,WAAW,CAAC,YAAY,CAAC,cAAc,aAAa;AAI1D,QAAM,UAAUC,QAAuB,IAAI;AAC3C,QAAM,CAAC,WAAW,YAAY,IAAIF,UAAS,KAAK;AAChD,kBAAgB,MAAM;AACpB,UAAM,KAAK,QAAQ;AACnB,QAAI,GAAI,cAAa,GAAG,eAAe,oBAAoB;AAAA,EAC7D,GAAG,CAAC,KAAK,MAAM,cAAc,CAAC;AAE9B,iBAAe,OAAO,UAA+B;AACnD,UAAM,UAAU,SAAS,KAAK;AAC9B,QAAI,aAAa,cAAc,CAAC,SAAS;AACvC,oBAAc,8DAA8D;AAC5E;AAAA,IACF;AACA,kBAAc,IAAI;AAClB,UAAM,OAAO,UAAU,aAAa,aAAa,UAAU,MAAS;AAAA,EACtE;AAEA,SACE,gBAAAD,MAAC,SAAI,WAAW,mDAAmD,aAAa,EAAE,IAChF;AAAA,oBAAAA,MAAC,SAAI,WAAU,0DACb;AAAA,sBAAAA,MAAC,SAAI,WAAU,qCACb;AAAA,wBAAAD,KAAC,oBAAiB,SAAQ,WAAU,2BAAa;AAAA,QACjD,gBAAAA,KAAC,oBAAiB,SAAS,KAAK,WAAW,aAAa,YAAY,KAAK,WAAW,cAAc,KAAK,WAAW,cAAc,gBAAgB,WAC7I,sBAAY,IAAI,GACnB;AAAA,SACF;AAAA,MACA,gBAAAC,MAAC,UAAK,WAAU,iCAAgC;AAAA;AAAA,QAAU,KAAK;AAAA,SAAS;AAAA,OAC1E;AAAA,IACC,KAAK,SAAS,gBAAAD,KAAC,OAAE,WAAU,+DAA+D,eAAK,OAAM;AAAA,IACtG,gBAAAC,MAAC,SAAI,WAAU,YACb;AAAA,sBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,KAAK;AAAA,UACL,WAAU;AAAA,UACV,OAAO,YAAY,CAAC,YAAY,SAAY,EAAE,WAAW,qBAAqB;AAAA,UAE7E,2BAAiB,eAAe,KAAK,IAAI,IAAI,gBAAAA,KAAC,OAAE,WAAU,iCAAiC,eAAK,MAAK;AAAA;AAAA,MACxG;AAAA,MACC,aAAa,CAAC,YAAY,gBAAAA,KAAC,SAAI,WAAU,kGAAiG;AAAA,MAC1I,aACC,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS,MAAM,YAAY,CAAC,UAAU,CAAC,KAAK;AAAA,UAC5C,WAAU;AAAA,UAET,qBAAW,kBAAkB;AAAA;AAAA,MAChC;AAAA,OAEJ;AAAA,IACC,cACC,gBAAAC,MAAC,SAAI,WAAU,kBACb;AAAA,sBAAAD,KAAC,WAAM,WAAU,uDAAsD,SAAS,yBAAyB,KAAK,MAAM,IAAI,KAAK,QAAQ,IAAI,4CAEzI;AAAA,MACA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,IAAI,yBAAyB,KAAK,MAAM,IAAI,KAAK,QAAQ;AAAA,UACzD,OAAO;AAAA,UACP;AAAA,UACA,UAAU,CAAC,UAAU,YAAY,MAAM,OAAO,KAAK;AAAA,UACnD,MAAM;AAAA,UACN,aAAY;AAAA,UACZ,WAAU;AAAA;AAAA,MACZ;AAAA,OACF;AAAA,KAEA,cAAc,UAAU,gBAAAA,KAAC,OAAE,WAAU,iCAAiC,wBAAc,OAAM;AAAA,IAC3F,cACC,gBAAAC,MAAC,SAAI,WAAU,4CACb;AAAA,sBAAAD,KAAC,2BAAwB,SAAQ,WAAU,SAAS,MAAM,KAAK,OAAO,UAAU,GAAG,UAChF,uBAAa,aAAa,kBAAa,mBAC1C;AAAA,MACA,gBAAAA,KAAC,2BAAwB,SAAS,MAAM,KAAK,OAAO,UAAU,GAAG,UAC9D,uBAAa,aAAa,oBAAe,gBAC5C;AAAA,OACF;AAAA,KAEJ;AAEJ;;;AGpHA,SAAS,mBAAAK,kBAAiB,WAAAC,UAAS,UAAAC,SAAQ,YAAAC,iBAAgC;AAkBrE,gBAAAC,MAwLA,QAAAC,aAxLA;AAHN,SAASC,YAAW,EAAE,UAAU,GAA2B;AACzD,SACE,gBAAAF,KAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACvJ,0BAAAA,KAAC,UAAK,GAAE,mBAAkB,GAC5B;AAEJ;AAEA,SAAS,iBAAiB,EAAE,UAAU,GAA2B;AAC/D,SACE,gBAAAA,KAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACvJ,0BAAAA,KAAC,UAAK,GAAE,gBAAe,GACzB;AAEJ;AA0BA,IAAMG,iBAAgB,wBAAwB;AAAA,EAC5C,SAAS;AAAA,EACT,UAAU;AAAA,EACV,UAAU;AACZ,CAAC;AAED,IAAMC,kBAAiB,yBAAyB,QAAQ;AAAA,EACtD,UAAU;AACZ,CAAC;AAED,IAAM,2BAA2B;AAIjC,IAAMC,wBAAuB;AAK7B,SAAS,mBAAmB,OAA6B,QAAoC;AAC3F,QAAM,SAAS,YAAY,OAAO,MAAM;AACxC,MAAI,WAAW,KAAM,QAAO;AAC5B,MAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,UAAM,UAAU,MAAM,SAAS,WAAW,MAAM,UAAU;AAC1D,WAAO,OAAO,IAAI,CAAC,UAAU,SAAS,KAAK,CAAC,WAAW,OAAO,UAAU,KAAK,GAAG,SAAS,KAAK,EAAE,KAAK,IAAI;AAAA,EAC3G;AACA,SAAO,OAAO,MAAM;AACtB;AAMA,SAAS,iBAAiB,MAA0B,gBAA+D;AACjH,QAAM,UAAUC,QAAuB,IAAI;AAC3C,QAAM,CAAC,WAAW,YAAY,IAAIC,UAAS,KAAK;AAChD,EAAAC,iBAAgB,MAAM;AACpB,UAAM,KAAK,QAAQ;AACnB,QAAI,GAAI,cAAa,GAAG,eAAeH,qBAAoB;AAAA,EAC7D,GAAG,CAAC,MAAM,cAAc,CAAC;AACzB,SAAO,EAAE,SAAS,UAAU;AAC9B;AAEO,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA6B;AAC3B,QAAM,CAAC,QAAQ,SAAS,IAAIE,UAAsB,CAAC,CAAC;AACpD,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAS,KAAK;AAC9C,QAAM,CAAC,YAAY,aAAa,IAAIA,UAAqD,IAAI;AAG7F,QAAM,CAAC,aAAa,cAAc,IAAIA,UAA2D,IAAI;AACrG,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,KAAK;AACpD,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAwB,IAAI;AACtD,QAAM,oBAAoBD,QAAO,KAAK;AACtC,QAAM,EAAE,SAAS,UAAU,IAAI,iBAAiB,YAAY,MAAM,cAAc;AAEhF,QAAM,SAAgC,4BAA4B,YAAY,MAAM,IAChF,YAAY,SACZ,eAAe,YAAY;AAC/B,QAAM,gBAAgB,uBAAuB,MAAM,KAAK,gBAAgB;AACxE,QAAM,eAAe,YAAY,iBAAiB,CAAC;AACnD,QAAM,WAAW,CAAC,YAAY,WAAW,aAAa,eAAe;AAKrE,QAAM,cAAcG,SAAQ,MAAM,gBAAgB,YAAY,QAAQ,MAAM,GAAG,CAAC,YAAY,QAAQ,MAAM,CAAC;AAG3G,QAAM,aAAaA,SAAQ,MAAM;AAC/B,UAAM,OAAwB,CAAC;AAC/B,eAAW,SAAS,YAAY,QAAQ;AACtC,YAAM,SAAS,YAAY,OAAO,MAAM;AACxC,UAAI,WAAW,KAAM,MAAK,MAAM,IAAI,IAAI;AAAA,IAC1C;AACA,WAAO;AAAA,EACT,GAAG,CAAC,YAAY,QAAQ,MAAM,CAAC;AAE/B,iBAAe,OAAO,SAAkC;AACtD,UAAM,OAAO,YAAY,aAAa,cAAc;AACpD,QAAI,kBAAkB,WAAW,YAAY,SAAS,KAAM;AAC5D,sBAAkB,UAAU;AAC5B,kBAAc,YAAY,aAAa,YAAY,QAAQ;AAC3D,aAAS,IAAI;AACb,QAAI;AACF,YAAM,SAAS,MAAM,aAAa,EAAE,IAAI,YAAY,IAAI,SAAS,KAAK,CAAC;AACvE,UAAI,OAAO,IAAI;AACb,cAAM,WAAW,YAAY,aAAa,aAAa;AACvD,uBAAe,QAAQ;AACvB,qBAAa,YAAY,IAAI,QAAQ;AACrC;AAAA,MACF;AACA,UAAI,OAAO,SAAS;AAClB,uBAAe,SAAS;AACxB,qBAAa,YAAY,IAAI,SAAS;AACtC;AAAA,MACF;AACA,eAAS,OAAO,OAAO;AAAA,IACzB,UAAE;AACA,wBAAkB,UAAU;AAC5B,oBAAc,IAAI;AAAA,IACpB;AAAA,EACF;AAEA,iBAAe,sBAAsB;AACnC,QAAI,kBAAkB,WAAW,CAAC,gBAAgB,CAAC,YAAa;AAChE,sBAAkB,UAAU;AAC5B,kBAAc,YAAY;AAC1B,aAAS,IAAI;AACb,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,YAAY,WAAW;AAAA,IAC1C,QAAQ;AACN,iBAAW;AAAA,IACb,UAAE;AACA,wBAAkB,UAAU;AAC5B,oBAAc,IAAI;AAAA,IACpB;AACA,QAAI,aAAa,OAAO;AACtB,eAAS,yCAAyC;AAClD;AAAA,IACF;AACA,mBAAe,IAAI;AAAA,EACrB;AAEA,QAAM,eAAeL,gBAAe,MAAM;AAC1C,QAAM,WAAW,WAAW;AAG5B,QAAM,kBAAkB,YAAY,UAChC,uBAAuB,YAAY,QAAQ,YAAY,OAAO,IAC9D;AAEJ;AAAA;AAAA;AAAA;AAAA;AAAA,IAKE,gBAAAH,MAAC,SAAI,WAAW,+DAA+D,aAAa,EAAE,IAC5F;AAAA,sBAAAA,MAAC,SAAI,WAAU,0CACb;AAAA,wBAAAD,KAAC,oBAAiB,SAAQ,WAAU,kBAAI;AAAA,QACxC,gBAAAA,KAAC,oBAAiB,SAAS,WAAW,YAAY,WAAW,aAAa,WAAW,aAAa,gBAAgB,WAC/G,UAAAG,eAAc,MAAM,GACvB;AAAA,SACF;AAAA,MAEC,YAAY,MAAM,KAAK,KACtB,gBAAAH,KAAC,OAAE,WAAU,+DAA+D,sBAAY,OAAM;AAAA,MAG/F,YAAY,QACX,gBAAAC,MAAC,SAAI,WAAU,YACb;AAAA,wBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,KAAK;AAAA,YACL,WAAU;AAAA,YACV,OAAO,YAAY,CAAC,YAAY,SAAY,EAAE,WAAWK,sBAAqB;AAAA,YAE7E,2BACG,eAAe,YAAY,IAAI,IAC/B,gBAAAL,KAAC,OAAE,WAAU,iCAAiC,sBAAY,MAAK;AAAA;AAAA,QACrE;AAAA,QACC,aAAa,CAAC,YACb,gBAAAA,KAAC,SAAI,WAAU,kGAAiG;AAAA,QAEjH,aACC,gBAAAC;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS,MAAM,YAAY,CAAC,SAAS,CAAC,IAAI;AAAA,YAC1C,WAAU;AAAA,YAEV;AAAA,8BAAAD,KAAC,oBAAiB,WAAW,gCAAgC,WAAW,eAAe,EAAE,IAAI;AAAA,cAC5F,WAAW,kBAAkB;AAAA;AAAA;AAAA,QAChC;AAAA,SAEJ;AAAA,MAGD,YAAY,OAAO,SAAS,KAAK,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAe3C,gBAAAA,KAAC,SAAI,WAAU,kBACZ,sBAAY,OAAO,IAAI,CAAC,UACvB,gBAAAC,MAAC,cAA0B,WAAU,aACnC;AAAA,wBAAAD,KAAC,OAAE,WAAU,iDAAiD,gBAAM,OAAM;AAAA,QACzE,qBAAqB,KAAK,IACzB,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO,OAAO,MAAM,IAAI,GAAG,QAAQ;AAAA,YACnC;AAAA,YACA,cAAY,MAAM;AAAA,YAClB,UAAU,CAAC,UACT,UAAU,CAAC,UAAU,EAAE,GAAG,MAAM,CAAC,MAAM,IAAI,GAAG,EAAE,GAAG,KAAK,MAAM,IAAI,GAAG,MAAM,MAAM,OAAO,MAAM,EAAE,EAAE;AAAA,YACpG,MAAM;AAAA,YACN,aAAa,MAAM,SAAS,SAAS,MAAM,eAAe,oCAAoC;AAAA,YAC9F,WAAU;AAAA;AAAA,QACZ,IAEA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,OAAO,OAAO,MAAM,IAAI,GAAG,QAAQ;AAAA,YACnC;AAAA,YACA,cAAY,MAAM;AAAA,YAClB,UAAU,CAAC,UACT,UAAU,CAAC,UAAU,EAAE,GAAG,MAAM,CAAC,MAAM,IAAI,GAAG,EAAE,GAAG,KAAK,MAAM,IAAI,GAAG,MAAM,MAAM,OAAO,MAAM,EAAE,EAAE;AAAA,YACpG,WAAU;AAAA;AAAA,QACZ;AAAA,WAtBW,MAAM,IAwBrB,CACD,GACH;AAAA,MAGD,YAAY,OAAO,SAAS,KAAK,WAAW,aAC3C,gBAAAA,KAAC,SAAI,WAAU,kBACZ,sBAAY,OAAO,IAAI,CAAC,UAAU;AACjC,cAAM,OAAO,mBAAmB,OAAO,eAAe;AACtD,YAAI,CAAC,KAAM,QAAO;AAClB,eACE,gBAAAC,MAAC,SACC;AAAA,0BAAAD,KAAC,OAAE,WAAU,6CAA6C,gBAAM,OAAM;AAAA,UACtE,gBAAAA,KAAC,OAAE,WAAU,sDAAsD,gBAAK;AAAA,aAFhE,MAAM,IAGhB;AAAA,MAEJ,CAAC,GACH;AAAA,MAGD,SAAS,gBAAAA,KAAC,OAAE,WAAU,iCAAiC,iBAAM;AAAA,MAC7D,gBAAgB,gBAAAA,KAAC,OAAE,WAAU,sCAAsC,wBAAa;AAAA,MAEhF,gBACC,gBAAAA,KAAC,SAAI,WAAU,sCACb,0BAAAA,KAAC,2BAAwB,SAAQ,WAAU,SAAS,MAAM,KAAK,oBAAoB,GAAG,UAAU,eAAe,MAC5G,yBAAe,eAAe,iBAAY,kBAAkB,0BAC/D,GACF;AAAA,MAED,eACC,gBAAAA,KAAC,SAAI,WAAU,sCACb,0BAAAC,MAAC,UAAK,WAAU,gEAA+D;AAAA,wBAAAD,KAACE,aAAA,EAAW,WAAU,WAAU;AAAA,QAAE;AAAA,SAAuB,GAC1I;AAAA,MAGD,WAAW,aACV,gBAAAD,MAAC,SAAI,WAAU,4CACb;AAAA,wBAAAD,KAAC,2BAAwB,SAAQ,WAAU,SAAS,MAAM,KAAK,OAAO,UAAU,GAAG,UAChF,yBAAe,WAAW,kBAAa,mBAC1C;AAAA,QACA,gBAAAA,KAAC,2BAAwB,SAAS,MAAM,KAAK,OAAO,UAAU,GAAG,UAAU,YAAY,gBAAgB,MACpG,yBAAe,YAAY,oBAAe,gBAC7C;AAAA,SACF;AAAA,MAED,YACC,gBAAAA,KAAC,SAAI,WAAU,sCACb,0BAAAC,MAAC,UAAK,WAAU,gEAA+D;AAAA,wBAAAD,KAACE,aAAA,EAAW,WAAU,WAAU;AAAA,QAAE;AAAA,SAAQ,GAC3H;AAAA,OAEJ;AAAA;AAEJ;;;ACrQY,gBAAAQ,YAAA;AAzEZ,SAAS,aAAa,QAAgB,UAA0B;AAC9D,SAAO,GAAG,MAAM,IAAI,QAAQ;AAC9B;AAKO,SAAS,0BAA0B,OAA0D;AAClG,QAAM,eAAe,oBAAI,IAAY;AACrC,aAAW,QAAQ,OAAO;AACxB,UAAM,OAAO,oBAAoB,IAAI;AACrC,QAAI,KAAM,cAAa,IAAI,aAAa,KAAK,QAAQ,KAAK,QAAQ,CAAC;AAAA,EACrE;AACA,QAAM,QAA2B,CAAC;AAClC,aAAW,QAAQ,OAAO;AACxB,UAAM,OAAO,oBAAoB,IAAI;AACrC,QAAI,MAAM;AACR,YAAM,KAAK,EAAE,MAAM,QAAQ,KAAK,QAAQ,aAAa,KAAK,QAAQ,KAAK,QAAQ,CAAC,IAAI,KAAK,CAAC;AAC1F;AAAA,IACF;AACA,UAAM,cAAc,2BAA2B,IAAI;AACnD,QAAI,CAAC,YAAa;AAClB,UAAM,iBAAiB,YAAY,SAAS,UAAU,OAAO,KAAK,WAAW,YAC3E,OAAO,KAAK,aAAa,YAAY,aAAa,IAAI,aAAa,KAAK,QAAQ,KAAK,QAAQ,CAAC;AAChG,QAAI,eAAgB;AACpB,UAAM,KAAK,EAAE,MAAM,eAAe,KAAK,eAAe,YAAY,EAAE,IAAI,YAAY,CAAC;AAAA,EACvF;AACA,SAAO;AACT;AAwBO,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA0B;AACxB,QAAM,QAAQ,0BAA0B,KAAK;AAC7C,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,SACE,gBAAAA,KAAC,SAAI,WAAW,aAAa,aAAa,EAAE,IACzC,gBAAM,IAAI,CAAC,SAAS;AACnB,QAAI,KAAK,SAAS,QAAQ;AACxB,aACE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UAEC,MAAM,KAAK;AAAA,UACX;AAAA,UACA,QAAQ,CAAC,UAAU,aAAa,WAAW,KAAK,MAAM,UAAU,QAAQ;AAAA,UACxE,UAAU,eAAe,KAAK,IAAI;AAAA,UAClC,OAAO,YAAY,KAAK,IAAI;AAAA,UAC5B;AAAA;AAAA,QANK,KAAK;AAAA,MAOZ;AAAA,IAEJ;AACA,QAAI,KAAK,YAAY,SAAS,QAAQ;AACpC,aACE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UAEC,aAAa,KAAK;AAAA,UAClB;AAAA,UACA,cAAc;AAAA,UACd,YAAY;AAAA,UACZ;AAAA,UACA;AAAA,UACA;AAAA;AAAA,QAPK,KAAK;AAAA,MAQZ;AAAA,IAEJ;AACA,WACE,gBAAAA;AAAA,MAAC;AAAA;AAAA,QAEC,aAAa,KAAK;AAAA,QAClB;AAAA,QACA,cAAc;AAAA,QACd,YAAY;AAAA,QACZ;AAAA;AAAA,MALK,KAAK;AAAA,IAMZ;AAAA,EAEJ,CAAC,GACH;AAEJ;;;AC9GA,SAAS,aAAa,aAAAC,YAAW,YAAAC,iBAAgC;AAO7D,SACE,OAAAC,MADF,QAAAC,aAAA;AAFJ,SAAS,UAAU,EAAE,UAAU,GAA2B;AACxD,SACE,gBAAAA,MAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACvJ;AAAA,oBAAAD,KAAC,UAAK,GAAE,8DAA6D;AAAA,IACrE,gBAAAA,KAAC,UAAK,GAAE,aAAY;AAAA,KACtB;AAEJ;AAEA,SAAS,WAAW,EAAE,UAAU,GAA2B;AACzD,SACE,gBAAAC,MAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACvJ;AAAA,oBAAAD,KAAC,UAAK,GAAE,KAAI,GAAE,KAAI,OAAM,MAAK,QAAO,MAAK,IAAG,KAAI;AAAA,IAChD,gBAAAA,KAAC,YAAO,IAAG,KAAI,IAAG,KAAI,GAAE,KAAI;AAAA,IAC5B,gBAAAA,KAAC,UAAK,GAAE,mBAAkB;AAAA,KAC5B;AAEJ;AAEA,SAAS,aAAa,EAAE,UAAU,GAA2B;AAC3D,SACE,gBAAAA,KAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACvJ,0BAAAA,KAAC,UAAK,GAAE,2GAA0G,GACpH;AAEJ;AAEA,SAAS,iBAAiB,WAA6E;AACrG,SAAO,WAAW,WAAW,QAAQ,IAAI,aAAa;AACxD;AAMA,SAAS,mBAAmB,OAAuB;AACjD,MAAI,QAAQ,KAAM,QAAO,GAAG,KAAK;AACjC,MAAI,QAAQ,OAAO,KAAM,QAAO,GAAG,KAAK,MAAM,QAAQ,IAAI,CAAC;AAC3D,QAAM,KAAK,SAAS,OAAO;AAC3B,SAAO,GAAG,MAAM,KAAK,KAAK,MAAM,EAAE,IAAI,GAAG,QAAQ,CAAC,CAAC;AACrD;AAWA,SAAS,sBAAsB,MAAyC;AACtE,MAAI,OAAO,KAAK,SAAS,YAAY,KAAK,KAAK,KAAK,EAAE,SAAS,EAAG,QAAO,KAAK;AAC9E,QAAM,OAAO,KAAK,KAAK,MAAM,GAAG,EAAE,IAAI,KAAK;AAC3C,QAAM,UAAU,KAAK,KAAK;AAC1B,SAAO,QAAQ,SAAS,IAAI,UAAU;AACxC;AAaA,IAAM,sBAAsB,oBAAI,IAA2C;AAEpE,SAAS,qCAA2C;AACzD,sBAAoB,MAAM;AAC5B;AAEA,eAAe,iBAAiB,KAAgC;AAC9D,SAAO,MAAM,KAAK,EAAE,aAAa,cAAc,CAAC;AAClD;AAEA,eAAe,oBACb,KACA,WAC+B;AAC/B,MAAI;AACF,UAAM,MAAM,MAAM,UAAU,GAAG;AAC/B,QAAI,CAAC,IAAI,GAAI,QAAO,EAAE,IAAI,OAAO,SAAS,8BAA8B,IAAI,MAAM,IAAI;AACtF,WAAO,EAAE,IAAI,MAAM,MAAM,MAAM,IAAI,KAAK,EAAE;AAAA,EAC5C,SAAS,KAAK;AACZ,WAAO,EAAE,IAAI,OAAO,SAAS,eAAe,SAAS,IAAI,UAAU,IAAI,UAAU,mCAAmC;AAAA,EACtH;AACF;AAMO,SAAS,mBACd,KACA,YAAgD,kBACjB;AAC/B,QAAM,SAAS,oBAAoB,IAAI,GAAG;AAC1C,MAAI,OAAQ,QAAO;AACnB,QAAM,UAAU,oBAAoB,KAAK,SAAS;AAClD,sBAAoB,IAAI,KAAK,OAAO;AACpC,OAAK,QAAQ,KAAK,CAAC,WAAW;AAC5B,QAAI,CAAC,OAAO,MAAM,oBAAoB,IAAI,GAAG,MAAM,QAAS,qBAAoB,OAAO,GAAG;AAAA,EAC5F,CAAC;AACD,SAAO;AACT;AAKO,SAAS,0BAA0B,MAAc,MAA2D;AACjH,MAAI;AACF,UAAM,MAAM,IAAI,gBAAgB,IAAI;AACpC,UAAM,OAAO,SAAS,cAAc,GAAG;AACvC,SAAK,OAAO;AACZ,SAAK,WAAW;AAChB,aAAS,KAAK,YAAY,IAAI;AAC9B,SAAK,MAAM;AACX,SAAK,OAAO;AACZ,QAAI,gBAAgB,GAAG;AACvB,WAAO,EAAE,IAAI,KAAK;AAAA,EACpB,SAAS,KAAK;AACZ,WAAO,EAAE,IAAI,OAAO,SAAS,eAAe,SAAS,IAAI,UAAU,IAAI,UAAU,gCAAgC;AAAA,EACnH;AACF;AAIA,SAAS,uBAAuB,MAAuC;AACrE,QAAM,CAAC,KAAK,MAAM,IAAID,UAAwB,IAAI;AAClD,EAAAD,WAAU,MAAM;AACd,QAAI,CAAC,MAAM;AACT,aAAO,IAAI;AACX;AAAA,IACF;AACA,UAAM,YAAY,IAAI,gBAAgB,IAAI;AAC1C,WAAO,SAAS;AAChB,WAAO,MAAM,IAAI,gBAAgB,SAAS;AAAA,EAC5C,GAAG,CAAC,IAAI,CAAC;AACT,SAAO;AACT;AAIA,SAAS,yBAAyB,EAAE,KAAK,GAAqB;AAC5D,SACE,gBAAAG,MAAC,UAAK,WAAU,yKACd;AAAA,oBAAAD,KAAC,gBAAa,WAAU,oBAAmB;AAAA,IAC3C,gBAAAA,KAAC,UAAK,WAAU,sCAAsC,gBAAK;AAAA,KAC7D;AAEJ;AAKA,SAAS,sBAAsB,EAAE,MAAM,GAAoC;AACzE,MAAI,UAAU,aAAa;AACzB,WACE,gBAAAC;AAAA,MAAC;AAAA;AAAA,QACC,iBAAc;AAAA,QACd,WAAU;AAAA,QAEV;AAAA,0BAAAD,KAAC,gBAAa,WAAU,oBAAmB;AAAA,UAC3C,gBAAAA,KAAC,UAAK,WAAU,sCAAqC,oCAAsB;AAAA;AAAA;AAAA,IAC7E;AAAA,EAEJ;AACA,SACE,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC,iBAAc;AAAA,MACd,WAAU;AAAA,MAEV;AAAA,wBAAAD,KAAC,gBAAa,WAAU,oBAAmB;AAAA,QAAE;AAAA;AAAA;AAAA,EAE/C;AAEJ;AAQA,SAAS,oBAAoB,EAAE,MAAM,gBAAgB,UAAU,GAAwB;AACrF,QAAM,MAAM,eAAe,IAAI;AAC/B,QAAM,cAAc,sBAAsB,IAAI;AAC9C,QAAM,CAAC,QAAQ,SAAS,IAAID,UAAsC,IAAI;AAMtE,EAAAD,WAAU,MAAM;AACd,QAAI,CAAC,YAAa;AAClB,QAAI,YAAY;AAChB,cAAU,IAAI;AACd,uBAAmB,KAAK,SAAS,EAAE,KAAK,CAAC,SAAS;AAChD,UAAI,CAAC,UAAW,WAAU,IAAI;AAAA,IAChC,CAAC;AACD,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,KAAK,WAAW,WAAW,CAAC;AAEhC,QAAM,YAAY,uBAAuB,QAAQ,KAAK,OAAO,OAAO,MAAS;AAE7E,QAAM,cAAc,YAAY,MAAM;AACpC,QAAI,CAAC,UAAW;AAChB,WAAO,KAAK,WAAW,UAAU,UAAU;AAAA,EAC7C,GAAG,CAAC,SAAS,CAAC;AAEd,MAAI,CAAC,aAAa;AAChB,WAAO,gBAAAE,KAAC,yBAAsB,OAAM,aAAY;AAAA,EAClD;AACA,MAAI,CAAC,QAAQ;AAKX,WAAO,gBAAAA,KAAC,UAAK,eAAY,QAAO,WAAU,qEAAoE;AAAA,EAChH;AACA,MAAI,CAAC,OAAO,MAAM,CAAC,WAAW;AAC5B,WAAO,gBAAAA,KAAC,4BAAyB,MAAM,aAAa;AAAA,EACtD;AAEA,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,SAAS;AAAA,MACT,cAAY,QAAQ,WAAW;AAAA,MAC/B,WAAU;AAAA,MAEV,0BAAAA,KAAC,SAAI,KAAK,WAAW,KAAK,aAAa,WAAU,0BAAyB;AAAA;AAAA,EAC5E;AAEJ;AAIA,SAAS,eAAe,EAAE,MAAM,gBAAgB,UAAU,GAAwB;AAChF,QAAM,cAAc,sBAAsB,IAAI;AAC9C,QAAM,CAAC,QAAQ,SAAS,IAAID,UAAuC,MAAM;AACzE,QAAM,CAAC,cAAc,eAAe,IAAIA,UAAwB,IAAI;AAIpE,QAAM,cAAc,YAAY,MAAM;AACpC,QAAI,CAAC,eAAe,WAAW,UAAW;AAC1C,cAAU,SAAS;AACnB,oBAAgB,IAAI;AACpB,UAAM,MAAM,eAAe,IAAI;AAC/B,SAAK,mBAAmB,KAAK,SAAS,EAAE,KAAK,CAAC,WAAW;AACvD,UAAI,CAAC,OAAO,IAAI;AACd,kBAAU,OAAO;AACjB,wBAAgB,OAAO,OAAO;AAC9B;AAAA,MACF;AACA,YAAM,WAAW,0BAA0B,aAAa,OAAO,IAAI;AACnE,UAAI,CAAC,SAAS,IAAI;AAChB,kBAAU,OAAO;AACjB,wBAAgB,SAAS,OAAO;AAChC;AAAA,MACF;AACA,gBAAU,MAAM;AAAA,IAClB,CAAC;AAAA,EACH,GAAG,CAAC,aAAa,QAAQ,gBAAgB,MAAM,SAAS,CAAC;AAEzD,MAAI,CAAC,aAAa;AAChB,WAAO,gBAAAC,KAAC,yBAAsB,OAAM,QAAO;AAAA,EAC7C;AAEA,QAAM,OAAO,WAAW,UAAU,eAAe,iBAAiB,KAAK,SAAS;AAChF,QAAM,YAAY;AAAA,IAChB;AAAA,IACA,WAAW,UACP,6DACA;AAAA,EACN,EAAE,KAAK,GAAG;AAEV,SACE,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,SAAS;AAAA,MACT,OAAO,WAAW,UAAU,gBAAgB,SAAY;AAAA,MACxD;AAAA,MAEA;AAAA,wBAAAD,KAAC,QAAK,WAAU,oBAAmB;AAAA,QAClC;AAAA,QACA,OAAO,KAAK,SAAS,YAAY,gBAAAC,MAAC,UAAK,WAAU,4BAA2B;AAAA;AAAA,UAAG,mBAAmB,KAAK,IAAI;AAAA,WAAE;AAAA;AAAA;AAAA,EAChH;AAEJ;AAoBO,SAAS,mBAAmB,EAAE,OAAO,gBAAgB,UAAU,OAAO,UAAU,GAAuC;AAC5H,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,SACE,gBAAAD,KAAC,SAAI,WAAW,0BAA0B,YAAY,UAAU,kBAAkB,aAAa,IAC5F,gBAAM;AAAA,IAAI,CAAC,SACV,KAAK,SAAS,UACZ,gBAAAA,KAAC,uBAAsD,MAAY,gBAAgC,aAAzE,GAAG,KAAK,IAAI,IAAI,KAAK,IAAI,EAAsE,IAEzH,gBAAAA,KAAC,kBAAiD,MAAY,gBAAgC,aAAzE,GAAG,KAAK,IAAI,IAAI,KAAK,IAAI,EAAsE;AAAA,EAExH,GACF;AAEJ;;;ACnOO,SAAS,uBAAuB,MAAc,IAGnD;AACA,MAAI,kBAAkB;AACtB,MAAI;AACJ,MAAI,CAAC,KAAK,KAAK,EAAG,QAAO,EAAE,gBAAgB;AAC3C,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,IAAI;AAAA,EAC1B,QAAQ;AACN,WAAO,EAAE,gBAAgB;AAAA,EAC3B;AAEA,MAAI,OAAO,SAAS,eAAe;AACjC,OAAG,eAAe;AAAA,MAChB,YAAY,OAAO;AAAA,MACnB,UAAU,OAAO;AAAA,MACjB,OAAO,OAAO;AAAA,MACd,SAAU,OAAO,WAAW,OAAO;AAAA,IACrC,CAAC;AACD,WAAO,EAAE,iBAAiB,KAAK;AAAA,EACjC;AAEA,QAAM,MAAO,OAAO,SAAS,UAAU,OAAO,QAAQ;AACtD,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO,EAAE,gBAAgB;AAE9D,UAAQ,IAAI,MAAM;AAAA,IAChB,KAAK;AACH,UAAI,OAAO,IAAI,WAAW,SAAU,UAAS,IAAI;AACjD;AAAA,IACF,KAAK;AACH,UAAI,OAAO,IAAI,SAAS,UAAU;AAChC,WAAG,SAAS,IAAI,IAAI;AACpB,0BAAkB;AAAA,MACpB;AACA;AAAA,IACF,KAAK;AACH,UAAI,OAAO,IAAI,SAAS,UAAU;AAChC,WAAG,cAAc,IAAI,IAAI;AACzB,0BAAkB;AAAA,MACpB;AACA;AAAA,IACF,KAAK,aAAa;AAChB,YAAM,OAAQ,IAAI,QAAQ;AAC1B,SAAG,aAAa;AAAA,QACd,YAAa,KAAK,cAAc,KAAK;AAAA,QACrC,UAAU,OAAO,KAAK,YAAY,KAAK,QAAQ,SAAS;AAAA,QACxD,MAAO,KAAK,QAAQ,CAAC;AAAA,MACvB,CAAC;AACD,wBAAkB;AAClB;AAAA,IACF;AAAA,IACA,KAAK;AACH,SAAG,eAAe;AAAA,QAChB,YAAY,IAAI;AAAA,QAChB,UAAU,IAAI;AAAA,QACd,OAAO,IAAI;AAAA,QACX,SAAU,IAAI,WAAW,IAAI;AAAA,MAC/B,CAAC;AACD,wBAAkB;AAClB;AAAA,IACF,KAAK,SAAS;AACZ,YAAM,IAAI,IAAI;AACd,UAAI,EAAG,IAAG,UAAU,EAAE,cAAc,EAAE,gBAAgB,GAAG,kBAAkB,EAAE,oBAAoB,EAAE,CAAC;AACpG;AAAA,IACF;AAAA,IACA,KAAK,UAAU;AACb,UACE,OAAO,IAAI,OAAO,aACjB,IAAI,eAAe,aAAa,IAAI,eAAe,oBACpD,OAAO,IAAI,SAAS,UACpB;AACA,WAAG,WAAW,EAAE,IAAI,IAAI,IAAI,YAAY,IAAI,YAAY,MAAM,IAAI,KAAK,CAAC;AACxE,0BAAkB;AAAA,MACpB;AACA;AAAA,IACF;AAAA,IACA,KAAK;AACH,SAAG,aAAc,IAAI,QAAQ,CAAC,CAA6B;AAC3D;AAAA,IACF,KAAK,eAAe;AAKlB,YAAME,UAAS,wBAAwB,IAAI,IAA2C;AACtF,UAAIA,QAAO,WAAW;AACpB,WAAG,gBAAgB,2BAA2BA,QAAO,KAAK,CAAC;AAC3D,0BAAkB;AAAA,MACpB,OAAO;AACL,gBAAQ,MAAM,sDAAsDA,QAAO,KAAK;AAAA,MAClF;AACA;AAAA,IACF;AAAA,IACA,KAAK,sBAAsB;AACzB,YAAM,YAAY,uBAAuB,IAAI,IAA2C;AACxF,UAAI,UAAU,WAAW;AACvB,WAAG,sBAAsB,UAAU,KAAK;AACxC,0BAAkB;AAAA,MACpB,OAAO;AACL,gBAAQ,MAAM,6DAA6D,UAAU,KAAK;AAAA,MAC5F;AACA;AAAA,IACF;AAAA,IACA,KAAK,SAAS;AAKZ,YAAM,OAAO,IAAI;AAKjB,YAAM,UAAU,OAAO,MAAM,WAAW,IAAI,WAAW,IAAI,SAAS,sBAAsB;AAC1F,SAAG,qBAAqB;AAAA,QACtB;AAAA,QACA,GAAI,OAAO,MAAM,SAAS,WAAW,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC;AAAA,QAC5D,GAAI,MAAM,WAAW,OAAO,KAAK,YAAY,WAAW,EAAE,SAAS,KAAK,QAAQ,IAAI,CAAC;AAAA,MACvF,CAAC;AACD,UAAI,GAAG,cAAc;AACnB,WAAG,aAAa,OAAO;AAAA,MACzB,OAAO;AAML,gBAAQ,MAAM,+CAA+C,OAAO;AACpE,WAAG,SAAS;AAAA;AAAA,gDAAqD,OAAO,EAAE;AAC1E,0BAAkB;AAAA,MACpB;AACA;AAAA,IACF;AAAA,IACA,SAAS;AACP,UAAI,OAAO,IAAI,SAAS,YAAY,IAAI,KAAK,WAAW,OAAO,GAAG;AAChE,cAAM,YAAY,wBAAwB,GAAG;AAC7C,cAAM,aACH,IAAI,MAA8C,QAClD,IAAI,YAAoD;AAE3D,cAAM,OAAO,UAAU,YACnB,UAAU,QACV,aAAa,oBAAoB,EAAE,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI;AACxE,YAAI,MAAM;AACR,aAAG,SAAS,IAAI;AAChB,4BAAkB;AAAA,QACpB,OAAO;AACL,kBAAQ,MAAM,uDAAuD,IAAI,IAAI;AAAA,QAC/E;AAAA,MACF;AACA;AAAA,IACF;AAAA,EACF;AACA,SAAO,EAAE,QAAQ,gBAAgB;AACnC;AAIA,eAAsB,kBACpB,MACA,IACkC;AAClC,QAAM,SAAS,KAAK,UAAU;AAC9B,QAAM,UAAU,IAAI,YAAY;AAChC,MAAI,SAAS;AACb,MAAI,SAAwB;AAC5B,MAAI,kBAAkB;AAEtB,QAAM,SAAS,CAAC,SAAiB;AAC/B,UAAM,IAAI,uBAAuB,MAAM,EAAE;AACzC,QAAI,EAAE,QAAQ;AACZ,eAAS,EAAE;AACX,SAAG,WAAW,EAAE,MAAM;AAAA,IACxB;AACA,QAAI,EAAE,gBAAiB,mBAAkB;AAAA,EAC3C;AAEA,aAAS;AACP,UAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,QAAI,MAAM;AACR,UAAI,OAAO,KAAK,EAAG,QAAO,MAAM;AAChC;AAAA,IACF;AACA,cAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAChD,UAAM,QAAQ,OAAO,MAAM,IAAI;AAC/B,aAAS,MAAM,IAAI,KAAK;AACxB,eAAW,QAAQ,MAAO,QAAO,IAAI;AAAA,EACvC;AACA,SAAO,EAAE,QAAQ,gBAAgB;AACnC;AAmBA,eAAsB,eAAe,MAA2D;AAC9F,QAAM,MAAM,MAAM,KAAK,MAAM;AAC7B,MAAI,CAAC,IAAI,MAAM,CAAC,IAAI,MAAM;AACxB,UAAM,MAAM,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,EAAE,OAAO,QAAQ,IAAI,MAAM,GAAG,EAAE;AAC1E,UAAM,IAAI,MAAM,IAAI,SAAS,QAAQ,IAAI,MAAM,EAAE;AAAA,EACnD;AACA,MAAI,SAAwB;AAC5B,QAAM,KAA0B;AAAA,IAC9B,GAAG,KAAK;AAAA,IACR,UAAU,CAAC,OAAO;AAChB,eAAS;AACT,WAAK,UAAU,WAAW,EAAE;AAAA,IAC9B;AAAA,EACF;AACA,MAAI;AACF,WAAO,MAAM,kBAAkB,IAAI,MAAM,EAAE;AAAA,EAC7C,SAAS,cAAc;AACrB,QAAI,CAAC,UAAU,CAAC,KAAK,OAAQ,OAAM;AACnC,SAAK,mBAAmB;AACxB,UAAM,UAAU,MAAM,KAAK,OAAO,QAAQ,CAAC;AAC3C,QAAI,CAAC,QAAQ,MAAM,CAAC,QAAQ,KAAM,OAAM;AACxC,WAAO,MAAM,kBAAkB,QAAQ,MAAM,EAAE;AAAA,EACjD;AACF;;;AC/TA;AAAA,EACE,eAAAC;AAAA,EACA,aAAAC;AAAA,EACA,WAAAC;AAAA,EACA;AAAA,EACA,UAAAC;AAAA,EACA,YAAAC;AAAA,OAMK;;;ACdP,SAAS,eAAAC,cAAa,aAAAC,YAAW,UAAAC,SAAQ,YAAAC,iBAAgB;AAiCzD,IAAM,uBAAuB,CAAC,0BAA0B,cAAc,WAAW;AAG1E,SAAS,wBAA4C;AAC1D,MAAI,OAAO,kBAAkB,eAAe,OAAO,cAAc,oBAAoB,YAAY;AAC/F,WAAO;AAAA,EACT;AACA,aAAW,QAAQ,sBAAsB;AACvC,QAAI,cAAc,gBAAgB,IAAI,EAAG,QAAO;AAAA,EAClD;AACA,SAAO;AACT;AAGA,SAAS,yBAAkC;AACzC,SACE,OAAO,cAAc,eACrB,OAAO,UAAU,cAAc,iBAAiB,cAChD,OAAO,kBAAkB;AAE7B;AAIO,SAAS,sBAAsB,OAAwB;AAC5D,MAAI,iBAAiB,cAAc;AACjC,QAAI,MAAM,SAAS,kBAAmB,QAAO;AAC7C,QAAI,MAAM,SAAS,gBAAiB,QAAO;AAAA,EAC7C;AACA,SAAO;AACT;AAGO,SAAS,uBAAuB,cAA8B;AACnE,QAAM,OAAO,OAAO,SAAS,YAAY,KAAK,eAAe,IAAI,KAAK,MAAM,YAAY,IAAI;AAC5F,QAAM,UAAU,KAAK,MAAM,OAAO,EAAE;AACpC,QAAM,UAAU,OAAO;AACvB,SAAO,GAAG,OAAO,IAAI,OAAO,OAAO,EAAE,SAAS,GAAG,GAAG,CAAC;AACvD;AAeA,SAAS,cAAc,QAA2B;AAChD,aAAW,SAAS,OAAO,UAAU,EAAG,OAAM,KAAK;AACrD;AAEO,SAAS,aAAa,EAAE,WAAW,QAAQ,GAA2C;AAC3F,QAAM,CAAC,SAAS,IAAIA,UAAS,sBAAsB;AACnD,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,KAAK;AAChD,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAAS,CAAC;AAEtD,QAAM,aAAaD,QAAgC,IAAI;AAEvD,QAAM,wBAAwBA,QAA4B,IAAI;AAI9D,QAAM,eAAeA,QAAO,EAAE,WAAW,QAAQ,CAAC;AAClD,eAAa,UAAU,EAAE,WAAW,QAAQ;AAI5C,EAAAD,WAAU,MAAM;AACd,QAAI,CAAC,UAAW;AAChB,sBAAkB,CAAC;AACnB,UAAM,KAAK,YAAY,MAAM,kBAAkB,CAAC,MAAM,IAAI,CAAC,GAAG,GAAI;AAClE,WAAO,MAAM,cAAc,EAAE;AAAA,EAC/B,GAAG,CAAC,SAAS,CAAC;AAGd,QAAM,WAAWD,aAAY,CAAC,cAAuB;AACnD,UAAM,UAAU,WAAW;AAC3B,QAAI,YAAY,KAAM;AACtB,YAAQ,YAAY,QAAQ,aAAa;AACzC,eAAW,UAAU;AACrB,kBAAc,QAAQ,MAAM;AAC5B,iBAAa,KAAK;AAAA,EACpB,GAAG,CAAC,CAAC;AAEL,QAAM,OAAOA,aAAY,MAAM;AAG7B,0BAAsB,UAAU;AAChC,0BAAsB,UAAU;AAChC,UAAM,UAAU,WAAW;AAC3B,QAAI,YAAY,QAAQ,QAAQ,UAAW;AAG3C,QAAI,QAAQ,SAAS,UAAU,WAAY,SAAQ,SAAS,KAAK;AAAA,EACnE,GAAG,CAAC,CAAC;AAEL,QAAM,QAAQA,aAAY,MAAM;AAC9B,QAAI,CAAC,UAAW;AAChB,QAAI,WAAW,YAAY,QAAQ,sBAAsB,YAAY,KAAM;AAE3E,QAAI,mBAAmB;AACvB,0BAAsB,UAAU,MAAM;AACpC,yBAAmB;AAAA,IACrB;AAEA,cAAU,aAAa,aAAa,EAAE,OAAO,KAAK,CAAC,EAAE;AAAA,MACnD,CAAC,WAAW;AACV,8BAAsB,UAAU;AAChC,YAAI,kBAAkB;AACpB,wBAAc,MAAM;AACpB;AAAA,QACF;AACA,cAAM,WAAW,sBAAsB;AACvC,cAAM,WAAW,IAAI,cAAc,QAAQ,aAAa,SAAY,SAAY,EAAE,SAAS,CAAC;AAC5F,cAAM,UAA4B;AAAA,UAChC;AAAA,UACA;AAAA,UACA,QAAQ,CAAC;AAAA,UACT,UAAU,SAAS,YAAY,YAAY;AAAA,UAC3C,WAAW,KAAK,IAAI;AAAA,UACpB,WAAW;AAAA,QACb;AACA,mBAAW,UAAU;AAErB,iBAAS,kBAAkB,CAAC,UAAU;AACpC,cAAI,MAAM,KAAK,OAAO,EAAG,SAAQ,OAAO,KAAK,MAAM,IAAI;AAAA,QACzD;AAEA,iBAAS,SAAS,MAAM;AACtB,mBAAS,QAAQ,SAAS;AAC1B,cAAI,QAAQ,UAAW;AACvB,gBAAM,OAAO,IAAI,KAAK,QAAQ,QAAQ,EAAE,MAAM,QAAQ,SAAS,CAAC;AAChE,cAAI,KAAK,SAAS,GAAG;AAGnB,yBAAa,QAAQ,UAAU,uBAAuB;AACtD;AAAA,UACF;AACA,gBAAM,kBAAkB,KAAK,IAAI,GAAG,KAAK,OAAO,KAAK,IAAI,IAAI,QAAQ,aAAa,GAAI,CAAC;AACvF,uBAAa,QAAQ,UAAU,EAAE,MAAM,UAAU,QAAQ,UAAU,gBAAgB,CAAC;AAAA,QACtF;AAEA,iBAAS,UAAU,MAAM;AAGvB,mBAAS,IAAI;AACb,uBAAa,QAAQ,UAAU,iCAAiC;AAAA,QAClE;AAEA,iBAAS,MAAM;AACf,qBAAa,IAAI;AAAA,MACnB;AAAA,MACA,CAAC,UAAmB;AAClB,8BAAsB,UAAU;AAChC,YAAI,iBAAkB;AACtB,qBAAa,QAAQ,UAAU,sBAAsB,KAAK,CAAC;AAAA,MAC7D;AAAA,IACF;AAAA,EACF,GAAG,CAAC,WAAW,QAAQ,CAAC;AAKxB,EAAAC;AAAA,IACE,MAAM,MAAM;AACV,4BAAsB,UAAU;AAChC,4BAAsB,UAAU;AAChC,YAAM,UAAU,WAAW;AAC3B,UAAI,YAAY,KAAM;AACtB,cAAQ,YAAY;AACpB,iBAAW,UAAU;AACrB,UAAI;AACF,YAAI,QAAQ,SAAS,UAAU,WAAY,SAAQ,SAAS,KAAK;AAAA,MACnE,UAAE;AACA,sBAAc,QAAQ,MAAM;AAAA,MAC9B;AAAA,IACF;AAAA,IACA,CAAC;AAAA,EACH;AAEA,SAAO,EAAE,WAAW,WAAW,gBAAgB,OAAO,KAAK;AAC7D;;;ADxLM,SAskCM,YAAAG,WAtkCN,OAAAC,MA+BF,QAAAC,aA/BE;AANN,IAAM,oBACJ,OAAO,cAAc,eAAe,wBAAwB,KAAK,UAAU,QAAQ;AAErF,SAAS,UAAU,EAAE,UAAU,GAA2B;AACxD,SACE,gBAAAD,KAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACvJ,0BAAAA,KAAC,UAAK,GAAE,wCAAuC,GACjD;AAEJ;AAEA,SAAS,UAAU,EAAE,UAAU,GAA2B;AACxD,SACE,gBAAAA,KAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,gBAAe,eAAW,MAC5E,0BAAAA,KAAC,UAAK,GAAE,KAAI,GAAE,KAAI,OAAM,MAAK,QAAO,MAAK,IAAG,KAAI,GAClD;AAEJ;AAEA,SAAS,aAAa,EAAE,UAAU,GAA2B;AAC3D,SACE,gBAAAA,KAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACvJ,0BAAAA,KAAC,UAAK,GAAE,yBAAwB,GAClC;AAEJ;AAEA,SAAS,eAAe,EAAE,UAAU,GAA2B;AAC7D,SACE,gBAAAA,KAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACvJ,0BAAAA,KAAC,UAAK,GAAE,oHAAmH,GAC7H;AAEJ;AAEA,SAAS,YAAY,EAAE,UAAU,GAA2B;AAC1D,SACE,gBAAAC,MAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACvJ;AAAA,oBAAAD,KAAC,UAAK,GAAE,8HAA6H;AAAA,IACrI,gBAAAA,KAAC,UAAK,GAAE,mBAAkB;AAAA,KAC5B;AAEJ;AAEA,SAAS,WAAW,EAAE,UAAU,GAA2B;AACzD,SACE,gBAAAA,KAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,eAAW,MAChI,0BAAAA,KAAC,UAAK,GAAE,wBAAuB,GACjC;AAEJ;AAEA,SAAS,WAAW,EAAE,UAAU,GAA2B;AACzD,SACE,gBAAAC,MAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACvJ;AAAA,oBAAAD,KAAC,UAAK,GAAE,qDAAoD;AAAA,IAC5D,gBAAAA,KAAC,UAAK,GAAE,YAAW;AAAA,KACrB;AAEJ;AAEA,SAAS,YAAY,EAAE,UAAU,GAA2B;AAC1D,SACE,gBAAAA,KAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACvJ,0BAAAA,KAAC,UAAK,GAAE,mEAAkE,GAC5E;AAEJ;AAEA,SAAS,SAAS,EAAE,UAAU,GAA2B;AACvD,SACE,gBAAAC,MAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACvJ;AAAA,oBAAAD,KAAC,UAAK,GAAE,KAAI,GAAE,KAAI,OAAM,KAAI,QAAO,MAAK,IAAG,KAAI;AAAA,IAC/C,gBAAAA,KAAC,UAAK,GAAE,qCAAoC;AAAA,KAC9C;AAEJ;AAmRA,IAAM,qBAAqB;AAK3B,IAAM,cAAc;AACpB,IAAM,qBAAqB;AAE3B,IAAM,uBAAuB;AAG7B,SAAS,kBAAkB,SAA+D;AACxF,SAAO,OAAO,YAAY,YAAY,YAAY,QAAQ,QAAQ,OAAO;AAC3E;AAEA,SAAS,UAAU,OAAkE;AACnF,SAAO,OAAQ,OAAoD,SAAS;AAC9E;AAIA,SAAS,gBAAgB,OAAgB,UAA0B;AACjE,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,QAAQ,OAAO;AAChE,UAAM,QAAS,MAA+B;AAC9C,QAAI,OAAO,UAAU,YAAY,MAAM,KAAK,MAAM,GAAI,QAAO;AAC7D,WAAO;AAAA,EACT;AACA,MAAI,OAAO,UAAU,YAAY,MAAM,KAAK,MAAM,GAAI,QAAO;AAC7D,MAAI,iBAAiB,SAAS,MAAM,QAAQ,KAAK,MAAM,GAAI,QAAO,MAAM;AACxE,SAAO;AACT;AAaO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA,qBAAqB;AAAA,EACrB;AAAA,EACA,cAAc;AAAA,EACd,WAAW;AAAA,EACX,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,oBAAoB;AAAA,EACpB;AAAA,EACA;AAAA,EACA,eAAe,CAAC;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,kBAAkB;AAAA,EAClB,eAAe,CAAC;AAAA,EAChB,2BAA2B;AAAA,EAC3B;AAAA,EACA,qBAAqB;AAAA,EACrB;AAAA,EACA,UAAU;AAAA,EACV,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA,gBAAgB;AAAA,EAChB,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,cAAc;AAAA,EACd;AACF,GAAsB;AACpB,QAAM,eAAe,UAAU;AAC/B,QAAM,CAAC,UAAU,WAAW,IAAIE,UAAS,gBAAgB,EAAE;AAC3D,QAAM,OAAO,eAAe,QAAQ;AAGpC,QAAM,UAAUC,QAAO,IAAI;AAC3B,UAAQ,UAAU;AAElB,QAAM,cAAcA,QAA4B,IAAI;AACpD,QAAM,eAAeA,QAAyB,IAAI;AAClD,QAAM,iBAAiBA,QAAyB,IAAI;AACpD,QAAM,CAAC,UAAU,WAAW,IAAID,UAAS,KAAK;AAC9C,QAAM,YAAYC,QAAO,CAAC;AAG1B,QAAM,mBAAmBA,QAAO,CAAC;AAEjC,QAAM,UAAUC;AAAA,IACd,CAAC,SAAiB;AAChB,UAAI,CAAC,aAAc,aAAY,IAAI;AACnC,sBAAgB,IAAI;AAAA,IACtB;AAAA,IACA,CAAC,cAAc,aAAa;AAAA,EAC9B;AAKA,QAAM,CAAC,cAAc,eAAe,IAAIF,UAAwB,IAAI;AACpE,QAAM,iBAAiBE;AAAA,IACrB,CAAC,UAA0B;AACzB,sBAAgB,IAAI;AACpB,kBAAY,KAAK;AAAA,IACnB;AAAA,IACA,CAAC,SAAS;AAAA,EACZ;AACA,QAAM,qBAAqBA;AAAA,IACzB,CAAC,YAAoB;AACnB,sBAAgB,OAAO;AACvB,uBAAiB,OAAO;AAAA,IAC1B;AAAA,IACA,CAAC,cAAc;AAAA,EACjB;AACA,QAAM,YAAY,aAAa,EAAE,WAAW,gBAAgB,SAAS,mBAAmB,CAAC;AAOzF,EAAAC,WAAU,MAAM;AACd,UAAM,KAAK,YAAY;AACvB,QAAI,CAAC,GAAI;AACT,OAAG,MAAM,SAAS;AAClB,OAAG,MAAM,SAAS,GAAG,KAAK,IAAI,GAAG,cAAc,SAAS,CAAC;AAAA,EAC3D,GAAG,CAAC,MAAM,WAAW,OAAO,CAAC;AAS7B,QAAM,cAAcF,QAAsB,IAAI;AAC9C,QAAM,kBAAkBA,QAAsB,IAAI;AAClD,EAAAE,WAAU,MAAM;AACd,UAAM,OAAO,YAAY;AACzB,gBAAY,UAAU,QAAQ;AAC9B,QAAI,QAAQ,QAAQ,SAAS,QAAQ,aAAc;AACnD,YAAQ,IAAI;AACZ,oBAAgB;AAChB,UAAM,KAAK,YAAY;AACvB,QAAI,MAAM,GAAG,UAAU,MAAM;AAK3B,SAAG,MAAM;AACT,SAAG,kBAAkB,KAAK,QAAQ,KAAK,MAAM;AAAA,IAC/C,OAAO;AAEL,sBAAgB,UAAU;AAAA,IAC5B;AAAA,EACF,GAAG,CAAC,MAAM,SAAS,eAAe,YAAY,CAAC;AAK/C,EAAAA,WAAU,MAAM;AACd,QAAI,gBAAgB,WAAW,QAAQ,gBAAgB,YAAY;AACjE;AACF,oBAAgB,UAAU;AAC1B,UAAM,KAAK,YAAY;AACvB,QAAI,CAAC,GAAI;AACT,OAAG,MAAM;AACT,OAAG,kBAAkB,KAAK,QAAQ,KAAK,MAAM;AAAA,EAC/C,GAAG,CAAC,IAAI,CAAC;AAKT,QAAM,kBAAkBF,QAA4D,IAAI;AACxF,EAAAE,WAAU,MAAM;AACd,UAAM,UAAU,gBAAgB;AAChC,QAAI,CAAC,WAAW,QAAQ,SAAS,KAAM;AACvC,oBAAgB,UAAU;AAC1B,UAAM,KAAK,YAAY;AACvB,QAAI,CAAC,GAAI;AACT,OAAG,MAAM;AACT,UAAM,QAAQ,KAAK,IAAI,QAAQ,OAAO,KAAK,MAAM;AACjD,UAAM,MAAM,KAAK,IAAI,QAAQ,KAAK,KAAK,MAAM;AAC7C,OAAG,kBAAkB,OAAO,GAAG;AAAA,EACjC,GAAG,CAAC,IAAI,CAAC;AAIT,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,iBAAiB,SAAU;AAChC,aAAS,UAAU,GAA6B;AAC9C,WAAK,EAAE,WAAW,EAAE,YAAY,EAAE,IAAI,YAAY,MAAM,KAAK;AAC3D,UAAE,eAAe;AACjB,oBAAY,SAAS,MAAM;AAAA,MAC7B;AAAA,IACF;AACA,aAAS,iBAAiB,WAAW,SAAS;AAC9C,WAAO,MAAM,SAAS,oBAAoB,WAAW,SAAS;AAAA,EAChE,GAAG,CAAC,eAAe,QAAQ,CAAC;AAQ5B,QAAM,gBAAgB,2BAClB,eACA,aAAa,OAAO,CAAC,MAAM,EAAE,WAAW,OAAO;AACnD,QAAM,cAAc,KAAK,KAAK,EAAE,SAAS,KAAK,cAAc,SAAS;AAIrE,QAAM,sBAAsB,eAAe,CAAC;AAC5C,QAAM,UAAU,eAAe,CAAC,uBAAuB,CAAC;AAExD,QAAM,CAAC,YAAY,aAAa,IAAIH,UAA4B,IAAI;AAMpE,QAAM,WAAWE;AAAA,IACf,CAAC,OAAgB,OAAe,SAAiB,OAA2B,UAA0C;AACpH,YAAM,UAAU,gBAAgB,OAAO,kBAAkB;AACzD,YAAM,WAAW,QAAQ,YAAY;AACrC,UAAI,UAAU;AACZ,cAAM,KAAK,YAAY;AACvB,gBAAQ,KAAK;AACb,YAAI,MAAM,GAAG,UAAU,OAAO;AAK5B,aAAG,MAAM;AACT,aAAG,kBAAkB,KAAK,IAAI,MAAM,OAAO,MAAM,MAAM,GAAG,KAAK,IAAI,MAAM,KAAK,MAAM,MAAM,CAAC;AAAA,QAC7F,OAAO;AACL,0BAAgB,UAAU,EAAE,MAAM,OAAO,OAAO,MAAM,OAAO,KAAK,MAAM,IAAI;AAAA,QAC9E;AAAA,MACF;AACA,oBAAc,EAAE,SAAS,MAAM,OAAO,SAAS,OAAO,SAAS,CAAC;AAChE,qBAAe,EAAE,SAAS,MAAM,OAAO,OAAO,OAAO,SAAS,CAAC;AAAA,IACjE;AAAA,IACA,CAAC,cAAc,oBAAoB,OAAO;AAAA,EAC5C;AAIA,QAAM,eAAeA;AAAA,IACnB,CAAC,OAAe,SAAiB,OAA2B,UAA0C;AACpG,UAAI;AACJ,UAAI;AACF,kBAAU,cAAc,YAAY,SAAS,KAAK,IAAI,SAAS,OAAO;AAAA,MACxE,SAAS,OAAO;AACd,iBAAS,OAAO,OAAO,SAAS,OAAO,KAAK;AAC5C;AAAA,MACF;AACA,UAAI,UAAU,OAAO,GAAG;AACtB,aAAK,QAAQ;AAAA,UACX,CAAC,YAAY;AACX,gBAAI,kBAAkB,OAAO,EAAG,UAAS,SAAS,OAAO,SAAS,OAAO,KAAK;AAAA,UAChF;AAAA,UACA,CAAC,UAAmB,SAAS,OAAO,OAAO,SAAS,OAAO,KAAK;AAAA,QAClE;AACA;AAAA,MACF;AACA,UAAI,kBAAkB,OAAO,EAAG,UAAS,SAAS,OAAO,SAAS,OAAO,KAAK;AAAA,IAChF;AAAA,IACA,CAAC,QAAQ,aAAa,QAAQ;AAAA,EAChC;AAEA,QAAM,OAAOA,aAAY,MAAM;AAC7B,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,uBAAuB,SAAU;AACrC,UAAM,aAAa,aAAa,OAAO,CAAC,MAAM,EAAE,WAAW,OAAO;AAClE,UAAM,WAAW,2BAA2B,eAAe;AAC3D,QAAI,CAAC,WAAW,SAAS,WAAW,EAAG;AAMvC,QAAI,CAAC,WAAW,WAAW,WAAW,GAAG;AACvC,YAAM,UACJ,+BACC,aAAa,KAAK,CAAC,MAAM,EAAE,WAAW,OAAO,IAC1C,0DACA;AACN,oBAAc,EAAE,SAAS,MAAM,IAAI,SAAS,IAAI,OAAO,CAAC,GAAG,UAAU,KAAK,CAAC;AAC3E;AAAA,IACF;AAKA,UAAM,QAAQ,cACV,WAAW,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,IAAwB,IACtE,CAAC;AACL,UAAM,KAAK,YAAY;AACvB,UAAM,QAAQ,EAAE,OAAO,IAAI,kBAAkB,KAAK,QAAQ,KAAK,IAAI,gBAAgB,KAAK,OAAO;AAC/F,kBAAc,IAAI;AAClB,YAAQ,EAAE;AACV,YAAQ,UAAU;AAClB,iBAAa,MAAM,SAAS,OAAO,KAAK;AAAA,EAC1C,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAKD,QAAM,kBAAkBA,aAAY,MAAM;AACxC,UAAM,UAAU;AAChB,QAAI,CAAC,WAAW,uBAAuB,SAAU;AACjD,kBAAc,IAAI;AAClB,UAAM,QAAQ,EAAE,OAAO,QAAQ,KAAK,QAAQ,KAAK,QAAQ,KAAK,OAAO;AACrE,iBAAa,QAAQ,MAAM,QAAQ,SAAS,QAAQ,OAAO,KAAK;AAAA,EAClE,GAAG,CAAC,YAAY,qBAAqB,UAAU,YAAY,CAAC;AAO5D,QAAM,gBAAgBD,QAAuB,IAAI;AACjD,QAAM,UAAUA,QAAuB,IAAI;AAC3C,QAAM,cAAc,MAAM;AAC1B,QAAM,CAAC,aAAa,cAAc,IAAID,UAAS,CAAC;AAChD,QAAM,CAAC,mBAAmB,oBAAoB,IAAIA,UAAwB,IAAI;AAC9E,QAAM,aACJ,iBAAiB,cAAc,SAAS,IAAI,YAAY,KAAK,IAAI,IAAI,CAAC,IAAI;AAC5E,QAAM,YAAY,eAAe,UAAa,SAAS;AACvD,QAAM,aAAaI;AAAA,IACjB,OACG,iBAAiB,CAAC,GAAG,IAAI,CAAC,aAAa;AAAA,MACtC,IAAI,QAAQ;AAAA,MACZ,OAAO;AAAA,MACP,OAAO,IAAI,QAAQ,IAAI;AAAA,MACvB,aAAa,QAAQ;AAAA,MACrB,UAAU,CAAC,QAAQ,MAAM,QAAQ,WAAW;AAAA,IAC9C,EAAE;AAAA,IACJ,CAAC,aAAa;AAAA,EAChB;AACA,QAAM,gBAAgBA;AAAA,IACpB,MAAO,eAAe,SAAY,CAAC,IAAI,0BAA0B,YAAY,UAAU;AAAA,IACvF,CAAC,YAAY,UAAU;AAAA,EACzB;AACA,QAAM,mBAAmB,cAAc,WAAW,IAAI,IAAI,KAAK,IAAI,aAAa,cAAc,SAAS,CAAC;AAExG,EAAAD,WAAU,MAAM;AACd,mBAAe,CAAC;AAAA,EAClB,GAAG,CAAC,UAAU,CAAC;AAEf,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,UAAW;AAChB,aACG,eAAe,GAAG,WAAW,IAAI,gBAAgB,EAAE,GAClD,iBAAiB,EAAE,OAAO,UAAU,CAAC;AAAA,EAC3C,GAAG,CAAC,WAAW,kBAAkB,WAAW,CAAC;AAE7C,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,UAAW;AAChB,aAAS,YAAY,GAAe;AAClC,YAAM,SAAS,EAAE;AACjB,UAAI,QAAQ,SAAS,SAAS,MAAM,EAAG;AACvC,UAAI,cAAc,SAAS,SAAS,MAAM,EAAG;AAC7C,2BAAqB,QAAQ,OAAO;AAAA,IACtC;AACA,aAAS,iBAAiB,aAAa,WAAW;AAClD,WAAO,MAAM,SAAS,oBAAoB,aAAa,WAAW;AAAA,EACpE,GAAG,CAAC,SAAS,CAAC;AAEd,QAAM,YAAYD;AAAA,IAChB,CAAC,SAAiB;AAChB,YAAM,UAAU,eAAe,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI;AAG1D,cAAQ,EAAE;AACV,2BAAqB,IAAI;AACzB,eAAS,IAAI;AAAA,IACf;AAAA,IACA,CAAC,eAAe,OAAO;AAAA,EACzB;AAEA,QAAM,gBAAgB,CAAC,MAA0C;AAE/D,QAAI,EAAE,YAAY,YAAa;AAC/B,QAAI,WAAW;AACb,UAAI,EAAE,QAAQ,aAAa;AACzB,UAAE,eAAe;AACjB,YAAI,cAAc,SAAS,EAAG,iBAAgB,mBAAmB,KAAK,cAAc,MAAM;AAC1F;AAAA,MACF;AACA,UAAI,EAAE,QAAQ,WAAW;AACvB,UAAE,eAAe;AACjB,YAAI,cAAc,SAAS;AACzB,0BAAgB,mBAAmB,IAAI,cAAc,UAAU,cAAc,MAAM;AACrF;AAAA,MACF;AACA,UAAK,EAAE,QAAQ,WAAW,CAAC,EAAE,YAAa,EAAE,QAAQ,OAAO;AACzD,cAAM,OAAO,cAAc,gBAAgB;AAC3C,YAAI,MAAM;AACR,YAAE,eAAe;AACjB,oBAAU,KAAK,EAAE;AACjB;AAAA,QACF;AAAA,MAEF;AACA,UAAI,EAAE,QAAQ,UAAU;AACtB,UAAE,eAAe;AACjB,6BAAqB,IAAI;AACzB;AAAA,MACF;AAAA,IACF;AACA,QAAI,EAAE,QAAQ,WAAW,CAAC,EAAE,UAAU;AACpC,QAAE,eAAe;AACjB,WAAK;AAAA,IACP;AAAA,EACF;AAOA,QAAM,eAAeA;AAAA,IACnB,CAAC,OAAe,aAAuB;AACrC,UAAI,CAAC,YAAY,MAAM,WAAW,EAAG;AACrC,YAAM,EAAE,UAAU,SAAS,IAAI,oBAAoB,OAAO,MAAM;AAChE,UAAI,SAAS,SAAS,EAAG,iBAAgB,QAAQ;AACjD,UAAI,SAAS,WAAW,EAAG;AAC3B,YAAM,YACJ,SAAS,WAAW,SAAS,UAAU,SAAS,MAAM,CAAC,MAAM,MAAM,SAAS,SAAS,CAAC,CAAC;AACzF,UAAI,WAAW;AACb,iBAAS,QAAQ;AACjB;AAAA,MACF;AACA,YAAM,WAAW,IAAI,aAAa;AAClC,iBAAW,QAAQ,SAAU,UAAS,MAAM,IAAI,IAAI;AACpD,eAAS,SAAS,KAAK;AAAA,IACzB;AAAA,IACA,CAAC,UAAU,eAAe,MAAM;AAAA,EAClC;AAEA,QAAM,mBAAmB,CAAC,MAAqC;AAG7D,QAAI,EAAE,OAAO,OAAO,OAAQ,cAAa,MAAM,KAAK,EAAE,OAAO,KAAK,GAAG,EAAE,OAAO,KAAK;AACnF,MAAE,OAAO,QAAQ;AAAA,EACnB;AAEA,QAAM,cAAc,CAAC,MAA2C;AAC9D,QAAI,CAAC,SAAU;AACf,UAAM,iBAAiB,EAAE,eAAe;AACxC,QAAI,CAAC,kBAAkB,eAAe,WAAW,EAAG;AAGpD,MAAE,eAAe;AAIjB,UAAM,EAAE,OAAO,UAAU,IAAI;AAAA,MAC3B,MAAM,KAAK,cAAc;AAAA,MACzB,iBAAiB;AAAA,MACjB,aAAa,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,IAChC;AACA,qBAAiB,UAAU;AAC3B,iBAAa,OAAO,cAAc;AAAA,EACpC;AAEA,QAAM,qBAAqB,CAAC,MAAqC;AAC/D,QAAI,EAAE,OAAO,OAAO,OAAQ,EAAC,kBAAkB,YAAY,EAAE,OAAO,KAAK;AACzE,MAAE,OAAO,QAAQ;AAAA,EACnB;AAEA,QAAM,kBAAkBA,aAAY,CAAC,MAAiB;AACpD,MAAE,eAAe;AACjB,MAAE,gBAAgB;AAClB,cAAU;AACV,QAAI,EAAE,cAAc,MAAM,SAAS,OAAO,EAAG,aAAY,IAAI;AAAA,EAC/D,GAAG,CAAC,CAAC;AAEL,QAAM,kBAAkBA,aAAY,CAAC,MAAiB;AACpD,MAAE,eAAe;AACjB,MAAE,gBAAgB;AAClB,cAAU;AACV,QAAI,UAAU,WAAW,GAAG;AAC1B,gBAAU,UAAU;AACpB,kBAAY,KAAK;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,iBAAiBA,aAAY,CAAC,MAAiB;AACnD,MAAE,eAAe;AACjB,MAAE,gBAAgB;AAClB,QAAI,EAAE,aAAc,GAAE,aAAa,aAAa;AAAA,EAClD,GAAG,CAAC,CAAC;AAEL,QAAM,aAAaA;AAAA,IACjB,CAAC,MAAiB;AAChB,QAAE,eAAe;AACjB,QAAE,gBAAgB;AAClB,gBAAU,UAAU;AACpB,kBAAY,KAAK;AACjB,YAAM,QAAQ,EAAE,cAAc;AAC9B,UAAI,OAAO,OAAQ,cAAa,MAAM,KAAK,KAAK,GAAG,KAAK;AAAA,IAC1D;AAAA,IACA,CAAC,YAAY;AAAA,EACf;AAEA,QAAM,cAAc,aAAa,OAAO,CAAC,MAAM,EAAE,SAAS,QAAQ;AAClE,QAAM,YAAY,aAAa,OAAO,CAAC,MAAM,EAAE,SAAS,QAAQ;AAMhE,QAAM,YAAY,YAAY,QAAQ,sBAAsB;AAC5D,QAAM,aAAa,YAAY,QAAQ,CAAC;AAExC,SACE,gBAAAH;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,YAAY,aAAa,EAAE;AAAA,MACtC,aAAa,WAAW,kBAAkB;AAAA,MAC1C,aAAa,WAAW,kBAAkB;AAAA,MAC1C,YAAY,WAAW,iBAAiB;AAAA,MACxC,QAAQ,WAAW,aAAa;AAAA,MAE/B;AAAA,oBACC,gBAAAD,KAAC,SAAI,WAAU,2IACb,0BAAAC,MAAC,SAAI,WAAU,eACb;AAAA,0BAAAD,KAAC,UAAK,WAAU,iGACd,0BAAAA,KAAC,eAAY,WAAU,WAAU,GACnC;AAAA,UACA,gBAAAA,KAAC,OAAE,WAAU,yCAAyC,qBAAU;AAAA,UAChE,gBAAAA,KAAC,OAAE,WAAU,wCAAwC,2BAAgB;AAAA,WACvE,GACF;AAAA,QAGD,aAAa,gBAAAA,KAAC,SAAI,WAAU,mDAAmD,oBAAS;AAAA,QAExF,gBACC,gBAAAC;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,eAAY;AAAA,YACZ,WAAU;AAAA,YAEV;AAAA,8BAAAD,KAAC,UAAK,WAAU,kBAAkB,wBAAa;AAAA,cAC/C,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,cAAW;AAAA,kBACX,SAAS,MAAM,gBAAgB,IAAI;AAAA,kBACnC,WAAU;AAAA,kBACX;AAAA;AAAA,cAED;AAAA;AAAA;AAAA,QACF;AAAA,QAGD,cACC,gBAAAC;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,eAAY;AAAA,YACZ,WAAU;AAAA,YAEV;AAAA,8BAAAA,MAAC,SAAI,WAAU,0BACb;AAAA,gCAAAD,KAAC,UAAK,WAAU,kBAAkB,qBAAW,SAAQ;AAAA,gBACrD,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,cAAW;AAAA,oBACX,SAAS,MAAM,cAAc,IAAI;AAAA,oBACjC,WAAU;AAAA,oBACX;AAAA;AAAA,gBAED;AAAA,iBACF;AAAA,cAIC,CAAC,WAAW,YACX,gBAAAC,MAAC,SAAI,WAAU,UACb;AAAA,gCAAAD;AAAA,kBAAC;AAAA;AAAA,oBACC,eAAY;AAAA,oBACZ,WAAU;AAAA,oBAET,qBAAW;AAAA;AAAA,gBACd;AAAA,gBACA,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,cAAW;AAAA,oBACX,SAAS;AAAA,oBACT,UAAU,uBAAuB;AAAA,oBACjC,WAAU;AAAA,oBACX;AAAA;AAAA,gBAED;AAAA,iBACF;AAAA;AAAA;AAAA,QAEJ;AAAA,QAGD,aAAa,SAAS,KACrB,gBAAAA,KAAC,SAAI,cAAW,mBAAkB,WAAU,uCACzC,uBAAa,IAAI,CAAC,SACjB,gBAAAC;AAAA,UAAC;AAAA;AAAA,YAEC,WAAU;AAAA,YAET;AAAA,mBAAK,QACJ,gBAAAD,KAAC,UAAK,WAAU,YAAW,eAAW,MACnC,eAAK,MACR;AAAA,cAEF,gBAAAA,KAAC,UAAK,WAAU,oBAAoB,eAAK,OAAM;AAAA,cAC9C,KAAK,YACJ,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,cAAY,kBAAkB,KAAK,KAAK;AAAA,kBACxC,SAAS,KAAK;AAAA,kBACd,WAAU;AAAA,kBAEV,0BAAAA,KAAC,cAAW,WAAU,WAAU;AAAA;AAAA,cAClC;AAAA;AAAA;AAAA,UAjBG,KAAK;AAAA,QAmBZ,CACD,GACH;AAAA,QAGD,aAAa,SAAS,KACrB,gBAAAA,KAAC,SAAI,WAAU,+BACZ,WAAC,GAAG,aAAa,GAAG,SAAS,EAAE,IAAI,CAAC,MAAM;AACzC,gBAAM,UAAU,EAAE,WAAW;AAC7B,iBACE,gBAAAC;AAAA,YAAC;AAAA;AAAA,cAEC,OAAO,UAAU,EAAE,eAAe;AAAA,cAClC,WAAW,4EACT,UACI,2CACA,4CACN,IAAI,EAAE,WAAW,YAAY,eAAe,EAAE;AAAA,cAI7C;AAAA,kBAAE,SAAS,YAAY,EAAE,aACxB,gBAAAD,KAAC,SAAI,KAAK,EAAE,YAAY,KAAI,IAAG,WAAU,yCAAwC,IAC/E,EAAE,SAAS,WACb,gBAAAA,KAAC,eAAY,WAAU,oBAAmB,IAE1C,gBAAAA,KAAC,kBAAe,WAAU,oBAAmB;AAAA,gBAE/C,gBAAAA,KAAC,UAAK,WAAU,0BAA0B,YAAE,MAAK;AAAA,gBAChD,EAAE,cAAc,UAAa,gBAAAC,MAAC,UAAK,WAAU,yBAAwB;AAAA;AAAA,kBAAE,EAAE;AAAA,kBAAU;AAAA,mBAAC;AAAA,gBACpF,EAAE,WAAW,eACZ,gBAAAD,KAAC,UAAK,WAAU,kFAAiF;AAAA,gBAElG,WAAW,EAAE,gBACZ,gBAAAA,KAAC,UAAK,WAAU,8CAA8C,YAAE,cAAa;AAAA,gBAE9E,WAAW,eACV,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,cAAY,gBAAgB,EAAE,IAAI;AAAA,oBAClC,SAAS,MAAM,YAAY,EAAE,EAAE;AAAA,oBAC/B,WAAU;AAAA,oBAEV,0BAAAA,KAAC,cAAW,WAAU,WAAU;AAAA;AAAA,gBAClC;AAAA,gBAED,gBACC,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,cAAY,UAAU,EAAE,IAAI;AAAA,oBAC5B,SAAS,MAAM,aAAa,EAAE,EAAE;AAAA,oBAChC,WAAU;AAAA,oBAEV,0BAAAA,KAAC,cAAW,WAAU,WAAU;AAAA;AAAA,gBAClC;AAAA;AAAA;AAAA,YA3CG,EAAE;AAAA,UA6CT;AAAA,QAEJ,CAAC,GACH;AAAA,QAQF,gBAAAC;AAAA,UAAC;AAAA;AAAA,YACC,KAAK;AAAA,YACL,eAAY;AAAA,YACZ,WAAW,4KACT,WAAW,kBAAkB,EAC/B;AAAA,YAEA;AAAA,8BAAAD;AAAA,gBAAC;AAAA;AAAA,kBACC,KAAK;AAAA,kBACL,OAAO;AAAA,kBACP,UAAU,CAAC,MAAM,QAAQ,EAAE,OAAO,KAAK;AAAA,kBACvC,WAAW;AAAA,kBACX,SAAS,WAAW,cAAc;AAAA,kBAClC;AAAA,kBACA;AAAA,kBACA;AAAA,kBAWA,MAAM;AAAA,kBACN,OAAO,EAAE,WAAW,UAAU,cAAc,oBAAoB,UAAU;AAAA,kBAC1E,cAAW;AAAA,kBACX,WAAU;AAAA;AAAA,cACZ;AAAA,cAEA,gBAAAC,MAAC,SAAI,WAAU,wBACZ;AAAA,4BACC,gBAAAA,MAAAF,WAAA,EACE;AAAA,kCAAAC;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,SAAS,MAAM,aAAa,SAAS,MAAM;AAAA,sBAC3C;AAAA,sBACA,cAAW;AAAA,sBACX,OAAM;AAAA,sBACN,WAAU;AAAA,sBAEV,0BAAAA,KAAC,kBAAe,WAAU,WAAU;AAAA;AAAA,kBACtC;AAAA,kBACA,gBAAAA,KAAC,WAAM,KAAK,cAAc,MAAK,QAAO,UAAQ,MAAC,WAAU,UAAS,QAAgB,UAAU,kBAAkB;AAAA,mBAChH;AAAA,gBAED,kBACC,gBAAAC,MAAAF,WAAA,EACE;AAAA,kCAAAC;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,SAAS,MAAM,eAAe,SAAS,MAAM;AAAA,sBAC7C;AAAA,sBACA,cAAW;AAAA,sBACX,OAAM;AAAA,sBACN,WAAU;AAAA,sBAEV,0BAAAA,KAAC,eAAY,WAAU,WAAU;AAAA;AAAA,kBACnC;AAAA,kBAEA,gBAAAA;AAAA,oBAAC;AAAA;AAAA,sBACC,KAAK;AAAA,sBACL,MAAK;AAAA,sBACL,UAAQ;AAAA,sBACR,WAAU;AAAA,sBACV,UAAU;AAAA,sBACT,GAAI,EAAE,iBAAiB,GAAG;AAAA;AAAA,kBAC7B;AAAA,mBACF;AAAA,gBAWF,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,eAAY;AAAA,oBACZ,WAAU;AAAA,oBAET,wBAAc;AAAA;AAAA,gBACjB;AAAA,gBAMC,YACC,gBAAAA,KAAC,SAAI,eAAY,qBAAoB,WAAU,sCAC5C,oBACH;AAAA,gBAQD,aAAa,UAAU,YACtB,UAAU,YACR,gBAAAC,MAAC,SAAI,WAAU,sCACb;AAAA,kCAAAD,KAAC,UAAK,eAAY,QAAO,WAAU,qDAAoD;AAAA,kBACvF,gBAAAA;AAAA,oBAAC;AAAA;AAAA,sBACC,eAAY;AAAA,sBACZ,eAAY;AAAA,sBACZ,WAAU;AAAA,sBAET,iCAAuB,UAAU,cAAc;AAAA;AAAA,kBAClD;AAAA,kBACA,gBAAAA,KAAC,UAAK,MAAK,UAAS,WAAU,WAAU,uBAExC;AAAA,kBACA,gBAAAA;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,SAAS,UAAU;AAAA,sBACnB,cAAW;AAAA,sBACX,OAAM;AAAA,sBACN,WAAU;AAAA,sBAEV,0BAAAA,KAAC,aAAU,WAAU,WAAU;AAAA;AAAA,kBACjC;AAAA,mBACF,IAEA,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,SAAS,UAAU;AAAA,oBACnB;AAAA,oBACA,cAAW;AAAA,oBACX,OAAM;AAAA,oBACN,WAAU;AAAA,oBAEV,0BAAAA,KAAC,YAAS,WAAU,WAAU;AAAA;AAAA,gBAChC,IAEA;AAAA,gBAEH,cACC,gBAAgB,SACd,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,SAAS;AAAA,oBACT,cAAW;AAAA,oBACX,OAAM;AAAA,oBACN,WAAU;AAAA,oBAEV,0BAAAA,KAAC,aAAU,WAAU,WAAU;AAAA;AAAA,gBACjC,IAEA,gBAAAC;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,SAAS;AAAA,oBACT,cAAW;AAAA,oBACX,WAAU;AAAA,oBAEV;AAAA,sCAAAD,KAAC,aAAU,WAAU,eAAc;AAAA,sBACnC,gBAAAA,KAAC,UAAK,kBAAI;AAAA;AAAA;AAAA,gBACZ,IAEA,gBAAgB,SAClB,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,SAAS;AAAA,oBACT,UAAU,CAAC;AAAA,oBACX,cAAY;AAAA,oBACZ,OAAO;AAAA,oBACP,WAAU;AAAA,oBAEV,0BAAAA,KAAC,gBAAa,WAAU,WAAU;AAAA;AAAA,gBACpC,IAEA,gBAAAC;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,SAAS;AAAA,oBACT,UAAU,CAAC;AAAA,oBACX,cAAY;AAAA,oBACZ,WAAU;AAAA,oBAEV;AAAA,sCAAAD,KAAC,aAAU,WAAU,eAAc;AAAA,sBACnC,gBAAAA,KAAC,UAAM,qBAAU;AAAA;AAAA;AAAA,gBACnB;AAAA,iBAEJ;AAAA;AAAA;AAAA,QACF;AAAA,QAOA,gBAAAC;AAAA,UAAC;AAAA;AAAA,YACC,MAAM;AAAA,YACN,IAAI;AAAA,YACJ,MAAK;AAAA,YACL,YAAY;AAAA,YACZ,UAAU;AAAA,YACV,WAAW,0EAA0E,cAAc;AAAA,YAElG;AAAA,4BAAc,WAAW,KACxB,gBAAAD,KAAC,SAAI,WAAU,uDAAsD,kCAAoB;AAAA,cAE1F,cAAc,IAAI,CAAC,MAAM,UACxB,gBAAAC;AAAA,gBAAC;AAAA;AAAA,kBAEC,MAAK;AAAA,kBACL,MAAK;AAAA,kBACL,iBAAe,UAAU;AAAA,kBACzB,IAAI,GAAG,WAAW,IAAI,KAAK;AAAA,kBAC3B,aAAa,CAAC,MAAM,EAAE,eAAe;AAAA,kBACrC,aAAa,MAAM,eAAe,KAAK;AAAA,kBACvC,SAAS,MAAM,UAAU,KAAK,EAAE;AAAA,kBAChC,WAAW,wFAAwF,oBAAoB,IACrH,UAAU,mBAAmB,cAAc,iBAC7C;AAAA,kBAEA;AAAA,oCAAAD,KAAC,UAAK,WAAU,wCAAwC,eAAK,OAAM;AAAA,oBACnE,gBAAAA,KAAC,UAAK,WAAU,0CAA0C,eAAK,aAAY;AAAA;AAAA;AAAA,gBAbtE,KAAK;AAAA,cAcZ,CACD;AAAA;AAAA;AAAA,QACH;AAAA,QAEC,iBACC,gBAAAA,KAAC,SAAI,WAAU,gCACb,0BAAAC,MAAC,UAAK,WAAU,iCACd;AAAA,0BAAAD,KAAC,SAAI,WAAU,kEAAkE,8BAAoB,QAAQ,QAAO;AAAA,UACpH,gBAAAA,KAAC,SAAI,WAAU,yEAAwE,eAAC;AAAA,UACxF,gBAAAA,KAAC,UAAK,WAAU,QAAO,sBAAQ;AAAA,WACjC,GACF;AAAA;AAAA;AAAA,EAEJ;AAEJ;;;AEp1CA,SAAS,eAAAO,cAAa,aAAAC,YAAW,UAAAC,SAAQ,YAAAC,iBAAgB;AA+ClD,IAAM,yBAAN,cAAqC,MAAM;AAAA,EAChD,YACE,SACS,QACA,MACA,aACT;AACA,UAAM,OAAO;AAJJ;AACA;AACA;AAGT,SAAK,OAAO;AAAA,EACd;AAAA,EANW;AAAA,EACA;AAAA,EACA;AAKb;AASA,SAAS,SAAS,OAAgD;AAChE,SAAO,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IAC7D,QACA;AACN;AAEA,SAAS,SAAS,OAAiC;AACjD,QAAM,OAAO,SAAS,KAAK;AAC3B,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,SAAS,OAAO,KAAK,WAAW,WAAW,KAAK,SAAS,OAAO,KAAK,OAAO,WAAW,KAAK,KAAK;AACvG,MAAI,CAAC,UAAU,OAAO,KAAK,aAAa,YAAY,OAAO,KAAK,SAAS,YACrE,OAAO,KAAK,gBAAgB,YAAY,OAAO,KAAK,WAAW,SAAU,QAAO;AACpF,SAAO,EAAE,GAAG,MAAM,OAAO;AAC3B;AAEA,SAAS,gBAAgB,MAAgB,UAA2C;AAClF,MAAI,OAAO,SAAS,cAAc,YAAY,SAAS,UAAW,QAAO,SAAS;AAClF,QAAM,SAAS,OAAO,SAAS,WAAW,WAAW,SAAS,SAAS;AACvE,SAAO,GAAG,KAAK,MAAM,IAAI,KAAK,QAAQ,IAAI,MAAM;AAClD;AAEA,SAAS,oBAAoB,OAAkD;AAC7E,QAAM,OAAO,SAAS,KAAK;AAC3B,QAAM,OAAO,SAAS,MAAM,IAAI;AAChC,MAAI,CAAC,QAAQ,CAAC,KAAM,QAAO;AAC3B,QAAM,cAAc,SAAS,KAAK,QAAQ,KAAK,SAAS,KAAK,OAAO;AACpE,QAAM,WAAW,eAAe,OAAO,YAAY,WAAW,WAC1D;AAAA,IACE,WAAW,gBAAgB,MAAM,WAAW;AAAA,IAC5C,QAAQ,KAAK;AAAA,IACb,UAAU,KAAK;AAAA,IACf,QAAQ,YAAY;AAAA,IACpB,OAAO,OAAO,YAAY,UAAU,WAAW,YAAY,QAAQ;AAAA,EACrE,IACA;AACJ,SAAO;AAAA,IACL;AAAA,IACA,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,IAC/B,YAAY,KAAK,eAAe,QAAQ,KAAK,aAAa;AAAA,IAC1D,GAAI,KAAK,sBAAsB,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;AAAA,IACrE,GAAI,KAAK,kBAAkB,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;AAAA,EAC/D;AACF;AAEA,eAAe,aAAa,UAAsD;AAChF,SAAO,SAAS,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,IAAI,CAAC,KAAK,CAAC;AAC/D;AAKO,SAAS,gCACd,SAC2B;AAC3B,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,SAAS,CAAC,UACd,OAAO,QAAQ,QAAQ,aAAa,QAAQ,IAAI,KAAK,IAAI,QAAQ;AAEnE,QAAM,OAAO,OAAO,aAA2D;AAC7E,UAAM,OAAO,MAAM,aAAa,QAAQ;AACxC,UAAM,SAAS,oBAAoB,IAAI;AACvC,QAAI,SAAS,MAAM,OAAQ,QAAO;AAClC,UAAM,cAAc,SAAS,KAAK,IAAI,KAAK;AAC3C,UAAM,UAAU,OAAO,KAAK,UAAU,WAClC,KAAK,QACL,OAAO,KAAK,YAAY,WAAW,KAAK,UAAU,wBAAwB,SAAS,MAAM;AAC7F,UAAM,IAAI;AAAA,MACR;AAAA,MACA,SAAS;AAAA,MACT,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO;AAAA,MAC5C;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM,QAAQ,OAAO;AACnB,YAAM,SAAS,OAAO,KAAK;AAC3B,YAAM,MAAM,IAAI,IAAI,QAAQ,WAAW,UAAU,UAAU,kBAAkB;AAC7E,UAAI,aAAa,IAAI,UAAU,MAAM,MAAM;AAC3C,UAAI,MAAM,aAAa,OAAW,KAAI,aAAa,IAAI,YAAY,OAAO,MAAM,QAAQ,CAAC;AACzF,YAAM,SAAS,WAAW,KAAK,MAAM,IACjC,IAAI,SAAS,IACb,GAAG,IAAI,QAAQ,GAAG,IAAI,MAAM;AAChC,aAAO,KAAK,MAAM,UAAU,QAAQ,EAAE,QAAQ,MAAM,CAAC,CAAC;AAAA,IACxD;AAAA,IACA,MAAM,OAAO,OAAO;AAClB,YAAM,QAAQ,OAAO,QAAQ,SAAS,aAAa,QAAQ,KAAK,KAAK,IAAI,QAAQ,QAAQ,CAAC;AAC1F,aAAO,KAAK,MAAM,UAAU,OAAO,KAAK,GAAG;AAAA,QACzC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,GAAG,OAAO,GAAG,MAAM,CAAC;AAAA,MAC7C,CAAC,CAAC;AAAA,IACJ;AAAA,EACF;AACF;AAyBO,SAAS,mBAAmB,SAA8D;AAC/F,QAAM,CAAC,MAAM,OAAO,IAAIA,UAAS,QAAQ,IAAI;AAC7C,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAqC,IAAI;AACzE,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,KAAK;AAChD,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAwB,IAAI;AACtD,QAAM,cAAcD,QAAO,oBAAI,IAA2B,CAAC;AAC3D,QAAM,mBAAmBA,QAAO,KAAK;AAErC,EAAAD,WAAU,MAAM,QAAQ,QAAQ,IAAI,GAAG,CAAC,QAAQ,IAAI,CAAC;AAErD,QAAM,QAAQD,aAAY,OAAO,WAAsC;AACrE,YAAQ,OAAO,IAAI;AACnB,YAAQ,YAAY,OAAO,IAAI;AAC/B,UAAM,UAAU,OAAO;AACvB,QAAI,CAAC,WAAW,CAAC,QAAQ,eAAgB;AACzC,QAAI,UAAU,YAAY,QAAQ,IAAI,QAAQ,SAAS;AACvD,QAAI,CAAC,SAAS;AACZ,gBAAU,QAAQ,QAAQ,QAAQ,eAAe,OAAO,CAAC;AACzD,kBAAY,QAAQ,IAAI,QAAQ,WAAW,OAAO;AAClD,WAAK,QAAQ,QAAQ,MAAM,YAAY,QAAQ,OAAO,QAAQ,SAAS,CAAC;AAAA,IAC1E;AACA,UAAM;AAAA,EACR,GAAG,CAAC,QAAQ,gBAAgB,QAAQ,SAAS,CAAC;AAE9C,QAAM,SAASA,aAAY,OAAO,UAA+B,aAAsB;AACrF,QAAI,iBAAiB,QAAS,QAAO;AACrC,qBAAiB,UAAU;AAC3B,gBAAY,QAAQ;AACpB,aAAS,IAAI;AACb,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,OAAO,OAAO;AAAA,QACzC,QAAQ,KAAK;AAAA,QACb,UAAU,KAAK;AAAA,QACf;AAAA,QACA,GAAI,UAAU,KAAK,IAAI,EAAE,UAAU,SAAS,KAAK,EAAE,IAAI,CAAC;AAAA,MAC1D,CAAC;AACD,YAAM,MAAM,MAAM;AAClB,aAAO;AAAA,IACT,SAAS,OAAO;AACd,UAAI,iBAAiB,0BAA0B,MAAM,aAAa;AAChE,gBAAQ,MAAM,WAAW;AACzB,gBAAQ,YAAY,MAAM,WAAW;AAAA,MACvC;AACA,eAAS,iBAAiB,QAAQ,MAAM,UAAU,4BAA4B;AAC9E,aAAO;AAAA,IACT,UAAE;AACA,uBAAiB,UAAU;AAC3B,kBAAY,IAAI;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,OAAO,QAAQ,QAAQ,QAAQ,WAAW,KAAK,QAAQ,KAAK,QAAQ,CAAC;AAEzE,QAAM,UAAUA,aAAY,YAAY;AACtC,iBAAa,IAAI;AACjB,aAAS,IAAI;AACb,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,OAAO,QAAQ,EAAE,QAAQ,KAAK,QAAQ,UAAU,KAAK,SAAS,CAAC;AAC5F,YAAM,MAAM,MAAM;AAClB,aAAO;AAAA,IACT,SAAS,OAAO;AACd,eAAS,iBAAiB,QAAQ,MAAM,UAAU,6BAA6B;AAC/E,aAAO;AAAA,IACT,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,OAAO,QAAQ,QAAQ,KAAK,QAAQ,KAAK,QAAQ,CAAC;AAEtD,SAAO,EAAE,MAAM,UAAU,WAAW,OAAO,QAAQ,SAAS,YAAY,MAAM,SAAS,IAAI,EAAE;AAC/F;;;AC5OA,SAAS,kBAAkB,WAAmB,eAA+B;AAC3E,SAAO,GAAG,SAAS,IAAI,mBAAmB,aAAa,CAAC;AAC1D;AAEA,SAAS,eAAe,SAAmC,KAAqC;AAC9F,MAAI;AACF,UAAM,QAAQ,KAAK,MAAM,QAAQ,QAAQ,GAAG,KAAK,IAAI;AACrD,WAAO,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,IAC7D,QACA,CAAC;AAAA,EACP,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAGO,SAAS,qCACd,SACA,YAAY,iCACa;AACzB,SAAO;AAAA,IACL,IAAI,IAAI,WAAW;AACjB,aAAO,eAAe,SAAS,kBAAkB,WAAW,EAAE,CAAC,EAAE,SAAS,KAAK;AAAA,IACjF;AAAA,IACA,IAAI,IAAI,WAAW,YAAY;AAC7B,YAAM,MAAM,kBAAkB,WAAW,EAAE;AAC3C,cAAQ,QAAQ,KAAK,KAAK,UAAU,EAAE,GAAG,eAAe,SAAS,GAAG,GAAG,CAAC,SAAS,GAAG,WAAW,CAAC,CAAC;AAAA,IACnG;AAAA,IACA,OAAO,IAAI,WAAW;AACpB,YAAM,MAAM,kBAAkB,WAAW,EAAE;AAC3C,YAAM,WAAW,eAAe,SAAS,GAAG;AAC5C,aAAO,SAAS,SAAS;AACzB,UAAI,OAAO,KAAK,QAAQ,EAAE,WAAW,EAAG,SAAQ,WAAW,GAAG;AAAA,UACzD,SAAQ,QAAQ,KAAK,KAAK,UAAU,QAAQ,CAAC;AAAA,IACpD;AAAA,EACF;AACF;AAGO,SAAS,sCAA+D;AAC7E,QAAM,WAAW,oBAAI,IAAoB;AACzC,QAAM,MAAM,CAAC,IAAY,cAAsB,GAAG,EAAE,KAAS,SAAS;AACtE,SAAO;AAAA,IACL,KAAK,CAAC,IAAI,cAAc,SAAS,IAAI,IAAI,IAAI,SAAS,CAAC,KAAK;AAAA,IAC5D,KAAK,CAAC,IAAI,WAAW,eAAe,SAAS,IAAI,IAAI,IAAI,SAAS,GAAG,UAAU;AAAA,IAC/E,QAAQ,CAAC,IAAI,cAAc;AAAE,eAAS,OAAO,IAAI,IAAI,SAAS,CAAC;AAAA,IAAE;AAAA,EACnE;AACF;AAEA,SAAS,YAAY,OAAyB;AAC5C,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,IAAI,WAAW;AACtD,MAAI,CAAC,SAAS,OAAO,UAAU,SAAU,QAAO;AAChD,SAAO,OAAO,YAAY,OAAO,QAAQ,KAAgC,EACtE,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,MAAM,KAAK,cAAc,KAAK,CAAC,EACnD,IAAI,CAAC,CAAC,KAAK,MAAM,MAAM,CAAC,KAAK,YAAY,MAAM,CAAC,CAAC,CAAC;AACvD;AAGO,SAAS,+BAA+B,YAAiD;AAC9F,SAAO,KAAK,UAAU,YAAY,UAAU,CAAC;AAC/C;AAQA,SAAS,oBAA4B;AACnC,MAAI,WAAW,QAAQ,WAAY,QAAO,WAAW,OAAO,WAAW;AACvE,SAAO,WAAW,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;AACrE;AAKO,SAAS,wCACd,SACyB;AACzB,QAAM,YAAY,QAAQ,aAAa;AACvC,QAAM,YAAY,QAAQ,aAAa;AACvC,SAAO,OAAO,eAAe;AAC3B,UAAM,YAAY,+BAA+B,UAAU;AAC3D,QAAI;AACJ,QAAI;AACF,mBAAa,QAAQ,SAAS,IAAI,WAAW,IAAI,SAAS,KAAK;AAC/D,UAAI,CAAC,YAAY;AACf,sBAAc,QAAQ,oBAAoB,mBAAmB;AAC7D,gBAAQ,SAAS,IAAI,WAAW,IAAI,WAAW,UAAU;AAAA,MAC3D;AAAA,IACF,SAAS,OAAO;AACd,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,SAAS;AAAA,QACT,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MACpD;AAAA,IACF;AACA,UAAM,MAAM,OAAO,QAAQ,QAAQ,aAAa,QAAQ,IAAI,UAAU,IAAI,QAAQ;AAClF,UAAM,QAAQ,OAAO,QAAQ,SAAS,aAAa,QAAQ,KAAK,UAAU,IAAI,QAAQ,QAAQ,CAAC;AAC/F,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,kCAAkC,GAAG,SAAS;AAC9F,QAAI;AACF,YAAM,WAAW,MAAM,UAAU,KAAK;AAAA,QACpC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,QAAQ,WAAW;AAAA,QACnB,MAAM,KAAK,UAAU;AAAA,UACnB,GAAG;AAAA,UACH,IAAI,WAAW;AAAA,UACf,SAAS,WAAW;AAAA,UACpB;AAAA,UACA,GAAI,WAAW,OAAO,EAAE,MAAM,WAAW,KAAK,IAAI,CAAC;AAAA,QACrD,CAAC;AAAA,MACH,CAAC;AACD,UAAI,SAAS,IAAI;AACf,gBAAQ,SAAS,OAAO,WAAW,IAAI,SAAS;AAChD,eAAO,EAAE,IAAI,KAAK;AAAA,MACpB;AACA,YAAM,UAAU,MAAM,qBAAqB,QAAQ;AACnD,UAAI,SAAS,SAAS,IAAK,SAAQ,SAAS,OAAO,WAAW,IAAI,SAAS;AAC3E,aAAO,EAAE,IAAI,OAAO,SAAS,SAAS,WAAW,KAAK,SAAS,QAAQ,QAAQ;AAAA,IACjF,SAAS,OAAO;AACd,UAAI,WAAW,OAAO,SAAS;AAC7B,eAAO,EAAE,IAAI,OAAO,SAAS,OAAO,SAAS,mCAAmC;AAAA,MAClF;AACA,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,SAAS;AAAA,QACT,SAAS,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MACpD;AAAA,IACF,UAAE;AACA,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF;AACF;;;ACjIA,SAAS,eAAAI,cAAa,WAAAC,UAAS,YAAAC,kBAAgB;AAa/C,SAAS,2BAA2B,MAAyB,aAAuC;AAClG,MAAI,YAAY,WAAW,UAAW,QAAO;AAC7C,QAAM,YAAY,oCAAoC,WAAW;AACjE,MAAI,CAAC,UAAW,QAAO;AACvB,SAAO,KAAK,KAAK,CAAC,SAChB,KAAK,OAAO,YAAY,MACxB,KAAK,WAAW,aAChB,oCAAoC,IAAI,MAAM,SAAS;AAC3D;AAKO,SAAS,sBAAsB,MAAyB,aAAiD;AAC9G,QAAM,QAAQ,KAAK,UAAU,CAAC,SAAS,KAAK,OAAO,YAAY,EAAE;AACjE,MAAI,UAAU,IAAI;AAChB,QAAI,2BAA2B,MAAM,WAAW,EAAG,QAAO;AAC1D,WAAO,CAAC,GAAG,MAAM,WAAW;AAAA,EAC9B;AACA,QAAM,WAAW,KAAK,KAAK;AAC3B,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI,4BAA4B,SAAS,MAAM,GAAG;AAChD,QACE,SAAS,WAAW,YAAY,WAC/B,CAAC,SAAS,WAAW,YAAY,WAAW,CAAC,SAAS,gBAAgB,YAAY,eACnF;AACA,YAAMC,QAAO,CAAC,GAAG,IAAI;AACrB,MAAAA,MAAK,KAAK,IAAI,EAAE,GAAG,UAAU,GAAG,YAAY;AAC5C,aAAOA;AAAA,IACT;AACA,WAAO;AAAA,EACT;AACA,QAAM,OAAO,CAAC,GAAG,IAAI;AACrB,OAAK,KAAK,IAAI;AACd,SAAO;AACT;AAIO,SAAS,sBAAsB,MAAyB,QAAkD;AAC/G,QAAM,QAAQ,KAAK,UAAU,CAAC,SAAS,KAAK,OAAO,OAAO,EAAE;AAC5D,QAAM,WAAW,KAAK,KAAK;AAC3B,MAAI,CAAC,YAAY,SAAS,WAAW,UAAW,QAAO;AACvD,QAAM,OAAO,CAAC,GAAG,IAAI;AACrB,OAAK,KAAK,IAAI;AAAA,IACZ,GAAG;AAAA,IACH,QAAQ,gBAAgB,OAAO,MAAM;AAAA,IACrC,GAAI,OAAO,SAAS,EAAE,cAAc,OAAO,OAAO,IAAI,CAAC;AAAA,EACzD;AACA,SAAO;AACT;AAGO,SAAS,uBACd,MACA,IACA,QACA,SACmB;AACnB,QAAM,QAAQ,KAAK,UAAU,CAAC,SAAS,KAAK,OAAO,EAAE;AACrD,QAAM,WAAW,KAAK,KAAK;AAC3B,MAAI,CAAC,YAAY,SAAS,WAAW,UAAW,QAAO;AACvD,QAAM,OAAO,CAAC,GAAG,IAAI;AACrB,OAAK,KAAK,IAAI,EAAE,GAAG,UAAU,QAAQ,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC,EAAG;AACrE,SAAO;AACT;AAIO,SAAS,mCACd,MACA,QACmB;AACnB,MAAI,CAAC,KAAK,KAAK,CAAC,SAAS,KAAK,WAAW,SAAS,EAAG,QAAO;AAC5D,SAAO,KAAK,IAAI,CAAC,SAAU,KAAK,WAAW,YAAY,EAAE,GAAG,MAAM,OAAO,IAAI,IAAK;AACpF;AAmBO,SAAS,wBACd,MACA,aACA,UAA0C,CAAC,GACxB;AACnB,MAAI,OAAO;AACX,aAAW,WAAW,aAAa;AACjC,UAAM,cAAc,2BAA2B,OAAO;AACtD,UAAM,QAAQ,KAAK,UAAU,CAAC,SAAS,KAAK,OAAO,YAAY,EAAE;AACjE,QAAI,UAAU,IAAI;AAChB,aAAO,sBAAsB,MAAM,WAAW;AAC9C;AAAA,IACF;AACA,UAAM,YAAY,oCAAoC,WAAW;AACjE,UAAM,WAAW,YACb,KAAK,UAAU,CAAC,SAAS,KAAK,WAAW,aAAa,oCAAoC,IAAI,MAAM,SAAS,IAC7G;AACJ,QAAI,aAAa,IAAI;AACnB,aAAO,CAAC,GAAG,MAAM,WAAW;AAC5B;AAAA,IACF;AACA,WAAO,CAAC,GAAG,IAAI;AACf,SAAK,QAAQ,IAAI;AAAA,EACnB;AACA,MAAI,QAAQ,SAAS,WAAW;AAC9B,UAAM,iBAAiB,IAAI,IAAI,YAAY,IAAI,CAAC,YAAY,QAAQ,EAAE,CAAC;AACvE,WAAO,KAAK,IAAI,CAAC,SACf,KAAK,WAAW,aAAa,CAAC,eAAe,IAAI,KAAK,EAAE,IACpD,EAAE,GAAG,MAAM,QAAQ,WAAoB,IACvC,IAAI;AAAA,EACZ;AACA,SAAO;AACT;AAKO,SAAS,wBACd,MACA,WACmB;AACnB,SAAO,UAAU,OAAO,uBAAuB,IAAI;AACrD;AA4BO,SAAS,oBAAoB,UAAsC,CAAC,GAA8B;AACvG,QAAM,CAAC,cAAc,eAAe,IAAIC,WAA4B,CAAC,CAAC;AAEtE,QAAM,SAASC,aAAY,CAAC,gBAAiC;AAC3D,oBAAgB,CAAC,SAAS,sBAAsB,MAAM,WAAW,CAAC;AAAA,EACpE,GAAG,CAAC,CAAC;AACL,QAAM,cAAcA,aAAY,CAAC,WAAkC;AACjE,oBAAgB,CAAC,SAAS,sBAAsB,MAAM,MAAM,CAAC;AAAA,EAC/D,GAAG,CAAC,CAAC;AACL,QAAM,eAAeA,aAAY,CAAC,IAAY,QAAmD,YAAiC;AAChI,oBAAgB,CAAC,SAAS,uBAAuB,MAAM,IAAI,QAAQ,OAAO,CAAC;AAAA,EAC7E,GAAG,CAAC,CAAC;AACL,QAAM,UAAUA,aAAY,CAAC,aAAuC,mBAAoD;AACtH,oBAAgB,CAAC,SAAS,wBAAwB,MAAM,aAAa;AAAA,MACnE,MAAM,gBAAgB,QAAQ,QAAQ;AAAA,IACxC,CAAC,CAAC;AAAA,EACJ,GAAG,CAAC,QAAQ,IAAI,CAAC;AACjB,QAAM,UAAUA,aAAY,CAAC,cAAiC;AAC5D,oBAAgB,CAAC,SAAS,wBAAwB,MAAM,SAAS,CAAC;AAAA,EACpE,GAAG,CAAC,CAAC;AACL,QAAM,qBAAqBA,aAAY,CAAC,WAAmE;AACzG,oBAAgB,CAAC,SAAS,mCAAmC,MAAM,MAAM,CAAC;AAAA,EAC5E,GAAG,CAAC,CAAC;AACL,QAAM,QAAQA,aAAY,MAAM,gBAAgB,CAAC,CAAC,GAAG,CAAC,CAAC;AAEvD,QAAM,UAAUC,SAAQ,MAAM,aAAa,OAAO,CAAC,SAAS,KAAK,WAAW,SAAS,GAAG,CAAC,YAAY,CAAC;AAEtG,SAAO,EAAE,cAAc,SAAS,QAAQ,aAAa,cAAc,SAAS,SAAS,oBAAoB,MAAM;AACjH;;;ACjNA,SAAS,eAAAC,cAAa,WAAAC,UAAS,UAAAC,SAAQ,YAAAC,kBAAgB;AA2BvD,IAAM,oBAAoB;AAE1B,SAAS,cAAc,MAAgC;AACrD,SAAO,EAAE,IAAI,KAAK,MAAM,OAAO,KAAK,MAAM,QAAQ,KAAK,MAAM,MAAM,kBAAkB;AACvF;AAEA,SAAS,cAAc,MAAgC;AACrD,SAAO,EAAE,MAAM,KAAK,IAAI,MAAM,KAAK,MAAM;AAC3C;AAWO,SAAS,iBACd,OACA,OACA,OACe;AACf,QAAM,IAAI,MAAM,KAAK,EAAE,YAAY;AACnC,MAAI,CAAC,EAAG,QAAO,MAAM,MAAM,GAAG,KAAK;AACnC,QAAM,SAAwD,CAAC;AAC/D,aAAW,QAAQ,OAAO;AACxB,UAAM,OAAO,KAAK,KAAK,YAAY;AACnC,QAAI,KAAK,WAAW,CAAC,GAAG;AACtB,aAAO,KAAK,EAAE,MAAM,MAAM,EAAE,CAAC;AAC7B;AAAA,IACF;AACA,QAAI,KAAK,SAAS,CAAC,GAAG;AACpB,aAAO,KAAK,EAAE,MAAM,MAAM,EAAE,CAAC;AAC7B;AAAA,IACF;AACA,QAAI,KAAK,KAAK,YAAY,EAAE,SAAS,CAAC,GAAG;AACvC,aAAO,KAAK,EAAE,MAAM,MAAM,EAAE,CAAC;AAAA,IAC/B;AAAA,EACF;AACA,SAAO,KAAK,CAAC,GAAG,MAAM;AACpB,QAAI,EAAE,SAAS,EAAE,KAAM,QAAO,EAAE,OAAO,EAAE;AACzC,QAAI,EAAE,KAAK,KAAK,WAAW,EAAE,KAAK,KAAK,OAAQ,QAAO,EAAE,KAAK,KAAK,SAAS,EAAE,KAAK,KAAK;AACvF,WAAO,EAAE,KAAK,KAAK,cAAc,EAAE,KAAK,IAAI;AAAA,EAC9C,CAAC;AACD,SAAO,OAAO,MAAM,GAAG,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AACjD;AAYA,IAAM,iBAAiB;AAKhB,IAAM,wBAAwB;AAQ9B,IAAM,yBAAyB,IAAI,KAAK;AAIxC,IAAM,6BAA6B;AAqC1C,SAAS,aAAa,OAAmB,UAA0B;AACjE,UAAQ,MAAM,MAAM;AAAA,IAClB,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,wBAAwB,MAAM,OAAO;AAAA,IAC9C,KAAK;AACH,aAAO;AAAA,EACX;AACF;AAGO,SAAS,gBAAgB,SAAwD;AACtF,QAAM;AAAA,IACJ;AAAA,IACA,QAAQ;AAAA,IACR,iBAAiB;AAAA,IACjB,YAAY;AAAA,EACd,IAAI;AACJ,QAAM,YAAY,QAAQ,aAAa;AAEvC,QAAM,CAAC,OAAO,QAAQ,IAAIA,WAAqB,EAAE,MAAM,OAAO,CAAC;AAS/D,QAAM,WAAWD,QAAO,KAAK;AAC7B,WAAS,UAAU;AACnB,QAAM,cAAcA,QAAmC,IAAI;AAC3D,QAAM,CAAC,UAAU,WAAW,IAAIC,WAAwB,CAAC,CAAC;AAE1D,QAAM,OAAOH,aAAY,MAA2B;AAClD,QAAI,YAAY,QAAS,QAAO,YAAY;AAC5C,QAAI,SAAS,QAAQ,SAAS,QAAQ;AACpC,eAAS,UAAU,EAAE,MAAM,UAAU;AACrC,eAAS,SAAS,OAAO;AAAA,IAC3B;AACA,UAAM,WAAW,YAAiC;AAChD,UAAI;AACJ,UAAI;AACF,cAAM,MAAM,MAAM,UAAU,QAAQ;AACpC,YAAI,CAAC,IAAI,IAAI;AACX,iBAAO,EAAE,MAAM,SAAS,SAAS,QAAQ,IAAI,MAAM,IAAI,aAAa,KAAK,IAAI,EAAE;AAAA,QACjF,OAAO;AACL,gBAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,iBACE,KAAK,WAAW,YACZ,EAAE,MAAM,WAAW,aAAa,KAAK,IAAI,EAAE,IAC3C;AAAA,YACE,MAAM;AAAA,YACN,OAAQ,KAAgC;AAAA,YACxC,WAAY,KAAgC;AAAA,YAC5C,WAAW,KAAK,IAAI;AAAA,UACtB;AAAA,QACR;AAAA,MACF,SAAS,KAAK;AACZ,eAAO,EAAE,MAAM,SAAS,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,GAAG,aAAa,KAAK,IAAI,EAAE;AAAA,MAC7G;AACA,eAAS,UAAU;AACnB,eAAS,IAAI;AACb,kBAAY,UAAU;AACtB,aAAO;AAAA,IACT,GAAG;AACH,gBAAY,UAAU;AACtB,WAAO;AAAA,EACT,GAAG,CAAC,WAAW,QAAQ,CAAC;AAMxB,QAAM,UAAUA,aAAY,YAA2B;AACrD,UAAM,KAAK;AAAA,EACb,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,aAAaA;AAAA,IACjB,OAAO,UAA0C;AAC/C,UAAI,UAAU,SAAS;AAKvB,UAAI,QAAQ,SAAS,UAAU,QAAQ,SAAS,WAAW;AACzD,kBAAU,MAAM,KAAK;AAAA,MACvB,WAAW,QAAQ,SAAS,SAAS;AACnC,YAAI,KAAK,IAAI,IAAI,QAAQ,YAAY,eAAgB,MAAK,KAAK;AAAA,MACjE,WAAW,KAAK,IAAI,IAAI,QAAQ,cAAc,gBAAgB;AAC5D,aAAK,KAAK;AAAA,MACZ;AACA,UAAI,QAAQ,SAAS,QAAS,QAAO,CAAC;AACtC,aAAO,iBAAiB,QAAQ,OAAO,OAAO,KAAK,EAAE,IAAI,aAAa;AAAA,IACxE;AAAA,IACA,CAAC,MAAM,OAAO,cAAc;AAAA,EAC9B;AAEA,QAAM,mBAAmBA,aAAY,CAAC,UAAyB;AAC7D,gBAAY,MAAM,OAAO,CAAC,SAAS,KAAK,SAAS,UAAa,KAAK,SAAS,iBAAiB,EAAE,IAAI,aAAa,CAAC;AAAA,EACnH,GAAG,CAAC,CAAC;AAEL,QAAM,gBAAgBA,aAAY,MAAM,YAAY,CAAC,CAAC,GAAG,CAAC,CAAC;AAE3D,QAAM,UAAUC;AAAA,IACd,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,WAAW,aAAa,OAAO,SAAS;AAAA,IAC1C;AAAA,IACA,CAAC,YAAY,kBAAkB,OAAO,SAAS;AAAA,EACjD;AAEA,SAAO,EAAE,SAAS,UAAU,eAAe,QAAQ;AACrD;;;AC3OA,IAAM,yBAAyB;AAI/B,IAAM,YAAY;AAiBX,SAAS,sBACd,SACA,OACmE;AACnE,QAAM,UAAU,oBAAI,IAAqB;AACzC,MAAI,CAAC,QAAS,QAAO,EAAE,UAAU,CAAC,GAAG,QAAQ;AAC7C,MAAI,MAAM,WAAW,EAAG,QAAO,EAAE,UAAU,CAAC,EAAE,MAAM,QAAQ,MAAM,QAAQ,CAAC,GAAG,QAAQ;AAEtF,QAAM,aAAa,MAChB,IAAI,CAAC,UAAU,EAAE,MAAM,OAAO,IAAI,KAAK,IAAI,GAAG,EAAE,EAChD,KAAK,CAAC,GAAG,MAAM,EAAE,MAAM,SAAS,EAAE,MAAM,MAAM;AAEjD,QAAM,WAAiC,CAAC;AACxC,MAAI,SAAS;AACb,MAAI,YAAY;AAChB,SAAO,SAAS,QAAQ,QAAQ;AAC9B,QAAI,QAAQ,MAAM,MAAM,KAAK;AAC3B,gBAAU;AACV;AAAA,IACF;AACA,UAAM,WAAW,SAAS,IAAI,QAAQ,SAAS,CAAC,IAAI;AACpD,QAAI,YAAY,UAAU,KAAK,QAAQ,GAAG;AACxC,gBAAU;AACV;AAAA,IACF;AACA,UAAM,YAAY,WAAW,KAAK,CAAC,EAAE,MAAM,MAAM,QAAQ,WAAW,OAAO,MAAM,CAAC;AAClF,QAAI,CAAC,WAAW;AACd,gBAAU;AACV;AAAA,IACF;AACA,UAAM,SAAS,SAAS,UAAU,MAAM;AACxC,UAAM,WAAW,SAAS,QAAQ,SAAS,QAAQ,MAAM,IAAI;AAC7D,QAAI,YAAY,uBAAuB,KAAK,QAAQ,GAAG;AACrD,gBAAU;AACV;AAAA,IACF;AAEA,QAAI,SAAS,UAAW,UAAS,KAAK,EAAE,MAAM,QAAQ,MAAM,QAAQ,MAAM,WAAW,MAAM,EAAE,CAAC;AAC9F,aAAS,KAAK,EAAE,MAAM,WAAW,MAAM,UAAU,OAAO,MAAM,UAAU,KAAK,CAAC;AAC9E,YAAQ,IAAI,UAAU,IAAI;AAC1B,aAAS;AACT,gBAAY;AAAA,EACd;AACA,MAAI,YAAY,QAAQ,OAAQ,UAAS,KAAK,EAAE,MAAM,QAAQ,MAAM,QAAQ,MAAM,SAAS,EAAE,CAAC;AAE9F,SAAO,EAAE,UAAU,QAAQ;AAC7B;;;ACzFA,SAAS,eAAAG,cAAa,aAAAC,YAAW,YAAAC,kBAAgC;AA0G3D,SAsSQ,YAAAC,WAtSR,OAAAC,MAeF,QAAAC,aAfE;AA/FN,IAAM,gBAAgB,oBAAI,IAAI,CAAC,WAAW,SAAS,CAAC;AACpD,IAAM,cAAc,oBAAI,IAAI,CAAC,aAAa,QAAQ,WAAW,CAAC;AAC9D,IAAM,iBAAiB,oBAAI,IAAI,CAAC,UAAU,SAAS,aAAa,SAAS,CAAC;AAGnE,SAAS,aAAa,QAA8B;AACzD,QAAM,IAAI,OAAO,YAAY;AAC7B,MAAI,cAAc,IAAI,CAAC,EAAG,QAAO;AACjC,MAAI,YAAY,IAAI,CAAC,EAAG,QAAO;AAC/B,MAAI,eAAe,IAAI,CAAC,EAAG,QAAO;AAClC,SAAO;AACT;AAGO,SAAS,mBAAmB,SAAiC;AAClE,MAAI,YAAY,UAAa,CAAC,SAAS,OAAO,KAAK,WAAW,EAAG,QAAO;AACxE,SAAO,UAAU,OAAO,IAAI,QAAQ,QAAQ,CAAC,CAAC,KAAK,IAAI,QAAQ,QAAQ,CAAC,CAAC;AAC3E;AAGO,SAAS,uBAAuB,YAAoC;AACzE,MAAI,eAAe,UAAa,CAAC,SAAS,UAAU,KAAK,aAAa,EAAG,QAAO;AAChF,QAAM,eAAe,KAAK,MAAM,aAAa,GAAI;AACjD,MAAI,eAAe,GAAI,QAAO,GAAG,YAAY;AAC7C,QAAM,UAAU,KAAK,MAAM,eAAe,EAAE;AAC5C,QAAM,UAAU,eAAe;AAC/B,MAAI,UAAU,GAAI,QAAO,GAAG,OAAO,KAAK,OAAO,OAAO,EAAE,SAAS,GAAG,GAAG,CAAC;AACxE,SAAO,GAAG,KAAK,MAAM,UAAU,EAAE,CAAC,KAAK,OAAO,UAAU,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC;AAC9E;AAmBO,SAAS,mBACd,UACA,UACuB;AACvB,QAAM,SAAS,oBAAI,IAAiC;AACpD,aAAW,OAAO,SAAU,QAAO,IAAI,IAAI,QAAQ,GAAG;AACtD,aAAW,OAAO,SAAU,QAAO,IAAI,IAAI,QAAQ,GAAG;AACtD,SAAO,CAAC,GAAG,OAAO,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,KAAK,MAAM,EAAE,SAAS,IAAI,KAAK,MAAM,EAAE,SAAS,CAAC;AAC9F;AAeO,SAAS,gBAAgB,OAAkC;AAChE,QAAM,QAAQ,MAAM,UAAU,IAAI,MAAM,UAAU;AAClD,SAAO,CAAC,GAAG,MAAM,KAAK,EACnB,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,OAAO,EACpC,IAAI,CAAC,SAAS;AACb,UAAM,OAAO,KAAK,QAAQ,CAAC;AAC3B,UAAM,SACJ,KAAK,OAAO,SAAU,OAAO,KAAK,WAAW,YAAY,aAAa,KAAK,MAAM,MAAM;AACzF,WAAO;AAAA,MACL,MAAM,KAAK;AAAA,MACX,MAAM,KAAK;AAAA,MACX,WAAW,KAAK,IAAI,GAAG,KAAK,IAAI,KAAM,KAAK,UAAU,QAAS,GAAG,CAAC;AAAA,MAClE,UAAU,KAAK,IAAI,KAAK,KAAK,IAAI,MAAO,KAAK,QAAQ,KAAK,WAAW,QAAS,GAAG,CAAC;AAAA,MAClF,eAAe,KAAK,KAAK,QAAQ,KAAK,WAAW,KAAM,QAAQ,CAAC,CAAC,IAAI,KAAK,SAAS,MAAM,EAAE;AAAA,MAC3F,QAAQ,KAAK,WAAW;AAAA,MACxB,IAAI,CAAC;AAAA,IACP;AAAA,EACF,CAAC;AACL;AAIA,SAAS,aAAa,EAAE,UAAU,GAA2B;AAC3D,SACE,gBAAAD,KAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACvJ,0BAAAA,KAAC,UAAK,GAAE,gBAAe,GACzB;AAEJ;AAEA,SAAS,aAAa,EAAE,UAAU,GAA2B;AAC3D,SACE,gBAAAA,KAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACvJ,0BAAAA,KAAC,UAAK,GAAE,wCAAuC,GACjD;AAEJ;AAEA,SAAS,UAAU,EAAE,UAAU,GAA2B;AACxD,SACE,gBAAAC,MAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACvJ;AAAA,oBAAAD,KAAC,UAAK,GAAE,KAAI,GAAE,KAAI,OAAM,MAAK,QAAO,MAAK,IAAG,KAAI;AAAA,IAChD,gBAAAA,KAAC,UAAK,GAAE,2DAA0D;AAAA,KACpE;AAEJ;AAKA,SAAS,YAAY,EAAE,QAAQ,GAAwB;AACrD,QAAM,CAAC,QAAQ,SAAS,IAAIE,WAAS,KAAK;AAC1C,QAAM,OAAOC,aAAY,MAAM;AAC7B,SAAK,UAAU,WAAW,UAAU,OAAO,EAAE;AAAA,MAC3C,MAAM;AACJ,kBAAU,IAAI;AACd,mBAAW,MAAM,UAAU,KAAK,GAAG,IAAI;AAAA,MACzC;AAAA,MACA,MAAM;AAAA,MAAC;AAAA,IACT;AAAA,EACF,GAAG,CAAC,OAAO,CAAC;AACZ,SACE,gBAAAF;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,SAAS;AAAA,MACT,OAAM;AAAA,MACN,cAAW;AAAA,MACX,WAAU;AAAA,MAEV;AAAA,wBAAAD,KAAC,UAAK,WAAU,YAAY,mBAAQ;AAAA,QACpC,gBAAAA,KAAC,aAAU,WAAU,oBAAmB;AAAA,QACvC,UAAU,gBAAAA,KAAC,UAAK,WAAU,oCAAmC,oBAAM;AAAA;AAAA;AAAA,EACtE;AAEJ;AAUA,SAAS,UAAU,EAAE,KAAK,GAA2B;AACnD,SACE,gBAAAC,MAAC,UAAK,WAAU,4BACd;AAAA,oBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,eAAW;AAAA,QACX,WAAW,iCACT,SAAS,SAAS,eAAe,SAAS,OAAO,eAAe,SAAS,UAAU,mBAAmB,wBACxG;AAAA;AAAA,IACF;AAAA,IACA,gBAAAA,KAAC,UAAK,WAAU,WAAW,gBAAK;AAAA,KAClC;AAEJ;AAkBA,SAAS,SAAS,EAAE,MAAM,QAAQ,KAAK,GAAoD;AACzF,SACE,gBAAAC,MAAC,UAAK,WAAU,2BACd;AAAA,oBAAAD,KAAC,UAAK,WAAW,OAAO,8BAA8B,eAAe,eAAa,OAAO,cAAc,QACpG,gBACH;AAAA,IACA,gBAAAC,MAAC,UAAK,WAAU,yBAAwB;AAAA;AAAA,MAAI;AAAA,OAAO;AAAA,KACrD;AAEJ;AAIA,IAAM,YAAkD;AAAA,EACtD,UAAU;AAAA,EACV,OAAO;AAAA,EACP,MAAM;AACR;AAQO,SAAS,cAAc,EAAE,MAAM,GAAuB;AAC3D,QAAM,OAAO,gBAAgB,KAAK;AAClC,MAAI,KAAK,WAAW,EAAG,QAAO;AAC9B,QAAM,OAAO,mBAAmB,MAAM,OAAO;AAC7C,SACE,gBAAAA,MAAC,SAAI,WAAU,aACZ;AAAA,SAAK,IAAI,CAAC,KAAK,MACd,gBAAAA,MAAC,SAAY,WAAU,wEACrB;AAAA,sBAAAD,KAAC,UAAK,WAAU,oDAAmD,OAAO,IAAI,MAC3E,cAAI,MACP;AAAA,MACA,gBAAAA,KAAC,SAAI,WAAU,wCACb,0BAAAA;AAAA,QAAC;AAAA;AAAA,UACC,WAAW,iCAAiC,IAAI,KAAK,UAAU,IAAI,IAAI,IAAI,mBAAmB,IAAI,IAAI,SAAS,eAAe,EAAE;AAAA,UAChI,OAAO,EAAE,MAAM,GAAG,IAAI,SAAS,KAAK,OAAO,GAAG,IAAI,QAAQ,IAAI;AAAA;AAAA,MAChE,GACF;AAAA,MACA,gBAAAA,KAAC,UAAK,WAAU,oEAAoE,cAAI,eAAc;AAAA,SAV9F,CAWV,CACD;AAAA,IACD,gBAAAC,MAAC,OAAE,WAAU,6EACT;AAAA,aAAM,UAAU,KAAM,QAAQ,CAAC;AAAA,MAAE;AAAA,MAAE,OAAO,SAAM,IAAI,KAAK;AAAA,OAC7D;AAAA,KACF;AAEJ;AAmBA,SAAS,QAAQ,EAAE,KAAK,aAAa,GAAqD;AACxF,QAAM,UAAU,gBAAgB,YAAY;AAC5C,QAAM,OAAO,aAAa,IAAI,MAAM;AACpC,QAAM,OAAO,mBAAmB,IAAI,OAAO;AAC3C,QAAM,WAAW,uBAAuB,IAAI,UAAU;AACtD,SACE,gBAAAA,MAAC,SAAI,WAAU,qDAAoD,OAAO,SACxE;AAAA,oBAAAD,KAAC,aAAU,MAAY;AAAA,IACvB,gBAAAA,KAAC,YAAS,MAAM,IAAI,MAAM,QAAQ,IAAI,QAAQ,MAAM,SAAS,QAAQ;AAAA,IACpE,SAAS,WAAW,IAAI,cAAc,UAAa,IAAI,UAAU,WAChE,gBAAAA,KAAC,UAAK,WAAU,oFACb,WAAC,IAAI,cAAc,SAAY,QAAQ,IAAI,SAAS,KAAK,MAAM,IAAI,SAAS,IAAI,EAC9E,OAAO,OAAO,EACd,KAAK,QAAK,GACf;AAAA,IAEF,gBAAAC,MAAC,UAAK,WAAU,8FACb;AAAA,eAAS,UAAU,SAAS,QAAQ,gBAAAD,KAAC,UAAM,cAAI,QAAO;AAAA,MACtD,QAAQ,gBAAAA,KAAC,UAAM,gBAAK;AAAA,MACpB,YAAY,gBAAAA,KAAC,UAAM,oBAAS;AAAA,OAC/B;AAAA,KACF;AAEJ;AAYO,SAAS,oBAAoB,EAAE,UAAU,WAAW,MAAM,GAA6B;AAC5F,QAAM,CAAC,UAAU,WAAW,IAAIE,WAAS,KAAK;AAC9C,MAAI,SAAS,WAAW,EAAG,QAAO;AAElC,SACE,gBAAAD,MAAC,SAAI,WAAU,oCACZ;AAAA,aAAS,IAAI,CAAC,KAAK,UAClB,gBAAAD,KAAC,WAAyB,KAAU,cAAc,SAApC,IAAI,MAAuC,CAC1D;AAAA,IACD,gBAAAC;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;AAAA,QACpC,WAAU;AAAA,QAEV;AAAA,0BAAAD,KAAC,gBAAa,WAAW,gCAAgC,WAAW,eAAe,EAAE,IAAI;AAAA,UAAE;AAAA;AAAA;AAAA,IAE7F;AAAA,IACC,YACC,gBAAAA,KAAC,SAAI,WAAU,oDACb,0BAAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,sBAAsB,UAAU;AAAA,UACrC,GAAI,cAAc,SAAY,EAAE,UAAU,IAAI,CAAC;AAAA,UAC/C,GAAI,UAAU,SAAY,EAAE,MAAM,IAAI,CAAC;AAAA,QACzC,CAAC;AAAA;AAAA,IACH,GACF;AAAA,KAEJ;AAEJ;AAeA,SAAS,YAAY;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,QAAM,UAAU,gBAAgB,YAAY;AAC5C,QAAM,CAAC,MAAM,OAAO,IAAIE,WAAS,KAAK;AACtC,QAAM,OAAO,aAAa,OAAO,MAAM;AACvC,QAAM,OAAO,mBAAmB,OAAO,OAAO;AAC9C,QAAM,WAAW,uBAAuB,OAAO,UAAU;AAEzD,SACE,gBAAAD,MAAC,SAAI,WAAU,2DAA0D,OAAO,SAC9E;AAAA,oBAAAA,MAAC,YAAO,MAAK,UAAS,SAAS,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,WAAU,gEACjE;AAAA,sBAAAD,KAAC,aAAU,MAAY;AAAA,MACvB,gBAAAA,KAAC,YAAS,MAAM,OAAO,MAAM,QAAQ,OAAO,QAAQ,MAAM,SAAS,QAAQ;AAAA,MAC1E,SAAS,WAAW,OAAO,cAAc,UAAa,OAAO,UAAU,WACtE,gBAAAA,KAAC,UAAK,WAAU,kFACb,WAAC,OAAO,cAAc,SAAY,QAAQ,OAAO,SAAS,KAAK,MAAM,OAAO,SAAS,IAAI,EACvF,OAAO,OAAO,EACd,KAAK,QAAK,GACf;AAAA,MAEF,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,WAAW,yDACT,SAAS,OACL,+BACA,SAAS,UACP,uCACA,SAAS,SACP,+BACA,oCACV;AAAA,UAEC,iBAAO;AAAA;AAAA,MACV;AAAA,MACC,QAAQ,gBAAAA,KAAC,UAAK,WAAU,iEAAiE,gBAAK;AAAA,MAC/F,gBAAAA,KAAC,gBAAa,WAAW,+DAA+D,OAAO,eAAe,EAAE,IAAI;AAAA,OACtH;AAAA,IACC,QACC,gBAAAC,MAAC,SAAI,WAAU,kDACZ;AAAA,aAAO,eAAe,UACrB,gBAAAD,KAAC,SAAI,WAAU,oDACb,0BAAAA,KAAC,iBAAc,OAAO,sBAAsB,CAAC,MAAM,CAAC,GAAG,GACzD;AAAA,MAEF,gBAAAC,MAAC,QAAG,WAAU,+DACZ;AAAA,wBAAAD,KAAC,QAAG,WAAU,4BAA2B,kBAAI;AAAA,QAC7C,gBAAAA,KAAC,QAAG,WAAU,kCAAkC,iBAAO,QAAO;AAAA,QAC9D,gBAAAA,KAAC,QAAG,WAAU,4BAA2B,qBAAO;AAAA,QAChD,gBAAAA,KAAC,QAAG,WAAU,yBAAyB,cAAI,KAAK,OAAO,SAAS,EAAE,eAAe,GAAE;AAAA,QAClF,YACC,gBAAAC,MAAAF,WAAA,EACE;AAAA,0BAAAC,KAAC,QAAG,WAAU,4BAA2B,sBAAQ;AAAA,UACjD,gBAAAA,KAAC,QAAG,WAAU,yBAAyB,oBAAS;AAAA,WAClD;AAAA,QAED,OAAO,WACN,gBAAAC,MAAAF,WAAA,EACE;AAAA,0BAAAC,KAAC,QAAG,WAAU,4BAA2B,mBAAK;AAAA,UAC9C,gBAAAA,KAAC,QAAG,WAAU,WACZ,0BAAAA,KAAC,eAAY,SAAS,OAAO,SAAS,GACxC;AAAA,WACF;AAAA,SAEJ;AAAA,MACC,OAAO,cAAc,mBAAmB,OAAO,YAAY,MAAM;AAAA,OACpE;AAAA,KAEJ;AAEJ;AAkBO,SAAS,mBAAmB,EAAE,eAAe,kBAAkB,QAAQ,kBAAkB,aAAa,qBAAqB,GAA4B;AAC5J,QAAM,CAAC,MAAM,OAAO,IAAIE,WAAgC,CAAC,CAAC;AAC1D,QAAM,CAAC,QAAQ,SAAS,IAAIA,WAA6B,MAAS;AAClE,QAAM,CAAC,QAAQ,SAAS,IAAIA,WAA8B,SAAS;AACnE,QAAM,CAAC,OAAO,QAAQ,IAAIA,WAAwB,IAAI;AAEtD,QAAM,OAAOC;AAAA,IACX,OAAO,SAAkB;AACvB,gBAAU,SAAS;AACnB,eAAS,IAAI;AACb,UAAI;AACF,cAAM,OAAO,MAAM,cAAc,IAAI;AACrC,gBAAQ,CAAC,SAAS,mBAAmB,SAAS,SAAY,CAAC,IAAI,MAAM,KAAK,KAAK,CAAC;AAChF,kBAAU,KAAK,UAAU;AACzB,kBAAU,OAAO;AAAA,MACnB,SAAS,GAAG;AACV,iBAAS,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;AACnD,kBAAU,OAAO;AAAA,MACnB;AAAA,IACF;AAAA,IACA,CAAC,aAAa;AAAA,EAChB;AAEA,EAAAC,WAAU,MAAM;AACd,SAAK,KAAK;AAAA,EACZ,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,UAAU,WAAW;AAE3B,SACE,gBAAAH,MAAC,SAAI,WAAU,aACb;AAAA,oBAAAA,MAAC,SAAI,WAAU,2BACb;AAAA,sBAAAD,KAAC,QAAG,WAAU,gCAAgC,iBAAM;AAAA,MACpD,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS,MAAM,KAAK,KAAK;AAAA,UACzB,UAAU;AAAA,UACV,cAAW;AAAA,UACX,WAAU;AAAA,UAEV,0BAAAA,KAAC,gBAAa,WAAW,eAAe,UAAU,iBAAiB,EAAE,IAAI;AAAA;AAAA,MAC3E;AAAA,OACF;AAAA,IACC,WAAW,WACV,gBAAAA,KAAC,OAAE,MAAK,SAAQ,WAAU,+FACvB,iBACH;AAAA,IAED,WAAW,WAAW,KAAK,WAAW,KAAK,gBAAAA,KAAC,OAAE,WAAU,sCAAsC,sBAAW;AAAA,IAO1G,gBAAAA,KAAC,UAAK,MAAK,UAAS,aAAU,UAAS,aAAW,SAAS,WAAU,WAClE,oBAAU,2BAAsB,IACnC;AAAA,IACA,gBAAAA,KAAC,SAAI,WAAU,eAAc,aAAW,SACrC,eAAK,IAAI,CAAC,QAAQ,UACjB,gBAAAA,KAAC,eAAgC,QAAgB,kBAAoC,cAAc,SAAjF,OAAO,MAAiF,CAC3G,GACH;AAAA,IACC,UACC,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS,MAAM,KAAK,KAAK,MAAM;AAAA,QAC/B,UAAU;AAAA,QACV,WAAU;AAAA,QACX;AAAA;AAAA,IAED;AAAA,KAEJ;AAEJ;;;ACpfA,SAAS,eAAAK,cAAa,aAAAC,YAAW,SAAAC,QAAO,UAAAC,SAAQ,YAAAC,kBAAoD;;;ACA7F,IAAM,mBAA+C,CAAC,aAAa,WAAW,YAAY,UAAU;AAc3G,IAAM,aAA2D;AAAA,EAC/D,WAAW;AAAA,IACT,OAAO;AAAA,IACP,SAAS;AAAA,IACT,kBAAkB;AAAA,EACpB;AAAA,EACA,SAAS;AAAA,IACP,OAAO;AAAA,IACP,SAAS;AAAA,IACT,kBAAkB;AAAA,EACpB;AAAA,EACA,UAAU;AAAA,IACR,OAAO;AAAA,IACP,SAAS;AAAA,IACT,kBAAkB;AAAA,EACpB;AAAA,EACA,UAAU;AAAA,IACR,OAAO;AAAA,IACP,SAAS;AAAA,IACT,kBAAkB;AAAA,EACpB;AACF;AAGO,SAAS,oBAAoB,OAA6C;AAC/E,SAAO,WAAW,KAAK;AACzB;AA2CA,IAAM,gBAAoE;AAAA,EACxE,SAAS,EAAE,OAAO,UAAU,QAAQ,0EAAqE;AAAA,EACzG,OAAO,EAAE,OAAO,oBAAoB,QAAQ,gEAAgE;AAAA,EAC5G,SAAS,EAAE,OAAO,kBAAkB,QAAQ,uDAAuD;AACrG;AAGO,SAAS,uBAAuB,UAAsD;AAC3F,SAAO,cAAc,QAAQ;AAC/B;AAEA,IAAM,oBAAwD,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,EAAE;AAG1F,SAAS,yBAAyB,GAAuB,GAA2C;AACzG,SAAO,kBAAkB,CAAC,KAAK,kBAAkB,CAAC,IAAI,IAAI;AAC5D;AAgBO,IAAM,uCAAmE;AAAA,EAC9E,kBAAkB;AAAA,EAClB,gBAAgB;AAClB;AA+BO,SAAS,uBACd,YACA,SAAqC,sCACjB;AACpB,MAAI,cAAc,OAAO,iBAAkB,QAAO;AAClD,MAAI,cAAc,OAAO,eAAgB,QAAO;AAChD,SAAO;AACT;AAGA,SAAS,kBAAkB,OAA4C;AAGrE,SAAO,UAAU,aAAa,UAAU;AAC1C;AAgBO,SAAS,+BAA+B,QAAyC;AACtF,MAAI,OAAO,WAAW,UAAW,QAAO,cAAc,OAAO,KAAK;AAClE,MAAI,OAAO,WAAW,eAAe;AACnC,WAAO,OAAO,oBACV,GAAG,OAAO,KAAK,+BAA0B,OAAO,iBAAiB,KACjE,GAAG,OAAO,KAAK;AAAA,EACrB;AACA,SAAO;AACT;AAOO,SAAS,eAAe,QAA2C;AACxE,QAAM,OAAwB,CAAC;AAC/B,QAAM,UAAU,OAAO,WAAW,CAAC;AACnC,MAAI,OAAO,UAAU,eAAe,QAAQ,WAAW,GAAG;AACxD,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACA,MAAI,OAAO,UAAU,eAAe,OAAO,UAAU,CAAC,GAAG,WAAW,GAAG;AACrE,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AACA,aAAW,UAAU,SAAS;AAC5B,QAAI,OAAO,WAAW,cAAe;AACrC,SAAK,KAAK;AAAA,MACR,MAAM;AAAA,MACN,SAAS,+BAA+B,MAAM,KAAK,GAAG,OAAO,KAAK;AAAA,MAClE;AAAA,IACF,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAIO,SAAS,yBAAyB,QAA8C;AACrF,UAAQ,OAAO,WAAW,CAAC,GAAG,OAAO,CAAC,WAAW,OAAO,WAAW,SAAS;AAC9E;AAgBO,SAAS,0BACd,QACA,SAAqC,sCACjB;AACpB,MAAI,WACF,OAAO,aACN,OAAO,eAAe,SAAY,kBAAkB,OAAO,KAAK,IAAI,uBAAuB,OAAO,YAAY,MAAM;AAEvH,MAAI,OAAO,UAAU,WAAY,YAAW,yBAAyB,UAAU,OAAO;AACtF,aAAW,OAAO,eAAe,MAAM,GAAG;AACxC,eAAW,yBAAyB,UAAU,IAAI,SAAS,uBAAuB,UAAU,SAAS;AAAA,EACvG;AACA,SAAO;AACT;AAYO,SAAS,yBACd,QACA,SAAqC,sCACrC,OAA8B,oBAAI,IAAI,GAClB;AACpB,MAAI,KAAK,IAAI,MAAM,EAAG,QAAO;AAC7B,OAAK,IAAI,MAAM;AACf,MAAI,WAAW,0BAA0B,QAAQ,MAAM;AACvD,aAAW,SAAS,OAAO,UAAU,CAAC,GAAG;AACvC,eAAW,yBAAyB,UAAU,yBAAyB,OAAO,QAAQ,IAAI,CAAC;AAAA,EAC7F;AACA,SAAO;AACT;AAEA,SAAS,aAAa,QAAkC;AACtD,SAAO,OAAO,UAAU,GAAG,OAAO,KAAK,KAAK,OAAO,OAAO,KAAK,OAAO;AACxE;AAEA,SAAS,YAAY,OAAiC;AACpD,SAAO,MAAM,SAAS,MAAM;AAC9B;AAOO,SAAS,mBAAmB,QAAkC;AACnE,QAAM,UAAU,OAAO,WAAW,CAAC;AACnC,QAAM,QAAQ,QAAQ,CAAC;AACvB,QAAM,OAAO,QAAQ,SAAS,IAAI,QAAQ,QAAQ,SAAS,CAAC,eAAe,QAAQ,SAAS,IAAI,MAAM,EAAE,KAAK;AAE7G,UAAQ,OAAO,OAAO;AAAA,IACpB,KAAK;AACH,aAAO,QAAQ,aAAa,aAAa,KAAK,CAAC,GAAG,IAAI,MAAM;AAAA,IAC9D,KAAK;AACH,aAAO,QAAQ,cAAc,aAAa,KAAK,CAAC,MAAM;AAAA,IACxD,KAAK,YAAY;AACf,YAAM,SAAS,OAAO,UAAU,CAAC;AACjC,UAAI,OAAO,WAAY,QAAO,iBAAiB,OAAO,UAAU;AAChE,UAAI,OAAO,WAAW,EAAG,QAAO;AAChC,aAAO,iBAAiB,OAAO,IAAI,WAAW,EAAE,KAAK,IAAI,CAAC;AAAA,IAC5D;AAAA,IACA,KAAK;AACH,aAAO,QACH,oCAAoC,aAAa,KAAK,CAAC,qCACvD;AAAA,EACR;AACF;AAUO,SAAS,mBACd,QACA,UACA,SAAqC,sCAC7B;AACR,QAAM,UAAU,uBAAuB,QAAQ,EAAE;AACjD,MAAI,aAAa,UAAW,QAAO;AAEnC,QAAM,OAAO,eAAe,MAAM;AAClC,MAAI,KAAK,KAAK,CAAC,QAAQ,IAAI,SAAS,WAAW,GAAG;AAChD,WAAO;AAAA,EACT;AACA,MAAI,KAAK,KAAK,CAAC,QAAQ,IAAI,SAAS,WAAW,GAAG;AAChD,WAAO;AAAA,EACT;AACA,MAAI,KAAK,KAAK,CAAC,QAAQ,IAAI,SAAS,oBAAoB,GAAG;AACzD,WAAO;AAAA,EACT;AACA,MAAI,OAAO,UAAU,eAAe,OAAO,WAAW,CAAC,GAAG,WAAW,GAAG;AACtE,WAAO;AAAA,EACT;AAGA,MAAI,OAAO,UAAU,eAAe,OAAO,UAAU,CAAC,GAAG,SAAS,KAAK,0BAA0B,QAAQ,MAAM,MAAM,WAAW;AAC9H,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAOO,SAAS,uBAAuB,QAA0B,UAAsC;AACrG,QAAM,QAAQ,OAAO,QAAQ,KAAK,MAAM,KAAK,uBAAuB,SAAI,OAAO,OAAO;AACtF,QAAM,QAAQ,OAAO,QAAQ,GAAG,OAAO,KAAK,IAAI,KAAK,KAAK;AAC1D,QAAM,QAAQ,oBAAoB,OAAO,KAAK,EAAE;AAChD,QAAM,OAAO,aAAa,YAAY,KAAK,IAAI,uBAAuB,QAAQ,EAAE,KAAK;AACrF,SAAO,SAAS,KAAK,qBAAgB,KAAK,IAAI,IAAI;AACpD;;;ADtTQ,SACE,OAAAC,MADF,QAAAC,aAAA;AA3BR,IAAM,cAA+C;AAAA,EACnD,WAAW;AAAA,EACX,SAAS;AAAA,EACT,UAAU;AAAA,EACV,UAAU;AACZ;AAEA,IAAM,iBAAqD;AAAA,EACzD,SAAS;AAAA,EACT,OAAO;AAAA,EACP,SAAS;AACX;AAEA,SAAS,WAAW,EAAE,OAAO,UAAU,GAAmD;AACxF,QAAM,SAAS;AAAA,IACb,SAAS;AAAA,IACT,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,eAAe;AAAA,IACf;AAAA,EACF;AACA,UAAQ,OAAO;AAAA,IACb,KAAK;AACH,aACE,gBAAAA,MAAC,SAAK,GAAG,QACP;AAAA,wBAAAD,KAAC,UAAK,GAAE,8DAA6D;AAAA,QACrE,gBAAAA,KAAC,cAAS,QAAO,kBAAiB;AAAA,QAClC,gBAAAA,KAAC,UAAK,IAAG,KAAI,IAAG,MAAK,IAAG,MAAK,IAAG,MAAK;AAAA,SACvC;AAAA,IAEJ,KAAK;AACH,aACE,gBAAAC,MAAC,SAAK,GAAG,QACP;AAAA,wBAAAD,KAAC,UAAK,GAAE,6CAA4C;AAAA,QACpD,gBAAAA,KAAC,YAAO,IAAG,MAAK,IAAG,KAAI,GAAE,KAAI;AAAA,SAC/B;AAAA,IAEJ,KAAK;AACH,aACE,gBAAAC,MAAC,SAAK,GAAG,QACP;AAAA,wBAAAD,KAAC,UAAK,IAAG,KAAI,IAAG,KAAI,IAAG,MAAK,IAAG,KAAI;AAAA,QACnC,gBAAAA,KAAC,UAAK,IAAG,KAAI,IAAG,MAAK,IAAG,MAAK,IAAG,MAAK;AAAA,QACrC,gBAAAA,KAAC,UAAK,IAAG,MAAK,IAAG,KAAI,IAAG,KAAI,IAAG,MAAK;AAAA,QACpC,gBAAAA,KAAC,UAAK,IAAG,MAAK,IAAG,KAAI,IAAG,MAAK,IAAG,MAAK;AAAA,SACvC;AAAA,IAEJ,KAAK;AACH,aACE,gBAAAC,MAAC,SAAK,GAAG,QACP;AAAA,wBAAAD,KAAC,UAAK,GAAE,wGAAuG;AAAA,QAC/G,gBAAAA,KAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,OAAM;AAAA,SAClC;AAAA,EAEN;AACF;AAYA,IAAM,aAAa,oBAAI,IAAe;AAWtC,SAAS,mBAAmB,MAAgC;AAC1D,aAAW,SAAS,MAAM,KAAK,UAAU,GAAG;AAC1C,QAAI,SAAS,QAAQ,CAAC,MAAM,KAAK,SAAS,IAAI,EAAG,OAAM,MAAM;AAAA,EAC/D;AACF;AAsCA,IAAM,8BAA8B;AAIpC,SAAS,UAAU;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,QAAM,SAAS,OAAO,UAAU;AAChC,QAAM,aAAa,+BAA+B,MAAM;AACxD,QAAM,WAAW,WAAW,YAAY,iBAAiB,UAAa,OAAO,SAAS;AAEtF,SACE,gBAAAC,MAAC,QAAG,WAAU,0DACZ;AAAA,oBAAAA,MAAC,SAAI,WAAU,iDACb;AAAA,sBAAAD,KAAC,UAAK,WAAU,uCAAuC,iBAAO,OAAM;AAAA,MACnE,OAAO,WAAW,gBAAAA,KAAC,UAAK,WAAU,iCAAiC,iBAAO,SAAQ;AAAA,MAClF,aACE,eACC,gBAAAC;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS,MAAM,aAAa,QAAQ,MAAM;AAAA,UAC1C,WAAU;AAAA,UACX;AAAA;AAAA,YACO,OAAO;AAAA;AAAA;AAAA,MACf,IAEA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,OAAO;AAAA,UACb,QAAO;AAAA,UACP,KAAI;AAAA,UACJ,WAAU;AAAA,UACX;AAAA;AAAA,YACO,OAAO;AAAA;AAAA;AAAA,MACf;AAAA,OAEN;AAAA,IACC,OAAO,SACN,gBAAAA,MAAC,gBAAW,WAAU,0FAAyF;AAAA;AAAA,MAC3G,OAAO;AAAA,MAAM;AAAA,OACjB;AAAA,IAED;AAAA;AAAA;AAAA;AAAA,IAKC,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,WAAW,gBAAgB,WAAW,gBAAgB,qBAAqB,uBAAuB;AAAA,QAEjG;AAAA;AAAA,UACA,WAAW,iBAAiB,iBAC3B,gBAAAD;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS,MAAM,cAAc,QAAQ,MAAM;AAAA,cAC3C,WAAU;AAAA,cACX;AAAA;AAAA,UAED;AAAA;AAAA;AAAA,IAEJ;AAAA,KAEJ;AAEJ;AAUO,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,cAAc;AAAA,EACd,oBAAoB;AAAA,EACpB;AACF,GAAyB;AACvB,QAAM,CAAC,MAAM,OAAO,IAAIE,WAAS,WAAW;AAC5C,QAAM,aAAaC,QAAiC,IAAI;AACxD,QAAM,UAAUA,QAA8B,IAAI;AAClD,QAAM,UAAUC,OAAM;AAEtB,QAAM,WAAW,yBAAyB,QAAQ,gBAAgB;AAClE,QAAM,YAAY,oBAAoB,OAAO,KAAK;AAClD,QAAM,eAAe,uBAAuB,QAAQ;AACpD,QAAM,OAAO,eAAe,MAAM;AAClC,QAAM,UAAU,yBAAyB,MAAM;AAC/C,QAAM,UAAU,OAAO,WAAW,CAAC;AACnC,QAAM,SAAS,OAAO,UAAU,CAAC;AACjC,QAAM,WAAW,OAAO,QAAQ,KAAK,MAAM;AAK3C,QAAM,YAAYC;AAAA,IAChB,CAAC,UAAyC;AACxC,UAAI,MAAM,QAAQ,YAAY,CAAC,KAAM;AACrC,YAAM,gBAAgB;AACtB,cAAQ,KAAK;AACb,iBAAW,SAAS,MAAM;AAAA,IAC5B;AAAA,IACA,CAAC,IAAI;AAAA,EACP;AAIA,QAAM,WAAWA,aAAY,MAAM;AACjC,QAAI,CAAC,KAAM,oBAAmB,QAAQ,OAAO;AAC7C,YAAQ,CAAC,IAAI;AAAA,EACf,GAAG,CAAC,IAAI,CAAC;AAKT,EAAAC,WAAU,MAAM;AACd,UAAM,OAAO,QAAQ;AACrB,QAAI,CAAC,QAAQ,SAAS,KAAM;AAE5B,UAAM,QAAmB,EAAE,MAAM,OAAO,MAAM,QAAQ,KAAK,EAAE;AAC7D,eAAW,IAAI,KAAK;AAEpB,UAAM,gBAAgB,CAAC,UAAiB;AACtC,YAAM,SAAS,MAAM;AACrB,UAAI,kBAAkB,QAAQ,KAAK,SAAS,MAAM,EAAG;AACrD,cAAQ,KAAK;AAAA,IACf;AACA,aAAS,iBAAiB,aAAa,eAAe,IAAI;AAC1D,aAAS,iBAAiB,cAAc,eAAe,IAAI;AAE3D,WAAO,MAAM;AACX,iBAAW,OAAO,KAAK;AACvB,eAAS,oBAAoB,aAAa,eAAe,IAAI;AAC7D,eAAS,oBAAoB,cAAc,eAAe,IAAI;AAAA,IAChE;AAAA,EACF,GAAG,CAAC,IAAI,CAAC;AAET,QAAM,UACJ,gBAAAL,MAAC,UAAK,WAAU,kDACb;AAAA,WAAO,SAAS,gBAAAD,KAAC,UAAK,WAAU,iCAAiC,iBAAO,OAAM;AAAA,IAC/E,gBAAAA,KAAC,UAAK,WAAW,WAAW,4BAA4B,wCACrD,qBAAW,OAAO,UAAU,mBAC/B;AAAA,KACF;AAMF,MAAI,YAAY,GAAG;AACjB,WACE,gBAAAC;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,2BAA2B,aAAa,EAAE;AAAA,QACrD,yBAAuB,OAAO;AAAA,QAC9B,4BAA0B;AAAA,QAEzB;AAAA;AAAA,UACD,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,WAAW,+FAA+F,YAAY,OAAO,KAAK,CAAC;AAAA,cAEnI;AAAA,gCAAAD,KAAC,cAAW,OAAO,OAAO,OAAO,WAAU,WAAU;AAAA,gBACpD,UAAU;AAAA;AAAA;AAAA,UACb;AAAA,UACA,gBAAAA,KAAC,UAAK,WAAU,8CAA8C,6BAAmB,MAAM,GAAE;AAAA;AAAA;AAAA,IAC3F;AAAA,EAEJ;AAEA,SACE,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,WAAW,2BAA2B,aAAa,EAAE;AAAA,MACrD;AAAA,MACA,yBAAuB,OAAO;AAAA,MAC9B,4BAA0B;AAAA,MAE1B;AAAA,wBAAAA,MAAC,UAAK,WAAU,gDACb;AAAA;AAAA,UACD,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,KAAK;AAAA,cACL,MAAK;AAAA,cACL,SAAS;AAAA,cACT,iBAAe;AAAA,cACf,iBAAe;AAAA,cACf,cAAY,uBAAuB,QAAQ,QAAQ;AAAA,cACnD,WAAW,wLAAwL,YAAY,OAAO,KAAK,CAAC;AAAA,cAE5N;AAAA,gCAAAD,KAAC,cAAW,OAAO,OAAO,OAAO,WAAU,WAAU;AAAA,gBACrD,gBAAAA,KAAC,UAAK,eAAW,MAAE,oBAAU,OAAM;AAAA;AAAA;AAAA,UACrC;AAAA,UACC,aAAa;AAAA;AAAA;AAAA,UAIZ,gBAAAA,KAAC,UAAK,eAAW,MAAC,WAAW,uBAAuB,eAAe,QAAQ,CAAC,IACzE,uBAAa,OAChB;AAAA,WAEJ;AAAA,QAEC,QACC,gBAAAC;AAAA,UAAC;AAAA;AAAA,YACC,IAAI;AAAA,YACJ,MAAK;AAAA,YACL,cAAY,uBAAuB,QAAQ,QAAQ;AAAA,YACnD,WAAU;AAAA,YAEV;AAAA,8BAAAA,MAAC,SACC;AAAA,gCAAAD,KAAC,OAAE,WAAU,4CAA4C,6BAAmB,MAAM,GAAE;AAAA,gBACpF,gBAAAC,MAAC,OAAE,WAAW,+BAA+B,eAAe,QAAQ,CAAC,IAClE;AAAA,+BAAa;AAAA,kBAAM;AAAA,kBAAI,mBAAmB,QAAQ,UAAU,gBAAgB;AAAA,mBAC/E;AAAA,gBACC,UAAU,qBAAqB,QAC9B,gBAAAA,MAAC,OAAE,WAAU,qDACV;AAAA,4BAAU;AAAA,kBAAQ;AAAA,mBACrB;AAAA,iBAEJ;AAAA,cAEC,QAAQ,SAAS,KAChB,gBAAAD,KAAC,QAAG,WAAU,eACX,kBAAQ,IAAI,CAAC,QAAQ,UACpB,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBAEC;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA;AAAA;AAAA,gBAJK,GAAG,OAAO,KAAK,IAAI,OAAO,WAAW,EAAE,IAAI,KAAK;AAAA,cAKvD,CACD,GACH;AAAA,cAGD,KACE,OAAO,CAAC,QAAQ,IAAI,SAAS,oBAAoB,EACjD,IAAI,CAAC;AAAA;AAAA;AAAA,gBAGJ,gBAAAA,KAAC,OAAiB,WAAU,kFACzB,cAAI,WADC,IAAI,IAEZ;AAAA,eACD;AAAA,cAEF,QAAQ,SAAS,KAAK,QAAQ,WAAW,KACxC,gBAAAA,KAAC,OAAE,MAAK,UAAS,WAAU,iCAAgC,mDAE3D;AAAA,cAGD,OAAO,SAAS,KACf,gBAAAC,MAAC,SAAI,WAAU,+BACb;AAAA,gCAAAD,KAAC,OAAE,WAAU,yEACV,iBAAO,aAAa,iBAAiB,OAAO,UAAU,KAAK,iBAC9D;AAAA,gBACA,gBAAAA,KAAC,QAAG,WAAU,sBACX,iBAAO,IAAI,CAAC,OAAO,UAClB,gBAAAA,KAAC,QACC,0BAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,QAAQ;AAAA,oBACR;AAAA,oBACA;AAAA,oBACA;AAAA,oBACA,UAAU,WAAW;AAAA,oBACrB;AAAA;AAAA,gBACF,KARO,GAAG,MAAM,SAAS,MAAM,OAAO,IAAI,KAAK,EASjD,CACD,GACH;AAAA,iBACF;AAAA;AAAA;AAAA,QAEJ;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAaO,SAAS,iBAAiB,EAAE,OAAO,UAAU,GAA0B;AAC5E,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,SACE,gBAAAA,KAAC,QAAG,WAAW,iDAAiD,aAAa,EAAE,IAC5E,gBAAM,IAAI,CAAC,UAAU;AACpB,UAAM,OAAO,oBAAoB,KAAK;AACtC,WACE,gBAAAC,MAAC,QAAe,WAAU,2DACxB;AAAA,sBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,WAAW,wFAAwF,YAAY,KAAK,CAAC;AAAA,UAErH;AAAA,4BAAAD,KAAC,cAAW,OAAc,WAAU,WAAU;AAAA,YAC7C,KAAK;AAAA;AAAA;AAAA,MACR;AAAA,MACA,gBAAAA,KAAC,UAAM,eAAK,SAAQ;AAAA,SAPb,KAQT;AAAA,EAEJ,CAAC,GACH;AAEJ;;;AEnbM,SAqDI,YAAAO,WArDJ,OAAAC,OAOF,QAAAC,aAPE;AArBN,SAAS,IAAI,OAAuB;AAClC,SAAO,IAAI,KAAK,aAAa,SAAS;AAAA,IACpC,OAAO;AAAA,IACP,UAAU;AAAA,IACV,uBAAuB,QAAQ,QAAQ,IAAI,IAAI;AAAA,IAC/C,uBAAuB,QAAQ,QAAQ,IAAI,IAAI;AAAA,EACjD,CAAC,EAAE,OAAO,QAAQ,GAAG;AACvB;AAEA,SAASC,cAAwB;AAC/B,SACE,gBAAAF;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,QAAO;AAAA,MACP,aAAY;AAAA,MACZ,eAAc;AAAA,MACd,gBAAe;AAAA,MACf,eAAW;AAAA,MAEX,0BAAAA,MAAC,UAAK,GAAE,mBAAkB;AAAA;AAAA,EAC5B;AAEJ;AAEA,SAAS,QAAQ,EAAE,SAAS,GAAuC;AACjE,SACE,gBAAAC,MAAC,QAAG,WAAU,oDACZ;AAAA,oBAAAD,MAAC,UAAK,WAAU,UACd,0BAAAA,MAACE,aAAA,EAAW,GACd;AAAA,IACA,gBAAAF,MAAC,UAAM,UAAS;AAAA,KAClB;AAEJ;AAUO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,mBAAmB;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAgC;AAC9B,QAAM,EAAE,SAAS,IAAI,IAAI,WAAW;AACpC,QAAM,iBAAiB,QAAQ,IAAI,MAAM,UAAU,UAAU,IAAI,IAAI,QAAQ;AAC7E,QAAM,iBAAiB,QACnB,IAAI,MAAM,UAAU,oBAAoB,IACxC,IAAI,gBAAgB;AACxB,QAAM,eAAe,OAAO,gBAAgB;AAC5C,SACE,gBAAAA,MAAC,SAAI,WAAU,4DACb,0BAAAC,MAAC,SAAI,WAAU,mEACb;AAAA,oBAAAD,MAAC,OAAE,WAAU,2EACV,mBACH;AAAA,IACA,gBAAAC,MAAC,QAAG,WAAU,8DAA6D;AAAA;AAAA,MACjE;AAAA,OACV;AAAA,IACC,WAAW,gBAAAD,MAAC,OAAE,WAAU,sCAAsC,mBAAQ;AAAA,IAEtE,eACC,gBAAAC,MAAAF,WAAA,EACE;AAAA,sBAAAE,MAAC,SAAI,WAAU,oCACb;AAAA,wBAAAD,MAAC,UAAK,WAAU,0CACb,cAAI,aAAa,UAAU,GAC9B;AAAA,QACA,gBAAAA,MAAC,UAAK,WAAU,iCAAgC,yBAAW;AAAA,SAC7D;AAAA,MACA,gBAAAC,MAAC,OAAE,WAAU,sCAAqC;AAAA;AAAA,QACtC,IAAI,aAAa,oBAAoB;AAAA,QAAE;AAAA,SACnD;AAAA,MACA,gBAAAA,MAAC,OAAE,WAAU,sCAAqC;AAAA;AAAA,QAC1C;AAAA,QAAe;AAAA,QAAgB;AAAA,QAAe;AAAA,SACtD;AAAA,OACF,IAEA,gBAAAA,MAAAF,WAAA,EACE;AAAA,sBAAAE,MAAC,SAAI,WAAU,oCACb;AAAA,wBAAAD,MAAC,UAAK,WAAU,0CAA0C,0BAAe;AAAA,QACzE,gBAAAA,MAAC,UAAK,WAAU,iCAAgC,iBAAG;AAAA,SACrD;AAAA,MACA,gBAAAC,MAAC,OAAE,WAAU,sCAAqC;AAAA;AAAA,QACtC;AAAA,QAAe;AAAA,SAC3B;AAAA,OACF;AAAA,IAGF,gBAAAD,MAAC,QAAG,WAAU,oBAIV,uBAAY;AAAA,MACZ,kBAAkB,OAAO;AAAA,IAC3B,GAAG,IAAI,CAAC,SAAS,MACf,gBAAAA,MAAC,WAAiB,qBAAJ,CAAY,CAC3B,GACH;AAAA,IAEA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,UAAU;AAAA,QACV,SAAS,MAAM,IAAI,UAAU;AAAA,QAC7B,WAAU;AAAA,QAET,oBAAU,2BAAsB,YAAY;AAAA;AAAA,IAC/C;AAAA,IACC,YACC,gBAAAA,MAAC,OAAE,WAAU,qDACV,oBACH;AAAA,KAEJ,GACF;AAEJ;;;AC1JA;AAAA,EAGE,eAAAG;AAAA,EACA,aAAAC;AAAA,EACA,SAAAC;AAAA,EACA,WAAAC;AAAA,EACA,UAAAC;AAAA,EACA,YAAAC;AAAA,OACK;AAkZK,qBAAAC,WACE,OAAAC,OADF,QAAAC,aAAA;AAxXZ,SAAS,aAAa,OAAgB;AACpC,iBAAe,MAAM;AACnB,UAAM;AAAA,EACR,CAAC;AACH;AAWO,SAAS,kBACd,YACA,EAAE,SAAS,MAAM,aAAa,QAAQ,GACF;AACpC,QAAM,CAAC,UAAU,WAAW,IAAIC,WAA6B,IAAI;AACjE,QAAM,cAAcC,aAAY,CAAC,SAA6B,YAAY,IAAI,GAAG,CAAC,CAAC;AACnF,QAAM,gBAAgBC,SAAO,UAAU;AAEvC,EAAAC,YAAU,MAAM;AACd,kBAAc,UAAU;AAAA,EAC1B,GAAG,CAAC,UAAU,CAAC;AAEf,EAAAA,YAAU,MAAM;AACd,QAAI,CAAC,YAAY,CAAC,QAAS;AAC3B,QAAI,OAAO,yBAAyB,YAAa;AACjD,UAAM,WAAW,IAAI;AAAA,MACnB,CAAC,YAAY;AACX,YAAI,CAAC,QAAQ,KAAK,CAAC,UAAU,MAAM,cAAc,EAAG;AACpD,YAAI;AACF,wBAAc,QAAQ;AAAA,QACxB,SAAS,OAAO;AACd,uBAAa,KAAK;AAAA,QACpB;AAAA,MACF;AAAA,MACA,EAAE,MAAM,MAAM,WAAW,MAAM,WAAW;AAAA,IAC5C;AACA,aAAS,QAAQ,QAAQ;AACzB,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,UAAU,SAAS,MAAM,UAAU,CAAC;AAExC,SAAO;AACT;AA8CA,SAAS,cAAc,MAA2B;AAChD,SAAO,GAAG,KAAK,cAAc,EAAE,IAAI,KAAK,MAAM,IAAI,CAAC,SAAS,KAAK,EAAE,EAAE,KAAK,GAAG,CAAC;AAChF;AAWO,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAChB,GAAkD;AAChD,QAAM,CAAC,OAAO,QAAQ,IAAIH,WAA2B,YAAY,KAAK;AACtE,QAAM,CAAC,YAAY,aAAa,IAAIA,WAAwB,YAAY,cAAc,IAAI;AAC1F,QAAM,CAAC,OAAO,QAAQ,IAAIA,WAA4D,MAAM;AAC5F,QAAM,CAAC,WAAW,YAAY,IAAIA,WAAS,CAAC;AAE5C,QAAM,SAASE,SAAO,CAAC;AACvB,QAAM,gBAAgBA,SAA+B,IAAI;AACzD,QAAM,mBAAmBA,SAA+B,IAAI;AAC5D,QAAM,iBAAiBA,SAAO,KAAK;AACnC,QAAM,YAAYA,SAAyB,OAAO;AAGlD,QAAM,gBAAgBA,SAAO,UAAU;AACvC,gBAAc,UAAU;AACxB,QAAM,UAAUA,SAAO,EAAE,GAAG,MAAM,UAAU,CAAC;AAC7C,UAAQ,UAAU,EAAE,GAAG,MAAM,UAAU;AACvC,QAAM,UAAUA,SAAO,WAAW;AAClC,UAAQ,UAAU;AAElB,QAAM,gBAAgB,MAAM,MAAM,SAAS;AAC3C,QAAM,UAAUE,SAAQ,MAAM,cAAc,WAAW,GAAG,CAAC,WAAW,CAAC;AAMvE,EAAAD,YAAU,MAAM;AACd,kBAAc,SAAS,MAAM;AAC7B,qBAAiB,SAAS,MAAM;AAChC,mBAAe,UAAU;AACzB,UAAM,MAAM,EAAE,OAAO;AAErB,QAAI,iBAAiB,cAAc,GAAG;AACpC,eAAS,QAAQ,QAAQ,KAAK;AAC9B,oBAAc,QAAQ,QAAQ,cAAc,IAAI;AAChD,eAAS,MAAM;AACf;AAAA,IACF;AAEA,UAAM,aAAa,IAAI,gBAAgB;AACvC,kBAAc,UAAU;AACxB,cAAU,UAAU;AACpB,aAAS,CAAC,CAAC;AACX,kBAAc,IAAI;AAClB,aAAS,cAAc;AAEvB,UAAM,YAAY;AAChB,UAAI;AACF,cAAM,OAAO,MAAM,QAAQ,QAAQ,UAAU,EAAE,GAAG,MAAM,QAAQ,MAAM,QAAQ,WAAW,OAAO,CAAC;AACjG,YAAI,QAAQ,OAAO,QAAS;AAC5B,iBAAS,KAAK,KAAK;AACnB,sBAAc,KAAK,cAAc,IAAI;AACrC,iBAAS,MAAM;AAAA,MACjB,QAAQ;AACN,YAAI,WAAW,OAAO,WAAW,QAAQ,OAAO,QAAS;AACzD,iBAAS,OAAO;AAAA,MAClB;AAAA,IACF,GAAG;AAEH,WAAO,MAAM,WAAW,MAAM;AAAA,EAChC,GAAG,CAAC,GAAG,MAAM,SAAS,eAAe,SAAS,CAAC;AAE/C,QAAM,WAAWF,aAAY,MAAM;AACjC,UAAM,SAAS,cAAc;AAC7B,QAAI,CAAC,UAAU,eAAe,QAAS;AAEvC,UAAM,EAAE,GAAG,UAAU,MAAM,aAAa,WAAW,aAAa,IAAI,QAAQ;AAC5E,UAAM,MAAM,OAAO;AACnB,mBAAe,UAAU;AACzB,cAAU,UAAU;AACpB,UAAM,aAAa,IAAI,gBAAgB;AACvC,qBAAiB,UAAU;AAC3B,aAAS,aAAa;AAEtB,UAAM,YAAY;AAChB,UAAI;AACF,cAAM,OAAO,MAAM,aAAa,EAAE,GAAG,UAAU,MAAM,aAAa,QAAQ,QAAQ,WAAW,OAAO,CAAC;AACrG,YAAI,QAAQ,OAAO,QAAS;AAC5B,iBAAS,CAAC,SAAS,kBAAkB,MAAM,KAAK,KAAK,CAAC;AACtD,sBAAc,KAAK,cAAc,IAAI;AACrC,iBAAS,MAAM;AAAA,MACjB,QAAQ;AACN,YAAI,WAAW,OAAO,WAAW,QAAQ,OAAO,QAAS;AACzD,iBAAS,OAAO;AAAA,MAClB,UAAE;AACA,YAAI,QAAQ,OAAO,QAAS,gBAAe,UAAU;AAAA,MACvD;AAAA,IACF,GAAG;AAAA,EACL,GAAG,CAAC,CAAC;AAEL,QAAM,QAAQA,aAAY,MAAM;AAC9B,QAAI,UAAU,YAAY,OAAQ,UAAS;AAAA,QACtC,cAAa,CAAC,QAAQ,MAAM,CAAC;AAAA,EACpC,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,SAASA,aAAY,MAAM;AAC/B,iBAAa,CAAC,QAAQ,MAAM,CAAC;AAAA,EAC/B,GAAG,CAAC,CAAC;AAEL,EAAAE;AAAA,IACE,MAAM,MAAM;AACV,oBAAc,SAAS,MAAM;AAC7B,uBAAiB,SAAS,MAAM;AAAA,IAClC;AAAA,IACA,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL;AAAA,IACA,SAAS,eAAe;AAAA,IACxB,gBAAgB,UAAU;AAAA,IAC1B,eAAe,UAAU;AAAA,IACzB,SAAS,UAAU;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAoCA,IAAM,iBAAsC;AAAA,EAC1C,aAAa;AAAA,EACb,aAAa;AAAA,EACb,cAAc;AAAA,EACd,aAAa;AAAA,EACb,YAAY,CAAC,UAAU,sCAAiC,KAAK;AAAA,EAC7D,cAAc;AAAA,EACd,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AAAA,EACT,cAAc;AAAA,EACd,cAAc;AAChB;AAkBO,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA0C;AACxC,QAAM,OAAO,EAAE,GAAG,gBAAgB,GAAG,OAAO;AAC5C,QAAM,CAAC,cAAc,eAAe,IAAIH,WAAgC,IAAI;AAC5E,QAAM,CAAC,aAAa,cAAc,IAAIA,WAAS,EAAE;AACjD,QAAM,CAAC,cAAc,eAAe,IAAIA,WAAgC,IAAI;AAC5E,QAAM,CAAC,MAAM,OAAO,IAAIA,WAAS,KAAK;AACtC,QAAM,CAAC,OAAO,QAAQ,IAAIA,WAAwB,IAAI;AAEtD,QAAM,aAAaC,aAAY,CAAC,YAA4B;AAC1D,aAAS,IAAI;AACb,oBAAgB,OAAO;AACvB,mBAAe,QAAQ,SAAS,EAAE;AAAA,EACpC,GAAG,CAAC,CAAC;AAEL,QAAM,aAAaA,aAAY,CAAC,YAA4B;AAC1D,aAAS,IAAI;AACb,oBAAgB,OAAO;AAAA,EACzB,GAAG,CAAC,CAAC;AAEL,QAAM,eAAeA,aAAY,YAAY;AAC3C,QAAI,CAAC,aAAc;AACnB,UAAM,QAAQ,YAAY,KAAK;AAG/B,QAAI,CAAC,SAAS,UAAU,aAAa,OAAO;AAC1C,sBAAgB,IAAI;AACpB;AAAA,IACF;AACA,YAAQ,IAAI;AACZ,aAAS,IAAI;AACb,QAAI;AACF,YAAM,cAAc,aAAa,IAAI,KAAK;AAC1C,sBAAgB,IAAI;AACpB,eAAS,WAAW,KAAK,OAAO;AAChC,kBAAY;AAAA,IACd,SAAS,GAAG;AACV,YAAM,UAAU,aAAa,QAAQ,EAAE,UAAU,KAAK;AACtD,eAAS,OAAO;AAChB,eAAS,SAAS,OAAO;AAAA,IAC3B,UAAE;AACA,cAAQ,KAAK;AAAA,IACf;AAAA,EACF,GAAG,CAAC,cAAc,aAAa,eAAe,QAAQ,WAAW,KAAK,SAAS,KAAK,YAAY,CAAC;AAEjG,QAAM,gBAAgBA,aAAY,YAAY;AAC5C,QAAI,CAAC,aAAc;AACnB,UAAM,kBAAkB,oBAAoB,QAAQ,aAAa,OAAO;AACxE,YAAQ,IAAI;AACZ,aAAS,IAAI;AACb,QAAI;AACF,YAAM,cAAc,aAAa,EAAE;AACnC,sBAAgB,IAAI;AACpB,eAAS,WAAW,KAAK,OAAO;AAChC,kBAAY;AACZ,UAAI,gBAAiB,oBAAmB;AAAA,IAC1C,SAAS,GAAG;AACV,YAAM,UAAU,aAAa,QAAQ,EAAE,UAAU,KAAK;AACtD,eAAS,OAAO;AAChB,eAAS,SAAS,OAAO;AAAA,IAC3B,UAAE;AACA,cAAQ,KAAK;AAAA,IACf;AAAA,EACF,GAAG,CAAC,cAAc,kBAAkB,eAAe,QAAQ,WAAW,kBAAkB,KAAK,SAAS,KAAK,YAAY,CAAC;AAExH,QAAM,UACJ,gBAAAF,MAAAF,WAAA,EACG;AAAA,oBACC,gBAAAE;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,KAAK;AAAA,QACZ,SAAS,MAAM,gBAAgB,IAAI;AAAA,QACnC;AAAA,QACA;AAAA,QACA,QACE,gBAAAA,MAAAF,WAAA,EACE;AAAA,0BAAAC,MAAC,gBAAa,SAAS,MAAM,gBAAgB,IAAI,GAAG,UAAU,MAAM,SAAQ,SACzE,eAAK,QACR;AAAA,UACA,gBAAAA,MAAC,gBAAa,SAAS,MAAM,KAAK,aAAa,GAAG,UAAU,QAAQ,CAAC,YAAY,KAAK,GACnF,eAAK,cACR;AAAA,WACF;AAAA,QAGF;AAAA,0BAAAA,MAAC,WAAM,SAAQ,4BAA2B,WAAU,iCACjD,eAAK,aACR;AAAA,UACA,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,IAAG;AAAA,cACH,OAAO;AAAA,cACP,WAAS;AAAA,cACT,UAAU,CAAC,MAAM,eAAe,EAAE,OAAO,KAAK;AAAA,cAC9C,WAAW,CAAC,MAAM;AAChB,oBAAI,EAAE,QAAQ,WAAW,CAAC,MAAM;AAC9B,oBAAE,eAAe;AACjB,uBAAK,aAAa;AAAA,gBACpB;AAAA,cACF;AAAA,cACA,WAAU;AAAA;AAAA,UACZ;AAAA;AAAA;AAAA,IACF;AAAA,IAGD,gBACC,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,KAAK;AAAA,QACZ,SAAS,MAAM,gBAAgB,IAAI;AAAA,QACnC;AAAA,QACA;AAAA,QACA,QACE,gBAAAC,MAAAF,WAAA,EACE;AAAA,0BAAAC,MAAC,gBAAa,SAAS,MAAM,gBAAgB,IAAI,GAAG,UAAU,MAAM,SAAQ,SACzE,eAAK,QACR;AAAA,UACA,gBAAAA,MAAC,gBAAa,SAAS,MAAM,KAAK,cAAc,GAAG,UAAU,MAAM,SAAQ,eACxE,eAAK,cACR;AAAA,WACF;AAAA,QAGF,0BAAAA,MAAC,OAAE,WAAU,iCAAiC,eAAK,WAAW,aAAa,YAAY,CAAC,GAAE;AAAA;AAAA,IAC5F;AAAA,KAEJ;AAGF,SAAO,EAAE,YAAY,YAAY,SAAS,KAAK;AACjD;AAEA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AACZ,GAKG;AACD,QAAM,OACJ,YAAY,UACR,gEACA,YAAY,gBACV,gEACA;AACR,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA,WAAW,0EAA0E,IAAI;AAAA,MAExF;AAAA;AAAA,EACH;AAEJ;AAEA,SAAS,cAAc;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAOG;AAGD,EAAAK,YAAU,MAAM;AACd,UAAM,QAAQ,CAAC,MAAqB;AAClC,UAAI,EAAE,QAAQ,YAAY,CAAC,KAAM,SAAQ;AAAA,IAC3C;AACA,aAAS,iBAAiB,WAAW,KAAK;AAC1C,WAAO,MAAM,SAAS,oBAAoB,WAAW,KAAK;AAAA,EAC5D,GAAG,CAAC,MAAM,OAAO,CAAC;AAElB,SACE,gBAAAJ,MAAC,SAAI,WAAU,2DACb;AAAA,oBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,SAAS,MAAM;AACb,cAAI,CAAC,KAAM,SAAQ;AAAA,QACrB;AAAA,QACA,eAAW;AAAA;AAAA,IACb;AAAA,IACA,gBAAAC;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,cAAW;AAAA,QACX,cAAY;AAAA,QACZ,WAAW,8EAA8E,cAAc;AAAA,QAEvG;AAAA,0BAAAD,MAAC,QAAG,WAAU,yCAAyC,iBAAM;AAAA,UAC7D,gBAAAA,MAAC,SAAI,WAAU,QAAQ,UAAS;AAAA,UAC/B,SACC,gBAAAA,MAAC,OAAE,MAAK,SAAQ,WAAU,wEACvB,iBACH;AAAA,UAEF,gBAAAA,MAAC,SAAI,WAAU,+BAA+B,kBAAO;AAAA;AAAA;AAAA,IACvD;AAAA,KACF;AAEJ;AAkEA,SAAS,WAAW,EAAE,IAAI,WAAW,SAAS,GAAkB;AAC9D,SACE,gBAAAA,MAAC,OAAE,MAAM,IAAI,WACV,UACH;AAEJ;AAEA,IAAM,SAAS;AACf,IAAM,OAAO,KAAK;AAClB,IAAM,MAAM,KAAK;AAIV,SAAS,uBAAuB,SAAgC;AACrE,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,KAAK,KAAK,MAAM,OAAO;AAC7B,MAAI,OAAO,MAAM,EAAE,EAAG,QAAO;AAC7B,QAAM,QAAQ,KAAK,IAAI,IAAI;AAC3B,MAAI,QAAQ,OAAQ,QAAO;AAC3B,MAAI,QAAQ,KAAM,QAAO,GAAG,KAAK,MAAM,QAAQ,MAAM,CAAC;AACtD,MAAI,QAAQ,IAAK,QAAO,GAAG,KAAK,MAAM,QAAQ,IAAI,CAAC;AACnD,MAAI,QAAQ,IAAI,IAAK,QAAO,GAAG,KAAK,MAAM,QAAQ,GAAG,CAAC;AACtD,SAAO,IAAI,KAAK,EAAE,EAAE,mBAAmB,QAAW,EAAE,OAAO,SAAS,KAAK,UAAU,CAAC;AACtF;AAEA,SAAS,eAAe;AAKtB,SACE,gBAAAC,MAAAF,WAAA,EACE;AAAA,oBAAAC,MAAC,UAAK,MAAK,UAAS,aAAU,UAAS,aAAW,MAAM,WAAU,WAAU,oCAE5E;AAAA,IACA,gBAAAA,MAAC,SAAI,WAAU,yBAAwB,eAAW,MAC/C,gBAAM,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,MACjC,gBAAAC,MAAC,SAAY,WAAU,uCACrB;AAAA,sBAAAD,MAAC,SAAI,WAAU,0CAAyC;AAAA,MACxD,gBAAAA,MAAC,SAAI,WAAU,4CAA2C;AAAA,MAC1D,gBAAAA,MAAC,SAAI,WAAU,mDAAkD;AAAA,SAHzD,CAIV,CACD,GACH;AAAA,KACF;AAEJ;AAEA,SAAS,YAAY,EAAE,UAAU,GAA2B;AAC1D,SACE,gBAAAA,MAAC,SAAI,SAAQ,aAAY,WAAsB,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACvJ,0BAAAA,MAAC,UAAK,GAAE,iEAAgE,GAC1E;AAEJ;AAUO,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe,OAAO;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR,gBAAgB;AAAA,EAChB,aAAa;AAAA,EACb,mBAAmB;AAAA,EACnB,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf;AACF,GAA6B;AAC3B,QAAM,YAAYI,SAA8B,IAAI;AACpD,QAAM,cAAc,kBAAkB,QAAQ,UAAU;AAAA,IACtD,SAAS,QAAQ,WAAW,CAAC,QAAQ,iBAAiB,CAAC,QAAQ;AAAA,IAC/D,MAAM;AAAA,IACN,YAAY;AAAA,EACd,CAAC;AACD,QAAM,aAAa,MAAM,KAAK;AAC9B,QAAM,cAAc,WAAW,SAAS;AACxC,QAAM,SAAS,iBAAiB,SAAS,WAAW;AACpD,QAAM,CAAC,aAAa,cAAc,IAAIF,WAAsB,oBAAI,IAAI,CAAC;AACrE,QAAM,CAAC,SAAS,UAAU,IAAIA,WAAS,IAAI;AAC3C,QAAM,CAAC,YAAY,aAAa,IAAIA,WAI1B,IAAI;AACd,QAAM,CAAC,UAAU,WAAW,IAAIA,WAAS,KAAK;AAC9C,QAAM,CAAC,WAAW,YAAY,IAAIA,WAAwB,IAAI;AAE9D,EAAAG,YAAU,MAAM;AACd,mBAAe,oBAAI,IAAI,CAAC;AAAA,EAC1B,GAAG,CAAC,YAAY,IAAI,CAAC;AAErB,EAAAA,YAAU,MAAM;AACd,UAAM,UAAU,IAAI,IAAI,QAAQ,MAAM,IAAI,CAAC,SAAS,KAAK,EAAE,CAAC;AAC5D,mBAAe,CAAC,YAAY;AAC1B,YAAM,OAAO,IAAI,IAAI,CAAC,GAAG,OAAO,EAAE,OAAO,CAAC,OAAO,QAAQ,IAAI,EAAE,CAAC,CAAC;AACjE,aAAO,KAAK,SAAS,QAAQ,OAAO,UAAU;AAAA,IAChD,CAAC;AAAA,EACH,GAAG,CAAC,QAAQ,KAAK,CAAC;AAElB,QAAM,gBAAgB,YAAY;AAClC,QAAM,qBAAqB,QAAQ,MAAM,SAAS,KAAK,QAAQ,MAAM,MAAM,CAAC,SAAS,YAAY,IAAI,KAAK,EAAE,CAAC;AAC7G,QAAM,gBAAgB,OAAO,OAAO;AACpC,QAAM,eAAe,OAAO,UAAU,aAAa,KAAK,iBAAiB,KAAK,iBAAiB;AAE/F,QAAM,iBAAiBF,aAAY,CAAC,WAA8B;AAChE,UAAM,OAAO,YAAY,YAAY;AACrC,QAAI,OAAO,SAAS,YAAY;AAC9B,oBAAc;AAAA,QACZ;AAAA,QACA,OAAO,GAAG,WAAW;AAAA,QACrB,MAAM,GAAG,SAAS,WAAW,6BAA6B,QAAQ,IAAI,GAAG,IAAI,OAAO,IAAI,MAAM,oBAAoB,OAAO,IAAI,WAAW,IAAI,KAAK,GAAG;AAAA,MACtJ,CAAC;AACD;AAAA,IACF;AACA,UAAM,QAAQ,OAAO,SAAS,eAAe,cAAc,OAAO,IAAI,UAAU,iBAAiB,OAAO,IAAI;AAC5G,kBAAc;AAAA,MACZ;AAAA,MACA,OAAO,GAAG,WAAW,aAAa,KAAK;AAAA,MACvC,MAAM;AAAA,IACR,CAAC;AAAA,EACH,GAAG,CAAC,WAAW,CAAC;AAEhB,QAAM,oBAAoBA,aAAY,YAAY;AAChD,QAAI,CAAC,cAAc,CAAC,aAAc;AAClC,gBAAY,IAAI;AAChB,iBAAa,IAAI;AACjB,QAAI;AACF,YAAM,aAAa,WAAW,MAAM;AACpC,oBAAc,IAAI;AAClB,qBAAe,oBAAI,IAAI,CAAC;AACxB,cAAQ,OAAO;AAAA,IACjB,SAAS,OAAO;AACd,mBAAa,iBAAiB,QAAQ,MAAM,UAAU,aAAa,YAAY,YAAY,CAAC,WAAW;AAAA,IACzG,UAAE;AACA,kBAAY,KAAK;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,YAAY,aAAa,SAAS,YAAY,CAAC;AAEnD,SACE,gBAAAF,MAAC,SAAI,WAAW,wCAAwC,aAAa,EAAE,IAErE;AAAA,oBAAAD,MAAC,YAAO,WAAU,uEAChB,0BAAAC,MAAC,SAAI,WAAW,gCAAgC,MAAM,IACtD;AAAA,sBAAAD,MAAC,QAAG,WAAU,yDAAyD,iBAAM;AAAA,MAC5E,kBACC,gBAAAC;AAAA,QAAC;AAAA;AAAA,UACC,IAAI;AAAA,UACJ,WAAU;AAAA,UAEV;AAAA,4BAAAD,MAAC,UAAK,eAAW,MAAC,WAAU,wBAAuB,eAAC;AAAA,YAAO;AAAA;AAAA;AAAA,MAE7D;AAAA,OAEF,GACF;AAAA,IAEA,gBAAAC,MAAC,SAAI,KAAK,WAAW,WAAU,kCAC5B;AAAA,wBACC,gBAAAA,MAAC,SAAI,WAAU,gDACb;AAAA,wBAAAA,MAAC,SAAI,WAAW,2EAA2E,MAAM,IAC/F;AAAA,0BAAAD;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,OAAO;AAAA,cACP,UAAU,CAAC,MAAM,cAAc,EAAE,OAAO,KAAK;AAAA,cAC7C,aAAY;AAAA,cACZ,cAAW;AAAA,cACX,WAAU;AAAA;AAAA,UACZ;AAAA,UACA,gBAAAC;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,UAAU,CAAC,MAAM,aAAa,EAAE,OAAO,KAAoB;AAAA,cAC3D,cAAW;AAAA,cACX,WAAU;AAAA,cAEV;AAAA,gCAAAD,MAAC,YAAO,OAAM,UAAS,oBAAM;AAAA,gBAC7B,gBAAAA,MAAC,YAAO,OAAM,UAAS,oBAAM;AAAA;AAAA;AAAA,UAC/B;AAAA,WACF;AAAA,QACC,gBACC,gBAAAC,MAAC,SAAI,WAAW,wDAAwD,MAAM,IAC5E;AAAA,0BAAAA,MAAC,SAAI,WAAU,qCACb;AAAA,4BAAAD;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAS,MAAM,eAAe,IAAI,IAAI,QAAQ,MAAM,IAAI,CAAC,SAAS,KAAK,EAAE,CAAC,CAAC;AAAA,gBAC3E,UAAU,sBAAsB,QAAQ,MAAM,WAAW;AAAA,gBACzD,WAAU;AAAA,gBACX;AAAA;AAAA,YAED;AAAA,YACA,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAS,MAAM,eAAe,oBAAI,IAAI,CAAC;AAAA,gBACvC,UAAU,kBAAkB;AAAA,gBAC5B,WAAU;AAAA,gBACX;AAAA;AAAA,YAED;AAAA,YACA,gBAAAC,MAAC,UAAK,WAAU,iCAAgC,aAAU,UACvD;AAAA;AAAA,cAAc;AAAA,eACjB;AAAA,YACC,gBAAgB,KACf,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAS,MAAM,eAAe,EAAE,MAAM,YAAY,KAAK,CAAC,GAAG,WAAW,EAAE,CAAC;AAAA,gBACzE,WAAU;AAAA,gBAET;AAAA;AAAA,kBAAY;AAAA;AAAA;AAAA,YACf;AAAA,aAEJ;AAAA,UACA,gBAAAA,MAAC,SAAI,WAAU,qCACb;AAAA,4BAAAD,MAAC,WAAM,SAAQ,yBAAwB,WAAU,iCAAgC,yBAEjF;AAAA,YACA,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,IAAG;AAAA,gBACH,MAAK;AAAA,gBACL,KAAK;AAAA,gBACL,KAAK;AAAA,gBACL,OAAO;AAAA,gBACP,UAAU,CAAC,UAAU,WAAW,MAAM,OAAO,KAAK;AAAA,gBAClD,gBAAc,QAAQ,SAAS,KAAK,CAAC;AAAA,gBACrC,WAAU;AAAA;AAAA,YACZ;AAAA,YACA,gBAAAA,MAAC,UAAK,WAAU,iCAAgC,kBAAI;AAAA,YACpD,gBAAAC;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAS,MAAM,eAAe,EAAE,MAAM,cAAc,MAAM,cAAc,CAAC;AAAA,gBACzE,UAAU,CAAC;AAAA,gBACX,WAAU;AAAA,gBAET;AAAA;AAAA,kBAAY;AAAA;AAAA;AAAA,YACf;AAAA,YACA,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAS,MAAM,eAAe,EAAE,MAAM,cAAc,MAAM,cAAc,CAAC;AAAA,gBACzE,UAAU,CAAC;AAAA,gBACX,WAAU;AAAA,gBAET;AAAA;AAAA,kBAAY;AAAA;AAAA;AAAA,YACf;AAAA,aACF;AAAA,WACF;AAAA,SAEJ;AAAA,MAGF,gBAAAD,MAAC,SAAI,WAAW,0BAA0B,MAAM,IAC7C,WAAC,iBACA,gBAAAC,MAAC,SAAI,WAAU,4EACb;AAAA,wBAAAD,MAAC,OAAE,WAAU,uCAAuC,sBAAW;AAAA,QAC/D,gBAAAA,MAAC,OAAE,WAAU,0CAA0C,4BAAiB;AAAA,SAC1E,IACE,QAAQ,iBACV,gBAAAA,MAAC,gBAAa,IACZ,QAAQ,MAAM,WAAW,IAC3B,QAAQ,UACN,gBAAAA,MAAC,cAAW,SAAS,QAAQ,OAAO,SAAQ,qCAA+B,IACzE,cACF,gBAAAC,MAAC,OAAE,WAAU,mDAAkD;AAAA;AAAA,QACzC;AAAA,QAAW;AAAA,SACjC,IAEA,gBAAAD,MAAC,OAAE,WAAU,mDAAkD,iCAAmB,IAGpF,gBAAAC,MAAC,SAAI,WAAU,yBACZ;AAAA,gBAAQ,MAAM,IAAI,CAAC,YAClB,gBAAAD;AAAA,UAAC;AAAA;AAAA,YAEC;AAAA,YACA,MAAM,eAAe,QAAQ,EAAE;AAAA,YAC/B;AAAA,YACA,YAAY,sBAAsB,IAAI,QAAQ,EAAE,KAAK;AAAA,YACrD;AAAA,YACA,WAAW,gBAAgB,QAAQ,SAAS;AAAA,YAC5C;AAAA,YACA;AAAA,YACA,YAAY,QAAQ,YAAY;AAAA,YAChC,UAAU,YAAY,IAAI,QAAQ,EAAE;AAAA,YACpC,kBAAkB,CAAC,aAAa;AAC9B,6BAAe,CAAC,YAAY;AAC1B,sBAAM,OAAO,IAAI,IAAI,OAAO;AAC5B,oBAAI,SAAU,MAAK,IAAI,QAAQ,EAAE;AAAA,oBAC5B,MAAK,OAAO,QAAQ,EAAE;AAC3B,uBAAO;AAAA,cACT,CAAC;AAAA,YACH;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA;AAAA,UArBK,QAAQ;AAAA,QAsBf,CACD;AAAA,QAEA,QAAQ,UACP,gBAAAA,MAAC,cAAW,SAAS,QAAQ,OAAO,SAAQ,qCAA+B,QAAM,MAAC,IAChF,QAAQ,UACV,gBAAAA,MAAC,SAAI,KAAK,aAAa,WAAU,yCAC9B,kBAAQ,iBACP,gBAAAA,MAAC,UAAK,MAAK,UAAS,aAAU,UAAS,aAAW,MAAM,WAAU,iCAAgC,2BAElG,GAEJ,IACE;AAAA,SACN,GAEJ;AAAA,OACF;AAAA,IACC,cACC,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,WAAW;AAAA,QAClB,SAAS,MAAM;AACb,cAAI,CAAC,UAAU;AACb,0BAAc,IAAI;AAClB,yBAAa,IAAI;AAAA,UACnB;AAAA,QACF;AAAA,QACA,MAAM;AAAA,QACN,OAAO;AAAA,QACP,QACE,gBAAAC,MAAAF,WAAA,EACE;AAAA,0BAAAC;AAAA,YAAC;AAAA;AAAA,cACC,SAAS,MAAM;AACb,8BAAc,IAAI;AAClB,6BAAa,IAAI;AAAA,cACnB;AAAA,cACA,UAAU;AAAA,cACV,SAAQ;AAAA,cACT;AAAA;AAAA,UAED;AAAA,UACA,gBAAAA,MAAC,gBAAa,SAAS,MAAM,KAAK,kBAAkB,GAAG,UAAU,UAAU,SAAQ,eAChF,qBAAW,kBAAa,aAC3B;AAAA,WACF;AAAA,QAGF,0BAAAA,MAAC,OAAE,WAAU,iCAAiC,qBAAW,MAAK;AAAA;AAAA,IAChE;AAAA,KAEJ;AAEJ;AAEA,SAAS,WAAW,EAAE,SAAS,SAAS,OAAO,GAA+D;AAC5G,SACE,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC,WACE,SACI,8EACA;AAAA,MAGN;AAAA,wBAAAD,MAAC,UAAK,WAAU,iCAAiC,mBAAQ;AAAA,QACzD,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS;AAAA,YACT,WAAU;AAAA,YACX;AAAA;AAAA,QAED;AAAA;AAAA;AAAA,EACF;AAEJ;AAEA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAeG;AACD,QAAM,CAAC,UAAU,WAAW,IAAIE,WAAS,KAAK;AAC9C,QAAM,UAAUK,OAAM;AACtB,QAAM,EAAE,cAAc,YAAY,UAAU,aAAa,IAAI,WAAW,UAAU,WAAW;AAC7F,QAAM,SAAS,eAAe,OAAO,KAAK,CAAC;AAC3C,QAAM,UAAU,QAAQ,QAAQ,KAAK,QAAQ,QAAQ,KAAK,OAAO,SAAS;AAG1E,QAAM,aAAa,QAAQ,QAAQ,MAAM,KAAK,CAAC;AAE/C,SACE,gBAAAN,MAAC,SAAI,WAAW,mGAAmG,WAAW,kBAAkB,EAAE,IAC/I;AAAA,kBACC,gBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS;AAAA,QACT,UAAU,CAAC,UAAU,iBAAiB,MAAM,OAAO,OAAO;AAAA,QAC1D,cAAY,UAAU,aAAa,SAAS,aAAa,CAAC;AAAA,QAC1D,WAAU;AAAA;AAAA,IACZ;AAAA,IAEF,gBAAAC,MAAC,QAAK,IAAI,MAAM,WAAU,0CACvB;AAAA,oBAAc,gBAAAD,MAAC,UAAK,WAAU,gDAA+C,eAAW,MAAC;AAAA,MAC1F,gBAAAA,MAAC,eAAY,WAAU,0CAAyC;AAAA,MAChE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,WAAW,oBAAoB,aAAa,0BAA0B,iBAAiB,IAAI,aAAa,kBAAkB,EAAE;AAAA,UAC3H,GAAI,aAAa,EAAE,MAAM,UAAU,cAAc,mBAAmB,IAAI,CAAC;AAAA,UAEzE,uBAAa,SAAS,aAAa;AAAA;AAAA,MACtC;AAAA,OACF;AAAA,IACA,gBAAAA,MAAC,UAAK,WAAU,uDAAuD,qBAAU;AAAA,IAChF,WACC,gBAAAC,MAAC,SAAI,KAAK,cAAc,WAAU,qBAChC;AAAA,sBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACJ,GAAG;AAAA,UACJ,cAAW;AAAA,UACX,iBAAe,WAAW,UAAU;AAAA,UACpC,SAAS,MAAM,YAAY,CAAC,SAAS,CAAC,IAAI;AAAA,UAI1C,WAAU;AAAA,UAEV,0BAAAA,MAAC,UAAK,eAAW,MAAC,WAAU,0BAAyB,oBAAC;AAAA;AAAA,MACxD;AAAA,MACA,gBAAAC;AAAA,QAAC;AAAA;AAAA,UACC,MAAM;AAAA,UACN,IAAI;AAAA,UACJ,MAAK;AAAA,UACL;AAAA,UACA;AAAA,UACA,WAAW,2EAA2E,cAAc;AAAA,UAEnG;AAAA,wBACC,gBAAAD;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,MAAK;AAAA,gBACL,SAAS,MAAM;AACb,8BAAY,KAAK;AACjB,2BAAS,OAAO;AAAA,gBAClB;AAAA,gBACA,WAAU;AAAA,gBAET;AAAA;AAAA,YACH;AAAA,YAED,OAAO,IAAI,CAAC,WACX,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBAEC,MAAK;AAAA,gBACL,MAAK;AAAA,gBACL,SAAS,MAAM;AACb,8BAAY,KAAK;AACjB,yBAAO,SAAS;AAAA,gBAClB;AAAA,gBACA,WAAW,yDACT,OAAO,cACH,6CACA,iCACN;AAAA,gBAEC,iBAAO;AAAA;AAAA,cAbH,OAAO;AAAA,YAcd,CACD;AAAA,YACA,YACC,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,MAAK;AAAA,gBACL,SAAS,MAAM;AACb,8BAAY,KAAK;AACjB,2BAAS,OAAO;AAAA,gBAClB;AAAA,gBACA,WAAU;AAAA,gBAET;AAAA;AAAA,YACH;AAAA;AAAA;AAAA,MAEJ;AAAA,OACF;AAAA,KAEJ;AAEJ;;;ACnlCA;AAAA,EACE,YAAAQ;AAAA,EACA;AAAA,EACA,eAAAC;AAAA,EACA,aAAAC;AAAA,EACA,SAAAC;AAAA,EACA,WAAAC;AAAA,EACA,UAAAC;AAAA,EACA,YAAAC;AAAA,OAGK;;;ACrBA,SAAS,aAAa,OAA+C;AAC1E,SAAO,EAAE,WAAW,MAAM,MAAM;AAClC;AAGO,SAAS,eAAe,OAAsC;AACnE,SAAO,EAAE,WAAW,OAAO,MAAM;AACnC;AA4JO,SAAS,2BACd,QACA,QACS;AACT,QAAM,aAAa,OAAO;AAC1B,MAAI,CAAC,WAAY,QAAO;AACxB,QAAM,OAAO,OAAO,WAAW,MAAM,KAAK;AAC1C,SAAO,oBAAoB,MAAM,WAAW,MAAM;AACpD;AAIO,SAAS,oBAAoB,GAAgC,GAAyC;AAC3G,SAAO,OAAO,GAAG,KAAK,MAAM,KAAK,IAAI;AACvC;AAoDO,SAAS,uBACd,MACA,UACqB;AACrB,QAAM,UAAU,SAAS,WAAW,CAAC;AACrC,QAAM,WAAW,IAAI,IAAI,SAAS,YAAY,CAAC,CAAC;AAChD,QAAM,YAAY,SAAS,aAAa,CAAC;AACzC,QAAM,UAAU,IAAI,IAAI,KAAK,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;AAEjD,QAAM,QAA6B,CAAC;AACpC,aAAW,OAAO,MAAM;AACtB,QAAI,SAAS,IAAI,IAAI,EAAE,GAAG;AACxB,YAAM,KAAK,EAAE,OAAO,IAAI,IAAI,MAAM,WAAW,OAAO,CAAC,GAAG,IAAI,CAAC;AAC7D;AAAA,IACF;AACA,UAAM,QAAQ,QAAQ,IAAI,EAAE;AAC5B,QAAI,UAAU,OAAW;AACzB,UAAM,QAA8B,CAAC;AACrC,eAAW,CAAC,UAAU,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AACrD,YAAM,SAAS,IAAI,OAAO,QAAQ,KAAK;AACvC,UAAI,CAAC,oBAAoB,QAAQ,KAAK,EAAG,OAAM,KAAK,EAAE,UAAU,QAAQ,MAAM,CAAC;AAAA,IACjF;AACA,QAAI,MAAM,SAAS,EAAG,OAAM,KAAK,EAAE,OAAO,IAAI,IAAI,MAAM,WAAW,OAAO,IAAI,CAAC;AAAA,EACjF;AACA,aAAW,OAAO,WAAW;AAC3B,QAAI,QAAQ,IAAI,IAAI,EAAE,EAAG;AACzB,UAAM,KAAK,EAAE,OAAO,IAAI,IAAI,MAAM,SAAS,OAAO,CAAC,GAAG,IAAI,CAAC;AAAA,EAC7D;AACA,SAAO;AACT;AAIA,IAAM,UAAU;AAChB,IAAM,WAAW;AAQjB,SAAS,iBAAiB,KAAsE;AAC9F,QAAM,UAAU,IAAI,KAAK;AACzB,MAAI,YAAY,GAAI,QAAO,EAAE,OAAO,KAAK;AACzC,QAAM,gBAAgB,WAAW,KAAK,OAAO;AAC7C,QAAM,QAAQ,gBAAgB,QAAQ,MAAM,GAAG,EAAE,EAAE,KAAK,IAAI,SACzD,QAAQ,UAAU,EAAE,EACpB,QAAQ,mBAAmB,IAAI;AAClC,MAAI,CAAC,QAAQ,KAAK,IAAI,EAAG,QAAO,EAAE,SAAS,KAAK;AAChD,QAAM,SAAS,OAAO,IAAI;AAC1B,MAAI,CAAC,OAAO,SAAS,MAAM,EAAG,QAAO,EAAE,SAAS,KAAK;AACrD,SAAO,EAAE,OAAO,gBAAgB,CAAC,SAAS,OAAO;AACnD;AAGA,SAAS,eAAe,MAAuB;AAC7C,MAAI,CAAC,SAAS,KAAK,IAAI,EAAG,QAAO;AACjC,QAAM,CAAC,MAAM,OAAO,GAAG,IAAI,KAAK,MAAM,GAAG,EAAE,IAAI,CAAC,SAAS,OAAO,IAAI,CAAC;AACrE,MAAI,UAAU,UAAa,QAAQ,UAAa,SAAS,OAAW,QAAO;AAC3E,MAAI,QAAQ,KAAK,QAAQ,MAAM,MAAM,EAAG,QAAO;AAC/C,QAAM,QAAQ,KAAK,IAAI,MAAM,QAAQ,GAAG,GAAG;AAC3C,QAAM,OAAO,IAAI,KAAK,KAAK;AAC3B,SAAO,KAAK,eAAe,MAAM,QAAQ,KAAK,YAAY,MAAM,QAAQ,KAAK,KAAK,WAAW,MAAM;AACrG;AAMO,SAAS,qBAAqB,QAA0B,KAAoC;AACjG,UAAQ,OAAO,MAAM;AAAA,IACnB,KAAK,QAAQ;AACX,YAAM,UAAU,IAAI,KAAK;AACzB,aAAO,aAAa,YAAY,KAAK,OAAO,OAAO;AAAA,IACrD;AAAA,IACA,KAAK,UAAU;AACb,YAAM,UAAU,IAAI,KAAK;AACzB,aAAO,aAAa,YAAY,KAAK,OAAO,OAAO;AAAA,IACrD;AAAA,IACA,KAAK,WAAW;AACd,YAAM,UAAU,IAAI,KAAK,EAAE,YAAY;AACvC,UAAI,YAAY,GAAI,QAAO,aAAa,IAAI;AAC5C,UAAI,YAAY,OAAQ,QAAO,aAAa,IAAI;AAChD,UAAI,YAAY,QAAS,QAAO,aAAa,KAAK;AAClD,aAAO,eAAe,GAAG,OAAO,MAAM,2CAAiC,GAAG,SAAI;AAAA,IAChF;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,UAAU,IAAI,KAAK;AACzB,UAAI,YAAY,GAAI,QAAO,aAAa,IAAI;AAC5C,UAAI,CAAC,eAAe,OAAO,GAAG;AAC5B,eAAO,eAAe,GAAG,OAAO,MAAM,uDAA6C,GAAG,SAAI;AAAA,MAC5F;AACA,aAAO,aAAa,OAAO;AAAA,IAC7B;AAAA,IACA,KAAK;AAAA,IACL,KAAK,YAAY;AACf,YAAM,SAAS,iBAAiB,GAAG;AACnC,UAAI,WAAW,OAAQ,QAAO,aAAa,IAAI;AAC/C,UAAI,aAAa,OAAQ,QAAO,eAAe,GAAG,OAAO,MAAM,sCAA4B,GAAG,SAAI;AAClG,aAAO,aAAa,OAAO,KAAK;AAAA,IAClC;AAAA,EACF;AACF;AAIA,SAAS,gBAAgB,QAAwC;AAC/D,SAAO,OAAO,QAAQ,IAAI,CAAC,WAAW,OAAO,KAAK,EAAE,KAAK,IAAI;AAC/D;AAOO,SAAS,uBAAuB,QAA0B,OAA+C;AAC9G,QAAM,aAAa,UAAU,KAAK,OAAO;AACzC,MAAI,eAAe,MAAM;AACvB,QAAI,OAAO,SAAU,QAAO,eAAe,GAAG,OAAO,MAAM,eAAe;AAC1E,UAAMC,UAAS,OAAO,WAAW,IAAI;AACrC,WAAOA,YAAW,UAAaA,YAAW,OAAO,aAAa,IAAI,IAAI,eAAeA,OAAM;AAAA,EAC7F;AAEA,UAAQ,OAAO,MAAM;AAAA,IACnB,KAAK,QAAQ;AACX,UAAI,OAAO,eAAe,SAAU,QAAO,eAAe,GAAG,OAAO,MAAM,gBAAgB;AAC1F,UAAI,OAAO,cAAc,UAAa,WAAW,SAAS,OAAO,WAAW;AAC1E,eAAO,eAAe,GAAG,OAAO,MAAM,qBAAqB,OAAO,SAAS,0BAAqB,WAAW,MAAM,GAAG;AAAA,MACtH;AACA,UAAI,OAAO,cAAc,UAAa,WAAW,SAAS,OAAO,WAAW;AAC1E,eAAO,eAAe,GAAG,OAAO,MAAM,oBAAoB,OAAO,SAAS,0BAAqB,WAAW,MAAM,GAAG;AAAA,MACrH;AACA,UAAI,OAAO,WAAW,CAAC,OAAO,QAAQ,KAAK,UAAU,GAAG;AACtD,eAAO,eAAe,OAAO,kBAAkB,GAAG,OAAO,MAAM,mBAAmB,OAAO,QAAQ,MAAM,GAAG;AAAA,MAC5G;AACA;AAAA,IACF;AAAA,IACA,KAAK;AAAA,IACL,KAAK,YAAY;AACf,UAAI,OAAO,eAAe,YAAY,CAAC,OAAO,SAAS,UAAU,GAAG;AAClE,eAAO,eAAe,GAAG,OAAO,MAAM,oBAAoB;AAAA,MAC5D;AACA,UAAI,OAAO,SAAS,YAAY,OAAO,YAAY,QAAQ,CAAC,OAAO,UAAU,UAAU,GAAG;AACxF,eAAO,eAAe,GAAG,OAAO,MAAM,sCAAiC,UAAU,GAAG;AAAA,MACtF;AACA,UAAI,OAAO,QAAQ,UAAa,aAAa,OAAO,KAAK;AACvD,eAAO,eAAe,GAAG,OAAO,MAAM,qBAAqB,OAAO,GAAG,eAAU,UAAU,GAAG;AAAA,MAC9F;AACA,UAAI,OAAO,QAAQ,UAAa,aAAa,OAAO,KAAK;AACvD,eAAO,eAAe,GAAG,OAAO,MAAM,oBAAoB,OAAO,GAAG,eAAU,UAAU,GAAG;AAAA,MAC7F;AACA;AAAA,IACF;AAAA,IACA,KAAK,QAAQ;AACX,UAAI,OAAO,eAAe,YAAY,CAAC,eAAe,UAAU,GAAG;AACjE,eAAO,eAAe,GAAG,OAAO,MAAM,qCAAqC;AAAA,MAC7E;AACA,UAAI,OAAO,QAAQ,UAAa,aAAa,OAAO,KAAK;AACvD,eAAO,eAAe,GAAG,OAAO,MAAM,wBAAwB,OAAO,GAAG,eAAU,UAAU,GAAG;AAAA,MACjG;AACA,UAAI,OAAO,QAAQ,UAAa,aAAa,OAAO,KAAK;AACvD,eAAO,eAAe,GAAG,OAAO,MAAM,yBAAyB,OAAO,GAAG,eAAU,UAAU,GAAG;AAAA,MAClG;AACA;AAAA,IACF;AAAA,IACA,KAAK,UAAU;AACb,UAAI,OAAO,eAAe,SAAU,QAAO,eAAe,GAAG,OAAO,MAAM,oBAAoB,gBAAgB,MAAM,CAAC,GAAG;AACxH,UAAI,CAAC,OAAO,QAAQ,KAAK,CAAC,WAAW,OAAO,UAAU,UAAU,GAAG;AACjE,eAAO,eAAe,GAAG,OAAO,MAAM,oBAAoB,gBAAgB,MAAM,CAAC,qBAAW,UAAU,SAAI;AAAA,MAC5G;AACA;AAAA,IACF;AAAA,IACA,KAAK,WAAW;AACd,UAAI,OAAO,eAAe,UAAW,QAAO,eAAe,GAAG,OAAO,MAAM,yBAAyB;AACpG;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAS,OAAO,WAAW,UAAU;AAC3C,MAAI,WAAW,UAAa,WAAW,KAAM,QAAO,eAAe,MAAM;AACzE,SAAO,aAAa,UAAU;AAChC;AAIO,SAAS,mBAAmB,QAA0B,KAAoC;AAC/F,QAAM,SAAS,qBAAqB,QAAQ,GAAG;AAC/C,MAAI,CAAC,OAAO,UAAW,QAAO;AAC9B,SAAO,uBAAuB,QAAQ,OAAO,KAAK;AACpD;AAOO,SAAS,sBACd,SACA,QACsB;AACtB,QAAM,WAA4C,CAAC;AACnD,QAAM,aAAqC,CAAC;AAC5C,MAAI,aAA4B;AAEhC,aAAW,UAAU,SAAS;AAC5B,QAAI,CAAC,2BAA2B,QAAQ,MAAM,GAAG;AAC/C,eAAS,OAAO,EAAE,IAAI;AACtB;AAAA,IACF;AACA,UAAM,UAAU,uBAAuB,QAAQ,OAAO,OAAO,EAAE,KAAK,IAAI;AACxE,QAAI,QAAQ,WAAW;AACrB,eAAS,OAAO,EAAE,IAAI,QAAQ;AAC9B;AAAA,IACF;AACA,eAAW,OAAO,EAAE,IAAI,QAAQ;AAChC,QAAI,eAAe,KAAM,cAAa,QAAQ;AAAA,EAChD;AAEA,MAAI,eAAe,MAAM;AACvB,UAAM,QAAQ,OAAO,KAAK,UAAU,EAAE;AACtC,UAAM,QAAQ,UAAU,IAAI,aAAa,GAAG,KAAK,2BAA2B,UAAU;AACtF,WAAO,EAAE,WAAW,OAAO,OAAO,WAAW;AAAA,EAC/C;AACA,SAAO,EAAE,WAAW,MAAM,OAAO,SAAS;AAC5C;AAMO,SAAS,sBACd,QACA,OACA,QACQ;AACR,MAAI,UAAU,QAAQ,UAAU,GAAI,QAAO;AAC3C,UAAQ,OAAO,MAAM;AAAA,IACnB,KAAK,YAAY;AACf,UAAI,OAAO,UAAU,SAAU,QAAO,OAAO,KAAK;AAClD,aAAO,IAAI,KAAK,aAAa,QAAQ;AAAA,QACnC,OAAO;AAAA,QACP,UAAU,OAAO;AAAA,QACjB,GAAI,OAAO,mBAAmB,SAC1B,CAAC,IACD,EAAE,uBAAuB,OAAO,gBAAgB,uBAAuB,OAAO,eAAe;AAAA,MACnG,CAAC,EAAE,OAAO,KAAK;AAAA,IACjB;AAAA,IACA,KAAK,UAAU;AACb,UAAI,OAAO,UAAU,SAAU,QAAO,OAAO,KAAK;AAClD,aAAO,IAAI,KAAK,aAAa,MAAM,EAAE,OAAO,KAAK;AAAA,IACnD;AAAA,IACA,KAAK,QAAQ;AACX,UAAI,OAAO,UAAU,YAAY,CAAC,eAAe,KAAK,EAAG,QAAO,OAAO,KAAK;AAC5E,aAAO,IAAI,KAAK,eAAe,QAAQ,EAAE,WAAW,UAAU,UAAU,MAAM,CAAC,EAAE;AAAA,QAC/E,oBAAI,KAAK,GAAG,KAAK,YAAY;AAAA,MAC/B;AAAA,IACF;AAAA,IACA,KAAK,UAAU;AACb,YAAM,SAAS,OAAO,QAAQ,KAAK,CAAC,cAAc,UAAU,UAAU,KAAK;AAC3E,aAAO,SAAS,OAAO,QAAQ,OAAO,KAAK;AAAA,IAC7C;AAAA,IACA,KAAK,WAAW;AACd,UAAI,OAAO,UAAU,UAAW,QAAO,OAAO,KAAK;AACnD,aAAO,QAAS,OAAO,aAAa,QAAU,OAAO,cAAc;AAAA,IACrE;AAAA,IACA,KAAK;AACH,aAAO,OAAO,KAAK;AAAA,EACvB;AACF;AAIO,SAAS,qBAAqB,QAA0B,OAAgC;AAC7F,MAAI,UAAU,KAAM,QAAO;AAC3B,MAAI,OAAO,SAAS,UAAW,QAAO,UAAU,OAAO,SAAS;AAChE,SAAO,OAAO,KAAK;AACrB;AAGO,SAAS,mBAAmB,SAAsC,KAA4B;AACnG,MAAI,IAAI,UAAU,UAAa,IAAI,UAAU,GAAI,QAAO,IAAI;AAC5D,aAAW,UAAU,SAAS;AAC5B,QAAI,OAAO,SAAS,UAAU,OAAO,SAAS,SAAU;AACxD,UAAM,QAAQ,IAAI,OAAO,OAAO,EAAE;AAClC,QAAI,OAAO,UAAU,YAAY,UAAU,GAAI,QAAO,sBAAsB,QAAQ,KAAK;AAAA,EAC3F;AACA,SAAO,IAAI;AACb;AAKO,SAAS,oBAAoB,MAAgC,UAA0B;AAC5F,MAAI,QAAQ;AACZ,aAAW,OAAO,MAAM;AACtB,UAAM,QAAQ,IAAI,OAAO,QAAQ;AACjC,QAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,EAAG,UAAS;AAAA,EACpE;AACA,SAAO;AACT;AAmBO,IAAM,4BAA+C,EAAE,SAAS,CAAC,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC,EAAE;AAI7F,SAAS,sBACd,MACA,SACiB;AACjB,QAAM,UAAU,IAAI,IAAI,QAAQ,OAAO;AACvC,QAAM,YAA6B,CAAC;AACpC,aAAW,OAAO,MAAM;AACtB,QAAI,QAAQ,IAAI,IAAI,EAAE,EAAG;AACzB,UAAM,QAAQ,QAAQ,QAAQ,IAAI,EAAE;AACpC,cAAU,KAAK,UAAU,SAAY,MAAM,EAAE,GAAG,KAAK,QAAQ,EAAE,GAAG,IAAI,QAAQ,GAAG,MAAM,EAAE,CAAC;AAAA,EAC5F;AACA,aAAW,OAAO,QAAQ,SAAS;AACjC,QAAI,QAAQ,IAAI,IAAI,EAAE,EAAG;AAIzB,UAAM,QAAQ,QAAQ,QAAQ,IAAI,EAAE;AACpC,cAAU,KAAK,UAAU,SAAY,MAAM,EAAE,GAAG,KAAK,QAAQ,EAAE,GAAG,IAAI,QAAQ,GAAG,MAAM,EAAE,CAAC;AAAA,EAC5F;AACA,SAAO;AACT;AAGO,SAAS,qBACd,SACA,OACA,UACA,OACmB;AACnB,QAAM,WAAW,EAAE,GAAI,QAAQ,QAAQ,KAAK,KAAK,CAAC,GAAI,CAAC,QAAQ,GAAG,MAAM;AACxE,SAAO,EAAE,GAAG,SAAS,SAAS,EAAE,GAAG,QAAQ,SAAS,CAAC,KAAK,GAAG,SAAS,EAAE;AAC1E;AAGO,SAAS,wBACd,SACA,OACA,UACmB;AACnB,QAAM,WAAW,QAAQ,QAAQ,KAAK;AACtC,MAAI,aAAa,UAAa,EAAE,YAAY,UAAW,QAAO;AAC9D,QAAM,YAA6C,CAAC;AACpD,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,GAAG;AACnD,QAAI,QAAQ,SAAU,WAAU,GAAG,IAAI;AAAA,EACzC;AACA,QAAM,UAAqE,EAAE,GAAG,QAAQ,QAAQ;AAChG,MAAI,OAAO,KAAK,SAAS,EAAE,WAAW,EAAG,QAAO,QAAQ,KAAK;AAAA,MACxD,SAAQ,KAAK,IAAI;AACtB,SAAO,EAAE,GAAG,SAAS,QAAQ;AAC/B;AAOO,SAAS,wBACd,SACA,SACA,KACmB;AACnB,QAAM,eAAe,QAAQ,QAAQ,UAAU,CAAC,cAAc,UAAU,OAAO,OAAO;AACtF,MAAI,gBAAgB,GAAG;AACrB,UAAM,UAAU,CAAC,GAAG,QAAQ,OAAO;AACnC,YAAQ,YAAY,IAAI;AACxB,WAAO,EAAE,GAAG,SAAS,SAAS,SAAS,kBAAkB,QAAQ,SAAS,SAAS,IAAI,EAAE,EAAE;AAAA,EAC7F;AACA,SAAO,EAAE,GAAG,SAAS,SAAS,EAAE,GAAG,QAAQ,SAAS,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,OAAO,EAAE,EAAE;AACpF;AAGA,SAAS,kBACP,YACG,QAC2B;AAC9B,QAAM,OAAO,IAAI,IAAI,MAAM;AAC3B,QAAM,OAAkE,CAAC;AACzE,MAAI,UAAU;AACd,aAAW,CAAC,OAAO,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAG;AACpD,QAAI,KAAK,IAAI,KAAK,EAAG,WAAU;AAAA,QAC1B,MAAK,KAAK,IAAI;AAAA,EACrB;AACA,SAAO,UAAU,OAAO;AAC1B;AAGO,SAAS,sBAAsB,SAA4B,KAAuC;AACvG,SAAO,EAAE,GAAG,SAAS,SAAS,CAAC,GAAG,QAAQ,SAAS,GAAG,EAAE;AAC1D;AAIO,SAAS,yBAAyB,SAA4B,OAAkC;AACrG,QAAM,UAAU,QAAQ,QAAQ,OAAO,CAAC,QAAQ,IAAI,OAAO,KAAK;AAChE,MAAI,QAAQ,WAAW,QAAQ,QAAQ,OAAQ,QAAO;AACtD,SAAO,EAAE,GAAG,SAAS,SAAS,SAAS,kBAAkB,QAAQ,SAAS,KAAK,EAAE;AACnF;AAGO,SAAS,sBAAsB,SAA4B,OAAkC;AAClG,MAAI,QAAQ,QAAQ,SAAS,KAAK,EAAG,QAAO;AAC5C,SAAO,EAAE,GAAG,SAAS,SAAS,CAAC,GAAG,QAAQ,SAAS,KAAK,EAAE;AAC5D;AAGO,SAAS,yBAAyB,SAA4B,OAAkC;AACrG,MAAI,CAAC,QAAQ,QAAQ,SAAS,KAAK,EAAG,QAAO;AAC7C,SAAO,EAAE,GAAG,SAAS,SAAS,QAAQ,QAAQ,OAAO,CAAC,cAAc,cAAc,KAAK,EAAE;AAC3F;AAUO,SAAS,uBACd,MACA,SACmB;AACnB,QAAM,OAAO,IAAI,IAAI,KAAK,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC;AACrD,QAAM,aAAa,IAAI,IAAI,QAAQ,QAAQ,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC;AAC/D,MAAI,UAAU;AAEd,QAAM,UAAqE,CAAC;AAC5E,aAAW,CAAC,OAAO,KAAK,KAAK,OAAO,QAAQ,QAAQ,OAAO,GAAG;AAC5D,UAAM,MAAM,KAAK,IAAI,KAAK;AAC1B,QAAI,QAAQ,QAAW;AAIrB,UAAI,WAAW,IAAI,KAAK,GAAG;AACzB,gBAAQ,KAAK,IAAI;AACjB;AAAA,MACF;AACA,gBAAU;AACV;AAAA,IACF;AACA,UAAM,OAAwC,CAAC;AAC/C,eAAW,CAAC,UAAU,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AACrD,UAAI,oBAAoB,IAAI,OAAO,QAAQ,GAAG,KAAK,EAAG,WAAU;AAAA,UAC3D,MAAK,QAAQ,IAAI;AAAA,IACxB;AACA,QAAI,OAAO,KAAK,IAAI,EAAE,SAAS,EAAG,SAAQ,KAAK,IAAI;AAAA,EACrD;AAEA,QAAM,UAAU,QAAQ,QAAQ,OAAO,CAAC,QAAQ;AAC9C,UAAM,UAAU,KAAK,IAAI,IAAI,EAAE;AAC/B,QAAI,QAAS,WAAU;AACvB,WAAO,CAAC;AAAA,EACV,CAAC;AAED,QAAM,UAAU,QAAQ,QAAQ,OAAO,CAAC,UAAU;AAChD,UAAM,UAAU,CAAC,KAAK,IAAI,KAAK;AAC/B,QAAI,QAAS,WAAU;AACvB,WAAO,CAAC;AAAA,EACV,CAAC;AAED,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,EAAE,SAAS,SAAS,QAAQ;AACrC;;;ADtLU,SAEE,OAAAC,OAFF,QAAAC,cAAA;AA3YV,IAAM,yBAAmD,CAAC;AAI1D,IAAM,qBAAqB;AAE3B,SAAS,QAAQ,OAAe,UAA0B;AACxD,SAAO,GAAG,KAAK,GAAG,kBAAkB,GAAG,QAAQ;AACjD;AAEA,IAAM,kBAAkB,oBAAI,IAAI,CAAC,WAAW,aAAa,aAAa,cAAc,QAAQ,KAAK,CAAC;AAElG,SAAS,MAAM,OAAe,KAAqB;AACjD,MAAI,QAAQ,EAAG,QAAO;AACtB,MAAI,QAAQ,IAAK,QAAO;AACxB,SAAO;AACT;AAEA,SAAS,eAAe,QAAkC;AACxD,QAAM,QAAQ,OAAO,UAAU,OAAO,SAAS,YAAY,OAAO,SAAS,aAAa,UAAU;AAClG,SAAO,UAAU,UAAU,eAAe;AAC5C;AAKA,SAAS,aACP,SAC8D;AAC9D,QAAM,SAAuE,CAAC;AAC9E,aAAW,UAAU,SAAS;AAC5B,UAAM,QAAQ,OAAO,SAAS;AAC9B,UAAM,WAAW,OAAO,KAAK,CAAC,UAAU,MAAM,UAAU,KAAK;AAC7D,QAAI,SAAU,UAAS,QAAQ,KAAK,MAAM;AAAA,QACrC,QAAO,KAAK,EAAE,OAAO,SAAS,CAAC,MAAM,EAAE,CAAC;AAAA,EAC/C;AACA,SAAO;AACT;AAEA,IAAM,cACJ;AAEF,IAAMC,eAAqD;AAAA,EACzD,WAAW;AAAA,EACX,SAAS;AAAA,EACT,UAAU;AAAA,EACV,UAAU;AACZ;AAEA,IAAM,eAAsD;AAAA,EAC1D,WAAW;AAAA,EACX,SAAS;AAAA,EACT,UAAU;AAAA,EACV,UAAU;AACZ;AAOO,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA,kBAAkB;AAAA,EAClB;AACF,GAAoB;AAClB,QAAM,cAAcC,OAAM;AAC1B,QAAM,CAAC,SAAS,UAAU,IAAIC,WAA4B,yBAAyB;AACnF,QAAM,CAAC,SAAS,eAAe,IAAIA,WAAmE,IAAI;AAC1G,QAAM,CAAC,YAAY,aAAa,IAAIA,WAA2C,CAAC,CAAC;AACjF,QAAM,CAAC,WAAW,YAAY,IAAIA,WAA2C,CAAC,CAAC;AAC/E,QAAM,CAAC,aAAa,cAAc,IAAIA,WAAyC,CAAC,CAAC;AACjF,QAAM,CAAC,OAAO,QAAQ,IAAIA,WAAqD,IAAI;AACnF,QAAM,CAAC,YAAY,aAAa,IAAIA,WAAwB,IAAI;AAChE,QAAM,CAAC,eAAe,gBAAgB,IAAIA,WAAwB,IAAI;AACtE,QAAM,CAAC,QAAQ,SAAS,IAAIA,WAAS,KAAK;AAC1C,QAAM,CAAC,OAAO,QAAQ,IAAIA,WAA0C,EAAE,GAAI,kBAAkB,CAAC,EAAG,CAAC;AACjG,QAAM,CAAC,aAAa,cAAc,IAAIA,WAA2C,CAAC,CAAC;AACnF,QAAM,CAAC,YAAY,aAAa,IAAIA,WAAwB,IAAI;AAChE,QAAM,CAAC,UAAU,WAAW,IAAIA,WAAS,KAAK;AAE9C,QAAM,WAAWC,SAAO,oBAAI,IAAgC,CAAC;AAC7D,QAAM,WAAWA,SAAO,KAAK;AAC7B,QAAM,eAAeA,SAAO,CAAC;AAG7B,QAAM,aAAaA,SAAiE,IAAI;AAExF,QAAM,aAAaC,cAAY,CAAC,SAAmE;AACjG,eAAW,UAAU;AACrB,oBAAgB,IAAI;AAAA,EACtB,GAAG,CAAC,CAAC;AAKL,QAAM,aAAa,MAAM,WAAW,WAAW,MAAM,WAAW,UAAU,MAAM,QAAQ;AAIxF,EAAAC,YAAU,MAAM;AACd,eAAW,CAAC,YAAY,uBAAuB,YAAY,OAAO,CAAC;AAAA,EACrE,GAAG,CAAC,UAAU,CAAC;AAEf,QAAM,cAAcC,SAAQ,MAAM,sBAAsB,YAAY,OAAO,GAAG,CAAC,YAAY,OAAO,CAAC;AAInG,QAAM,QAAQA;AAAA,IACZ,MAAO,aAAa,SAAY,OAAO,uBAAuB,aAAa,QAAQ;AAAA,IACnF,CAAC,UAAU,WAAW;AAAA,EACxB;AACA,QAAM,YAAY,UAAU,QAAQ,MAAM,SAAS;AACnD,QAAM,YAAYA,SAAQ,MAAM,IAAI,KAAK,SAAS,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;AACjG,QAAM,gBAAgBA,SAAQ,MAAM;AAClC,UAAM,MAAM,oBAAI,IAAgC;AAChD,eAAW,QAAQ,SAAS,CAAC,GAAG;AAC9B,iBAAW,QAAQ,KAAK,MAAO,KAAI,IAAI,QAAQ,KAAK,OAAO,KAAK,QAAQ,GAAG,IAAI;AAAA,IACjF;AACA,WAAO;AAAA,EACT,GAAG,CAAC,KAAK,CAAC;AACV,QAAM,YAAYA;AAAA,IAChB,OAAO,SAAS,CAAC,GAAG,OAAO,CAAC,SAAS,KAAK,SAAS,OAAO,EAAE,IAAI,CAAC,SAAS,KAAK,GAAG;AAAA,IAClF,CAAC,KAAK;AAAA,EACR;AAEA,QAAM,cAAcA,SAAQ,MAAM;AAChC,QAAI,UAAU,KAAM,QAAO;AAC3B,QAAI,CAAC,YAAY,KAAK,CAAC,QAAQ,IAAI,OAAO,MAAM,KAAK,EAAG,QAAO;AAC/D,QAAI,CAAC,QAAQ,KAAK,CAAC,WAAW,OAAO,OAAO,MAAM,QAAQ,EAAG,QAAO;AACpE,WAAO;AAAA,EACT,GAAG,CAAC,SAAS,OAAO,WAAW,CAAC;AAEhC,QAAM,eAAeF,cAAY,CAAC,KAAa,YAA2B;AACxE,kBAAc,CAAC,YAAY;AACzB,UAAI,YAAY,MAAM;AACpB,YAAI,EAAE,OAAO,SAAU,QAAO;AAC9B,cAAM,OAAO,EAAE,GAAG,QAAQ;AAC1B,eAAO,KAAK,GAAG;AACf,eAAO;AAAA,MACT;AACA,UAAI,QAAQ,GAAG,MAAM,QAAS,QAAO;AACrC,aAAO,EAAE,GAAG,SAAS,CAAC,GAAG,GAAG,QAAQ;AAAA,IACtC,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AAEL,QAAM,cAAcA,cAAY,CAAC,OAAe,YAA2B;AACzE,iBAAa,CAAC,YAAY;AACxB,UAAI,YAAY,MAAM;AACpB,YAAI,EAAE,SAAS,SAAU,QAAO;AAChC,cAAM,OAAO,EAAE,GAAG,QAAQ;AAC1B,eAAO,KAAK,KAAK;AACjB,eAAO;AAAA,MACT;AACA,aAAO,EAAE,GAAG,SAAS,CAAC,KAAK,GAAG,QAAQ;AAAA,IACxC,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AAEL,QAAM,gBAAgBA,cAAY,CAAC,OAAe,YAAqB;AACrE,mBAAe,CAAC,YAAY;AAC1B,UAAI,QAAS,QAAO,SAAS,UAAU,UAAU,EAAE,GAAG,SAAS,CAAC,KAAK,GAAG,KAAK;AAC7E,UAAI,EAAE,SAAS,SAAU,QAAO;AAChC,YAAM,OAAO,EAAE,GAAG,QAAQ;AAC1B,aAAO,KAAK,KAAK;AACjB,aAAO;AAAA,IACT,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AAEL,QAAM,YAAYA,cAAY,CAAC,OAAe,aAAqB;AACjE,aAAS,EAAE,OAAO,SAAS,CAAC;AAC5B,aAAS,QAAQ,IAAI,QAAQ,OAAO,QAAQ,CAAC,GAAG,MAAM;AAAA,EACxD,GAAG,CAAC,CAAC;AAEL,QAAM,YAAYA;AAAA,IAChB,CAAC,KAAoB,WAA6B;AAGhD,mBAAa,QAAQ,IAAI,IAAI,OAAO,EAAE,GAAG,IAAI;AAC7C,iBAAW;AAAA,QACT,OAAO,IAAI;AAAA,QACX,UAAU,OAAO;AAAA,QACjB,MAAM,qBAAqB,QAAQ,IAAI,OAAO,OAAO,EAAE,KAAK,IAAI;AAAA,MAClE,CAAC;AAAA,IACH;AAAA,IACA,CAAC,cAAc,UAAU;AAAA,EAC3B;AAIA,QAAM,iBAAiBA;AAAA,IACrB,OAAO,KAAoB,QAA0B,UAA2B;AAC9E,UAAI,CAAC,SAAU;AACf,YAAM,SAAS,EAAE,GAAG,IAAI,QAAQ,CAAC,OAAO,EAAE,GAAG,MAAM;AACnD,iBAAW,CAAC,YAAY,qBAAqB,SAAS,IAAI,IAAI,OAAO,IAAI,KAAK,CAAC;AAC/E,kBAAY,IAAI,IAAI,IAAI;AACxB,oBAAc,IAAI,IAAI,IAAI;AAE1B,UAAI;AACJ,UAAI;AACF,kBAAU,MAAM,SAAS,EAAE,KAAK,UAAU,OAAO,IAAI,OAAO,OAAO,CAAC;AAAA,MACtE,SAAS,OAAO;AACd,kBAAU,EAAE,WAAW,OAAO,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE;AAAA,MAC9F;AAEA,oBAAc,IAAI,IAAI,KAAK;AAC3B,UAAI,QAAQ,WAAW;AACrB,cAAM,YAAY,QAAQ;AAC1B,YAAI,UAAW,YAAW,CAAC,YAAY,wBAAwB,SAAS,IAAI,IAAI,SAAS,CAAC;AAC1F;AAAA,MACF;AACA,iBAAW,CAAC,YAAY,wBAAwB,SAAS,IAAI,IAAI,OAAO,EAAE,CAAC;AAC3E,YAAM,WAAW,sBAAsB,QAAQ,OAAO,MAAM;AAC5D;AAAA,QACE,IAAI;AAAA,QACJ,aAAa,KACT,kBAAkB,OAAO,MAAM,KAAK,QAAQ,KAAK,KACjD,kBAAkB,OAAO,MAAM,OAAO,QAAQ,KAAK,QAAQ,KAAK;AAAA,MACtE;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,UAAU,aAAa,aAAa;AAAA,EAC/C;AAEA,QAAM,aAAaA;AAAA,IACjB,OAAO,KAAoB,QAA0B,SAAiB;AACpE,YAAM,OAAO,WAAW;AACxB,UAAI,SAAS,QAAQ,KAAK,UAAU,IAAI,MAAM,KAAK,aAAa,OAAO,GAAI;AAC3E,UAAI,SAAS,QAAS;AACtB,eAAS,UAAU;AACnB,UAAI;AACF,cAAM,MAAM,QAAQ,IAAI,IAAI,OAAO,EAAE;AACrC,cAAM,SAAS,mBAAmB,QAAQ,IAAI;AAC9C,YAAI,CAAC,OAAO,WAAW;AAGrB,uBAAa,KAAK,OAAO,KAAK;AAC9B,qBAAW,EAAE,OAAO,IAAI,IAAI,UAAU,OAAO,IAAI,KAAK,CAAC;AACvD;AAAA,QACF;AACA,qBAAa,KAAK,IAAI;AACtB,mBAAW,IAAI;AACf,YAAI,oBAAoB,IAAI,OAAO,OAAO,EAAE,GAAG,OAAO,KAAK,EAAG;AAC9D,cAAM,eAAe,KAAK,QAAQ,OAAO,KAAK;AAAA,MAChD,UAAE;AACA,iBAAS,UAAU;AAAA,MACrB;AAAA,IACF;AAAA,IACA,CAAC,gBAAgB,cAAc,UAAU;AAAA,EAC3C;AAEA,QAAM,aAAaA;AAAA,IACjB,CAAC,KAAoB,WAA6B;AAChD,mBAAa,QAAQ,IAAI,IAAI,OAAO,EAAE,GAAG,IAAI;AAC7C,iBAAW,IAAI;AACf,gBAAU,IAAI,IAAI,OAAO,EAAE;AAAA,IAC7B;AAAA,IACA,CAAC,WAAW,cAAc,UAAU;AAAA,EACtC;AAEA,QAAM,gBAAgBA;AAAA,IACpB,OAAO,QAAuB;AAC5B,UAAI,CAAC,SAAU;AACf,uBAAiB,IAAI;AACrB,kBAAY,IAAI,IAAI,IAAI;AACxB,iBAAW,CAAC,YAAY,sBAAsB,SAAS,IAAI,EAAE,CAAC;AAE9D,UAAI;AACJ,UAAI;AACF,kBAAU,MAAM,SAAS,GAAG;AAAA,MAC9B,SAAS,OAAO;AACd,kBAAU,EAAE,WAAW,OAAO,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE;AAAA,MAC9F;AACA,UAAI,QAAQ,UAAW;AACvB,iBAAW,CAAC,YAAY,yBAAyB,SAAS,IAAI,EAAE,CAAC;AACjE,kBAAY,IAAI,IAAI,oBAAoB,mBAAmB,SAAS,GAAG,CAAC,KAAK,QAAQ,KAAK,EAAE;AAAA,IAC9F;AAAA,IACA,CAAC,SAAS,UAAU,WAAW;AAAA,EACjC;AAEA,QAAM,aAAaA,cAAY,MAAM;AACnC,aAAS,EAAE,GAAI,kBAAkB,CAAC,EAAG,CAAC;AACtC,mBAAe,CAAC,CAAC;AACjB,kBAAc,IAAI;AAAA,EACpB,GAAG,CAAC,cAAc,CAAC;AAEnB,QAAM,UAAUA,cAAY,MAAM;AAChC,eAAW;AACX,cAAU,IAAI;AAAA,EAChB,GAAG,CAAC,UAAU,CAAC;AAEf,QAAM,cAAcA,cAAY,YAAY;AAC1C,QAAI,CAAC,SAAU;AACf,UAAM,YAAY,sBAAsB,SAAS,KAAK;AACtD,QAAI,CAAC,UAAU,WAAW;AACxB,qBAAe,UAAU,UAAU;AACnC,oBAAc,UAAU,KAAK;AAC7B;AAAA,IACF;AACA,mBAAe,CAAC,CAAC;AACjB,kBAAc,IAAI;AAElB,iBAAa,WAAW;AACxB,UAAM,UAAU,GAAG,WAAW,UAAU,aAAa,OAAO;AAC5D,eAAW,CAAC,YAAY,sBAAsB,SAAS,EAAE,IAAI,SAAS,QAAQ,UAAU,MAAM,CAAC,CAAC;AAChG,gBAAY,IAAI;AAEhB,QAAI;AACJ,QAAI;AACF,gBAAU,MAAM,SAAS,UAAU,KAAK;AAAA,IAC1C,SAAS,OAAO;AACd,gBAAU,EAAE,WAAW,OAAO,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE;AAAA,IAC9F;AAEA,gBAAY,KAAK;AACjB,QAAI,QAAQ,WAAW;AACrB,iBAAW,CAAC,YAAY,wBAAwB,SAAS,SAAS,QAAQ,KAAK,CAAC;AAChF,gBAAU,KAAK;AACf,iBAAW;AACX;AAAA,IACF;AAGA,eAAW,CAAC,YAAY,yBAAyB,SAAS,OAAO,CAAC;AAClE,kBAAc,QAAQ,KAAK;AAAA,EAC7B,GAAG,CAAC,SAAS,OAAO,aAAa,UAAU,UAAU,CAAC;AAEtD,QAAM,oBAAoBA;AAAA,IACxB,CAAC,UAA2C;AAC1C,UAAI,YAAY,KAAM;AACtB,YAAM,SAAS,MAAM;AACrB,YAAM,QAAQ,OAAO,SAAS;AAC9B,YAAM,WAAW,OAAO,SAAS;AACjC,UAAI,UAAU,UAAa,aAAa,OAAW;AACnD,YAAM,WAAW,YAAY,UAAU,CAAC,QAAQ,IAAI,OAAO,KAAK;AAChE,YAAM,cAAc,QAAQ,UAAU,CAAC,WAAW,OAAO,OAAO,QAAQ;AACxE,UAAI,WAAW,KAAK,cAAc,EAAG;AAErC,UAAI,MAAM,QAAQ,SAAS;AACzB,cAAM,MAAM,YAAY,QAAQ;AAChC,cAAM,SAAS,QAAQ,WAAW;AAClC,YAAI,CAAC,OAAO,CAAC,OAAQ;AACrB,YAAI,aAAa,CAAC,YAAY,OAAO,aAAa,SAAS,IAAI,aAAa,KAAM;AAClF,YAAI,OAAO,SAAS,UAAW;AAC/B,YAAI,CAAC,2BAA2B,QAAQ,IAAI,MAAM,EAAG;AACrD,cAAM,eAAe;AACrB,kBAAU,KAAK,MAAM;AACrB;AAAA,MACF;AACA,UAAI,CAAC,gBAAgB,IAAI,MAAM,GAAG,EAAG;AACrC,YAAM,eAAe;AAErB,UAAI,UAAU;AACd,UAAI,aAAa;AACjB,UAAI,MAAM,QAAQ,UAAW,WAAU,MAAM,WAAW,GAAG,YAAY,SAAS,CAAC;AACjF,UAAI,MAAM,QAAQ,YAAa,WAAU,MAAM,WAAW,GAAG,YAAY,SAAS,CAAC;AACnF,UAAI,MAAM,QAAQ,YAAa,cAAa,MAAM,cAAc,GAAG,QAAQ,SAAS,CAAC;AACrF,UAAI,MAAM,QAAQ,aAAc,cAAa,MAAM,cAAc,GAAG,QAAQ,SAAS,CAAC;AACtF,UAAI,MAAM,QAAQ,OAAQ,cAAa;AACvC,UAAI,MAAM,QAAQ,MAAO,cAAa,QAAQ,SAAS;AAEvD,YAAM,iBAAiB,YAAY,OAAO;AAC1C,YAAM,oBAAoB,QAAQ,UAAU;AAC5C,UAAI,CAAC,kBAAkB,CAAC,kBAAmB;AAC3C,gBAAU,eAAe,IAAI,kBAAkB,EAAE;AAAA,IACnD;AAAA,IACA,CAAC,WAAW,SAAS,SAAS,WAAW,UAAU,WAAW,WAAW;AAAA,EAC3E;AAKA,MAAI,MAAM,WAAW,UAAU,MAAM,WAAW,WAAW;AACzD,WACE,gBAAAL,OAAC,SAAI,WAAW,aAAa,aAAa,EAAE,IACzC;AAAA;AAAA,MACD,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,aAAU;AAAA,UACV,aAAU;AAAA,UACV,WAAU;AAAA,UAEV;AAAA,4BAAAA,OAAC,UAAK,WAAU,WAAU;AAAA;AAAA,cAAS;AAAA,eAAQ;AAAA,YAC1C,MAAM,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAG,eAAe,EAAE,GAAG,CAAC,GAAG,UACxD,gBAAAD,MAAC,SAAgB,WAAU,6CAA4C,eAAW,QAAxE,KAAyE,CACpF;AAAA;AAAA;AAAA,MACH;AAAA,OACF;AAAA,EAEJ;AAEA,MAAI,MAAM,WAAW,SAAS;AAC5B,WACE,gBAAAC,OAAC,SAAI,WAAW,aAAa,aAAa,EAAE,IACzC;AAAA;AAAA,MACD,gBAAAA,OAAC,SAAI,MAAK,SAAQ,WAAU,uEAC1B;AAAA,wBAAAD,MAAC,OAAE,WAAU,wCAAwC,gBAAM,SAAQ;AAAA,QACnE,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS,MAAM;AAAA,YACf,WAAU;AAAA,YACX;AAAA;AAAA,QAED;AAAA,SACF;AAAA,OACF;AAAA,EAEJ;AAEA,QAAM,UACJ,UAAU,YAAY,CAAC,YACrB,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,MAAM;AAAA,MACN,OAAO;AAAA,MACP;AAAA,MACA,UAAU,MAAM,KAAK,YAAY;AAAA,MACjC,UAAU,MAAM;AACd,kBAAU,KAAK;AACf,mBAAW;AAAA,MACb;AAAA;AAAA,EACF,IACE;AAIN,MAAI,YAAY,WAAW,KAAK,CAAC,WAAW;AAC1C,WACE,gBAAAC,OAAC,SAAI,WAAW,aAAa,aAAa,EAAE,IACzC;AAAA;AAAA,MACA,WACC,gBAAAA,OAAC,SAAI,WAAU,wEACb;AAAA,wBAAAD,MAAC,OAAE,WAAU,uCAAuC,gBAAM,OAAM;AAAA,QAC/D,MAAM,eACL,gBAAAA,MAAC,OAAE,WAAU,uDAAuD,gBAAM,aAAY;AAAA,QAExF,gBAAAC,OAAC,SAAI,WAAU,yDACZ;AAAA,gBAAM,WACJ,eAAe,MAAM,MAAM,IAC1B,MAAM,SAEN,gBAAAD;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAU,MAAM,OAA4B;AAAA,cAC5C,WAAU;AAAA,cAER,gBAAM,OAA4B;AAAA;AAAA,UACtC;AAAA,UAEH,YACC,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS;AAAA,cACT,WAAU;AAAA,cAET;AAAA;AAAA,UACH;AAAA,WAEJ;AAAA,SACF;AAAA,OAEJ;AAAA,EAEJ;AAEA,QAAM,YAAY,QAAQ,KAAK,CAAC,WAAW,OAAO,gBAAgB,MAAS;AAC3E,QAAM,oBAAoB,aAAa,aAAa;AACpD,QAAM,aAAa,QAAQ,UAAU,oBAAoB,IAAI;AAE7D,QAAM,gBAAgB,SAAS,CAAC,GAAG,OAAO,CAAC,SAAS,KAAK,SAAS,SAAS,EAAE;AAC7E,QAAM,aAAa,UAAU;AAC7B,QAAM,gBAAgB,SAAS,CAAC,GAAG,OAAO,CAAC,SAAS,KAAK,SAAS,SAAS,EAAE;AAC7E,QAAM,oBAAoB,SAAS,CAAC,GAAG,OAAO,CAAC,OAAO,SAAS,QAAQ,KAAK,MAAM,QAAQ,CAAC;AAC3F,QAAM,gBAAgB;AAAA,IACpB,eAAe,IAAI,GAAG,YAAY,aAAa,gBAAgB,IAAI,qBAAqB,IAAI,SAAS,OAAO,MAAM;AAAA,IAClH,aAAa,IAAI,GAAG,UAAU,WAAW;AAAA,IACzC,eAAe,IAAI,GAAG,YAAY,aAAa;AAAA,EACjD,EACG,OAAO,CAAC,SAAS,SAAS,IAAI,EAC9B,KAAK,QAAK;AAEb,QAAM,YAAY,YAChB,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC,2BAAwB;AAAA,MACxB,WAAU;AAAA,MAEV;AAAA,wBAAAA,OAAC,SAAI,WAAU,WACb;AAAA,0BAAAD,MAAC,OAAE,WAAU,+EAA8E,8BAE3F;AAAA,UACA,gBAAAA,MAAC,OAAE,WAAU,qDAAqD,yBAAc;AAAA,WAClF;AAAA,SACE,eAAe,gBACf,gBAAAC,OAAC,SAAI,WAAU,2BACZ;AAAA,yBACC,gBAAAD;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,cAAY,kCAAkC,OAAO;AAAA,cACrD,SAAS;AAAA,cACT,WAAU;AAAA,cACX;AAAA;AAAA,UAED;AAAA,UAED,eACC,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,cAAY,kCAAkC,OAAO;AAAA,cACrD,SAAS;AAAA,cACT,WAAU;AAAA,cACX;AAAA;AAAA,UAED;AAAA,WAEJ;AAAA;AAAA;AAAA,EAEJ,IACE;AAEJ,SACE,gBAAAC,OAAC,SAAI,WAAW,aAAa,aAAa,EAAE,IACzC;AAAA;AAAA,IACA;AAAA,IACD,gBAAAD,MAAC,SAAI,WAAU,8DACb,0BAAAC;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,cAAY;AAAA,QACZ,WAAU;AAAA,QACV,WAAW;AAAA,QAEX;AAAA,0BAAAD,MAAC,WACC,0BAAAC,OAAC,QAAG,MAAK,OAAM,WAAU,oFACtB;AAAA,oBAAQ,IAAI,CAAC,WACZ,gBAAAD;AAAA,cAAC;AAAA;AAAA,gBAEC,MAAK;AAAA,gBACL,OAAM;AAAA,gBACN,WAAW,yBAAyB,eAAe,MAAM,CAAC;AAAA,gBAEzD,iBAAO;AAAA;AAAA,cALH,OAAO;AAAA,YAMd,CACD;AAAA,YACA,qBACC,gBAAAA,MAAC,QAAG,MAAK,gBAAe,OAAM,OAAM,WAAU,8BAC5C,0BAAAA,MAAC,UAAK,WAAU,WAAW,sBAAY,WAAW,eAAc,GAClE;AAAA,aAEJ,GACF;AAAA,UACA,gBAAAC,OAAC,WACE;AAAA,wBAAY,IAAI,CAAC,QAAQ;AACxB,oBAAM,WAAW,mBAAmB,SAAS,GAAG;AAChD,oBAAM,UAAU,IAAI,MAAM;AAC1B,oBAAM,WAAW,UAAU,IAAI,EAAE;AACjC,oBAAM,UAAU,YAAY,UAAU,IAAI,IAAI,EAAE,IAAI;AACpD,oBAAM,aAAa,SAAS,SAAS;AACrC,qBACE,gBAAAA,OAACQ,WAAA,EACC;AAAA,gCAAAR;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,aAAW;AAAA,oBACX,yBAAuB,SAAS;AAAA,oBAChC,WAAW,0BAA0B,UAAU,eAAe,EAAE,IAAI,aAAa,0BAA0B,EAAE;AAAA,oBAE5G;AAAA,8BAAQ,IAAI,CAAC,WAAW;AACvB,8BAAM,MAAM,QAAQ,IAAI,IAAI,OAAO,EAAE;AACrC,8BAAM,aAAa,2BAA2B,QAAQ,IAAI,MAAM;AAChE,8BAAM,WACJ,CAAC,aACD,aAAa,UACb,OAAO,aAAa,SACpB,IAAI,aAAa,QACjB;AACF,8BAAM,QAAQ,IAAI,OAAO,OAAO,EAAE,KAAK;AACvC,8BAAM,YAAY,SAAS,UAAU,IAAI,MAAM,QAAQ,aAAa,OAAO;AAC3E,8BAAM,YAAY,WAAW,GAAG;AAChC,8BAAM,SACJ,gBAAgB,OACZ,IAAI,OAAO,YAAY,CAAC,GAAG,MAAM,OAAO,OAAO,QAAQ,CAAC,GAAG,KAC3D,YAAY,UAAU,IAAI,MAAM,YAAY,aAAa,OAAO;AACtE,8BAAM,SAAS,IAAI,UAAU,OAAO,EAAE;AACtC,8BAAM,UAAU,GAAG,WAAW,eAAe,IAAI,EAAE,IAAI,OAAO,EAAE;AAChE,8BAAM,WAAW,SAAS,SAAS,YAAY,cAAc,IAAI,GAAG,IAAI;AAExE,4BAAI,aAAa,UAAU;AACzB,iCACE,gBAAAA,OAAC,QAAmB,MAAK,YAAW,WAAW,eAAe,eAAe,MAAM,CAAC,IAClF;AAAA,4CAAAD;AAAA,8BAAC;AAAA;AAAA,gCACC;AAAA,gCACA;AAAA,gCACA,MAAM,QAAQ;AAAA,gCACd,SAAS,cAAc;AAAA,gCACvB,aAAa,cAAc,SAAY,SAAY;AAAA,gCACnD,QAAQ,CAAC,SAAS,WAAW,EAAE,OAAO,IAAI,IAAI,UAAU,OAAO,IAAI,KAAK,CAAC;AAAA,gCACzE,UAAU,CAAC,SAAS,KAAK,WAAW,KAAK,QAAQ,IAAI;AAAA,gCACrD,UAAU,MAAM,WAAW,KAAK,MAAM;AAAA;AAAA,4BACxC;AAAA,4BACC,cAAc,UACb,gBAAAA,MAAC,OAAE,IAAI,SAAS,MAAK,SAAQ,WAAU,8CACpC,qBACH;AAAA,+BAdK,OAAO,EAgBhB;AAAA,wBAEJ;AAEA,4BAAI,OAAO,SAAS,aAAa,UAAU;AACzC,iCACE,gBAAAA,MAAC,QAAmB,MAAK,YAAW,WAAW,aAAa,eAAe,MAAM,CAAC,IAChF,0BAAAA;AAAA,4BAAC;AAAA;AAAA,8BACC,MAAK;AAAA,8BACL,SAAS,UAAU;AAAA,8BACnB,cAAY,GAAG,OAAO,MAAM,KAAK,QAAQ;AAAA,8BACzC,wBAAsB,IAAI;AAAA,8BAC1B,2BAAyB,OAAO;AAAA,8BAChC,UAAU,SAAS,IAAI;AAAA,8BACvB,KAAK,CAAC,SAAS;AACb,yCAAS,QAAQ,IAAI,KAAK,IAAI;AAAA,8BAChC;AAAA,8BACA,SAAS,MAAM,SAAS,EAAE,OAAO,IAAI,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,8BAC9D,UAAU,CAAC,UAAU,KAAK,eAAe,KAAK,QAAQ,MAAM,OAAO,OAAO;AAAA,8BAC1E,WAAU;AAAA;AAAA,0BACZ,KAdO,OAAO,EAehB;AAAA,wBAEJ;AAEA,8BAAM,UAAU,aAAa,sBAAsB,QAAQ,OAAO,MAAM,IAAI;AAC5E,+BACE,gBAAAA;AAAA,0BAAC;AAAA;AAAA,4BAEC,MAAK;AAAA,4BACL,iBAAe,WAAW,SAAY;AAAA,4BACtC,wBAAsB,IAAI;AAAA,4BAC1B,2BAAyB,OAAO;AAAA,4BAChC,UAAU,SAAS,IAAI;AAAA,4BACvB,KAAK,CAAC,SAAS;AACb,uCAAS,QAAQ,IAAI,KAAK,IAAI;AAAA,4BAChC;AAAA,4BACA,SAAS,MAAM,SAAS,EAAE,OAAO,IAAI,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,4BAC9D,SAAS,MAAM;AACb,kCAAI,SAAU,WAAU,KAAK,MAAM;AAAA,4BACrC;AAAA,4BACA,WAAW,8EAA8E;AAAA,8BACvF;AAAA,4BACF,CAAC,IAAI,WAAW,gBAAgB,EAAE;AAAA,4BAElC,0BAAAC,OAAC,UAAK,WAAU,+CACb;AAAA,qCAAO,OAAO,QAAQ,CAAC,GAAG,MAAM,cAC/B,gBAAAD,MAAC,UAAK,WAAU,+IAA8I,oBAE9J;AAAA,8BAED,WACC,gBAAAC,OAAC,UAAK,WAAU,6DACd;AAAA,gDAAAD,MAAC,UAAK,WAAU,wEACb,gCAAsB,QAAQ,SAAS,QAAQ,MAAM,KAAK,UAC7D;AAAA,gCACA,gBAAAA,MAAC,UAAK,eAAY,QAAO,WAAU,yBAAwB,oBAE3D;AAAA,gCACA,gBAAAA,MAAC,UAAK,WAAU,yCACb,gCAAsB,QAAQ,SAAS,OAAO,MAAM,KAAK,UAC5D;AAAA,iCACF,IAEA,gBAAAA;AAAA,gCAAC;AAAA;AAAA,kCACC,WACE,aACI,qEACA,YAAY,KACV,0BACA;AAAA,kCAGP,sBAAY,KAAM,aAAa,WAAM,QAAS;AAAA;AAAA,8BACjD;AAAA,8BAED,UACC,gBAAAA;AAAA,gCAAC;AAAA;AAAA,kCACC,SAAS,GAAG,WAAW,WAAW,IAAI,EAAE,IAAI,OAAO,EAAE;AAAA,kCACrD,cAAc,OAAO;AAAA,kCACrB;AAAA,kCACA;AAAA,kCACA,MAAM,eAAe;AAAA,kCACrB,UAAU,MAAM,cAAc,CAAC,YAAa,YAAY,MAAM,OAAO,GAAI;AAAA;AAAA,8BAC3E;AAAA,+BAEJ;AAAA;AAAA,0BA1DK,OAAO;AAAA,wBA2Dd;AAAA,sBAEJ,CAAC;AAAA,sBACA,qBACC,gBAAAA,MAAC,QAAG,MAAK,YAAW,WAAU,wBAC3B,sBACC,WACE,gBAAAA;AAAA,wBAAC;AAAA;AAAA,0BACC,OAAO,IAAI;AAAA,0BACX,MAAM,QAAQ;AAAA,0BACd;AAAA,0BACA,UAAU;AAAA,0BACV,UAAU;AAAA;AAAA,sBACZ,IAEA,IAAI,aAAa,OAAO,OAAO,kBAAkB,IAAI,KACvD,gBAAAC,OAAC,UAAK,WAAU,oCACd;AAAA,wCAAAD;AAAA,0BAAC;AAAA;AAAA,4BACC,MAAK;AAAA,4BACL,cAAY,kBAAkB,QAAQ;AAAA,4BACtC,SAAS,MAAM,KAAK,cAAc,GAAG;AAAA,4BACrC,WAAU;AAAA,4BACX;AAAA;AAAA,wBAED;AAAA,wBACA,gBAAAA;AAAA,0BAAC;AAAA;AAAA,4BACC,MAAK;AAAA,4BACL,cAAY,QAAQ,QAAQ;AAAA,4BAC5B,SAAS,MAAM,iBAAiB,IAAI;AAAA,4BACpC,WAAU;AAAA,4BACX;AAAA;AAAA,wBAED;AAAA,yBACF,IAEA,gBAAAA;AAAA,wBAAC;AAAA;AAAA,0BACC,MAAK;AAAA,0BACL,cAAY,UAAU,QAAQ;AAAA,0BAC9B,SAAS,MAAM,iBAAiB,IAAI,EAAE;AAAA,0BACtC,WAAU;AAAA,0BAEV,0BAAAC;AAAA,4BAAC;AAAA;AAAA,8BACC,SAAQ;AAAA,8BACR,WAAU;AAAA,8BACV,MAAK;AAAA,8BACL,QAAO;AAAA,8BACP,aAAY;AAAA,8BACZ,eAAc;AAAA,8BACd,gBAAe;AAAA,8BACf,eAAW;AAAA,8BAEX;AAAA,gDAAAD,MAAC,cAAS,QAAO,gBAAe;AAAA,gCAChC,gBAAAA,MAAC,UAAK,GAAE,kFAAiF;AAAA;AAAA;AAAA,0BAC3F;AAAA;AAAA,sBACF,GAEJ;AAAA;AAAA;AAAA,gBAEJ;AAAA,gBACC,aAAa,UACZ,gBAAAA,MAAC,QAAG,MAAK,OAAM,WAAU,0BACvB,0BAAAA,MAAC,QAAG,MAAK,YAAW,SAAS,YAAY,WAAU,aACjD,0BAAAA,MAAC,OAAE,MAAK,SAAQ,WAAU,uEACvB,oBACH,GACF,GACF;AAAA,mBAtMW,IAAI,EAwMnB;AAAA,YAEJ,CAAC;AAAA,YACA,aACC,UAAU,IAAI,CAAC,QAAQ;AACrB,oBAAM,WAAW,mBAAmB,SAAS,GAAG;AAChD,qBACE,gBAAAC;AAAA,gBAAC;AAAA;AAAA,kBAEC,MAAK;AAAA,kBACL,yBAAsB;AAAA,kBACtB,WAAU;AAAA,kBAET;AAAA,4BAAQ,IAAI,CAAC,QAAQ,gBAAgB;AACpC,4BAAM,aAAa,2BAA2B,QAAQ,IAAI,MAAM;AAChE,4BAAM,QAAQ,IAAI,OAAO,OAAO,EAAE,KAAK;AACvC,4BAAM,UAAU,aAAa,sBAAsB,QAAQ,OAAO,MAAM,IAAI;AAC5E,6BACE,gBAAAD,MAAC,QAAmB,MAAK,YAAW,WAAW,aAAa,eAAe,MAAM,CAAC,IAChF,0BAAAC,OAAC,UAAK,WAAU,+CACb;AAAA,wCAAgB,KACf,gBAAAD,MAAC,UAAK,WAAU,uIAAsI,iBAEtJ;AAAA,wBAEF,gBAAAA;AAAA,0BAAC;AAAA;AAAA,4BACC,WAAW,gBAAgB,YAAY,KAAK,0BAA0B,0BAA0B;AAAA,4BAE/F,sBAAY,KAAM,aAAa,WAAM,QAAS;AAAA;AAAA,wBACjD;AAAA,yBACF,KAZO,OAAO,EAahB;AAAA,oBAEJ,CAAC;AAAA,oBACD,gBAAAA,MAAC,QAAG,MAAK,YAAW,WAAU,wBAC5B,0BAAAA;AAAA,sBAAC;AAAA;AAAA,wBACC,OAAO,IAAI;AAAA,wBACX,MAAK;AAAA,wBACL;AAAA,wBACA,UAAU;AAAA,wBACV,UAAU;AAAA;AAAA,oBACZ,GACF;AAAA;AAAA;AAAA,gBAlCK,IAAI;AAAA,cAmCX;AAAA,YAEJ,CAAC;AAAA,aACL;AAAA,UACC,aACC,gBAAAA,MAAC,WACC,0BAAAC,OAAC,QAAG,MAAK,OAAM,WAAU,4BACtB;AAAA,oBAAQ,IAAI,CAAC,WACZ,gBAAAD;AAAA,cAAC;AAAA;AAAA,gBAEC,MAAK;AAAA,gBACL,WAAW,mDAAmD,eAAe,MAAM,CAAC;AAAA,gBAEnF,iBAAO,cAAc,sBAAsB,QAAQ,OAAO,YAAY,WAAW,GAAG,MAAM,IAAI;AAAA;AAAA,cAJ1F,OAAO;AAAA,YAKd,CACD;AAAA,YACA,qBAAqB,gBAAAA,MAAC,QAAG,MAAK,YAAW;AAAA,aAC5C,GACF;AAAA;AAAA;AAAA,IAEJ,GACF;AAAA,IAEC,YACC,CAAC,cACA,WACC,gBAAAC;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS;AAAA,QACT,WAAU;AAAA,QAEV;AAAA,0BAAAA;AAAA,YAAC;AAAA;AAAA,cACC,SAAQ;AAAA,cACR,WAAU;AAAA,cACV,MAAK;AAAA,cACL,QAAO;AAAA,cACP,aAAY;AAAA,cACZ,eAAc;AAAA,cACd,gBAAe;AAAA,cACf,eAAW;AAAA,cAEX;AAAA,gCAAAD,MAAC,UAAK,IAAG,MAAK,IAAG,KAAI,IAAG,MAAK,IAAG,MAAK;AAAA,gBACrC,gBAAAA,MAAC,UAAK,IAAG,KAAI,IAAG,MAAK,IAAG,MAAK,IAAG,MAAK;AAAA;AAAA;AAAA,UACvC;AAAA,UACC;AAAA;AAAA;AAAA,IACH;AAAA,KAEN;AAEJ;AAaA,SAAS,cAAc,EAAE,OAAO,MAAM,UAAU,UAAU,SAAS,GAAuB;AACxF,QAAM,OAAO,SAAS,YAAY,sBAAsB,QAAQ,KAAK,SAAS,UAAU,WAAW,QAAQ,KAAK,cAAc,QAAQ;AACtI,SACE,gBAAAC,OAAC,UAAK,WAAU,oCACb;AAAA,gBACC,gBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,cAAY,UAAU,IAAI;AAAA,QAC1B,SAAS,MAAM,SAAS,KAAK;AAAA,QAC7B,WAAU;AAAA,QACX;AAAA;AAAA,IAED;AAAA,IAED,YACC,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,cAAY,UAAU,IAAI;AAAA,QAC1B,SAAS,MAAM,SAAS,KAAK;AAAA,QAC7B,WAAU;AAAA,QACX;AAAA;AAAA,IAED;AAAA,KAEJ;AAEJ;AAeA,SAAS,WAAW,EAAE,QAAQ,UAAU,MAAM,SAAS,aAAa,QAAQ,UAAU,SAAS,GAAoB;AACjH,QAAM,SAAS;AAAA,IACb,cAAc,GAAG,OAAO,MAAM,KAAK,QAAQ;AAAA,IAC3C,gBAAgB,UAAU,OAAO;AAAA,IACjC,oBAAoB;AAAA,IACpB,WAAW;AAAA,IACX,WAAW;AAAA,EACb;AAEA,MAAI,OAAO,SAAS,UAAU;AAC5B,WACE,gBAAAC;AAAA,MAAC;AAAA;AAAA,QACE,GAAG;AAAA,QACJ,OAAO;AAAA,QACP,UAAU,CAAC,UAAU;AACnB,iBAAO,MAAM,OAAO,KAAK;AACzB,mBAAS,MAAM,OAAO,KAAK;AAAA,QAC7B;AAAA,QACA,WAAW,CAAC,UAAU;AACpB,cAAI,MAAM,QAAQ,UAAU;AAC1B,kBAAM,eAAe;AACrB,qBAAS;AAAA,UACX;AAAA,QACF;AAAA,QACA,QAAQ,MAAM,SAAS,IAAI;AAAA,QAE3B;AAAA,0BAAAD,MAAC,YAAO,OAAM,IAAG,oBAAC;AAAA,UACjB,OAAO,QAAQ,IAAI,CAAC,WACnB,gBAAAA,MAAC,YAA0B,OAAO,OAAO,OACtC,iBAAO,SADG,OAAO,KAEpB,CACD;AAAA;AAAA;AAAA,IACH;AAAA,EAEJ;AAEA,QAAM,UAAU,CAAC,UAAiE;AAChF,UAAM,YAAY,OAAO,SAAS,UAAU,OAAO,cAAc;AACjE,QAAI,MAAM,QAAQ,WAAW,CAAC,WAAW;AACvC,YAAM,eAAe;AACrB,eAAS,IAAI;AACb;AAAA,IACF;AACA,QAAI,MAAM,QAAQ,UAAU;AAC1B,YAAM,eAAe;AACrB,eAAS;AAAA,IACX;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,UAAU,OAAO,cAAc,MAAM;AACvD,WACE,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACE,GAAG;AAAA,QACJ,MAAM;AAAA,QACN,OAAO;AAAA,QACP,UAAU,CAAC,UAAU,OAAO,MAAM,OAAO,KAAK;AAAA,QAC9C,WAAW;AAAA,QACX,QAAQ,MAAM,SAAS,IAAI;AAAA;AAAA,IAC7B;AAAA,EAEJ;AAEA,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ,MAAM,OAAO,SAAS,SAAS,SAAS;AAAA,MACxC,WAAW,OAAO,SAAS,YAAY,OAAO,SAAS,aAAa,YAAY;AAAA,MAChF,OAAO;AAAA,MACP,UAAU,CAAC,UAAU,OAAO,MAAM,OAAO,KAAK;AAAA,MAC9C,WAAW;AAAA,MACX,QAAQ,MAAM,SAAS,IAAI;AAAA;AAAA,EAC7B;AAEJ;AA2BA,SAAS,aAAa,EAAE,SAAS,cAAc,UAAU,QAAQ,MAAM,SAAS,GAAsB;AAOpG,QAAM,QAA+B,OAAO,SAAS;AACrD,QAAM,UAAUM;AAAA,IACd,CAAC,SAAkB;AACjB,UAAI,CAAC,KAAM,UAAS;AAAA,IACtB;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AACA,QAAM,EAAE,cAAc,YAAY,UAAU,aAAa,IAAI,WAAW,MAAM,OAAO;AACrF,SACE,gBAAAL,OAAC,UAAK,KAAK,cAAc,WAAU,wBACjC;AAAA,oBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACJ,GAAG;AAAA,QACJ,cAAY,cAAc,YAAY,KAAK,QAAQ;AAAA,QACnD,iBAAe,OAAO,UAAU;AAAA,QAChC,OAAO,aAAa,KAAK;AAAA,QACzB,SAAS,CAAC,UAAU;AAClB,gBAAM,gBAAgB;AACtB,mBAAS;AAAA,QACX;AAAA,QACA,WAAW,gFAAgFE,aAAY,KAAK,CAAC;AAAA,QAE7G,0BAAAF;AAAA,UAAC;AAAA;AAAA,YACC,SAAQ;AAAA,YACR,WAAU;AAAA,YACV,MAAK;AAAA,YACL,QAAO;AAAA,YACP,aAAY;AAAA,YACZ,eAAc;AAAA,YACd,eAAW;AAAA,YAEX,0BAAAA,MAAC,UAAK,GAAE,wBAAuB;AAAA;AAAA,QACjC;AAAA;AAAA,IACF;AAAA,IACA,gBAAAC;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,IAAI;AAAA,QACJ,MAAK;AAAA,QACL;AAAA,QACA;AAAA,QACA,WAAW,oEAAoE,cAAc;AAAA,QAE5F;AAAA,iBAAO,SACN,gBAAAA,OAAC,UAAK,WAAU,uFAAsF;AAAA;AAAA,YAClG,OAAO;AAAA,YAAM;AAAA,aACjB;AAAA,UAEF,gBAAAA,OAAC,UAAK,WAAU,4CACb;AAAA,mBAAO,SAAS;AAAA,YAChB,OAAO,UAAU,SAAM,OAAO,OAAO,KAAK;AAAA,YAC1C,SAAM,aAAa,KAAK,CAAC;AAAA,aAC5B;AAAA,UACC,OAAO,QACN,gBAAAD;AAAA,YAAC;AAAA;AAAA,cACC,MAAM,OAAO;AAAA,cACb,QAAO;AAAA,cACP,KAAI;AAAA,cACJ,SAAS,CAAC,UAAU,MAAM,gBAAgB;AAAA,cAC1C,WAAU;AAAA,cACX;AAAA;AAAA,UAED;AAAA;AAAA;AAAA,IAEJ;AAAA,KACF;AAEJ;AAkBA,SAAS,cAAc;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAuB;AACrB,QAAM,SAASQ,SAAQ,MAAM,aAAa,OAAO,GAAG,CAAC,OAAO,CAAC;AAE7D,SACE,gBAAAP;AAAA,IAAC;AAAA;AAAA,MACC,YAAU;AAAA,MACV,cAAY;AAAA,MACZ,UAAU,CAAC,UAAU;AACnB,cAAM,eAAe;AACrB,iBAAS;AAAA,MACX;AAAA,MACA,WAAU;AAAA,MAEV;AAAA,wBAAAD,MAAC,QAAG,WAAU,yCAAyC,iBAAM;AAAA,QAC5D,cAAc,QACb,gBAAAA,MAAC,OAAE,MAAK,SAAQ,WAAU,mEACvB,qBACH;AAAA,QAED,OAAO,IAAI,CAAC,UAAU;AACrB,gBAAM,SAAS,MAAM,QAAQ,OAAO,CAAC,WAAW,2BAA2B,QAAQ,KAAK,CAAC;AACzF,cAAI,OAAO,WAAW,EAAG,QAAO;AAChC,iBACE,gBAAAC;AAAA,YAAC;AAAA;AAAA,cAEC,WAAW,MAAM,UAAU,OAAO,YAAY;AAAA,cAE7C;AAAA,sBAAM,UAAU,QACf,gBAAAD,MAAC,YAAO,WAAU,kDAAkD,gBAAM,OAAM;AAAA,gBAElF,gBAAAA,MAAC,SAAI,WAAU,6BACZ,iBAAO,IAAI,CAAC,WAAW;AACtB,wBAAM,UAAU,GAAG,WAAW,IAAI,OAAO,EAAE;AAC3C,wBAAM,UAAU,GAAG,OAAO;AAC1B,wBAAM,UAAU,OAAO,OAAO,EAAE;AAChC,yBACE,gBAAAC;AAAA,oBAAC;AAAA;AAAA,sBAEC,WAAW,OAAO,SAAS,UAAU,OAAO,cAAc,OAAO,kBAAkB;AAAA,sBAEnF;AAAA,wCAAAA,OAAC,WAAM,SAAS,SAAS,WAAU,wDAChC;AAAA,iCAAO;AAAA,0BACP,OAAO,aAAa,QAAQ,gBAAAD,MAAC,UAAK,WAAU,2BAA0B,eAAC;AAAA,2BAC1E;AAAA,wBACA,gBAAAA;AAAA,0BAAC;AAAA;AAAA,4BACC;AAAA,4BACA,IAAI;AAAA,4BACJ,OAAO,MAAM,OAAO,EAAE,KAAK;AAAA,4BAC3B,SAAS,YAAY;AAAA,4BACrB,aAAa,YAAY,SAAY,SAAY;AAAA,4BACjD,SAAS,CAAC,SAAS,SAAS,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC;AAAA;AAAA,wBAC7D;AAAA,wBACC,OAAO,QAAQ,gBAAAA,MAAC,OAAE,WAAU,sCAAsC,iBAAO,MAAK;AAAA,wBAC9E,YAAY,UACX,gBAAAA,MAAC,OAAE,IAAI,SAAS,MAAK,SAAQ,WAAU,iCACpC,mBACH;AAAA;AAAA;AAAA,oBAnBG,OAAO;AAAA,kBAqBd;AAAA,gBAEJ,CAAC,GACH;AAAA;AAAA;AAAA,YArCK,MAAM,SAAS;AAAA,UAsCtB;AAAA,QAEJ,CAAC;AAAA,QACD,gBAAAC,OAAC,SAAI,WAAU,2BACb;AAAA,0BAAAD;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,UAAU;AAAA,cACV,WAAU;AAAA,cAET,iBAAO,iBAAY;AAAA;AAAA,UACtB;AAAA,UACA,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS;AAAA,cACT,WAAU;AAAA,cACX;AAAA;AAAA,UAED;AAAA,WACF;AAAA;AAAA;AAAA,EACF;AAEJ;AAcA,SAAS,WAAW,EAAE,QAAQ,IAAI,OAAO,SAAS,aAAa,QAAQ,GAAoB;AACzF,QAAM,SAAS;AAAA,IACb;AAAA,IACA,gBAAgB,UAAU,OAAO;AAAA,IACjC,oBAAoB;AAAA,EACtB;AAEA,MAAI,OAAO,SAAS,WAAW;AAC7B,WACE,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACE,GAAG;AAAA,QACJ,MAAK;AAAA,QACL,SAAS,UAAU;AAAA,QACnB,UAAU,CAAC,UAAU,QAAQ,MAAM,OAAO,OAAO;AAAA,QACjD,WAAU;AAAA;AAAA,IACZ;AAAA,EAEJ;AAEA,MAAI,OAAO,SAAS,UAAU;AAC5B,WACE,gBAAAC;AAAA,MAAC;AAAA;AAAA,QACE,GAAG;AAAA,QACJ,WAAW;AAAA,QACX,OAAO,OAAO,UAAU,WAAW,QAAQ;AAAA,QAC3C,UAAU,CAAC,UAAU,QAAQ,MAAM,OAAO,UAAU,KAAK,OAAO,MAAM,OAAO,KAAK;AAAA,QAElF;AAAA,0BAAAD,MAAC,YAAO,OAAM,IAAG,oBAAC;AAAA,UACjB,OAAO,QAAQ,IAAI,CAAC,WACnB,gBAAAA,MAAC,YAA0B,OAAO,OAAO,OACtC,iBAAO,SADG,OAAO,KAEpB,CACD;AAAA;AAAA;AAAA,IACH;AAAA,EAEJ;AAEA,MAAI,OAAO,SAAS,UAAU,OAAO,cAAc,MAAM;AACvD,WACE,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACE,GAAG;AAAA,QACJ,WAAW;AAAA,QACX,MAAM;AAAA,QACN,OAAO,OAAO,UAAU,WAAW,QAAQ;AAAA,QAC3C,UAAU,CAAC,UAAU,QAAQ,MAAM,OAAO,UAAU,KAAK,OAAO,MAAM,OAAO,KAAK;AAAA;AAAA,IACpF;AAAA,EAEJ;AAEA,MAAI,OAAO,SAAS,YAAY,OAAO,SAAS,YAAY;AAC1D,WACE,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACE,GAAG;AAAA,QACJ,WAAW;AAAA,QACX,MAAK;AAAA,QACL,WAAU;AAAA,QACV,OAAO,UAAU,OAAO,KAAK,OAAO,KAAK;AAAA,QACzC,UAAU,CAAC,UAAU;AACnB,gBAAM,MAAM,MAAM,OAAO;AACzB,cAAI,IAAI,KAAK,MAAM,IAAI;AACrB,oBAAQ,IAAI;AACZ;AAAA,UACF;AACA,gBAAM,SAAS,mBAAmB,QAAQ,GAAG;AAC7C,kBAAQ,OAAO,YAAY,OAAO,QAAQ,GAAG;AAAA,QAC/C;AAAA;AAAA,IACF;AAAA,EAEJ;AAEA,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ,WAAW;AAAA,MACX,MAAM,OAAO,SAAS,SAAS,SAAS;AAAA,MACxC,OAAO,OAAO,UAAU,WAAW,QAAQ;AAAA,MAC3C,UAAU,CAAC,UAAU,QAAQ,MAAM,OAAO,UAAU,KAAK,OAAO,MAAM,OAAO,KAAK;AAAA;AAAA,EACpF;AAEJ;;;AEx6CA;AAAA,EACE,eAAAU;AAAA,EACA,aAAAC;AAAA,EACA,SAAAC;AAAA,EACA,WAAAC;AAAA,EACA,UAAAC;AAAA,EACA,YAAAC;AAAA,OAEK;AACP,SAAS,oBAAoB;AAezB,SAoIA,YAAAC,WAnIE,OAAAC,OADF,QAAAC,cAAA;AAFJ,SAAS,YAAY,EAAE,UAAU,GAA2B;AAC1D,SACE,gBAAAA,OAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACvJ;AAAA,oBAAAD,MAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,KAAI;AAAA,IAC9B,gBAAAA,MAAC,UAAK,GAAE,kBAAiB;AAAA,KAC3B;AAEJ;AA2BO,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA,MAAM;AAAA,EACN;AAAA,EACA,SAAS;AAAA,EACT,UAAU;AAAA,EACV;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA,QAAQ;AACV,GAAwB;AACtB,QAAM,CAAC,cAAc,eAAe,IAAIE,WAAS,KAAK;AACtD,QAAM,OAAO,kBAAkB;AAC/B,QAAM,UAAUC;AAAA,IACd,CAAC,SAAkB;AACjB,UAAI,mBAAmB,OAAW,iBAAgB,IAAI;AACtD,qBAAe,IAAI;AAAA,IACrB;AAAA,IACA,CAAC,gBAAgB,YAAY;AAAA,EAC/B;AAEA,QAAM,CAAC,OAAO,QAAQ,IAAID,WAAS,gBAAgB,EAAE;AACrD,QAAM,CAAC,QAAQ,SAAS,IAAIA,WAAS,CAAC;AACtC,QAAM,WAAWE,SAAyB,IAAI;AAC9C,QAAM,YAAYC,OAAM;AACxB,QAAM,SAAS,GAAG,SAAS;AAI3B,QAAM,OAAOC,SAAQ,MAAM,0BAA0B,OAAO,KAAK,GAAG,CAAC,OAAO,KAAK,CAAC;AAClF,QAAM,WAAWA,SAAQ,MAAM,yBAAyB,IAAI,GAAG,CAAC,IAAI,CAAC;AACrE,QAAM,cAAc,KAAK,WAAW,IAAI,IAAI,KAAK,IAAI,QAAQ,KAAK,SAAS,CAAC;AAC5E,QAAM,WAAW,KAAK,SAAS,IAAI,GAAG,MAAM,IAAI,WAAW,KAAK;AAGhE,EAAAC,YAAU,MAAM;AACd,QAAI,CAAC,OAAQ;AACb,aAAS,UAAU,GAA6B;AAC9C,WAAK,EAAE,WAAW,EAAE,YAAY,EAAE,IAAI,YAAY,MAAM,KAAK;AAC3D,UAAE,eAAe;AACjB,gBAAQ,CAAC,IAAI;AAAA,MACf;AAAA,IACF;AACA,aAAS,iBAAiB,WAAW,SAAS;AAC9C,WAAO,MAAM,SAAS,oBAAoB,WAAW,SAAS;AAAA,EAChE,GAAG,CAAC,QAAQ,MAAM,OAAO,CAAC;AAI1B,QAAM,kBAAkBH,SAAuB,IAAI;AACnD,EAAAG,YAAU,MAAM;AACd,QAAI,MAAM;AACR,sBAAgB,UAAU,SAAS;AACnC,eAAS,SAAS,MAAM;AACxB;AAAA,IACF;AACA,aAAS,gBAAgB,EAAE;AAC3B,cAAU,CAAC;AACX,UAAM,UAAU,gBAAgB;AAChC,oBAAgB,UAAU;AAC1B,QAAI,mBAAmB,YAAa,SAAQ,MAAM;AAAA,EAEpD,GAAG,CAAC,IAAI,CAAC;AAGT,EAAAA,YAAU,MAAM;AACd,QAAI,CAAC,QAAQ,CAAC,SAAU;AACxB,aAAS,eAAe,QAAQ,GAAG,iBAAiB,EAAE,OAAO,UAAU,CAAC;AAAA,EAC1E,GAAG,CAAC,MAAM,QAAQ,CAAC;AAEnB,QAAM,SAASJ;AAAA,IACb,CAAC,SAA6B;AAC5B,eAAS,IAAI;AACb,cAAQ,KAAK;AAAA,IACf;AAAA,IACA,CAAC,UAAU,OAAO;AAAA,EACpB;AAEA,QAAM,gBAAgB,CAAC,MAAuC;AAC5D,QAAI,EAAE,QAAQ,aAAa;AACzB,QAAE,eAAe;AACjB,UAAI,KAAK,SAAS,EAAG,YAAW,cAAc,KAAK,KAAK,MAAM;AAAA,IAChE,WAAW,EAAE,QAAQ,WAAW;AAC9B,QAAE,eAAe;AACjB,UAAI,KAAK,SAAS,EAAG,YAAW,cAAc,IAAI,KAAK,UAAU,KAAK,MAAM;AAAA,IAC9E,WAAW,EAAE,QAAQ,SAAS;AAC5B,QAAE,eAAe;AACjB,YAAM,OAAO,KAAK,WAAW;AAC7B,UAAI,KAAM,QAAO,IAAI;AAAA,IACvB,WAAW,EAAE,QAAQ,UAAU;AAC7B,QAAE,eAAe;AACjB,cAAQ,KAAK;AAAA,IACf;AAAA,EACF;AAEA,MAAI,CAAC,QAAQ,OAAO,aAAa,YAAa,QAAO;AAErD,MAAI,WAAW;AACf,SAAO;AAAA,IACL,gBAAAF,OAAAF,WAAA,EACE;AAAA,sBAAAC;AAAA,QAAC;AAAA;AAAA,UACC,eAAW;AAAA,UACX,eAAY;AAAA,UACZ,aAAa,MAAM,QAAQ,KAAK;AAAA,UAChC,WAAU;AAAA;AAAA,MACZ;AAAA,MAOA,gBAAAA,MAAC,SAAI,WAAU,mFACf,0BAAAC;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,cAAW;AAAA,UACX,cAAY;AAAA,UACX,GAAG,EAAE,CAAC,oBAAoB,GAAG,UAAU;AAAA,UACxC,WAAW,kJAAkJ,cAAc;AAAA,UAE3K;AAAA,4BAAAA,OAAC,SAAI,WAAU,uEACb;AAAA,8BAAAD,MAAC,eAAY,WAAU,0CAAyC;AAAA,cAChE,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,KAAK;AAAA,kBACL,MAAK;AAAA,kBACL,MAAK;AAAA,kBACL,iBAAa;AAAA,kBACb,iBAAe;AAAA,kBACf,yBAAuB;AAAA,kBACvB,cAAY;AAAA,kBACZ,OAAO;AAAA,kBACP,UAAU,CAAC,MAAM;AACf,6BAAS,EAAE,OAAO,KAAK;AACvB,8BAAU,CAAC;AAAA,kBACb;AAAA,kBACA,WAAW;AAAA,kBACX;AAAA,kBACA,WAAU;AAAA;AAAA,cACZ;AAAA,eACF;AAAA,YAIA,gBAAAC,OAAC,SAAI,MAAK,WAAU,IAAI,QAAQ,WAAU,2CACvC;AAAA,yBACC,gBAAAD,MAAC,SAAI,WAAU,uDAAsD,2BAAQ;AAAA,cAE9E,CAAC,WAAW,KAAK,WAAW,KAC3B,gBAAAA,MAAC,SAAI,WAAU,uDACZ,2BAAiB,MAAM,KAAK,IAAI,wBAAmB,MAAM,KAAK,CAAC,WAAM,qBACxE;AAAA,cAED,CAAC,WACA,SAAS,IAAI,CAAC,YACZ,gBAAAC,OAAC,SACC;AAAA,gCAAAD,MAAC,SAAI,WAAU,sFACZ,kBAAQ,OACX;AAAA,gBACC,QAAQ,MAAM,IAAI,CAAC,SAAS;AAC3B,8BAAY;AACZ,wBAAM,QAAQ;AACd,yBACE,gBAAAC;AAAA,oBAAC;AAAA;AAAA,sBAEC,IAAI,GAAG,MAAM,IAAI,KAAK;AAAA,sBACtB,MAAK;AAAA,sBACL,iBAAe,UAAU;AAAA,sBACzB,aAAa,MAAM,UAAU,KAAK;AAAA,sBAClC,SAAS,MAAM,OAAO,IAAI;AAAA,sBAC1B,WAAW,4FACT,UAAU,cAAc,cAAc,EACxC;AAAA,sBAEA;AAAA,wCAAAD,MAAC,UAAK,WAAU,4BAA4B,eAAK,OAAM;AAAA,wBACtD,KAAK,eACJ,gBAAAA,MAAC,UAAK,WAAU,0CAA0C,eAAK,aAAY;AAAA,wBAE5E,KAAK,QACJ,gBAAAA,MAAC,UAAK,WAAU,+DAA+D,eAAK,MAAK;AAAA;AAAA;AAAA,oBAftF,KAAK;AAAA,kBAiBZ;AAAA,gBAEJ,CAAC;AAAA,mBA5BO,QAAQ,KA6BlB,CACD;AAAA,eACL;AAAA,YAEA,gBAAAC,OAAC,SAAI,WAAU,6GACb;AAAA,8BAAAD,MAAC,UAAK,WAAU,gBACb,gBAAM,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,MAAM,MAAM,KAAK,GAAG,MAAM,MAAM,UACvE;AAAA,cACA,gBAAAC,OAAC,UAAK,WAAU,6BACd;AAAA,gCAAAD,MAAC,SAAI,WAAU,0DAAyD,0BAAE;AAAA,gBAC1E,gBAAAA,MAAC,UAAK,sBAAQ;AAAA,gBACd,gBAAAA,MAAC,SAAI,WAAU,iEAAgE,oBAAC;AAAA,gBAChF,gBAAAA,MAAC,UAAK,oBAAM;AAAA,gBACZ,gBAAAA,MAAC,SAAI,WAAU,iEAAgE,iBAAG;AAAA,gBAClF,gBAAAA,MAAC,UAAK,mBAAK;AAAA,iBACb;AAAA,eACF;AAAA;AAAA;AAAA,MACF,GACA;AAAA,OACF;AAAA,IACA,SAAS;AAAA,EACX;AACF;;;ACjRO,SAAS,eAAe,OAAiE;AAC9F,QAAM,OAAiB,CAAC;AACxB,aAAW,QAAQ,OAAO;AACxB,QAAI,OAAO,SAAS,SAAU;AAC9B,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,QAAQ,SAAS,EAAG,MAAK,KAAK,OAAO;AAAA,EAC3C;AACA,SAAO,KAAK,KAAK,GAAG;AACtB;;;AC4RM,SAIE,OAAAQ,OAJF,QAAAC,cAAA;AAtNC,IAAM,0BAA0B;AAChC,IAAM,2BAA2B;AACxC,IAAM,gBAAgB;AACtB,IAAM,eAAe;AACrB,IAAM,aAAa;AAGZ,IAAM,0BAA0B;AAChC,IAAM,gCAAgC;AAItC,IAAM,sCAAsC;AAEnD,IAAM,gBAAgB,IAAI,KAAK,aAAa,SAAS,EAAE,uBAAuB,EAAE,CAAC;AAM1E,SAAS,qBAAqB,OAAuB;AAC1D,SAAO,cAAc,OAAO,KAAK;AACnC;AAEA,SAAS,UAAU,OAAiC;AAClD,SAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK;AAC3D;AAcO,SAAS,kBAAkB,QAAqC;AACrE,SAAO,OAAO,OAAO,SAAS;AAChC;AAIA,SAAS,MAAM,OAAuB;AACpC,SAAO,KAAK,MAAM,QAAQ,GAAG,IAAI;AACnC;AAQO,SAAS,kBACd,QACA,EAAE,QAAQ,yBAAyB,SAAS,0BAA0B,QAAQ,cAAc,IAA8B,CAAC,GACxG;AACnB,QAAM,UAAU,OAAO;AAIvB,QAAM,UAAqE,CAAC;AAC5E,WAAS,QAAQ,GAAG,QAAQ,SAAS,SAAS,GAAG;AAC/C,UAAM,QAAQ,OAAO,KAAK;AAC1B,QAAI,UAAU,KAAK,EAAG,SAAQ,KAAK,EAAE,OAAO,MAAM,CAAC;AAAA,EACrD;AACA,QAAM,WAAW,QAAQ,IAAI,CAAC,UAAU,MAAM,KAAK;AACnD,QAAM,OAAO,UAAU,SAAS;AAChC,MAAI,SAAS,WAAW,GAAG;AACzB,WAAO,EAAE,UAAU,QAAQ,CAAC,GAAG,UAAU,CAAC,GAAG,MAAM,KAAK,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,GAAG,WAAW,OAAO;AAAA,EAC1G;AAKA,MAAI,MAAM,SAAS,CAAC;AACpB,MAAI,MAAM,SAAS,CAAC;AACpB,aAAW,SAAS,UAAU;AAC5B,QAAI,QAAQ,IAAK,OAAM;AACvB,QAAI,QAAQ,IAAK,OAAM;AAAA,EACzB;AAEA,QAAM,QAAQ,SAAS,CAAC;AACxB,QAAM,OAAO,SAAS,SAAS,SAAS,CAAC;AACzC,QAAM,OAAO,MAAM;AACnB,QAAM,MAAM;AACZ,QAAM,SAAS,SAAS;AACxB,QAAM,OAAO;AACb,QAAM,QAAQ,QAAQ;AAEtB,QAAM,SAAS,QAAQ,IAAI,CAAC,EAAE,OAAO,MAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,IAKhD,GAAG,MAAM,WAAW,IAAI,QAAQ,IAAI,QAAS,QAAQ,QAAQ,SAAU,UAAU,EAAE;AAAA;AAAA;AAAA,IAGnF,GAAG,MAAM,SAAS,IAAI,SAAS,IAAI,UAAW,SAAS,QAAQ,QAAQ,OAAQ,IAAI;AAAA,EACrF,EAAE;AAKF,QAAM,WAA+B,CAAC;AACtC,MAAI,MAAwB,CAAC;AAC7B,MAAI,WAAW,OAAO;AACtB,UAAQ,QAAQ,CAAC,EAAE,MAAM,GAAG,aAAa;AACvC,QAAI,UAAU,WAAW,KAAK,IAAI,SAAS,GAAG;AAC5C,eAAS,KAAK,GAAG;AACjB,YAAM,CAAC;AAAA,IACT;AACA,QAAI,KAAK,OAAO,QAAQ,CAAmB;AAC3C,eAAW;AAAA,EACb,CAAC;AACD,MAAI,IAAI,SAAS,EAAG,UAAS,KAAK,GAAG;AAErC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,OAAO,QAAQ,WAAW,OAAO,QAAQ,YAAY;AAAA,EAClE;AACF;AAGO,SAAS,yBAAyB,QAA2C;AAClF,SAAO,OAAO,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,IAAI,MAAM,CAAC,EAAE,EAAE,KAAK,GAAG;AAChE;AAkBO,SAAS,eACd,QACA,EAAE,QAAQ,yBAAyB,SAAS,qBAAqB,IAA2B,CAAC,GACrF;AACR,QAAM,EAAE,UAAU,MAAM,KAAK,KAAK,OAAO,MAAM,UAAU,IAAI,kBAAkB,MAAM;AAGrF,QAAM,UAAU,SAAS,IAAI,KAAK,KAAK,IAAI;AAG3C,MAAI,SAAS,WAAW,EAAG,QAAO,SAAS,IAAI,GAAG,KAAK,sBAAsB,GAAG,KAAK,gBAAgB,OAAO;AAC5G,MAAI,SAAS,WAAW,EAAG,QAAO,GAAG,KAAK,gBAAgB,OAAO,KAAK,OAAO,KAAK,CAAC;AACnF,MAAI,QAAQ,IAAK,QAAO,GAAG,KAAK,KAAK,SAAS,MAAM,YAAY,OAAO,kBAAkB,OAAO,KAAK,CAAC;AAItG,QAAM,WAAW,cAAc,SAAS,kBAAkB;AAC1D,SACE,GAAG,KAAK,KAAK,SAAS,MAAM,YAAY,OAAO,WAAW,OAAO,GAAG,CAAC,OAAO,OAAO,GAAG,CAAC,KACpF,QAAQ,SAAS,OAAO,KAAK,CAAC,OAAO,OAAO,IAAI,CAAC;AAExD;AAoBO,SAAS,UAAU;AAAA,EACxB;AAAA,EACA,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,aAAa;AAAA,EACb,mBAAmB;AAAA,EACnB;AACF,GAAiC;AAC/B,QAAM,WAAW,kBAAkB,QAAQ,EAAE,OAAO,OAAO,CAAC;AAC5D,QAAM,iBAAiB,eAAe,QAAQ,EAAE,OAAO,OAAO,CAAC;AAE/D,MAAI,SAAS,OAAO,WAAW,GAAG;AAKhC,WACE,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,kBAAgB,SAAS,OAAO,IAAI,gBAAgB;AAAA,QACpD,WAAW,YAAY,qCAAqC,SAAS;AAAA,QAErE;AAAA,0BAAAD,MAAC,UAAK,WAAU,WAAW,0BAAe;AAAA,UAC1C,gBAAAA,MAAC,UAAK,eAAY,QAAQ,mBAAS,OAAO,IAAI,mBAAmB,YAAW;AAAA;AAAA;AAAA,IAC9E;AAAA,EAEJ;AAEA,QAAM,YAAY,SAAS,SAAS,KAAK,CAAC,YAAY,QAAQ,SAAS,CAAC;AACxE,QAAM,MAAM,SAAS,OAAO,SAAS,OAAO,SAAS,CAAC;AAEtD,SACE,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAY;AAAA,MACZ,kBAAgB,YAAY,SAAS;AAAA,MACrC,kBAAgB,SAAS;AAAA,MAGzB,aAAW,SAAS,OAAO,IAAI,SAAS,OAAO;AAAA,MAC/C;AAAA,MACA;AAAA,MACA,SAAS,OAAO,KAAK,IAAI,MAAM;AAAA,MAC/B;AAAA,MAGA,WAAU;AAAA,MAMT;AAAA,iBAAS,SAAS,IAAI,CAAC,SAAS,UAAU;AACzC,gBAAM,MAAM,WAAW,KAAK;AAC5B,cAAI,QAAQ,SAAS,GAAG;AACtB,mBACE,gBAAAD;AAAA,cAAC;AAAA;AAAA,gBAEC,QAAQ,yBAAyB,OAAO;AAAA,gBACxC,MAAK;AAAA,gBACL,QAAO;AAAA,gBACP,aAAa;AAAA,gBACb,eAAc;AAAA,gBACd,gBAAe;AAAA,gBACf,cAAa;AAAA;AAAA,cAPR;AAAA,YAQP;AAAA,UAEJ;AACA,gBAAM,OAAO,QAAQ,CAAC;AAEtB,cAAI,KAAK,MAAM,IAAI,KAAK,KAAK,MAAM,IAAI,EAAG,QAAO;AACjD,iBAAO,gBAAAA,MAAC,YAAiB,IAAI,KAAK,GAAG,IAAI,KAAK,GAAG,GAAG,YAAY,MAAK,kBAAjD,GAAgE;AAAA,QACtF,CAAC;AAAA,QAGD,gBAAAA,MAAC,YAAO,IAAI,IAAI,GAAG,IAAI,IAAI,GAAG,GAAG,YAAY,MAAK,gBAAe;AAAA;AAAA;AAAA,EACnE;AAEJ;;;AC5TA;AAAA,EACE,kBAAAE;AAAA,EACA,eAAAC;AAAA,EACA,aAAAC;AAAA,EACA,UAAAC;AAAA,EACA,YAAAC;AAAA,OAKK;AA4KC,SAuBE,YAAAC,WAvBF,OAAAC,OAIF,QAAAC,cAJE;AAxID,SAAS,aAAa,OAAgB,UAAwC;AACnF,MAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO;AACjE,MAAI,OAAO,aAAa,YAAY,CAAC,OAAO,SAAS,QAAQ,EAAG,QAAO;AACvE,QAAM,WAAW,QAAQ;AACzB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,SAAS,aAAa,IAAI,OAAO,WAAW;AAAA,IAC5C,WAAW,WAAW,IAAI,OAAO,WAAW,IAAI,SAAS;AAAA,EAC3D;AACF;AAGO,SAAS,iBAAiB,WAA6B,WAA4B,WAAwB;AAChH,MAAI,cAAc,UAAU,aAAa,UAAW,QAAO;AAC3D,QAAM,UAA4B,aAAa,qBAAqB,OAAO;AAC3E,SAAO,cAAc,UAAU,aAAa;AAC9C;AAOO,SAAS,mBAAmB,OAAqB,SAAoC,sBAA8B;AACxH,QAAM,OAAO,OAAO,MAAM,QAAQ;AAClC,MAAI,MAAM,cAAc,OAAQ,QAAO,kBAAkB,IAAI;AAC7D,QAAM,OAAO,MAAM,cAAc,OAAO,OAAO;AAC/C,QAAM,YACJ,MAAM,YAAY,OAAO,OAAO,KAAK,IAAI,MAAM,QAAQ,CAAC,IAAI,IAAI,KAAK,IAAI,MAAM,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC;AAC3G,SAAO,GAAG,IAAI,IAAI,SAAS,SAAS,IAAI;AAC1C;AAEA,IAAM,aAA0C;AAAA,EAC9C,UAAU;AAAA,EACV,UAAU;AAAA,EACV,SAAS;AACX;AAEA,IAAM,kBAAoD,EAAE,IAAI,UAAK,MAAM,UAAK,MAAM,SAAI;AAY1F,IAAM,4BAA4B;AAGlC,IAAM,4BAA4B;AA+C3B,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AAAA,EACX,SAAS;AAAA,EACT;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,YAAY;AAAA,EACZ;AAAA,EACA;AACF,GAAmC;AACjC,QAAM,QAAQ,aAAa,OAAO,QAAQ;AAC1C,QAAM,OAAO,QAAQ,iBAAiB,MAAM,WAAW,QAAQ,IAAI;AAInE,QAAM,cAAc,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK;AACvE,QAAM,QAAQ,OAAO,UAAU,WAAW,OAAO,KAAK,IAAI;AAE1D,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,qBAAkB;AAAA,MAClB,aAAW;AAAA,MAGX,WAAW,YAAY,oFAAoF,SAAS;AAAA,MACpH;AAAA,MAEC;AAAA,kBACC,gBAAAD,MAAC,OAAE,wBAAqB,IAAG,WAAU,sFAClC,mBACH,IACE;AAAA,QACJ,gBAAAC,OAAC,SAAI,WAAU,6CACb;AAAA,0BAAAD,MAAC,QAAG,WAAU,iDAAiD,iBAAM;AAAA,UACpE;AAAA;AAAA;AAAA;AAAA,YAIC,gBAAAA,MAAC,UAAK,WAAU,kDAAiD,qBAAkB,IAChF,qBACH;AAAA,cACE;AAAA,WACN;AAAA,QAEA,gBAAAA,MAAC,OAAE,WAAU,kCACV,wBACC,gBAAAC,OAAC,UAAK,sBAAmB,eAAc,WAAU,+CAC/C;AAAA,0BAAAD,MAAC,UAAK,eAAY,QAAQ,qCAA0B;AAAA,UACpD,gBAAAA,MAAC,UAAK,WAAU,WAAW,qCAA0B;AAAA,WACvD,IAEA,gBAAAC,OAAAF,WAAA,EAGE;AAAA,0BAAAC,MAAC,UAAK,WAAU,sDAAsD,iBAAM;AAAA,UAC3E,OAAO,gBAAAA,MAAC,UAAK,WAAU,qCAAqC,gBAAK,IAAU;AAAA,WAC9E,GAEJ;AAAA,QAEC,QACC,gBAAAC,OAAC,OAAE,sBAAoB,MAAM,WAAW,WAAW,kCAAkC,WAAW,IAAI,CAAC,IACnG;AAAA,0BAAAA,OAAC,UAAK,eAAY,QAAQ;AAAA,4BAAgB,MAAM,SAAS;AAAA,YAAE;AAAA,aAAC;AAAA,UAC3D,mBAAmB,OAAO,MAAM;AAAA,WACnC,IACE;AAAA,QAEH,cAAc,gBAAAD,MAAC,OAAE,WAAU,0CAA0C,uBAAY,IAAO;AAAA,QAExF,SACC,gBAAAA,MAAC,SAAI,WAAU,8BAKb,0BAAAA,MAAC,aAAU,QAAQ,QAAQ,OAAO,eAAe,OAAO,QAAgB,GAC1E,IACE;AAAA,QAEH,SAAS,gBAAAA,MAAC,SAAI,WAAU,QAAQ,8BAAoB,MAAM,GAAE,IAAS;AAAA;AAAA;AAAA,EACxE;AAEJ;AAEA,SAAS,oBAAoB,QAAiD;AAC5E,MAAIE,gBAAe,MAAM,EAAG,QAAO;AACnC,SACE,gBAAAF;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,SAAS,OAAO;AAAA,MAChB,WAAU;AAAA,MAET,iBAAO;AAAA;AAAA,EACV;AAEJ;AAYO,IAAM,4BAA4B;AAGzC,IAAM,gBAAgB;AAkBtB,IAAM,yBAAwD;AAAA,EAC5D,gBAAgB;AAAA,EAChB,aAAa;AAAA,EACb,YAAY;AACd;AAKA,IAAM,uBAAuB;AAE7B,IAAM,kBAA4F;AAAA,EAChG,gBAAgB,EAAE,OAAO,oBAAI,IAAY,GAAG,SAAS,MAAM;AAAA,EAC3D,aAAa,EAAE,OAAO,oBAAI,IAAY,GAAG,SAAS,MAAM;AAAA,EACxD,YAAY,EAAE,OAAO,oBAAI,IAAY,GAAG,SAAS,MAAM;AACzD;AAEA,SAAS,cAAc,UAAiC;AACtD,MAAI,CAAC,OAAO,SAAS,QAAQ,EAAG,QAAO;AACvC,MAAI,WAAW,EAAG,QAAO;AACzB,SAAO;AACT;AAEA,SAAS,aAAa,UAAwB;AAC5C,QAAM,QAAQ,cAAc,QAAQ;AACpC,QAAM,SAAS,gBAAgB,KAAK;AAIpC,MAAI,OAAO,WAAW,OAAO,MAAM,IAAI,QAAQ,EAAG;AAClD,SAAO,MAAM,IAAI,QAAQ;AACzB,UAAQ;AAAA,IACN,sFAAiF,OAAO,QAAQ,CAAC,WACtF,yBAAyB,KAAK,uBAAuB,KAAK,CAAC;AAAA,EACxE;AACA,MAAI,OAAO,MAAM,QAAQ,sBAAsB;AAG7C,WAAO,MAAM,MAAM;AACnB,WAAO,UAAU;AACjB,YAAQ,KAAK,2BAA2B,KAAK,qCAAqC;AAAA,EACpF;AACF;AAiBO,SAAS,gBAAgB,WAAmB,2BAAmC;AACpF,MAAI,OAAO,UAAU,QAAQ,KAAK,YAAY,EAAG,QAAO;AACxD,eAAa,QAAQ;AACrB,SAAO;AACT;AAGO,SAAS,iBAAiB,OAAe,WAAmB,2BAAmC;AACpG,QAAM,OAAO,gBAAgB,QAAQ;AAIrC,QAAM,UAAU,OAAO,SAAS,KAAK,KAAK,QAAQ,IAAI,QAAQ;AAC9D,SAAO,KAAK,IAAI,GAAG,KAAK,KAAK,UAAU,IAAI,CAAC;AAC9C;AAIO,SAAS,iBAAoB,OAAqB,MAAc,WAAmB,2BAAyC;AACjI,QAAM,OAAO,gBAAgB,QAAQ;AACrC,QAAM,QAAQ,iBAAiB,MAAM,QAAQ,IAAI;AACjD,QAAM,YAAY,OAAO,SAAS,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI;AAC7D,QAAM,OAAO,KAAK,IAAI,KAAK,IAAI,WAAW,CAAC,GAAG,QAAQ,CAAC;AACvD,SAAO,MAAM,MAAM,OAAO,MAAM,OAAO,OAAO,IAAI;AACpD;AA4DO,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA;AACF,GAAmC;AACjC,QAAM,CAAC,MAAM,OAAO,IAAIG,WAAS,CAAC;AAClC,QAAM,CAAC,MAAM,OAAO,IAAIA,WAAoC,IAAI;AAKhE,QAAM,WAAW,MAAM,WAAW,WAAW,MAAM,WAAW;AAC9D,QAAM,UAAU,MAAM,WAAW,UAAU,MAAM,QAAQ,WAAW,OAAO;AAC3E,MAAI,YAAY,KAAM,SAAQ,OAAO;AAErC,QAAM,QACJ,YAAY,QAAQ,MAAM,WAAW,UAAU,EAAE,QAAQ,SAAS,OAAO,SAAS,OAAO,MAAM,MAAM,IAAI;AAC3G,QAAM,aAAa,UAAU;AAI7B,QAAM,WAAWC,SAAO,CAAC;AACzB,QAAM,aAAaC;AAAA,IACjB,CAAC,SAAiB;AAChB,UAAI,SAAS,YAAY,KAAM;AAC/B,eAAS,UAAU;AACnB,qBAAe,IAAI;AAAA,IACrB;AAAA,IACA,CAAC,YAAY;AAAA,EACf;AAEA,SACE,gBAAAL;AAAA,IAAC;AAAA;AAAA,MACC,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MAGA;AAAA,MAEC,WAAC,aACA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,MAAM;AAAA,UACN,cAAc;AAAA,UACd,eAAe;AAAA;AAAA,MACjB;AAAA;AAAA,EAEJ;AAEJ;AAGA,IAAM,eAAe;AAiBrB,IAAM,kBAAuC,oBAAI,IAAI;AAAA,EACnD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,SAAS,cAAc,QAA4B,UAAuC;AACxF,MAAI,OAAO,kBAAkB,UAAU,SAAS;AAChD,SAAO,SAAS,QAAQ,SAAS,UAAU;AACzC,QAAI,aAAa,KAAK,KAAK,OAAO,EAAG,QAAO;AAC5C,QAAI,gBAAgB,eAAe,KAAK,kBAAmB,QAAO;AAClE,UAAM,OAAO,KAAK,aAAa,MAAM;AACrC,QAAI,SAAS,QAAQ,KAAK,MAAM,KAAK,EAAE,KAAK,CAAC,UAAU,gBAAgB,IAAI,KAAK,CAAC,EAAG,QAAO;AAC3F,WAAO,KAAK;AAAA,EACd;AACA,SAAO;AACT;AAEA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GASiB;AACf,QAAM,OAAO,gBAAgB,QAAQ;AACrC,QAAM,YAAY,iBAAiB,SAAS,QAAQ,IAAI;AAMxD,QAAM,UAAU,KAAK,IAAI,KAAK,IAAI,MAAM,CAAC,GAAG,YAAY,CAAC;AACzD,QAAM,UAAU,iBAAiB,UAAU,SAAS,IAAI;AAExD,QAAM,aAAaI,SAA2B,IAAI;AAClD,QAAM,UAAUA,SAAgC,IAAI;AAGpD,QAAM,eAAeA,SAAO,KAAK;AAIjC,EAAAE,YAAU,MAAM;AACd,kBAAc,OAAO;AAAA,EACvB,GAAG,CAAC,SAAS,aAAa,CAAC;AAe3B,EAAAA,YAAU,MAAM;AACd,QAAI,CAAC,aAAa,QAAS;AAC3B,iBAAa,UAAU;AACvB,eAAW,SAAS,MAAM;AAAA,EAC5B,GAAG,CAAC,OAAO,CAAC;AAIZ,QAAM,OAAOD;AAAA,IACX,CAAC,SAA0B;AACzB,YAAM,UAAU,KAAK,IAAI,KAAK,IAAI,MAAM,CAAC,GAAG,YAAY,CAAC;AACzD,UAAI,YAAY,QAAS,QAAO;AAChC,YAAM,SAAS,OAAO,aAAa,cAAc,OAAO,SAAS;AACjE,mBAAa,UAAU,kBAAkB,SAAS,QAAQ,SAAS,SAAS,MAAM,KAAK;AACvF,mBAAa,OAAO;AACpB,aAAO;AAAA,IACT;AAAA,IACA,CAAC,SAAS,cAAc,SAAS;AAAA,EACnC;AAEA,QAAM,YAAY,CAAC,UAA4C;AAC7D,QAAI,MAAM,iBAAkB;AAI5B,QAAI,cAAc,MAAM,QAAQ,MAAM,aAAa,EAAG;AACtD,QAAI,QAAQ;AACZ,YAAQ,MAAM,KAAK;AAAA,MACjB,KAAK;AAAA,MACL,KAAK;AACH,gBAAQ,KAAK,UAAU,CAAC;AACxB;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AACH,gBAAQ,KAAK,UAAU,CAAC;AACxB;AAAA,MACF,KAAK;AACH,gBAAQ,KAAK,CAAC;AACd;AAAA,MACF,KAAK;AACH,gBAAQ,KAAK,YAAY,CAAC;AAC1B;AAAA,MACF;AACE;AAAA,IACJ;AAIA,QAAI,MAAO,OAAM,eAAe;AAAA,EAClC;AAEA,SACE,gBAAAJ;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,cAAY;AAAA,MACZ,qBAAkB;AAAA,MAKlB,aAAW;AAAA,MACX,WAAW,YAAY,aAAa,SAAS;AAAA,MAC7C;AAAA,MAMA,UAAU,YAAY,IAAI,IAAI;AAAA,MAC9B,qBAAmB,YAAY,IAAI,kDAAkD;AAAA,MAErF;AAAA,wBAAAD,MAAC,QAAG,KAAK,SAAS,WAAU,4CACzB,kBAAQ,IAAI,CAAC,EAAE,IAAI,OAAO,GAAG,KAAK,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UAWpC,gBAAAA,MAAC,QACC,0BAAAA,MAAC,eAAa,GAAG,MAAM,OAAO,aAAa,OAAO,KAAK,GAAG,KADnD,GAAG,OAAO,IAAI,EAAE,EAEzB;AAAA,SACD,GACH;AAAA,QASA,gBAAAC,OAAC,SAAI,WAAW,YAAY,IAAI,4CAA4C,QAC1E;AAAA,0BAAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,aAAU;AAAA,cACV,WAAW,YAAY,IAAI,sCAAsC;AAAA,cAClE;AAAA;AAAA,gBACO,UAAU;AAAA,gBAAE;AAAA,gBAAK;AAAA;AAAA;AAAA,UACzB;AAAA,UACC,YAAY,IACX,gBAAAA,OAAC,SAAI,WAAU,2BACb;AAAA,4BAAAD,MAAC,eAAY,OAAM,qBAAoB,OAAM,UAAI,OAAO,YAAY,GAAG,SAAS,MAAM,KAAK,UAAU,CAAC,GAAG;AAAA,YACxG,aAAa,gBACV,MAAM,KAAK,EAAE,QAAQ,UAAU,GAAG,CAAC,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAOpC,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBAEC,MAAK;AAAA,kBACL,cAAY,QAAQ,QAAQ,CAAC,OAAO,SAAS;AAAA,kBAG7C,gBAAc,UAAU,UAAU,SAAS;AAAA,kBAC3C,SAAS,MAAM,KAAK,KAAK;AAAA,kBACzB,WAAU;AAAA,kBAsBV,0BAAAA;AAAA,oBAAC;AAAA;AAAA,sBACC,eAAY;AAAA,sBACZ,WAAW;AAAA,wBACT;AAAA,wBACA,UAAU,UAAU,0BAA0B;AAAA,sBAChD;AAAA;AAAA,kBACF;AAAA;AAAA,gBAnCK;AAAA,cAoCP;AAAA,aACD,IACD;AAAA,YACJ,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,OAAM;AAAA,gBACN,OAAM;AAAA,gBACN,OAAO,YAAY,YAAY;AAAA,gBAC/B,SAAS,MAAM,KAAK,UAAU,CAAC;AAAA;AAAA,YACjC;AAAA,aACF,IACE;AAAA,WACN;AAAA;AAAA;AAAA,EACF;AAEJ;AAUA,SAAS,YAAY;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKiB;AACf,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAY;AAAA,MACZ,iBAAe;AAAA,MACf,SAAS,MAAM;AACb,YAAI,CAAC,MAAO,SAAQ;AAAA,MACtB;AAAA,MACA,WAAW,qHACT,QAAQ,eAAe,uCACzB;AAAA,MAEA,0BAAAA,MAAC,UAAK,eAAY,QAAQ,iBAAM;AAAA;AAAA,EAClC;AAEJ;;;A5BlqBQ,SAojBE,YAAAO,WApjBF,OAAAC,OAKA,QAAAC,cALA;AA7DD,SAAS,gBAAgB,KAAyB,QAAuC;AAC9F,MAAI,IAAI,gBAAgB,QAAQ,IAAI,oBAAoB,KAAM,QAAO;AACrE,QAAM,UAAU,OAAO,KAAK,CAAC,MAAM,EAAE,OAAO,IAAI,SAAS,GAAG;AAC5D,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,QACH,IAAI,gBAAgB,KAAK,OAAO,QAAQ,UAAU,CAAC,KACnD,IAAI,oBAAoB,KAAK,OAAO,QAAQ,cAAc,CAAC;AAC9D,MAAI,CAAC,SAAS,IAAI,KAAK,QAAQ,EAAG,QAAO;AACzC,SAAO,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,KAAK,IAAI,KAAK,QAAQ,CAAC,CAAC;AAClE;AAIA,SAAS,iBAAiB,WAAuC;AAC/D,QAAM,OAAO,UAAU,QAAQ,QAAQ,GAAG,EAAE,KAAK;AACjD,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,KAAK,SAAS,MAAM,GAAG,KAAK,MAAM,GAAG,GAAG,CAAC,WAAM;AACxD;AAGO,SAAS,sBAAsB,KAAwC;AAC5E,MAAI,IAAI,oBAAoB,QAAQ,CAAC,IAAI,WAAY,QAAO;AAC5D,SAAO,GAAG,KAAK,MAAM,IAAI,oBAAoB,IAAI,aAAa,IAAK,CAAC;AACtE;AAkCO,SAAS,WAAW,EAAE,KAAK,QAAQ,GAAoB;AAC5D,SACE,gBAAAA,OAAC,SAAI,WAAW,wGAAwG,cAAc,IACpI;AAAA,oBAAAA,OAAC,SAAI,WAAU,4DACb;AAAA,sBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,WAAW,iCACT,IAAI,WAAW,YAAY,eAAe,IAAI,WAAW,UAAU,mBAAmB,YACxF;AAAA;AAAA,MACF;AAAA,MACA,gBAAAC,OAAC,SAAI,WAAU,kBACb;AAAA,wBAAAD,MAAC,OAAE,WAAU,sCAAsC,cAAI,OAAM;AAAA,QAC7D,gBAAAA,MAAC,OAAE,WAAU,oDAAoD,cAAI,UAAS;AAAA,SAChF;AAAA,MACA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS;AAAA,UACT,cAAW;AAAA,UACX,WAAU;AAAA,UAEV,0BAAAA,MAAC,SAAI,WAAU,WAAU,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,eAAW,MAC9H,0BAAAA,MAAC,UAAK,GAAE,wBAAuB,GACjC;AAAA;AAAA,MACF;AAAA,OACF;AAAA,IACA,gBAAAC,OAAC,SAAI,WAAU,wCACZ;AAAA,UAAI,MAAM,WAAW,KACpB,gBAAAD,MAAC,OAAE,WAAU,iCAAgC,oCAAsB;AAAA,MAEpE,IAAI,MAAM,IAAI,CAAC,MAAM,MACpB,gBAAAC,OAAC,SAAY,WAAU,8CACrB;AAAA,wBAAAA,OAAC,SAAI,WAAU,gEACb;AAAA,0BAAAD,MAAC,UAAK,WAAW,qBAAqB,KAAK,WAAW,UAAU,qBAAqB,uBAAuB,IACzG,eAAK,WAAW,UAAU,WAAM,KACnC;AAAA,UACA,gBAAAA,MAAC,UAAK,WAAU,6CAA6C,eAAK,OAAM;AAAA,UACxE,gBAAAA,MAAC,UAAK,WAAU,uDACb,cAAI,KAAK,KAAK,EAAE,EAAE,mBAAmB,CAAC,GAAG,EAAE,MAAM,WAAW,QAAQ,UAAU,CAAC,GAClF;AAAA,WACF;AAAA,QACC,KAAK,UACJ,gBAAAA,MAAC,SAAI,WAAU,gHACZ,eAAK,QACR;AAAA,WAbM,CAeV,CACD;AAAA,OACH;AAAA,IACA,gBAAAA,MAAC,OAAE,WAAU,kEAAiE,iEAE9E;AAAA,KACF;AAEJ;AAmBO,SAAS,kBAAkB,MAAuD;AACvF,QAAM,UAAU,KAAK;AACrB,MAAI,CAAC,SAAS,MAAM,QAAQ,QAAQ,WAAW,yBAAyB,CAAC,QAAQ,OAAO,WAAY,QAAO;AAC3G,SAAO,EAAE,YAAY,QAAQ,OAAO,WAAW;AACjD;AAyHO,SAAS,eAAe;AAAA,EAC7B,cAAc;AAAA,EACd,WAAW;AAAA,EACX,UAAU;AAAA,EACV;AACF,GAAwB;AAItB,QAAM,YAAY,KAAK,IAAI,OAAO,UAAU,GAAG,CAAC;AAChD,QAAM,iBACJ,cAAc,IACV,oCACA,cAAc,IACZ,mBACA;AACR,SACE,gBAAAC,OAAC,SAAI,WAAU,uFACb;AAAA,oBAAAD,MAAC,UAAK,WAAU,2GACd,0BAAAA,MAAC,aAAU,MAAM,IAAI,WAAU,YAAW,GAC5C;AAAA,IACA,gBAAAA,MAAC,OAAE,WAAU,2EAA2E,uBAAY;AAAA,IACpG,gBAAAA,MAAC,QAAG,WAAU,4EACX,oBACH;AAAA,IACC,WAAW,gBAAAA,MAAC,OAAE,WAAU,mEAAmE,mBAAQ;AAAA,IACnG,SAAS,MAAM,SAAS,KACvB,gBAAAA,MAAC,SAAI,WAAW,4BAA4B,cAAc,IACvD,gBAAM,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,MAAM,MAC5B,gBAAAC;AAAA,MAAC;AAAA;AAAA,QAEC,MAAK;AAAA,QACL,SAAS,KAAK;AAAA,QACd,WAAU;AAAA,QAEV;AAAA,0BAAAA,OAAC,UAAK,WAAU,iEACb;AAAA,iBAAK;AAAA,YACL,KAAK;AAAA,aACR;AAAA,UACC,KAAK,eACJ,gBAAAD,MAAC,UAAK,WAAU,yDAAyD,eAAK,aAAY;AAAA;AAAA;AAAA,MAVvF;AAAA,IAYP,CACD,GACH;AAAA,KAEJ;AAEJ;AAeA,SAAS,UAAU,EAAE,MAAM,UAAU,GAAyC;AAC5E,MAAI,KAAK,WAAW,UAAU,GAAG;AAC/B,WACE,gBAAAC,OAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACvJ;AAAA,sBAAAD,MAAC,cAAS,QAAO,kBAAiB;AAAA,MAClC,gBAAAA,MAAC,UAAK,IAAG,MAAK,IAAG,MAAK,IAAG,MAAK,IAAG,MAAK;AAAA,OACxC;AAAA,EAEJ;AACA,MAAI,SAAS,mBAAmB;AAC9B,WACE,gBAAAC,OAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACvJ;AAAA,sBAAAD,MAAC,UAAK,GAAE,8DAA6D;AAAA,MACrE,gBAAAA,MAAC,UAAK,GAAE,0BAAyB;AAAA,OACnC;AAAA,EAEJ;AACA,MAAI,SAAS,qBAAqB;AAChC,WACE,gBAAAC,OAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,eAAW,MAChI;AAAA,sBAAAD,MAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,KAAI;AAAA,MAC9B,gBAAAA,MAAC,UAAK,GAAE,eAAc;AAAA,OACxB;AAAA,EAEJ;AACA,SACE,gBAAAC,OAAC,SAAI,WAAsB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACvJ;AAAA,oBAAAD,MAAC,UAAK,GAAE,gCAA+B;AAAA,IACvC,gBAAAA,MAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,KAAI;AAAA,KAChC;AAEJ;AAEA,SAAS,cAAc,MAA0G;AAC/H,SAAO,KAAK;AACd;AAKA,SAAS,eAAe,MAAiC;AACvD,SAAO,KAAK,WAAW,WAAW,cAAc,IAAI,GAAG,OAAO;AAChE;AAWO,SAAS,iBAAiB,MAAkC;AACjE,QAAM,SAAS,eAAe,IAAI;AAClC,SAAO;AAAA,IACL,MAAM;AAAA,IACN,IAAI,KAAK;AAAA,IACT,MAAM,KAAK,SAAS,wBAAwB,SAAS,KAAK;AAAA,IAC1D,OAAO;AAAA,MACL,QAAQ,KAAK,WAAW,YAAY,YAAY,SAAS,UAAU;AAAA,MACnE,OAAO,KAAK;AAAA,MACZ,QAAQ,KAAK;AAAA,MACb,OAAO,SAAU,cAAc,IAAI,GAAG,WAAW,gBAAiB;AAAA,IACpE;AAAA,EACF;AACF;AASA,SAAS,YAAY,MAAmC;AACtD,MAAI,KAAK,SAAS,kBAAmB,QAAO;AAC5C,MAAI,KAAK,SAAS,oBAAqB,QAAO;AAC9C,MAAI,KAAK,KAAK,WAAW,UAAU,EAAG,QAAO;AAC7C,SAAO;AACT;AAOA,SAAS,iBAAiB,MAAsB;AAC9C,QAAM,QAAQ,KACX,QAAQ,sBAAsB,OAAO,EACrC,QAAQ,UAAU,GAAG,EACrB,KAAK;AACR,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,MAAM,OAAO,CAAC,EAAE,YAAY,IAAI,MAAM,MAAM,CAAC;AACtD;AAMA,SAAS,kBAAkB,MAAgC;AACzD,QAAM,IAAI,KAAK,QAAQ,CAAC;AACxB,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK;AACH,aAAO,EAAE,QAAQ,YAAY,OAAO,EAAE,KAAK,CAAC,MAAM;AAAA,IACpD,KAAK;AACH,aAAO,oBAAoB,OAAO,EAAE,eAAe,WAAW,CAAC;AAAA,IACjE,KAAK;AACH,aAAO,OAAO,OAAO,EAAE,WAAW,SAAS,CAAC;AAAA,IAC9C,KAAK;AACH,aAAO,qBAAqB,OAAO,EAAE,cAAc,EAAE,CAAC;AAAA,IACxD,KAAK;AACH,aAAO,cAAc,OAAO,EAAE,SAAS,WAAW,CAAC;AAAA,IACrD,KAAK;AACH,aAAO,sBAAmB,OAAO,EAAE,SAAS,EAAE,CAAC;AAAA,IACjD,KAAK;AACH,aAAO,SAAS,OAAO,EAAE,QAAQ,EAAE,CAAC;AAAA,IACtC;AACE,aAAO,iBAAiB,KAAK,IAAI;AAAA,EACrC;AACF;AAUA,SAAS,gBAAgB,MAA6F;AACpH,QAAM,IAAK,KAAK,QAAQ,CAAC;AACzB,QAAM,WAAW,CAAC,MAChB,OAAO,MAAM,YAAY,EAAE,KAAK,IAAI,EAAE,KAAK,IAAI;AACjD,QAAM,SAAS,CAAC,MACd,MAAM,QAAQ,CAAC,IACX,EAAE,IAAI,CAAC,MAAO,OAAO,MAAM,WAAW,IAAI,IAAK,EAAE,OAAO,CAAC,MAAmB,CAAC,CAAC,CAAC,IAC/E,SAAS,CAAC,IACR,CAAC,SAAS,CAAC,CAAW,IACtB,CAAC;AAGT,QAAM,aAAa,SAAS,EAAE,OAAO,KAAK,SAAS,EAAE,WAAW,KAAK;AAErE,QAAM,eAAe;AAAA,IACnB,GAAG,OAAO,EAAE,YAAY;AAAA,IACxB,GAAG,OAAO,EAAE,QAAQ;AAAA,IACpB,GAAG,OAAO,EAAE,OAAO;AAAA,IACnB,GAAG,OAAO,EAAE,SAAS;AAAA,EACvB;AACA,QAAM,OAAO,aAAa,SAAS,OAAO,aAAa,KAAK,OAAO,CAAC,KAAK;AACzE,QAAM,UAAU,aAAa,GAAG,UAAU,GAAG,IAAI,KAAK,aAAa,SAAS,cAAc,aAAa,KAAK,OAAO,CAAC,KAAK;AAGzH,QAAM,WAAW,YAAY,OAAO,SAAS,EAAE,IAAI,IAAI;AAGvD,QAAM,OAAiB,CAAC;AACxB,QAAM,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE;AACpC,MAAI,OAAO,SAAS,YAAY,OAAO,EAAG,MAAK,KAAK,KAAK,OAAO,OAAO,KAAK,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,EAAE;AAAA,WACjG,SAAS,IAAI,EAAG,MAAK,KAAK,SAAS,IAAI,CAAW;AAC3D,QAAM,QAAQ,EAAE,SAAS,EAAE,YAAY,EAAE;AACzC,MAAI,OAAO,UAAU,YAAY,QAAQ,EAAG,MAAK,KAAK,YAAY,MAAM,eAAe,CAAC,EAAE;AAAA,WACjF,SAAS,KAAK,EAAG,MAAK,KAAK,SAAS,KAAK,CAAW;AAE7D,SAAO,EAAE,SAAS,MAAM,SAAS;AACnC;AAEA,SAAS,SAAS,GAAY,MAAM,KAAa;AAC/C,QAAM,IAAI,OAAO,MAAM,WAAW,IAAI,KAAK,UAAU,CAAC;AAKtD,MAAI,OAAO,MAAM,SAAU,QAAO;AAClC,SAAO,EAAE,SAAS,MAAM,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,WAAM;AAClD;AAEA,SAAS,OAAO,EAAE,KAAK,GAAsC;AAC3D,QAAM,UAAU,OAAO,QAAQ,IAAI,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,MAAM,MAAM,UAAa,MAAM,QAAQ,MAAM,EAAE;AAChG,MAAI,CAAC,QAAQ,OAAQ,QAAO;AAC5B,SACE,gBAAAA,MAAC,QAAG,WAAU,6CACX,kBAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,MACjB,gBAAAC,OAAC,SAAY,WAAU,YACrB;AAAA,oBAAAD,MAAC,QAAG,WAAU,2CAA2C,aAAE;AAAA,IAC3D,gBAAAA,MAAC,QAAG,WAAU,mFACX,mBAAS,CAAC,GACb;AAAA,OAJQ,CAKV,CACD,GACH;AAEJ;AAGA,SAAS,YAAY,EAAE,KAAK,GAA+B;AACzD,QAAM,UAAU,cAAc,IAAI;AAClC,QAAM,IAAK,SAAS,UAAU,CAAC;AAC/B,SACE,gBAAAC,OAAC,SAAI,WAAU,4EACb;AAAA,oBAAAA,OAAC,SAAI,WAAU,mDACb;AAAA,sBAAAD,MAAC,UAAK,WAAU,6BAA4B,eAAC;AAAA,MAC7C,gBAAAA,MAAC,UAAK,WAAU,yCAAyC,iBAAO,KAAK,MAAM,WAAW,EAAE,GAAE;AAAA,MACzF,EAAE,YAAY,QACb,gBAAAC,OAAC,UAAK,WAAW,EAAE,aAAa,IAAI,iBAAiB,oBAAoB;AAAA;AAAA,QAAM,EAAE;AAAA,SAAS;AAAA,OAE9F;AAAA,IACA,gBAAAD,MAAC,SAAI,WAAU,+EACZ,mBAAS,OAAO,QAAS,QAAQ,WAAW,WAAY,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI,KAAK,eAC9G;AAAA,KACF;AAEJ;AAGA,SAAS,kBAAkB,EAAE,KAAK,GAA+B;AAK/D,QAAM,SAAkB,KAAK;AAC7B,QAAM,WACJ,OAAO,WAAW,YAAY,WAAW,OACpC,SACD;AACN,SACE,gBAAAC,OAAC,SAAI,WAAU,aACZ;AAAA,SAAK,QAAQ,OAAO,KAAK,KAAK,IAAI,EAAE,SAAS,KAC5C,gBAAAA,OAAC,SACC;AAAA,sBAAAD,MAAC,OAAE,WAAU,4EAA2E,yBAAW;AAAA,MACnG,gBAAAA,MAAC,UAAO,MAAM,KAAK,MAAM;AAAA,OAC3B;AAAA,IAED,WACC,gBAAAC,OAAC,SACC;AAAA,sBAAAD,MAAC,OAAE,WAAU,4EACV,mBAAS,OAAO,QAAQ,WAAW,UACtC;AAAA,MACC,SAAS,OAAO,QACf,gBAAAA,MAAC,OAAE,WAAU,4BAA4B,mBAAS,WAAW,eAAc,IACzE,SAAS,UAAU,OAAO,SAAS,WAAW,WAChD,gBAAAA,MAAC,UAAO,MAAM,SAAS,QAAmC,IACxD,SAAS,UAAU,OACrB,gBAAAA,MAAC,OAAE,WAAU,2CAA2C,mBAAS,SAAS,MAAM,GAAE,IAChF;AAAA,OACN,IACE,UAAU,OACZ,gBAAAC,OAAC,SACC;AAAA,sBAAAD,MAAC,OAAE,WAAU,4EAA2E,oBAAM;AAAA,MAC9F,gBAAAA,MAAC,OAAE,WAAU,2CAA2C,mBAAS,MAAM,GAAE;AAAA,OAC3E,IACE;AAAA,KACN;AAEJ;AAOA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMG;AACD,QAAM,CAAC,UAAU,WAAW,IAAIE,WAAS,KAAK;AAC9C,QAAM,EAAE,SAAS,MAAM,SAAS,IAAI,gBAAgB,IAAI;AACxD,QAAM,SAAS,YAAY,KAAK,IAAI,IAAI,MAAM,OAAO;AACrD,QAAM,EAAE,SAAS,UAAU,KAAK,OAAO,IAAI,WAAW;AAEtD,SACE,gBAAAD,OAAC,SAAI,WAAU,oHACb;AAAA,oBAAAA,OAAC,SAAI,WAAU,wCACb;AAAA,sBAAAD,MAAC,UAAK,WAAU,2GACd,0BAAAA,MAAC,aAAU,MAAM,KAAK,MAAM,WAAU,eAAc,GACtD;AAAA,MACA,gBAAAC,OAAC,SAAI,WAAU,kBAIb;AAAA,wBAAAD,MAAC,OAAE,WAAU,yEACV,qBAAW,wBAAwB,qBACtC;AAAA,QACA,gBAAAA,MAAC,OAAE,WAAU,iEAAiE,4BAAkB,IAAI,GAAE;AAAA,QACrG,WAAW,gBAAAA,MAAC,OAAE,WAAU,sDAAsD,mBAAQ;AAAA,QACtF,YAAY,gBAAAA,MAAC,OAAE,WAAU,gDAAgD,oBAAS;AAAA,QAClF,KAAK,SAAS,KACb,gBAAAA,MAAC,SAAI,WAAU,8CACZ,eAAK,IAAI,CAAC,GAAG,MACZ,gBAAAA,MAAC,UAAa,WAAU,mFACrB,eADQ,CAEX,CACD,GACH;AAAA,SAEJ;AAAA,OACF;AAAA,IACA,gBAAAC,OAAC,SAAI,WAAU,sDACZ;AAAA,kBACC,gBAAAA,OAAAF,WAAA,EACE;AAAA,wBAAAC;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,UAAU;AAAA,YACV,SAAS,MAAM,OAAO,MAAM,SAAS,UAAU,QAAQ,YAAY,KAAK,EAAE,CAAC;AAAA,YAC3E,WAAU;AAAA,YACX;AAAA;AAAA,QAED;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,UAAU;AAAA,YACV,SAAS,MAAM,OAAO,MAAM,SAAS,SAAS,QAAQ,YAAY,KAAK,EAAE,CAAC;AAAA,YAC1E,WAAU;AAAA,YACX;AAAA;AAAA,QAED;AAAA,SACF;AAAA,MAEF,gBAAAC;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;AAAA,UACpC,iBAAe;AAAA,UACf,WAAU;AAAA,UAET;AAAA,uBAAW,iBAAiB;AAAA,YAC7B,gBAAAD,MAAC,eAAY,WAAW,gCAAgC,WAAW,eAAe,EAAE,IAAI;AAAA;AAAA;AAAA,MAC1F;AAAA,OACF;AAAA,IACC,YACC,gBAAAA,MAAC,SAAI,WAAU,gDACZ,oBAAU,gBAAAA,MAAC,qBAAkB,MAAY,GAC5C;AAAA,KAEJ;AAEJ;AAKA,SAAS,mBAAmB,MAAsB;AAChD,QAAM,OAAO,IAAI,KAAK,IAAI;AAC1B,MAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,EAAG,QAAO;AACzC,QAAM,MAAM,KAAK,mBAAmB,CAAC,GAAG,EAAE,SAAS,SAAS,OAAO,SAAS,KAAK,UAAU,CAAC;AAC5F,QAAM,OAAO,KAAK,mBAAmB,CAAC,GAAG,EAAE,MAAM,WAAW,QAAQ,UAAU,CAAC;AAC/E,SAAO,GAAG,GAAG,SAAM,IAAI;AACzB;AAWA,SAAS,aAAa,EAAE,KAAK,GAA+B;AAC1D,QAAM,IAAK,KAAK,QAAQ,CAAC;AACzB,QAAM,OAAO,OAAO,EAAE,SAAS,WAAW,EAAE,OAAO,OAAO,EAAE,OAAO,WAAW,EAAE,KAAK,OAAO,EAAE,aAAa,WAAW,EAAE,WAAW;AACnI,QAAM,SAAS,eAAe,IAAI;AAClC,QAAM,YAAY,SAAU,cAAc,IAAI,GAAG,WAAW,sBAAuB;AACnF,SACE,gBAAAA,MAAC,SAAI,WAAU,0BACb,0BAAAC,OAAC,SAAI,WAAU,oIACb;AAAA,oBAAAA,OAAC,SAAI,WAAU,8CACb;AAAA,sBAAAD,MAAC,UAAK,WAAU,oIACd,0BAAAA,MAAC,aAAU,MAAM,KAAK,MAAM,WAAU,eAAc,GACtD;AAAA,MACA,gBAAAA,MAAC,UAAK,WAAU,kEAAkE,4BAAkB,IAAI,GAAE;AAAA,MACzG,QACC,gBAAAA,MAAC,UAAK,OAAO,MAAM,WAAU,iGAC1B,6BAAmB,IAAI,GAC1B;AAAA,MAEF,gBAAAA,MAAC,UAAK,WAAU,8CACb,eAAK,WAAW,YACf,gBAAAA,MAAC,SAAI,WAAU,2DAA0D,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAW,MACxJ,0BAAAA,MAAC,UAAK,GAAE,+BAA8B,eAAc,SAAQ,GAC9D,IAEA,gBAAAA,MAAC,UAAK,WAAW,qCAAqC,SAAS,oCAAoC,kCAAkC,IAAI,GAE7I;AAAA,OACF;AAAA,IACC,aACC,gBAAAA,MAAC,SAAI,WAAU,8EAA8E,qBAAU;AAAA,KAE3G,GACF;AAEJ;AAIA,SAAS,aAAa,MAAgC;AACpD,MAAI,KAAK,SAAS,sBAAuB,QAAO;AAChD,SAAO,kBAAkB,IAAI;AAC/B;AAKA,SAAS,mBAAmB,MAA4C;AACtE,MAAI,KAAK,SAAS,sBAAuB,QAAO;AAChD,QAAM,UAAU,KAAK,MAAM;AAC3B,SAAO,OAAO,YAAY,YAAY,UAAU,UAAU;AAC5D;AAEA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GASG;AACD,QAAM,UAAU,gBAAgB,gBAAgB,CAAC;AACjD,QAAM,UAAU,KAAK,WAAW,SAAS,kBAAkB,IAAI,IAAI;AACnE,QAAM,OAAO,YAAY,IAAI;AAO7B,QAAM,SAAS,CAAC,QACd,gBAAAA,MAAC,SAAI,WAAU,gBAAe,OAAO,SAClC,eACH;AAKF,MAAI,SAAS;AACX,WAAO;AAAA,MACL,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAKA,MAAI,SAAS,YAAY;AACvB,WAAO,OAAO,gBAAAA,MAAC,gBAAa,MAAY,CAAE;AAAA,EAC5C;AAQA,QAAM,SAAS,YAAY,KAAK,IAAI,IAAI,MAAM,OAAO;AACrD,SAAO;AAAA,IACL,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,iBAAiB,IAAI;AAAA,QAC3B,OAAO,aAAa,IAAI;AAAA,QACxB,aAAa,mBAAmB,IAAI;AAAA,QACpC,kBAAkB,MAChB,WACC,KAAK,SAAS,wBAAwB,gBAAAA,MAAC,eAAY,MAAY,IAAK,gBAAAA,MAAC,qBAAkB,MAAY;AAAA,QAEtG,SACE,aAAa,KAAK,KAAK,WAAW,UAAU,IAC1C,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS,MAAM,UAAU,MAAM,OAAO;AAAA,YACtC,cAAW;AAAA,YACX,OAAM;AAAA,YACN,WAAU;AAAA,YAEV,0BAAAC,OAAC,SAAI,WAAU,eAAc,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACzJ;AAAA,8BAAAD,MAAC,UAAK,GAAE,cAAa;AAAA,cACrB,gBAAAA,MAAC,UAAK,GAAE,cAAa;AAAA,eACvB;AAAA;AAAA,QACF,IACE;AAAA;AAAA,IAER;AAAA,EACF;AACF;AAKA,SAAS,iBAAiB;AACxB,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MAIC,WAAU;AAAA,MACV,eAAY;AAAA,MACZ,eAAW;AAAA;AAAA,EACb;AAEJ;AAMA,SAAS,YAAY;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMG;AACD,QAAM,OAAO,cAAc,SAAS,SAAS;AAC7C,QAAM,OAAOG,SAAQ,MAAM,WAAW,IAAI,GAAG,CAAC,YAAY,IAAI,CAAC;AAK/D,MAAI,CAAC,QAAQ,KAAK,KAAK,CAAC,UAAW,QAAO;AAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAME,gBAAAF,OAAC,SAAI,WAAW,GAAG,gBAAgB,GAAG,YAAY,KAAK,kBAAkB,IACtE;AAAA;AAAA,MAGA,aAAa,gBAAAD,MAAC,kBAAe;AAAA,OAChC;AAAA;AAEJ;AAKA,IAAM,uBAAuB;AAQ7B,SAAS,gBAAgB,MAAiC;AACxD,SACE,KAAK,WAAW,aAChB,eAAe,IAAI,KACnB,kBAAkB,IAAI,MAAM;AAEhC;AAQA,SAAS,cAAc;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GASG;AACD,QAAM,YAAY,SAAS,SAAS;AACpC,QAAM,iBAAiB,IAAI;AAAA,IACzB,SAAS,QAAQ,CAAC,MAAO,EAAE,SAAS,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,CAAE;AAAA,EAChE;AACA,QAAM,qBAAqB,IAAI,aAAa,CAAC,GAAG;AAAA,IAC9C,CAAC,OAAO,CAAC,eAAe,IAAI,GAAG,EAAE;AAAA,EACnC;AAKA,QAAM,iBAAiB,CAAC,MAAwB,UAC9C,gBAAAA;AAAA,IAAC;AAAA;AAAA,MAEC;AAAA,MACA,SAAS;AAAA,MACT;AAAA,MACA,WAAW;AAAA,MACX,WAAW;AAAA,MACX,cAAc;AAAA;AAAA,IANT,QAAQ,KAAK,EAAE;AAAA,EAOtB;AAQF,QAAM,SAGF,CAAC;AACL,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,UAAM,MAAM,SAAS,CAAC;AACtB,QAAI,CAAC,IAAK;AACV,QAAI,IAAI,SAAS,QAAQ;AACvB,aAAO,KAAK,EAAE,MAAM,QAAQ,OAAO,GAAG,SAAS,IAAI,QAAQ,CAAC;AAAA,IAC9D,OAAO;AACL,YAAM,OAAO,OAAO,OAAO,SAAS,CAAC;AACrC,UAAI,QAAQ,KAAK,SAAS,QAAS,MAAK,MAAM,KAAK,IAAI,IAAI;AAAA,UACtD,QAAO,KAAK,EAAE,MAAM,SAAS,OAAO,GAAG,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC;AAAA,IACjE;AAAA,EACF;AAYA,QAAM,WAAwB,CAAC;AAC/B,aAAW,KAAK,QAAQ;AACtB,QAAI,EAAE,SAAS,QAAQ;AACrB,eAAS;AAAA,QACP,gBAAAA;AAAA,UAAC;AAAA;AAAA,YAKC,SAAS,EAAE;AAAA,YAEX,WAAW,aAAa,EAAE,UAAU;AAAA,YACpC,WAAW,aAAa,EAAE,UAAU;AAAA,YACpC;AAAA,YACA;AAAA;AAAA,UANK,QAAQ,EAAE,KAAK;AAAA,QAOtB;AAAA,MACF;AACA;AAAA,IACF;AACA,QACE,CAAC,aACD,EAAE,MAAM,UAAU,wBAClB,CAAC,EAAE,MAAM,KAAK,eAAe,GAC7B;AAiBA,eAAS;AAAA,QACP,gBAAAC,OAAC,aAGC;AAAA,0BAAAA,OAAC,aAAQ,WAAU,yJAAwJ;AAAA;AAAA,YACzJ,EAAE,MAAM;AAAA,YAAO;AAAA,aACjC;AAAA,UACA,gBAAAD,MAAC,SAAI,WAAU,gCACZ,YAAE,MAAM,IAAI,cAAc,GAC7B;AAAA,aARY,cAAc,EAAE,KAAK,EASnC;AAAA,MACF;AACA;AAAA,IACF;AACA,MAAE,MAAM,QAAQ,CAAC,MAAM,UAAU,SAAS,KAAK,eAAe,MAAM,KAAK,CAAC,CAAC;AAAA,EAC7E;AACA,oBAAkB,QAAQ,CAAC,MAAM,UAAU,SAAS,KAAK,eAAe,MAAM,KAAK,CAAC,CAAC;AACrF,MAAI,aAAa,SAAS,SAAS,GAAG,SAAS,QAAQ;AACrD,aAAS,KAAK,gBAAAA,MAAC,oBAAmB,iBAAkB,CAAE;AAAA,EACxD;AAEA,SAAO,gBAAAA,MAAC,SAAI,WAAU,uBAAuB,UAAS;AACxD;AAUA,IAAM,wBACJ;AAIF,SAAS,WAAW,KAA4B;AAC9C,QAAM,WAAW,IAAI,UAAU,OAAO,CAAC,MAAM,EAAE,SAAS,MAAM,KAAK,CAAC;AACpE,MAAI,SAAS,SAAS,EAAG,QAAO,SAAS,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,MAAM;AAC1E,SAAO,IAAI;AACb;AAIA,SAAS,kBAAkB,EAAE,KAAK,GAAqB;AACrD,QAAM,CAAC,QAAQ,SAAS,IAAIE,WAAS,KAAK;AAC1C,QAAM,WAAWE,SAA6C,IAAI;AAClE,EAAAC;AAAA,IACE,MAAM,MAAM;AACV,UAAI,SAAS,YAAY,KAAM,cAAa,SAAS,OAAO;AAAA,IAC9D;AAAA,IACA,CAAC;AAAA,EACH;AACA,QAAM,OAAO,MAAM;AACjB,UAAM,YAAY,UAAU;AAC5B,QAAI,CAAC,UAAW;AAChB,SAAK,UAAU,UAAU,IAAI,EAAE;AAAA,MAC7B,MAAM;AACJ,kBAAU,IAAI;AACd,YAAI,SAAS,YAAY,KAAM,cAAa,SAAS,OAAO;AAC5D,iBAAS,UAAU,WAAW,MAAM,UAAU,KAAK,GAAG,IAAI;AAAA,MAC5D;AAAA,MACA,MAAM;AAAA,MAAC;AAAA,IACT;AAAA,EACF;AACA,SACE,gBAAAL;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,SAAS;AAAA,MACT,cAAW;AAAA,MACX,OAAM;AAAA,MACN,WAAU;AAAA,MAET,mBACC,gBAAAA,MAAC,SAAI,WAAU,eAAc,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACzJ,0BAAAA,MAAC,cAAS,QAAO,kBAAiB,GACpC,IAEA,gBAAAC,OAAC,SAAI,WAAU,eAAc,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACzJ;AAAA,wBAAAD,MAAC,UAAK,GAAE,KAAI,GAAE,KAAI,OAAM,MAAK,QAAO,MAAK,IAAG,KAAI;AAAA,QAChD,gBAAAA,MAAC,UAAK,GAAE,2DAA0D;AAAA,SACpE;AAAA;AAAA,EAEJ;AAEJ;AAEA,SAAS,qBAAqB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAeG;AAKD,QAAM,UAAU,cAAc,IAAI,SAAS,SAAS;AACpD,QAAM,YAAY,cAAc,IAAI,aAAa,IAAI,SAAS;AAK9D,QAAM,OAAOG,SAAQ,MAAM,WAAW,OAAO,GAAG,CAAC,YAAY,OAAO,CAAC;AAGrE,QAAM,WAAW,IAAI;AAMrB,QAAM,gBACJ,YAAY,OACX,UAAU,KAAK,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,QAAQ,KAAK,MAAM,EAAE,KACjE;AACJ,QAAM,qBAAqBC,SAAuB,IAAI;AAEtD,QAAM,gBAAgBA,SAAsB,IAAI;AAChD,QAAM,aAAaA,SAAsB,IAAI;AAC7C,MAAI,aAAa,aAAa,CAAC,iBAAiB,cAAc,YAAY,MAAM;AAC9E,kBAAc,UAAU,YAAY,IAAI;AAAA,EAC1C;AACA,MACE,iBACA,cAAc,YAAY,QAC1B,WAAW,YAAY,MACvB;AACA,eAAW,UAAU,YAAY,IAAI,IAAI,cAAc;AAAA,EACzD;AACA,EAAAC,YAAU,MAAM;AACd,UAAM,KAAK,mBAAmB;AAC9B,QAAI,MAAM,aAAa,CAAC,cAAe,IAAG,YAAY,GAAG;AAAA,EAC3D,GAAG,CAAC,WAAW,WAAW,aAAa,CAAC;AAGxC,QAAM,kBAAkB;AAAA,IACtB,aAAa,CAAC,CAAC,aAAa,CAAC;AAAA,EAC/B;AAMA,QAAM,CAAC,kBAAkB,mBAAmB,IAAIH,WAAyB,IAAI;AAC7E,QAAM,gBAAgB,oBAAoB,CAAC;AAE3C,QAAM,QAAQ,WAAW;AACzB,SACE,gBAAAD,OAAC,SAAI,WAAW,iCAAiC,QAAQ,oBAAoB,MAAM,IAChF;AAAA,KAAC,SACA,gBAAAA,OAAC,SAAI,WAAU,6EACb;AAAA,sBAAAD,MAAC,UAAK,WAAU,6CAA6C,sBAAW;AAAA,MACvE,IAAI,aAAa,gBAAAA,MAAC,UAAK,WAAU,yBAAyB,cAAI,WAAU;AAAA,MACxE,sBAAsB,GAAG,KAAK,gBAAAA,MAAC,UAAM,gCAAsB,GAAG,GAAE;AAAA,MAChE,gBAAgB,KAAK,MAAM,KAAK,gBAAAA,MAAC,UAAM,0BAAgB,KAAK,MAAM,GAAE;AAAA,OACvE;AAAA,IAED;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASC,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,MAAM,gBAAAA,MAAC,cAAW,WAAU,eAAc;AAAA,QAC1C,OACE,CAAC,gBACC,gBAAAC,OAAC,UAAK,WAAU,iBAAgB,eAAY,aAAY;AAAA;AAAA,UAC7C,mBAAmB,IAAI,SAAM,eAAe,MAAM;AAAA,WAC7D,IACE,WAAW,WAAW;AAAA;AAAA;AAAA,UAGxB,gBAAgB,MAAM;AAAE,kBAAM,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,WAAW,UAAW,GAAI,CAAC;AAAG,mBAAO,GAAG,CAAC,UAAU,MAAM,IAAI,KAAK,GAAG;AAAA,UAAG,GAAG,CAAC;AAAA,YAErI;AAAA,QAMJ,aAAa,gBAAgB,iBAAiB,SAAS,IAAI;AAAA,QAC3D,QAAQ,gBAAgB,SAAS;AAAA,QACjC,MAAM;AAAA,QACN,cAAc,CAAC,SAAS,oBAAoB,IAAI;AAAA,QAEhD,0BAAAD;AAAA,UAAC;AAAA;AAAA,YACC,KAAK;AAAA,YACL,WAAU;AAAA,YAET;AAAA;AAAA,QACH;AAAA;AAAA,IACF;AAAA,IAED,YAAY,SAAS,SAAS,IAC7B,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,IACF,IAEA,gBAAAC,OAAAF,WAAA,EACE;AAAA,sBAAAE,OAAC,SAAI,WAAW,kBACb;AAAA;AAAA,QACA,aAAa,WAAW,CAAC,IAAI,WAAW,UAAU,gBAAAD,MAAC,kBAAe;AAAA,SACrE;AAAA,MACC,IAAI,aAAa,IAAI,UAAU,SAAS,KACvC,gBAAAA,MAAC,SAAI,WAAU,8BACZ,cAAI,UAAU,IAAI,CAAC,IAAI,UACtB,gBAAAA;AAAA,QAAC;AAAA;AAAA,UAEC,MAAM;AAAA,UACN,SAAS;AAAA,UACT;AAAA,UACA,WAAW;AAAA,UACX,WAAW;AAAA,UACX,cAAc;AAAA;AAAA,QANT,GAAG;AAAA,MAOV,CACD,GACH;AAAA,OAEJ;AAAA,IAED,gBAAgB,IAAI,SACnB,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACE,GAAG;AAAA,QACJ,OAAO,IAAI;AAAA,QACX,gBAAgB;AAAA,QAChB,WAAU;AAAA;AAAA,IACZ;AAAA,IAED,oBACC,iCAAiC,IAAI,KAAK,EAAE,IAAI,CAAC,SAC/C,gBAAAA;AAAA,MAAC;AAAA;AAAA,QAEC;AAAA,QACA,QAAQ,iBAAiB;AAAA,QACzB,WAAU;AAAA;AAAA,MAHL,GAAG,KAAK,IAAI,EAAE,IAAI,KAAK,IAAI,OAAO;AAAA,IAIzC,CACD;AAAA,IACF,eAAe,GAAG;AAAA,IAClB,wBAAwB,gCAAgC,IAAI,KAAK,EAAE,SAAS,KAC3E,gBAAAA,MAAC,SAAI,WAAU,QACb,0BAAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,gCAAgC,IAAI,KAAK;AAAA,QAChD,gBAAgB;AAAA,QAChB,SAAQ;AAAA;AAAA,IACV,GACF;AAAA,IAED,SACC,gBAAAC,OAAC,SAAI,eAAY,qBAAoB,WAAW,uBAC9C;AAAA,sBAAAD,MAAC,qBAAkB,MAAM,WAAW,GAAG,GAAG;AAAA,MACzC,IAAI,aAAa,gBAAAA,MAAC,UAAK,WAAU,aAAa,cAAI,WAAU;AAAA,MAC5D,sBAAsB,GAAG,KAAK,gBAAAA,MAAC,UAAM,gCAAsB,GAAG,GAAE;AAAA,MAChE,gBAAgB,KAAK,MAAM,KAAK,gBAAAA,MAAC,UAAM,0BAAgB,KAAK,MAAM,GAAE;AAAA,OACvE;AAAA,KAEJ;AAEJ;AASA,IAAM,mBAAmB,KAAK,oBAAoB;AAM3C,SAAS,mBAAmB,QAAyB;AAC1D,QAAM,CAAC,SAAS,UAAU,IAAIE,WAAS,CAAC;AACxC,EAAAG,YAAU,MAAM;AACd,QAAI,CAAC,OAAQ;AAGb,eAAW,CAAC;AACZ,UAAM,KAAK,YAAY,MAAM,WAAW,CAAC,MAAM,IAAI,CAAC,GAAG,GAAI;AAC3D,WAAO,MAAM,cAAc,EAAE;AAAA,EAC/B,GAAG,CAAC,MAAM,CAAC;AACX,SAAO;AACT;AAEA,SAAS,YAAY,EAAE,YAAY,SAAS,UAAU,GAAyD;AAC7G,QAAM,UAAU,mBAAmB,IAAI;AACvC,SACE,gBAAAJ,OAAC,SAAI,WAAU,sCACZ;AAAA,eAAW,WACV,gBAAAD,MAAC,OAAE,WAAU,gFAAgF,sBAAW;AAAA,IAE1G,gBAAAC,OAAC,SAAI,WAAU,6DACb;AAAA,sBAAAD,MAAC,SAAI,WAAU,wBAAuB,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAW,MACrH,0BAAAA,MAAC,UAAK,GAAE,+BAA8B,eAAc,SAAQ,GAC9D;AAAA,MAAM;AAAA,MACG,WAAW,IAAI,SAAM,OAAO,MAAM;AAAA,OAC7C;AAAA,KACF;AAEJ;AAIA,SAAS,eAAe,EAAE,SAAS,QAAQ,GAA8C;AACvF,SACE,gBAAAA,MAAC,SAAI,WAAU,sCACb,0BAAAC,OAAC,SAAI,MAAK,SAAQ,WAAU,0HAC1B;AAAA,oBAAAA,OAAC,SAAI,WAAU,2BAA0B,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,eAAW,MACrK;AAAA,sBAAAD,MAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,KAAI;AAAA,MAC9B,gBAAAA,MAAC,UAAK,GAAE,mBAAkB;AAAA,OAC5B;AAAA,IACA,gBAAAA,MAAC,UAAK,WAAU,8BAA8B,mBAAQ;AAAA,IACrD,WACC,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS;AAAA,QACT,WAAW,6IAA6I,oBAAoB;AAAA,QAC7K;AAAA;AAAA,IAED;AAAA,KAEJ,GACF;AAEJ;AASO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA,cAAc;AAAA,EACd,SAAS;AAAA,EACT,SAAS,CAAC;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,aAAa;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAsB;AACpB,QAAM,mBACJ,gBAAgB,UACZ,qDACA;AAKN,QAAM,aAAaG;AAAA,IACjB,MAAM,mBAAmB,CAAC,YAAoB,gBAAAH,MAAC,OAAE,WAAU,uBAAuB,mBAAQ;AAAA,IAC1F,CAAC,cAAc;AAAA,EACjB;AACA,QAAM,aAAa,SAAS,SAAS,SAAS,CAAC,GAAG,SAAS;AAC3D,QAAM,QAAQ,WAAW;AACzB,MAAI,SAAS,WAAW,KAAK,CAAC,WAAW,CAAC,OAAO;AAG/C,UAAM,QAAQ,cAAc,YAAY,IAAI,gBAAAA,MAAC,kBAAgB,GAAG,YAAY;AAC5E,WACE,gBAAAC,OAAAF,WAAA,EACG;AAAA;AAAA,MACA;AAAA,OACH;AAAA,EAEJ;AACA,SACE,gBAAAE,OAAAF,WAAA,EACG;AAAA;AAAA,IACA,SAAS;AAAA,MAAI,CAAC,QACb,IAAI,SAAS,SACX,gBAAAE,OAAC,SAAiB,WAAW,iCAAiC,QAAQ,oBAAoB,MAAM,IAC9F;AAAA,wBAAAA,OAAC,SAAI,WAAW,iBAAiB,QAAQ,gBAAgB,aAAa,IACnE;AAAA,WAAC,SACA,gBAAAD,MAAC,OAAE,WAAU,2FACV,qBACH;AAAA,UAEF,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,WACE,QACI,oGAAoG,gBAAgB,KACpH,uDAAuD,gBAAgB;AAAA,cAG7E,0BAAAA,MAAC,OAAE,WAAU,uBAAuB,cAAI,SAAQ;AAAA;AAAA,UAClD;AAAA,UACC,wBAAwB,gCAAgC,IAAI,KAAK,EAAE,SAAS,KAC3E,gBAAAA,MAAC,SAAI,WAAU,UACb,0BAAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,gCAAgC,IAAI,KAAK;AAAA,cAChD,gBAAgB;AAAA,cAChB,SAAQ;AAAA;AAAA,UACV,GACF;AAAA,WAEJ;AAAA,QACC,SACC,gBAAAA,MAAC,SAAI,eAAY,qBAAoB,WAAW,GAAG,qBAAqB,gBACtE,0BAAAA,MAAC,qBAAkB,MAAM,IAAI,SAAS,GACxC;AAAA,WA7BM,IAAI,EA+Bd,IAEA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UAEC;AAAA,UACA,WAAW,CAAC,CAAC,WAAW,IAAI,OAAO,SAAS,SAAS,SAAS,CAAC,GAAG;AAAA,UAClE;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA;AAAA,QAdK,IAAI;AAAA,MAeX;AAAA,IAEJ;AAAA,IACC,WAAW,cAAc,gBAAAA,MAAC,eAAY,YAAwB,QAAgB;AAAA,IAC9E,SAAS,CAAC,WAAW,gBAAAA,MAAC,kBAAe,SAAS,OAAO,SAAkB;AAAA,KAC1E;AAEJ;","names":["useEffect","useMemo","useRef","useState","useState","useEffect","useRef","useState","useEffect","useRef","useState","jsx","useState","useRef","useEffect","jsx","jsxs","useState","useEffect","useRef","useLayoutEffect","useMemo","useRef","useState","jsx","jsxs","CheckGlyph","STATUS_LABELS","TERMINAL_NOTES","COLLAPSED_MAX_HEIGHT","useRef","useState","useLayoutEffect","useMemo","jsx","useEffect","useState","jsx","jsxs","parsed","useCallback","useEffect","useMemo","useRef","useState","useCallback","useEffect","useRef","useState","Fragment","jsx","jsxs","useState","useRef","useCallback","useEffect","useMemo","useCallback","useEffect","useRef","useState","useCallback","useMemo","useState","next","useState","useCallback","useMemo","useCallback","useMemo","useRef","useState","useCallback","useEffect","useState","Fragment","jsx","jsxs","useState","useCallback","useEffect","useCallback","useEffect","useId","useRef","useState","jsx","jsxs","useState","useRef","useId","useCallback","useEffect","Fragment","jsx","jsxs","CheckGlyph","useCallback","useEffect","useId","useMemo","useRef","useState","Fragment","jsx","jsxs","useState","useCallback","useRef","useEffect","useMemo","useId","Fragment","useCallback","useEffect","useId","useMemo","useRef","useState","custom","jsx","jsxs","BASIS_TONES","useId","useState","useRef","useCallback","useEffect","useMemo","Fragment","useCallback","useEffect","useId","useMemo","useRef","useState","Fragment","jsx","jsxs","useState","useCallback","useRef","useId","useMemo","useEffect","jsx","jsxs","isValidElement","useCallback","useEffect","useRef","useState","Fragment","jsx","jsxs","isValidElement","useState","useRef","useCallback","useEffect","Fragment","jsx","jsxs","useState","useMemo","useRef","useEffect"]}