@open-mercato/ui 0.6.1-develop.3246.1.dbef9d7392 → 0.6.1-develop.3256.1.fe3dec2464

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ai/AiChat.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport {\n Bot,\n Check,\n ChevronDown,\n ChevronRight,\n Copy,\n Lightbulb,\n Loader2,\n Paperclip,\n Plus,\n Send,\n Square,\n User,\n Wrench,\n X,\n} from 'lucide-react'\nimport ReactMarkdown from 'react-markdown'\nimport remarkGfm from 'remark-gfm'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { Alert, AlertDescription, AlertTitle } from '../primitives/alert'\nimport { Button } from '../primitives/button'\nimport { IconButton } from '../primitives/icon-button'\nimport { Label } from '../primitives/label'\nimport { Textarea } from '../primitives/textarea'\nimport { parseAiContentSegments } from './AiMessageContent'\nimport { RecordCard } from './records/RecordCard'\nimport {\n defaultAiUiPartRegistry,\n isReservedAiUiPartId,\n type AiUiPartComponentId,\n type AiUiPartRegistry,\n} from './ui-part-registry'\nimport {\n useAiChat,\n type AiChatMessage,\n type AiChatMessageFile,\n type AiChatMessageUiPart,\n type AiChatToolCallSnapshot,\n} from './useAiChat'\nimport { useAiChatUpload } from './useAiChatUpload'\nimport { useAiShortcuts } from './useAiShortcuts'\n\n// Cap inline previews so we do not blow past localStorage quota (~5MB on most\n// browsers). Images larger than this still upload + send to the LLM as inline\n// base64 server-side; only the in-chat preview is dropped on reload.\nconst PREVIEW_DATA_URL_MAX_BYTES = 2 * 1024 * 1024\n\nasync function readFileAsDataUrl(file: File): Promise<string | undefined> {\n if (!file.type.startsWith('image/')) return undefined\n if (file.size > PREVIEW_DATA_URL_MAX_BYTES) return undefined\n return new Promise<string | undefined>((resolve) => {\n const reader = new FileReader()\n reader.onload = () =>\n resolve(typeof reader.result === 'string' ? reader.result : undefined)\n reader.onerror = () => resolve(undefined)\n try {\n reader.readAsDataURL(file)\n } catch {\n resolve(undefined)\n }\n })\n}\n\n/**\n * Optional resolved-tool snapshot the host can feed into the debug panel.\n * Step 4.6 wires this from the `GET /api/ai_assistant/ai/agents` response\n * (`tools[]`). Step 5.3+ will replace the manual wiring with a streamed\n * `debug` part once the dispatcher emits one.\n */\nexport interface AiChatDebugTool {\n name: string\n displayName?: string\n isMutation?: boolean\n requiredFeatures?: string[]\n}\n\n/**\n * Resolved prompt-section snapshot for the debug panel. Until Phase 3 Step\n * 5.3 lands structured `PromptTemplate.sections`, hosts synthesise this\n * from the agent's `systemPrompt` + additive overrides.\n */\nexport interface AiChatDebugPromptSection {\n id: string\n source?: 'default' | 'override' | 'placeholder'\n text?: string\n}\n\n/** Quick-action suggestion shown in the welcome state. */\nexport interface AiChatSuggestion {\n label: string\n prompt: string\n icon?: React.ReactNode\n}\n\n/** Context item displayed as a chip/pill in the chat header. */\nexport interface AiChatContextItem {\n label: string\n detail?: string\n}\n\nexport interface AiChatProps {\n agent: string\n apiPath?: string\n pageContext?: Record<string, unknown>\n attachmentIds?: string[]\n initialMessages?: Array<{ role: 'user' | 'assistant'; content: string }>\n debug?: boolean\n className?: string\n placeholder?: string\n onMutationRequested?: (pendingActionId: string) => void\n onError?: (err: { code?: string; message: string }) => void\n /**\n * Optional stable conversation id. Forwarded verbatim to\n * `POST /api/ai_assistant/ai/chat` request bodies and reused across\n * turns so the Step 5.6 `prepareMutation` idempotency hash stays\n * stable across retries within the same chat. When omitted, the hook\n * mints a fresh random id once on mount \u2014 remounting the component\n * resets the conversation.\n */\n conversationId?: string\n /**\n * Optional UI-part registry. Defaults to the module-global\n * {@link defaultAiUiPartRegistry}. Pass a scoped registry from\n * {@link createAiUiPartRegistry} when embedding multiple `<AiChat>`\n * instances that should not share registrations (playground, tests).\n */\n registry?: AiUiPartRegistry\n /**\n * Optional list of server-emitted UI parts to render inside the chat\n * transcript. The registry resolves each part via `componentId`. Phase 3\n * will populate this from the streamed dispatcher response; Phase 2 WS-A\n * leaves the wiring exposed so hosts can preview the registry path.\n */\n uiParts?: Array<{\n componentId: AiUiPartComponentId\n payload?: unknown\n pendingActionId?: string\n }>\n /**\n * Optional resolved-tool map for the debug panel. Ignored when\n * `debug` is falsy.\n */\n debugTools?: AiChatDebugTool[]\n /**\n * Optional resolved prompt sections for the debug panel. Ignored when\n * `debug` is falsy.\n */\n debugPromptSections?: AiChatDebugPromptSection[]\n /** Suggested prompts shown in the empty / welcome state. */\n suggestions?: AiChatSuggestion[]\n /** Context items shown as pills above the transcript (e.g. selected products). */\n contextItems?: AiChatContextItem[]\n /** Welcome heading shown when there are no messages yet. */\n welcomeTitle?: string\n /** Welcome description shown below the heading. */\n welcomeDescription?: string\n}\n\ninterface ServerEmittedUiPartRef {\n componentId: AiUiPartComponentId\n payload?: unknown\n pendingActionId?: string\n}\n\nfunction mapErrorCodeToVariant(\n code: string | undefined,\n): 'destructive' | 'warning' {\n if (!code) return 'destructive'\n // Policy denies that describe a filtered tool or attachment surface a\n // warning alert; caller can still continue. Hard denials (agent_unknown,\n // agent_features_denied, unauthenticated, execution_mode_not_supported,\n // mutation_blocked_by_*, validation_error) surface destructive alerts.\n const warningCodes = new Set<string>([\n 'tool_not_whitelisted',\n 'tool_features_denied',\n 'attachment_type_not_accepted',\n ])\n return warningCodes.has(code) ? 'warning' : 'destructive'\n}\n\nconst MARKDOWN_TYPOGRAPHY_CLASS = cn(\n '[&_p]:my-1 [&_p:first-child]:mt-0 [&_p:last-child]:mb-0',\n '[&_ul]:my-2 [&_ol]:my-2 [&_ul]:ml-4 [&_ol]:ml-4 [&_ul]:list-disc [&_ol]:list-decimal',\n '[&_li]:my-0.5',\n '[&_h1]:mt-3 [&_h1]:mb-2 [&_h1]:text-base [&_h1]:font-semibold',\n '[&_h2]:mt-3 [&_h2]:mb-2 [&_h2]:text-sm [&_h2]:font-semibold',\n '[&_h3]:mt-2 [&_h3]:mb-1 [&_h3]:text-sm [&_h3]:font-semibold',\n '[&_a]:text-primary [&_a]:underline [&_a]:underline-offset-2',\n '[&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-xs',\n '[&_pre]:my-2 [&_pre]:overflow-x-auto [&_pre]:rounded-md [&_pre]:border [&_pre]:border-border [&_pre]:bg-muted [&_pre]:p-3',\n '[&_pre_code]:bg-transparent [&_pre_code]:p-0',\n '[&_blockquote]:my-2 [&_blockquote]:border-l-2 [&_blockquote]:border-border [&_blockquote]:pl-3 [&_blockquote]:text-muted-foreground',\n '[&_table]:my-2 [&_table]:w-full [&_table]:border-collapse [&_table]:text-xs',\n '[&_th]:border [&_th]:border-border [&_th]:px-2 [&_th]:py-1 [&_th]:text-left [&_th]:font-medium',\n '[&_td]:border [&_td]:border-border [&_td]:px-2 [&_td]:py-1',\n)\n\nconst MARKDOWN_COMPONENTS = {\n a: ({ node, ...props }: React.AnchorHTMLAttributes<HTMLAnchorElement> & { node?: unknown }) => (\n <a {...props} target=\"_blank\" rel=\"noreferrer\" />\n ),\n}\n\nfunction MarkdownChunk({ text }: { text: string }) {\n if (!text.trim()) return null\n return (\n <div className={cn('text-sm', MARKDOWN_TYPOGRAPHY_CLASS)}>\n <ReactMarkdown remarkPlugins={[remarkGfm]} components={MARKDOWN_COMPONENTS}>\n {text}\n </ReactMarkdown>\n </div>\n )\n}\n\nfunction MessageContent({ content, isAssistant }: { content: string; isAssistant: boolean }) {\n if (!isAssistant) {\n return <div className=\"whitespace-pre-wrap text-sm\">{content}</div>\n }\n if (!content) {\n return null\n }\n const segments = parseAiContentSegments(content)\n if (segments.length === 0) {\n return null\n }\n return (\n <div className=\"space-y-1\" data-ai-message-content=\"\">\n {segments.map((segment, index) => {\n if (segment.kind === 'record-card') {\n return <RecordCard key={`card-${index}`} data={segment.payload} />\n }\n if (segment.kind === 'invalid-card') {\n return (\n <pre\n key={`raw-${index}`}\n className=\"my-2 max-h-60 overflow-auto rounded-md border border-dashed border-border bg-muted p-2 text-xs\"\n data-ai-record-card-invalid={segment.info}\n >\n {segment.raw}\n </pre>\n )\n }\n return <MarkdownChunk key={`md-${index}`} text={segment.text} />\n })}\n </div>\n )\n}\n\nfunction safeStringify(value: unknown): string {\n if (value === null || value === undefined) return ''\n if (typeof value === 'string') return value\n try {\n return JSON.stringify(value, null, 2)\n } catch {\n return String(value)\n }\n}\n\nfunction ToolCallList({ toolCalls }: { toolCalls: AiChatToolCallSnapshot[] }) {\n const t = useT()\n const [openId, setOpenId] = React.useState<string | null>(null)\n if (!toolCalls || toolCalls.length === 0) return null\n return (\n <div className=\"space-y-1\" data-ai-chat-tool-calls=\"\">\n {toolCalls.map((call) => {\n const isOpen = openId === call.id\n const isError = call.state === 'error'\n const isPending = call.state === 'pending'\n const isComplete = call.state === 'complete'\n const statusLabel = isError\n ? t('ai_assistant.chat.toolError', 'failed')\n : isPending\n ? t('ai_assistant.chat.toolRunning', 'running\u2026')\n : t('ai_assistant.chat.toolDone', 'done')\n return (\n <div\n key={call.id}\n className={cn(\n 'rounded-md border border-border bg-muted/30',\n isError ? 'border-destructive/40 bg-destructive/5' : '',\n )}\n data-ai-chat-tool-call={call.toolName}\n data-ai-chat-tool-state={call.state}\n >\n <button\n type=\"button\"\n onClick={() => setOpenId(isOpen ? null : call.id)}\n className=\"flex w-full items-center gap-2 px-2 py-1.5 text-left text-xs font-medium hover:bg-muted/60\"\n >\n {isOpen ? (\n <ChevronDown className=\"size-3.5 text-muted-foreground\" aria-hidden />\n ) : (\n <ChevronRight className=\"size-3.5 text-muted-foreground\" aria-hidden />\n )}\n {isPending ? (\n <Loader2 className=\"size-3.5 animate-spin text-muted-foreground\" aria-hidden />\n ) : (\n <Wrench\n className={cn(\n 'size-3.5',\n isError ? 'text-destructive' : 'text-muted-foreground',\n )}\n aria-hidden\n />\n )}\n <span className=\"font-mono\">{call.toolName}</span>\n <span\n className={cn(\n 'ml-auto text-[10px] uppercase tracking-wide',\n isError\n ? 'text-destructive'\n : isComplete\n ? 'text-status-success-text'\n : 'text-muted-foreground',\n )}\n >\n {statusLabel}\n </span>\n </button>\n {isOpen ? (\n <div className=\"space-y-1 border-t border-border/60 px-2 py-1.5 text-xs\">\n {call.input !== undefined ? (\n <div>\n <div className=\"text-[10px] uppercase tracking-wide text-muted-foreground\">\n {t('ai_assistant.chat.toolInput', 'Input')}\n </div>\n <pre className=\"mt-0.5 max-h-32 overflow-auto rounded bg-background p-1.5 font-mono text-[11px]\">\n {safeStringify(call.input)}\n </pre>\n </div>\n ) : null}\n {call.output !== undefined && !isError ? (\n <div>\n <div className=\"text-[10px] uppercase tracking-wide text-muted-foreground\">\n {t('ai_assistant.chat.toolOutput', 'Output')}\n </div>\n <pre className=\"mt-0.5 max-h-32 overflow-auto rounded bg-background p-1.5 font-mono text-[11px]\">\n {safeStringify(call.output)}\n </pre>\n </div>\n ) : null}\n {call.errorMessage ? (\n <div className=\"text-destructive\">{call.errorMessage}</div>\n ) : null}\n </div>\n ) : null}\n </div>\n )\n })}\n </div>\n )\n}\n\nfunction ReasoningPanel({ text, streaming }: { text: string; streaming: boolean }) {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n if (!text) return null\n return (\n <div\n className=\"rounded-md border border-border bg-muted/30\"\n data-ai-chat-reasoning={streaming ? 'streaming' : 'complete'}\n >\n <button\n type=\"button\"\n onClick={() => setOpen((o) => !o)}\n className=\"flex w-full items-center gap-2 px-2 py-1.5 text-left text-xs font-medium hover:bg-muted/60\"\n >\n {open ? (\n <ChevronDown className=\"size-3.5 text-muted-foreground\" aria-hidden />\n ) : (\n <ChevronRight className=\"size-3.5 text-muted-foreground\" aria-hidden />\n )}\n <Lightbulb className=\"size-3.5 text-muted-foreground\" aria-hidden />\n <span>{t('ai_assistant.chat.reasoning', 'Reasoning')}</span>\n {streaming ? (\n <Loader2\n className=\"ml-1 size-3 animate-spin text-muted-foreground\"\n aria-hidden\n />\n ) : null}\n </button>\n {open ? (\n <pre className=\"max-h-48 overflow-auto whitespace-pre-wrap border-t border-border/60 px-2 py-1.5 text-xs text-muted-foreground\">\n {text}\n </pre>\n ) : null}\n </div>\n )\n}\n\nfunction MessageRow({\n message,\n registry,\n onMutationRequested,\n}: {\n message: AiChatMessage\n registry?: AiUiPartRegistry\n onMutationRequested?: (pendingActionId: string) => void\n}) {\n const t = useT()\n const isAssistant = message.role === 'assistant'\n const label = isAssistant\n ? t('ai_assistant.chat.assistantRoleLabel', 'Assistant')\n : t('ai_assistant.chat.userRoleLabel', 'You')\n const Icon = isAssistant ? Bot : User\n const [copied, setCopied] = React.useState(false)\n const copyTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null)\n\n React.useEffect(() => {\n return () => {\n if (copyTimerRef.current) clearTimeout(copyTimerRef.current)\n }\n }, [])\n\n const handleCopy = React.useCallback(async () => {\n const text = message.content\n if (!text) return\n try {\n if (typeof navigator !== 'undefined' && navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(text)\n } else {\n const textarea = document.createElement('textarea')\n textarea.value = text\n textarea.setAttribute('readonly', '')\n textarea.style.position = 'absolute'\n textarea.style.left = '-9999px'\n document.body.appendChild(textarea)\n textarea.select()\n document.execCommand('copy')\n document.body.removeChild(textarea)\n }\n setCopied(true)\n if (copyTimerRef.current) clearTimeout(copyTimerRef.current)\n copyTimerRef.current = setTimeout(() => setCopied(false), 1500)\n } catch {\n // Clipboard API blocked (no permission, http context) \u2014 silently fail.\n }\n }, [message.content])\n\n return (\n <div\n className={cn(\n 'group/message flex gap-3 px-3 py-2',\n isAssistant ? 'bg-muted/40 rounded-md' : '',\n )}\n data-role={message.role}\n >\n <div\n className={cn(\n 'flex size-6 shrink-0 items-center justify-center rounded-full',\n isAssistant\n ? 'bg-primary/10 text-primary'\n : 'bg-secondary text-secondary-foreground',\n )}\n aria-hidden\n >\n <Icon className=\"size-4\" />\n </div>\n <div className=\"flex-1 space-y-1\">\n <div className=\"flex items-center justify-between gap-2\">\n <div className=\"text-xs font-medium text-muted-foreground\">{label}</div>\n {message.content ? (\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={handleCopy}\n aria-label={\n copied\n ? t('ai_assistant.chat.copied', 'Copied')\n : t('ai_assistant.chat.copyMessage', 'Copy message')\n }\n data-ai-chat-copy-button=\"\"\n className=\"opacity-0 transition-opacity group-hover/message:opacity-100 focus-visible:opacity-100\"\n >\n {copied ? (\n <Check className=\"size-3.5 text-status-success-icon\" aria-hidden />\n ) : (\n <Copy className=\"size-3.5\" aria-hidden />\n )}\n </IconButton>\n ) : null}\n </div>\n {message.files && message.files.length > 0 ? (\n <div className=\"flex flex-wrap gap-2 py-1\">\n {message.files.map((file, i) =>\n file.previewUrl ? (\n <img\n key={i}\n src={file.previewUrl}\n alt={file.name}\n className=\"max-h-32 max-w-[200px] rounded-md border border-border object-cover\"\n />\n ) : (\n <span\n key={i}\n className=\"inline-flex items-center gap-1 rounded-full border border-border bg-muted px-2 py-0.5 text-xs\"\n >\n <Paperclip className=\"size-3\" aria-hidden />\n {file.name}\n </span>\n ),\n )}\n </div>\n ) : null}\n {isAssistant && message.reasoning ? (\n <ReasoningPanel\n text={message.reasoning}\n streaming={message.reasoningStreaming === true}\n />\n ) : null}\n {isAssistant && message.toolCalls && message.toolCalls.length > 0 ? (\n <ToolCallList toolCalls={message.toolCalls} />\n ) : null}\n <MessageContent content={message.content} isAssistant={isAssistant} />\n {isAssistant && registry && message.uiParts && message.uiParts.length > 0 ? (\n <MessageUiParts\n parts={message.uiParts}\n registry={registry}\n onMutationRequested={onMutationRequested}\n />\n ) : null}\n </div>\n </div>\n )\n}\n\nfunction MessageUiParts({\n parts,\n registry,\n onMutationRequested,\n}: {\n parts: AiChatMessageUiPart[]\n registry: AiUiPartRegistry\n onMutationRequested?: (pendingActionId: string) => void\n}) {\n return (\n <div className=\"mt-2 flex flex-col gap-2\" data-ai-message-ui-parts=\"\">\n {parts.map((part) => (\n <AiUiPartRenderer\n key={part.key}\n part={{\n componentId: part.componentId as AiUiPartComponentId,\n payload: part.payload,\n pendingActionId: part.pendingActionId,\n }}\n registry={registry}\n onMutationRequested={onMutationRequested}\n />\n ))}\n </div>\n )\n}\n\nfunction UnknownUiPartPlaceholder({ componentId }: { componentId: AiUiPartComponentId }) {\n const t = useT()\n return (\n <div\n className=\"inline-flex items-center gap-2 rounded-full border border-dashed border-border bg-muted px-3 py-1 text-xs text-muted-foreground\"\n data-ai-ui-part-placeholder={componentId}\n >\n <span>\n {t('ai_assistant.chat.uiPartPending', 'Pending UI part:')} {componentId}\n </span>\n </div>\n )\n}\n\nfunction AiUiPartRenderer({\n part,\n registry,\n onMutationRequested,\n}: {\n part: ServerEmittedUiPartRef\n registry: AiUiPartRegistry\n onMutationRequested?: (pendingActionId: string) => void\n}) {\n const Component = registry.resolve(part.componentId)\n const isReserved = isReservedAiUiPartId(part.componentId)\n React.useEffect(() => {\n if (Component) return\n if (isReserved) return\n try {\n // eslint-disable-next-line no-console\n console.warn(\n `[AiChat] No component registered for UI part \"${part.componentId}\".`,\n )\n } catch {\n // noop\n }\n }, [Component, part.componentId, isReserved])\n React.useEffect(() => {\n if (part.pendingActionId) {\n onMutationRequested?.(part.pendingActionId)\n }\n }, [part.pendingActionId, onMutationRequested])\n if (!Component) {\n return <UnknownUiPartPlaceholder componentId={part.componentId} />\n }\n return (\n <Component\n componentId={part.componentId}\n payload={part.payload}\n pendingActionId={part.pendingActionId}\n />\n )\n}\n\nfunction WelcomeState({\n title,\n description,\n suggestions,\n onSuggestionClick,\n}: {\n title?: string\n description?: string\n suggestions?: AiChatSuggestion[]\n onSuggestionClick: (prompt: string) => void\n}) {\n const t = useT()\n const heading = title ?? t('ai_assistant.chat.welcomeTitle', 'How can I help?')\n const desc =\n description ??\n t(\n 'ai_assistant.chat.welcomeDescription',\n 'Ask me anything about your data. Here are some things I can do:',\n )\n return (\n <div className=\"flex flex-1 flex-col items-center justify-center gap-4 px-4 py-8\">\n <div\n className=\"flex size-12 items-center justify-center rounded-full bg-primary/10 text-primary\"\n aria-hidden\n >\n <Bot className=\"size-6\" />\n </div>\n <div className=\"space-y-1 text-center\">\n <h3 className=\"text-sm font-semibold\">{heading}</h3>\n <p className=\"text-xs text-muted-foreground\">{desc}</p>\n </div>\n {suggestions && suggestions.length > 0 ? (\n <div className=\"flex w-full max-w-md flex-col gap-2\" data-ai-chat-suggestions=\"\">\n {suggestions.map((suggestion, index) => (\n <button\n key={index}\n type=\"button\"\n className=\"flex items-center gap-2 rounded-lg border border-border bg-card px-3 py-2.5 text-left text-sm transition-colors hover:bg-accent hover:text-accent-foreground\"\n onClick={() => onSuggestionClick(suggestion.prompt)}\n data-ai-chat-suggestion={index}\n >\n {suggestion.icon ? (\n <span className=\"shrink-0 text-muted-foreground\" aria-hidden>\n {suggestion.icon}\n </span>\n ) : null}\n <span>{suggestion.label}</span>\n </button>\n ))}\n </div>\n ) : null}\n </div>\n )\n}\n\nfunction ContextItemsPill({ items }: { items: AiChatContextItem[] }) {\n if (items.length === 0) return null\n return (\n <div\n className=\"flex flex-wrap gap-1.5 border-b border-border px-3 py-2\"\n data-ai-chat-context-items=\"\"\n >\n {items.map((item, index) => (\n <span\n key={index}\n className=\"inline-flex items-center gap-1 rounded-full border border-border bg-secondary px-2 py-0.5 text-xs text-secondary-foreground\"\n data-ai-chat-context-item={index}\n title={item.detail}\n >\n {item.label}\n </span>\n ))}\n </div>\n )\n}\n\n/**\n * Embeddable AI chat component. Binds to the dispatcher route\n * `POST /api/ai_assistant/ai/chat?agent=<module>.<agent>` via\n * {@link createAiAgentTransport}. Phase 2 WS-A deliverable (Step 4.1).\n *\n * - Keyboard: `Enter` submits; `Shift+Enter` inserts a newline; `Escape`\n * aborts streaming (or blurs the composer when idle).\n * - Error envelopes from the dispatcher surface as `Alert` + `onError`.\n * - UI parts render via the client-side registry; unknown parts render a\n * neutral placeholder chip so mutation-card slots reserved for Phase 3\n * never throw before their implementations land.\n */\nexport function AiChat({\n agent,\n apiPath,\n pageContext,\n attachmentIds,\n initialMessages,\n debug,\n className,\n placeholder,\n onMutationRequested,\n onError,\n registry,\n uiParts: uiPartsProp,\n debugTools,\n debugPromptSections,\n conversationId,\n suggestions,\n contextItems,\n welcomeTitle,\n welcomeDescription,\n}: AiChatProps) {\n const t = useT()\n const textareaRef = React.useRef<HTMLTextAreaElement | null>(null)\n const transcriptRef = React.useRef<HTMLDivElement | null>(null)\n const fileInputRef = React.useRef<HTMLInputElement | null>(null)\n const [input, setInput] = React.useState('')\n type PendingAttachment = {\n file: File\n attachmentId?: string\n /**\n * Base64 data URL of the image (capped by PREVIEW_DATA_URL_MAX_BYTES).\n * Stored on the message so the preview survives a reload \u2014 durable\n * server URLs were intentionally avoided because the LLM provider can\n * never reach a localhost dev URL anyway, and HTTP fetches add latency\n * the chat doesn't need.\n */\n previewDataUrl?: string\n error?: string\n }\n const [pendingFiles, setPendingFiles] = React.useState<PendingAttachment[]>([])\n const upload = useAiChatUpload()\n const isUploading = upload.busy\n\n const uploadedAttachmentIds = React.useMemo(\n () =>\n pendingFiles\n .map((entry) => entry.attachmentId)\n .filter((id): id is string => typeof id === 'string' && id.length > 0),\n [pendingFiles],\n )\n\n const allAttachmentIds = React.useMemo(\n () => [...(attachmentIds ?? []), ...uploadedAttachmentIds],\n [attachmentIds, uploadedAttachmentIds],\n )\n\n const chat = useAiChat({\n agent,\n apiPath,\n pageContext,\n attachmentIds: allAttachmentIds.length > 0 ? allAttachmentIds : undefined,\n debug,\n initialMessages,\n onError,\n conversationId,\n })\n\n const isStreaming = chat.status === 'streaming'\n const isSubmitting = chat.status === 'submitting'\n const isBusy = isStreaming || isSubmitting\n\n // Surface a \"Thinking...\" placeholder so the chat does not look frozen.\n // Visible whenever ANY of the following is true while a turn is in flight:\n // (a) we're still in the submit phase before the first stream chunk\n // (b) streaming, but no content / reasoning / tool calls have arrived yet\n // (c) streaming, and at least one tool call is still in `pending` state\n // (the model is waiting on a tool result \u2014 the previous version\n // treated `toolCalls.length > 0` as \"has content\" and hid the\n // indicator the moment the first tool started, even though the\n // model had not produced any user-visible output yet)\n // (d) streaming, and the last visible event was a finished tool call\n // \u2014 the model is reasoning about the result before emitting more\n // text or kicking off the next tool\n // (e) streaming, but no delta has landed in the last ~300 ms (idle gap)\n const lastAssistant = React.useMemo(() => {\n for (let index = chat.messages.length - 1; index >= 0; index -= 1) {\n const candidate = chat.messages[index]\n if (candidate?.role === 'assistant') return candidate\n }\n return null\n }, [chat.messages])\n\n const trimmedContent = lastAssistant?.content?.trim() ?? ''\n const hasReasoning = !!(lastAssistant?.reasoning && lastAssistant.reasoning.length > 0)\n const toolCalls = lastAssistant?.toolCalls ?? []\n const hasPendingToolCall = toolCalls.some((call) => call.state === 'pending')\n const hasCompletedToolCall = toolCalls.some(\n (call) => call.state === 'complete' || call.state === 'error',\n )\n const hasAnyVisibleSignal = !!(\n trimmedContent || hasReasoning || toolCalls.length > 0\n )\n\n const assistantStreamSnapshot = React.useMemo(() => {\n if (!lastAssistant) return ''\n const toolSig = toolCalls\n .map((call) => `${call.id}:${call.state}:${call.output != null ? 1 : 0}`)\n .join('|')\n return [\n lastAssistant.id,\n lastAssistant.content?.length ?? 0,\n lastAssistant.reasoning?.length ?? 0,\n lastAssistant.reasoningStreaming ? 1 : 0,\n toolSig,\n ].join('#')\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [lastAssistant])\n\n const lastStreamUpdateRef = React.useRef<number>(Date.now())\n const lastSnapshotRef = React.useRef<string>('')\n const [, setStreamTick] = React.useState(0)\n\n React.useEffect(() => {\n if (assistantStreamSnapshot !== lastSnapshotRef.current) {\n lastSnapshotRef.current = assistantStreamSnapshot\n lastStreamUpdateRef.current = Date.now()\n setStreamTick((value) => value + 1)\n }\n }, [assistantStreamSnapshot])\n\n React.useEffect(() => {\n if (!isStreaming && !isSubmitting) return\n const interval = window.setInterval(() => {\n setStreamTick((value) => value + 1)\n }, 200)\n return () => window.clearInterval(interval)\n }, [isStreaming, isSubmitting])\n\n const idleDuringStream =\n isStreaming && Date.now() - lastStreamUpdateRef.current >= 300\n\n const showThinkingIndicator =\n isSubmitting ||\n (isStreaming &&\n (\n !hasAnyVisibleSignal ||\n hasPendingToolCall ||\n // Tool just returned and the model hasn't started speaking yet.\n (hasCompletedToolCall && !trimmedContent) ||\n idleDuringStream\n ))\n\n const activeRegistry = registry ?? defaultAiUiPartRegistry\n\n // Reserved UI parts. Phase 3 will populate this from the streamed response;\n // for Phase 2 WS-A it stays empty unless the host surfaces test/debug parts\n // via the optional `uiParts` prop so the registry resolution path can be\n // exercised without waiting for the runtime emitter.\n const uiParts: ServerEmittedUiPartRef[] = React.useMemo(\n () => (uiPartsProp ?? []).map((part) => ({\n componentId: part.componentId,\n payload: part.payload,\n pendingActionId: part.pendingActionId,\n })),\n [uiPartsProp],\n )\n\n const hasUploadingFiles = React.useMemo(\n () => pendingFiles.some((entry) => !entry.attachmentId && !entry.error),\n [pendingFiles],\n )\n\n const handleSendMessage = React.useCallback(\n (text: string) => {\n if (!text.trim() || isBusy) return\n // Block send while any attachment is still uploading. Without this guard\n // the message would ship with an empty attachmentIds list (the chip is\n // visible but the server hasn't returned an id yet), the model would\n // never see the file, and `setPendingFiles([])` below would erase the\n // chip \u2014 so the upload finishes into the void. Surface the wait via the\n // disabled Send button + composer hint instead.\n if (hasUploadingFiles || isUploading) return\n const filesToAttach = pendingFiles.map((entry): AiChatMessageFile => {\n const isImage = entry.file.type.startsWith('image/')\n const fallback = isImage ? URL.createObjectURL(entry.file) : undefined\n return {\n name: entry.file.name,\n type: entry.file.type,\n previewUrl: isImage ? (entry.previewDataUrl ?? fallback) : undefined,\n }\n })\n setInput('')\n setPendingFiles([])\n void chat.sendMessage(text, filesToAttach.length > 0 ? filesToAttach : undefined)\n },\n [chat, hasUploadingFiles, isBusy, isUploading, pendingFiles],\n )\n\n const handleSubmit = React.useCallback(() => {\n handleSendMessage(input)\n }, [handleSendMessage, input])\n\n // Listen for \"Fix with AI\" requests dispatched by the failure variant\n // of `MutationResultCard`. Any rendered failure card can fire a custom\n // DOM event with the prompt \u2014 this side-steps having to thread a\n // sendMessage callback through the UI-part registry while keeping the\n // chat the single owner of message creation. Idempotency is handled\n // server-side: `prepareMutation` only dedupes against active `pending`\n // rows, so a retry after a terminal failure always produces a fresh\n // pending action.\n React.useEffect(() => {\n if (typeof window === 'undefined') return\n const handler = (event: Event) => {\n const detail = (event as CustomEvent<{ message?: string }>).detail\n const message = detail?.message\n if (typeof message !== 'string' || message.trim().length === 0) return\n handleSendMessage(message)\n }\n window.addEventListener('om-ai-chat-fix-request', handler as EventListener)\n return () => {\n window.removeEventListener('om-ai-chat-fix-request', handler as EventListener)\n }\n }, [handleSendMessage])\n\n const cancelOrBlur = React.useCallback(() => {\n if (isBusy) {\n chat.cancel()\n return\n }\n textareaRef.current?.blur()\n }, [chat, isBusy])\n\n const handleFileSelect = React.useCallback(\n async (event: React.ChangeEvent<HTMLInputElement>) => {\n const files = Array.from(event.target.files ?? [])\n if (files.length === 0) return\n const queued: PendingAttachment[] = files.map((file) => ({ file }))\n setPendingFiles((prev) => [...prev, ...queued])\n const [previewResults, result] = await Promise.all([\n Promise.all(files.map((file) => readFileAsDataUrl(file))),\n upload.upload(files),\n ])\n // Pair upload outcomes back to chips by input INDEX, not by filename.\n // The server may sanitize the uploaded name (whitespace, unicode,\n // dangerous characters), and two files in the same batch can share a\n // name \u2014 both cases broke the previous Map-by-fileName matching and\n // left the chip stuck on the spinner forever.\n const idByIndex = new Map<number, string>()\n for (const item of result.items) {\n if (typeof item.inputIndex === 'number') idByIndex.set(item.inputIndex, item.attachmentId)\n }\n const errorByIndex = new Map<number, string>()\n for (const failure of result.failed) {\n if (typeof failure.inputIndex === 'number') errorByIndex.set(failure.inputIndex, failure.message)\n }\n setPendingFiles((prev) => {\n const next = prev.slice()\n const baseIndex = next.length - files.length\n for (let offset = 0; offset < files.length; offset += 1) {\n const index = baseIndex + offset\n if (index < 0) continue\n const entry = next[index]\n if (!entry) continue\n const dataUrl = previewResults[offset]\n const patch: PendingAttachment = {\n ...entry,\n previewDataUrl: dataUrl ?? entry.previewDataUrl,\n }\n if (!patch.attachmentId) {\n const id = idByIndex.get(offset)\n if (id) {\n patch.attachmentId = id\n patch.error = undefined\n } else {\n // Defensive fallback: if neither success nor failure carried an\n // index for this slot (older transports, partial outcome), the\n // chip would otherwise stay on the spinner. Mark it as a\n // generic error so the user can remove it and retry instead of\n // staring at a dead spinner that also blocks the Send button.\n const explicitError = errorByIndex.get(offset)\n patch.error =\n explicitError ??\n 'Upload finished without a server response. Remove the file and try again.'\n }\n }\n next[index] = patch\n }\n return next\n })\n if (fileInputRef.current) fileInputRef.current.value = ''\n },\n [upload],\n )\n\n const removePendingFile = React.useCallback((index: number) => {\n setPendingFiles((prev) => prev.filter((_, i) => i !== index))\n }, [])\n\n const { handleKeyDown } = useAiShortcuts({\n onSubmit: handleSubmit,\n onCancel: cancelOrBlur,\n })\n\n React.useEffect(() => {\n textareaRef.current?.focus()\n }, [])\n\n // Sticky-bottom autoscroll: only re-pin to the bottom when the user is\n // already there (or within a small tolerance). If they have scrolled up to\n // read an earlier part of a long response, every streaming delta would\n // otherwise yank them back to the tail and make the message look truncated.\n // Tolerance is generous enough to absorb sub-pixel rounding, but tight\n // enough that an intentional scroll-up keeps the user where they want.\n const stickToBottomRef = React.useRef(true)\n const SCROLL_STICK_TOLERANCE_PX = 64\n\n React.useEffect(() => {\n const node = transcriptRef.current\n if (!node) return\n const handleScroll = () => {\n const distanceFromBottom = node.scrollHeight - node.scrollTop - node.clientHeight\n stickToBottomRef.current = distanceFromBottom <= SCROLL_STICK_TOLERANCE_PX\n }\n node.addEventListener('scroll', handleScroll, { passive: true })\n return () => node.removeEventListener('scroll', handleScroll)\n }, [])\n\n React.useEffect(() => {\n const node = transcriptRef.current\n if (!node) return\n if (!stickToBottomRef.current) return\n node.scrollTop = node.scrollHeight\n }, [chat.messages])\n\n // Mark the body so floating UI surfaces (e.g. the demo feedback FAB) can\n // hide themselves while the chat is open and would otherwise overlay the\n // composer's send button. CSS lives in `apps/mercato/src/app/globals.css`\n // alongside the column-chooser precedent.\n React.useEffect(() => {\n if (typeof document === 'undefined') return\n const previous = document.body.getAttribute('data-ai-chat-open')\n document.body.setAttribute('data-ai-chat-open', 'true')\n return () => {\n if (previous === null) {\n document.body.removeAttribute('data-ai-chat-open')\n } else {\n document.body.setAttribute('data-ai-chat-open', previous)\n }\n }\n }, [])\n\n const handleNewConversation = React.useCallback(() => {\n chat.reset()\n setInput('')\n setPendingFiles([])\n upload.reset()\n setTimeout(() => textareaRef.current?.focus(), 0)\n }, [chat, upload])\n\n const resolvedPlaceholder =\n placeholder ?? t('ai_assistant.chat.composerPlaceholder', 'Message the AI agent...')\n\n const errorVariant = mapErrorCodeToVariant(chat.error?.code)\n\n return (\n <section\n className={cn(\n 'flex h-full min-h-[320px] flex-col gap-3 rounded-lg border border-border bg-background p-3',\n className,\n )}\n aria-label={t('ai_assistant.chat.regionLabel', 'AI chat')}\n data-ai-chat-agent={agent}\n data-ai-chat-conversation-id={chat.conversationId}\n >\n <div className=\"flex items-center justify-between gap-2 border-b border-border pb-2\">\n <div className=\"flex flex-1 items-center gap-2 text-xs text-muted-foreground\">\n {contextItems && contextItems.length > 0 ? (\n <ContextItemsPill items={contextItems} />\n ) : (\n <span className=\"font-mono opacity-70\" aria-hidden>\n {chat.conversationId.slice(0, 8)}\n </span>\n )}\n </div>\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={handleNewConversation}\n disabled={isBusy}\n aria-label={t('ai_assistant.chat.newConversation', 'Start new conversation')}\n title={t('ai_assistant.chat.newConversation', 'Start new conversation')}\n data-ai-chat-new-conversation=\"\"\n >\n <Plus className=\"size-4\" aria-hidden />\n </IconButton>\n </div>\n <div\n ref={transcriptRef}\n role=\"log\"\n aria-live=\"polite\"\n aria-label={t('ai_assistant.chat.transcriptLabel', 'Chat transcript')}\n className=\"flex-1 space-y-2 overflow-y-auto pr-1\"\n >\n {chat.messages.length === 0 ? (\n <WelcomeState\n title={welcomeTitle}\n description={welcomeDescription}\n suggestions={suggestions}\n onSuggestionClick={handleSendMessage}\n />\n ) : (\n chat.messages.map((message) => (\n <MessageRow\n key={message.id}\n message={message}\n registry={activeRegistry}\n onMutationRequested={onMutationRequested}\n />\n ))\n )}\n {uiParts.map((part, index) => (\n <AiUiPartRenderer\n key={`${part.componentId}-${index}`}\n part={part}\n registry={activeRegistry}\n onMutationRequested={onMutationRequested}\n />\n ))}\n {showThinkingIndicator ? (\n <div\n className=\"flex items-center gap-2 px-3 py-2 text-xs text-muted-foreground\"\n data-ai-chat-state=\"thinking\"\n >\n <Loader2 className=\"size-4 animate-spin\" aria-hidden />\n <span>{t('ai_assistant.chat.thinking', 'Thinking...')}</span>\n </div>\n ) : null}\n </div>\n\n {chat.error ? (\n <Alert variant={errorVariant} data-ai-chat-error={chat.error.code ?? 'unknown'}>\n <AlertTitle>\n {t('ai_assistant.chat.errorTitle', 'Agent dispatch failed')}\n </AlertTitle>\n <AlertDescription>\n {chat.error.code ? (\n <span className=\"mr-2 font-mono text-xs\">{chat.error.code}</span>\n ) : null}\n {chat.error.message}\n </AlertDescription>\n </Alert>\n ) : null}\n\n <form\n className=\"flex flex-col gap-2\"\n onSubmit={(event) => {\n event.preventDefault()\n handleSubmit()\n }}\n >\n <Label\n htmlFor=\"ai-chat-composer\"\n className=\"sr-only\"\n >\n {t('ai_assistant.chat.composerLabel', 'Message composer')}\n </Label>\n {pendingFiles.length > 0 ? (\n <div className=\"flex flex-wrap gap-1.5 rounded-md border border-border bg-muted/30 px-2 py-1.5\" data-ai-chat-attachments=\"\">\n {pendingFiles.map((entry, index) => (\n <span\n key={index}\n className=\"inline-flex items-center gap-1 rounded-full border border-border bg-background px-2 py-0.5 text-xs\"\n title={entry.error ? entry.error : undefined}\n data-ai-chat-attachment-state={\n entry.error ? 'error' : entry.attachmentId ? 'ready' : 'uploading'\n }\n >\n <Paperclip className=\"size-3 text-muted-foreground\" aria-hidden />\n <span className=\"max-w-[120px] truncate\">{entry.file.name}</span>\n {!entry.attachmentId && !entry.error ? (\n <Loader2 className=\"size-3 animate-spin text-muted-foreground\" aria-hidden />\n ) : null}\n <button\n type=\"button\"\n className=\"ml-0.5 rounded-full p-0.5 hover:bg-muted\"\n onClick={() => removePendingFile(index)}\n aria-label={t('ai_assistant.chat.removeFile', 'Remove file')}\n >\n <X className=\"size-3\" />\n </button>\n </span>\n ))}\n {isUploading ? <Loader2 className=\"size-3 animate-spin text-muted-foreground\" aria-hidden /> : null}\n </div>\n ) : null}\n <Textarea\n id=\"ai-chat-composer\"\n ref={textareaRef}\n value={input}\n placeholder={resolvedPlaceholder}\n onChange={(event) => setInput(event.target.value)}\n onKeyDown={handleKeyDown}\n rows={3}\n aria-label={t('ai_assistant.chat.composerLabel', 'Message composer')}\n className=\"resize-none\"\n />\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n accept=\"image/*,.pdf,.doc,.docx,.txt,.csv\"\n className=\"hidden\"\n onChange={handleFileSelect}\n data-ai-chat-file-input=\"\"\n />\n <div className=\"flex items-center justify-between gap-2\">\n <div className=\"flex items-center gap-2\">\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => fileInputRef.current?.click()}\n disabled={isBusy || isUploading}\n aria-label={t('ai_assistant.chat.attachFile', 'Attach file')}\n >\n <Paperclip className=\"size-4\" aria-hidden />\n </IconButton>\n <p className=\"text-xs text-muted-foreground\">\n {hasUploadingFiles || isUploading\n ? t(\n 'ai_assistant.chat.uploadingHint',\n 'Uploading attachments\u2026 Send is disabled until they finish.',\n )\n : t(\n 'ai_assistant.chat.shortcutHint',\n 'Press Enter to send, Shift+Enter for new line.',\n )}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n {isStreaming ? (\n <IconButton\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => chat.cancel()}\n aria-label={t('ai_assistant.chat.cancel', 'Cancel streaming response')}\n >\n <Square className=\"size-4\" aria-hidden />\n </IconButton>\n ) : null}\n <Button\n type=\"submit\"\n size=\"sm\"\n disabled={\n isBusy ||\n isUploading ||\n hasUploadingFiles ||\n input.trim().length === 0\n }\n aria-label={\n hasUploadingFiles || isUploading\n ? t('ai_assistant.chat.sendWaitingForUpload', 'Waiting for upload to finish\u2026')\n : t('ai_assistant.chat.send', 'Send message')\n }\n title={\n hasUploadingFiles || isUploading\n ? t('ai_assistant.chat.sendWaitingForUpload', 'Waiting for upload to finish\u2026')\n : undefined\n }\n >\n <Send className=\"size-4\" aria-hidden />\n <span>{t('ai_assistant.chat.send', 'Send message')}</span>\n </Button>\n </div>\n </div>\n </form>\n\n {debug ? (\n <AiChatDebugPanel\n tools={debugTools}\n promptSections={debugPromptSections}\n lastRequestDebug={chat.lastRequestDebug}\n lastResponseDebug={chat.lastResponseDebug}\n status={chat.status}\n errorCode={chat.error?.code}\n />\n ) : null}\n </section>\n )\n}\n\ninterface DebugPanelProps {\n tools?: AiChatDebugTool[]\n promptSections?: AiChatDebugPromptSection[]\n lastRequestDebug: { url: string; body: unknown } | null\n lastResponseDebug: { status: number; text: string } | null\n status: 'idle' | 'submitting' | 'streaming'\n errorCode?: string\n}\n\nfunction AiChatDebugPanel({\n tools,\n promptSections,\n lastRequestDebug,\n lastResponseDebug,\n status,\n errorCode,\n}: DebugPanelProps) {\n const t = useT()\n return (\n <div\n className=\"flex flex-col gap-2 rounded-md border border-border bg-muted/60 p-2 text-xs\"\n data-ai-chat-debug=\"true\"\n >\n <div className=\"font-semibold\">\n {t('ai_assistant.chat.debug.panelTitle', 'Debug panel')}\n </div>\n\n <details className=\"rounded border border-border bg-background\" data-ai-chat-debug-section=\"tools\" open>\n <summary className=\"cursor-pointer px-2 py-1 font-semibold\">\n {t('ai_assistant.chat.debug.toolsSection', 'Resolved tools')}\n {tools ? (\n <span className=\"ml-2 font-mono text-muted-foreground\">({tools.length})</span>\n ) : null}\n </summary>\n <div className=\"px-2 pb-2\">\n {tools && tools.length > 0 ? (\n <ul className=\"flex flex-col gap-1\" data-ai-chat-debug-tools>\n {tools.map((tool) => (\n <li\n key={tool.name}\n className=\"flex flex-col rounded border border-border bg-muted/40 px-2 py-1\"\n data-ai-chat-debug-tool={tool.name}\n >\n <span className=\"font-mono\">{tool.name}</span>\n {tool.displayName ? (\n <span className=\"text-muted-foreground\">{tool.displayName}</span>\n ) : null}\n <span className=\"mt-1 flex flex-wrap gap-2 text-muted-foreground\">\n <span>\n {tool.isMutation\n ? t('ai_assistant.chat.debug.toolMutation', 'mutation')\n : t('ai_assistant.chat.debug.toolRead', 'read')}\n </span>\n {tool.requiredFeatures && tool.requiredFeatures.length > 0 ? (\n <span className=\"font-mono\">\n [{tool.requiredFeatures.join(', ')}]\n </span>\n ) : (\n <span>\n {t('ai_assistant.chat.debug.toolNoFeatures', 'no required features')}\n </span>\n )}\n </span>\n </li>\n ))}\n </ul>\n ) : (\n <p className=\"text-muted-foreground\">\n {t(\n 'ai_assistant.chat.debug.toolsEmpty',\n 'No tools resolved for this agent yet.',\n )}\n </p>\n )}\n </div>\n </details>\n\n <details\n className=\"rounded border border-border bg-background\"\n data-ai-chat-debug-section=\"promptSections\"\n >\n <summary className=\"cursor-pointer px-2 py-1 font-semibold\">\n {t('ai_assistant.chat.debug.promptSection', 'Prompt sections')}\n {promptSections ? (\n <span className=\"ml-2 font-mono text-muted-foreground\">({promptSections.length})</span>\n ) : null}\n </summary>\n <div className=\"px-2 pb-2\">\n {promptSections && promptSections.length > 0 ? (\n <ul className=\"flex flex-col gap-1\" data-ai-chat-debug-prompt-sections>\n {promptSections.map((section) => (\n <li\n key={section.id}\n className=\"rounded border border-border bg-muted/40 px-2 py-1\"\n data-ai-chat-debug-prompt-section-id={section.id}\n >\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"font-mono\">{section.id}</span>\n <span className=\"text-muted-foreground\">\n {section.source === 'override'\n ? t('ai_assistant.chat.debug.promptOverride', 'override')\n : section.source === 'placeholder'\n ? t('ai_assistant.chat.debug.promptPlaceholder', 'placeholder')\n : t('ai_assistant.chat.debug.promptDefault', 'default')}\n </span>\n </div>\n {section.text ? (\n <pre className=\"mt-1 max-h-24 overflow-auto whitespace-pre-wrap font-mono text-muted-foreground\">\n {section.text}\n </pre>\n ) : null}\n </li>\n ))}\n </ul>\n ) : (\n <p className=\"text-muted-foreground\">\n {t(\n 'ai_assistant.chat.debug.promptEmpty',\n 'No prompt sections resolved for this agent.',\n )}\n </p>\n )}\n </div>\n </details>\n\n <details\n className=\"rounded border border-border bg-background\"\n data-ai-chat-debug-section=\"lastRequest\"\n >\n <summary className=\"cursor-pointer px-2 py-1 font-semibold\">\n {t('ai_assistant.chat.debug.lastRequestSection', 'Last request')}\n </summary>\n <div className=\"px-2 pb-2\">\n {lastRequestDebug ? (\n <pre\n className=\"max-h-40 overflow-auto whitespace-pre-wrap font-mono\"\n data-ai-chat-debug-last-request\n >\n {JSON.stringify(lastRequestDebug, null, 2)}\n </pre>\n ) : (\n <p className=\"text-muted-foreground\">\n {t(\n 'ai_assistant.chat.debug.lastRequestEmpty',\n 'No request has been sent yet.',\n )}\n </p>\n )}\n </div>\n </details>\n\n <details\n className=\"rounded border border-border bg-background\"\n data-ai-chat-debug-section=\"lastResponse\"\n >\n <summary className=\"cursor-pointer px-2 py-1 font-semibold\">\n {t('ai_assistant.chat.debug.lastResponseSection', 'Last response')}\n </summary>\n <div className=\"px-2 pb-2\">\n {lastResponseDebug ? (\n <pre\n className=\"max-h-40 overflow-auto whitespace-pre-wrap font-mono\"\n data-ai-chat-debug-last-response\n >\n {JSON.stringify(\n { status: lastResponseDebug.status, text: lastResponseDebug.text, errorCode },\n null,\n 2,\n )}\n </pre>\n ) : (\n <p className=\"text-muted-foreground\">\n {t(\n 'ai_assistant.chat.debug.lastResponseEmpty',\n 'No response received yet.',\n )}\n </p>\n )}\n </div>\n </details>\n\n <div className=\"text-muted-foreground\" data-ai-chat-debug-status={status}>\n {t('ai_assistant.chat.debug.statusLabel', 'Status:')}{' '}\n <span className=\"font-mono\">{status}</span>\n </div>\n </div>\n )\n}\n\nexport default AiChat\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport {\n Bot,\n Check,\n ChevronDown,\n ChevronRight,\n Copy,\n Lightbulb,\n Loader2,\n Paperclip,\n Plus,\n Send,\n Square,\n User,\n Wrench,\n X,\n} from 'lucide-react'\nimport ReactMarkdown from 'react-markdown'\nimport remarkGfm from 'remark-gfm'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { Alert, AlertDescription, AlertTitle } from '../primitives/alert'\nimport { Button } from '../primitives/button'\nimport { IconButton } from '../primitives/icon-button'\nimport { Label } from '../primitives/label'\nimport { Textarea } from '../primitives/textarea'\nimport { apiCall } from '../backend/utils/apiCall'\nimport { parseAiContentSegments } from './AiMessageContent'\nimport {\n ModelPicker,\n type ModelPickerProvider,\n type ModelPickerValue,\n} from './ModelPicker'\nimport { RecordCard } from './records/RecordCard'\nimport {\n defaultAiUiPartRegistry,\n isReservedAiUiPartId,\n type AiUiPartComponentId,\n type AiUiPartRegistry,\n} from './ui-part-registry'\nimport {\n useAiChat,\n type AiChatMessage,\n type AiChatMessageFile,\n type AiChatMessageUiPart,\n type AiChatToolCallSnapshot,\n} from './useAiChat'\nimport { useAiChatUpload } from './useAiChatUpload'\nimport { useAiShortcuts } from './useAiShortcuts'\n\n// Cap inline previews so we do not blow past localStorage quota (~5MB on most\n// browsers). Images larger than this still upload + send to the LLM as inline\n// base64 server-side; only the in-chat preview is dropped on reload.\nconst PREVIEW_DATA_URL_MAX_BYTES = 2 * 1024 * 1024\nconst COMPACT_FOOTER_MAX_WIDTH = 640\n\nconst MODEL_PICKER_STORAGE_PREFIX = 'om-ai-model-picker:'\n\nfunction readModelPickerValue(agentId: string): ModelPickerValue | null {\n if (typeof window === 'undefined') return null\n try {\n const raw = window.localStorage.getItem(`${MODEL_PICKER_STORAGE_PREFIX}${agentId}`)\n if (!raw) return null\n const parsed = JSON.parse(raw) as unknown\n if (\n parsed &&\n typeof parsed === 'object' &&\n typeof (parsed as Record<string, unknown>).providerId === 'string' &&\n typeof (parsed as Record<string, unknown>).modelId === 'string'\n ) {\n const value = parsed as ModelPickerValue\n return { providerId: value.providerId, modelId: value.modelId }\n }\n return null\n } catch {\n return null\n }\n}\n\nfunction writeModelPickerValue(agentId: string, value: ModelPickerValue | null): void {\n if (typeof window === 'undefined') return\n try {\n const key = `${MODEL_PICKER_STORAGE_PREFIX}${agentId}`\n if (value === null) {\n window.localStorage.removeItem(key)\n } else {\n window.localStorage.setItem(key, JSON.stringify({ providerId: value.providerId, modelId: value.modelId }))\n }\n } catch {\n // Quota exceeded / privacy mode \u2014 silently ignore.\n }\n}\n\ninterface ModelsApiResponse {\n agentId: string\n allowRuntimeModelOverride: boolean\n defaultProviderId: string | null\n defaultModelId: string | null\n defaultProviderName?: string | null\n defaultModelName?: string | null\n providers: ModelPickerProvider[]\n}\n\nfunction useAgentModels(agent: string): {\n providers: ModelPickerProvider[]\n allowRuntimeModelOverride: boolean\n defaultLabel: string | null\n loaded: boolean\n} {\n const [providers, setProviders] = React.useState<ModelPickerProvider[]>([])\n const [allowRuntimeModelOverride, setAllowRuntimeModelOverride] = React.useState(false)\n const [defaultLabel, setDefaultLabel] = React.useState<string | null>(null)\n const [loaded, setLoaded] = React.useState(false)\n\n React.useEffect(() => {\n const modelsUrl = `/api/ai_assistant/ai/agents/${encodeURIComponent(agent)}/models`\n setLoaded(false)\n setDefaultLabel(null)\n void apiCall<ModelsApiResponse>(modelsUrl).then((result) => {\n if (!result.ok || !result.result) {\n setLoaded(true)\n return\n }\n setAllowRuntimeModelOverride(result.result.allowRuntimeModelOverride)\n setProviders(result.result.providers)\n setDefaultLabel(\n result.result.defaultProviderName && result.result.defaultModelName\n ? `${result.result.defaultProviderName} / ${result.result.defaultModelName}`\n : result.result.defaultProviderId && result.result.defaultModelId\n ? `${result.result.defaultProviderId} / ${result.result.defaultModelId}`\n : null,\n )\n setLoaded(true)\n })\n }, [agent])\n\n return { providers, allowRuntimeModelOverride, defaultLabel, loaded }\n}\n\nfunction firstAvailableModelPickerValue(\n providers: ModelPickerProvider[],\n): ModelPickerValue | null {\n for (const provider of providers) {\n const model = provider.models[0]\n if (model) {\n return { providerId: provider.id, modelId: model.id }\n }\n }\n return null\n}\n\nfunction isModelPickerValueAvailable(\n value: ModelPickerValue,\n providers: ModelPickerProvider[],\n): boolean {\n return providers.some(\n (provider) =>\n provider.id === value.providerId &&\n provider.models.some((model) => model.id === value.modelId),\n )\n}\n\nasync function readFileAsDataUrl(file: File): Promise<string | undefined> {\n if (!file.type.startsWith('image/')) return undefined\n if (file.size > PREVIEW_DATA_URL_MAX_BYTES) return undefined\n return new Promise<string | undefined>((resolve) => {\n const reader = new FileReader()\n reader.onload = () =>\n resolve(typeof reader.result === 'string' ? reader.result : undefined)\n reader.onerror = () => resolve(undefined)\n try {\n reader.readAsDataURL(file)\n } catch {\n resolve(undefined)\n }\n })\n}\n\n/**\n * Optional resolved-tool snapshot the host can feed into the debug panel.\n * Step 4.6 wires this from the `GET /api/ai_assistant/ai/agents` response\n * (`tools[]`). Step 5.3+ will replace the manual wiring with a streamed\n * `debug` part once the dispatcher emits one.\n */\nexport interface AiChatDebugTool {\n name: string\n displayName?: string\n isMutation?: boolean\n requiredFeatures?: string[]\n}\n\n/**\n * Resolved prompt-section snapshot for the debug panel. Until Phase 3 Step\n * 5.3 lands structured `PromptTemplate.sections`, hosts synthesise this\n * from the agent's `systemPrompt` + additive overrides.\n */\nexport interface AiChatDebugPromptSection {\n id: string\n source?: 'default' | 'override' | 'placeholder'\n text?: string\n}\n\n/** Quick-action suggestion shown in the welcome state. */\nexport interface AiChatSuggestion {\n label: string\n prompt: string\n icon?: React.ReactNode\n}\n\n/** Context item displayed as a chip/pill in the chat header. */\nexport interface AiChatContextItem {\n label: string\n detail?: string\n}\n\nexport interface AiChatProps {\n agent: string\n apiPath?: string\n pageContext?: Record<string, unknown>\n attachmentIds?: string[]\n initialMessages?: Array<{ role: 'user' | 'assistant'; content: string }>\n debug?: boolean\n className?: string\n placeholder?: string\n onMutationRequested?: (pendingActionId: string) => void\n onError?: (err: { code?: string; message: string }) => void\n /**\n * Optional stable conversation id. Forwarded verbatim to\n * `POST /api/ai_assistant/ai/chat` request bodies and reused across\n * turns so the Step 5.6 `prepareMutation` idempotency hash stays\n * stable across retries within the same chat. When omitted, the hook\n * mints a fresh random id once on mount \u2014 remounting the component\n * resets the conversation.\n */\n conversationId?: string\n /**\n * Optional UI-part registry. Defaults to the module-global\n * {@link defaultAiUiPartRegistry}. Pass a scoped registry from\n * {@link createAiUiPartRegistry} when embedding multiple `<AiChat>`\n * instances that should not share registrations (playground, tests).\n */\n registry?: AiUiPartRegistry\n /**\n * Optional list of server-emitted UI parts to render inside the chat\n * transcript. The registry resolves each part via `componentId`. Phase 3\n * will populate this from the streamed dispatcher response; Phase 2 WS-A\n * leaves the wiring exposed so hosts can preview the registry path.\n */\n uiParts?: Array<{\n componentId: AiUiPartComponentId\n payload?: unknown\n pendingActionId?: string\n }>\n /**\n * Optional resolved-tool map for the debug panel. Ignored when\n * `debug` is falsy.\n */\n debugTools?: AiChatDebugTool[]\n /**\n * Optional resolved prompt sections for the debug panel. Ignored when\n * `debug` is falsy.\n */\n debugPromptSections?: AiChatDebugPromptSection[]\n /** Suggested prompts shown in the empty / welcome state. */\n suggestions?: AiChatSuggestion[]\n /** Context items shown as pills above the transcript (e.g. selected products). */\n contextItems?: AiChatContextItem[]\n /** Welcome heading shown when there are no messages yet. */\n welcomeTitle?: string\n /** Welcome description shown below the heading. */\n welcomeDescription?: string\n /** Initial compact composer state used before the footer has been measured. */\n defaultCompactFooter?: boolean\n}\n\ninterface ServerEmittedUiPartRef {\n componentId: AiUiPartComponentId\n payload?: unknown\n pendingActionId?: string\n}\n\nfunction mapErrorCodeToVariant(\n code: string | undefined,\n): 'destructive' | 'warning' {\n if (!code) return 'destructive'\n // Policy denies that describe a filtered tool or attachment surface a\n // warning alert; caller can still continue. Hard denials (agent_unknown,\n // agent_features_denied, unauthenticated, execution_mode_not_supported,\n // mutation_blocked_by_*, validation_error) surface destructive alerts.\n const warningCodes = new Set<string>([\n 'tool_not_whitelisted',\n 'tool_features_denied',\n 'attachment_type_not_accepted',\n ])\n return warningCodes.has(code) ? 'warning' : 'destructive'\n}\n\nconst MARKDOWN_TYPOGRAPHY_CLASS = cn(\n '[&_p]:my-1 [&_p:first-child]:mt-0 [&_p:last-child]:mb-0',\n '[&_ul]:my-2 [&_ol]:my-2 [&_ul]:ml-4 [&_ol]:ml-4 [&_ul]:list-disc [&_ol]:list-decimal',\n '[&_li]:my-0.5',\n '[&_h1]:mt-3 [&_h1]:mb-2 [&_h1]:text-base [&_h1]:font-semibold',\n '[&_h2]:mt-3 [&_h2]:mb-2 [&_h2]:text-sm [&_h2]:font-semibold',\n '[&_h3]:mt-2 [&_h3]:mb-1 [&_h3]:text-sm [&_h3]:font-semibold',\n '[&_a]:text-primary [&_a]:underline [&_a]:underline-offset-2',\n '[&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-xs',\n '[&_pre]:my-2 [&_pre]:overflow-x-auto [&_pre]:rounded-md [&_pre]:border [&_pre]:border-border [&_pre]:bg-muted [&_pre]:p-3',\n '[&_pre_code]:bg-transparent [&_pre_code]:p-0',\n '[&_blockquote]:my-2 [&_blockquote]:border-l-2 [&_blockquote]:border-border [&_blockquote]:pl-3 [&_blockquote]:text-muted-foreground',\n '[&_table]:my-2 [&_table]:w-full [&_table]:border-collapse [&_table]:text-xs',\n '[&_th]:border [&_th]:border-border [&_th]:px-2 [&_th]:py-1 [&_th]:text-left [&_th]:font-medium',\n '[&_td]:border [&_td]:border-border [&_td]:px-2 [&_td]:py-1',\n)\n\nconst MARKDOWN_COMPONENTS = {\n a: ({ node, ...props }: React.AnchorHTMLAttributes<HTMLAnchorElement> & { node?: unknown }) => (\n <a {...props} target=\"_blank\" rel=\"noreferrer\" />\n ),\n}\n\nfunction MarkdownChunk({ text }: { text: string }) {\n if (!text.trim()) return null\n return (\n <div className={cn('text-sm', MARKDOWN_TYPOGRAPHY_CLASS)}>\n <ReactMarkdown remarkPlugins={[remarkGfm]} components={MARKDOWN_COMPONENTS}>\n {text}\n </ReactMarkdown>\n </div>\n )\n}\n\nfunction MessageContent({ content, isAssistant }: { content: string; isAssistant: boolean }) {\n if (!isAssistant) {\n return <div className=\"whitespace-pre-wrap text-sm\">{content}</div>\n }\n if (!content) {\n return null\n }\n const segments = parseAiContentSegments(content)\n if (segments.length === 0) {\n return null\n }\n return (\n <div className=\"space-y-1\" data-ai-message-content=\"\">\n {segments.map((segment, index) => {\n if (segment.kind === 'record-card') {\n return <RecordCard key={`card-${index}`} data={segment.payload} />\n }\n if (segment.kind === 'invalid-card') {\n return (\n <pre\n key={`raw-${index}`}\n className=\"my-2 max-h-60 overflow-auto rounded-md border border-dashed border-border bg-muted p-2 text-xs\"\n data-ai-record-card-invalid={segment.info}\n >\n {segment.raw}\n </pre>\n )\n }\n return <MarkdownChunk key={`md-${index}`} text={segment.text} />\n })}\n </div>\n )\n}\n\nfunction safeStringify(value: unknown): string {\n if (value === null || value === undefined) return ''\n if (typeof value === 'string') return value\n try {\n return JSON.stringify(value, null, 2)\n } catch {\n return String(value)\n }\n}\n\nfunction ToolCallList({ toolCalls }: { toolCalls: AiChatToolCallSnapshot[] }) {\n const t = useT()\n const [openId, setOpenId] = React.useState<string | null>(null)\n if (!toolCalls || toolCalls.length === 0) return null\n return (\n <div className=\"space-y-1\" data-ai-chat-tool-calls=\"\">\n {toolCalls.map((call) => {\n const isOpen = openId === call.id\n const isError = call.state === 'error'\n const isPending = call.state === 'pending'\n const isComplete = call.state === 'complete'\n const statusLabel = isError\n ? t('ai_assistant.chat.toolError', 'failed')\n : isPending\n ? t('ai_assistant.chat.toolRunning', 'running\u2026')\n : t('ai_assistant.chat.toolDone', 'done')\n return (\n <div\n key={call.id}\n className={cn(\n 'rounded-md border border-border bg-muted/30',\n isError ? 'border-destructive/40 bg-destructive/5' : '',\n )}\n data-ai-chat-tool-call={call.toolName}\n data-ai-chat-tool-state={call.state}\n >\n <button\n type=\"button\"\n onClick={() => setOpenId(isOpen ? null : call.id)}\n className=\"flex w-full items-center gap-2 px-2 py-1.5 text-left text-xs font-medium hover:bg-muted/60\"\n >\n {isOpen ? (\n <ChevronDown className=\"size-3.5 text-muted-foreground\" aria-hidden />\n ) : (\n <ChevronRight className=\"size-3.5 text-muted-foreground\" aria-hidden />\n )}\n {isPending ? (\n <Loader2 className=\"size-3.5 animate-spin text-muted-foreground\" aria-hidden />\n ) : (\n <Wrench\n className={cn(\n 'size-3.5',\n isError ? 'text-destructive' : 'text-muted-foreground',\n )}\n aria-hidden\n />\n )}\n <span className=\"font-mono\">{call.toolName}</span>\n <span\n className={cn(\n 'ml-auto text-[10px] uppercase tracking-wide',\n isError\n ? 'text-destructive'\n : isComplete\n ? 'text-status-success-text'\n : 'text-muted-foreground',\n )}\n >\n {statusLabel}\n </span>\n </button>\n {isOpen ? (\n <div className=\"space-y-1 border-t border-border/60 px-2 py-1.5 text-xs\">\n {call.input !== undefined ? (\n <div>\n <div className=\"text-[10px] uppercase tracking-wide text-muted-foreground\">\n {t('ai_assistant.chat.toolInput', 'Input')}\n </div>\n <pre className=\"mt-0.5 max-h-32 overflow-auto rounded bg-background p-1.5 font-mono text-[11px]\">\n {safeStringify(call.input)}\n </pre>\n </div>\n ) : null}\n {call.output !== undefined && !isError ? (\n <div>\n <div className=\"text-[10px] uppercase tracking-wide text-muted-foreground\">\n {t('ai_assistant.chat.toolOutput', 'Output')}\n </div>\n <pre className=\"mt-0.5 max-h-32 overflow-auto rounded bg-background p-1.5 font-mono text-[11px]\">\n {safeStringify(call.output)}\n </pre>\n </div>\n ) : null}\n {call.errorMessage ? (\n <div className=\"text-destructive\">{call.errorMessage}</div>\n ) : null}\n </div>\n ) : null}\n </div>\n )\n })}\n </div>\n )\n}\n\nfunction ReasoningPanel({ text, streaming }: { text: string; streaming: boolean }) {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n if (!text) return null\n return (\n <div\n className=\"rounded-md border border-border bg-muted/30\"\n data-ai-chat-reasoning={streaming ? 'streaming' : 'complete'}\n >\n <button\n type=\"button\"\n onClick={() => setOpen((o) => !o)}\n className=\"flex w-full items-center gap-2 px-2 py-1.5 text-left text-xs font-medium hover:bg-muted/60\"\n >\n {open ? (\n <ChevronDown className=\"size-3.5 text-muted-foreground\" aria-hidden />\n ) : (\n <ChevronRight className=\"size-3.5 text-muted-foreground\" aria-hidden />\n )}\n <Lightbulb className=\"size-3.5 text-muted-foreground\" aria-hidden />\n <span>{t('ai_assistant.chat.reasoning', 'Reasoning')}</span>\n {streaming ? (\n <Loader2\n className=\"ml-1 size-3 animate-spin text-muted-foreground\"\n aria-hidden\n />\n ) : null}\n </button>\n {open ? (\n <pre className=\"max-h-48 overflow-auto whitespace-pre-wrap border-t border-border/60 px-2 py-1.5 text-xs text-muted-foreground\">\n {text}\n </pre>\n ) : null}\n </div>\n )\n}\n\nfunction MessageRow({\n message,\n registry,\n onMutationRequested,\n}: {\n message: AiChatMessage\n registry?: AiUiPartRegistry\n onMutationRequested?: (pendingActionId: string) => void\n}) {\n const t = useT()\n const isAssistant = message.role === 'assistant'\n const label = isAssistant\n ? t('ai_assistant.chat.assistantRoleLabel', 'Assistant')\n : t('ai_assistant.chat.userRoleLabel', 'You')\n const Icon = isAssistant ? Bot : User\n const [copied, setCopied] = React.useState(false)\n const copyTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null)\n\n React.useEffect(() => {\n return () => {\n if (copyTimerRef.current) clearTimeout(copyTimerRef.current)\n }\n }, [])\n\n const handleCopy = React.useCallback(async () => {\n const text = message.content\n if (!text) return\n try {\n if (typeof navigator !== 'undefined' && navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(text)\n } else {\n const textarea = document.createElement('textarea')\n textarea.value = text\n textarea.setAttribute('readonly', '')\n textarea.style.position = 'absolute'\n textarea.style.left = '-9999px'\n document.body.appendChild(textarea)\n textarea.select()\n document.execCommand('copy')\n document.body.removeChild(textarea)\n }\n setCopied(true)\n if (copyTimerRef.current) clearTimeout(copyTimerRef.current)\n copyTimerRef.current = setTimeout(() => setCopied(false), 1500)\n } catch {\n // Clipboard API blocked (no permission, http context) \u2014 silently fail.\n }\n }, [message.content])\n\n return (\n <div\n className={cn(\n 'group/message flex gap-3 px-3 py-2',\n isAssistant ? 'bg-muted/40 rounded-md' : '',\n )}\n data-role={message.role}\n >\n <div\n className={cn(\n 'flex size-6 shrink-0 items-center justify-center rounded-full',\n isAssistant\n ? 'bg-primary/10 text-primary'\n : 'bg-secondary text-secondary-foreground',\n )}\n aria-hidden\n >\n <Icon className=\"size-4\" />\n </div>\n <div className=\"flex-1 space-y-1\">\n <div className=\"flex items-center justify-between gap-2\">\n <div className=\"text-xs font-medium text-muted-foreground\">{label}</div>\n {message.content ? (\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={handleCopy}\n aria-label={\n copied\n ? t('ai_assistant.chat.copied', 'Copied')\n : t('ai_assistant.chat.copyMessage', 'Copy message')\n }\n data-ai-chat-copy-button=\"\"\n className=\"opacity-0 transition-opacity group-hover/message:opacity-100 focus-visible:opacity-100\"\n >\n {copied ? (\n <Check className=\"size-3.5 text-status-success-icon\" aria-hidden />\n ) : (\n <Copy className=\"size-3.5\" aria-hidden />\n )}\n </IconButton>\n ) : null}\n </div>\n {message.files && message.files.length > 0 ? (\n <div className=\"flex flex-wrap gap-2 py-1\">\n {message.files.map((file, i) =>\n file.previewUrl ? (\n <img\n key={i}\n src={file.previewUrl}\n alt={file.name}\n className=\"max-h-32 max-w-[200px] rounded-md border border-border object-cover\"\n />\n ) : (\n <span\n key={i}\n className=\"inline-flex items-center gap-1 rounded-full border border-border bg-muted px-2 py-0.5 text-xs\"\n >\n <Paperclip className=\"size-3\" aria-hidden />\n {file.name}\n </span>\n ),\n )}\n </div>\n ) : null}\n {isAssistant && message.reasoning ? (\n <ReasoningPanel\n text={message.reasoning}\n streaming={message.reasoningStreaming === true}\n />\n ) : null}\n {isAssistant && message.toolCalls && message.toolCalls.length > 0 ? (\n <ToolCallList toolCalls={message.toolCalls} />\n ) : null}\n <MessageContent content={message.content} isAssistant={isAssistant} />\n {isAssistant && registry && message.uiParts && message.uiParts.length > 0 ? (\n <MessageUiParts\n parts={message.uiParts}\n registry={registry}\n onMutationRequested={onMutationRequested}\n />\n ) : null}\n </div>\n </div>\n )\n}\n\nfunction MessageUiParts({\n parts,\n registry,\n onMutationRequested,\n}: {\n parts: AiChatMessageUiPart[]\n registry: AiUiPartRegistry\n onMutationRequested?: (pendingActionId: string) => void\n}) {\n return (\n <div className=\"mt-2 flex flex-col gap-2\" data-ai-message-ui-parts=\"\">\n {parts.map((part) => (\n <AiUiPartRenderer\n key={part.key}\n part={{\n componentId: part.componentId as AiUiPartComponentId,\n payload: part.payload,\n pendingActionId: part.pendingActionId,\n }}\n registry={registry}\n onMutationRequested={onMutationRequested}\n />\n ))}\n </div>\n )\n}\n\nfunction UnknownUiPartPlaceholder({ componentId }: { componentId: AiUiPartComponentId }) {\n const t = useT()\n return (\n <div\n className=\"inline-flex items-center gap-2 rounded-full border border-dashed border-border bg-muted px-3 py-1 text-xs text-muted-foreground\"\n data-ai-ui-part-placeholder={componentId}\n >\n <span>\n {t('ai_assistant.chat.uiPartPending', 'Pending UI part:')} {componentId}\n </span>\n </div>\n )\n}\n\nfunction AiUiPartRenderer({\n part,\n registry,\n onMutationRequested,\n}: {\n part: ServerEmittedUiPartRef\n registry: AiUiPartRegistry\n onMutationRequested?: (pendingActionId: string) => void\n}) {\n const Component = registry.resolve(part.componentId)\n const isReserved = isReservedAiUiPartId(part.componentId)\n React.useEffect(() => {\n if (Component) return\n if (isReserved) return\n try {\n // eslint-disable-next-line no-console\n console.warn(\n `[AiChat] No component registered for UI part \"${part.componentId}\".`,\n )\n } catch {\n // noop\n }\n }, [Component, part.componentId, isReserved])\n React.useEffect(() => {\n if (part.pendingActionId) {\n onMutationRequested?.(part.pendingActionId)\n }\n }, [part.pendingActionId, onMutationRequested])\n if (!Component) {\n return <UnknownUiPartPlaceholder componentId={part.componentId} />\n }\n return (\n <Component\n componentId={part.componentId}\n payload={part.payload}\n pendingActionId={part.pendingActionId}\n />\n )\n}\n\nfunction WelcomeState({\n title,\n description,\n suggestions,\n onSuggestionClick,\n}: {\n title?: string\n description?: string\n suggestions?: AiChatSuggestion[]\n onSuggestionClick: (prompt: string) => void\n}) {\n const t = useT()\n const heading = title ?? t('ai_assistant.chat.welcomeTitle', 'How can I help?')\n const desc =\n description ??\n t(\n 'ai_assistant.chat.welcomeDescription',\n 'Ask me anything about your data. Here are some things I can do:',\n )\n return (\n <div className=\"flex flex-1 flex-col items-center justify-center gap-4 px-4 py-8\">\n <div\n className=\"flex size-12 items-center justify-center rounded-full bg-primary/10 text-primary\"\n aria-hidden\n >\n <Bot className=\"size-6\" />\n </div>\n <div className=\"space-y-1 text-center\">\n <h3 className=\"text-sm font-semibold\">{heading}</h3>\n <p className=\"text-xs text-muted-foreground\">{desc}</p>\n </div>\n {suggestions && suggestions.length > 0 ? (\n <div className=\"flex w-full max-w-md flex-col gap-2\" data-ai-chat-suggestions=\"\">\n {suggestions.map((suggestion, index) => (\n <button\n key={index}\n type=\"button\"\n className=\"flex items-center gap-2 rounded-lg border border-border bg-card px-3 py-2.5 text-left text-sm transition-colors hover:bg-accent hover:text-accent-foreground\"\n onClick={() => onSuggestionClick(suggestion.prompt)}\n data-ai-chat-suggestion={index}\n >\n {suggestion.icon ? (\n <span className=\"shrink-0 text-muted-foreground\" aria-hidden>\n {suggestion.icon}\n </span>\n ) : null}\n <span>{suggestion.label}</span>\n </button>\n ))}\n </div>\n ) : null}\n </div>\n )\n}\n\nfunction ContextItemsPill({ items }: { items: AiChatContextItem[] }) {\n if (items.length === 0) return null\n return (\n <div\n className=\"flex flex-wrap gap-1.5 border-b border-border px-3 py-2\"\n data-ai-chat-context-items=\"\"\n >\n {items.map((item, index) => (\n <span\n key={index}\n className=\"inline-flex items-center gap-1 rounded-full border border-border bg-secondary px-2 py-0.5 text-xs text-secondary-foreground\"\n data-ai-chat-context-item={index}\n title={item.detail}\n >\n {item.label}\n </span>\n ))}\n </div>\n )\n}\n\n/**\n * Embeddable AI chat component. Binds to the dispatcher route\n * `POST /api/ai_assistant/ai/chat?agent=<module>.<agent>` via\n * {@link createAiAgentTransport}. Phase 2 WS-A deliverable (Step 4.1).\n *\n * - Keyboard: `Enter` submits; `Shift+Enter` inserts a newline; `Escape`\n * aborts streaming (or blurs the composer when idle).\n * - Error envelopes from the dispatcher surface as `Alert` + `onError`.\n * - UI parts render via the client-side registry; unknown parts render a\n * neutral placeholder chip so mutation-card slots reserved for Phase 3\n * never throw before their implementations land.\n */\nexport function AiChat({\n agent,\n apiPath,\n pageContext,\n attachmentIds,\n initialMessages,\n debug,\n className,\n placeholder,\n onMutationRequested,\n onError,\n registry,\n uiParts: uiPartsProp,\n debugTools,\n debugPromptSections,\n conversationId,\n suggestions,\n contextItems,\n welcomeTitle,\n welcomeDescription,\n defaultCompactFooter = false,\n}: AiChatProps) {\n const t = useT()\n const textareaRef = React.useRef<HTMLTextAreaElement | null>(null)\n const transcriptRef = React.useRef<HTMLDivElement | null>(null)\n const fileInputRef = React.useRef<HTMLInputElement | null>(null)\n const [input, setInput] = React.useState('')\n type PendingAttachment = {\n file: File\n attachmentId?: string\n /**\n * Base64 data URL of the image (capped by PREVIEW_DATA_URL_MAX_BYTES).\n * Stored on the message so the preview survives a reload \u2014 durable\n * server URLs were intentionally avoided because the LLM provider can\n * never reach a localhost dev URL anyway, and HTTP fetches add latency\n * the chat doesn't need.\n */\n previewDataUrl?: string\n error?: string\n }\n const [pendingFiles, setPendingFiles] = React.useState<PendingAttachment[]>([])\n const upload = useAiChatUpload()\n const isUploading = upload.busy\n const footerRef = React.useRef<HTMLDivElement | null>(null)\n const [isCompactFooter, setIsCompactFooter] = React.useState(defaultCompactFooter)\n\n React.useEffect(() => {\n const element = footerRef.current\n if (!element || typeof ResizeObserver === 'undefined') return\n\n const updateCompactState = (width: number) => {\n setIsCompactFooter(width < COMPACT_FOOTER_MAX_WIDTH)\n }\n\n updateCompactState(element.getBoundingClientRect().width)\n const observer = new ResizeObserver((entries) => {\n const entry = entries[0]\n if (!entry) return\n updateCompactState(entry.contentRect.width)\n })\n observer.observe(element)\n return () => observer.disconnect()\n }, [])\n\n const uploadedAttachmentIds = React.useMemo(\n () =>\n pendingFiles\n .map((entry) => entry.attachmentId)\n .filter((id): id is string => typeof id === 'string' && id.length > 0),\n [pendingFiles],\n )\n\n const allAttachmentIds = React.useMemo(\n () => [...(attachmentIds ?? []), ...uploadedAttachmentIds],\n [attachmentIds, uploadedAttachmentIds],\n )\n\n const {\n providers: modelProviders,\n allowRuntimeModelOverride,\n defaultLabel: modelDefaultLabel,\n loaded: modelProvidersLoaded,\n } = useAgentModels(agent)\n\n const [modelPickerValue, setModelPickerValue] = React.useState<ModelPickerValue | null>(() =>\n readModelPickerValue(agent),\n )\n\n const effectiveModelPickerValue = React.useMemo(() => {\n if (!modelProvidersLoaded || !allowRuntimeModelOverride || modelProviders.length === 0) {\n return null\n }\n if (modelPickerValue && isModelPickerValueAvailable(modelPickerValue, modelProviders)) {\n return modelPickerValue\n }\n return firstAvailableModelPickerValue(modelProviders)\n }, [\n allowRuntimeModelOverride,\n modelPickerValue,\n modelProviders,\n modelProvidersLoaded,\n ])\n\n React.useEffect(() => {\n setModelPickerValue(readModelPickerValue(agent))\n }, [agent])\n\n React.useEffect(() => {\n if (!modelProvidersLoaded) return\n if (!allowRuntimeModelOverride || modelProviders.length === 0) {\n if (modelPickerValue !== null) {\n setModelPickerValue(null)\n writeModelPickerValue(agent, null)\n }\n return\n }\n if (modelPickerValue && !isModelPickerValueAvailable(modelPickerValue, modelProviders)) {\n const fallback = firstAvailableModelPickerValue(modelProviders)\n setModelPickerValue(fallback)\n writeModelPickerValue(agent, fallback)\n }\n }, [\n agent,\n allowRuntimeModelOverride,\n modelPickerValue,\n modelProviders,\n modelProvidersLoaded,\n ])\n\n const handleModelPickerChange = React.useCallback(\n (value: ModelPickerValue | null) => {\n setModelPickerValue(value)\n writeModelPickerValue(agent, value)\n },\n [agent],\n )\n\n const chat = useAiChat({\n agent,\n apiPath,\n pageContext,\n attachmentIds: allAttachmentIds.length > 0 ? allAttachmentIds : undefined,\n debug,\n initialMessages,\n onError,\n conversationId,\n providerOverride: effectiveModelPickerValue?.providerId ?? null,\n modelOverride: effectiveModelPickerValue?.modelId ?? null,\n })\n\n const isStreaming = chat.status === 'streaming'\n const isSubmitting = chat.status === 'submitting'\n const isBusy = isStreaming || isSubmitting\n\n // Surface a \"Thinking...\" placeholder so the chat does not look frozen.\n // Visible whenever ANY of the following is true while a turn is in flight:\n // (a) we're still in the submit phase before the first stream chunk\n // (b) streaming, but no content / reasoning / tool calls have arrived yet\n // (c) streaming, and at least one tool call is still in `pending` state\n // (the model is waiting on a tool result \u2014 the previous version\n // treated `toolCalls.length > 0` as \"has content\" and hid the\n // indicator the moment the first tool started, even though the\n // model had not produced any user-visible output yet)\n // (d) streaming, and the last visible event was a finished tool call\n // \u2014 the model is reasoning about the result before emitting more\n // text or kicking off the next tool\n // (e) streaming, but no delta has landed in the last ~300 ms (idle gap)\n const lastAssistant = React.useMemo(() => {\n for (let index = chat.messages.length - 1; index >= 0; index -= 1) {\n const candidate = chat.messages[index]\n if (candidate?.role === 'assistant') return candidate\n }\n return null\n }, [chat.messages])\n\n const trimmedContent = lastAssistant?.content?.trim() ?? ''\n const hasReasoning = !!(lastAssistant?.reasoning && lastAssistant.reasoning.length > 0)\n const toolCalls = lastAssistant?.toolCalls ?? []\n const hasPendingToolCall = toolCalls.some((call) => call.state === 'pending')\n const hasCompletedToolCall = toolCalls.some(\n (call) => call.state === 'complete' || call.state === 'error',\n )\n const hasAnyVisibleSignal = !!(\n trimmedContent || hasReasoning || toolCalls.length > 0\n )\n\n const assistantStreamSnapshot = React.useMemo(() => {\n if (!lastAssistant) return ''\n const toolSig = toolCalls\n .map((call) => `${call.id}:${call.state}:${call.output != null ? 1 : 0}`)\n .join('|')\n return [\n lastAssistant.id,\n lastAssistant.content?.length ?? 0,\n lastAssistant.reasoning?.length ?? 0,\n lastAssistant.reasoningStreaming ? 1 : 0,\n toolSig,\n ].join('#')\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [lastAssistant])\n\n const lastStreamUpdateRef = React.useRef<number>(Date.now())\n const lastSnapshotRef = React.useRef<string>('')\n const [, setStreamTick] = React.useState(0)\n\n React.useEffect(() => {\n if (assistantStreamSnapshot !== lastSnapshotRef.current) {\n lastSnapshotRef.current = assistantStreamSnapshot\n lastStreamUpdateRef.current = Date.now()\n setStreamTick((value) => value + 1)\n }\n }, [assistantStreamSnapshot])\n\n React.useEffect(() => {\n if (!isStreaming && !isSubmitting) return\n const interval = window.setInterval(() => {\n setStreamTick((value) => value + 1)\n }, 200)\n return () => window.clearInterval(interval)\n }, [isStreaming, isSubmitting])\n\n const idleDuringStream =\n isStreaming && Date.now() - lastStreamUpdateRef.current >= 300\n\n const showThinkingIndicator =\n isSubmitting ||\n (isStreaming &&\n (\n !hasAnyVisibleSignal ||\n hasPendingToolCall ||\n // Tool just returned and the model hasn't started speaking yet.\n (hasCompletedToolCall && !trimmedContent) ||\n idleDuringStream\n ))\n\n const activeRegistry = registry ?? defaultAiUiPartRegistry\n\n // Reserved UI parts. Phase 3 will populate this from the streamed response;\n // for Phase 2 WS-A it stays empty unless the host surfaces test/debug parts\n // via the optional `uiParts` prop so the registry resolution path can be\n // exercised without waiting for the runtime emitter.\n const uiParts: ServerEmittedUiPartRef[] = React.useMemo(\n () => (uiPartsProp ?? []).map((part) => ({\n componentId: part.componentId,\n payload: part.payload,\n pendingActionId: part.pendingActionId,\n })),\n [uiPartsProp],\n )\n\n const hasUploadingFiles = React.useMemo(\n () => pendingFiles.some((entry) => !entry.attachmentId && !entry.error),\n [pendingFiles],\n )\n\n const handleSendMessage = React.useCallback(\n (text: string) => {\n if (!text.trim() || isBusy) return\n // Block send while any attachment is still uploading. Without this guard\n // the message would ship with an empty attachmentIds list (the chip is\n // visible but the server hasn't returned an id yet), the model would\n // never see the file, and `setPendingFiles([])` below would erase the\n // chip \u2014 so the upload finishes into the void. Surface the wait via the\n // disabled Send button + composer hint instead.\n if (hasUploadingFiles || isUploading) return\n const filesToAttach = pendingFiles.map((entry): AiChatMessageFile => {\n const isImage = entry.file.type.startsWith('image/')\n const fallback = isImage ? URL.createObjectURL(entry.file) : undefined\n return {\n name: entry.file.name,\n type: entry.file.type,\n previewUrl: isImage ? (entry.previewDataUrl ?? fallback) : undefined,\n }\n })\n setInput('')\n setPendingFiles([])\n void chat.sendMessage(text, filesToAttach.length > 0 ? filesToAttach : undefined)\n },\n [chat, hasUploadingFiles, isBusy, isUploading, pendingFiles],\n )\n\n const handleSubmit = React.useCallback(() => {\n handleSendMessage(input)\n }, [handleSendMessage, input])\n\n // Listen for \"Fix with AI\" requests dispatched by the failure variant\n // of `MutationResultCard`. Any rendered failure card can fire a custom\n // DOM event with the prompt \u2014 this side-steps having to thread a\n // sendMessage callback through the UI-part registry while keeping the\n // chat the single owner of message creation. Idempotency is handled\n // server-side: `prepareMutation` only dedupes against active `pending`\n // rows, so a retry after a terminal failure always produces a fresh\n // pending action.\n React.useEffect(() => {\n if (typeof window === 'undefined') return\n const handler = (event: Event) => {\n const detail = (event as CustomEvent<{ message?: string }>).detail\n const message = detail?.message\n if (typeof message !== 'string' || message.trim().length === 0) return\n handleSendMessage(message)\n }\n window.addEventListener('om-ai-chat-fix-request', handler as EventListener)\n return () => {\n window.removeEventListener('om-ai-chat-fix-request', handler as EventListener)\n }\n }, [handleSendMessage])\n\n const cancelOrBlur = React.useCallback(() => {\n if (isBusy) {\n chat.cancel()\n return\n }\n textareaRef.current?.blur()\n }, [chat, isBusy])\n\n const handleFileSelect = React.useCallback(\n async (event: React.ChangeEvent<HTMLInputElement>) => {\n const files = Array.from(event.target.files ?? [])\n if (files.length === 0) return\n const queued: PendingAttachment[] = files.map((file) => ({ file }))\n setPendingFiles((prev) => [...prev, ...queued])\n const [previewResults, result] = await Promise.all([\n Promise.all(files.map((file) => readFileAsDataUrl(file))),\n upload.upload(files),\n ])\n // Pair upload outcomes back to chips by input INDEX, not by filename.\n // The server may sanitize the uploaded name (whitespace, unicode,\n // dangerous characters), and two files in the same batch can share a\n // name \u2014 both cases broke the previous Map-by-fileName matching and\n // left the chip stuck on the spinner forever.\n const idByIndex = new Map<number, string>()\n for (const item of result.items) {\n if (typeof item.inputIndex === 'number') idByIndex.set(item.inputIndex, item.attachmentId)\n }\n const errorByIndex = new Map<number, string>()\n for (const failure of result.failed) {\n if (typeof failure.inputIndex === 'number') errorByIndex.set(failure.inputIndex, failure.message)\n }\n setPendingFiles((prev) => {\n const next = prev.slice()\n const baseIndex = next.length - files.length\n for (let offset = 0; offset < files.length; offset += 1) {\n const index = baseIndex + offset\n if (index < 0) continue\n const entry = next[index]\n if (!entry) continue\n const dataUrl = previewResults[offset]\n const patch: PendingAttachment = {\n ...entry,\n previewDataUrl: dataUrl ?? entry.previewDataUrl,\n }\n if (!patch.attachmentId) {\n const id = idByIndex.get(offset)\n if (id) {\n patch.attachmentId = id\n patch.error = undefined\n } else {\n // Defensive fallback: if neither success nor failure carried an\n // index for this slot (older transports, partial outcome), the\n // chip would otherwise stay on the spinner. Mark it as a\n // generic error so the user can remove it and retry instead of\n // staring at a dead spinner that also blocks the Send button.\n const explicitError = errorByIndex.get(offset)\n patch.error =\n explicitError ??\n 'Upload finished without a server response. Remove the file and try again.'\n }\n }\n next[index] = patch\n }\n return next\n })\n if (fileInputRef.current) fileInputRef.current.value = ''\n },\n [upload],\n )\n\n const removePendingFile = React.useCallback((index: number) => {\n setPendingFiles((prev) => prev.filter((_, i) => i !== index))\n }, [])\n\n const { handleKeyDown } = useAiShortcuts({\n onSubmit: handleSubmit,\n onCancel: cancelOrBlur,\n })\n\n React.useEffect(() => {\n textareaRef.current?.focus()\n }, [])\n\n // Sticky-bottom autoscroll: only re-pin to the bottom when the user is\n // already there (or within a small tolerance). If they have scrolled up to\n // read an earlier part of a long response, every streaming delta would\n // otherwise yank them back to the tail and make the message look truncated.\n // Tolerance is generous enough to absorb sub-pixel rounding, but tight\n // enough that an intentional scroll-up keeps the user where they want.\n const stickToBottomRef = React.useRef(true)\n const SCROLL_STICK_TOLERANCE_PX = 64\n\n React.useEffect(() => {\n const node = transcriptRef.current\n if (!node) return\n const handleScroll = () => {\n const distanceFromBottom = node.scrollHeight - node.scrollTop - node.clientHeight\n stickToBottomRef.current = distanceFromBottom <= SCROLL_STICK_TOLERANCE_PX\n }\n node.addEventListener('scroll', handleScroll, { passive: true })\n return () => node.removeEventListener('scroll', handleScroll)\n }, [])\n\n React.useEffect(() => {\n const node = transcriptRef.current\n if (!node) return\n if (!stickToBottomRef.current) return\n node.scrollTop = node.scrollHeight\n }, [chat.messages])\n\n // Mark the body so floating UI surfaces (e.g. the demo feedback FAB) can\n // hide themselves while the chat is open and would otherwise overlay the\n // composer's send button. CSS lives in `apps/mercato/src/app/globals.css`\n // alongside the column-chooser precedent.\n React.useEffect(() => {\n if (typeof document === 'undefined') return\n const previous = document.body.getAttribute('data-ai-chat-open')\n document.body.setAttribute('data-ai-chat-open', 'true')\n return () => {\n if (previous === null) {\n document.body.removeAttribute('data-ai-chat-open')\n } else {\n document.body.setAttribute('data-ai-chat-open', previous)\n }\n }\n }, [])\n\n const handleNewConversation = React.useCallback(() => {\n chat.reset()\n setInput('')\n setPendingFiles([])\n upload.reset()\n setTimeout(() => textareaRef.current?.focus(), 0)\n }, [chat, upload])\n\n const resolvedPlaceholder =\n placeholder ?? t('ai_assistant.chat.composerPlaceholder', 'Message the AI agent...')\n\n const errorVariant = mapErrorCodeToVariant(chat.error?.code)\n\n return (\n <section\n className={cn(\n 'flex h-full min-h-[320px] min-w-0 flex-col gap-3 overflow-hidden rounded-lg border border-border bg-background p-3',\n className,\n )}\n aria-label={t('ai_assistant.chat.regionLabel', 'AI chat')}\n data-ai-chat-agent={agent}\n data-ai-chat-conversation-id={chat.conversationId}\n >\n <div className=\"flex min-w-0 items-center justify-between gap-2 border-b border-border pb-2\">\n <div className=\"flex min-w-0 flex-1 items-center gap-2 text-xs text-muted-foreground\">\n {contextItems && contextItems.length > 0 ? (\n <ContextItemsPill items={contextItems} />\n ) : (\n <span className=\"font-mono opacity-70\" aria-hidden>\n {chat.conversationId.slice(0, 8)}\n </span>\n )}\n </div>\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={handleNewConversation}\n disabled={isBusy}\n aria-label={t('ai_assistant.chat.newConversation', 'Start new conversation')}\n title={t('ai_assistant.chat.newConversation', 'Start new conversation')}\n data-ai-chat-new-conversation=\"\"\n >\n <Plus className=\"size-4\" aria-hidden />\n </IconButton>\n </div>\n <div\n ref={transcriptRef}\n role=\"log\"\n aria-live=\"polite\"\n aria-label={t('ai_assistant.chat.transcriptLabel', 'Chat transcript')}\n className=\"min-w-0 flex-1 space-y-2 overflow-y-auto overflow-x-hidden pr-1\"\n >\n {chat.messages.length === 0 ? (\n <WelcomeState\n title={welcomeTitle}\n description={welcomeDescription}\n suggestions={suggestions}\n onSuggestionClick={handleSendMessage}\n />\n ) : (\n chat.messages.map((message) => (\n <MessageRow\n key={message.id}\n message={message}\n registry={activeRegistry}\n onMutationRequested={onMutationRequested}\n />\n ))\n )}\n {uiParts.map((part, index) => (\n <AiUiPartRenderer\n key={`${part.componentId}-${index}`}\n part={part}\n registry={activeRegistry}\n onMutationRequested={onMutationRequested}\n />\n ))}\n {showThinkingIndicator ? (\n <div\n className=\"flex items-center gap-2 px-3 py-2 text-xs text-muted-foreground\"\n data-ai-chat-state=\"thinking\"\n >\n <Loader2 className=\"size-4 animate-spin\" aria-hidden />\n <span>{t('ai_assistant.chat.thinking', 'Thinking...')}</span>\n </div>\n ) : null}\n </div>\n\n {chat.error ? (\n <Alert variant={errorVariant} data-ai-chat-error={chat.error.code ?? 'unknown'}>\n <AlertTitle>\n {t('ai_assistant.chat.errorTitle', 'Agent dispatch failed')}\n </AlertTitle>\n <AlertDescription>\n {chat.error.code ? (\n <span className=\"mr-2 font-mono text-xs\">{chat.error.code}</span>\n ) : null}\n {chat.error.message}\n </AlertDescription>\n </Alert>\n ) : null}\n\n <form\n className=\"flex min-w-0 flex-col gap-2\"\n onSubmit={(event) => {\n event.preventDefault()\n handleSubmit()\n }}\n >\n <Label\n htmlFor=\"ai-chat-composer\"\n className=\"sr-only\"\n >\n {t('ai_assistant.chat.composerLabel', 'Message composer')}\n </Label>\n {pendingFiles.length > 0 ? (\n <div className=\"flex flex-wrap gap-1.5 rounded-md border border-border bg-muted/30 px-2 py-1.5\" data-ai-chat-attachments=\"\">\n {pendingFiles.map((entry, index) => (\n <span\n key={index}\n className=\"inline-flex items-center gap-1 rounded-full border border-border bg-background px-2 py-0.5 text-xs\"\n title={entry.error ? entry.error : undefined}\n data-ai-chat-attachment-state={\n entry.error ? 'error' : entry.attachmentId ? 'ready' : 'uploading'\n }\n >\n <Paperclip className=\"size-3 text-muted-foreground\" aria-hidden />\n <span className=\"max-w-[120px] truncate\">{entry.file.name}</span>\n {!entry.attachmentId && !entry.error ? (\n <Loader2 className=\"size-3 animate-spin text-muted-foreground\" aria-hidden />\n ) : null}\n <button\n type=\"button\"\n className=\"ml-0.5 rounded-full p-0.5 hover:bg-muted\"\n onClick={() => removePendingFile(index)}\n aria-label={t('ai_assistant.chat.removeFile', 'Remove file')}\n >\n <X className=\"size-3\" />\n </button>\n </span>\n ))}\n {isUploading ? <Loader2 className=\"size-3 animate-spin text-muted-foreground\" aria-hidden /> : null}\n </div>\n ) : null}\n <Textarea\n id=\"ai-chat-composer\"\n ref={textareaRef}\n value={input}\n placeholder={resolvedPlaceholder}\n onChange={(event) => setInput(event.target.value)}\n onKeyDown={handleKeyDown}\n rows={3}\n aria-label={t('ai_assistant.chat.composerLabel', 'Message composer')}\n className=\"min-w-0 resize-none\"\n />\n <input\n ref={fileInputRef}\n type=\"file\"\n multiple\n accept=\"image/*,.pdf,.doc,.docx,.txt,.csv\"\n className=\"hidden\"\n onChange={handleFileSelect}\n data-ai-chat-file-input=\"\"\n />\n <div\n ref={footerRef}\n className=\"flex min-w-0 items-center justify-between gap-2\"\n data-ai-chat-footer=\"\"\n data-ai-chat-footer-compact={isCompactFooter ? 'true' : 'false'}\n >\n <div className=\"flex min-w-0 items-center gap-2\">\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => fileInputRef.current?.click()}\n disabled={isBusy || isUploading}\n aria-label={t('ai_assistant.chat.attachFile', 'Attach file')}\n >\n <Paperclip className=\"size-4\" aria-hidden />\n </IconButton>\n {allowRuntimeModelOverride && modelProviders.length > 0 ? (\n <ModelPicker\n agentId={agent}\n value={effectiveModelPickerValue}\n onChange={handleModelPickerChange}\n availableProviders={modelProviders}\n disabled={isBusy}\n compact={isCompactFooter}\n defaultLabel={modelDefaultLabel}\n className=\"shrink-0\"\n />\n ) : null}\n <p className={cn('text-xs text-muted-foreground', isCompactFooter && 'hidden')}>\n {hasUploadingFiles || isUploading\n ? t(\n 'ai_assistant.chat.uploadingHint',\n 'Uploading attachments\u2026 Send is disabled until they finish.',\n )\n : t(\n 'ai_assistant.chat.shortcutHint',\n 'Press Enter to send, Shift+Enter for new line.',\n )}\n </p>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n {isStreaming ? (\n <IconButton\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => chat.cancel()}\n aria-label={t('ai_assistant.chat.cancel', 'Cancel streaming response')}\n >\n <Square className=\"size-4\" aria-hidden />\n </IconButton>\n ) : null}\n <Button\n type=\"submit\"\n size=\"sm\"\n disabled={\n isBusy ||\n isUploading ||\n hasUploadingFiles ||\n input.trim().length === 0\n }\n aria-label={\n hasUploadingFiles || isUploading\n ? t('ai_assistant.chat.sendWaitingForUpload', 'Waiting for upload to finish\u2026')\n : t('ai_assistant.chat.send', 'Send message')\n }\n title={\n hasUploadingFiles || isUploading\n ? t('ai_assistant.chat.sendWaitingForUpload', 'Waiting for upload to finish\u2026')\n : undefined\n }\n className={cn(isCompactFooter && 'w-8 px-0')}\n >\n <Send className=\"size-4\" aria-hidden />\n {!isCompactFooter ? (\n <span>{t('ai_assistant.chat.send', 'Send message')}</span>\n ) : null}\n </Button>\n </div>\n </div>\n </form>\n\n {debug ? (\n <AiChatDebugPanel\n tools={debugTools}\n promptSections={debugPromptSections}\n lastRequestDebug={chat.lastRequestDebug}\n lastResponseDebug={chat.lastResponseDebug}\n status={chat.status}\n errorCode={chat.error?.code}\n />\n ) : null}\n </section>\n )\n}\n\ninterface DebugPanelProps {\n tools?: AiChatDebugTool[]\n promptSections?: AiChatDebugPromptSection[]\n lastRequestDebug: { url: string; body: unknown } | null\n lastResponseDebug: { status: number; text: string } | null\n status: 'idle' | 'submitting' | 'streaming'\n errorCode?: string\n}\n\nfunction AiChatDebugPanel({\n tools,\n promptSections,\n lastRequestDebug,\n lastResponseDebug,\n status,\n errorCode,\n}: DebugPanelProps) {\n const t = useT()\n return (\n <div\n className=\"flex flex-col gap-2 rounded-md border border-border bg-muted/60 p-2 text-xs\"\n data-ai-chat-debug=\"true\"\n >\n <div className=\"font-semibold\">\n {t('ai_assistant.chat.debug.panelTitle', 'Debug panel')}\n </div>\n\n <details className=\"rounded border border-border bg-background\" data-ai-chat-debug-section=\"tools\" open>\n <summary className=\"cursor-pointer px-2 py-1 font-semibold\">\n {t('ai_assistant.chat.debug.toolsSection', 'Resolved tools')}\n {tools ? (\n <span className=\"ml-2 font-mono text-muted-foreground\">({tools.length})</span>\n ) : null}\n </summary>\n <div className=\"px-2 pb-2\">\n {tools && tools.length > 0 ? (\n <ul className=\"flex flex-col gap-1\" data-ai-chat-debug-tools>\n {tools.map((tool) => (\n <li\n key={tool.name}\n className=\"flex flex-col rounded border border-border bg-muted/40 px-2 py-1\"\n data-ai-chat-debug-tool={tool.name}\n >\n <span className=\"font-mono\">{tool.name}</span>\n {tool.displayName ? (\n <span className=\"text-muted-foreground\">{tool.displayName}</span>\n ) : null}\n <span className=\"mt-1 flex flex-wrap gap-2 text-muted-foreground\">\n <span>\n {tool.isMutation\n ? t('ai_assistant.chat.debug.toolMutation', 'mutation')\n : t('ai_assistant.chat.debug.toolRead', 'read')}\n </span>\n {tool.requiredFeatures && tool.requiredFeatures.length > 0 ? (\n <span className=\"font-mono\">\n [{tool.requiredFeatures.join(', ')}]\n </span>\n ) : (\n <span>\n {t('ai_assistant.chat.debug.toolNoFeatures', 'no required features')}\n </span>\n )}\n </span>\n </li>\n ))}\n </ul>\n ) : (\n <p className=\"text-muted-foreground\">\n {t(\n 'ai_assistant.chat.debug.toolsEmpty',\n 'No tools resolved for this agent yet.',\n )}\n </p>\n )}\n </div>\n </details>\n\n <details\n className=\"rounded border border-border bg-background\"\n data-ai-chat-debug-section=\"promptSections\"\n >\n <summary className=\"cursor-pointer px-2 py-1 font-semibold\">\n {t('ai_assistant.chat.debug.promptSection', 'Prompt sections')}\n {promptSections ? (\n <span className=\"ml-2 font-mono text-muted-foreground\">({promptSections.length})</span>\n ) : null}\n </summary>\n <div className=\"px-2 pb-2\">\n {promptSections && promptSections.length > 0 ? (\n <ul className=\"flex flex-col gap-1\" data-ai-chat-debug-prompt-sections>\n {promptSections.map((section) => (\n <li\n key={section.id}\n className=\"rounded border border-border bg-muted/40 px-2 py-1\"\n data-ai-chat-debug-prompt-section-id={section.id}\n >\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"font-mono\">{section.id}</span>\n <span className=\"text-muted-foreground\">\n {section.source === 'override'\n ? t('ai_assistant.chat.debug.promptOverride', 'override')\n : section.source === 'placeholder'\n ? t('ai_assistant.chat.debug.promptPlaceholder', 'placeholder')\n : t('ai_assistant.chat.debug.promptDefault', 'default')}\n </span>\n </div>\n {section.text ? (\n <pre className=\"mt-1 max-h-24 overflow-auto whitespace-pre-wrap font-mono text-muted-foreground\">\n {section.text}\n </pre>\n ) : null}\n </li>\n ))}\n </ul>\n ) : (\n <p className=\"text-muted-foreground\">\n {t(\n 'ai_assistant.chat.debug.promptEmpty',\n 'No prompt sections resolved for this agent.',\n )}\n </p>\n )}\n </div>\n </details>\n\n <details\n className=\"rounded border border-border bg-background\"\n data-ai-chat-debug-section=\"lastRequest\"\n >\n <summary className=\"cursor-pointer px-2 py-1 font-semibold\">\n {t('ai_assistant.chat.debug.lastRequestSection', 'Last request')}\n </summary>\n <div className=\"px-2 pb-2\">\n {lastRequestDebug ? (\n <pre\n className=\"max-h-40 overflow-auto whitespace-pre-wrap font-mono\"\n data-ai-chat-debug-last-request\n >\n {JSON.stringify(lastRequestDebug, null, 2)}\n </pre>\n ) : (\n <p className=\"text-muted-foreground\">\n {t(\n 'ai_assistant.chat.debug.lastRequestEmpty',\n 'No request has been sent yet.',\n )}\n </p>\n )}\n </div>\n </details>\n\n <details\n className=\"rounded border border-border bg-background\"\n data-ai-chat-debug-section=\"lastResponse\"\n >\n <summary className=\"cursor-pointer px-2 py-1 font-semibold\">\n {t('ai_assistant.chat.debug.lastResponseSection', 'Last response')}\n </summary>\n <div className=\"px-2 pb-2\">\n {lastResponseDebug ? (\n <pre\n className=\"max-h-40 overflow-auto whitespace-pre-wrap font-mono\"\n data-ai-chat-debug-last-response\n >\n {JSON.stringify(\n { status: lastResponseDebug.status, text: lastResponseDebug.text, errorCode },\n null,\n 2,\n )}\n </pre>\n ) : (\n <p className=\"text-muted-foreground\">\n {t(\n 'ai_assistant.chat.debug.lastResponseEmpty',\n 'No response received yet.',\n )}\n </p>\n )}\n </div>\n </details>\n\n <div className=\"text-muted-foreground\" data-ai-chat-debug-status={status}>\n {t('ai_assistant.chat.debug.statusLabel', 'Status:')}{' '}\n <span className=\"font-mono\">{status}</span>\n </div>\n </div>\n )\n}\n\nexport default AiChat\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/dist/ai/AiDock.js CHANGED
@@ -238,7 +238,7 @@ function AiDockPanel({
238
238
  // primary surface (full-screen sheet) and a fixed side panel would
239
239
  // crowd the viewport.
240
240
  "hidden lg:flex",
241
- "fixed top-0 right-0 z-overlay h-svh flex-col border-l bg-background shadow-lg",
241
+ "fixed top-0 right-0 z-overlay h-svh min-w-0 flex-col overflow-hidden border-l bg-background shadow-lg",
242
242
  collapsed ? "w-12" : ""
243
243
  ),
244
244
  style: collapsed ? void 0 : { width },
@@ -323,13 +323,14 @@ function DockedChatBody({ assistant }) {
323
323
  }, [assistant.agent, session, sessions]);
324
324
  return /* @__PURE__ */ jsxs(Fragment, { children: [
325
325
  /* @__PURE__ */ jsx(ChatPaneTabs, { agentId: assistant.agent, className: "border-b" }),
326
- /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1", "data-ai-dock-chat-container": "", children: session ? /* @__PURE__ */ jsx(React.Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
326
+ /* @__PURE__ */ jsx("div", { className: "min-h-0 min-w-0 flex-1 overflow-hidden", "data-ai-dock-chat-container": "", children: session ? /* @__PURE__ */ jsx(React.Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
327
327
  LazyAiChat,
328
328
  {
329
329
  agent: assistant.agent,
330
330
  conversationId: session.conversationId,
331
331
  pageContext: assistant.pageContext,
332
332
  className: "h-full",
333
+ defaultCompactFooter: true,
333
334
  placeholder: assistant.placeholder,
334
335
  suggestions: assistant.suggestions,
335
336
  contextItems: assistant.contextItems,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/ai/AiDock.tsx"],
4
- "sourcesContent": ["\"use client\"\n\n/**\n * Global AI Dock \u2014 a persistent right-side panel that hosts an `<AiChat>`\n * surface across page navigations.\n *\n * Modules invoke `useAiDock().dock({ agent, label, pageContext })` (typically\n * from the dialog header of an injection trigger) to move an active\n * assistant from a transient dialog into the dock. The dock survives router\n * navigation because the provider is mounted at the layout root (AppShell).\n *\n * The panel renders only when something is docked. The docked assistant, its\n * collapsed state, and its width are persisted in localStorage so refreshing\n * the page restores the same open dock.\n */\n\nimport * as React from 'react'\nimport { Maximize2, Minimize2, X } from 'lucide-react'\nimport type { AiChatContextItem, AiChatSuggestion } from './AiChat'\nimport { ChatPaneTabs } from './ChatPaneTabs'\nimport { useAiChatSessions } from './AiChatSessions'\nimport { IconButton } from '../primitives/icon-button'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\n// Lazy import keeps the heavy chat surface (AI SDK + streaming runtime) out\n// of the AppShell import graph. The dock provider only renders the chat when\n// something is actually docked, so tests that never dock skip the import.\nconst LazyAiChat = React.lazy(async () => {\n const mod = await import('./AiChat')\n return { default: mod.AiChat }\n})\n\nconst STORAGE_KEY = 'om-ai-dock-v1'\nconst MIN_WIDTH = 320\nconst MAX_WIDTH = 960\nconst DEFAULT_WIDTH = 420\n\nexport interface AiDockedAssistant {\n /** AI agent id (must be enabled for the current user). */\n agent: string\n /** Human-readable label shown in the dock header. */\n label: string\n /** Optional secondary description (e.g. module name). */\n description?: string\n /** Spec \u00A710.1 page-context payload sent with each turn. */\n pageContext?: Record<string, unknown>\n /** Composer placeholder copy. */\n placeholder?: string\n /** Welcome card title. */\n welcomeTitle?: string\n /** Welcome card description. */\n welcomeDescription?: string\n /** Optional starter suggestions. */\n suggestions?: AiChatSuggestion[]\n /** Optional pinned context chips (e.g. \"3 selected\"). */\n contextItems?: AiChatContextItem[]\n}\n\ninterface AiDockState {\n assistant: AiDockedAssistant | null\n width: number\n collapsed: boolean\n}\n\ninterface PersistedAiChatSuggestion {\n label: string\n prompt: string\n}\n\ninterface PersistedAiDockedAssistant {\n agent: string\n label: string\n description?: string\n pageContext?: Record<string, unknown>\n placeholder?: string\n welcomeTitle?: string\n welcomeDescription?: string\n suggestions?: PersistedAiChatSuggestion[]\n contextItems?: AiChatContextItem[]\n}\n\ninterface PersistedAiDockState {\n assistant?: PersistedAiDockedAssistant | null\n width?: number\n collapsed?: boolean\n}\n\ninterface AiDockApi {\n state: AiDockState\n /** Open / replace the docked assistant. */\n dock: (assistant: AiDockedAssistant) => void\n /** Close the dock. */\n undock: () => void\n /** True when the docked agent matches `agentId`. */\n isDocked: (agentId: string) => boolean\n}\n\nconst COLLAPSED_WIDTH = 48\n\nconst AiDockContext = React.createContext<AiDockApi | null>(null)\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === 'object' && !Array.isArray(value)\n}\n\nfunction readOptionalString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined\n}\n\nfunction readWidth(value: unknown): number {\n return Math.min(\n MAX_WIDTH,\n Math.max(MIN_WIDTH, typeof value === 'number' ? value : DEFAULT_WIDTH),\n )\n}\n\nfunction readPersistedSuggestions(value: unknown): PersistedAiChatSuggestion[] | undefined {\n if (!Array.isArray(value)) return undefined\n const suggestions = value\n .filter((entry): entry is Record<string, unknown> => isRecord(entry))\n .map((entry) => {\n const label = readOptionalString(entry.label)\n const prompt = readOptionalString(entry.prompt)\n if (!label || !prompt) return null\n return { label, prompt }\n })\n .filter((entry): entry is PersistedAiChatSuggestion => entry !== null)\n return suggestions.length > 0 ? suggestions : undefined\n}\n\nfunction readPersistedContextItems(value: unknown): AiChatContextItem[] | undefined {\n if (!Array.isArray(value)) return undefined\n const contextItems = value\n .filter((entry): entry is Record<string, unknown> => isRecord(entry))\n .map((entry) => {\n const label = readOptionalString(entry.label)\n if (!label) return null\n const detail = readOptionalString(entry.detail)\n return detail ? { label, detail } : { label }\n })\n .filter((entry): entry is AiChatContextItem => entry !== null)\n return contextItems.length > 0 ? contextItems : undefined\n}\n\nfunction readPersistedAssistant(value: unknown): AiDockedAssistant | null {\n if (!isRecord(value)) return null\n const agent = readOptionalString(value.agent)\n const label = readOptionalString(value.label)\n if (!agent || !label) return null\n\n return {\n agent,\n label,\n description: readOptionalString(value.description),\n pageContext: isRecord(value.pageContext) ? value.pageContext : undefined,\n placeholder: readOptionalString(value.placeholder),\n welcomeTitle: readOptionalString(value.welcomeTitle),\n welcomeDescription: readOptionalString(value.welcomeDescription),\n suggestions: readPersistedSuggestions(value.suggestions),\n contextItems: readPersistedContextItems(value.contextItems),\n }\n}\n\nfunction serializeAssistant(assistant: AiDockedAssistant | null): PersistedAiDockedAssistant | null {\n if (!assistant) return null\n return {\n agent: assistant.agent,\n label: assistant.label,\n description: assistant.description,\n pageContext: assistant.pageContext,\n placeholder: assistant.placeholder,\n welcomeTitle: assistant.welcomeTitle,\n welcomeDescription: assistant.welcomeDescription,\n suggestions: assistant.suggestions?.map((suggestion) => ({\n label: suggestion.label,\n prompt: suggestion.prompt,\n })),\n contextItems: assistant.contextItems?.map((item) => ({\n label: item.label,\n detail: item.detail,\n })),\n }\n}\n\nfunction readPersisted(): AiDockState {\n if (typeof window === 'undefined') return { assistant: null, width: DEFAULT_WIDTH, collapsed: false }\n try {\n const raw = window.localStorage.getItem(STORAGE_KEY)\n if (!raw) return { assistant: null, width: DEFAULT_WIDTH, collapsed: false }\n const parsed = JSON.parse(raw) as PersistedAiDockState | null\n return {\n assistant: readPersistedAssistant(parsed?.assistant),\n width: readWidth(parsed?.width),\n collapsed: parsed?.collapsed === true,\n }\n } catch {\n return { assistant: null, width: DEFAULT_WIDTH, collapsed: false }\n }\n}\n\nfunction writePersisted(state: AiDockState) {\n if (typeof window === 'undefined') return\n try {\n window.localStorage.setItem(\n STORAGE_KEY,\n JSON.stringify({\n assistant: serializeAssistant(state.assistant),\n width: state.width,\n collapsed: state.collapsed,\n } satisfies PersistedAiDockState),\n )\n } catch {\n /* ignore */\n }\n}\n\nexport function AiDockProvider({ children }: { children: React.ReactNode }) {\n const [state, setState] = React.useState<AiDockState>(() => ({\n assistant: null,\n width: DEFAULT_WIDTH,\n collapsed: false,\n }))\n const [hydrated, setHydrated] = React.useState(false)\n\n React.useEffect(() => {\n const persisted = readPersisted()\n setState(persisted)\n setHydrated(true)\n }, [])\n\n React.useEffect(() => {\n if (!hydrated) return\n writePersisted(state)\n }, [hydrated, state])\n\n const dock = React.useCallback((assistant: AiDockedAssistant) => {\n // Always reset `collapsed` when (re)docking \u2014 the operator just clicked\n // \"dock to side\" and expects the panel to be visible at full width.\n setState((prev) => ({ ...prev, assistant, collapsed: false }))\n }, [])\n\n const undock = React.useCallback(() => {\n // Hard reset: drop the assistant AND the collapsed flag so the next\n // dock call starts from a clean slate. The wrapper observes `assistant`\n // turning null and clears its layout shift in the same render tick.\n setState((prev) => ({ ...prev, assistant: null, collapsed: false }))\n }, [])\n\n const isDocked = React.useCallback(\n (agentId: string) => state.assistant?.agent === agentId,\n [state.assistant?.agent],\n )\n\n const setWidth = React.useCallback((width: number) => {\n setState((prev) => ({ ...prev, width }))\n }, [])\n\n const setCollapsed = React.useCallback((collapsed: boolean) => {\n setState((prev) => ({ ...prev, collapsed }))\n }, [])\n\n const api = React.useMemo<AiDockApi>(\n () => ({ state, dock, undock, isDocked }),\n [state, dock, undock, isDocked],\n )\n\n // The right-side padding the page must reserve for the dock panel. Stays\n // null while nothing is docked so the wrapper renders no className/style\n // and the underlying layout (DataTable, sidebar grid) reclaims its full\n // width on undock.\n const reservedWidth = state.assistant\n ? state.collapsed\n ? COLLAPSED_WIDTH\n : state.width\n : null\n\n return (\n <AiDockContext.Provider value={api}>\n <div\n // The dock is desktop-only (`lg+`); reserve right-side padding only\n // at that breakpoint so mobile layout stays full-width.\n className={reservedWidth != null ? 'lg:pr-[var(--om-ai-dock-width)]' : undefined}\n style={\n reservedWidth != null\n ? ({ ['--om-ai-dock-width' as string]: `${reservedWidth}px` } as React.CSSProperties)\n : undefined\n }\n >\n {children}\n </div>\n {state.assistant ? (\n <AiDockPanel\n assistant={state.assistant}\n width={state.width}\n collapsed={state.collapsed}\n onCollapsedChange={setCollapsed}\n onWidthChange={setWidth}\n onClose={undock}\n />\n ) : null}\n </AiDockContext.Provider>\n )\n}\n\nexport function useAiDock(): AiDockApi {\n const ctx = React.useContext(AiDockContext)\n if (ctx) return ctx\n // Fallback no-op API \u2014 keeps consumers safe when the provider is absent\n // (e.g. unit tests rendering a widget in isolation).\n return {\n state: { assistant: null, width: DEFAULT_WIDTH, collapsed: false },\n dock: () => {},\n undock: () => {},\n isDocked: () => false,\n }\n}\n\ninterface AiDockPanelProps {\n assistant: AiDockedAssistant\n width: number\n collapsed: boolean\n onCollapsedChange: (collapsed: boolean) => void\n onWidthChange: (width: number) => void\n onClose: () => void\n}\n\nfunction AiDockPanel({\n assistant,\n width,\n collapsed,\n onCollapsedChange,\n onWidthChange,\n onClose,\n}: AiDockPanelProps) {\n const t = useT()\n const dragStateRef = React.useRef<{ startX: number; startWidth: number } | null>(null)\n\n const handlePointerDown = React.useCallback(\n (event: React.PointerEvent<HTMLDivElement>) => {\n event.preventDefault()\n const target = event.currentTarget\n target.setPointerCapture(event.pointerId)\n dragStateRef.current = { startX: event.clientX, startWidth: width }\n },\n [width],\n )\n\n const handlePointerMove = React.useCallback(\n (event: React.PointerEvent<HTMLDivElement>) => {\n if (!dragStateRef.current) return\n const delta = dragStateRef.current.startX - event.clientX\n const next = Math.min(\n MAX_WIDTH,\n Math.max(MIN_WIDTH, dragStateRef.current.startWidth + delta),\n )\n onWidthChange(next)\n },\n [onWidthChange],\n )\n\n const handlePointerUp = React.useCallback(\n (event: React.PointerEvent<HTMLDivElement>) => {\n const target = event.currentTarget\n try {\n target.releasePointerCapture(event.pointerId)\n } catch {\n /* ignore */\n }\n dragStateRef.current = null\n },\n [],\n )\n\n return (\n <aside\n data-ai-dock-panel=\"\"\n data-ai-dock-agent={assistant.agent}\n className={cn(\n // Dock is desktop-only \u2014 on small screens the AiChat dialog is the\n // primary surface (full-screen sheet) and a fixed side panel would\n // crowd the viewport.\n 'hidden lg:flex',\n 'fixed top-0 right-0 z-overlay h-svh flex-col border-l bg-background shadow-lg',\n collapsed ? 'w-12' : '',\n )}\n style={collapsed ? undefined : { width }}\n aria-label={assistant.label}\n >\n {!collapsed ? (\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n tabIndex={-1}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n onPointerCancel={handlePointerUp}\n className=\"absolute left-0 top-0 h-full w-1 cursor-col-resize bg-transparent hover:bg-primary/20\"\n data-ai-dock-resize-handle=\"\"\n />\n ) : null}\n <header className=\"flex items-center gap-2 px-3 py-2\">\n {collapsed ? (\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t('ai_assistant.chat.dock.expand', 'Expand AI dock')}\n title={t('ai_assistant.chat.dock.expand', 'Expand AI dock')}\n onClick={() => onCollapsedChange(false)}\n >\n <Maximize2 className=\"size-4\" />\n </IconButton>\n ) : (\n <>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2 text-sm font-medium\" data-ai-dock-label=\"\">\n <span className=\"truncate\">{assistant.label}</span>\n <span\n className=\"inline-flex shrink-0 items-center rounded-full border border-border bg-secondary px-1.5 py-0 text-[10px] font-medium uppercase tracking-wide text-secondary-foreground\"\n data-ai-beta-chip=\"\"\n >\n {t('ai_assistant.chat.betaChip', 'beta')}\n </span>\n </div>\n {assistant.description ? (\n <div className=\"truncate text-xs text-muted-foreground\">\n {assistant.description}\n </div>\n ) : null}\n </div>\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t('ai_assistant.chat.dock.collapse', 'Collapse AI dock')}\n title={t('ai_assistant.chat.dock.collapse', 'Collapse AI dock')}\n onClick={() => onCollapsedChange(true)}\n >\n <Minimize2 className=\"size-4\" />\n </IconButton>\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t('ai_assistant.chat.dock.close', 'Close AI dock')}\n title={t('ai_assistant.chat.dock.close', 'Close AI dock')}\n onClick={onClose}\n data-ai-dock-close=\"\"\n >\n <X className=\"size-4\" />\n </IconButton>\n </>\n )}\n </header>\n {!collapsed ? (\n <DockedChatBody assistant={assistant} />\n ) : null}\n </aside>\n )\n}\n\nfunction DockedChatBody({ assistant }: { assistant: AiDockedAssistant }) {\n const sessions = useAiChatSessions()\n const session = sessions.getActiveSession(assistant.agent)\n\n // Lazily ensure an open session exists. Running `ensureSession` inside an\n // effect (not inline during render) keeps the provider's setState calls\n // outside of the render phase. The first frame may render without a\n // session \u2014 that's fine, we render the tab strip alone until the next\n // tick when the new session is committed and `getActiveSession` returns\n // it.\n React.useEffect(() => {\n if (!session) sessions.ensureSession(assistant.agent)\n }, [assistant.agent, session, sessions])\n\n return (\n <>\n <ChatPaneTabs agentId={assistant.agent} className=\"border-b\" />\n <div className=\"min-h-0 flex-1\" data-ai-dock-chat-container=\"\">\n {session ? (\n <React.Suspense fallback={null}>\n <LazyAiChat\n // `key` forces a fresh `<AiChat>` mount whenever the active\n // session changes \u2014 without it the AI SDK's internal status\n // would carry across tabs and surface the previous tab's\n // streaming indicator on a brand-new conversation.\n key={session.id}\n agent={assistant.agent}\n conversationId={session.conversationId}\n pageContext={assistant.pageContext}\n className=\"h-full\"\n placeholder={assistant.placeholder}\n suggestions={assistant.suggestions}\n contextItems={assistant.contextItems}\n welcomeTitle={assistant.welcomeTitle}\n welcomeDescription={assistant.welcomeDescription}\n />\n </React.Suspense>\n ) : null}\n </div>\n </>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\n/**\n * Global AI Dock \u2014 a persistent right-side panel that hosts an `<AiChat>`\n * surface across page navigations.\n *\n * Modules invoke `useAiDock().dock({ agent, label, pageContext })` (typically\n * from the dialog header of an injection trigger) to move an active\n * assistant from a transient dialog into the dock. The dock survives router\n * navigation because the provider is mounted at the layout root (AppShell).\n *\n * The panel renders only when something is docked. The docked assistant, its\n * collapsed state, and its width are persisted in localStorage so refreshing\n * the page restores the same open dock.\n */\n\nimport * as React from 'react'\nimport { Maximize2, Minimize2, X } from 'lucide-react'\nimport type { AiChatContextItem, AiChatSuggestion } from './AiChat'\nimport { ChatPaneTabs } from './ChatPaneTabs'\nimport { useAiChatSessions } from './AiChatSessions'\nimport { IconButton } from '../primitives/icon-button'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { cn } from '@open-mercato/shared/lib/utils'\n\n// Lazy import keeps the heavy chat surface (AI SDK + streaming runtime) out\n// of the AppShell import graph. The dock provider only renders the chat when\n// something is actually docked, so tests that never dock skip the import.\nconst LazyAiChat = React.lazy(async () => {\n const mod = await import('./AiChat')\n return { default: mod.AiChat }\n})\n\nconst STORAGE_KEY = 'om-ai-dock-v1'\nconst MIN_WIDTH = 320\nconst MAX_WIDTH = 960\nconst DEFAULT_WIDTH = 420\n\nexport interface AiDockedAssistant {\n /** AI agent id (must be enabled for the current user). */\n agent: string\n /** Human-readable label shown in the dock header. */\n label: string\n /** Optional secondary description (e.g. module name). */\n description?: string\n /** Spec \u00A710.1 page-context payload sent with each turn. */\n pageContext?: Record<string, unknown>\n /** Composer placeholder copy. */\n placeholder?: string\n /** Welcome card title. */\n welcomeTitle?: string\n /** Welcome card description. */\n welcomeDescription?: string\n /** Optional starter suggestions. */\n suggestions?: AiChatSuggestion[]\n /** Optional pinned context chips (e.g. \"3 selected\"). */\n contextItems?: AiChatContextItem[]\n}\n\ninterface AiDockState {\n assistant: AiDockedAssistant | null\n width: number\n collapsed: boolean\n}\n\ninterface PersistedAiChatSuggestion {\n label: string\n prompt: string\n}\n\ninterface PersistedAiDockedAssistant {\n agent: string\n label: string\n description?: string\n pageContext?: Record<string, unknown>\n placeholder?: string\n welcomeTitle?: string\n welcomeDescription?: string\n suggestions?: PersistedAiChatSuggestion[]\n contextItems?: AiChatContextItem[]\n}\n\ninterface PersistedAiDockState {\n assistant?: PersistedAiDockedAssistant | null\n width?: number\n collapsed?: boolean\n}\n\ninterface AiDockApi {\n state: AiDockState\n /** Open / replace the docked assistant. */\n dock: (assistant: AiDockedAssistant) => void\n /** Close the dock. */\n undock: () => void\n /** True when the docked agent matches `agentId`. */\n isDocked: (agentId: string) => boolean\n}\n\nconst COLLAPSED_WIDTH = 48\n\nconst AiDockContext = React.createContext<AiDockApi | null>(null)\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === 'object' && !Array.isArray(value)\n}\n\nfunction readOptionalString(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined\n}\n\nfunction readWidth(value: unknown): number {\n return Math.min(\n MAX_WIDTH,\n Math.max(MIN_WIDTH, typeof value === 'number' ? value : DEFAULT_WIDTH),\n )\n}\n\nfunction readPersistedSuggestions(value: unknown): PersistedAiChatSuggestion[] | undefined {\n if (!Array.isArray(value)) return undefined\n const suggestions = value\n .filter((entry): entry is Record<string, unknown> => isRecord(entry))\n .map((entry) => {\n const label = readOptionalString(entry.label)\n const prompt = readOptionalString(entry.prompt)\n if (!label || !prompt) return null\n return { label, prompt }\n })\n .filter((entry): entry is PersistedAiChatSuggestion => entry !== null)\n return suggestions.length > 0 ? suggestions : undefined\n}\n\nfunction readPersistedContextItems(value: unknown): AiChatContextItem[] | undefined {\n if (!Array.isArray(value)) return undefined\n const contextItems = value\n .filter((entry): entry is Record<string, unknown> => isRecord(entry))\n .map((entry) => {\n const label = readOptionalString(entry.label)\n if (!label) return null\n const detail = readOptionalString(entry.detail)\n return detail ? { label, detail } : { label }\n })\n .filter((entry): entry is AiChatContextItem => entry !== null)\n return contextItems.length > 0 ? contextItems : undefined\n}\n\nfunction readPersistedAssistant(value: unknown): AiDockedAssistant | null {\n if (!isRecord(value)) return null\n const agent = readOptionalString(value.agent)\n const label = readOptionalString(value.label)\n if (!agent || !label) return null\n\n return {\n agent,\n label,\n description: readOptionalString(value.description),\n pageContext: isRecord(value.pageContext) ? value.pageContext : undefined,\n placeholder: readOptionalString(value.placeholder),\n welcomeTitle: readOptionalString(value.welcomeTitle),\n welcomeDescription: readOptionalString(value.welcomeDescription),\n suggestions: readPersistedSuggestions(value.suggestions),\n contextItems: readPersistedContextItems(value.contextItems),\n }\n}\n\nfunction serializeAssistant(assistant: AiDockedAssistant | null): PersistedAiDockedAssistant | null {\n if (!assistant) return null\n return {\n agent: assistant.agent,\n label: assistant.label,\n description: assistant.description,\n pageContext: assistant.pageContext,\n placeholder: assistant.placeholder,\n welcomeTitle: assistant.welcomeTitle,\n welcomeDescription: assistant.welcomeDescription,\n suggestions: assistant.suggestions?.map((suggestion) => ({\n label: suggestion.label,\n prompt: suggestion.prompt,\n })),\n contextItems: assistant.contextItems?.map((item) => ({\n label: item.label,\n detail: item.detail,\n })),\n }\n}\n\nfunction readPersisted(): AiDockState {\n if (typeof window === 'undefined') return { assistant: null, width: DEFAULT_WIDTH, collapsed: false }\n try {\n const raw = window.localStorage.getItem(STORAGE_KEY)\n if (!raw) return { assistant: null, width: DEFAULT_WIDTH, collapsed: false }\n const parsed = JSON.parse(raw) as PersistedAiDockState | null\n return {\n assistant: readPersistedAssistant(parsed?.assistant),\n width: readWidth(parsed?.width),\n collapsed: parsed?.collapsed === true,\n }\n } catch {\n return { assistant: null, width: DEFAULT_WIDTH, collapsed: false }\n }\n}\n\nfunction writePersisted(state: AiDockState) {\n if (typeof window === 'undefined') return\n try {\n window.localStorage.setItem(\n STORAGE_KEY,\n JSON.stringify({\n assistant: serializeAssistant(state.assistant),\n width: state.width,\n collapsed: state.collapsed,\n } satisfies PersistedAiDockState),\n )\n } catch {\n /* ignore */\n }\n}\n\nexport function AiDockProvider({ children }: { children: React.ReactNode }) {\n const [state, setState] = React.useState<AiDockState>(() => ({\n assistant: null,\n width: DEFAULT_WIDTH,\n collapsed: false,\n }))\n const [hydrated, setHydrated] = React.useState(false)\n\n React.useEffect(() => {\n const persisted = readPersisted()\n setState(persisted)\n setHydrated(true)\n }, [])\n\n React.useEffect(() => {\n if (!hydrated) return\n writePersisted(state)\n }, [hydrated, state])\n\n const dock = React.useCallback((assistant: AiDockedAssistant) => {\n // Always reset `collapsed` when (re)docking \u2014 the operator just clicked\n // \"dock to side\" and expects the panel to be visible at full width.\n setState((prev) => ({ ...prev, assistant, collapsed: false }))\n }, [])\n\n const undock = React.useCallback(() => {\n // Hard reset: drop the assistant AND the collapsed flag so the next\n // dock call starts from a clean slate. The wrapper observes `assistant`\n // turning null and clears its layout shift in the same render tick.\n setState((prev) => ({ ...prev, assistant: null, collapsed: false }))\n }, [])\n\n const isDocked = React.useCallback(\n (agentId: string) => state.assistant?.agent === agentId,\n [state.assistant?.agent],\n )\n\n const setWidth = React.useCallback((width: number) => {\n setState((prev) => ({ ...prev, width }))\n }, [])\n\n const setCollapsed = React.useCallback((collapsed: boolean) => {\n setState((prev) => ({ ...prev, collapsed }))\n }, [])\n\n const api = React.useMemo<AiDockApi>(\n () => ({ state, dock, undock, isDocked }),\n [state, dock, undock, isDocked],\n )\n\n // The right-side padding the page must reserve for the dock panel. Stays\n // null while nothing is docked so the wrapper renders no className/style\n // and the underlying layout (DataTable, sidebar grid) reclaims its full\n // width on undock.\n const reservedWidth = state.assistant\n ? state.collapsed\n ? COLLAPSED_WIDTH\n : state.width\n : null\n\n return (\n <AiDockContext.Provider value={api}>\n <div\n // The dock is desktop-only (`lg+`); reserve right-side padding only\n // at that breakpoint so mobile layout stays full-width.\n className={reservedWidth != null ? 'lg:pr-[var(--om-ai-dock-width)]' : undefined}\n style={\n reservedWidth != null\n ? ({ ['--om-ai-dock-width' as string]: `${reservedWidth}px` } as React.CSSProperties)\n : undefined\n }\n >\n {children}\n </div>\n {state.assistant ? (\n <AiDockPanel\n assistant={state.assistant}\n width={state.width}\n collapsed={state.collapsed}\n onCollapsedChange={setCollapsed}\n onWidthChange={setWidth}\n onClose={undock}\n />\n ) : null}\n </AiDockContext.Provider>\n )\n}\n\nexport function useAiDock(): AiDockApi {\n const ctx = React.useContext(AiDockContext)\n if (ctx) return ctx\n // Fallback no-op API \u2014 keeps consumers safe when the provider is absent\n // (e.g. unit tests rendering a widget in isolation).\n return {\n state: { assistant: null, width: DEFAULT_WIDTH, collapsed: false },\n dock: () => {},\n undock: () => {},\n isDocked: () => false,\n }\n}\n\ninterface AiDockPanelProps {\n assistant: AiDockedAssistant\n width: number\n collapsed: boolean\n onCollapsedChange: (collapsed: boolean) => void\n onWidthChange: (width: number) => void\n onClose: () => void\n}\n\nfunction AiDockPanel({\n assistant,\n width,\n collapsed,\n onCollapsedChange,\n onWidthChange,\n onClose,\n}: AiDockPanelProps) {\n const t = useT()\n const dragStateRef = React.useRef<{ startX: number; startWidth: number } | null>(null)\n\n const handlePointerDown = React.useCallback(\n (event: React.PointerEvent<HTMLDivElement>) => {\n event.preventDefault()\n const target = event.currentTarget\n target.setPointerCapture(event.pointerId)\n dragStateRef.current = { startX: event.clientX, startWidth: width }\n },\n [width],\n )\n\n const handlePointerMove = React.useCallback(\n (event: React.PointerEvent<HTMLDivElement>) => {\n if (!dragStateRef.current) return\n const delta = dragStateRef.current.startX - event.clientX\n const next = Math.min(\n MAX_WIDTH,\n Math.max(MIN_WIDTH, dragStateRef.current.startWidth + delta),\n )\n onWidthChange(next)\n },\n [onWidthChange],\n )\n\n const handlePointerUp = React.useCallback(\n (event: React.PointerEvent<HTMLDivElement>) => {\n const target = event.currentTarget\n try {\n target.releasePointerCapture(event.pointerId)\n } catch {\n /* ignore */\n }\n dragStateRef.current = null\n },\n [],\n )\n\n return (\n <aside\n data-ai-dock-panel=\"\"\n data-ai-dock-agent={assistant.agent}\n className={cn(\n // Dock is desktop-only \u2014 on small screens the AiChat dialog is the\n // primary surface (full-screen sheet) and a fixed side panel would\n // crowd the viewport.\n 'hidden lg:flex',\n 'fixed top-0 right-0 z-overlay h-svh min-w-0 flex-col overflow-hidden border-l bg-background shadow-lg',\n collapsed ? 'w-12' : '',\n )}\n style={collapsed ? undefined : { width }}\n aria-label={assistant.label}\n >\n {!collapsed ? (\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n tabIndex={-1}\n onPointerDown={handlePointerDown}\n onPointerMove={handlePointerMove}\n onPointerUp={handlePointerUp}\n onPointerCancel={handlePointerUp}\n className=\"absolute left-0 top-0 h-full w-1 cursor-col-resize bg-transparent hover:bg-primary/20\"\n data-ai-dock-resize-handle=\"\"\n />\n ) : null}\n <header className=\"flex items-center gap-2 px-3 py-2\">\n {collapsed ? (\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t('ai_assistant.chat.dock.expand', 'Expand AI dock')}\n title={t('ai_assistant.chat.dock.expand', 'Expand AI dock')}\n onClick={() => onCollapsedChange(false)}\n >\n <Maximize2 className=\"size-4\" />\n </IconButton>\n ) : (\n <>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2 text-sm font-medium\" data-ai-dock-label=\"\">\n <span className=\"truncate\">{assistant.label}</span>\n <span\n className=\"inline-flex shrink-0 items-center rounded-full border border-border bg-secondary px-1.5 py-0 text-[10px] font-medium uppercase tracking-wide text-secondary-foreground\"\n data-ai-beta-chip=\"\"\n >\n {t('ai_assistant.chat.betaChip', 'beta')}\n </span>\n </div>\n {assistant.description ? (\n <div className=\"truncate text-xs text-muted-foreground\">\n {assistant.description}\n </div>\n ) : null}\n </div>\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t('ai_assistant.chat.dock.collapse', 'Collapse AI dock')}\n title={t('ai_assistant.chat.dock.collapse', 'Collapse AI dock')}\n onClick={() => onCollapsedChange(true)}\n >\n <Minimize2 className=\"size-4\" />\n </IconButton>\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n aria-label={t('ai_assistant.chat.dock.close', 'Close AI dock')}\n title={t('ai_assistant.chat.dock.close', 'Close AI dock')}\n onClick={onClose}\n data-ai-dock-close=\"\"\n >\n <X className=\"size-4\" />\n </IconButton>\n </>\n )}\n </header>\n {!collapsed ? (\n <DockedChatBody assistant={assistant} />\n ) : null}\n </aside>\n )\n}\n\nfunction DockedChatBody({ assistant }: { assistant: AiDockedAssistant }) {\n const sessions = useAiChatSessions()\n const session = sessions.getActiveSession(assistant.agent)\n\n // Lazily ensure an open session exists. Running `ensureSession` inside an\n // effect (not inline during render) keeps the provider's setState calls\n // outside of the render phase. The first frame may render without a\n // session \u2014 that's fine, we render the tab strip alone until the next\n // tick when the new session is committed and `getActiveSession` returns\n // it.\n React.useEffect(() => {\n if (!session) sessions.ensureSession(assistant.agent)\n }, [assistant.agent, session, sessions])\n\n return (\n <>\n <ChatPaneTabs agentId={assistant.agent} className=\"border-b\" />\n <div className=\"min-h-0 min-w-0 flex-1 overflow-hidden\" data-ai-dock-chat-container=\"\">\n {session ? (\n <React.Suspense fallback={null}>\n <LazyAiChat\n // `key` forces a fresh `<AiChat>` mount whenever the active\n // session changes \u2014 without it the AI SDK's internal status\n // would carry across tabs and surface the previous tab's\n // streaming indicator on a brand-new conversation.\n key={session.id}\n agent={assistant.agent}\n conversationId={session.conversationId}\n pageContext={assistant.pageContext}\n className=\"h-full\"\n defaultCompactFooter\n placeholder={assistant.placeholder}\n suggestions={assistant.suggestions}\n contextItems={assistant.contextItems}\n welcomeTitle={assistant.welcomeTitle}\n welcomeDescription={assistant.welcomeDescription}\n />\n </React.Suspense>\n ) : null}\n </div>\n </>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }