@waniwani/sdk 0.1.9 → 0.1.12

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 +1 @@
1
- {"version":3,"sources":["../../src/chat/web/layouts/chat-bar.tsx","../../src/chat/web/ai-elements/conversation.tsx","../../src/chat/web/lib/utils.ts","../../src/chat/web/ui/button.tsx","../../src/chat/web/ai-elements/prompt-input.tsx","../../src/chat/web/ai-elements/attachments.tsx","../../src/chat/web/ai-elements/loader.tsx","../../src/chat/web/ai-elements/message.tsx","../../src/chat/web/ai-elements/reasoning.tsx","../../src/chat/web/ai-elements/tool.tsx","../../src/chat/web/components/mcp-app-frame.tsx","../../src/chat/web/components/widget-error-boundary.tsx","../../src/chat/web/components/message-list.tsx","../../src/chat/web/components/suggestions.tsx","../../src/chat/web/hooks/use-chat-engine.ts","../../src/chat/web/hooks/use-suggestions.ts","../../src/chat/web/hooks/use-typing-placeholder.ts","../../src/chat/web/theme.ts","../../src/chat/web/layouts/chat-card.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n\tforwardRef,\n\tuseCallback,\n\tuseEffect,\n\tuseImperativeHandle,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport type { ChatBarProps, ChatHandle } from \"../@types\";\nimport {\n\tConversation,\n\tConversationContent,\n\tConversationScrollButton,\n} from \"../ai-elements/conversation\";\nimport {\n\tPromptInput,\n\tPromptInputAddAttachments,\n\tPromptInputSubmit,\n\tPromptInputTextarea,\n} from \"../ai-elements/prompt-input\";\nimport { MessageList } from \"../components/message-list\";\nimport { Suggestions } from \"../components/suggestions\";\nimport { useChatEngine } from \"../hooks/use-chat-engine\";\nimport { useSuggestions } from \"../hooks/use-suggestions\";\nimport { useTypingPlaceholder } from \"../hooks/use-typing-placeholder\";\nimport { cn } from \"../lib/utils\";\nimport { isDarkTheme, mergeTheme, themeToCSSProperties } from \"../theme\";\n\nexport const ChatBar = forwardRef<ChatHandle, ChatBarProps>(\n\tfunction ChatBar(props, ref) {\n\t\tconst {\n\t\t\ttheme: userTheme,\n\t\t\twidth = 600,\n\t\t\texpandedHeight = 400,\n\t\t\tallowAttachments = false,\n\t\t\twelcomeMessage,\n\t\t\tplaceholder = \"Ask me anything...\",\n\t\t\ttriggerEvent = \"triggerDemoRequest\",\n\t\t\tresourceEndpoint,\n\t\t\tapi,\n\t\t} = props;\n\n\t\tconst effectiveResourceEndpoint =\n\t\t\tresourceEndpoint ?? (api ? `${api}/resource` : undefined);\n\n\t\tconst resolvedTheme = mergeTheme(userTheme);\n\t\tconst cssVars = themeToCSSProperties(resolvedTheme);\n\t\tconst isDark = isDarkTheme(resolvedTheme);\n\n\t\tconst engine = useChatEngine(props);\n\n\t\tconst suggestionsState = useSuggestions({\n\t\t\tmessages: engine.messages,\n\t\t\tstatus: engine.status,\n\t\t\tconfig: props.suggestions,\n\t\t});\n\n\t\tconst handleWidgetMessage = useCallback(\n\t\t\t(message: {\n\t\t\t\trole: string;\n\t\t\t\tcontent: Array<{ type: string; text?: string }>;\n\t\t\t}) => {\n\t\t\t\tconst text = message.content\n\t\t\t\t\t.map((c) => c.text ?? \"\")\n\t\t\t\t\t.join(\"\")\n\t\t\t\t\t.trim();\n\t\t\t\tif (text) engine.handleSubmit({ text, files: [] });\n\t\t\t},\n\t\t\t[engine.handleSubmit],\n\t\t);\n\n\t\tconst handleSuggestionSelect = useCallback(\n\t\t\t(suggestion: string) => {\n\t\t\t\tsuggestionsState.clear();\n\t\t\t\tengine.handleSubmit({ text: suggestion, files: [] });\n\t\t\t},\n\t\t\t[suggestionsState.clear, engine.handleSubmit],\n\t\t);\n\n\t\tconst animatedPlaceholder = useTypingPlaceholder(placeholder, !engine.text);\n\n\t\tconst [isFocused, setIsFocused] = useState(false);\n\t\tconst [isHighlighted, setIsHighlighted] = useState(false);\n\t\tconst containerRef = useRef<HTMLDivElement>(null);\n\t\tconst highlightTimerRef = useRef<ReturnType<typeof setTimeout>>(undefined);\n\n\t\tconst focusInput = useCallback(() => {\n\t\t\tconst container = containerRef.current;\n\t\t\tif (!container) return;\n\t\t\tcontainer.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n\t\t\tconst textarea = container.querySelector(\"textarea\");\n\t\t\tif (textarea) {\n\t\t\t\tsetTimeout(() => textarea.focus(), 300);\n\t\t\t}\n\t\t\tsetIsFocused(true);\n\t\t\tsetIsHighlighted(true);\n\t\t\tclearTimeout(highlightTimerRef.current);\n\t\t\thighlightTimerRef.current = setTimeout(\n\t\t\t\t() => setIsHighlighted(false),\n\t\t\t\t2000,\n\t\t\t);\n\t\t}, []);\n\n\t\tuseImperativeHandle(\n\t\t\tref,\n\t\t\t() => ({\n\t\t\t\tsendMessage: (text: string) => {\n\t\t\t\t\tengine.handleSubmit({ text, files: [] });\n\t\t\t\t\tfocusInput();\n\t\t\t\t},\n\t\t\t\tfocus: focusInput,\n\t\t\t}),\n\t\t\t[engine.handleSubmit, focusInput],\n\t\t);\n\n\t\t// Listen for custom trigger event (e.g. \"triggerDemoRequest\")\n\t\tuseEffect(() => {\n\t\t\tif (!triggerEvent) return;\n\t\t\tconst handler = (e: Event) => {\n\t\t\t\tconst detail = (e as CustomEvent).detail;\n\t\t\t\tconst message =\n\t\t\t\t\ttypeof detail?.message === \"string\" ? detail.message : undefined;\n\t\t\t\tif (message) {\n\t\t\t\t\tengine.handleSubmit({ text: message, files: [] });\n\t\t\t\t}\n\t\t\t\tfocusInput();\n\t\t\t};\n\t\t\twindow.addEventListener(triggerEvent, handler);\n\t\t\treturn () => window.removeEventListener(triggerEvent, handler);\n\t\t}, [triggerEvent, engine.handleSubmit, focusInput]);\n\t\tconst isExpanded = isFocused;\n\n\t\t// Close on outside click\n\t\tuseEffect(() => {\n\t\t\tif (!isFocused) return;\n\t\t\tconst handleClickOutside = (e: MouseEvent) => {\n\t\t\t\tif (\n\t\t\t\t\tcontainerRef.current &&\n\t\t\t\t\t!containerRef.current.contains(e.target as Node)\n\t\t\t\t) {\n\t\t\t\t\tsetIsFocused(false);\n\t\t\t\t}\n\t\t\t};\n\t\t\tdocument.addEventListener(\"mousedown\", handleClickOutside);\n\t\t\treturn () =>\n\t\t\t\tdocument.removeEventListener(\"mousedown\", handleClickOutside);\n\t\t}, [isFocused]);\n\n\t\tconst handleFocus = useCallback(() => {\n\t\t\tsetIsFocused(true);\n\t\t}, []);\n\n\t\treturn (\n\t\t\t<div\n\t\t\t\tref={containerRef}\n\t\t\t\tstyle={{ ...cssVars, width }}\n\t\t\t\tdata-waniwani-chat=\"\"\n\t\t\t\tdata-waniwani-layout=\"bar\"\n\t\t\t\t{...(isDark ? { \"data-waniwani-dark\": \"\" } : {})}\n\t\t\t\tclassName=\"ww:flex ww:flex-col ww:font-[family-name:var(--ww-font)] ww:text-foreground\"\n\t\t\t>\n\t\t\t\t{/* Messages panel — fades up on expand */}\n\t\t\t\t<div\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"ww:overflow-hidden ww:bg-background/80 ww:backdrop-blur-xl ww:transition-all ww:duration-300 ww:ease-out\",\n\t\t\t\t\t\tisExpanded\n\t\t\t\t\t\t\t? \"ww:opacity-100 ww:translate-y-0\"\n\t\t\t\t\t\t\t: \"ww:opacity-0 ww:translate-y-2 ww:pointer-events-none ww:max-h-0\",\n\t\t\t\t\t)}\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\t...(isExpanded ? { maxHeight: expandedHeight } : undefined),\n\t\t\t\t\t\tmaskImage:\n\t\t\t\t\t\t\t\"linear-gradient(to bottom, transparent, black 24px, black calc(100% - 16px), transparent), linear-gradient(to right, transparent, black 16px, black calc(100% - 16px), transparent)\",\n\t\t\t\t\t\tmaskComposite: \"intersect\",\n\t\t\t\t\t\tWebkitMaskImage:\n\t\t\t\t\t\t\t\"linear-gradient(to bottom, transparent, black 24px, black calc(100% - 16px), transparent), linear-gradient(to right, transparent, black 16px, black calc(100% - 16px), transparent)\",\n\t\t\t\t\t\tWebkitMaskComposite: \"source-in\",\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<Conversation\n\t\t\t\t\t\tclassName=\"ww:flex-1\"\n\t\t\t\t\t\tstyle={{ height: expandedHeight }}\n\t\t\t\t\t>\n\t\t\t\t\t\t<ConversationContent>\n\t\t\t\t\t\t\t<MessageList\n\t\t\t\t\t\t\t\tmessages={engine.messages}\n\t\t\t\t\t\t\t\tstatus={engine.status}\n\t\t\t\t\t\t\t\twelcomeMessage={welcomeMessage}\n\t\t\t\t\t\t\t\tresourceEndpoint={effectiveResourceEndpoint}\n\t\t\t\t\t\t\t\tisDark={isDark}\n\t\t\t\t\t\t\t\tonFollowUp={handleWidgetMessage}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</ConversationContent>\n\t\t\t\t\t\t<ConversationScrollButton />\n\t\t\t\t\t</Conversation>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Suggestions */}\n\t\t\t\t<Suggestions\n\t\t\t\t\tsuggestions={suggestionsState.suggestions}\n\t\t\t\t\tisLoading={suggestionsState.isLoading}\n\t\t\t\t\tonSelect={handleSuggestionSelect}\n\t\t\t\t/>\n\n\t\t\t\t{/* Input bar — always visible */}\n\t\t\t\t<div className=\"ww:shrink-0\">\n\t\t\t\t\t<PromptInput\n\t\t\t\t\t\tonSubmit={engine.handleSubmit}\n\t\t\t\t\t\tglobalDrop={allowAttachments}\n\t\t\t\t\t\tmultiple={allowAttachments}\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"ww:rounded-[var(--ww-radius)] ww:shadow-sm ww:transition-all ww:duration-300 ww:ease-out\",\n\t\t\t\t\t\t\tisHighlighted &&\n\t\t\t\t\t\t\t\t\"ww:ring-2 ww:ring-blue-400/70 ww:ring-offset-2 ww:ring-offset-background\",\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"ww:flex ww:items-center ww:gap-1 ww:px-3 ww:py-2\">\n\t\t\t\t\t\t\t{allowAttachments && <PromptInputAddAttachments />}\n\t\t\t\t\t\t\t<PromptInputTextarea\n\t\t\t\t\t\t\t\tonChange={engine.handleTextChange}\n\t\t\t\t\t\t\t\tvalue={engine.text}\n\t\t\t\t\t\t\t\tplaceholder={animatedPlaceholder}\n\t\t\t\t\t\t\t\tonFocus={handleFocus}\n\t\t\t\t\t\t\t\tclassName=\"ww:min-h-0 ww:py-1.5 ww:px-2\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<PromptInputSubmit status={engine.status} />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</PromptInput>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t},\n);\n","\"use client\";\n\nimport { ArrowDownIcon } from \"lucide-react\";\nimport type { ComponentProps } from \"react\";\nimport { useCallback } from \"react\";\nimport { StickToBottom, useStickToBottomContext } from \"use-stick-to-bottom\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"../ui/button\";\n\nexport type ConversationProps = ComponentProps<typeof StickToBottom>;\n\nexport const Conversation = ({ className, ...props }: ConversationProps) => (\n\t<StickToBottom\n\t\tclassName={cn(\"ww:relative ww:flex-1 ww:overflow-y-hidden\", className)}\n\t\tinitial=\"smooth\"\n\t\tresize=\"smooth\"\n\t\trole=\"log\"\n\t\t{...props}\n\t/>\n);\n\nexport type ConversationContentProps = ComponentProps<\n\ttypeof StickToBottom.Content\n>;\n\nexport const ConversationContent = ({\n\tclassName,\n\t...props\n}: ConversationContentProps) => (\n\t<StickToBottom.Content\n\t\tclassName={cn(\"ww:flex ww:flex-col ww:gap-8 ww:p-4\", className)}\n\t\t{...props}\n\t/>\n);\n\nexport type ConversationScrollButtonProps = ComponentProps<typeof Button>;\n\nexport const ConversationScrollButton = ({\n\tclassName,\n\t...props\n}: ConversationScrollButtonProps) => {\n\tconst { isAtBottom, scrollToBottom } = useStickToBottomContext();\n\n\tconst handleScrollToBottom = useCallback(() => {\n\t\tscrollToBottom();\n\t}, [scrollToBottom]);\n\n\treturn (\n\t\t!isAtBottom && (\n\t\t\t<Button\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"ww:absolute ww:bottom-4 ww:left-[50%] ww:translate-x-[-50%] ww:rounded-full\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tonClick={handleScrollToBottom}\n\t\t\t\tsize=\"icon\"\n\t\t\t\tvariant=\"outline\"\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t<ArrowDownIcon className=\"ww:size-4\" />\n\t\t\t</Button>\n\t\t)\n\t);\n};\n","import { type ClassValue, clsx } from \"clsx\";\nimport { extendTailwindMerge } from \"tailwind-merge\";\n\nconst twMerge = extendTailwindMerge({ prefix: \"ww\" });\n\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport type { ComponentProps } from \"react\";\nimport { cn } from \"../lib/utils\";\n\nexport type ButtonProps = ComponentProps<\"button\"> & {\n\tvariant?: \"default\" | \"outline\" | \"ghost\";\n\tsize?: \"default\" | \"sm\" | \"icon\" | \"icon-sm\";\n};\n\nexport const Button = ({\n\tclassName,\n\tvariant = \"default\",\n\tsize = \"default\",\n\ttype = \"button\",\n\t...props\n}: ButtonProps) => (\n\t<button\n\t\ttype={type}\n\t\tclassName={cn(\n\t\t\t\"ww:inline-flex ww:cursor-pointer ww:items-center ww:justify-center ww:rounded-md ww:font-medium ww:transition-colors ww:disabled:pointer-events-none ww:disabled:opacity-50\",\n\t\t\tvariant === \"default\" &&\n\t\t\t\t\"ww:bg-primary ww:text-primary-foreground ww:hover:bg-primary/90\",\n\t\t\tvariant === \"outline\" &&\n\t\t\t\t\"ww:border ww:border-border ww:bg-background ww:hover:bg-accent ww:hover:text-accent-foreground\",\n\t\t\tvariant === \"ghost\" &&\n\t\t\t\t\"ww:hover:bg-accent ww:hover:text-accent-foreground\",\n\t\t\tsize === \"default\" && \"ww:h-9 ww:px-4 ww:py-2 ww:text-sm\",\n\t\t\tsize === \"sm\" && \"ww:h-8 ww:px-3 ww:text-xs\",\n\t\t\tsize === \"icon\" && \"ww:size-9\",\n\t\t\tsize === \"icon-sm\" && \"ww:size-7\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n);\n","\"use client\";\n\nimport type { ChatStatus, FileUIPart } from \"ai\";\nimport {\n\tArrowUpIcon,\n\tLoaderIcon,\n\tPaperclipIcon,\n\tSquareIcon,\n\tXIcon,\n} from \"lucide-react\";\nimport { nanoid } from \"nanoid\";\nimport type {\n\tChangeEvent,\n\tClipboardEventHandler,\n\tComponentProps,\n\tFormEvent,\n\tFormEventHandler,\n\tHTMLAttributes,\n\tKeyboardEventHandler,\n} from \"react\";\nimport {\n\tcreateContext,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"../ui/button\";\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nconst convertBlobUrlToDataUrl = async (url: string): Promise<string | null> => {\n\ttry {\n\t\tconst response = await fetch(url);\n\t\tconst blob = await response.blob();\n\t\treturn new Promise((resolve) => {\n\t\t\tconst reader = new FileReader();\n\t\t\treader.onloadend = () => resolve(reader.result as string);\n\t\t\treader.onerror = () => resolve(null);\n\t\t\treader.readAsDataURL(blob);\n\t\t});\n\t} catch {\n\t\treturn null;\n\t}\n};\n\n// ============================================================================\n// Attachments Context\n// ============================================================================\n\nexport interface AttachmentsContext {\n\tfiles: (FileUIPart & { id: string })[];\n\tadd: (files: File[] | FileList) => void;\n\tremove: (id: string) => void;\n\tclear: () => void;\n\topenFileDialog: () => void;\n}\n\nconst LocalAttachmentsContext = createContext<AttachmentsContext | null>(null);\n\nexport const usePromptInputAttachments = () => {\n\tconst context = useContext(LocalAttachmentsContext);\n\tif (!context) {\n\t\tthrow new Error(\n\t\t\t\"usePromptInputAttachments must be used within a PromptInput\",\n\t\t);\n\t}\n\treturn context;\n};\n\n// ============================================================================\n// PromptInput Message Type\n// ============================================================================\n\nexport interface PromptInputMessage {\n\ttext: string;\n\tfiles: FileUIPart[];\n}\n\n// ============================================================================\n// PromptInput\n// ============================================================================\n\nexport type PromptInputProps = Omit<\n\tHTMLAttributes<HTMLFormElement>,\n\t\"onSubmit\"\n> & {\n\taccept?: string;\n\tmultiple?: boolean;\n\tglobalDrop?: boolean;\n\tmaxFiles?: number;\n\tmaxFileSize?: number;\n\tonSubmit: (\n\t\tmessage: PromptInputMessage,\n\t\tevent: FormEvent<HTMLFormElement>,\n\t) => void | Promise<void>;\n};\n\nexport const PromptInput = ({\n\tclassName,\n\taccept,\n\tmultiple,\n\tglobalDrop,\n\tmaxFiles,\n\tmaxFileSize,\n\tonSubmit,\n\tchildren,\n\t...props\n}: PromptInputProps) => {\n\tconst inputRef = useRef<HTMLInputElement | null>(null);\n\tconst formRef = useRef<HTMLFormElement | null>(null);\n\tconst [items, setItems] = useState<(FileUIPart & { id: string })[]>([]);\n\tconst filesRef = useRef(items);\n\n\tuseEffect(() => {\n\t\tfilesRef.current = items;\n\t}, [items]);\n\n\tconst openFileDialog = useCallback(() => {\n\t\tinputRef.current?.click();\n\t}, []);\n\n\tconst add = useCallback(\n\t\t(fileList: File[] | FileList) => {\n\t\t\tconst incoming = [...fileList];\n\t\t\tif (incoming.length === 0) return;\n\n\t\t\tconst withinSize = (f: File) =>\n\t\t\t\tmaxFileSize ? f.size <= maxFileSize : true;\n\t\t\tconst valid = incoming.filter(withinSize);\n\n\t\t\tsetItems((prev) => {\n\t\t\t\tconst capacity =\n\t\t\t\t\ttypeof maxFiles === \"number\"\n\t\t\t\t\t\t? Math.max(0, maxFiles - prev.length)\n\t\t\t\t\t\t: undefined;\n\t\t\t\tconst capped =\n\t\t\t\t\ttypeof capacity === \"number\" ? valid.slice(0, capacity) : valid;\n\t\t\t\treturn [\n\t\t\t\t\t...prev,\n\t\t\t\t\t...capped.map((file) => ({\n\t\t\t\t\t\tfilename: file.name,\n\t\t\t\t\t\tid: nanoid(),\n\t\t\t\t\t\tmediaType: file.type,\n\t\t\t\t\t\ttype: \"file\" as const,\n\t\t\t\t\t\turl: URL.createObjectURL(file),\n\t\t\t\t\t})),\n\t\t\t\t];\n\t\t\t});\n\t\t},\n\t\t[maxFiles, maxFileSize],\n\t);\n\n\tconst remove = useCallback((id: string) => {\n\t\tsetItems((prev) => {\n\t\t\tconst found = prev.find((f) => f.id === id);\n\t\t\tif (found?.url) URL.revokeObjectURL(found.url);\n\t\t\treturn prev.filter((f) => f.id !== id);\n\t\t});\n\t}, []);\n\n\tconst clear = useCallback(() => {\n\t\tsetItems((prev) => {\n\t\t\tfor (const f of prev) {\n\t\t\t\tif (f.url) URL.revokeObjectURL(f.url);\n\t\t\t}\n\t\t\treturn [];\n\t\t});\n\t}, []);\n\n\t// Cleanup blob URLs on unmount\n\tuseEffect(\n\t\t() => () => {\n\t\t\tfor (const f of filesRef.current) {\n\t\t\t\tif (f.url) URL.revokeObjectURL(f.url);\n\t\t\t}\n\t\t},\n\t\t[],\n\t);\n\n\tconst handleChange = useCallback(\n\t\t(event: ChangeEvent<HTMLInputElement>) => {\n\t\t\tif (event.currentTarget.files) {\n\t\t\t\tadd(event.currentTarget.files);\n\t\t\t}\n\t\t\tevent.currentTarget.value = \"\";\n\t\t},\n\t\t[add],\n\t);\n\n\t// Global drop support\n\tuseEffect(() => {\n\t\tif (!globalDrop) return;\n\t\tconst onDragOver = (e: DragEvent) => {\n\t\t\tif (e.dataTransfer?.types?.includes(\"Files\")) e.preventDefault();\n\t\t};\n\t\tconst onDrop = (e: DragEvent) => {\n\t\t\tif (e.dataTransfer?.types?.includes(\"Files\")) e.preventDefault();\n\t\t\tif (e.dataTransfer?.files && e.dataTransfer.files.length > 0) {\n\t\t\t\tadd(e.dataTransfer.files);\n\t\t\t}\n\t\t};\n\t\tdocument.addEventListener(\"dragover\", onDragOver);\n\t\tdocument.addEventListener(\"drop\", onDrop);\n\t\treturn () => {\n\t\t\tdocument.removeEventListener(\"dragover\", onDragOver);\n\t\t\tdocument.removeEventListener(\"drop\", onDrop);\n\t\t};\n\t}, [add, globalDrop]);\n\n\tconst handleSubmit: FormEventHandler<HTMLFormElement> = useCallback(\n\t\tasync (event) => {\n\t\t\tevent.preventDefault();\n\t\t\tconst form = event.currentTarget;\n\t\t\tconst formData = new FormData(form);\n\t\t\tconst text = (formData.get(\"message\") as string) || \"\";\n\n\t\t\tform.reset();\n\n\t\t\tconst convertedFiles: FileUIPart[] = await Promise.all(\n\t\t\t\titems.map(async ({ id: _id, ...item }) => {\n\t\t\t\t\tif (item.url?.startsWith(\"blob:\")) {\n\t\t\t\t\t\tconst dataUrl = await convertBlobUrlToDataUrl(item.url);\n\t\t\t\t\t\treturn { ...item, url: dataUrl ?? item.url };\n\t\t\t\t\t}\n\t\t\t\t\treturn item;\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\ttry {\n\t\t\t\tconst result = onSubmit({ files: convertedFiles, text }, event);\n\t\t\t\tif (result instanceof Promise) {\n\t\t\t\t\tawait result;\n\t\t\t\t}\n\t\t\t\tclear();\n\t\t\t} catch {\n\t\t\t\t// Don't clear on error\n\t\t\t}\n\t\t},\n\t\t[items, onSubmit, clear],\n\t);\n\n\tconst attachmentsCtx = useMemo<AttachmentsContext>(\n\t\t() => ({\n\t\t\tadd,\n\t\t\tclear,\n\t\t\tfiles: items,\n\t\t\topenFileDialog,\n\t\t\tremove,\n\t\t}),\n\t\t[items, add, remove, clear, openFileDialog],\n\t);\n\n\treturn (\n\t\t<LocalAttachmentsContext.Provider value={attachmentsCtx}>\n\t\t\t<input\n\t\t\t\taccept={accept}\n\t\t\t\taria-label=\"Upload files\"\n\t\t\t\tclassName=\"ww:hidden\"\n\t\t\t\tmultiple={multiple}\n\t\t\t\tonChange={handleChange}\n\t\t\t\tref={inputRef}\n\t\t\t\ttitle=\"Upload files\"\n\t\t\t\ttype=\"file\"\n\t\t\t/>\n\t\t\t<form\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"ww:flex ww:w-full ww:flex-col ww:rounded-lg ww:border ww:border-border ww:bg-background\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tonSubmit={handleSubmit}\n\t\t\t\tref={formRef}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t</form>\n\t\t</LocalAttachmentsContext.Provider>\n\t);\n};\n\n// ============================================================================\n// Layout Components\n// ============================================================================\n\nexport type PromptInputHeaderProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputHeader = ({\n\tclassName,\n\t...props\n}: PromptInputHeaderProps) => (\n\t<div\n\t\tclassName={cn(\"ww:flex ww:flex-wrap ww:gap-1 ww:px-3 ww:pt-3\", className)}\n\t\t{...props}\n\t/>\n);\n\nexport type PromptInputBodyProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputBody = ({\n\tclassName,\n\t...props\n}: PromptInputBodyProps) => (\n\t<div className={cn(\"ww:contents\", className)} {...props} />\n);\n\nexport type PromptInputFooterProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputFooter = ({\n\tclassName,\n\t...props\n}: PromptInputFooterProps) => (\n\t<div\n\t\tclassName={cn(\n\t\t\t\"ww:flex ww:items-center ww:justify-between ww:gap-1 ww:px-3 ww:pb-3\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n);\n\nexport type PromptInputToolsProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputTools = ({\n\tclassName,\n\t...props\n}: PromptInputToolsProps) => (\n\t<div\n\t\tclassName={cn(\"ww:flex ww:min-w-0 ww:items-center ww:gap-1\", className)}\n\t\t{...props}\n\t/>\n);\n\n// ============================================================================\n// Textarea\n// ============================================================================\n\nexport type PromptInputTextareaProps = ComponentProps<\"textarea\">;\n\nexport const PromptInputTextarea = ({\n\tonChange,\n\tonKeyDown,\n\tclassName,\n\tplaceholder = \"What would you like to know?\",\n\t...props\n}: PromptInputTextareaProps) => {\n\tconst attachments = usePromptInputAttachments();\n\tconst [isComposing, setIsComposing] = useState(false);\n\n\tconst handleKeyDown: KeyboardEventHandler<HTMLTextAreaElement> = useCallback(\n\t\t(e) => {\n\t\t\tonKeyDown?.(e);\n\t\t\tif (e.defaultPrevented) return;\n\n\t\t\tif (e.key === \"Enter\") {\n\t\t\t\tif (isComposing || e.nativeEvent.isComposing) return;\n\t\t\t\tif (e.shiftKey) return;\n\t\t\t\te.preventDefault();\n\n\t\t\t\tconst { form } = e.currentTarget;\n\t\t\t\tconst submitButton = form?.querySelector(\n\t\t\t\t\t'button[type=\"submit\"]',\n\t\t\t\t) as HTMLButtonElement | null;\n\t\t\t\tif (submitButton?.disabled) return;\n\t\t\t\tform?.requestSubmit();\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\te.key === \"Backspace\" &&\n\t\t\t\te.currentTarget.value === \"\" &&\n\t\t\t\tattachments.files.length > 0\n\t\t\t) {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst lastAttachment = attachments.files.at(-1);\n\t\t\t\tif (lastAttachment) attachments.remove(lastAttachment.id);\n\t\t\t}\n\t\t},\n\t\t[onKeyDown, isComposing, attachments],\n\t);\n\n\tconst handlePaste: ClipboardEventHandler<HTMLTextAreaElement> = useCallback(\n\t\t(event) => {\n\t\t\tconst items = event.clipboardData?.items;\n\t\t\tif (!items) return;\n\n\t\t\tconst files: File[] = [];\n\t\t\tfor (const item of items) {\n\t\t\t\tif (item.kind === \"file\") {\n\t\t\t\t\tconst file = item.getAsFile();\n\t\t\t\t\tif (file) files.push(file);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (files.length > 0) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tattachments.add(files);\n\t\t\t}\n\t\t},\n\t\t[attachments],\n\t);\n\n\treturn (\n\t\t<textarea\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:field-sizing-content ww:max-h-48 ww:min-h-16 ww:w-full ww:resize-none ww:border-0 ww:bg-transparent ww:px-3 ww:py-3 ww:text-sm ww:outline-none ww:placeholder:text-muted-foreground\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\tname=\"message\"\n\t\t\tonCompositionEnd={() => setIsComposing(false)}\n\t\t\tonCompositionStart={() => setIsComposing(true)}\n\t\t\tonKeyDown={handleKeyDown}\n\t\t\tonPaste={handlePaste}\n\t\t\tplaceholder={placeholder}\n\t\t\tonChange={onChange}\n\t\t\t{...props}\n\t\t/>\n\t);\n};\n\n// ============================================================================\n// Submit Button\n// ============================================================================\n\nexport type PromptInputSubmitProps = ComponentProps<typeof Button> & {\n\tstatus?: ChatStatus;\n\tonStop?: () => void;\n};\n\nexport const PromptInputSubmit = ({\n\tclassName,\n\tstatus,\n\tonStop,\n\tonClick,\n\tchildren,\n\t...props\n}: PromptInputSubmitProps) => {\n\tconst isGenerating = status === \"submitted\" || status === \"streaming\";\n\n\tlet Icon = <ArrowUpIcon className=\"ww:size-4\" />;\n\tif (status === \"submitted\") {\n\t\tIcon = <LoaderIcon className=\"ww:size-4 ww:animate-spin\" />;\n\t} else if (status === \"streaming\") {\n\t\tIcon = <SquareIcon className=\"ww:size-4\" />;\n\t}\n\n\tconst handleClick = useCallback(\n\t\t(e: React.MouseEvent<HTMLButtonElement>) => {\n\t\t\tif (isGenerating && onStop) {\n\t\t\t\te.preventDefault();\n\t\t\t\tonStop();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tonClick?.(e);\n\t\t},\n\t\t[isGenerating, onStop, onClick],\n\t);\n\n\treturn (\n\t\t<Button\n\t\t\taria-label={isGenerating ? \"Stop\" : \"Submit\"}\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:bg-foreground ww:text-background ww:hover:bg-foreground\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\tonClick={handleClick}\n\t\t\tsize=\"icon-sm\"\n\t\t\ttype={isGenerating && onStop ? \"button\" : \"submit\"}\n\t\t\tvariant=\"ghost\"\n\t\t\t{...props}\n\t\t>\n\t\t\t{children ?? Icon}\n\t\t</Button>\n\t);\n};\n\n// ============================================================================\n// Attachment Add Button (simple file picker, no Radix dropdown)\n// ============================================================================\n\nexport type PromptInputAddAttachmentsProps = ComponentProps<typeof Button>;\n\nexport const PromptInputAddAttachments = ({\n\tclassName,\n\tchildren,\n\t...props\n}: PromptInputAddAttachmentsProps) => {\n\tconst attachments = usePromptInputAttachments();\n\tconst hasFiles = attachments.files.length > 0;\n\n\tif (hasFiles) {\n\t\treturn (\n\t\t\t<Button\n\t\t\t\tclassName={cn(\"ww:group ww:relative\", className)}\n\t\t\t\tonClick={() => attachments.clear()}\n\t\t\t\tsize=\"icon-sm\"\n\t\t\t\ttype=\"button\"\n\t\t\t\tvariant=\"ghost\"\n\t\t\t\taria-label=\"Remove all attachments\"\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t<span className=\"ww:flex ww:size-5 ww:items-center ww:justify-center ww:rounded-full ww:bg-primary ww:text-[10px] ww:font-medium ww:text-primary-foreground ww:transition-opacity ww:group-hover:opacity-0\">\n\t\t\t\t\t{attachments.files.length}\n\t\t\t\t</span>\n\t\t\t\t<XIcon className=\"ww:absolute ww:size-4 ww:opacity-0 ww:transition-opacity ww:group-hover:opacity-100\" />\n\t\t\t</Button>\n\t\t);\n\t}\n\n\treturn (\n\t\t<Button\n\t\t\tclassName={cn(className)}\n\t\t\tonClick={() => attachments.openFileDialog()}\n\t\t\tsize=\"icon-sm\"\n\t\t\ttype=\"button\"\n\t\t\tvariant=\"ghost\"\n\t\t\t{...props}\n\t\t>\n\t\t\t{children ?? <PaperclipIcon className=\"ww:size-4\" />}\n\t\t</Button>\n\t);\n};\n","\"use client\";\n\nimport type { FileUIPart } from \"ai\";\nimport { FileIcon } from \"lucide-react\";\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"../lib/utils\";\n\n// ============================================================================\n// Attachments (inline list for chat bubbles)\n// ============================================================================\n\nexport type AttachmentsProps = HTMLAttributes<HTMLDivElement> & {\n\tfiles: FileUIPart[];\n};\n\nexport const Attachments = ({\n\tfiles,\n\tclassName,\n\t...props\n}: AttachmentsProps) => {\n\tif (files.length === 0) return null;\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\"ww:flex ww:flex-wrap ww:gap-1.5\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{files.map((file, i) => (\n\t\t\t\t<AttachmentItem key={i} file={file} />\n\t\t\t))}\n\t\t</div>\n\t);\n};\n\n// ============================================================================\n// AttachmentItem\n// ============================================================================\n\nfunction AttachmentItem({ file }: { file: FileUIPart }) {\n\tconst isImage = file.mediaType?.startsWith(\"image/\");\n\n\tif (isImage && file.url) {\n\t\treturn (\n\t\t\t<img\n\t\t\t\tsrc={file.url}\n\t\t\t\talt={file.filename ?? \"attachment\"}\n\t\t\t\tclassName=\"ww:h-16 ww:max-w-32 ww:rounded ww:object-cover\"\n\t\t\t/>\n\t\t);\n\t}\n\n\treturn (\n\t\t<span className=\"ww:inline-flex ww:items-center ww:gap-1.5 ww:rounded ww:bg-background/20 ww:px-2 ww:py-1 ww:text-xs\">\n\t\t\t<FileIcon className=\"ww:size-3 ww:shrink-0\" />\n\t\t\t<span className=\"ww:max-w-24 ww:truncate\">{file.filename ?? \"file\"}</span>\n\t\t</span>\n\t);\n}\n","\"use client\";\n\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"../lib/utils\";\n\nexport type LoaderProps = HTMLAttributes<HTMLDivElement> & {\n\tsize?: number;\n};\n\nexport const Loader = ({ className, size = 5, ...props }: LoaderProps) => (\n\t<div className={cn(\"ww:flex ww:items-center ww:gap-1\", className)} {...props}>\n\t\t{[0, 1, 2].map((i) => (\n\t\t\t<div\n\t\t\t\tkey={i}\n\t\t\t\tclassName=\"ww:rounded-full ww:bg-muted-foreground/60\"\n\t\t\t\tstyle={{\n\t\t\t\t\twidth: size,\n\t\t\t\t\theight: size,\n\t\t\t\t\tanimation: \"ww-pulse 1.4s ease-in-out infinite\",\n\t\t\t\t\tanimationDelay: `${i * 0.2}s`,\n\t\t\t\t}}\n\t\t\t/>\n\t\t))}\n\t</div>\n);\n","\"use client\";\n\nimport { cjk } from \"@streamdown/cjk\";\nimport { code } from \"@streamdown/code\";\nimport type { UIMessage } from \"ai\";\nimport type { ComponentProps, HTMLAttributes } from \"react\";\nimport { memo } from \"react\";\nimport { Streamdown } from \"streamdown\";\nimport { cn } from \"../lib/utils\";\n\nexport type MessageProps = HTMLAttributes<HTMLDivElement> & {\n\tfrom: UIMessage[\"role\"];\n};\n\nexport const Message = ({ className, from, ...props }: MessageProps) => (\n\t<div\n\t\tclassName={cn(\n\t\t\t\"ww:group ww:flex ww:w-full ww:max-w-[95%] ww:flex-col ww:gap-2\",\n\t\t\tfrom === \"user\" ? \"is-user ww:ml-auto ww:justify-end\" : \"is-assistant\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n);\n\nexport type MessageContentProps = HTMLAttributes<HTMLDivElement>;\n\nexport const MessageContent = ({\n\tchildren,\n\tclassName,\n\t...props\n}: MessageContentProps) => (\n\t<div\n\t\tclassName={cn(\n\t\t\t\"ww:flex ww:w-fit ww:min-w-0 ww:max-w-full ww:flex-col ww:gap-2 ww:overflow-hidden ww:text-base\",\n\t\t\t\"ww:group-[.is-user]:ml-auto ww:group-[.is-user]:rounded-lg ww:group-[.is-user]:bg-user-bubble ww:group-[.is-user]:px-4 ww:group-[.is-user]:py-3 ww:group-[.is-user]:text-primary-foreground\",\n\t\t\t\"ww:group-[.is-assistant]:text-foreground\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t>\n\t\t{children}\n\t</div>\n);\n\nexport type MessageResponseProps = ComponentProps<typeof Streamdown>;\n\nconst streamdownPlugins = { cjk, code };\n\nexport const MessageResponse = memo(\n\t({ className, ...props }: MessageResponseProps) => (\n\t\t<Streamdown\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:size-full ww:[&>*:first-child]:mt-0 ww:[&>*:last-child]:mb-0\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\tplugins={streamdownPlugins}\n\t\t\t{...props}\n\t\t/>\n\t),\n\t(prevProps, nextProps) => prevProps.children === nextProps.children,\n);\n\nMessageResponse.displayName = \"MessageResponse\";\n","\"use client\";\n\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"../lib/utils\";\n\nexport type ReasoningProps = HTMLAttributes<HTMLPreElement> & {\n\ttext: string;\n};\n\n/** Displays reasoning text inline with muted styling. */\nexport function Reasoning({ className, text, ...props }: ReasoningProps) {\n\tif (!text) return null;\n\n\treturn (\n\t\t<pre\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:mb-2 ww:overflow-x-auto ww:whitespace-pre-wrap ww:break-words ww:text-xs ww:font-mono ww:text-muted-foreground\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{text}\n\t\t</pre>\n\t);\n}\n","\"use client\";\n\nimport type { ToolUIPart } from \"ai\";\nimport {\n\tBracesIcon,\n\tCheckIcon,\n\tChevronDownIcon,\n\tChevronRightIcon,\n\tClipboardCopyIcon,\n\tServerIcon,\n} from \"lucide-react\";\nimport type { HTMLAttributes } from \"react\";\nimport {\n\tcreateContext,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"../ui/button\";\n\n/**\n * Produces an abbreviated single-line JSON preview for collapsed display.\n * Objects show truncated keys/values: `{ci… 'M…, pos… '2…, squa… 80}`\n * Arrays show their length: `Array(13)`\n */\nfunction truncateJSON(data: unknown, maxLength = 80): string {\n\tif (data === null || data === undefined) return String(data);\n\tif (typeof data !== \"object\") return String(data).slice(0, maxLength);\n\n\tif (Array.isArray(data)) {\n\t\treturn `Array(${data.length})`;\n\t}\n\n\tconst stringified = JSON.stringify(data);\n\tif (stringified.length <= maxLength) return stringified;\n\n\tconst entries = Object.entries(data as Record<string, unknown>);\n\tconst parts: string[] = [];\n\tlet remaining = maxLength - 2;\n\n\tfor (const [key, value] of entries) {\n\t\tif (remaining <= 8) break;\n\t\tconst keyAbbrev = key.length > 4 ? `${key.slice(0, 4)}\\u2026` : key;\n\t\tlet valStr: string;\n\t\tif (typeof value === \"string\") {\n\t\t\tvalStr = value.length > 2 ? `'${value.slice(0, 1)}\\u2026` : `'${value}'`;\n\t\t} else if (Array.isArray(value)) {\n\t\t\tvalStr = `Array(${value.length})`;\n\t\t} else if (typeof value === \"object\" && value !== null) {\n\t\t\tvalStr = `{\\u2026}`;\n\t\t} else {\n\t\t\tvalStr = String(value);\n\t\t}\n\t\tconst part = `${keyAbbrev}\\u2009${valStr}`;\n\t\tparts.push(part);\n\t\tremaining -= part.length + 3;\n\t}\n\n\treturn `{${parts.join(\", \")}}`;\n}\n\ninterface CopyButtonProps {\n\ttext: string;\n\tclassName?: string;\n}\n\n/** Ghost button that copies `text` to clipboard, showing \"Copied\" for 2s. */\nfunction CopyButton({ text, className }: CopyButtonProps) {\n\tconst [copied, setCopied] = useState(false);\n\tconst timeoutRef = useRef<ReturnType<typeof setTimeout>>(null);\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (timeoutRef.current) {\n\t\t\t\tclearTimeout(timeoutRef.current);\n\t\t\t}\n\t\t};\n\t}, []);\n\n\tconst handleCopy = useCallback(\n\t\tasync (e: React.MouseEvent) => {\n\t\t\te.stopPropagation();\n\t\t\ttry {\n\t\t\t\tawait navigator.clipboard.writeText(text);\n\t\t\t\tsetCopied(true);\n\t\t\t\tif (timeoutRef.current) {\n\t\t\t\t\tclearTimeout(timeoutRef.current);\n\t\t\t\t}\n\t\t\t\ttimeoutRef.current = setTimeout(() => setCopied(false), 2000);\n\t\t\t} catch {\n\t\t\t\t// Clipboard API not available\n\t\t\t}\n\t\t},\n\t\t[text],\n\t);\n\n\treturn (\n\t\t<Button\n\t\t\tvariant=\"ghost\"\n\t\t\tsize=\"sm\"\n\t\t\tonClick={handleCopy}\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:h-auto ww:gap-1 ww:px-1.5 ww:py-0.5 ww:text-xs ww:text-muted-foreground ww:hover:text-foreground\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t>\n\t\t\t{copied ? (\n\t\t\t\t<>\n\t\t\t\t\t<CheckIcon className=\"ww:size-3.5\" />\n\t\t\t\t\t<span>Copied</span>\n\t\t\t\t</>\n\t\t\t) : (\n\t\t\t\t<>\n\t\t\t\t\t<ClipboardCopyIcon className=\"ww:size-3.5\" />\n\t\t\t\t\t<span>Copy</span>\n\t\t\t\t</>\n\t\t\t)}\n\t\t</Button>\n\t);\n}\n\ninterface CollapsibleJSONProps extends HTMLAttributes<HTMLDivElement> {\n\tdata: unknown;\n\tlabel: string;\n}\n\n/**\n * Labeled JSON section with a Copy button and a collapsible preview.\n * Collapsed: shows a truncated single-line abbreviation with a `>` chevron.\n * Expanded: rotates the chevron and shows full pretty-printed JSON.\n */\nfunction CollapsibleJSON({\n\tdata,\n\tlabel,\n\tclassName,\n\t...props\n}: CollapsibleJSONProps) {\n\tconst [expanded, setExpanded] = useState(false);\n\tconst fullJSON = useMemo(() => JSON.stringify(data, null, 2), [data]);\n\tconst preview = truncateJSON(data);\n\n\treturn (\n\t\t<div className={cn(\"ww:rounded-lg ww:bg-tool-card\", className)} {...props}>\n\t\t\t<div className=\"ww:flex ww:items-center ww:justify-between ww:px-3 ww:pt-2.5 ww:pb-1.5\">\n\t\t\t\t<span className=\"ww:text-xs ww:font-medium ww:text-muted-foreground\">\n\t\t\t\t\t{label}\n\t\t\t\t</span>\n\t\t\t\t<CopyButton text={fullJSON} />\n\t\t\t</div>\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tonClick={() => setExpanded((v) => !v)}\n\t\t\t\tclassName=\"ww:flex ww:w-full ww:items-start ww:gap-2 ww:px-3 ww:pb-3 ww:text-left\"\n\t\t\t>\n\t\t\t\t<ChevronRightIcon\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"ww:mt-0.5 ww:size-3.5 ww:shrink-0 ww:text-muted-foreground ww:transition-transform ww:duration-150\",\n\t\t\t\t\t\texpanded && \"ww:rotate-90\",\n\t\t\t\t\t)}\n\t\t\t\t/>\n\t\t\t\t{expanded ? (\n\t\t\t\t\t<pre className=\"ww:overflow-x-auto ww:text-xs ww:font-mono ww:text-foreground ww:whitespace-pre-wrap ww:break-all\">\n\t\t\t\t\t\t<code>{fullJSON}</code>\n\t\t\t\t\t</pre>\n\t\t\t\t) : (\n\t\t\t\t\t<span className=\"ww:truncate ww:text-xs ww:font-mono ww:text-foreground/80\">\n\t\t\t\t\t\t{preview}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t</button>\n\t\t</div>\n\t);\n}\n\nconst ToolOpenContext = createContext<{\n\topen: boolean;\n\ttoggle: () => void;\n}>({ open: false, toggle: () => {} });\n\nexport type ToolProps = HTMLAttributes<HTMLDivElement> & {\n\tdefaultOpen?: boolean;\n};\n\n/**\n * Compound component root for a tool call display.\n * Provides open/closed state via context to ToolHeader and ToolContent.\n *\n * ```tsx\n * <Tool defaultOpen>\n * <ToolHeader title=\"Price estimate ready\" state=\"output-available\" />\n * <ToolContent>\n * <ToolServerInfo toolName=\"get_price_estimate\" serverName=\"Tuio v2\" />\n * <ToolInput input={args} />\n * <ToolOutput output={result} errorText={undefined} />\n * </ToolContent>\n * </Tool>\n * ```\n */\nexport function Tool({\n\tclassName,\n\tdefaultOpen = false,\n\tchildren,\n\t...props\n}: ToolProps) {\n\tconst [open, setOpen] = useState(defaultOpen);\n\treturn (\n\t\t<ToolOpenContext.Provider\n\t\t\tvalue={{ open, toggle: () => setOpen((o) => !o) }}\n\t\t>\n\t\t\t<div\n\t\t\t\tclassName={cn(\"ww:mb-4 ww:w-full\", className)}\n\t\t\t\tdata-state={open ? \"open\" : \"closed\"}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t</div>\n\t\t</ToolOpenContext.Provider>\n\t);\n}\n\nexport type ToolHeaderProps = HTMLAttributes<HTMLButtonElement> & {\n\ttitle?: string;\n\tstate: ToolUIPart[\"state\"];\n};\n\n/** Clickable header that toggles the tool accordion. Shows a `{≡}` icon, title, and chevron. */\nexport function ToolHeader({\n\tclassName,\n\ttitle,\n\tstate,\n\t...props\n}: ToolHeaderProps) {\n\tconst { open, toggle } = useContext(ToolOpenContext);\n\tconst isRunning = state === \"input-available\" || state === \"input-streaming\";\n\n\treturn (\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\tonClick={toggle}\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:flex ww:w-full ww:items-center ww:justify-between ww:gap-3 ww:py-1.5\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\taria-expanded={open}\n\t\t\t{...props}\n\t\t>\n\t\t\t<div className=\"ww:flex ww:min-w-0 ww:items-center ww:gap-2\">\n\t\t\t\t<BracesIcon className=\"ww:size-4 ww:shrink-0 ww:text-muted-foreground\" />\n\t\t\t\t<span className=\"ww:truncate ww:text-sm ww:font-medium\">{title}</span>\n\t\t\t\t{isRunning && (\n\t\t\t\t\t<span className=\"ww:size-2 ww:shrink-0 ww:rounded-full ww:bg-primary ww:animate-pulse\" />\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t<ChevronDownIcon\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"ww:size-4 ww:shrink-0 ww:text-muted-foreground ww:transition-transform ww:duration-200\",\n\t\t\t\t\topen && \"ww:rotate-180\",\n\t\t\t\t)}\n\t\t\t/>\n\t\t</button>\n\t);\n}\n\nexport type ToolServerInfoProps = HTMLAttributes<HTMLDivElement> & {\n\tserverName?: string;\n\tserverIcon?: string;\n\ttoolName: string;\n};\n\n/** Optional MCP server identity card. Shows server icon + name and the tool function name. Renders nothing if no props need display. */\nexport function ToolServerInfo({\n\tclassName,\n\tserverName,\n\tserverIcon,\n\ttoolName,\n\t...props\n}: ToolServerInfoProps) {\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:flex ww:items-center ww:gap-3 ww:rounded-lg ww:border ww:border-border ww:px-3 ww:py-2.5\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{serverIcon ? (\n\t\t\t\t<img\n\t\t\t\t\tsrc={serverIcon}\n\t\t\t\t\talt={serverName ?? \"\"}\n\t\t\t\t\tclassName=\"ww:size-8 ww:shrink-0 ww:rounded-full ww:object-cover\"\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<div className=\"ww:flex ww:size-8 ww:shrink-0 ww:items-center ww:justify-center ww:rounded-full ww:border ww:border-border ww:bg-muted\">\n\t\t\t\t\t<ServerIcon className=\"ww:size-4 ww:text-muted-foreground\" />\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t<div className=\"ww:flex ww:min-w-0 ww:flex-col\">\n\t\t\t\t{serverName && (\n\t\t\t\t\t<span className=\"ww:text-xs ww:text-muted-foreground\">\n\t\t\t\t\t\t{serverName}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t\t<span className=\"ww:truncate ww:text-sm ww:font-semibold\">\n\t\t\t\t\t{toolName}\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport type ToolContentProps = HTMLAttributes<HTMLDivElement>;\n\n/** Collapsible body that animates open/closed. Content below smoothly pushes up/down via a grid-row height transition. */\nexport function ToolContent({\n\tclassName,\n\tchildren,\n\t...props\n}: ToolContentProps) {\n\tconst { open } = useContext(ToolOpenContext);\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:grid ww:transition-[grid-template-rows,opacity] ww:duration-200 ww:ease-out\",\n\t\t\t\topen\n\t\t\t\t\t? \"ww:grid-rows-[1fr] ww:opacity-100\"\n\t\t\t\t\t: \"ww:grid-rows-[0fr] ww:opacity-0\",\n\t\t\t)}\n\t\t>\n\t\t\t<div className=\"ww:min-h-0 ww:overflow-hidden\">\n\t\t\t\t<div\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"ww:mt-2 ww:space-y-3 ww:rounded-lg ww:border ww:border-border ww:bg-background ww:p-3\",\n\t\t\t\t\t\tclassName,\n\t\t\t\t\t)}\n\t\t\t\t\t{...props}\n\t\t\t\t>\n\t\t\t\t\t{children}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport type ToolInputProps = HTMLAttributes<HTMLDivElement> & {\n\tinput: ToolUIPart[\"input\"];\n};\n\n/** Displays the tool call request parameters as a collapsible JSON section labeled \"Request\". */\nexport function ToolInput({ className, input, ...props }: ToolInputProps) {\n\treturn (\n\t\t<CollapsibleJSON\n\t\t\tdata={input}\n\t\t\tlabel=\"Request\"\n\t\t\tclassName={className}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\nexport type ToolOutputProps = HTMLAttributes<HTMLDivElement> & {\n\toutput: ToolUIPart[\"output\"];\n\terrorText: ToolUIPart[\"errorText\"];\n};\n\nfunction getUiMeta(output: unknown): Record<string, unknown> | undefined {\n\tif (typeof output !== \"object\" || output === null) return undefined;\n\tconst meta = (output as Record<string, unknown>)._meta;\n\tif (typeof meta !== \"object\" || meta === null) return undefined;\n\tconst ui = (meta as Record<string, unknown>).ui;\n\tif (typeof ui !== \"object\" || ui === null) return undefined;\n\treturn ui as Record<string, unknown>;\n}\n\n/** Extract the MCP app resource URI from `output._meta.ui.resourceUri`, if present. */\nexport function getResourceUri(output: unknown): string | undefined {\n\tconst uri = getUiMeta(output)?.resourceUri;\n\treturn typeof uri === \"string\" ? uri : undefined;\n}\n\n/** Extract the auto-height flag from `output._meta.ui.autoHeight`, if present. */\nexport function getAutoHeight(output: unknown): boolean {\n\treturn getUiMeta(output)?.autoHeight === true;\n}\n\n/** Displays the tool call result as a collapsible JSON section labeled \"Response\", or an error block if `errorText` is set. */\nexport function ToolOutput({\n\tclassName,\n\toutput,\n\terrorText,\n\t...props\n}: ToolOutputProps) {\n\tif (!(output || errorText)) return null;\n\n\tif (errorText) {\n\t\treturn (\n\t\t\t<div className={cn(\"ww:space-y-2\", className)} {...props}>\n\t\t\t\t<h4 className=\"ww:text-xs ww:font-medium ww:uppercase ww:tracking-wide ww:text-muted-foreground\">\n\t\t\t\t\tError\n\t\t\t\t</h4>\n\t\t\t\t<div className=\"ww:rounded-lg ww:bg-destructive/10 ww:p-3 ww:text-xs ww:text-destructive\">\n\t\t\t\t\t{errorText}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<CollapsibleJSON\n\t\t\tdata={output}\n\t\t\tlabel=\"Response\"\n\t\t\tclassName={className}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { cn } from \"../lib/utils\";\n\nconst DEFAULT_RESOURCE_ENDPOINT = \"/api/mcp/resource\";\nconst MAX_HEIGHT = 500;\nconst DEFAULT_HEIGHT = 0;\nconst PROTOCOL_VERSION = \"2026-01-26\";\nconst RESIZE_ANIMATION_MS = 300;\nconst HANDSHAKE_TIMEOUT_MS = 3000;\nconst MAX_RETRIES = 3;\n\nexport interface McpAppFrameProps {\n\tresourceUri: string;\n\ttoolInput: Record<string, unknown>;\n\ttoolResult: {\n\t\tcontent?: Array<{ type: string; text?: string }>;\n\t\tstructuredContent?: Record<string, unknown>;\n\t};\n\tresourceEndpoint?: string;\n\tisDark?: boolean;\n\tclassName?: string;\n\t/** When true, the iframe height auto-adapts to its content. Set via `_meta.ui.autoHeight` in the tool result. */\n\tautoHeight?: boolean;\n\t/** Called when the view requests to open a URL */\n\tonOpenLink?: (url: string) => void;\n\t/** Called when a widget sends a follow-up message via `ui/message` */\n\tonFollowUp?: (message: {\n\t\trole: string;\n\t\tcontent: Array<{ type: string; text?: string }>;\n\t}) => void;\n}\n\nexport function McpAppFrame({\n\tresourceUri,\n\ttoolInput,\n\ttoolResult,\n\tresourceEndpoint = DEFAULT_RESOURCE_ENDPOINT,\n\tisDark = false,\n\tclassName,\n\t// TODO: REMOVE — defaulting to true for playground testing\n\tautoHeight = true,\n\tonOpenLink,\n\tonFollowUp,\n}: McpAppFrameProps) {\n\tconst iframeRef = useRef<HTMLIFrameElement>(null);\n\tconst toolInputRef = useRef(toolInput);\n\tconst toolResultRef = useRef(toolResult);\n\tconst lastSizeRef = useRef({ width: 0, height: 0 });\n\tconst animationRef = useRef<Animation | null>(null);\n\tconst initializedRef = useRef(false);\n\tconst retryCountRef = useRef(0);\n\tconst [height, setHeight] = useState(DEFAULT_HEIGHT);\n\tconst [width, setWidth] = useState<number | undefined>(undefined);\n\tconst onOpenLinkRef = useRef(onOpenLink);\n\tconst onFollowUpRef = useRef(onFollowUp);\n\n\ttoolInputRef.current = toolInput;\n\ttoolResultRef.current = toolResult;\n\tonOpenLinkRef.current = onOpenLink;\n\tonFollowUpRef.current = onFollowUp;\n\n\tconst clampHeight = useCallback(\n\t\t(h: number) => {\n\t\t\tif (autoHeight) return Math.max(h, 0);\n\t\t\treturn Math.min(Math.max(h, 50), MAX_HEIGHT);\n\t\t},\n\t\t[autoHeight],\n\t);\n\n\t// Build the iframe src URL directly — avoids null-origin issues with srcdoc\n\tconst iframeSrc = useMemo(\n\t\t() => `${resourceEndpoint}?uri=${encodeURIComponent(resourceUri)}`,\n\t\t[resourceEndpoint, resourceUri],\n\t);\n\n\tconst isDarkRef = useRef(isDark);\n\tisDarkRef.current = isDark;\n\n\t// Send theme changes to the iframe (only after handshake is complete)\n\tuseEffect(() => {\n\t\tif (!initializedRef.current) return;\n\t\tconst iframe = iframeRef.current;\n\t\tif (!iframe?.contentWindow) return;\n\n\t\tiframe.contentWindow.postMessage(\n\t\t\t{\n\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\tmethod: \"ui/notifications/host-context-changed\",\n\t\t\t\tparams: {\n\t\t\t\t\ttheme: isDark ? \"dark\" : \"light\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"*\",\n\t\t);\n\t}, [isDark]);\n\n\t// Synchronous postMessage protocol handler — no async imports, no timing issues.\n\t// Handles the MCP UI protocol (ui/initialize, notifications, etc.) directly.\n\tuseEffect(() => {\n\t\tconst iframe = iframeRef.current;\n\t\tif (!iframe) return;\n\n\t\tlet disposed = false;\n\t\tlet handshakeReceived = false;\n\t\tconst debug = (...args: unknown[]) =>\n\t\t\tconsole.debug(\"[McpAppFrame]\", ...args);\n\n\t\tdebug(\"effect mounted, waiting for handshake\");\n\n\t\t// Retry: reload iframe if handshake doesn't arrive in time\n\t\tconst handshakeTimer = setTimeout(() => {\n\t\t\tif (disposed || handshakeReceived) return;\n\t\t\tif (retryCountRef.current >= MAX_RETRIES) {\n\t\t\t\tdebug(\"handshake failed after\", MAX_RETRIES, \"retries, giving up\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tretryCountRef.current += 1;\n\t\t\tdebug(\n\t\t\t\t\"handshake timeout, reloading iframe (retry\",\n\t\t\t\tretryCountRef.current,\n\t\t\t\t\"of\",\n\t\t\t\tMAX_RETRIES,\n\t\t\t\t\")\",\n\t\t\t);\n\t\t\t// Force reload with a cache-busting param\n\t\t\tconst url = new URL(iframe.src);\n\t\t\turl.searchParams.set(\"_retry\", String(retryCountRef.current));\n\t\t\tiframe.src = url.toString();\n\t\t}, HANDSHAKE_TIMEOUT_MS);\n\n\t\tconst postToIframe = (msg: Record<string, unknown>) => {\n\t\t\tdebug(\"→ send\", msg.method ?? `response:${msg.id}`, msg);\n\t\t\tiframe.contentWindow?.postMessage(msg, \"*\");\n\t\t};\n\n\t\tconst handleMessage = (event: MessageEvent) => {\n\t\t\tif (disposed) return;\n\t\t\tif (event.source !== iframe.contentWindow) return;\n\n\t\t\tconst data = event.data;\n\t\t\tif (!data || typeof data !== \"object\" || data.jsonrpc !== \"2.0\") return;\n\n\t\t\tconst method: string | undefined = data.method;\n\t\t\tconst id: number | string | undefined = data.id;\n\n\t\t\tdebug(\"← recv\", method ?? `response:${id}`, data);\n\n\t\t\t// ui/initialize — widget requests handshake\n\t\t\tif (method === \"ui/initialize\" && id != null) {\n\t\t\t\thandshakeReceived = true;\n\t\t\t\tclearTimeout(handshakeTimer);\n\t\t\t\tdebug(\"handshake started\");\n\t\t\t\tpostToIframe({\n\t\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\t\tid,\n\t\t\t\t\tresult: {\n\t\t\t\t\t\tprotocolVersion: data.params?.protocolVersion ?? PROTOCOL_VERSION,\n\t\t\t\t\t\thostInfo: { name: \"WaniWani Chat\", version: \"1.0.0\" },\n\t\t\t\t\t\thostCapabilities: {\n\t\t\t\t\t\t\topenLinks: {},\n\t\t\t\t\t\t\tmessage: {},\n\t\t\t\t\t\t},\n\t\t\t\t\t\thostContext: {\n\t\t\t\t\t\t\ttheme: isDarkRef.current ? \"dark\" : \"light\",\n\t\t\t\t\t\t\tdisplayMode: \"inline\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ui/notifications/initialized — widget confirms init, we send tool data\n\t\t\tif (method === \"ui/notifications/initialized\") {\n\t\t\t\tdebug(\"handshake complete, sending tool data\");\n\t\t\t\tinitializedRef.current = true;\n\t\t\t\tconst input = toolInputRef.current;\n\t\t\t\tconst result = toolResultRef.current;\n\n\t\t\t\tpostToIframe({\n\t\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\t\tmethod: \"ui/notifications/tool-input\",\n\t\t\t\t\tparams: { arguments: input },\n\t\t\t\t});\n\n\t\t\t\tconst content = result.content ?? [\n\t\t\t\t\t{ type: \"text\", text: JSON.stringify(result) },\n\t\t\t\t];\n\t\t\t\tpostToIframe({\n\t\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\t\tmethod: \"ui/notifications/tool-result\",\n\t\t\t\t\tparams: {\n\t\t\t\t\t\tcontent,\n\t\t\t\t\t\tstructuredContent: result.structuredContent,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ui/notifications/size-changed — widget reports content size\n\t\t\tif (method === \"ui/notifications/size-changed\") {\n\t\t\t\tconst params = data.params;\n\t\t\t\tconst newHeight =\n\t\t\t\t\ttypeof params?.height === \"number\" ? params.height : undefined;\n\t\t\t\tconst newWidth =\n\t\t\t\t\ttypeof params?.width === \"number\" ? params.width : undefined;\n\n\t\t\t\t// Deduplicate — only update if values actually changed (prevents feedback loops)\n\t\t\t\tconst last = lastSizeRef.current;\n\t\t\t\tconst heightChanged =\n\t\t\t\t\tnewHeight !== undefined && newHeight !== last.height;\n\t\t\t\tconst widthChanged = newWidth !== undefined && newWidth !== last.width;\n\n\t\t\t\tdebug(\"size-changed\", {\n\t\t\t\t\tnewHeight,\n\t\t\t\t\tnewWidth,\n\t\t\t\t\tlastHeight: last.height,\n\t\t\t\t\tlastWidth: last.width,\n\t\t\t\t\theightChanged,\n\t\t\t\t\twidthChanged,\n\t\t\t\t});\n\n\t\t\t\tif (!heightChanged && !widthChanged) return;\n\n\t\t\t\tif (heightChanged && newHeight !== undefined) {\n\t\t\t\t\tlast.height = newHeight;\n\t\t\t\t\tconst clamped = clampHeight(newHeight);\n\n\t\t\t\t\t// Get current visual height before canceling the old animation\n\t\t\t\t\tconst from = iframe.getBoundingClientRect().height;\n\n\t\t\t\t\t// Cancel previous animation so its fill: \"forwards\" stops overriding inline style\n\t\t\t\t\tif (animationRef.current) {\n\t\t\t\t\t\tanimationRef.current.cancel();\n\t\t\t\t\t\tanimationRef.current = null;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Set the target height in React state (takes effect once no animation overrides it)\n\t\t\t\t\tsetHeight(clamped);\n\n\t\t\t\t\t// Animate the height transition\n\t\t\t\t\tif (iframe.animate && Math.abs(from - clamped) > 2) {\n\t\t\t\t\t\tconst anim = iframe.animate(\n\t\t\t\t\t\t\t[{ height: `${from}px` }, { height: `${clamped}px` }],\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tduration: RESIZE_ANIMATION_MS,\n\t\t\t\t\t\t\t\teasing: \"ease-out\",\n\t\t\t\t\t\t\t\tfill: \"forwards\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t\tanimationRef.current = anim;\n\t\t\t\t\t\t// Once done, remove the animation so the inline style is the source of truth\n\t\t\t\t\t\tanim.onfinish = () => {\n\t\t\t\t\t\t\tif (animationRef.current === anim) {\n\t\t\t\t\t\t\t\tanim.cancel();\n\t\t\t\t\t\t\t\tanimationRef.current = null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (widthChanged && autoHeight && newWidth !== undefined) {\n\t\t\t\t\tlast.width = newWidth;\n\t\t\t\t\tsetWidth(newWidth);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ui/open-link — widget requests to open a URL\n\t\t\tif (method === \"ui/open-link\" && id != null) {\n\t\t\t\tconst url = data.params?.url;\n\t\t\t\tif (typeof url === \"string\") {\n\t\t\t\t\tif (onOpenLinkRef.current) {\n\t\t\t\t\t\tonOpenLinkRef.current(url);\n\t\t\t\t\t} else {\n\t\t\t\t\t\twindow.open(url, \"_blank\", \"noopener,noreferrer\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tpostToIframe({ jsonrpc: \"2.0\", id, result: {} });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ui/message — widget sends a chat message\n\t\t\tif (method === \"ui/message\" && id != null) {\n\t\t\t\tif (onFollowUpRef.current && data.params) {\n\t\t\t\t\tonFollowUpRef.current(data.params);\n\t\t\t\t}\n\t\t\t\tpostToIframe({ jsonrpc: \"2.0\", id, result: {} });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ui/request-display-mode — widget requests fullscreen/inline/pip\n\t\t\tif (method === \"ui/request-display-mode\" && id != null) {\n\t\t\t\t// Acknowledge but stay inline for now\n\t\t\t\tpostToIframe({ jsonrpc: \"2.0\", id, result: {} });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ui/resource-teardown — graceful shutdown\n\t\t\tif (method === \"ui/resource-teardown\" && id != null) {\n\t\t\t\tpostToIframe({ jsonrpc: \"2.0\", id, result: {} });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ping — keep-alive\n\t\t\tif (method === \"ping\" && id != null) {\n\t\t\t\tpostToIframe({ jsonrpc: \"2.0\", id, result: {} });\n\t\t\t}\n\t\t};\n\n\t\twindow.addEventListener(\"message\", handleMessage);\n\n\t\treturn () => {\n\t\t\tdebug(\"effect cleanup (disposed)\");\n\t\t\tdisposed = true;\n\t\t\tclearTimeout(handshakeTimer);\n\t\t\twindow.removeEventListener(\"message\", handleMessage);\n\t\t};\n\t}, [autoHeight, clampHeight]);\n\n\treturn (\n\t\t<iframe\n\t\t\tref={iframeRef}\n\t\t\tsrc={iframeSrc}\n\t\t\tsandbox=\"allow-scripts allow-forms allow-same-origin\"\n\t\t\tclassName={cn(\"ww:rounded-md ww:border ww:border-border\", className)}\n\t\t\tstyle={{\n\t\t\t\theight,\n\t\t\t\tminWidth: width ? `min(${width}px, 100%)` : undefined,\n\t\t\t\twidth: \"100%\",\n\t\t\t\tborder: \"none\",\n\t\t\t\tcolorScheme: \"auto\",\n\t\t\t}}\n\t\t\ttitle=\"MCP App\"\n\t\t/>\n\t);\n}\n","\"use client\";\n\nimport { Component, type ReactNode } from \"react\";\n\ninterface Props {\n\tchildren: ReactNode;\n}\n\ninterface State {\n\thasError: boolean;\n}\n\nexport class WidgetErrorBoundary extends Component<Props, State> {\n\tstate: State = { hasError: false };\n\n\tstatic getDerivedStateFromError(): State {\n\t\treturn { hasError: true };\n\t}\n\n\tcomponentDidCatch(error: Error) {\n\t\tconsole.warn(\"[WaniWani] Widget failed to render:\", error.message);\n\t}\n\n\trender() {\n\t\tif (this.state.hasError) {\n\t\t\treturn (\n\t\t\t\t<div className=\"ww:flex ww:items-center ww:justify-between ww:rounded-md ww:border ww:border-border ww:bg-muted/50 ww:px-4 ww:py-3 ww:text-sm ww:text-muted-foreground\">\n\t\t\t\t\t<span>Widget failed to load</span>\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonClick={() => this.setState({ hasError: false })}\n\t\t\t\t\t\tclassName=\"ww:text-xs ww:font-medium ww:text-primary ww:hover:underline\"\n\t\t\t\t\t>\n\t\t\t\t\t\tRetry\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t);\n\t\t}\n\n\t\treturn this.props.children;\n\t}\n}\n","\"use client\";\n\nimport type { ChatStatus, ReasoningUIPart, ToolUIPart, UIMessage } from \"ai\";\n\nimport { Attachments } from \"../ai-elements/attachments\";\nimport { Loader } from \"../ai-elements/loader\";\nimport {\n\tMessage,\n\tMessageContent,\n\tMessageResponse,\n} from \"../ai-elements/message\";\nimport { Reasoning } from \"../ai-elements/reasoning\";\nimport {\n\tgetAutoHeight,\n\tgetResourceUri,\n\tTool,\n\tToolContent,\n\tToolHeader,\n\tToolInput,\n\tToolOutput,\n} from \"../ai-elements/tool\";\nimport { McpAppFrame } from \"./mcp-app-frame\";\nimport { WidgetErrorBoundary } from \"./widget-error-boundary\";\n\n/** Converts `get_price_estimate` or `compare-prices` → `Get price estimate` / `Compare prices` */\nfunction formatToolName(name: string): string {\n\treturn name.replace(/[-_]/g, \" \").replace(/^\\w/, (c) => c.toUpperCase());\n}\n\ninterface MessageListProps {\n\tmessages: UIMessage[];\n\tstatus: ChatStatus;\n\twelcomeMessage?: string;\n\tresourceEndpoint?: string;\n\tisDark?: boolean;\n\tonFollowUp?: (message: {\n\t\trole: string;\n\t\tcontent: Array<{ type: string; text?: string }>;\n\t}) => void;\n}\n\nexport function MessageList({\n\tmessages,\n\tstatus,\n\twelcomeMessage,\n\tresourceEndpoint,\n\tisDark,\n\tonFollowUp,\n}: MessageListProps) {\n\tconst isLoading = status === \"submitted\" || status === \"streaming\";\n\tconst lastMessage = messages[messages.length - 1];\n\tconst hasMessages = messages.length > 0;\n\tconst showLoaderBubble =\n\t\tisLoading && (!hasMessages || lastMessage.role === \"user\");\n\n\treturn (\n\t\t<>\n\t\t\t{welcomeMessage && (\n\t\t\t\t<Message from=\"assistant\">\n\t\t\t\t\t<MessageContent>\n\t\t\t\t\t\t<MessageResponse>{welcomeMessage}</MessageResponse>\n\t\t\t\t\t</MessageContent>\n\t\t\t\t</Message>\n\t\t\t)}\n\t\t\t{messages.map((message) => {\n\t\t\t\tconst textParts = message.parts.filter((p) => p.type === \"text\");\n\t\t\t\tconst reasoningParts = message.parts.filter(\n\t\t\t\t\t(p): p is ReasoningUIPart => p.type === \"reasoning\",\n\t\t\t\t);\n\t\t\t\tconst fileParts = message.parts.filter((p) => p.type === \"file\");\n\t\t\t\tconst toolParts = message.parts.filter(\n\t\t\t\t\t(\n\t\t\t\t\t\tp,\n\t\t\t\t\t): p is typeof p & {\n\t\t\t\t\t\ttoolCallId: string;\n\t\t\t\t\t\ttoolName: string;\n\t\t\t\t\t\tstate: ToolUIPart[\"state\"];\n\t\t\t\t\t\tinput: unknown;\n\t\t\t\t\t\ttitle?: string;\n\t\t\t\t\t} => \"toolCallId\" in p,\n\t\t\t\t);\n\t\t\t\tconst isLastAssistant =\n\t\t\t\t\tmessage === lastMessage && message.role === \"assistant\";\n\t\t\t\tconst hasTextContent = textParts.length > 0;\n\n\t\t\t\treturn (\n\t\t\t\t\t<Message from={message.role} key={message.id}>\n\t\t\t\t\t\t{reasoningParts.map((part, i) => (\n\t\t\t\t\t\t\t<Reasoning\n\t\t\t\t\t\t\t\tkey={`reasoning-${message.id}-${i}`}\n\t\t\t\t\t\t\t\ttext={part.text}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t))}\n\t\t\t\t\t\t{toolParts.map((part) => {\n\t\t\t\t\t\t\tconst output = \"output\" in part ? part.output : undefined;\n\t\t\t\t\t\t\tconst resourceUri =\n\t\t\t\t\t\t\t\toutput !== undefined ? getResourceUri(output) : undefined;\n\t\t\t\t\t\t\tconst autoHeight =\n\t\t\t\t\t\t\t\toutput !== undefined ? getAutoHeight(output) : false;\n\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<div key={part.toolCallId}>\n\t\t\t\t\t\t\t\t\t<Tool defaultOpen={part.state === \"output-available\"}>\n\t\t\t\t\t\t\t\t\t\t<ToolHeader\n\t\t\t\t\t\t\t\t\t\t\ttitle={part.title ?? formatToolName(part.toolName)}\n\t\t\t\t\t\t\t\t\t\t\tstate={part.state}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t<ToolContent>\n\t\t\t\t\t\t\t\t\t\t\t<ToolInput input={part.input} />\n\t\t\t\t\t\t\t\t\t\t\t{output !== undefined && (\n\t\t\t\t\t\t\t\t\t\t\t\t<ToolOutput\n\t\t\t\t\t\t\t\t\t\t\t\t\toutput={output}\n\t\t\t\t\t\t\t\t\t\t\t\t\terrorText={\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"errorText\" in part ? part.errorText : undefined\n\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t</ToolContent>\n\t\t\t\t\t\t\t\t\t</Tool>\n\t\t\t\t\t\t\t\t\t{resourceUri && output !== undefined && (\n\t\t\t\t\t\t\t\t\t\t<WidgetErrorBoundary>\n\t\t\t\t\t\t\t\t\t\t\t<McpAppFrame\n\t\t\t\t\t\t\t\t\t\t\t\tresourceUri={resourceUri}\n\t\t\t\t\t\t\t\t\t\t\t\ttoolInput={\n\t\t\t\t\t\t\t\t\t\t\t\t\t(part.input as Record<string, unknown>) ?? {}\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\ttoolResult={{\n\t\t\t\t\t\t\t\t\t\t\t\t\tcontent: (output as Record<string, unknown>)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t.content as\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t| Array<{ type: string; text?: string }>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t| undefined,\n\t\t\t\t\t\t\t\t\t\t\t\t\tstructuredContent: (output as Record<string, unknown>)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t.structuredContent as\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t| Record<string, unknown>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t| undefined,\n\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\tresourceEndpoint={resourceEndpoint}\n\t\t\t\t\t\t\t\t\t\t\t\tisDark={isDark}\n\t\t\t\t\t\t\t\t\t\t\t\tautoHeight={autoHeight}\n\t\t\t\t\t\t\t\t\t\t\t\tonFollowUp={onFollowUp}\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t</WidgetErrorBoundary>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})}\n\t\t\t\t\t\t<MessageContent>\n\t\t\t\t\t\t\t{fileParts.length > 0 && <Attachments files={fileParts} />}\n\t\t\t\t\t\t\t{hasTextContent\n\t\t\t\t\t\t\t\t? textParts.map((part, i) => (\n\t\t\t\t\t\t\t\t\t\t<MessageResponse key={`${message.id}-${i}`}>\n\t\t\t\t\t\t\t\t\t\t\t{part.type === \"text\" ? part.text : \"\"}\n\t\t\t\t\t\t\t\t\t\t</MessageResponse>\n\t\t\t\t\t\t\t\t\t))\n\t\t\t\t\t\t\t\t: isLastAssistant && isLoading && <Loader />}\n\t\t\t\t\t\t</MessageContent>\n\t\t\t\t\t</Message>\n\t\t\t\t);\n\t\t\t})}\n\t\t\t{showLoaderBubble && (\n\t\t\t\t<Message from=\"assistant\">\n\t\t\t\t\t<MessageContent>\n\t\t\t\t\t\t<Loader />\n\t\t\t\t\t</MessageContent>\n\t\t\t\t</Message>\n\t\t\t)}\n\t\t</>\n\t);\n}\n","\"use client\";\n\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"../lib/utils\";\n\nexport interface SuggestionsProps\n\textends Omit<HTMLAttributes<HTMLDivElement>, \"onSelect\"> {\n\tsuggestions: string[];\n\tisLoading?: boolean;\n\tonSelect: (suggestion: string) => void;\n}\n\nexport function Suggestions({\n\tsuggestions,\n\tisLoading,\n\tonSelect,\n\tclassName,\n\t...props\n}: SuggestionsProps) {\n\tif (suggestions.length === 0 && !isLoading) return null;\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\"ww:flex ww:flex-wrap ww:gap-2 ww:px-3 ww:py-2\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{isLoading\n\t\t\t\t? [0, 1, 2].map((i) => (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tkey={i}\n\t\t\t\t\t\t\tclassName=\"ww:h-7 ww:rounded-full ww:bg-accent ww:animate-pulse\"\n\t\t\t\t\t\t\tstyle={{ width: `${60 + i * 20}px` }}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))\n\t\t\t\t: suggestions.map((suggestion, index) => (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\tkey={suggestion}\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tonClick={() => onSelect(suggestion)}\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"ww:rounded-full ww:border ww:border-border ww:bg-background ww:px-3 ww:py-1 ww:text-xs\",\n\t\t\t\t\t\t\t\t\"ww:text-foreground ww:hover:bg-accent ww:hover:border-primary/30\",\n\t\t\t\t\t\t\t\t\"ww:transition-all ww:duration-200 ww:ease-out ww:cursor-pointer\",\n\t\t\t\t\t\t\t\t\"ww:animate-[ww-fade-in_0.2s_ease-out_both]\",\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\tstyle={{ animationDelay: `${index * 50}ms` }}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{suggestion}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t))}\n\t\t</div>\n\t);\n}\n","\"use client\";\n\nimport { useChat } from \"@ai-sdk/react\";\nimport { DefaultChatTransport } from \"ai\";\nimport { useCallback, useRef, useState } from \"react\";\nimport type { ChatBaseProps } from \"../@types\";\nimport type { PromptInputMessage } from \"../ai-elements/prompt-input\";\n\nexport function useChatEngine(props: ChatBaseProps) {\n\tconst {\n\t\tapi = \"https://app.waniwani.ai/api/chat\",\n\t\theaders: userHeaders,\n\t\tbody,\n\t\tonMessageSent,\n\t\tonResponseReceived,\n\t} = props;\n\n\tconst transportRef = useRef(\n\t\tnew DefaultChatTransport({\n\t\t\tapi,\n\t\t\theaders: {\n\t\t\t\t...userHeaders,\n\t\t\t},\n\t\t\tbody,\n\t\t}),\n\t);\n\n\tconst { messages, sendMessage, status } = useChat({\n\t\ttransport: transportRef.current,\n\t\tonFinish() {\n\t\t\tonResponseReceived?.();\n\t\t},\n\t\tonError(error) {\n\t\t\tconsole.warn(\"[WaniWani] Chat error:\", error.message);\n\t\t},\n\t});\n\n\tconst [text, setText] = useState(\"\");\n\n\tconst handleSubmit = useCallback(\n\t\t(message: PromptInputMessage) => {\n\t\t\tconst hasText = Boolean(message.text?.trim());\n\t\t\tconst hasFiles = Boolean(message.files?.length);\n\t\t\tif (!(hasText || hasFiles)) return;\n\n\t\t\tsendMessage({\n\t\t\t\ttext: message.text || \"\",\n\t\t\t\tfiles: message.files,\n\t\t\t});\n\n\t\t\tonMessageSent?.(message.text || \"\");\n\t\t\tsetText(\"\");\n\t\t},\n\t\t[sendMessage, onMessageSent],\n\t);\n\n\tconst handleTextChange = useCallback(\n\t\t(e: React.ChangeEvent<HTMLTextAreaElement>) => {\n\t\t\tsetText(e.target.value);\n\t\t},\n\t\t[],\n\t);\n\n\tconst isLoading = status === \"submitted\" || status === \"streaming\";\n\tconst lastMessage = messages[messages.length - 1];\n\tconst hasMessages = messages.length > 0;\n\tconst showLoaderBubble =\n\t\tisLoading && (!hasMessages || lastMessage.role === \"user\");\n\n\treturn {\n\t\tmessages,\n\t\tstatus,\n\t\ttext,\n\t\tsetText,\n\t\thandleSubmit,\n\t\thandleTextChange,\n\t\tisLoading,\n\t\tshowLoaderBubble,\n\t\tlastMessage,\n\t\thasMessages,\n\t\tsendMessage,\n\t};\n}\n","\"use client\";\n\nimport type { ChatStatus, UIMessage } from \"ai\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { SuggestionsConfig } from \"../@types\";\n\nexport interface UseSuggestionsOptions {\n\tmessages: UIMessage[];\n\tstatus: ChatStatus;\n\tconfig?: boolean | SuggestionsConfig;\n}\n\n/**\n * Extract suggestions from the last assistant message's data part.\n * The API streams a `data-suggestions` part at the end of the response:\n * `{ type: \"data-suggestions\", data: { suggestions: string[] } }`\n */\nfunction extractSuggestions(message: UIMessage): string[] | null {\n\tfor (const part of message.parts) {\n\t\tconst p = part as Record<string, unknown>;\n\t\t// Handle both \"data-suggestions\" and generic \"data\" part types\n\t\tif (p.type === \"data\" || p.type === \"data-suggestions\") {\n\t\t\tconst data = p.data as Record<string, unknown> | undefined;\n\t\t\tif (data && Array.isArray(data.suggestions)) {\n\t\t\t\treturn data.suggestions as string[];\n\t\t\t}\n\t\t}\n\t}\n\treturn null;\n}\n\nfunction isConfigObject(\n\tconfig: boolean | SuggestionsConfig | undefined,\n): config is SuggestionsConfig {\n\treturn typeof config === \"object\" && config !== null && \"initial\" in config;\n}\n\nexport function useSuggestions(options: UseSuggestionsOptions) {\n\tconst { messages, status, config } = options;\n\n\tconst [suggestions, setSuggestions] = useState<string[]>(\n\t\t(isConfigObject(config) && config.initial ? config.initial : []) ?? [],\n\t);\n\tconst prevStatusRef = useRef<ChatStatus>(status);\n\n\tconst isDynamicEnabled =\n\t\tconfig === true || (isConfigObject(config) && config.dynamic !== false);\n\n\tconst clear = useCallback(() => {\n\t\tsetSuggestions([]);\n\t}, []);\n\n\t// Clear when a new user message arrives\n\tconst lastMessage = messages[messages.length - 1];\n\tuseEffect(() => {\n\t\tif (lastMessage?.role === \"user\") {\n\t\t\tclear();\n\t\t}\n\t}, [lastMessage, clear]);\n\n\t// Extract suggestions from message parts on streaming → ready transition\n\tuseEffect(() => {\n\t\tconst prevStatus = prevStatusRef.current;\n\t\tprevStatusRef.current = status;\n\n\t\tif (prevStatus === \"streaming\" && status === \"ready\" && isDynamicEnabled) {\n\t\t\tconst lastAssistant = [...messages]\n\t\t\t\t.reverse()\n\t\t\t\t.find((m) => m.role === \"assistant\");\n\t\t\tif (!lastAssistant) return;\n\n\t\t\tconsole.log(\"[WaniWani] Assistant parts:\", lastAssistant.parts);\n\n\t\t\tconst extracted = extractSuggestions(lastAssistant);\n\t\t\tconsole.log(\"[WaniWani] Extracted suggestions:\", extracted);\n\t\t\tif (extracted) {\n\t\t\t\tsetSuggestions(extracted);\n\t\t\t}\n\t\t}\n\t}, [status, isDynamicEnabled, messages]);\n\n\treturn { suggestions, isLoading: false, clear };\n}\n","import { useEffect, useRef, useState } from \"react\";\n\nconst TYPE_SPEED_MS = 50;\nconst DELETE_SPEED_MS = 30;\nconst PAUSE_AFTER_TYPE_MS = 2000;\nconst PAUSE_AFTER_DELETE_MS = 500;\n\n/**\n * Returns a string that animates like someone typing and deleting the placeholder text.\n * Only animates when `active` is true (i.e. the input is empty).\n */\nexport function useTypingPlaceholder(text: string, active = true): string {\n\tconst [displayed, setDisplayed] = useState(\"\");\n\tconst timerRef = useRef<ReturnType<typeof setTimeout>>(undefined);\n\n\tuseEffect(() => {\n\t\tif (!active) {\n\t\t\t// Reset so the animation restarts fresh when re-activated\n\t\t\tsetDisplayed(\"\");\n\t\t\treturn;\n\t\t}\n\n\t\tlet i = 0;\n\t\tlet deleting = false;\n\t\tlet disposed = false;\n\n\t\tconst tick = () => {\n\t\t\tif (disposed) return;\n\n\t\t\tif (!deleting) {\n\t\t\t\t// Typing forward\n\t\t\t\ti++;\n\t\t\t\tsetDisplayed(text.slice(0, i));\n\n\t\t\t\tif (i >= text.length) {\n\t\t\t\t\t// Finished typing — pause then start deleting\n\t\t\t\t\tdeleting = true;\n\t\t\t\t\ttimerRef.current = setTimeout(tick, PAUSE_AFTER_TYPE_MS);\n\t\t\t\t} else {\n\t\t\t\t\ttimerRef.current = setTimeout(tick, TYPE_SPEED_MS);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Deleting\n\t\t\t\ti--;\n\t\t\t\tsetDisplayed(text.slice(0, i));\n\n\t\t\t\tif (i <= 0) {\n\t\t\t\t\t// Finished deleting — pause then start typing again\n\t\t\t\t\tdeleting = false;\n\t\t\t\t\ttimerRef.current = setTimeout(tick, PAUSE_AFTER_DELETE_MS);\n\t\t\t\t} else {\n\t\t\t\t\ttimerRef.current = setTimeout(tick, DELETE_SPEED_MS);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\t// Start after a small delay\n\t\ttimerRef.current = setTimeout(tick, PAUSE_AFTER_DELETE_MS);\n\n\t\treturn () => {\n\t\t\tdisposed = true;\n\t\t\tclearTimeout(timerRef.current);\n\t\t};\n\t}, [text, active]);\n\n\treturn displayed;\n}\n","import type { ChatTheme } from \"./@types\";\n\nexport const DEFAULT_THEME: Required<ChatTheme> = {\n\tprimaryColor: \"#6366f1\",\n\tprimaryForeground: \"#1f2937\",\n\tbackgroundColor: \"#ffffff\",\n\ttextColor: \"#1f2937\",\n\tmutedColor: \"#6b7280\",\n\tborderColor: \"#e5e7eb\",\n\tassistantBubbleColor: \"#f3f4f6\",\n\tuserBubbleColor: \"#f4f4f4\",\n\tinputBackgroundColor: \"#f9fafb\",\n\tborderRadius: 16,\n\tmessageBorderRadius: 12,\n\tfontFamily: \"system-ui, -apple-system, 'Segoe UI', sans-serif\",\n\theaderBackgroundColor: \"#ffffff\",\n\theaderTextColor: \"#1f2937\",\n\tstatusColor: \"#22c55e\",\n\ttoolCardColor: \"#f4f4f5\",\n};\n\nexport const DARK_THEME: ChatTheme = {\n\tbackgroundColor: \"#212121\",\n\theaderBackgroundColor: \"#1e1e1e\",\n\theaderTextColor: \"#ececec\",\n\ttextColor: \"#ececec\",\n\tprimaryForeground: \"#ffffff\",\n\tmutedColor: \"#8e8ea0\",\n\tborderColor: \"#444444\",\n\tassistantBubbleColor: \"#2f2f2f\",\n\tuserBubbleColor: \"#303030\",\n\tinputBackgroundColor: \"#2f2f2f\",\n\tprimaryColor: \"#6366f1\",\n\tstatusColor: \"#22c55e\",\n\ttoolCardColor: \"#262626\",\n};\n\nconst CSS_VAR_MAP: Record<keyof ChatTheme, string[]> = {\n\tprimaryColor: [\"--ww-primary\", \"--ww-color-primary\"],\n\tprimaryForeground: [\"--ww-primary-fg\", \"--ww-color-primary-foreground\"],\n\tbackgroundColor: [\"--ww-bg\", \"--ww-color-background\"],\n\ttextColor: [\n\t\t\"--ww-text\",\n\t\t\"--ww-color-foreground\",\n\t\t\"--ww-color-accent-foreground\",\n\t],\n\tmutedColor: [\"--ww-muted\", \"--ww-color-muted-foreground\"],\n\tborderColor: [\"--ww-border\", \"--ww-color-border\"],\n\tassistantBubbleColor: [\"--ww-assistant-bubble\", \"--ww-color-accent\"],\n\tuserBubbleColor: [\"--ww-user-bubble\"],\n\tinputBackgroundColor: [\"--ww-input-bg\", \"--ww-color-input\"],\n\tborderRadius: [\"--ww-radius\"],\n\tmessageBorderRadius: [\"--ww-msg-radius\"],\n\tfontFamily: [\"--ww-font\"],\n\theaderBackgroundColor: [\"--ww-header-bg\"],\n\theaderTextColor: [\"--ww-header-text\"],\n\tstatusColor: [\"--ww-status\"],\n\ttoolCardColor: [\"--ww-tool-card\", \"--ww-color-tool-card\"],\n};\n\nexport function mergeTheme(userTheme?: ChatTheme): Required<ChatTheme> {\n\treturn { ...DEFAULT_THEME, ...userTheme };\n}\n\nexport function isDarkTheme(theme: Required<ChatTheme>): boolean {\n\tconst hex = theme.backgroundColor.replace(\"#\", \"\");\n\tconst r = parseInt(hex.substring(0, 2), 16);\n\tconst g = parseInt(hex.substring(2, 4), 16);\n\tconst b = parseInt(hex.substring(4, 6), 16);\n\treturn (r * 299 + g * 587 + b * 114) / 1000 < 128;\n}\n\nexport function themeToCSSProperties(\n\ttheme: Required<ChatTheme>,\n): Record<string, string> {\n\tconst vars: Record<string, string> = {};\n\tfor (const [key, cssVars] of Object.entries(CSS_VAR_MAP)) {\n\t\tconst value = theme[key as keyof ChatTheme];\n\t\tconst resolved = typeof value === \"number\" ? `${value}px` : String(value);\n\t\tfor (const cssVar of cssVars) {\n\t\t\tvars[cssVar] = resolved;\n\t\t}\n\t}\n\treturn vars;\n}\n","\"use client\";\n\nimport {\n\tforwardRef,\n\tuseCallback,\n\tuseEffect,\n\tuseImperativeHandle,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport type { ChatCardProps, ChatHandle } from \"../@types\";\nimport {\n\tConversation,\n\tConversationContent,\n\tConversationScrollButton,\n} from \"../ai-elements/conversation\";\nimport {\n\tPromptInput,\n\tPromptInputAddAttachments,\n\tPromptInputSubmit,\n\tPromptInputTextarea,\n} from \"../ai-elements/prompt-input\";\nimport { MessageList } from \"../components/message-list\";\nimport { Suggestions } from \"../components/suggestions\";\nimport { useChatEngine } from \"../hooks/use-chat-engine\";\nimport { useSuggestions } from \"../hooks/use-suggestions\";\nimport { useTypingPlaceholder } from \"../hooks/use-typing-placeholder\";\nimport { cn } from \"../lib/utils\";\nimport { isDarkTheme, mergeTheme, themeToCSSProperties } from \"../theme\";\n\nexport const ChatCard = forwardRef<ChatHandle, ChatCardProps>(\n\tfunction ChatCard(props, ref) {\n\t\tconst {\n\t\t\ttheme: userTheme,\n\t\t\ttitle = \"Assistant\",\n\t\t\tsubtitle,\n\t\t\tshowStatus = true,\n\t\t\twidth = 500,\n\t\t\theight = 600,\n\t\t\tallowAttachments = false,\n\t\t\twelcomeMessage,\n\t\t\tplaceholder = \"Ask me anything...\",\n\t\t\ttriggerEvent = \"triggerDemoRequest\",\n\t\t\tresourceEndpoint,\n\t\t\tapi,\n\t\t} = props;\n\n\t\tconst effectiveResourceEndpoint =\n\t\t\tresourceEndpoint ?? (api ? `${api}/resource` : undefined);\n\n\t\tconst resolvedTheme = mergeTheme(userTheme);\n\t\tconst cssVars = themeToCSSProperties(resolvedTheme);\n\t\tconst isDark = isDarkTheme(resolvedTheme);\n\n\t\tconst engine = useChatEngine(props);\n\n\t\tconst animatedPlaceholder = useTypingPlaceholder(placeholder, !engine.text);\n\n\t\tconst [isHighlighted, setIsHighlighted] = useState(false);\n\t\tconst cardRef = useRef<HTMLDivElement>(null);\n\t\tconst highlightTimerRef = useRef<ReturnType<typeof setTimeout>>(undefined);\n\n\t\tconst focusInput = useCallback(() => {\n\t\t\tconst container = cardRef.current;\n\t\t\tif (!container) return;\n\t\t\tcontainer.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n\t\t\tconst textarea = container.querySelector(\"textarea\");\n\t\t\tif (textarea) {\n\t\t\t\tsetTimeout(() => textarea.focus(), 300);\n\t\t\t}\n\t\t\tsetIsHighlighted(true);\n\t\t\tclearTimeout(highlightTimerRef.current);\n\t\t\thighlightTimerRef.current = setTimeout(\n\t\t\t\t() => setIsHighlighted(false),\n\t\t\t\t2000,\n\t\t\t);\n\t\t}, []);\n\n\t\tconst suggestionsState = useSuggestions({\n\t\t\tmessages: engine.messages,\n\t\t\tstatus: engine.status,\n\t\t\tconfig: props.suggestions,\n\t\t});\n\n\t\tconst handleWidgetMessage = useCallback(\n\t\t\t(message: {\n\t\t\t\trole: string;\n\t\t\t\tcontent: Array<{ type: string; text?: string }>;\n\t\t\t}) => {\n\t\t\t\tconst text = message.content\n\t\t\t\t\t.map((c) => c.text ?? \"\")\n\t\t\t\t\t.join(\"\")\n\t\t\t\t\t.trim();\n\t\t\t\tif (text) engine.handleSubmit({ text, files: [] });\n\t\t\t},\n\t\t\t[engine.handleSubmit],\n\t\t);\n\n\t\tconst handleSuggestionSelect = useCallback(\n\t\t\t(suggestion: string) => {\n\t\t\t\tsuggestionsState.clear();\n\t\t\t\tengine.handleSubmit({ text: suggestion, files: [] });\n\t\t\t},\n\t\t\t[suggestionsState.clear, engine.handleSubmit],\n\t\t);\n\n\t\tuseImperativeHandle(\n\t\t\tref,\n\t\t\t() => ({\n\t\t\t\tsendMessage: (text: string) => {\n\t\t\t\t\tengine.handleSubmit({ text, files: [] });\n\t\t\t\t\tfocusInput();\n\t\t\t\t},\n\t\t\t\tfocus: focusInput,\n\t\t\t}),\n\t\t\t[engine.handleSubmit, focusInput],\n\t\t);\n\n\t\t// Listen for custom trigger event (e.g. \"triggerDemoRequest\")\n\t\tuseEffect(() => {\n\t\t\tif (!triggerEvent) return;\n\t\t\tconst handler = (e: Event) => {\n\t\t\t\tconst detail = (e as CustomEvent).detail;\n\t\t\t\tconst message =\n\t\t\t\t\ttypeof detail?.message === \"string\" ? detail.message : undefined;\n\t\t\t\tif (message) {\n\t\t\t\t\tengine.handleSubmit({ text: message, files: [] });\n\t\t\t\t}\n\t\t\t\tfocusInput();\n\t\t\t};\n\t\t\twindow.addEventListener(triggerEvent, handler);\n\t\t\treturn () => window.removeEventListener(triggerEvent, handler);\n\t\t}, [triggerEvent, engine.handleSubmit, focusInput]);\n\n\t\treturn (\n\t\t\t<div\n\t\t\t\tref={cardRef}\n\t\t\t\tstyle={{ ...cssVars, width, height }}\n\t\t\t\tdata-waniwani-chat=\"\"\n\t\t\t\tdata-waniwani-layout=\"card\"\n\t\t\t\t{...(isDark ? { \"data-waniwani-dark\": \"\" } : {})}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"ww:flex ww:flex-col ww:font-[family-name:var(--ww-font)] ww:text-foreground ww:bg-background ww:rounded-[var(--ww-radius)] ww:border ww:border-border ww:shadow-md ww:overflow-hidden ww:transition-shadow ww:duration-300\",\n\t\t\t\t\tisHighlighted &&\n\t\t\t\t\t\t\"ww:ring-2 ww:ring-blue-400/70 ww:ring-offset-2 ww:ring-offset-background\",\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{/* Header */}\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"ww:shrink-0 ww:flex ww:items-center ww:gap-3 ww:px-4 ww:py-2 ww:border-b ww:border-border\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tbackgroundColor: resolvedTheme.headerBackgroundColor,\n\t\t\t\t\t\tcolor: resolvedTheme.headerTextColor,\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{showStatus && (\n\t\t\t\t\t\t<span className=\"ww:size-2.5 ww:rounded-full ww:bg-status\" />\n\t\t\t\t\t)}\n\t\t\t\t\t<div className=\"ww:flex-1 ww:min-w-0\">\n\t\t\t\t\t\t<div className=\"ww:text-xs ww:font-semibold ww:truncate\">\n\t\t\t\t\t\t\t{title}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{subtitle && (\n\t\t\t\t\t\t\t<div className=\"ww:text-[11px] ww:text-muted-foreground ww:truncate\">\n\t\t\t\t\t\t\t\t{subtitle}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Messages */}\n\t\t\t\t<Conversation className=\"ww:flex-1 ww:min-h-0 ww:bg-background\">\n\t\t\t\t\t<ConversationContent>\n\t\t\t\t\t\t<MessageList\n\t\t\t\t\t\t\tmessages={engine.messages}\n\t\t\t\t\t\t\tstatus={engine.status}\n\t\t\t\t\t\t\twelcomeMessage={welcomeMessage}\n\t\t\t\t\t\t\tresourceEndpoint={effectiveResourceEndpoint}\n\t\t\t\t\t\t\tisDark={isDark}\n\t\t\t\t\t\t\tonFollowUp={handleWidgetMessage}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</ConversationContent>\n\t\t\t\t\t<ConversationScrollButton />\n\t\t\t\t</Conversation>\n\n\t\t\t\t{/* Suggestions */}\n\t\t\t\t<Suggestions\n\t\t\t\t\tsuggestions={suggestionsState.suggestions}\n\t\t\t\t\tisLoading={suggestionsState.isLoading}\n\t\t\t\t\tonSelect={handleSuggestionSelect}\n\t\t\t\t\tclassName=\"ww:border-t ww:border-border\"\n\t\t\t\t/>\n\n\t\t\t\t{/* Input */}\n\t\t\t\t<div className=\"ww:shrink-0 ww:border-t ww:border-border ww:bg-background\">\n\t\t\t\t\t<PromptInput\n\t\t\t\t\t\tonSubmit={engine.handleSubmit}\n\t\t\t\t\t\tglobalDrop={allowAttachments}\n\t\t\t\t\t\tmultiple={allowAttachments}\n\t\t\t\t\t\tclassName={cn(\"ww:rounded-none ww:border-0\")}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"ww:flex ww:items-center ww:gap-1 ww:px-3 ww:py-2\">\n\t\t\t\t\t\t\t{allowAttachments && <PromptInputAddAttachments />}\n\t\t\t\t\t\t\t<PromptInputTextarea\n\t\t\t\t\t\t\t\tonChange={engine.handleTextChange}\n\t\t\t\t\t\t\t\tvalue={engine.text}\n\t\t\t\t\t\t\t\tplaceholder={animatedPlaceholder}\n\t\t\t\t\t\t\t\tclassName=\"ww:min-h-0 ww:py-1.5 ww:px-2\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<PromptInputSubmit status={engine.status} />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</PromptInput>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t},\n);\n"],"mappings":"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","names":["forwardRef","useCallback","useEffect","useImperativeHandle","useRef","useState","ArrowDownIcon","useCallback","StickToBottom","useStickToBottomContext","clsx","extendTailwindMerge","twMerge","cn","inputs","jsx","Button","className","variant","size","type","props","cn","jsx","Conversation","className","props","StickToBottom","cn","ConversationContent","ConversationScrollButton","isAtBottom","scrollToBottom","useStickToBottomContext","handleScrollToBottom","useCallback","Button","ArrowDownIcon","ArrowUpIcon","LoaderIcon","PaperclipIcon","SquareIcon","XIcon","nanoid","createContext","useCallback","useContext","useEffect","useMemo","useRef","useState","jsx","jsxs","convertBlobUrlToDataUrl","url","blob","resolve","reader","LocalAttachmentsContext","createContext","usePromptInputAttachments","context","useContext","PromptInput","className","accept","multiple","globalDrop","maxFiles","maxFileSize","onSubmit","children","props","inputRef","useRef","formRef","items","setItems","useState","filesRef","useEffect","openFileDialog","useCallback","add","fileList","incoming","withinSize","f","valid","prev","capacity","capped","file","nanoid","remove","id","found","clear","handleChange","event","onDragOver","e","onDrop","handleSubmit","form","text","convertedFiles","_id","item","dataUrl","result","attachmentsCtx","useMemo","cn","PromptInputTextarea","onChange","onKeyDown","className","placeholder","props","attachments","usePromptInputAttachments","isComposing","setIsComposing","useState","handleKeyDown","useCallback","e","form","lastAttachment","handlePaste","event","items","files","item","file","jsx","cn","PromptInputSubmit","status","onStop","onClick","children","isGenerating","Icon","ArrowUpIcon","LoaderIcon","SquareIcon","handleClick","Button","PromptInputAddAttachments","jsxs","XIcon","PaperclipIcon","FileIcon","jsx","jsxs","Attachments","files","className","props","cn","file","i","AttachmentItem","FileIcon","jsx","Loader","className","size","props","cn","i","cjk","code","memo","Streamdown","jsx","Message","className","from","props","cn","MessageContent","children","streamdownPlugins","cjk","code","MessageResponse","memo","Streamdown","prevProps","nextProps","jsx","Reasoning","className","text","props","cn","BracesIcon","CheckIcon","ChevronDownIcon","ChevronRightIcon","ClipboardCopyIcon","ServerIcon","createContext","useCallback","useContext","useEffect","useMemo","useRef","useState","Fragment","jsx","jsxs","truncateJSON","data","maxLength","stringified","entries","parts","remaining","key","value","keyAbbrev","valStr","part","CopyButton","text","className","copied","setCopied","useState","timeoutRef","useRef","useEffect","handleCopy","useCallback","e","Button","cn","CheckIcon","ClipboardCopyIcon","CollapsibleJSON","label","props","expanded","setExpanded","fullJSON","useMemo","preview","v","ChevronRightIcon","ToolOpenContext","createContext","Tool","defaultOpen","children","open","setOpen","o","ToolHeader","title","state","toggle","useContext","isRunning","BracesIcon","ChevronDownIcon","ToolContent","className","children","props","open","useContext","ToolOpenContext","jsx","cn","ToolInput","input","CollapsibleJSON","getUiMeta","output","meta","ui","getResourceUri","uri","getAutoHeight","ToolOutput","errorText","jsxs","useCallback","useEffect","useMemo","useRef","useState","jsx","DEFAULT_RESOURCE_ENDPOINT","MAX_HEIGHT","DEFAULT_HEIGHT","PROTOCOL_VERSION","RESIZE_ANIMATION_MS","HANDSHAKE_TIMEOUT_MS","MAX_RETRIES","McpAppFrame","resourceUri","toolInput","toolResult","resourceEndpoint","isDark","className","autoHeight","onOpenLink","onFollowUp","iframeRef","useRef","toolInputRef","toolResultRef","lastSizeRef","animationRef","initializedRef","retryCountRef","height","setHeight","useState","width","setWidth","onOpenLinkRef","onFollowUpRef","clampHeight","useCallback","h","iframeSrc","useMemo","isDarkRef","useEffect","iframe","disposed","handshakeReceived","debug","args","handshakeTimer","url","postToIframe","msg","handleMessage","event","data","method","id","input","result","content","params","newHeight","newWidth","last","heightChanged","widthChanged","clamped","from","anim","cn","Component","jsx","jsxs","WidgetErrorBoundary","error","Fragment","jsx","jsxs","formatToolName","name","c","MessageList","messages","status","welcomeMessage","resourceEndpoint","isDark","onFollowUp","isLoading","lastMessage","hasMessages","showLoaderBubble","Message","MessageContent","MessageResponse","message","textParts","p","reasoningParts","fileParts","toolParts","isLastAssistant","hasTextContent","part","i","Reasoning","output","resourceUri","getResourceUri","autoHeight","getAutoHeight","Tool","ToolHeader","ToolContent","ToolInput","ToolOutput","WidgetErrorBoundary","McpAppFrame","Attachments","Loader","jsx","Suggestions","suggestions","isLoading","onSelect","className","props","cn","i","suggestion","index","useChat","DefaultChatTransport","useCallback","useRef","useState","useChatEngine","props","api","userHeaders","body","onMessageSent","onResponseReceived","transportRef","messages","sendMessage","status","error","text","setText","handleSubmit","message","hasText","hasFiles","handleTextChange","e","isLoading","lastMessage","hasMessages","showLoaderBubble","useCallback","useEffect","useRef","useState","extractSuggestions","message","part","p","data","isConfigObject","config","useSuggestions","options","messages","status","suggestions","setSuggestions","prevStatusRef","isDynamicEnabled","clear","lastMessage","prevStatus","lastAssistant","m","extracted","useEffect","useRef","useState","TYPE_SPEED_MS","DELETE_SPEED_MS","PAUSE_AFTER_TYPE_MS","PAUSE_AFTER_DELETE_MS","useTypingPlaceholder","text","active","displayed","setDisplayed","timerRef","i","deleting","disposed","tick","DEFAULT_THEME","DARK_THEME","CSS_VAR_MAP","mergeTheme","userTheme","isDarkTheme","theme","hex","r","g","b","themeToCSSProperties","vars","key","cssVars","value","resolved","cssVar","jsx","jsxs","ChatBar","forwardRef","props","ref","userTheme","width","expandedHeight","allowAttachments","welcomeMessage","placeholder","triggerEvent","resourceEndpoint","api","effectiveResourceEndpoint","resolvedTheme","mergeTheme","cssVars","themeToCSSProperties","isDark","isDarkTheme","engine","useChatEngine","suggestionsState","useSuggestions","handleWidgetMessage","useCallback","message","text","c","handleSuggestionSelect","suggestion","animatedPlaceholder","useTypingPlaceholder","isFocused","setIsFocused","useState","isHighlighted","setIsHighlighted","containerRef","useRef","highlightTimerRef","focusInput","container","textarea","useImperativeHandle","useEffect","handler","e","detail","isExpanded","handleClickOutside","handleFocus","cn","Conversation","ConversationContent","MessageList","ConversationScrollButton","Suggestions","PromptInput","PromptInputAddAttachments","PromptInputTextarea","PromptInputSubmit","forwardRef","useCallback","useEffect","useImperativeHandle","useRef","useState","jsx","jsxs","ChatCard","forwardRef","props","ref","userTheme","title","subtitle","showStatus","width","height","allowAttachments","welcomeMessage","placeholder","triggerEvent","resourceEndpoint","api","effectiveResourceEndpoint","resolvedTheme","mergeTheme","cssVars","themeToCSSProperties","isDark","isDarkTheme","engine","useChatEngine","animatedPlaceholder","useTypingPlaceholder","isHighlighted","setIsHighlighted","useState","cardRef","useRef","highlightTimerRef","focusInput","useCallback","container","textarea","suggestionsState","useSuggestions","handleWidgetMessage","message","text","c","handleSuggestionSelect","suggestion","useImperativeHandle","useEffect","handler","e","detail","cn","Conversation","ConversationContent","MessageList","ConversationScrollButton","Suggestions","PromptInput","PromptInputAddAttachments","PromptInputTextarea","PromptInputSubmit"]}
1
+ {"version":3,"sources":["../../src/chat/web/layouts/chat-bar.tsx","../../src/chat/web/ai-elements/conversation.tsx","../../src/chat/web/lib/utils.ts","../../src/chat/web/ui/button.tsx","../../src/chat/web/ai-elements/prompt-input.tsx","../../src/chat/web/components/chat-queue.tsx","../../src/chat/web/ai-elements/queue.tsx","../../src/chat/web/ai-elements/attachments.tsx","../../src/chat/web/ai-elements/loader.tsx","../../src/chat/web/ai-elements/message.tsx","../../src/chat/web/ai-elements/reasoning.tsx","../../src/chat/web/ai-elements/tool.tsx","../../src/chat/web/components/mcp-app-frame.tsx","../../src/chat/web/components/widget-error-boundary.tsx","../../src/chat/web/components/message-list.tsx","../../src/chat/web/components/suggestions.tsx","../../src/chat/web/hooks/use-chat-engine.ts","../../src/chat/web/hooks/use-suggestions.ts","../../src/chat/web/hooks/use-typing-placeholder.ts","../../src/chat/web/theme.ts","../../src/chat/web/layouts/chat-card.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n\tforwardRef,\n\tuseCallback,\n\tuseEffect,\n\tuseImperativeHandle,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport type { ChatBarProps, ChatHandle } from \"../@types\";\nimport {\n\tConversation,\n\tConversationContent,\n\tConversationScrollButton,\n} from \"../ai-elements/conversation\";\nimport {\n\tPromptInput,\n\tPromptInputAddAttachments,\n\tPromptInputSubmit,\n\tPromptInputTextarea,\n} from \"../ai-elements/prompt-input\";\nimport { ChatQueue } from \"../components/chat-queue\";\nimport { MessageList } from \"../components/message-list\";\nimport { Suggestions } from \"../components/suggestions\";\nimport { useChatEngine } from \"../hooks/use-chat-engine\";\nimport { useSuggestions } from \"../hooks/use-suggestions\";\nimport { useTypingPlaceholder } from \"../hooks/use-typing-placeholder\";\nimport { cn } from \"../lib/utils\";\nimport { isDarkTheme, mergeTheme, themeToCSSProperties } from \"../theme\";\n\nexport const ChatBar = forwardRef<ChatHandle, ChatBarProps>(\n\tfunction ChatBar(props, ref) {\n\t\tconst {\n\t\t\ttheme: userTheme,\n\t\t\twidth = 600,\n\t\t\texpandedHeight = 400,\n\t\t\tallowAttachments = false,\n\t\t\twelcomeMessage,\n\t\t\tplaceholder = \"Ask me anything...\",\n\t\t\ttriggerEvent = \"triggerDemoRequest\",\n\t\t\tresourceEndpoint,\n\t\t\tapi,\n\t\t} = props;\n\n\t\tconst effectiveResourceEndpoint =\n\t\t\tresourceEndpoint ?? (api ? `${api}/resource` : undefined);\n\n\t\tconst resolvedTheme = mergeTheme(userTheme);\n\t\tconst cssVars = themeToCSSProperties(resolvedTheme);\n\t\tconst isDark = isDarkTheme(resolvedTheme);\n\n\t\tconst engine = useChatEngine(props);\n\n\t\tconst suggestionsState = useSuggestions({\n\t\t\tmessages: engine.messages,\n\t\t\tstatus: engine.status,\n\t\t\tconfig: props.suggestions,\n\t\t});\n\n\t\tconst handleWidgetMessage = useCallback(\n\t\t\t(message: {\n\t\t\t\trole: string;\n\t\t\t\tcontent: Array<{ type: string; text?: string }>;\n\t\t\t}) => {\n\t\t\t\tconst text = message.content\n\t\t\t\t\t.map((c) => c.text ?? \"\")\n\t\t\t\t\t.join(\"\")\n\t\t\t\t\t.trim();\n\t\t\t\tif (text) engine.handleSubmit({ text, files: [] });\n\t\t\t},\n\t\t\t[engine.handleSubmit],\n\t\t);\n\n\t\tconst handleSuggestionSelect = useCallback(\n\t\t\t(suggestion: string) => {\n\t\t\t\tsuggestionsState.clear();\n\t\t\t\tengine.handleSubmit({ text: suggestion, files: [] });\n\t\t\t},\n\t\t\t[suggestionsState.clear, engine.handleSubmit],\n\t\t);\n\n\t\tconst animatedPlaceholder = useTypingPlaceholder(placeholder, !engine.text);\n\n\t\tconst [isFocused, setIsFocused] = useState(false);\n\t\tconst [isHighlighted, setIsHighlighted] = useState(false);\n\t\tconst [fullscreenToolCallId, setFullscreenToolCallId] = useState<\n\t\t\tstring | null\n\t\t>(null);\n\t\tconst containerRef = useRef<HTMLDivElement>(null);\n\t\tconst highlightTimerRef = useRef<ReturnType<typeof setTimeout>>(undefined);\n\n\t\tconst focusInput = useCallback(() => {\n\t\t\tconst container = containerRef.current;\n\t\t\tif (!container) return;\n\t\t\tcontainer.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n\t\t\tconst textarea = container.querySelector(\"textarea\");\n\t\t\tif (textarea) {\n\t\t\t\tsetTimeout(() => textarea.focus(), 300);\n\t\t\t}\n\t\t\tsetIsFocused(true);\n\t\t\tsetIsHighlighted(true);\n\t\t\tclearTimeout(highlightTimerRef.current);\n\t\t\thighlightTimerRef.current = setTimeout(\n\t\t\t\t() => setIsHighlighted(false),\n\t\t\t\t2000,\n\t\t\t);\n\t\t}, []);\n\n\t\tuseImperativeHandle(\n\t\t\tref,\n\t\t\t() => ({\n\t\t\t\tsendMessage: (text: string) => {\n\t\t\t\t\tengine.handleSubmit({ text, files: [] });\n\t\t\t\t\tfocusInput();\n\t\t\t\t},\n\t\t\t\tfocus: focusInput,\n\t\t\t}),\n\t\t\t[engine.handleSubmit, focusInput],\n\t\t);\n\n\t\t// Listen for custom trigger event (e.g. \"triggerDemoRequest\")\n\t\tuseEffect(() => {\n\t\t\tif (!triggerEvent) return;\n\t\t\tconst handler = (e: Event) => {\n\t\t\t\tconst detail = (e as CustomEvent).detail;\n\t\t\t\tconst message =\n\t\t\t\t\ttypeof detail?.message === \"string\" ? detail.message : undefined;\n\t\t\t\tif (message) {\n\t\t\t\t\tengine.handleSubmit({ text: message, files: [] });\n\t\t\t\t}\n\t\t\t\tfocusInput();\n\t\t\t};\n\t\t\twindow.addEventListener(triggerEvent, handler);\n\t\t\treturn () => window.removeEventListener(triggerEvent, handler);\n\t\t}, [triggerEvent, engine.handleSubmit, focusInput]);\n\t\tconst isExpanded = isFocused || fullscreenToolCallId !== null;\n\n\t\t// Close on outside click\n\t\tuseEffect(() => {\n\t\t\tif (!isFocused) return;\n\t\t\tconst handleClickOutside = (e: MouseEvent) => {\n\t\t\t\tif (\n\t\t\t\t\tcontainerRef.current &&\n\t\t\t\t\t!containerRef.current.contains(e.target as Node)\n\t\t\t\t) {\n\t\t\t\t\tsetIsFocused(false);\n\t\t\t\t}\n\t\t\t};\n\t\t\tdocument.addEventListener(\"mousedown\", handleClickOutside);\n\t\t\treturn () =>\n\t\t\t\tdocument.removeEventListener(\"mousedown\", handleClickOutside);\n\t\t}, [isFocused]);\n\n\t\tconst handleFocus = useCallback(() => {\n\t\t\tsetIsFocused(true);\n\t\t}, []);\n\n\t\treturn (\n\t\t\t<div\n\t\t\t\tref={containerRef}\n\t\t\t\tstyle={{ ...cssVars, width }}\n\t\t\t\tdata-waniwani-chat=\"\"\n\t\t\t\tdata-waniwani-layout=\"bar\"\n\t\t\t\t{...(isDark ? { \"data-waniwani-dark\": \"\" } : {})}\n\t\t\t\tclassName=\"ww:flex ww:flex-col ww:font-[family-name:var(--ww-font)] ww:text-foreground\"\n\t\t\t>\n\t\t\t\t{/* Messages panel — fades up on expand */}\n\t\t\t\t<div\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"ww:overflow-hidden ww:bg-background/80 ww:backdrop-blur-xl ww:transition-all ww:duration-300 ww:ease-out\",\n\t\t\t\t\t\tisExpanded\n\t\t\t\t\t\t\t? \"ww:opacity-100 ww:translate-y-0\"\n\t\t\t\t\t\t\t: \"ww:opacity-0 ww:translate-y-2 ww:pointer-events-none ww:max-h-0\",\n\t\t\t\t\t)}\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\t...(isExpanded ? { maxHeight: expandedHeight } : undefined),\n\t\t\t\t\t\tmaskImage:\n\t\t\t\t\t\t\t\"linear-gradient(to bottom, transparent, black 24px, black calc(100% - 16px), transparent), linear-gradient(to right, transparent, black 16px, black calc(100% - 16px), transparent)\",\n\t\t\t\t\t\tmaskComposite: \"intersect\",\n\t\t\t\t\t\tWebkitMaskImage:\n\t\t\t\t\t\t\t\"linear-gradient(to bottom, transparent, black 24px, black calc(100% - 16px), transparent), linear-gradient(to right, transparent, black 16px, black calc(100% - 16px), transparent)\",\n\t\t\t\t\t\tWebkitMaskComposite: \"source-in\",\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<Conversation\n\t\t\t\t\t\tclassName=\"ww:flex-1\"\n\t\t\t\t\t\tstyle={{ height: expandedHeight }}\n\t\t\t\t\t>\n\t\t\t\t\t\t<ConversationContent>\n\t\t\t\t\t\t\t<MessageList\n\t\t\t\t\t\t\t\tmessages={engine.messages}\n\t\t\t\t\t\t\t\tstatus={engine.status}\n\t\t\t\t\t\t\t\twelcomeMessage={welcomeMessage}\n\t\t\t\t\t\t\t\tresourceEndpoint={effectiveResourceEndpoint}\n\t\t\t\t\t\t\t\tisDark={isDark}\n\t\t\t\t\t\t\t\tonFollowUp={handleWidgetMessage}\n\t\t\t\t\t\t\t\tfullscreenToolCallId={fullscreenToolCallId}\n\t\t\t\t\t\t\t\tonWidgetDisplayModeChange={(mode, widget) => {\n\t\t\t\t\t\t\t\t\tsetFullscreenToolCallId(\n\t\t\t\t\t\t\t\t\t\tmode === \"fullscreen\" ? widget.toolCallId : null,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</ConversationContent>\n\t\t\t\t\t\t{!fullscreenToolCallId && <ConversationScrollButton />}\n\t\t\t\t\t</Conversation>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Suggestions */}\n\t\t\t\t<Suggestions\n\t\t\t\t\tsuggestions={suggestionsState.suggestions}\n\t\t\t\t\tisLoading={suggestionsState.isLoading}\n\t\t\t\t\tonSelect={handleSuggestionSelect}\n\t\t\t\t/>\n\n\t\t\t\t{/* Queue */}\n\t\t\t\t<ChatQueue\n\t\t\t\t\tqueuedMessages={engine.queuedMessages}\n\t\t\t\t\tonRemove={engine.removeQueuedMessage}\n\t\t\t\t/>\n\n\t\t\t\t{/* Input bar — always visible */}\n\t\t\t\t<div className=\"ww:shrink-0\">\n\t\t\t\t\t<PromptInput\n\t\t\t\t\t\tonSubmit={engine.handleSubmit}\n\t\t\t\t\t\tglobalDrop={allowAttachments}\n\t\t\t\t\t\tmultiple={allowAttachments}\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"ww:rounded-[var(--ww-radius)] ww:shadow-sm ww:transition-all ww:duration-300 ww:ease-out\",\n\t\t\t\t\t\t\tisHighlighted &&\n\t\t\t\t\t\t\t\t\"ww:ring-2 ww:ring-blue-400/70 ww:ring-offset-2 ww:ring-offset-background\",\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"ww:flex ww:items-center ww:gap-1 ww:px-3 ww:py-2\">\n\t\t\t\t\t\t\t{allowAttachments && <PromptInputAddAttachments />}\n\t\t\t\t\t\t\t<PromptInputTextarea\n\t\t\t\t\t\t\t\tonChange={engine.handleTextChange}\n\t\t\t\t\t\t\t\tvalue={engine.text}\n\t\t\t\t\t\t\t\tplaceholder={animatedPlaceholder}\n\t\t\t\t\t\t\t\tonFocus={handleFocus}\n\t\t\t\t\t\t\t\tclassName=\"ww:min-h-0 ww:py-1.5 ww:px-2\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<PromptInputSubmit\n\t\t\t\t\t\t\t\tstatus={engine.status}\n\t\t\t\t\t\t\t\tdisabled={engine.queueFull}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</PromptInput>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t},\n);\n","\"use client\";\n\nimport { ArrowDownIcon } from \"lucide-react\";\nimport type { ComponentProps } from \"react\";\nimport { useCallback } from \"react\";\nimport { StickToBottom, useStickToBottomContext } from \"use-stick-to-bottom\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"../ui/button\";\n\nexport type ConversationProps = ComponentProps<typeof StickToBottom>;\n\nexport const Conversation = ({ className, ...props }: ConversationProps) => (\n\t<StickToBottom\n\t\tclassName={cn(\"ww:relative ww:flex-1 ww:overflow-y-hidden\", className)}\n\t\tinitial=\"smooth\"\n\t\tresize=\"smooth\"\n\t\trole=\"log\"\n\t\t{...props}\n\t/>\n);\n\nexport type ConversationContentProps = ComponentProps<\n\ttypeof StickToBottom.Content\n>;\n\nexport const ConversationContent = ({\n\tclassName,\n\t...props\n}: ConversationContentProps) => (\n\t<StickToBottom.Content\n\t\tclassName={cn(\"ww:flex ww:flex-col ww:gap-8 ww:p-4\", className)}\n\t\t{...props}\n\t/>\n);\n\nexport type ConversationScrollButtonProps = ComponentProps<typeof Button>;\n\nexport const ConversationScrollButton = ({\n\tclassName,\n\t...props\n}: ConversationScrollButtonProps) => {\n\tconst { isAtBottom, scrollToBottom } = useStickToBottomContext();\n\n\tconst handleScrollToBottom = useCallback(() => {\n\t\tscrollToBottom();\n\t}, [scrollToBottom]);\n\n\treturn (\n\t\t!isAtBottom && (\n\t\t\t<Button\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"ww:absolute ww:bottom-4 ww:left-[50%] ww:translate-x-[-50%] ww:rounded-full\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tonClick={handleScrollToBottom}\n\t\t\t\tsize=\"icon\"\n\t\t\t\tvariant=\"outline\"\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t<ArrowDownIcon className=\"ww:size-4\" />\n\t\t\t</Button>\n\t\t)\n\t);\n};\n","import { type ClassValue, clsx } from \"clsx\";\nimport { extendTailwindMerge } from \"tailwind-merge\";\n\nconst twMerge = extendTailwindMerge({ prefix: \"ww\" });\n\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport type { ComponentProps } from \"react\";\nimport { cn } from \"../lib/utils\";\n\nexport type ButtonProps = ComponentProps<\"button\"> & {\n\tvariant?: \"default\" | \"outline\" | \"ghost\";\n\tsize?: \"default\" | \"sm\" | \"icon\" | \"icon-sm\";\n};\n\nexport const Button = ({\n\tclassName,\n\tvariant = \"default\",\n\tsize = \"default\",\n\ttype = \"button\",\n\t...props\n}: ButtonProps) => (\n\t<button\n\t\ttype={type}\n\t\tclassName={cn(\n\t\t\t\"ww:inline-flex ww:cursor-pointer ww:items-center ww:justify-center ww:rounded-md ww:font-medium ww:transition-colors ww:disabled:pointer-events-none ww:disabled:opacity-50\",\n\t\t\tvariant === \"default\" &&\n\t\t\t\t\"ww:bg-primary ww:text-primary-foreground ww:hover:bg-primary/90\",\n\t\t\tvariant === \"outline\" &&\n\t\t\t\t\"ww:border ww:border-border ww:bg-background ww:hover:bg-accent ww:hover:text-accent-foreground\",\n\t\t\tvariant === \"ghost\" &&\n\t\t\t\t\"ww:hover:bg-accent ww:hover:text-accent-foreground\",\n\t\t\tsize === \"default\" && \"ww:h-9 ww:px-4 ww:py-2 ww:text-sm\",\n\t\t\tsize === \"sm\" && \"ww:h-8 ww:px-3 ww:text-xs\",\n\t\t\tsize === \"icon\" && \"ww:size-9\",\n\t\t\tsize === \"icon-sm\" && \"ww:size-7\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n);\n","\"use client\";\n\nimport type { ChatStatus, FileUIPart } from \"ai\";\nimport {\n\tArrowUpIcon,\n\tLoaderIcon,\n\tPaperclipIcon,\n\tSquareIcon,\n\tXIcon,\n} from \"lucide-react\";\nimport { nanoid } from \"nanoid\";\nimport type {\n\tChangeEvent,\n\tClipboardEventHandler,\n\tComponentProps,\n\tFormEvent,\n\tFormEventHandler,\n\tHTMLAttributes,\n\tKeyboardEventHandler,\n} from \"react\";\nimport {\n\tcreateContext,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"../ui/button\";\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nconst convertBlobUrlToDataUrl = async (url: string): Promise<string | null> => {\n\ttry {\n\t\tconst response = await fetch(url);\n\t\tconst blob = await response.blob();\n\t\treturn new Promise((resolve) => {\n\t\t\tconst reader = new FileReader();\n\t\t\treader.onloadend = () => resolve(reader.result as string);\n\t\t\treader.onerror = () => resolve(null);\n\t\t\treader.readAsDataURL(blob);\n\t\t});\n\t} catch {\n\t\treturn null;\n\t}\n};\n\n// ============================================================================\n// Attachments Context\n// ============================================================================\n\nexport interface AttachmentsContext {\n\tfiles: (FileUIPart & { id: string })[];\n\tadd: (files: File[] | FileList) => void;\n\tremove: (id: string) => void;\n\tclear: () => void;\n\topenFileDialog: () => void;\n}\n\nconst LocalAttachmentsContext = createContext<AttachmentsContext | null>(null);\n\nexport const usePromptInputAttachments = () => {\n\tconst context = useContext(LocalAttachmentsContext);\n\tif (!context) {\n\t\tthrow new Error(\n\t\t\t\"usePromptInputAttachments must be used within a PromptInput\",\n\t\t);\n\t}\n\treturn context;\n};\n\n// ============================================================================\n// PromptInput Message Type\n// ============================================================================\n\nexport interface PromptInputMessage {\n\ttext: string;\n\tfiles: FileUIPart[];\n}\n\n// ============================================================================\n// PromptInput\n// ============================================================================\n\nexport type PromptInputProps = Omit<\n\tHTMLAttributes<HTMLFormElement>,\n\t\"onSubmit\"\n> & {\n\taccept?: string;\n\tmultiple?: boolean;\n\tglobalDrop?: boolean;\n\tmaxFiles?: number;\n\tmaxFileSize?: number;\n\tonSubmit: (\n\t\tmessage: PromptInputMessage,\n\t\tevent: FormEvent<HTMLFormElement>,\n\t) => void | Promise<void>;\n};\n\nexport const PromptInput = ({\n\tclassName,\n\taccept,\n\tmultiple,\n\tglobalDrop,\n\tmaxFiles,\n\tmaxFileSize,\n\tonSubmit,\n\tchildren,\n\t...props\n}: PromptInputProps) => {\n\tconst inputRef = useRef<HTMLInputElement | null>(null);\n\tconst formRef = useRef<HTMLFormElement | null>(null);\n\tconst [items, setItems] = useState<(FileUIPart & { id: string })[]>([]);\n\tconst filesRef = useRef(items);\n\n\tuseEffect(() => {\n\t\tfilesRef.current = items;\n\t}, [items]);\n\n\tconst openFileDialog = useCallback(() => {\n\t\tinputRef.current?.click();\n\t}, []);\n\n\tconst add = useCallback(\n\t\t(fileList: File[] | FileList) => {\n\t\t\tconst incoming = [...fileList];\n\t\t\tif (incoming.length === 0) return;\n\n\t\t\tconst withinSize = (f: File) =>\n\t\t\t\tmaxFileSize ? f.size <= maxFileSize : true;\n\t\t\tconst valid = incoming.filter(withinSize);\n\n\t\t\tsetItems((prev) => {\n\t\t\t\tconst capacity =\n\t\t\t\t\ttypeof maxFiles === \"number\"\n\t\t\t\t\t\t? Math.max(0, maxFiles - prev.length)\n\t\t\t\t\t\t: undefined;\n\t\t\t\tconst capped =\n\t\t\t\t\ttypeof capacity === \"number\" ? valid.slice(0, capacity) : valid;\n\t\t\t\treturn [\n\t\t\t\t\t...prev,\n\t\t\t\t\t...capped.map((file) => ({\n\t\t\t\t\t\tfilename: file.name,\n\t\t\t\t\t\tid: nanoid(),\n\t\t\t\t\t\tmediaType: file.type,\n\t\t\t\t\t\ttype: \"file\" as const,\n\t\t\t\t\t\turl: URL.createObjectURL(file),\n\t\t\t\t\t})),\n\t\t\t\t];\n\t\t\t});\n\t\t},\n\t\t[maxFiles, maxFileSize],\n\t);\n\n\tconst remove = useCallback((id: string) => {\n\t\tsetItems((prev) => {\n\t\t\tconst found = prev.find((f) => f.id === id);\n\t\t\tif (found?.url) URL.revokeObjectURL(found.url);\n\t\t\treturn prev.filter((f) => f.id !== id);\n\t\t});\n\t}, []);\n\n\tconst clear = useCallback(() => {\n\t\tsetItems((prev) => {\n\t\t\tfor (const f of prev) {\n\t\t\t\tif (f.url) URL.revokeObjectURL(f.url);\n\t\t\t}\n\t\t\treturn [];\n\t\t});\n\t}, []);\n\n\t// Cleanup blob URLs on unmount\n\tuseEffect(\n\t\t() => () => {\n\t\t\tfor (const f of filesRef.current) {\n\t\t\t\tif (f.url) URL.revokeObjectURL(f.url);\n\t\t\t}\n\t\t},\n\t\t[],\n\t);\n\n\tconst handleChange = useCallback(\n\t\t(event: ChangeEvent<HTMLInputElement>) => {\n\t\t\tif (event.currentTarget.files) {\n\t\t\t\tadd(event.currentTarget.files);\n\t\t\t}\n\t\t\tevent.currentTarget.value = \"\";\n\t\t},\n\t\t[add],\n\t);\n\n\t// Global drop support\n\tuseEffect(() => {\n\t\tif (!globalDrop) return;\n\t\tconst onDragOver = (e: DragEvent) => {\n\t\t\tif (e.dataTransfer?.types?.includes(\"Files\")) e.preventDefault();\n\t\t};\n\t\tconst onDrop = (e: DragEvent) => {\n\t\t\tif (e.dataTransfer?.types?.includes(\"Files\")) e.preventDefault();\n\t\t\tif (e.dataTransfer?.files && e.dataTransfer.files.length > 0) {\n\t\t\t\tadd(e.dataTransfer.files);\n\t\t\t}\n\t\t};\n\t\tdocument.addEventListener(\"dragover\", onDragOver);\n\t\tdocument.addEventListener(\"drop\", onDrop);\n\t\treturn () => {\n\t\t\tdocument.removeEventListener(\"dragover\", onDragOver);\n\t\t\tdocument.removeEventListener(\"drop\", onDrop);\n\t\t};\n\t}, [add, globalDrop]);\n\n\tconst handleSubmit: FormEventHandler<HTMLFormElement> = useCallback(\n\t\tasync (event) => {\n\t\t\tevent.preventDefault();\n\t\t\tconst form = event.currentTarget;\n\t\t\tconst formData = new FormData(form);\n\t\t\tconst text = (formData.get(\"message\") as string) || \"\";\n\n\t\t\tform.reset();\n\n\t\t\tconst convertedFiles: FileUIPart[] = await Promise.all(\n\t\t\t\titems.map(async ({ id: _id, ...item }) => {\n\t\t\t\t\tif (item.url?.startsWith(\"blob:\")) {\n\t\t\t\t\t\tconst dataUrl = await convertBlobUrlToDataUrl(item.url);\n\t\t\t\t\t\treturn { ...item, url: dataUrl ?? item.url };\n\t\t\t\t\t}\n\t\t\t\t\treturn item;\n\t\t\t\t}),\n\t\t\t);\n\n\t\t\ttry {\n\t\t\t\tconst result = onSubmit({ files: convertedFiles, text }, event);\n\t\t\t\tif (result instanceof Promise) {\n\t\t\t\t\tawait result;\n\t\t\t\t}\n\t\t\t\tclear();\n\t\t\t} catch {\n\t\t\t\t// Don't clear on error\n\t\t\t}\n\t\t},\n\t\t[items, onSubmit, clear],\n\t);\n\n\tconst attachmentsCtx = useMemo<AttachmentsContext>(\n\t\t() => ({\n\t\t\tadd,\n\t\t\tclear,\n\t\t\tfiles: items,\n\t\t\topenFileDialog,\n\t\t\tremove,\n\t\t}),\n\t\t[items, add, remove, clear, openFileDialog],\n\t);\n\n\treturn (\n\t\t<LocalAttachmentsContext.Provider value={attachmentsCtx}>\n\t\t\t<input\n\t\t\t\taccept={accept}\n\t\t\t\taria-label=\"Upload files\"\n\t\t\t\tclassName=\"ww:hidden\"\n\t\t\t\tmultiple={multiple}\n\t\t\t\tonChange={handleChange}\n\t\t\t\tref={inputRef}\n\t\t\t\ttitle=\"Upload files\"\n\t\t\t\ttype=\"file\"\n\t\t\t/>\n\t\t\t<form\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"ww:flex ww:w-full ww:flex-col ww:rounded-lg ww:border ww:border-border ww:bg-background\",\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t\tonSubmit={handleSubmit}\n\t\t\t\tref={formRef}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t</form>\n\t\t</LocalAttachmentsContext.Provider>\n\t);\n};\n\n// ============================================================================\n// Layout Components\n// ============================================================================\n\nexport type PromptInputHeaderProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputHeader = ({\n\tclassName,\n\t...props\n}: PromptInputHeaderProps) => (\n\t<div\n\t\tclassName={cn(\"ww:flex ww:flex-wrap ww:gap-1 ww:px-3 ww:pt-3\", className)}\n\t\t{...props}\n\t/>\n);\n\nexport type PromptInputBodyProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputBody = ({\n\tclassName,\n\t...props\n}: PromptInputBodyProps) => (\n\t<div className={cn(\"ww:contents\", className)} {...props} />\n);\n\nexport type PromptInputFooterProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputFooter = ({\n\tclassName,\n\t...props\n}: PromptInputFooterProps) => (\n\t<div\n\t\tclassName={cn(\n\t\t\t\"ww:flex ww:items-center ww:justify-between ww:gap-1 ww:px-3 ww:pb-3\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n);\n\nexport type PromptInputToolsProps = HTMLAttributes<HTMLDivElement>;\n\nexport const PromptInputTools = ({\n\tclassName,\n\t...props\n}: PromptInputToolsProps) => (\n\t<div\n\t\tclassName={cn(\"ww:flex ww:min-w-0 ww:items-center ww:gap-1\", className)}\n\t\t{...props}\n\t/>\n);\n\n// ============================================================================\n// Textarea\n// ============================================================================\n\nexport type PromptInputTextareaProps = ComponentProps<\"textarea\">;\n\nexport const PromptInputTextarea = ({\n\tonChange,\n\tonKeyDown,\n\tclassName,\n\tplaceholder = \"What would you like to know?\",\n\t...props\n}: PromptInputTextareaProps) => {\n\tconst attachments = usePromptInputAttachments();\n\tconst [isComposing, setIsComposing] = useState(false);\n\n\tconst handleKeyDown: KeyboardEventHandler<HTMLTextAreaElement> = useCallback(\n\t\t(e) => {\n\t\t\tonKeyDown?.(e);\n\t\t\tif (e.defaultPrevented) return;\n\n\t\t\tif (e.key === \"Enter\") {\n\t\t\t\tif (isComposing || e.nativeEvent.isComposing) return;\n\t\t\t\tif (e.shiftKey) return;\n\t\t\t\te.preventDefault();\n\n\t\t\t\tconst { form } = e.currentTarget;\n\t\t\t\tconst submitButton = form?.querySelector(\n\t\t\t\t\t'button[type=\"submit\"]',\n\t\t\t\t) as HTMLButtonElement | null;\n\t\t\t\tif (submitButton?.disabled) return;\n\t\t\t\tform?.requestSubmit();\n\t\t\t}\n\n\t\t\tif (\n\t\t\t\te.key === \"Backspace\" &&\n\t\t\t\te.currentTarget.value === \"\" &&\n\t\t\t\tattachments.files.length > 0\n\t\t\t) {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst lastAttachment = attachments.files.at(-1);\n\t\t\t\tif (lastAttachment) attachments.remove(lastAttachment.id);\n\t\t\t}\n\t\t},\n\t\t[onKeyDown, isComposing, attachments],\n\t);\n\n\tconst handlePaste: ClipboardEventHandler<HTMLTextAreaElement> = useCallback(\n\t\t(event) => {\n\t\t\tconst items = event.clipboardData?.items;\n\t\t\tif (!items) return;\n\n\t\t\tconst files: File[] = [];\n\t\t\tfor (const item of items) {\n\t\t\t\tif (item.kind === \"file\") {\n\t\t\t\t\tconst file = item.getAsFile();\n\t\t\t\t\tif (file) files.push(file);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (files.length > 0) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tattachments.add(files);\n\t\t\t}\n\t\t},\n\t\t[attachments],\n\t);\n\n\treturn (\n\t\t<textarea\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:field-sizing-content ww:max-h-48 ww:min-h-16 ww:w-full ww:resize-none ww:border-0 ww:bg-transparent ww:px-3 ww:py-3 ww:text-sm ww:outline-none ww:placeholder:text-muted-foreground\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\tname=\"message\"\n\t\t\tonCompositionEnd={() => setIsComposing(false)}\n\t\t\tonCompositionStart={() => setIsComposing(true)}\n\t\t\tonKeyDown={handleKeyDown}\n\t\t\tonPaste={handlePaste}\n\t\t\tplaceholder={placeholder}\n\t\t\tonChange={onChange}\n\t\t\t{...props}\n\t\t/>\n\t);\n};\n\n// ============================================================================\n// Submit Button\n// ============================================================================\n\nexport type PromptInputSubmitProps = ComponentProps<typeof Button> & {\n\tstatus?: ChatStatus;\n\tonStop?: () => void;\n};\n\nexport const PromptInputSubmit = ({\n\tclassName,\n\tstatus,\n\tonStop,\n\tonClick,\n\tchildren,\n\t...props\n}: PromptInputSubmitProps) => {\n\tconst isGenerating = status === \"submitted\" || status === \"streaming\";\n\n\tlet Icon = <ArrowUpIcon className=\"ww:size-4\" />;\n\tif (status === \"submitted\") {\n\t\tIcon = <LoaderIcon className=\"ww:size-4 ww:animate-spin\" />;\n\t} else if (status === \"streaming\") {\n\t\tIcon = <SquareIcon className=\"ww:size-4\" />;\n\t}\n\n\tconst handleClick = useCallback(\n\t\t(e: React.MouseEvent<HTMLButtonElement>) => {\n\t\t\tif (isGenerating && onStop) {\n\t\t\t\te.preventDefault();\n\t\t\t\tonStop();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tonClick?.(e);\n\t\t},\n\t\t[isGenerating, onStop, onClick],\n\t);\n\n\treturn (\n\t\t<Button\n\t\t\taria-label={isGenerating ? \"Stop\" : \"Submit\"}\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:bg-foreground ww:text-background ww:hover:bg-foreground\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\tonClick={handleClick}\n\t\t\tsize=\"icon-sm\"\n\t\t\ttype={isGenerating && onStop ? \"button\" : \"submit\"}\n\t\t\tvariant=\"ghost\"\n\t\t\t{...props}\n\t\t>\n\t\t\t{children ?? Icon}\n\t\t</Button>\n\t);\n};\n\n// ============================================================================\n// Attachment Add Button (simple file picker, no Radix dropdown)\n// ============================================================================\n\nexport type PromptInputAddAttachmentsProps = ComponentProps<typeof Button>;\n\nexport const PromptInputAddAttachments = ({\n\tclassName,\n\tchildren,\n\t...props\n}: PromptInputAddAttachmentsProps) => {\n\tconst attachments = usePromptInputAttachments();\n\tconst hasFiles = attachments.files.length > 0;\n\n\tif (hasFiles) {\n\t\treturn (\n\t\t\t<Button\n\t\t\t\tclassName={cn(\"ww:group ww:relative\", className)}\n\t\t\t\tonClick={() => attachments.clear()}\n\t\t\t\tsize=\"icon-sm\"\n\t\t\t\ttype=\"button\"\n\t\t\t\tvariant=\"ghost\"\n\t\t\t\taria-label=\"Remove all attachments\"\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t<span className=\"ww:flex ww:size-5 ww:items-center ww:justify-center ww:rounded-full ww:bg-primary ww:text-[10px] ww:font-medium ww:text-primary-foreground ww:transition-opacity ww:group-hover:opacity-0\">\n\t\t\t\t\t{attachments.files.length}\n\t\t\t\t</span>\n\t\t\t\t<XIcon className=\"ww:absolute ww:size-4 ww:opacity-0 ww:transition-opacity ww:group-hover:opacity-100\" />\n\t\t\t</Button>\n\t\t);\n\t}\n\n\treturn (\n\t\t<Button\n\t\t\tclassName={cn(className)}\n\t\t\tonClick={() => attachments.openFileDialog()}\n\t\t\tsize=\"icon-sm\"\n\t\t\ttype=\"button\"\n\t\t\tvariant=\"ghost\"\n\t\t\t{...props}\n\t\t>\n\t\t\t{children ?? <PaperclipIcon className=\"ww:size-4\" />}\n\t\t</Button>\n\t);\n};\n","\"use client\";\n\nimport { XIcon } from \"lucide-react\";\nimport { memo, useCallback } from \"react\";\nimport {\n\tQueue,\n\tQueueItem,\n\tQueueItemAction,\n\tQueueItemActions,\n\tQueueItemContent,\n\tQueueItemIndicator,\n} from \"../ai-elements/queue\";\nimport type { QueuedMessage } from \"../hooks/use-chat-engine\";\nimport { cn } from \"../lib/utils\";\n\ninterface ChatQueueItemProps {\n\tmessage: QueuedMessage;\n\tonRemove: (id: string) => void;\n}\n\nconst ChatQueueItem = memo(({ message, onRemove }: ChatQueueItemProps) => {\n\tconst handleRemove = useCallback(\n\t\t() => onRemove(message.id),\n\t\t[onRemove, message.id],\n\t);\n\n\treturn (\n\t\t<QueueItem>\n\t\t\t<QueueItemIndicator />\n\t\t\t<QueueItemContent>{message.text || \"(attachment)\"}</QueueItemContent>\n\t\t\t<QueueItemActions>\n\t\t\t\t<QueueItemAction aria-label=\"Remove from queue\" onClick={handleRemove}>\n\t\t\t\t\t<XIcon className=\"ww:size-3\" />\n\t\t\t\t</QueueItemAction>\n\t\t\t</QueueItemActions>\n\t\t</QueueItem>\n\t);\n});\n\nChatQueueItem.displayName = \"ChatQueueItem\";\n\ninterface ChatQueueProps {\n\tqueuedMessages: QueuedMessage[];\n\tonRemove: (id: string) => void;\n\tclassName?: string;\n}\n\nexport function ChatQueue({\n\tqueuedMessages,\n\tonRemove,\n\tclassName,\n}: ChatQueueProps) {\n\tif (queuedMessages.length === 0) return null;\n\n\treturn (\n\t\t<Queue className={cn(\"ww:border-t ww:border-border\", className)}>\n\t\t\t<div className=\"ww:text-[11px] ww:font-medium ww:text-muted-foreground ww:px-2\">\n\t\t\t\t{queuedMessages.length} queued\n\t\t\t</div>\n\t\t\t<ul>\n\t\t\t\t{queuedMessages.map((msg) => (\n\t\t\t\t\t<ChatQueueItem key={msg.id} message={msg} onRemove={onRemove} />\n\t\t\t\t))}\n\t\t\t</ul>\n\t\t</Queue>\n\t);\n}\n","\"use client\";\n\nimport type { ComponentProps } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"../ui/button\";\n\nexport type QueueProps = ComponentProps<\"div\">;\n\nexport const Queue = ({ className, ...props }: QueueProps) => (\n\t<div\n\t\tclassName={cn(\"ww:flex ww:flex-col ww:gap-1 ww:px-3 ww:py-2\", className)}\n\t\t{...props}\n\t/>\n);\n\nexport type QueueItemProps = ComponentProps<\"li\">;\n\nexport const QueueItem = ({ className, ...props }: QueueItemProps) => (\n\t<li\n\t\tclassName={cn(\n\t\t\t\"ww:group ww:flex ww:items-center ww:gap-2 ww:rounded-md ww:px-2 ww:py-1 ww:text-sm ww:transition-colors ww:hover:bg-muted\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n);\n\nexport type QueueItemIndicatorProps = ComponentProps<\"span\">;\n\nexport const QueueItemIndicator = ({\n\tclassName,\n\t...props\n}: QueueItemIndicatorProps) => (\n\t<span\n\t\tclassName={cn(\n\t\t\t\"ww:inline-block ww:size-2 ww:shrink-0 ww:rounded-full ww:border ww:border-muted-foreground/50\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n);\n\nexport type QueueItemContentProps = ComponentProps<\"span\">;\n\nexport const QueueItemContent = ({\n\tclassName,\n\t...props\n}: QueueItemContentProps) => (\n\t<span\n\t\tclassName={cn(\n\t\t\t\"ww:line-clamp-1 ww:grow ww:break-words ww:text-muted-foreground\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n);\n\nexport type QueueItemActionsProps = ComponentProps<\"div\">;\n\nexport const QueueItemActions = ({\n\tclassName,\n\t...props\n}: QueueItemActionsProps) => (\n\t<div\n\t\tclassName={cn(\n\t\t\t\"ww:flex ww:shrink-0 ww:gap-1 ww:opacity-0 ww:transition-opacity ww:group-hover:opacity-100\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n);\n\nexport type QueueItemActionProps = Omit<\n\tComponentProps<typeof Button>,\n\t\"variant\" | \"size\"\n>;\n\nexport const QueueItemAction = ({\n\tclassName,\n\t...props\n}: QueueItemActionProps) => (\n\t<Button\n\t\tclassName={cn(\n\t\t\t\"ww:size-auto ww:rounded ww:p-1 ww:text-muted-foreground ww:hover:bg-muted-foreground/10 ww:hover:text-foreground\",\n\t\t\tclassName,\n\t\t)}\n\t\tsize=\"icon\"\n\t\ttype=\"button\"\n\t\tvariant=\"ghost\"\n\t\t{...props}\n\t/>\n);\n","\"use client\";\n\nimport type { FileUIPart } from \"ai\";\nimport { FileIcon } from \"lucide-react\";\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"../lib/utils\";\n\n// ============================================================================\n// Attachments (inline list for chat bubbles)\n// ============================================================================\n\nexport type AttachmentsProps = HTMLAttributes<HTMLDivElement> & {\n\tfiles: FileUIPart[];\n};\n\nexport const Attachments = ({\n\tfiles,\n\tclassName,\n\t...props\n}: AttachmentsProps) => {\n\tif (files.length === 0) return null;\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\"ww:flex ww:flex-wrap ww:gap-1.5\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{files.map((file, i) => (\n\t\t\t\t<AttachmentItem key={i} file={file} />\n\t\t\t))}\n\t\t</div>\n\t);\n};\n\n// ============================================================================\n// AttachmentItem\n// ============================================================================\n\nfunction AttachmentItem({ file }: { file: FileUIPart }) {\n\tconst isImage = file.mediaType?.startsWith(\"image/\");\n\n\tif (isImage && file.url) {\n\t\treturn (\n\t\t\t<img\n\t\t\t\tsrc={file.url}\n\t\t\t\talt={file.filename ?? \"attachment\"}\n\t\t\t\tclassName=\"ww:h-16 ww:max-w-32 ww:rounded ww:object-cover\"\n\t\t\t/>\n\t\t);\n\t}\n\n\treturn (\n\t\t<span className=\"ww:inline-flex ww:items-center ww:gap-1.5 ww:rounded ww:bg-background/20 ww:px-2 ww:py-1 ww:text-xs\">\n\t\t\t<FileIcon className=\"ww:size-3 ww:shrink-0\" />\n\t\t\t<span className=\"ww:max-w-24 ww:truncate\">{file.filename ?? \"file\"}</span>\n\t\t</span>\n\t);\n}\n","\"use client\";\n\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"../lib/utils\";\n\nexport type LoaderProps = HTMLAttributes<HTMLDivElement> & {\n\tsize?: number;\n};\n\nexport const Loader = ({ className, size = 5, ...props }: LoaderProps) => (\n\t<div className={cn(\"ww:flex ww:items-center ww:gap-1\", className)} {...props}>\n\t\t{[0, 1, 2].map((i) => (\n\t\t\t<div\n\t\t\t\tkey={i}\n\t\t\t\tclassName=\"ww:rounded-full ww:bg-muted-foreground/60\"\n\t\t\t\tstyle={{\n\t\t\t\t\twidth: size,\n\t\t\t\t\theight: size,\n\t\t\t\t\tanimation: \"ww-pulse 1.4s ease-in-out infinite\",\n\t\t\t\t\tanimationDelay: `${i * 0.2}s`,\n\t\t\t\t}}\n\t\t\t/>\n\t\t))}\n\t</div>\n);\n","\"use client\";\n\nimport { cjk } from \"@streamdown/cjk\";\nimport { code } from \"@streamdown/code\";\nimport type { UIMessage } from \"ai\";\nimport type { ComponentProps, HTMLAttributes } from \"react\";\nimport { memo } from \"react\";\nimport { Streamdown } from \"streamdown\";\nimport { cn } from \"../lib/utils\";\n\nexport type MessageProps = HTMLAttributes<HTMLDivElement> & {\n\tfrom: UIMessage[\"role\"];\n};\n\nexport const Message = ({ className, from, ...props }: MessageProps) => (\n\t<div\n\t\tclassName={cn(\n\t\t\t\"ww:group ww:flex ww:w-full ww:max-w-[95%] ww:flex-col ww:gap-2\",\n\t\t\tfrom === \"user\" ? \"is-user ww:ml-auto ww:justify-end\" : \"is-assistant\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t/>\n);\n\nexport type MessageContentProps = HTMLAttributes<HTMLDivElement>;\n\nexport const MessageContent = ({\n\tchildren,\n\tclassName,\n\t...props\n}: MessageContentProps) => (\n\t<div\n\t\tclassName={cn(\n\t\t\t\"ww:flex ww:w-fit ww:min-w-0 ww:max-w-full ww:flex-col ww:gap-2 ww:overflow-hidden ww:text-base\",\n\t\t\t\"ww:group-[.is-user]:ml-auto ww:group-[.is-user]:rounded-lg ww:group-[.is-user]:bg-user-bubble ww:group-[.is-user]:px-4 ww:group-[.is-user]:py-3 ww:group-[.is-user]:text-primary-foreground\",\n\t\t\t\"ww:group-[.is-assistant]:text-foreground\",\n\t\t\tclassName,\n\t\t)}\n\t\t{...props}\n\t>\n\t\t{children}\n\t</div>\n);\n\nexport type MessageResponseProps = ComponentProps<typeof Streamdown>;\n\nconst streamdownPlugins = { cjk, code };\n\nexport const MessageResponse = memo(\n\t({ className, ...props }: MessageResponseProps) => (\n\t\t<Streamdown\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:size-full ww:[&>*:first-child]:mt-0 ww:[&>*:last-child]:mb-0\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\tplugins={streamdownPlugins}\n\t\t\t{...props}\n\t\t/>\n\t),\n\t(prevProps, nextProps) => prevProps.children === nextProps.children,\n);\n\nMessageResponse.displayName = \"MessageResponse\";\n","\"use client\";\n\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"../lib/utils\";\n\nexport type ReasoningProps = HTMLAttributes<HTMLPreElement> & {\n\ttext: string;\n};\n\n/** Displays reasoning text inline with muted styling. */\nexport function Reasoning({ className, text, ...props }: ReasoningProps) {\n\tif (!text) return null;\n\n\treturn (\n\t\t<pre\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:mb-2 ww:overflow-x-auto ww:whitespace-pre-wrap ww:break-words ww:text-xs ww:font-mono ww:text-muted-foreground\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{text}\n\t\t</pre>\n\t);\n}\n","\"use client\";\n\nimport type { ToolUIPart } from \"ai\";\nimport {\n\tBracesIcon,\n\tCheckIcon,\n\tChevronDownIcon,\n\tChevronRightIcon,\n\tClipboardCopyIcon,\n\tServerIcon,\n} from \"lucide-react\";\nimport type { HTMLAttributes } from \"react\";\nimport {\n\tcreateContext,\n\tuseCallback,\n\tuseContext,\n\tuseEffect,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"../ui/button\";\n\n/**\n * Produces an abbreviated single-line JSON preview for collapsed display.\n * Objects show truncated keys/values: `{ci… 'M…, pos… '2…, squa… 80}`\n * Arrays show their length: `Array(13)`\n */\nfunction truncateJSON(data: unknown, maxLength = 80): string {\n\tif (data === null || data === undefined) return String(data);\n\tif (typeof data !== \"object\") return String(data).slice(0, maxLength);\n\n\tif (Array.isArray(data)) {\n\t\treturn `Array(${data.length})`;\n\t}\n\n\tconst stringified = JSON.stringify(data);\n\tif (stringified.length <= maxLength) return stringified;\n\n\tconst entries = Object.entries(data as Record<string, unknown>);\n\tconst parts: string[] = [];\n\tlet remaining = maxLength - 2;\n\n\tfor (const [key, value] of entries) {\n\t\tif (remaining <= 8) break;\n\t\tconst keyAbbrev = key.length > 4 ? `${key.slice(0, 4)}\\u2026` : key;\n\t\tlet valStr: string;\n\t\tif (typeof value === \"string\") {\n\t\t\tvalStr = value.length > 2 ? `'${value.slice(0, 1)}\\u2026` : `'${value}'`;\n\t\t} else if (Array.isArray(value)) {\n\t\t\tvalStr = `Array(${value.length})`;\n\t\t} else if (typeof value === \"object\" && value !== null) {\n\t\t\tvalStr = `{\\u2026}`;\n\t\t} else {\n\t\t\tvalStr = String(value);\n\t\t}\n\t\tconst part = `${keyAbbrev}\\u2009${valStr}`;\n\t\tparts.push(part);\n\t\tremaining -= part.length + 3;\n\t}\n\n\treturn `{${parts.join(\", \")}}`;\n}\n\ninterface CopyButtonProps {\n\ttext: string;\n\tclassName?: string;\n}\n\n/** Ghost button that copies `text` to clipboard, showing \"Copied\" for 2s. */\nfunction CopyButton({ text, className }: CopyButtonProps) {\n\tconst [copied, setCopied] = useState(false);\n\tconst timeoutRef = useRef<ReturnType<typeof setTimeout>>(null);\n\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tif (timeoutRef.current) {\n\t\t\t\tclearTimeout(timeoutRef.current);\n\t\t\t}\n\t\t};\n\t}, []);\n\n\tconst handleCopy = useCallback(\n\t\tasync (e: React.MouseEvent) => {\n\t\t\te.stopPropagation();\n\t\t\ttry {\n\t\t\t\tawait navigator.clipboard.writeText(text);\n\t\t\t\tsetCopied(true);\n\t\t\t\tif (timeoutRef.current) {\n\t\t\t\t\tclearTimeout(timeoutRef.current);\n\t\t\t\t}\n\t\t\t\ttimeoutRef.current = setTimeout(() => setCopied(false), 2000);\n\t\t\t} catch {\n\t\t\t\t// Clipboard API not available\n\t\t\t}\n\t\t},\n\t\t[text],\n\t);\n\n\treturn (\n\t\t<Button\n\t\t\tvariant=\"ghost\"\n\t\t\tsize=\"sm\"\n\t\t\tonClick={handleCopy}\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:h-auto ww:gap-1 ww:px-1.5 ww:py-0.5 ww:text-xs ww:text-muted-foreground ww:hover:text-foreground\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t>\n\t\t\t{copied ? (\n\t\t\t\t<>\n\t\t\t\t\t<CheckIcon className=\"ww:size-3.5\" />\n\t\t\t\t\t<span>Copied</span>\n\t\t\t\t</>\n\t\t\t) : (\n\t\t\t\t<>\n\t\t\t\t\t<ClipboardCopyIcon className=\"ww:size-3.5\" />\n\t\t\t\t\t<span>Copy</span>\n\t\t\t\t</>\n\t\t\t)}\n\t\t</Button>\n\t);\n}\n\ninterface CollapsibleJSONProps extends HTMLAttributes<HTMLDivElement> {\n\tdata: unknown;\n\tlabel: string;\n}\n\n/**\n * Labeled JSON section with a Copy button and a collapsible preview.\n * Collapsed: shows a truncated single-line abbreviation with a `>` chevron.\n * Expanded: rotates the chevron and shows full pretty-printed JSON.\n */\nfunction CollapsibleJSON({\n\tdata,\n\tlabel,\n\tclassName,\n\t...props\n}: CollapsibleJSONProps) {\n\tconst [expanded, setExpanded] = useState(false);\n\tconst fullJSON = useMemo(() => JSON.stringify(data, null, 2), [data]);\n\tconst preview = truncateJSON(data);\n\n\treturn (\n\t\t<div className={cn(\"ww:rounded-lg ww:bg-tool-card\", className)} {...props}>\n\t\t\t<div className=\"ww:flex ww:items-center ww:justify-between ww:px-3 ww:pt-2.5 ww:pb-1.5\">\n\t\t\t\t<span className=\"ww:text-xs ww:font-medium ww:text-muted-foreground\">\n\t\t\t\t\t{label}\n\t\t\t\t</span>\n\t\t\t\t<CopyButton text={fullJSON} />\n\t\t\t</div>\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tonClick={() => setExpanded((v) => !v)}\n\t\t\t\tclassName=\"ww:flex ww:w-full ww:items-start ww:gap-2 ww:px-3 ww:pb-3 ww:text-left\"\n\t\t\t>\n\t\t\t\t<ChevronRightIcon\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"ww:mt-0.5 ww:size-3.5 ww:shrink-0 ww:text-muted-foreground ww:transition-transform ww:duration-150\",\n\t\t\t\t\t\texpanded && \"ww:rotate-90\",\n\t\t\t\t\t)}\n\t\t\t\t/>\n\t\t\t\t{expanded ? (\n\t\t\t\t\t<pre className=\"ww:overflow-x-auto ww:text-xs ww:font-mono ww:text-foreground ww:whitespace-pre-wrap ww:break-all\">\n\t\t\t\t\t\t<code>{fullJSON}</code>\n\t\t\t\t\t</pre>\n\t\t\t\t) : (\n\t\t\t\t\t<span className=\"ww:truncate ww:text-xs ww:font-mono ww:text-foreground/80\">\n\t\t\t\t\t\t{preview}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t</button>\n\t\t</div>\n\t);\n}\n\nconst ToolOpenContext = createContext<{\n\topen: boolean;\n\ttoggle: () => void;\n}>({ open: false, toggle: () => {} });\n\nexport type ToolProps = HTMLAttributes<HTMLDivElement> & {\n\tdefaultOpen?: boolean;\n};\n\n/**\n * Compound component root for a tool call display.\n * Provides open/closed state via context to ToolHeader and ToolContent.\n *\n * ```tsx\n * <Tool defaultOpen>\n * <ToolHeader title=\"Price estimate ready\" state=\"output-available\" />\n * <ToolContent>\n * <ToolServerInfo toolName=\"get_price_estimate\" serverName=\"Tuio v2\" />\n * <ToolInput input={args} />\n * <ToolOutput output={result} errorText={undefined} />\n * </ToolContent>\n * </Tool>\n * ```\n */\nexport function Tool({\n\tclassName,\n\tdefaultOpen = false,\n\tchildren,\n\t...props\n}: ToolProps) {\n\tconst [open, setOpen] = useState(defaultOpen);\n\treturn (\n\t\t<ToolOpenContext.Provider\n\t\t\tvalue={{ open, toggle: () => setOpen((o) => !o) }}\n\t\t>\n\t\t\t<div\n\t\t\t\tclassName={cn(\"ww:mb-4 ww:w-full\", className)}\n\t\t\t\tdata-state={open ? \"open\" : \"closed\"}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t{children}\n\t\t\t</div>\n\t\t</ToolOpenContext.Provider>\n\t);\n}\n\nexport type ToolHeaderProps = HTMLAttributes<HTMLButtonElement> & {\n\ttitle?: string;\n\tstate: ToolUIPart[\"state\"];\n};\n\n/** Clickable header that toggles the tool accordion. Shows a `{≡}` icon, title, and chevron. */\nexport function ToolHeader({\n\tclassName,\n\ttitle,\n\tstate,\n\t...props\n}: ToolHeaderProps) {\n\tconst { open, toggle } = useContext(ToolOpenContext);\n\tconst isRunning = state === \"input-available\" || state === \"input-streaming\";\n\n\treturn (\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\tonClick={toggle}\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:flex ww:w-full ww:items-center ww:justify-between ww:gap-3 ww:py-1.5\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\taria-expanded={open}\n\t\t\t{...props}\n\t\t>\n\t\t\t<div className=\"ww:flex ww:min-w-0 ww:items-center ww:gap-2\">\n\t\t\t\t<BracesIcon className=\"ww:size-4 ww:shrink-0 ww:text-muted-foreground\" />\n\t\t\t\t<span className=\"ww:truncate ww:text-sm ww:font-medium\">{title}</span>\n\t\t\t\t{isRunning && (\n\t\t\t\t\t<span className=\"ww:size-2 ww:shrink-0 ww:rounded-full ww:bg-primary ww:animate-pulse\" />\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t<ChevronDownIcon\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"ww:size-4 ww:shrink-0 ww:text-muted-foreground ww:transition-transform ww:duration-200\",\n\t\t\t\t\topen && \"ww:rotate-180\",\n\t\t\t\t)}\n\t\t\t/>\n\t\t</button>\n\t);\n}\n\nexport type ToolServerInfoProps = HTMLAttributes<HTMLDivElement> & {\n\tserverName?: string;\n\tserverIcon?: string;\n\ttoolName: string;\n};\n\n/** Optional MCP server identity card. Shows server icon + name and the tool function name. Renders nothing if no props need display. */\nexport function ToolServerInfo({\n\tclassName,\n\tserverName,\n\tserverIcon,\n\ttoolName,\n\t...props\n}: ToolServerInfoProps) {\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:flex ww:items-center ww:gap-3 ww:rounded-lg ww:border ww:border-border ww:px-3 ww:py-2.5\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{serverIcon ? (\n\t\t\t\t<img\n\t\t\t\t\tsrc={serverIcon}\n\t\t\t\t\talt={serverName ?? \"\"}\n\t\t\t\t\tclassName=\"ww:size-8 ww:shrink-0 ww:rounded-full ww:object-cover\"\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<div className=\"ww:flex ww:size-8 ww:shrink-0 ww:items-center ww:justify-center ww:rounded-full ww:border ww:border-border ww:bg-muted\">\n\t\t\t\t\t<ServerIcon className=\"ww:size-4 ww:text-muted-foreground\" />\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t<div className=\"ww:flex ww:min-w-0 ww:flex-col\">\n\t\t\t\t{serverName && (\n\t\t\t\t\t<span className=\"ww:text-xs ww:text-muted-foreground\">\n\t\t\t\t\t\t{serverName}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t\t<span className=\"ww:truncate ww:text-sm ww:font-semibold\">\n\t\t\t\t\t{toolName}\n\t\t\t\t</span>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport type ToolContentProps = HTMLAttributes<HTMLDivElement>;\n\n/** Collapsible body that animates open/closed. Content below smoothly pushes up/down via a grid-row height transition. */\nexport function ToolContent({\n\tclassName,\n\tchildren,\n\t...props\n}: ToolContentProps) {\n\tconst { open } = useContext(ToolOpenContext);\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"ww:grid ww:transition-[grid-template-rows,opacity] ww:duration-200 ww:ease-out\",\n\t\t\t\topen\n\t\t\t\t\t? \"ww:grid-rows-[1fr] ww:opacity-100\"\n\t\t\t\t\t: \"ww:grid-rows-[0fr] ww:opacity-0\",\n\t\t\t)}\n\t\t>\n\t\t\t<div className=\"ww:min-h-0 ww:overflow-hidden\">\n\t\t\t\t<div\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"ww:mt-2 ww:space-y-3 ww:rounded-lg ww:border ww:border-border ww:bg-background ww:p-3\",\n\t\t\t\t\t\tclassName,\n\t\t\t\t\t)}\n\t\t\t\t\t{...props}\n\t\t\t\t>\n\t\t\t\t\t{children}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport type ToolInputProps = HTMLAttributes<HTMLDivElement> & {\n\tinput: ToolUIPart[\"input\"];\n};\n\n/** Displays the tool call request parameters as a collapsible JSON section labeled \"Request\". */\nexport function ToolInput({ className, input, ...props }: ToolInputProps) {\n\treturn (\n\t\t<CollapsibleJSON\n\t\t\tdata={input}\n\t\t\tlabel=\"Request\"\n\t\t\tclassName={className}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n\nexport type ToolOutputProps = HTMLAttributes<HTMLDivElement> & {\n\toutput: ToolUIPart[\"output\"];\n\terrorText: ToolUIPart[\"errorText\"];\n};\n\nfunction getUiMeta(output: unknown): Record<string, unknown> | undefined {\n\tif (typeof output !== \"object\" || output === null) return undefined;\n\tconst meta = (output as Record<string, unknown>)._meta;\n\tif (typeof meta !== \"object\" || meta === null) return undefined;\n\tconst ui = (meta as Record<string, unknown>).ui;\n\tif (typeof ui !== \"object\" || ui === null) return undefined;\n\treturn ui as Record<string, unknown>;\n}\n\n/** Extract the MCP app resource URI from `output._meta.ui.resourceUri`, if present. */\nexport function getResourceUri(output: unknown): string | undefined {\n\tconst uri = getUiMeta(output)?.resourceUri;\n\treturn typeof uri === \"string\" ? uri : undefined;\n}\n\n/** Extract the auto-height flag from `output._meta.ui.autoHeight`, if present. */\nexport function getAutoHeight(output: unknown): boolean {\n\treturn getUiMeta(output)?.autoHeight === true;\n}\n\n/** Displays the tool call result as a collapsible JSON section labeled \"Response\", or an error block if `errorText` is set. */\nexport function ToolOutput({\n\tclassName,\n\toutput,\n\terrorText,\n\t...props\n}: ToolOutputProps) {\n\tif (!(output || errorText)) return null;\n\n\tif (errorText) {\n\t\treturn (\n\t\t\t<div className={cn(\"ww:space-y-2\", className)} {...props}>\n\t\t\t\t<h4 className=\"ww:text-xs ww:font-medium ww:uppercase ww:tracking-wide ww:text-muted-foreground\">\n\t\t\t\t\tError\n\t\t\t\t</h4>\n\t\t\t\t<div className=\"ww:rounded-lg ww:bg-destructive/10 ww:p-3 ww:text-xs ww:text-destructive\">\n\t\t\t\t\t{errorText}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<CollapsibleJSON\n\t\t\tdata={output}\n\t\t\tlabel=\"Response\"\n\t\t\tclassName={className}\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { cn } from \"../lib/utils\";\n\nconst DEFAULT_RESOURCE_ENDPOINT = \"/api/mcp/resource\";\nconst MAX_HEIGHT = 500;\nconst DEFAULT_HEIGHT = 0;\nconst PROTOCOL_VERSION = \"2026-01-26\";\nconst RESIZE_ANIMATION_MS = 300;\nconst HANDSHAKE_TIMEOUT_MS = 3000;\nconst MAX_RETRIES = 3;\n\nexport type McpAppDisplayMode = \"inline\" | \"pip\" | \"fullscreen\";\n\nexport interface McpAppFrameProps {\n\tresourceUri: string;\n\ttoolInput: Record<string, unknown>;\n\ttoolResult: {\n\t\tcontent?: Array<{ type: string; text?: string }>;\n\t\tstructuredContent?: Record<string, unknown>;\n\t};\n\tresourceEndpoint?: string;\n\tisDark?: boolean;\n\tclassName?: string;\n\t/** When true, the iframe height auto-adapts to its content. Set via `_meta.ui.autoHeight` in the tool result. */\n\tautoHeight?: boolean;\n\t/** Called when the view requests to open a URL */\n\tonOpenLink?: (url: string) => void;\n\t/** Called when a widget sends a follow-up message via `ui/message` */\n\tonFollowUp?: (message: {\n\t\trole: string;\n\t\tcontent: Array<{ type: string; text?: string }>;\n\t}) => void;\n\t/** Called when the widget requests a display mode change (e.g. \"fullscreen\" for expand) */\n\tonDisplayModeChange?: (mode: McpAppDisplayMode) => void;\n}\n\nexport function McpAppFrame({\n\tresourceUri,\n\ttoolInput,\n\ttoolResult,\n\tresourceEndpoint = DEFAULT_RESOURCE_ENDPOINT,\n\tisDark = false,\n\tclassName,\n\t// TODO: REMOVE — defaulting to true for playground testing\n\tautoHeight = true,\n\tonOpenLink,\n\tonFollowUp,\n\tonDisplayModeChange,\n}: McpAppFrameProps) {\n\tconst iframeRef = useRef<HTMLIFrameElement>(null);\n\tconst toolInputRef = useRef(toolInput);\n\tconst toolResultRef = useRef(toolResult);\n\tconst lastSizeRef = useRef({ width: 0, height: 0 });\n\tconst animationRef = useRef<Animation | null>(null);\n\tconst initializedRef = useRef(false);\n\tconst retryCountRef = useRef(0);\n\tconst displayModeRef = useRef<McpAppDisplayMode>(\"inline\");\n\tconst [height, setHeight] = useState(DEFAULT_HEIGHT);\n\tconst [width, setWidth] = useState<number | undefined>(undefined);\n\tconst onOpenLinkRef = useRef(onOpenLink);\n\tconst onFollowUpRef = useRef(onFollowUp);\n\tconst onDisplayModeChangeRef = useRef(onDisplayModeChange);\n\n\ttoolInputRef.current = toolInput;\n\ttoolResultRef.current = toolResult;\n\tonOpenLinkRef.current = onOpenLink;\n\tonFollowUpRef.current = onFollowUp;\n\tonDisplayModeChangeRef.current = onDisplayModeChange;\n\n\tconst clampHeight = useCallback(\n\t\t(h: number) => {\n\t\t\tif (autoHeight) return Math.max(h, 0);\n\t\t\treturn Math.min(Math.max(h, 50), MAX_HEIGHT);\n\t\t},\n\t\t[autoHeight],\n\t);\n\n\t// Build the iframe src URL directly — avoids null-origin issues with srcdoc\n\tconst iframeSrc = useMemo(\n\t\t() => `${resourceEndpoint}?uri=${encodeURIComponent(resourceUri)}`,\n\t\t[resourceEndpoint, resourceUri],\n\t);\n\n\tconst isDarkRef = useRef(isDark);\n\tisDarkRef.current = isDark;\n\n\t// Send theme changes to the iframe (only after handshake is complete)\n\tuseEffect(() => {\n\t\tif (!initializedRef.current) return;\n\t\tconst iframe = iframeRef.current;\n\t\tif (!iframe?.contentWindow) return;\n\n\t\tiframe.contentWindow.postMessage(\n\t\t\t{\n\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\tmethod: \"ui/notifications/host-context-changed\",\n\t\t\t\tparams: {\n\t\t\t\t\ttheme: isDark ? \"dark\" : \"light\",\n\t\t\t\t},\n\t\t\t},\n\t\t\t\"*\",\n\t\t);\n\t}, [isDark]);\n\n\t// Synchronous postMessage protocol handler — no async imports, no timing issues.\n\t// Handles the MCP UI protocol (ui/initialize, notifications, etc.) directly.\n\tuseEffect(() => {\n\t\tconst iframe = iframeRef.current;\n\t\tif (!iframe) return;\n\n\t\tlet disposed = false;\n\t\tlet handshakeReceived = false;\n\t\tconst debug = (...args: unknown[]) =>\n\t\t\tconsole.debug(\"[McpAppFrame]\", ...args);\n\n\t\tdebug(\"effect mounted, waiting for handshake\");\n\n\t\t// Retry: reload iframe if handshake doesn't arrive in time\n\t\tconst handshakeTimer = setTimeout(() => {\n\t\t\tif (disposed || handshakeReceived) return;\n\t\t\tif (retryCountRef.current >= MAX_RETRIES) {\n\t\t\t\tdebug(\"handshake failed after\", MAX_RETRIES, \"retries, giving up\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tretryCountRef.current += 1;\n\t\t\tdebug(\n\t\t\t\t\"handshake timeout, reloading iframe (retry\",\n\t\t\t\tretryCountRef.current,\n\t\t\t\t\"of\",\n\t\t\t\tMAX_RETRIES,\n\t\t\t\t\")\",\n\t\t\t);\n\t\t\t// Force reload with a cache-busting param\n\t\t\tconst url = new URL(iframe.src);\n\t\t\turl.searchParams.set(\"_retry\", String(retryCountRef.current));\n\t\t\tiframe.src = url.toString();\n\t\t}, HANDSHAKE_TIMEOUT_MS);\n\n\t\tconst postToIframe = (msg: Record<string, unknown>) => {\n\t\t\tdebug(\"→ send\", msg.method ?? `response:${msg.id}`, msg);\n\t\t\tiframe.contentWindow?.postMessage(msg, \"*\");\n\t\t};\n\n\t\tconst handleMessage = (event: MessageEvent) => {\n\t\t\tif (disposed) return;\n\t\t\tif (event.source !== iframe.contentWindow) return;\n\n\t\t\tconst data = event.data;\n\t\t\tif (!data || typeof data !== \"object\" || data.jsonrpc !== \"2.0\") return;\n\n\t\t\tconst method: string | undefined = data.method;\n\t\t\tconst id: number | string | undefined = data.id;\n\n\t\t\tdebug(\"← recv\", method ?? `response:${id}`, data);\n\n\t\t\t// ui/initialize — widget requests handshake\n\t\t\tif (method === \"ui/initialize\" && id != null) {\n\t\t\t\thandshakeReceived = true;\n\t\t\t\tclearTimeout(handshakeTimer);\n\t\t\t\tdebug(\"handshake started\");\n\t\t\t\tpostToIframe({\n\t\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\t\tid,\n\t\t\t\t\tresult: {\n\t\t\t\t\t\tprotocolVersion: data.params?.protocolVersion ?? PROTOCOL_VERSION,\n\t\t\t\t\t\thostInfo: { name: \"WaniWani Chat\", version: \"1.0.0\" },\n\t\t\t\t\t\thostCapabilities: {\n\t\t\t\t\t\t\topenLinks: {},\n\t\t\t\t\t\t\tmessage: {},\n\t\t\t\t\t\t},\n\t\t\t\t\t\thostContext: {\n\t\t\t\t\t\t\ttheme: isDarkRef.current ? \"dark\" : \"light\",\n\t\t\t\t\t\t\tdisplayMode: displayModeRef.current,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ui/notifications/initialized — widget confirms init, we send tool data\n\t\t\tif (method === \"ui/notifications/initialized\") {\n\t\t\t\tdebug(\"handshake complete, sending tool data\");\n\t\t\t\tinitializedRef.current = true;\n\t\t\t\tconst input = toolInputRef.current;\n\t\t\t\tconst result = toolResultRef.current;\n\n\t\t\t\tpostToIframe({\n\t\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\t\tmethod: \"ui/notifications/tool-input\",\n\t\t\t\t\tparams: { arguments: input },\n\t\t\t\t});\n\n\t\t\t\tconst content = result.content ?? [\n\t\t\t\t\t{ type: \"text\", text: JSON.stringify(result) },\n\t\t\t\t];\n\t\t\t\tpostToIframe({\n\t\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\t\tmethod: \"ui/notifications/tool-result\",\n\t\t\t\t\tparams: {\n\t\t\t\t\t\tcontent,\n\t\t\t\t\t\tstructuredContent: result.structuredContent,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ui/notifications/size-changed — widget reports content size\n\t\t\tif (method === \"ui/notifications/size-changed\") {\n\t\t\t\tconst params = data.params;\n\t\t\t\tconst newHeight =\n\t\t\t\t\ttypeof params?.height === \"number\" ? params.height : undefined;\n\t\t\t\tconst newWidth =\n\t\t\t\t\ttypeof params?.width === \"number\" ? params.width : undefined;\n\n\t\t\t\t// Deduplicate — only update if values actually changed (prevents feedback loops)\n\t\t\t\tconst last = lastSizeRef.current;\n\t\t\t\tconst heightChanged =\n\t\t\t\t\tnewHeight !== undefined && newHeight !== last.height;\n\t\t\t\tconst widthChanged = newWidth !== undefined && newWidth !== last.width;\n\n\t\t\t\tdebug(\"size-changed\", {\n\t\t\t\t\tnewHeight,\n\t\t\t\t\tnewWidth,\n\t\t\t\t\tlastHeight: last.height,\n\t\t\t\t\tlastWidth: last.width,\n\t\t\t\t\theightChanged,\n\t\t\t\t\twidthChanged,\n\t\t\t\t});\n\n\t\t\t\tif (!heightChanged && !widthChanged) return;\n\n\t\t\t\tif (heightChanged && newHeight !== undefined) {\n\t\t\t\t\tlast.height = newHeight;\n\t\t\t\t\tconst clamped = clampHeight(newHeight);\n\n\t\t\t\t\t// Get current visual height before canceling the old animation\n\t\t\t\t\tconst from = iframe.getBoundingClientRect().height;\n\n\t\t\t\t\t// Cancel previous animation so its fill: \"forwards\" stops overriding inline style\n\t\t\t\t\tif (animationRef.current) {\n\t\t\t\t\t\tanimationRef.current.cancel();\n\t\t\t\t\t\tanimationRef.current = null;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Set the target height in React state (takes effect once no animation overrides it)\n\t\t\t\t\tsetHeight(clamped);\n\n\t\t\t\t\t// Animate the height transition\n\t\t\t\t\tif (iframe.animate && Math.abs(from - clamped) > 2) {\n\t\t\t\t\t\tconst anim = iframe.animate(\n\t\t\t\t\t\t\t[{ height: `${from}px` }, { height: `${clamped}px` }],\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tduration: RESIZE_ANIMATION_MS,\n\t\t\t\t\t\t\t\teasing: \"ease-out\",\n\t\t\t\t\t\t\t\tfill: \"forwards\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t\tanimationRef.current = anim;\n\t\t\t\t\t\t// Once done, remove the animation so the inline style is the source of truth\n\t\t\t\t\t\tanim.onfinish = () => {\n\t\t\t\t\t\t\tif (animationRef.current === anim) {\n\t\t\t\t\t\t\t\tanim.cancel();\n\t\t\t\t\t\t\t\tanimationRef.current = null;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (widthChanged && autoHeight && newWidth !== undefined) {\n\t\t\t\t\tlast.width = newWidth;\n\t\t\t\t\tsetWidth(newWidth);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ui/open-link — widget requests to open a URL\n\t\t\tif (method === \"ui/open-link\" && id != null) {\n\t\t\t\tconst url = data.params?.url;\n\t\t\t\tif (typeof url === \"string\") {\n\t\t\t\t\tif (onOpenLinkRef.current) {\n\t\t\t\t\t\tonOpenLinkRef.current(url);\n\t\t\t\t\t} else {\n\t\t\t\t\t\twindow.open(url, \"_blank\", \"noopener,noreferrer\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tpostToIframe({ jsonrpc: \"2.0\", id, result: {} });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ui/message — widget sends a chat message\n\t\t\tif (method === \"ui/message\" && id != null) {\n\t\t\t\tif (onFollowUpRef.current && data.params) {\n\t\t\t\t\tonFollowUpRef.current(data.params);\n\t\t\t\t}\n\t\t\t\tpostToIframe({ jsonrpc: \"2.0\", id, result: {} });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ui/request-display-mode — widget requests fullscreen/inline/pip\n\t\t\tif (method === \"ui/request-display-mode\" && id != null) {\n\t\t\t\tconst requested = data.params?.mode;\n\t\t\t\tconst granted =\n\t\t\t\t\trequested === \"fullscreen\" ||\n\t\t\t\t\trequested === \"inline\" ||\n\t\t\t\t\trequested === \"pip\"\n\t\t\t\t\t\t? requested\n\t\t\t\t\t\t: \"inline\";\n\t\t\t\tdisplayModeRef.current = granted;\n\t\t\t\t// Reply with the granted mode\n\t\t\t\tpostToIframe({\n\t\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\t\tid,\n\t\t\t\t\tresult: { mode: granted },\n\t\t\t\t});\n\t\t\t\t// Notify the widget of the context change\n\t\t\t\tpostToIframe({\n\t\t\t\t\tjsonrpc: \"2.0\",\n\t\t\t\t\tmethod: \"ui/notifications/host-context-changed\",\n\t\t\t\t\tparams: { displayMode: granted },\n\t\t\t\t});\n\t\t\t\tonDisplayModeChangeRef.current?.(granted);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ui/resource-teardown — graceful shutdown\n\t\t\tif (method === \"ui/resource-teardown\" && id != null) {\n\t\t\t\tpostToIframe({ jsonrpc: \"2.0\", id, result: {} });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// ping — keep-alive\n\t\t\tif (method === \"ping\" && id != null) {\n\t\t\t\tpostToIframe({ jsonrpc: \"2.0\", id, result: {} });\n\t\t\t}\n\t\t};\n\n\t\twindow.addEventListener(\"message\", handleMessage);\n\n\t\treturn () => {\n\t\t\tdebug(\"effect cleanup (disposed)\");\n\t\t\tdisposed = true;\n\t\t\tclearTimeout(handshakeTimer);\n\t\t\twindow.removeEventListener(\"message\", handleMessage);\n\t\t};\n\t}, [autoHeight, clampHeight]);\n\n\treturn (\n\t\t<iframe\n\t\t\tref={iframeRef}\n\t\t\tsrc={iframeSrc}\n\t\t\tsandbox=\"allow-scripts allow-forms allow-same-origin\"\n\t\t\tclassName={cn(\"ww:rounded-md ww:border ww:border-border\", className)}\n\t\t\tstyle={{\n\t\t\t\theight,\n\t\t\t\tminWidth: width ? `min(${width}px, 100%)` : undefined,\n\t\t\t\twidth: \"100%\",\n\t\t\t\tborder: \"none\",\n\t\t\t\tcolorScheme: \"auto\",\n\t\t\t}}\n\t\t\ttitle=\"MCP App\"\n\t\t/>\n\t);\n}\n","\"use client\";\n\nimport { Component, type ReactNode } from \"react\";\n\ninterface Props {\n\tchildren: ReactNode;\n}\n\ninterface State {\n\thasError: boolean;\n}\n\nexport class WidgetErrorBoundary extends Component<Props, State> {\n\tstate: State = { hasError: false };\n\n\tstatic getDerivedStateFromError(): State {\n\t\treturn { hasError: true };\n\t}\n\n\tcomponentDidCatch(error: Error) {\n\t\tconsole.warn(\"[WaniWani] Widget failed to render:\", error.message);\n\t}\n\n\trender() {\n\t\tif (this.state.hasError) {\n\t\t\treturn (\n\t\t\t\t<div className=\"ww:flex ww:items-center ww:justify-between ww:rounded-md ww:border ww:border-border ww:bg-muted/50 ww:px-4 ww:py-3 ww:text-sm ww:text-muted-foreground\">\n\t\t\t\t\t<span>Widget failed to load</span>\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonClick={() => this.setState({ hasError: false })}\n\t\t\t\t\t\tclassName=\"ww:text-xs ww:font-medium ww:text-primary ww:hover:underline\"\n\t\t\t\t\t>\n\t\t\t\t\t\tRetry\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t);\n\t\t}\n\n\t\treturn this.props.children;\n\t}\n}\n","\"use client\";\n\nimport type { ChatStatus, ReasoningUIPart, ToolUIPart, UIMessage } from \"ai\";\n\nimport { Attachments } from \"../ai-elements/attachments\";\nimport { Loader } from \"../ai-elements/loader\";\nimport {\n\tMessage,\n\tMessageContent,\n\tMessageResponse,\n} from \"../ai-elements/message\";\nimport { Reasoning } from \"../ai-elements/reasoning\";\nimport {\n\tgetAutoHeight,\n\tgetResourceUri,\n\tTool,\n\tToolContent,\n\tToolHeader,\n\tToolInput,\n\tToolOutput,\n} from \"../ai-elements/tool\";\nimport type { McpAppDisplayMode } from \"./mcp-app-frame\";\nimport { McpAppFrame } from \"./mcp-app-frame\";\nimport { WidgetErrorBoundary } from \"./widget-error-boundary\";\n\n/** Converts `get_price_estimate` or `compare-prices` → `Get price estimate` / `Compare prices` */\nfunction formatToolName(name: string): string {\n\treturn name.replace(/[-_]/g, \" \").replace(/^\\w/, (c) => c.toUpperCase());\n}\n\nexport interface FullscreenWidget {\n\ttoolCallId: string;\n\tresourceUri: string;\n\ttoolInput: Record<string, unknown>;\n\ttoolResult: {\n\t\tcontent?: Array<{ type: string; text?: string }>;\n\t\tstructuredContent?: Record<string, unknown>;\n\t};\n\tautoHeight: boolean;\n}\n\ninterface MessageListProps {\n\tmessages: UIMessage[];\n\tstatus: ChatStatus;\n\twelcomeMessage?: string;\n\tresourceEndpoint?: string;\n\tisDark?: boolean;\n\tonFollowUp?: (message: {\n\t\trole: string;\n\t\tcontent: Array<{ type: string; text?: string }>;\n\t}) => void;\n\tonWidgetDisplayModeChange?: (\n\t\tmode: McpAppDisplayMode,\n\t\twidget: FullscreenWidget,\n\t) => void;\n\t/** When set, only the matching widget is shown (fullscreen mode). The iframe stays mounted. */\n\tfullscreenToolCallId?: string | null;\n}\n\nexport function MessageList({\n\tmessages,\n\tstatus,\n\twelcomeMessage,\n\tresourceEndpoint,\n\tisDark,\n\tonFollowUp,\n\tonWidgetDisplayModeChange,\n\tfullscreenToolCallId,\n}: MessageListProps) {\n\tconst isLoading = status === \"submitted\" || status === \"streaming\";\n\tconst lastMessage = messages[messages.length - 1];\n\tconst hasMessages = messages.length > 0;\n\tconst showLoaderBubble =\n\t\tisLoading && (!hasMessages || lastMessage.role === \"user\");\n\n\treturn (\n\t\t<>\n\t\t\t{welcomeMessage && !fullscreenToolCallId && (\n\t\t\t\t<Message from=\"assistant\">\n\t\t\t\t\t<MessageContent>\n\t\t\t\t\t\t<MessageResponse>{welcomeMessage}</MessageResponse>\n\t\t\t\t\t</MessageContent>\n\t\t\t\t</Message>\n\t\t\t)}\n\t\t\t{messages.map((message) => {\n\t\t\t\tconst textParts = message.parts.filter((p) => p.type === \"text\");\n\t\t\t\tconst reasoningParts = message.parts.filter(\n\t\t\t\t\t(p): p is ReasoningUIPart => p.type === \"reasoning\",\n\t\t\t\t);\n\t\t\t\tconst fileParts = message.parts.filter((p) => p.type === \"file\");\n\t\t\t\tconst toolParts = message.parts.filter(\n\t\t\t\t\t(\n\t\t\t\t\t\tp,\n\t\t\t\t\t): p is typeof p & {\n\t\t\t\t\t\ttoolCallId: string;\n\t\t\t\t\t\ttoolName: string;\n\t\t\t\t\t\tstate: ToolUIPart[\"state\"];\n\t\t\t\t\t\tinput: unknown;\n\t\t\t\t\t\ttitle?: string;\n\t\t\t\t\t} => \"toolCallId\" in p,\n\t\t\t\t);\n\t\t\t\tconst isLastAssistant =\n\t\t\t\t\tmessage === lastMessage && message.role === \"assistant\";\n\t\t\t\tconst hasTextContent = textParts.length > 0;\n\n\t\t\t\t// Skip messages that don't contain the fullscreen widget\n\t\t\t\tconst hasFullscreenWidget =\n\t\t\t\t\tfullscreenToolCallId &&\n\t\t\t\t\ttoolParts.some((p) => p.toolCallId === fullscreenToolCallId);\n\t\t\t\tif (fullscreenToolCallId && !hasFullscreenWidget) return null;\n\n\t\t\t\treturn (\n\t\t\t\t\t<Message from={message.role} key={message.id}>\n\t\t\t\t\t\t{/* Hide reasoning when a widget is fullscreen */}\n\t\t\t\t\t\t{reasoningParts.map((part, i) => (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tkey={`reasoning-${message.id}-${i}`}\n\t\t\t\t\t\t\t\tstyle={fullscreenToolCallId ? { display: \"none\" } : undefined}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Reasoning text={part.text} />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t))}\n\t\t\t\t\t\t{toolParts.map((part) => {\n\t\t\t\t\t\t\tconst output = \"output\" in part ? part.output : undefined;\n\t\t\t\t\t\t\tconst resourceUri =\n\t\t\t\t\t\t\t\toutput !== undefined ? getResourceUri(output) : undefined;\n\t\t\t\t\t\t\tconst autoHeight =\n\t\t\t\t\t\t\t\toutput !== undefined ? getAutoHeight(output) : false;\n\t\t\t\t\t\t\tconst isFullscreen = part.toolCallId === fullscreenToolCallId;\n\t\t\t\t\t\t\tconst isHidden = fullscreenToolCallId != null && !isFullscreen;\n\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tkey={part.toolCallId}\n\t\t\t\t\t\t\t\t\tstyle={\n\t\t\t\t\t\t\t\t\t\tisHidden\n\t\t\t\t\t\t\t\t\t\t\t? { display: \"none\" }\n\t\t\t\t\t\t\t\t\t\t\t: isFullscreen\n\t\t\t\t\t\t\t\t\t\t\t\t? { height: \"100%\" }\n\t\t\t\t\t\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{/* Keep Tool in tree but hide when fullscreen (preserves child positions) */}\n\t\t\t\t\t\t\t\t\t<div style={isFullscreen ? { display: \"none\" } : undefined}>\n\t\t\t\t\t\t\t\t\t\t<Tool defaultOpen={part.state === \"output-available\"}>\n\t\t\t\t\t\t\t\t\t\t\t<ToolHeader\n\t\t\t\t\t\t\t\t\t\t\t\ttitle={part.title ?? formatToolName(part.toolName)}\n\t\t\t\t\t\t\t\t\t\t\t\tstate={part.state}\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t<ToolContent>\n\t\t\t\t\t\t\t\t\t\t\t\t<ToolInput input={part.input} />\n\t\t\t\t\t\t\t\t\t\t\t\t{output !== undefined && (\n\t\t\t\t\t\t\t\t\t\t\t\t\t<ToolOutput\n\t\t\t\t\t\t\t\t\t\t\t\t\t\toutput={output}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\terrorText={\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"errorText\" in part ? part.errorText : undefined\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t</ToolContent>\n\t\t\t\t\t\t\t\t\t\t</Tool>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t{resourceUri && output !== undefined && (\n\t\t\t\t\t\t\t\t\t\t<WidgetErrorBoundary>\n\t\t\t\t\t\t\t\t\t\t\t<McpAppFrame\n\t\t\t\t\t\t\t\t\t\t\t\tresourceUri={resourceUri}\n\t\t\t\t\t\t\t\t\t\t\t\ttoolInput={\n\t\t\t\t\t\t\t\t\t\t\t\t\t(part.input as Record<string, unknown>) ?? {}\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\ttoolResult={{\n\t\t\t\t\t\t\t\t\t\t\t\t\tcontent: (output as Record<string, unknown>)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t.content as\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t| Array<{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: string;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttext?: string;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t }>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t| undefined,\n\t\t\t\t\t\t\t\t\t\t\t\t\tstructuredContent: (output as Record<string, unknown>)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t.structuredContent as\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t| Record<string, unknown>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t| undefined,\n\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\tresourceEndpoint={resourceEndpoint}\n\t\t\t\t\t\t\t\t\t\t\t\tisDark={isDark}\n\t\t\t\t\t\t\t\t\t\t\t\tautoHeight={autoHeight}\n\t\t\t\t\t\t\t\t\t\t\t\tonFollowUp={onFollowUp}\n\t\t\t\t\t\t\t\t\t\t\t\tonDisplayModeChange={\n\t\t\t\t\t\t\t\t\t\t\t\t\tonWidgetDisplayModeChange\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? (mode) =>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonWidgetDisplayModeChange(mode, {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttoolCallId: part.toolCallId,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tresourceUri,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttoolInput:\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(part.input as Record<string, unknown>) ??\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttoolResult: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tcontent: (output as Record<string, unknown>)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t.content as\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t| Array<{\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype: string;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttext?: string;\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t }>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t| undefined,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tstructuredContent: (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\toutput as Record<string, unknown>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t).structuredContent as\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t| Record<string, unknown>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t| undefined,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tautoHeight,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t</WidgetErrorBoundary>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})}\n\t\t\t\t\t\t{/* Hide text content when fullscreen */}\n\t\t\t\t\t\t<div style={fullscreenToolCallId ? { display: \"none\" } : undefined}>\n\t\t\t\t\t\t\t<MessageContent>\n\t\t\t\t\t\t\t\t{fileParts.length > 0 && <Attachments files={fileParts} />}\n\t\t\t\t\t\t\t\t{hasTextContent\n\t\t\t\t\t\t\t\t\t? textParts.map((part, i) => (\n\t\t\t\t\t\t\t\t\t\t\t<MessageResponse key={`${message.id}-${i}`}>\n\t\t\t\t\t\t\t\t\t\t\t\t{part.type === \"text\" ? part.text : \"\"}\n\t\t\t\t\t\t\t\t\t\t\t</MessageResponse>\n\t\t\t\t\t\t\t\t\t\t))\n\t\t\t\t\t\t\t\t\t: isLastAssistant && isLoading && <Loader />}\n\t\t\t\t\t\t\t</MessageContent>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</Message>\n\t\t\t\t);\n\t\t\t})}\n\t\t\t{showLoaderBubble && !fullscreenToolCallId && (\n\t\t\t\t<Message from=\"assistant\">\n\t\t\t\t\t<MessageContent>\n\t\t\t\t\t\t<Loader />\n\t\t\t\t\t</MessageContent>\n\t\t\t\t</Message>\n\t\t\t)}\n\t\t</>\n\t);\n}\n","\"use client\";\n\nimport type { HTMLAttributes } from \"react\";\nimport { cn } from \"../lib/utils\";\n\nexport interface SuggestionsProps\n\textends Omit<HTMLAttributes<HTMLDivElement>, \"onSelect\"> {\n\tsuggestions: string[];\n\tisLoading?: boolean;\n\tonSelect: (suggestion: string) => void;\n}\n\nexport function Suggestions({\n\tsuggestions,\n\tisLoading,\n\tonSelect,\n\tclassName,\n\t...props\n}: SuggestionsProps) {\n\tif (suggestions.length === 0 && !isLoading) return null;\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\"ww:flex ww:flex-wrap ww:gap-2 ww:px-3 ww:py-2\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{isLoading\n\t\t\t\t? [0, 1, 2].map((i) => (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tkey={i}\n\t\t\t\t\t\t\tclassName=\"ww:h-7 ww:rounded-full ww:bg-accent ww:animate-pulse\"\n\t\t\t\t\t\t\tstyle={{ width: `${60 + i * 20}px` }}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))\n\t\t\t\t: suggestions.map((suggestion, index) => (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\tkey={suggestion}\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tonClick={() => onSelect(suggestion)}\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"ww:rounded-full ww:border ww:border-border ww:bg-background ww:px-3 ww:py-1 ww:text-xs\",\n\t\t\t\t\t\t\t\t\"ww:text-foreground ww:hover:bg-accent ww:hover:border-primary/30\",\n\t\t\t\t\t\t\t\t\"ww:transition-all ww:duration-200 ww:ease-out ww:cursor-pointer\",\n\t\t\t\t\t\t\t\t\"ww:animate-[ww-fade-in_0.2s_ease-out_both]\",\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\tstyle={{ animationDelay: `${index * 50}ms` }}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{suggestion}\n\t\t\t\t\t\t</button>\n\t\t\t\t\t))}\n\t\t</div>\n\t);\n}\n","\"use client\";\n\nimport { useChat } from \"@ai-sdk/react\";\nimport type { FileUIPart } from \"ai\";\nimport { DefaultChatTransport } from \"ai\";\nimport { nanoid } from \"nanoid\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { ChatBaseProps } from \"../@types\";\nimport type { PromptInputMessage } from \"../ai-elements/prompt-input\";\n\nconst SESSION_STORAGE_KEY = \"waniwani-chat-session-id\";\nconst SESSION_HEADER_NAME = \"x-session-id\";\n\nfunction normalizeSessionId(value: unknown): string | undefined {\n\tif (typeof value !== \"string\") return undefined;\n\tconst trimmed = value.trim();\n\treturn trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction readSessionIdFromStorage(): string | undefined {\n\tif (typeof window === \"undefined\") return undefined;\n\n\ttry {\n\t\treturn normalizeSessionId(\n\t\t\twindow.sessionStorage.getItem(SESSION_STORAGE_KEY),\n\t\t);\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction writeSessionIdToStorage(sessionId: string): void {\n\tif (typeof window === \"undefined\") return;\n\n\ttry {\n\t\twindow.sessionStorage.setItem(SESSION_STORAGE_KEY, sessionId);\n\t} catch {\n\t\t// Ignore storage failures (private mode, security policy, etc.)\n\t}\n}\n\nfunction removeSessionIdFromStorage(): void {\n\tif (typeof window === \"undefined\") return;\n\n\ttry {\n\t\twindow.sessionStorage.removeItem(SESSION_STORAGE_KEY);\n\t} catch {\n\t\t// Ignore storage failures (private mode, security policy, etc.)\n\t}\n}\n\nexport interface QueuedMessage {\n\tid: string;\n\ttext: string;\n\tfiles: FileUIPart[];\n}\n\nexport function useChatEngine(props: ChatBaseProps) {\n\tconst {\n\t\tapi = \"https://app.waniwani.ai/api/chat\",\n\t\theaders: userHeaders,\n\t\tbody,\n\t\tonMessageSent,\n\t\tonResponseReceived,\n\t} = props;\n\n\tconst headersRef = useRef(userHeaders);\n\tconst bodyRef = useRef(body);\n\tconst sessionIdRef = useRef<string | undefined>(undefined);\n\n\tconst getSessionId = useCallback((): string | undefined => {\n\t\tif (sessionIdRef.current) return sessionIdRef.current;\n\n\t\tconst storedSessionId = readSessionIdFromStorage();\n\t\tif (storedSessionId) sessionIdRef.current = storedSessionId;\n\t\treturn storedSessionId;\n\t}, []);\n\n\tconst setSessionId = useCallback((value: unknown) => {\n\t\tconst sessionId = normalizeSessionId(value);\n\t\tif (!sessionId) return;\n\t\tif (sessionIdRef.current === sessionId) return;\n\n\t\tsessionIdRef.current = sessionId;\n\t\twriteSessionIdToStorage(sessionId);\n\t}, []);\n\n\tconst clearSessionId = useCallback(() => {\n\t\tsessionIdRef.current = undefined;\n\t\tremoveSessionIdFromStorage();\n\t}, []);\n\n\tuseEffect(() => {\n\t\theadersRef.current = userHeaders;\n\t}, [userHeaders]);\n\n\tuseEffect(() => {\n\t\tbodyRef.current = body;\n\t}, [body]);\n\n\tconst transportRef = useRef(\n\t\tnew DefaultChatTransport({\n\t\t\tapi,\n\t\t\theaders: () => ({\n\t\t\t\t...headersRef.current,\n\t\t\t}),\n\t\t\tbody: () => {\n\t\t\t\tconst resolvedBody = {\n\t\t\t\t\t...(bodyRef.current ?? {}),\n\t\t\t\t};\n\n\t\t\t\tconst hasExplicitSessionId = Object.hasOwn(resolvedBody, \"sessionId\");\n\t\t\t\tconst bodySessionId = normalizeSessionId(resolvedBody.sessionId);\n\t\t\t\tif (bodySessionId) {\n\t\t\t\t\tsetSessionId(bodySessionId);\n\t\t\t\t\tresolvedBody.sessionId = bodySessionId;\n\t\t\t\t\treturn resolvedBody;\n\t\t\t\t}\n\t\t\t\tif (hasExplicitSessionId) {\n\t\t\t\t\tclearSessionId();\n\t\t\t\t\tdelete resolvedBody.sessionId;\n\t\t\t\t\treturn resolvedBody;\n\t\t\t\t}\n\n\t\t\t\tconst storedSessionId = getSessionId();\n\t\t\t\tif (storedSessionId) {\n\t\t\t\t\tresolvedBody.sessionId = storedSessionId;\n\t\t\t\t}\n\n\t\t\t\treturn resolvedBody;\n\t\t\t},\n\t\t\tfetch: async (input, init) => {\n\t\t\t\tconst response = await fetch(input, init);\n\t\t\t\tsetSessionId(response.headers.get(SESSION_HEADER_NAME));\n\t\t\t\treturn response;\n\t\t\t},\n\t\t}),\n\t);\n\n\tconst { messages, sendMessage, status } = useChat({\n\t\ttransport: transportRef.current,\n\t\tonFinish() {\n\t\t\tonResponseReceived?.();\n\t\t},\n\t\tonError(error) {\n\t\t\tconsole.warn(\"[WaniWani] Chat error:\", error.message);\n\t\t},\n\t});\n\n\tconst [text, setText] = useState(\"\");\n\tconst [queuedMessages, setQueuedMessages] = useState<QueuedMessage[]>([]);\n\n\tconst isLoading = status === \"submitted\" || status === \"streaming\";\n\n\tconst removeQueuedMessage = useCallback((id: string) => {\n\t\tsetQueuedMessages((prev) => prev.filter((m) => m.id !== id));\n\t}, []);\n\n\tconst queueFull = isLoading && queuedMessages.length > 0;\n\n\tconst handleSubmit = useCallback(\n\t\t(message: PromptInputMessage) => {\n\t\t\tconst hasText = Boolean(message.text?.trim());\n\t\t\tconst hasFiles = Boolean(message.files?.length);\n\t\t\tif (!(hasText || hasFiles)) return;\n\n\t\t\tif (isLoading) {\n\t\t\t\t// Only allow one queued message at a time\n\t\t\t\tif (queuedMessages.length > 0) return;\n\n\t\t\t\tsetQueuedMessages((prev) => [\n\t\t\t\t\t...prev,\n\t\t\t\t\t{\n\t\t\t\t\t\tid: nanoid(),\n\t\t\t\t\t\ttext: message.text || \"\",\n\t\t\t\t\t\tfiles: message.files ?? [],\n\t\t\t\t\t},\n\t\t\t\t]);\n\t\t\t\tsetText(\"\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsendMessage({\n\t\t\t\ttext: message.text || \"\",\n\t\t\t\tfiles: message.files,\n\t\t\t});\n\n\t\t\tonMessageSent?.(message.text || \"\");\n\t\t\tsetText(\"\");\n\t\t},\n\t\t[sendMessage, onMessageSent, isLoading, queuedMessages.length],\n\t);\n\n\t// Flush first queued message once the current response finishes\n\tuseEffect(() => {\n\t\tif (status !== \"ready\") return;\n\t\tif (queuedMessages.length === 0) return;\n\n\t\tconst [first, ...rest] = queuedMessages;\n\t\tsetQueuedMessages(rest);\n\n\t\tsendMessage({\n\t\t\ttext: first.text,\n\t\t\tfiles: first.files.length > 0 ? first.files : undefined,\n\t\t});\n\t\tonMessageSent?.(first.text);\n\t}, [status, sendMessage, onMessageSent, queuedMessages]);\n\n\tconst handleTextChange = useCallback(\n\t\t(e: React.ChangeEvent<HTMLTextAreaElement>) => {\n\t\t\tsetText(e.target.value);\n\t\t},\n\t\t[],\n\t);\n\n\tconst lastMessage = messages[messages.length - 1];\n\tconst hasMessages = messages.length > 0;\n\tconst showLoaderBubble =\n\t\tisLoading && (!hasMessages || lastMessage.role === \"user\");\n\n\treturn {\n\t\tmessages,\n\t\tstatus,\n\t\ttext,\n\t\tsetText,\n\t\thandleSubmit,\n\t\thandleTextChange,\n\t\tisLoading,\n\t\tshowLoaderBubble,\n\t\tlastMessage,\n\t\thasMessages,\n\t\tsendMessage,\n\t\tqueuedMessages,\n\t\tqueueFull,\n\t\tremoveQueuedMessage,\n\t};\n}\n","\"use client\";\n\nimport type { ChatStatus, UIMessage } from \"ai\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { SuggestionsConfig } from \"../@types\";\n\nexport interface UseSuggestionsOptions {\n\tmessages: UIMessage[];\n\tstatus: ChatStatus;\n\tconfig?: boolean | SuggestionsConfig;\n}\n\n/**\n * Extract suggestions from the last assistant message's data part.\n * The API streams a `data-suggestions` part at the end of the response:\n * `{ type: \"data-suggestions\", data: { suggestions: string[] } }`\n */\nfunction extractSuggestions(message: UIMessage): string[] | null {\n\tfor (const part of message.parts) {\n\t\tconst p = part as Record<string, unknown>;\n\t\t// Handle both \"data-suggestions\" and generic \"data\" part types\n\t\tif (p.type === \"data\" || p.type === \"data-suggestions\") {\n\t\t\tconst data = p.data as Record<string, unknown> | undefined;\n\t\t\tif (data && Array.isArray(data.suggestions)) {\n\t\t\t\treturn data.suggestions as string[];\n\t\t\t}\n\t\t}\n\t}\n\treturn null;\n}\n\nfunction isConfigObject(\n\tconfig: boolean | SuggestionsConfig | undefined,\n): config is SuggestionsConfig {\n\treturn typeof config === \"object\" && config !== null && \"initial\" in config;\n}\n\nexport function useSuggestions(options: UseSuggestionsOptions) {\n\tconst { messages, status, config } = options;\n\n\tconst [suggestions, setSuggestions] = useState<string[]>(\n\t\t(isConfigObject(config) && config.initial ? config.initial : []) ?? [],\n\t);\n\tconst prevStatusRef = useRef<ChatStatus>(status);\n\n\tconst isDynamicEnabled =\n\t\tconfig === true || (isConfigObject(config) && config.dynamic !== false);\n\n\tconst clear = useCallback(() => {\n\t\tsetSuggestions([]);\n\t}, []);\n\n\t// Clear when a new user message arrives\n\tconst lastMessage = messages[messages.length - 1];\n\tuseEffect(() => {\n\t\tif (lastMessage?.role === \"user\") {\n\t\t\tclear();\n\t\t}\n\t}, [lastMessage, clear]);\n\n\t// Extract suggestions from message parts on streaming → ready transition\n\tuseEffect(() => {\n\t\tconst prevStatus = prevStatusRef.current;\n\t\tprevStatusRef.current = status;\n\n\t\tif (prevStatus === \"streaming\" && status === \"ready\" && isDynamicEnabled) {\n\t\t\tconst lastAssistant = [...messages]\n\t\t\t\t.reverse()\n\t\t\t\t.find((m) => m.role === \"assistant\");\n\t\t\tif (!lastAssistant) return;\n\n\t\t\tconsole.log(\"[WaniWani] Assistant parts:\", lastAssistant.parts);\n\n\t\t\tconst extracted = extractSuggestions(lastAssistant);\n\t\t\tconsole.log(\"[WaniWani] Extracted suggestions:\", extracted);\n\t\t\tif (extracted) {\n\t\t\t\tsetSuggestions(extracted);\n\t\t\t}\n\t\t}\n\t}, [status, isDynamicEnabled, messages]);\n\n\treturn { suggestions, isLoading: false, clear };\n}\n","import { useEffect, useRef, useState } from \"react\";\n\nconst TYPE_SPEED_MS = 50;\nconst DELETE_SPEED_MS = 30;\nconst PAUSE_AFTER_TYPE_MS = 2000;\nconst PAUSE_AFTER_DELETE_MS = 500;\n\n/**\n * Returns a string that animates like someone typing and deleting the placeholder text.\n * Only animates when `active` is true (i.e. the input is empty).\n */\nexport function useTypingPlaceholder(text: string, active = true): string {\n\tconst [displayed, setDisplayed] = useState(\"\");\n\tconst timerRef = useRef<ReturnType<typeof setTimeout>>(undefined);\n\n\tuseEffect(() => {\n\t\tif (!active) {\n\t\t\t// Reset so the animation restarts fresh when re-activated\n\t\t\tsetDisplayed(\"\");\n\t\t\treturn;\n\t\t}\n\n\t\tlet i = 0;\n\t\tlet deleting = false;\n\t\tlet disposed = false;\n\n\t\tconst tick = () => {\n\t\t\tif (disposed) return;\n\n\t\t\tif (!deleting) {\n\t\t\t\t// Typing forward\n\t\t\t\ti++;\n\t\t\t\tsetDisplayed(text.slice(0, i));\n\n\t\t\t\tif (i >= text.length) {\n\t\t\t\t\t// Finished typing — pause then start deleting\n\t\t\t\t\tdeleting = true;\n\t\t\t\t\ttimerRef.current = setTimeout(tick, PAUSE_AFTER_TYPE_MS);\n\t\t\t\t} else {\n\t\t\t\t\ttimerRef.current = setTimeout(tick, TYPE_SPEED_MS);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Deleting\n\t\t\t\ti--;\n\t\t\t\tsetDisplayed(text.slice(0, i));\n\n\t\t\t\tif (i <= 0) {\n\t\t\t\t\t// Finished deleting — pause then start typing again\n\t\t\t\t\tdeleting = false;\n\t\t\t\t\ttimerRef.current = setTimeout(tick, PAUSE_AFTER_DELETE_MS);\n\t\t\t\t} else {\n\t\t\t\t\ttimerRef.current = setTimeout(tick, DELETE_SPEED_MS);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\t// Start after a small delay\n\t\ttimerRef.current = setTimeout(tick, PAUSE_AFTER_DELETE_MS);\n\n\t\treturn () => {\n\t\t\tdisposed = true;\n\t\t\tclearTimeout(timerRef.current);\n\t\t};\n\t}, [text, active]);\n\n\treturn displayed;\n}\n","import type { ChatTheme } from \"./@types\";\n\nexport const DEFAULT_THEME: Required<ChatTheme> = {\n\tprimaryColor: \"#6366f1\",\n\tprimaryForeground: \"#1f2937\",\n\tbackgroundColor: \"#ffffff\",\n\ttextColor: \"#1f2937\",\n\tmutedColor: \"#6b7280\",\n\tborderColor: \"#e5e7eb\",\n\tassistantBubbleColor: \"#f3f4f6\",\n\tuserBubbleColor: \"#f4f4f4\",\n\tinputBackgroundColor: \"#f9fafb\",\n\tborderRadius: 16,\n\tmessageBorderRadius: 12,\n\tfontFamily: \"system-ui, -apple-system, 'Segoe UI', sans-serif\",\n\theaderBackgroundColor: \"#ffffff\",\n\theaderTextColor: \"#1f2937\",\n\tstatusColor: \"#22c55e\",\n\ttoolCardColor: \"#f4f4f5\",\n};\n\nexport const DARK_THEME: ChatTheme = {\n\tbackgroundColor: \"#212121\",\n\theaderBackgroundColor: \"#1e1e1e\",\n\theaderTextColor: \"#ececec\",\n\ttextColor: \"#ececec\",\n\tprimaryForeground: \"#ffffff\",\n\tmutedColor: \"#8e8ea0\",\n\tborderColor: \"#444444\",\n\tassistantBubbleColor: \"#2f2f2f\",\n\tuserBubbleColor: \"#303030\",\n\tinputBackgroundColor: \"#2f2f2f\",\n\tprimaryColor: \"#6366f1\",\n\tstatusColor: \"#22c55e\",\n\ttoolCardColor: \"#262626\",\n};\n\nconst CSS_VAR_MAP: Record<keyof ChatTheme, string[]> = {\n\tprimaryColor: [\"--ww-primary\", \"--ww-color-primary\"],\n\tprimaryForeground: [\"--ww-primary-fg\", \"--ww-color-primary-foreground\"],\n\tbackgroundColor: [\"--ww-bg\", \"--ww-color-background\"],\n\ttextColor: [\n\t\t\"--ww-text\",\n\t\t\"--ww-color-foreground\",\n\t\t\"--ww-color-accent-foreground\",\n\t],\n\tmutedColor: [\"--ww-muted\", \"--ww-color-muted-foreground\"],\n\tborderColor: [\"--ww-border\", \"--ww-color-border\"],\n\tassistantBubbleColor: [\"--ww-assistant-bubble\", \"--ww-color-accent\"],\n\tuserBubbleColor: [\"--ww-user-bubble\"],\n\tinputBackgroundColor: [\"--ww-input-bg\", \"--ww-color-input\"],\n\tborderRadius: [\"--ww-radius\"],\n\tmessageBorderRadius: [\"--ww-msg-radius\"],\n\tfontFamily: [\"--ww-font\"],\n\theaderBackgroundColor: [\"--ww-header-bg\"],\n\theaderTextColor: [\"--ww-header-text\"],\n\tstatusColor: [\"--ww-status\"],\n\ttoolCardColor: [\"--ww-tool-card\", \"--ww-color-tool-card\"],\n};\n\nexport function mergeTheme(userTheme?: ChatTheme): Required<ChatTheme> {\n\treturn { ...DEFAULT_THEME, ...userTheme };\n}\n\nexport function isDarkTheme(theme: Required<ChatTheme>): boolean {\n\tconst hex = theme.backgroundColor.replace(\"#\", \"\");\n\tconst r = parseInt(hex.substring(0, 2), 16);\n\tconst g = parseInt(hex.substring(2, 4), 16);\n\tconst b = parseInt(hex.substring(4, 6), 16);\n\treturn (r * 299 + g * 587 + b * 114) / 1000 < 128;\n}\n\nexport function themeToCSSProperties(\n\ttheme: Required<ChatTheme>,\n): Record<string, string> {\n\tconst vars: Record<string, string> = {};\n\tfor (const [key, cssVars] of Object.entries(CSS_VAR_MAP)) {\n\t\tconst value = theme[key as keyof ChatTheme];\n\t\tconst resolved = typeof value === \"number\" ? `${value}px` : String(value);\n\t\tfor (const cssVar of cssVars) {\n\t\t\tvars[cssVar] = resolved;\n\t\t}\n\t}\n\treturn vars;\n}\n","\"use client\";\n\nimport {\n\tforwardRef,\n\tuseCallback,\n\tuseEffect,\n\tuseImperativeHandle,\n\tuseRef,\n\tuseState,\n} from \"react\";\nimport type { ChatCardProps, ChatHandle } from \"../@types\";\nimport {\n\tConversation,\n\tConversationContent,\n\tConversationScrollButton,\n} from \"../ai-elements/conversation\";\nimport {\n\tPromptInput,\n\tPromptInputAddAttachments,\n\tPromptInputSubmit,\n\tPromptInputTextarea,\n} from \"../ai-elements/prompt-input\";\nimport { ChatQueue } from \"../components/chat-queue\";\nimport { MessageList } from \"../components/message-list\";\nimport { Suggestions } from \"../components/suggestions\";\nimport { useChatEngine } from \"../hooks/use-chat-engine\";\nimport { useSuggestions } from \"../hooks/use-suggestions\";\nimport { useTypingPlaceholder } from \"../hooks/use-typing-placeholder\";\nimport { cn } from \"../lib/utils\";\nimport { isDarkTheme, mergeTheme, themeToCSSProperties } from \"../theme\";\n\nexport const ChatCard = forwardRef<ChatHandle, ChatCardProps>(\n\tfunction ChatCard(props, ref) {\n\t\tconst {\n\t\t\ttheme: userTheme,\n\t\t\ttitle = \"Assistant\",\n\t\t\tsubtitle,\n\t\t\tshowStatus = true,\n\t\t\twidth = 500,\n\t\t\theight = 600,\n\t\t\tallowAttachments = false,\n\t\t\twelcomeMessage,\n\t\t\tplaceholder = \"Ask me anything...\",\n\t\t\ttriggerEvent = \"triggerDemoRequest\",\n\t\t\tresourceEndpoint,\n\t\t\tapi,\n\t\t} = props;\n\n\t\tconst effectiveResourceEndpoint =\n\t\t\tresourceEndpoint ?? (api ? `${api}/resource` : undefined);\n\n\t\tconst resolvedTheme = mergeTheme(userTheme);\n\t\tconst cssVars = themeToCSSProperties(resolvedTheme);\n\t\tconst isDark = isDarkTheme(resolvedTheme);\n\n\t\tconst engine = useChatEngine(props);\n\n\t\tconst animatedPlaceholder = useTypingPlaceholder(placeholder, !engine.text);\n\n\t\tconst [isHighlighted, setIsHighlighted] = useState(false);\n\t\tconst [fullscreenToolCallId, setFullscreenToolCallId] = useState<\n\t\t\tstring | null\n\t\t>(null);\n\t\tconst cardRef = useRef<HTMLDivElement>(null);\n\t\tconst highlightTimerRef = useRef<ReturnType<typeof setTimeout>>(undefined);\n\n\t\tconst focusInput = useCallback(() => {\n\t\t\tconst container = cardRef.current;\n\t\t\tif (!container) return;\n\t\t\tcontainer.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n\t\t\tconst textarea = container.querySelector(\"textarea\");\n\t\t\tif (textarea) {\n\t\t\t\tsetTimeout(() => textarea.focus(), 300);\n\t\t\t}\n\t\t\tsetIsHighlighted(true);\n\t\t\tclearTimeout(highlightTimerRef.current);\n\t\t\thighlightTimerRef.current = setTimeout(\n\t\t\t\t() => setIsHighlighted(false),\n\t\t\t\t2000,\n\t\t\t);\n\t\t}, []);\n\n\t\tconst suggestionsState = useSuggestions({\n\t\t\tmessages: engine.messages,\n\t\t\tstatus: engine.status,\n\t\t\tconfig: props.suggestions,\n\t\t});\n\n\t\tconst handleWidgetMessage = useCallback(\n\t\t\t(message: {\n\t\t\t\trole: string;\n\t\t\t\tcontent: Array<{ type: string; text?: string }>;\n\t\t\t}) => {\n\t\t\t\tconst text = message.content\n\t\t\t\t\t.map((c) => c.text ?? \"\")\n\t\t\t\t\t.join(\"\")\n\t\t\t\t\t.trim();\n\t\t\t\tif (text) engine.handleSubmit({ text, files: [] });\n\t\t\t},\n\t\t\t[engine.handleSubmit],\n\t\t);\n\n\t\tconst handleSuggestionSelect = useCallback(\n\t\t\t(suggestion: string) => {\n\t\t\t\tsuggestionsState.clear();\n\t\t\t\tengine.handleSubmit({ text: suggestion, files: [] });\n\t\t\t},\n\t\t\t[suggestionsState.clear, engine.handleSubmit],\n\t\t);\n\n\t\tuseImperativeHandle(\n\t\t\tref,\n\t\t\t() => ({\n\t\t\t\tsendMessage: (text: string) => {\n\t\t\t\t\tengine.handleSubmit({ text, files: [] });\n\t\t\t\t\tfocusInput();\n\t\t\t\t},\n\t\t\t\tfocus: focusInput,\n\t\t\t}),\n\t\t\t[engine.handleSubmit, focusInput],\n\t\t);\n\n\t\t// Listen for custom trigger event (e.g. \"triggerDemoRequest\")\n\t\tuseEffect(() => {\n\t\t\tif (!triggerEvent) return;\n\t\t\tconst handler = (e: Event) => {\n\t\t\t\tconst detail = (e as CustomEvent).detail;\n\t\t\t\tconst message =\n\t\t\t\t\ttypeof detail?.message === \"string\" ? detail.message : undefined;\n\t\t\t\tif (message) {\n\t\t\t\t\tengine.handleSubmit({ text: message, files: [] });\n\t\t\t\t}\n\t\t\t\tfocusInput();\n\t\t\t};\n\t\t\twindow.addEventListener(triggerEvent, handler);\n\t\t\treturn () => window.removeEventListener(triggerEvent, handler);\n\t\t}, [triggerEvent, engine.handleSubmit, focusInput]);\n\n\t\treturn (\n\t\t\t<div\n\t\t\t\tref={cardRef}\n\t\t\t\tstyle={{ ...cssVars, width, height }}\n\t\t\t\tdata-waniwani-chat=\"\"\n\t\t\t\tdata-waniwani-layout=\"card\"\n\t\t\t\t{...(isDark ? { \"data-waniwani-dark\": \"\" } : {})}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"ww:flex ww:flex-col ww:font-[family-name:var(--ww-font)] ww:text-foreground ww:bg-background ww:rounded-[var(--ww-radius)] ww:border ww:border-border ww:shadow-md ww:overflow-hidden ww:transition-shadow ww:duration-300\",\n\t\t\t\t\tisHighlighted &&\n\t\t\t\t\t\t\"ww:ring-2 ww:ring-blue-400/70 ww:ring-offset-2 ww:ring-offset-background\",\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{/* Header */}\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"ww:shrink-0 ww:flex ww:items-center ww:gap-3 ww:px-4 ww:py-2 ww:border-b ww:border-border\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tbackgroundColor: resolvedTheme.headerBackgroundColor,\n\t\t\t\t\t\tcolor: resolvedTheme.headerTextColor,\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t{showStatus && (\n\t\t\t\t\t\t<span className=\"ww:size-2.5 ww:rounded-full ww:bg-status\" />\n\t\t\t\t\t)}\n\t\t\t\t\t<div className=\"ww:flex-1 ww:min-w-0\">\n\t\t\t\t\t\t<div className=\"ww:text-xs ww:font-semibold ww:truncate\">\n\t\t\t\t\t\t\t{title}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{subtitle && (\n\t\t\t\t\t\t\t<div className=\"ww:text-[11px] ww:text-muted-foreground ww:truncate\">\n\t\t\t\t\t\t\t\t{subtitle}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Messages */}\n\t\t\t\t<Conversation className=\"ww:flex-1 ww:min-h-0 ww:bg-background\">\n\t\t\t\t\t<ConversationContent>\n\t\t\t\t\t\t<MessageList\n\t\t\t\t\t\t\tmessages={engine.messages}\n\t\t\t\t\t\t\tstatus={engine.status}\n\t\t\t\t\t\t\twelcomeMessage={welcomeMessage}\n\t\t\t\t\t\t\tresourceEndpoint={effectiveResourceEndpoint}\n\t\t\t\t\t\t\tisDark={isDark}\n\t\t\t\t\t\t\tonFollowUp={handleWidgetMessage}\n\t\t\t\t\t\t\tfullscreenToolCallId={fullscreenToolCallId}\n\t\t\t\t\t\t\tonWidgetDisplayModeChange={(mode, widget) => {\n\t\t\t\t\t\t\t\tsetFullscreenToolCallId(\n\t\t\t\t\t\t\t\t\tmode === \"fullscreen\" ? widget.toolCallId : null,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</ConversationContent>\n\t\t\t\t\t{!fullscreenToolCallId && <ConversationScrollButton />}\n\t\t\t\t</Conversation>\n\n\t\t\t\t{/* Suggestions — hide when fullscreen */}\n\t\t\t\t{!fullscreenToolCallId && (\n\t\t\t\t\t<Suggestions\n\t\t\t\t\t\tsuggestions={suggestionsState.suggestions}\n\t\t\t\t\t\tisLoading={suggestionsState.isLoading}\n\t\t\t\t\t\tonSelect={handleSuggestionSelect}\n\t\t\t\t\t\tclassName=\"ww:border-t ww:border-border\"\n\t\t\t\t\t/>\n\t\t\t\t)}\n\n\t\t\t\t{/* Queue — hide when fullscreen */}\n\t\t\t\t{!fullscreenToolCallId && (\n\t\t\t\t\t<ChatQueue\n\t\t\t\t\t\tqueuedMessages={engine.queuedMessages}\n\t\t\t\t\t\tonRemove={engine.removeQueuedMessage}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\n\t\t\t\t{/* Input — hide when fullscreen */}\n\t\t\t\t{!fullscreenToolCallId && (\n\t\t\t\t\t<div className=\"ww:shrink-0 ww:border-t ww:border-border ww:bg-background\">\n\t\t\t\t\t\t<PromptInput\n\t\t\t\t\t\t\tonSubmit={engine.handleSubmit}\n\t\t\t\t\t\t\tglobalDrop={allowAttachments}\n\t\t\t\t\t\t\tmultiple={allowAttachments}\n\t\t\t\t\t\t\tclassName={cn(\"ww:rounded-none ww:border-0\")}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<div className=\"ww:flex ww:items-center ww:gap-1 ww:px-3 ww:py-2\">\n\t\t\t\t\t\t\t\t{allowAttachments && <PromptInputAddAttachments />}\n\t\t\t\t\t\t\t\t<PromptInputTextarea\n\t\t\t\t\t\t\t\t\tonChange={engine.handleTextChange}\n\t\t\t\t\t\t\t\t\tvalue={engine.text}\n\t\t\t\t\t\t\t\t\tplaceholder={animatedPlaceholder}\n\t\t\t\t\t\t\t\t\tclassName=\"ww:min-h-0 ww:py-1.5 ww:px-2\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<PromptInputSubmit\n\t\t\t\t\t\t\t\t\tstatus={engine.status}\n\t\t\t\t\t\t\t\t\tdisabled={engine.queueFull}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</PromptInput>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t);\n\t},\n);\n"],"mappings":"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","names":["forwardRef","useCallback","useEffect","useImperativeHandle","useRef","useState","ArrowDownIcon","useCallback","StickToBottom","useStickToBottomContext","clsx","extendTailwindMerge","twMerge","cn","inputs","jsx","Button","className","variant","size","type","props","cn","jsx","Conversation","className","props","StickToBottom","cn","ConversationContent","ConversationScrollButton","isAtBottom","scrollToBottom","useStickToBottomContext","handleScrollToBottom","useCallback","Button","ArrowDownIcon","ArrowUpIcon","LoaderIcon","PaperclipIcon","SquareIcon","XIcon","nanoid","createContext","useCallback","useContext","useEffect","useMemo","useRef","useState","jsx","jsxs","convertBlobUrlToDataUrl","url","blob","resolve","reader","LocalAttachmentsContext","createContext","usePromptInputAttachments","context","useContext","PromptInput","className","accept","multiple","globalDrop","maxFiles","maxFileSize","onSubmit","children","props","inputRef","useRef","formRef","items","setItems","useState","filesRef","useEffect","openFileDialog","useCallback","add","fileList","incoming","withinSize","f","valid","prev","capacity","capped","file","nanoid","remove","id","found","clear","handleChange","event","onDragOver","e","onDrop","handleSubmit","form","text","convertedFiles","_id","item","dataUrl","result","attachmentsCtx","useMemo","cn","PromptInputTextarea","onChange","onKeyDown","className","placeholder","props","attachments","usePromptInputAttachments","isComposing","setIsComposing","useState","handleKeyDown","useCallback","e","form","lastAttachment","handlePaste","event","items","files","item","file","jsx","cn","PromptInputSubmit","status","onStop","onClick","children","isGenerating","Icon","ArrowUpIcon","LoaderIcon","SquareIcon","handleClick","Button","PromptInputAddAttachments","jsxs","XIcon","PaperclipIcon","XIcon","memo","useCallback","jsx","Queue","className","props","cn","QueueItem","QueueItemIndicator","QueueItemContent","QueueItemActions","QueueItemAction","Button","jsx","jsxs","ChatQueueItem","memo","message","onRemove","handleRemove","useCallback","QueueItem","QueueItemIndicator","QueueItemContent","QueueItemActions","QueueItemAction","XIcon","ChatQueue","queuedMessages","className","Queue","cn","msg","FileIcon","jsx","jsxs","Attachments","files","className","props","cn","file","i","AttachmentItem","FileIcon","jsx","Loader","className","size","props","cn","i","cjk","code","memo","Streamdown","jsx","Message","className","from","props","cn","MessageContent","children","streamdownPlugins","cjk","code","MessageResponse","memo","Streamdown","prevProps","nextProps","jsx","Reasoning","className","text","props","cn","BracesIcon","CheckIcon","ChevronDownIcon","ChevronRightIcon","ClipboardCopyIcon","ServerIcon","createContext","useCallback","useContext","useEffect","useMemo","useRef","useState","Fragment","jsx","jsxs","truncateJSON","data","maxLength","stringified","entries","parts","remaining","key","value","keyAbbrev","valStr","part","CopyButton","text","className","copied","setCopied","useState","timeoutRef","useRef","useEffect","handleCopy","useCallback","e","Button","cn","CheckIcon","ClipboardCopyIcon","CollapsibleJSON","label","props","expanded","setExpanded","fullJSON","useMemo","preview","v","ChevronRightIcon","ToolOpenContext","createContext","Tool","defaultOpen","children","open","setOpen","o","ToolHeader","title","state","toggle","useContext","isRunning","BracesIcon","ChevronDownIcon","ToolContent","className","children","props","open","useContext","ToolOpenContext","jsx","cn","ToolInput","input","CollapsibleJSON","getUiMeta","output","meta","ui","getResourceUri","uri","getAutoHeight","ToolOutput","errorText","jsxs","useCallback","useEffect","useMemo","useRef","useState","jsx","DEFAULT_RESOURCE_ENDPOINT","MAX_HEIGHT","DEFAULT_HEIGHT","PROTOCOL_VERSION","RESIZE_ANIMATION_MS","HANDSHAKE_TIMEOUT_MS","MAX_RETRIES","McpAppFrame","resourceUri","toolInput","toolResult","resourceEndpoint","isDark","className","autoHeight","onOpenLink","onFollowUp","onDisplayModeChange","iframeRef","useRef","toolInputRef","toolResultRef","lastSizeRef","animationRef","initializedRef","retryCountRef","displayModeRef","height","setHeight","useState","width","setWidth","onOpenLinkRef","onFollowUpRef","onDisplayModeChangeRef","clampHeight","useCallback","h","iframeSrc","useMemo","isDarkRef","useEffect","iframe","disposed","handshakeReceived","debug","args","handshakeTimer","url","postToIframe","msg","handleMessage","event","data","method","id","input","result","content","params","newHeight","newWidth","last","heightChanged","widthChanged","clamped","from","anim","requested","granted","cn","Component","jsx","jsxs","WidgetErrorBoundary","error","Fragment","jsx","jsxs","formatToolName","name","c","MessageList","messages","status","welcomeMessage","resourceEndpoint","isDark","onFollowUp","onWidgetDisplayModeChange","fullscreenToolCallId","isLoading","lastMessage","hasMessages","showLoaderBubble","Message","MessageContent","MessageResponse","message","textParts","p","reasoningParts","fileParts","toolParts","isLastAssistant","hasTextContent","hasFullscreenWidget","part","i","Reasoning","output","resourceUri","getResourceUri","autoHeight","getAutoHeight","isFullscreen","isHidden","Tool","ToolHeader","ToolContent","ToolInput","ToolOutput","WidgetErrorBoundary","McpAppFrame","mode","Attachments","Loader","jsx","Suggestions","suggestions","isLoading","onSelect","className","props","cn","suggestion","index","useChat","DefaultChatTransport","nanoid","useCallback","useEffect","useRef","useState","SESSION_STORAGE_KEY","SESSION_HEADER_NAME","normalizeSessionId","value","trimmed","readSessionIdFromStorage","writeSessionIdToStorage","sessionId","removeSessionIdFromStorage","useChatEngine","props","api","userHeaders","body","onMessageSent","onResponseReceived","headersRef","bodyRef","sessionIdRef","getSessionId","storedSessionId","setSessionId","clearSessionId","transportRef","resolvedBody","hasExplicitSessionId","bodySessionId","input","init","response","messages","sendMessage","status","error","text","setText","queuedMessages","setQueuedMessages","isLoading","removeQueuedMessage","id","prev","m","queueFull","handleSubmit","message","hasText","hasFiles","first","rest","handleTextChange","e","lastMessage","hasMessages","showLoaderBubble","useCallback","useEffect","useRef","useState","extractSuggestions","message","part","p","data","isConfigObject","config","useSuggestions","options","messages","status","suggestions","setSuggestions","prevStatusRef","isDynamicEnabled","clear","lastMessage","prevStatus","lastAssistant","m","extracted","useEffect","useRef","useState","TYPE_SPEED_MS","DELETE_SPEED_MS","PAUSE_AFTER_TYPE_MS","PAUSE_AFTER_DELETE_MS","useTypingPlaceholder","text","active","displayed","setDisplayed","timerRef","deleting","disposed","tick","DEFAULT_THEME","DARK_THEME","CSS_VAR_MAP","mergeTheme","userTheme","isDarkTheme","theme","hex","r","g","b","themeToCSSProperties","vars","key","cssVars","value","resolved","cssVar","jsx","jsxs","ChatBar","forwardRef","props","ref","userTheme","width","expandedHeight","allowAttachments","welcomeMessage","placeholder","triggerEvent","resourceEndpoint","api","effectiveResourceEndpoint","resolvedTheme","mergeTheme","cssVars","themeToCSSProperties","isDark","isDarkTheme","engine","useChatEngine","suggestionsState","useSuggestions","handleWidgetMessage","useCallback","message","text","c","handleSuggestionSelect","suggestion","animatedPlaceholder","useTypingPlaceholder","isFocused","setIsFocused","useState","isHighlighted","setIsHighlighted","fullscreenToolCallId","setFullscreenToolCallId","containerRef","useRef","highlightTimerRef","focusInput","container","textarea","useImperativeHandle","useEffect","handler","e","detail","isExpanded","handleClickOutside","handleFocus","cn","Conversation","ConversationContent","MessageList","mode","widget","ConversationScrollButton","Suggestions","ChatQueue","PromptInput","PromptInputAddAttachments","PromptInputTextarea","PromptInputSubmit","forwardRef","useCallback","useEffect","useImperativeHandle","useRef","useState","jsx","jsxs","ChatCard","forwardRef","props","ref","userTheme","title","subtitle","showStatus","width","height","allowAttachments","welcomeMessage","placeholder","triggerEvent","resourceEndpoint","api","effectiveResourceEndpoint","resolvedTheme","mergeTheme","cssVars","themeToCSSProperties","isDark","isDarkTheme","engine","useChatEngine","animatedPlaceholder","useTypingPlaceholder","isHighlighted","setIsHighlighted","useState","fullscreenToolCallId","setFullscreenToolCallId","cardRef","useRef","highlightTimerRef","focusInput","useCallback","container","textarea","suggestionsState","useSuggestions","handleWidgetMessage","message","text","c","handleSuggestionSelect","suggestion","useImperativeHandle","useEffect","handler","e","detail","cn","Conversation","ConversationContent","MessageList","mode","widget","ConversationScrollButton","Suggestions","ChatQueue","PromptInput","PromptInputAddAttachments","PromptInputTextarea","PromptInputSubmit"]}