@surf-kit/agent 0.3.0 → 0.4.1

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.
Files changed (43) hide show
  1. package/dist/chat/index.cjs +131 -41
  2. package/dist/chat/index.cjs.map +1 -1
  3. package/dist/chat/index.d.cts +3 -2
  4. package/dist/chat/index.d.ts +3 -2
  5. package/dist/chat/index.js +132 -42
  6. package/dist/chat/index.js.map +1 -1
  7. package/dist/{chat-CGamM7Mz.d.cts → chat-BRY3xGg_.d.cts} +1 -1
  8. package/dist/{chat-BIIDOGrD.d.ts → chat-CcKc6OAR.d.ts} +1 -1
  9. package/dist/{hooks-CTeEqnBQ.d.ts → hooks-BLeiVk-x.d.ts} +3 -2
  10. package/dist/{hooks-B1NYoLLs.d.cts → hooks-CSGGLd7j.d.cts} +3 -2
  11. package/dist/hooks.cjs +41 -11
  12. package/dist/hooks.cjs.map +1 -1
  13. package/dist/hooks.d.cts +3 -3
  14. package/dist/hooks.d.ts +3 -3
  15. package/dist/hooks.js +41 -11
  16. package/dist/hooks.js.map +1 -1
  17. package/dist/index.cjs +131 -41
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.cts +3 -3
  20. package/dist/index.d.ts +3 -3
  21. package/dist/index.js +136 -46
  22. package/dist/index.js.map +1 -1
  23. package/dist/layouts/index.cjs +131 -41
  24. package/dist/layouts/index.cjs.map +1 -1
  25. package/dist/layouts/index.d.cts +1 -1
  26. package/dist/layouts/index.d.ts +1 -1
  27. package/dist/layouts/index.js +134 -44
  28. package/dist/layouts/index.js.map +1 -1
  29. package/dist/response/index.cjs +34 -3
  30. package/dist/response/index.cjs.map +1 -1
  31. package/dist/response/index.d.cts +1 -1
  32. package/dist/response/index.d.ts +1 -1
  33. package/dist/response/index.js +35 -4
  34. package/dist/response/index.js.map +1 -1
  35. package/dist/streaming/index.cjs +14 -3
  36. package/dist/streaming/index.cjs.map +1 -1
  37. package/dist/streaming/index.d.cts +2 -2
  38. package/dist/streaming/index.d.ts +2 -2
  39. package/dist/streaming/index.js +14 -3
  40. package/dist/streaming/index.js.map +1 -1
  41. package/dist/{streaming-x7umFHoP.d.cts → streaming-BHPXnwwo.d.cts} +3 -1
  42. package/dist/{streaming-Bx-ff2tt.d.ts → streaming-C6mbU7My.d.ts} +3 -1
  43. package/package.json +5 -4
@@ -118,6 +118,7 @@ function useCharacterDrain(target, msPerChar = 15) {
118
118
  var import_react2 = __toESM(require("react"), 1);
119
119
  var import_react_markdown = __toESM(require("react-markdown"), 1);
120
120
  var import_rehype_sanitize = __toESM(require("rehype-sanitize"), 1);
121
+ var import_remark_gfm = __toESM(require("remark-gfm"), 1);
121
122
  var import_tailwind_merge = require("tailwind-merge");
122
123
  var import_jsx_runtime = require("react/jsx-runtime");
123
124
  function normalizeMarkdownLists(content) {
@@ -142,6 +143,10 @@ function ResponseMessage({ content, className }) {
142
143
  "[&_pre]:bg-surface-raised [&_pre]:border [&_pre]:border-border [&_pre]:rounded-xl [&_pre]:p-4 [&_pre]:overflow-x-auto",
143
144
  "[&_hr]:my-3 [&_hr]:border-border",
144
145
  "[&_blockquote]:border-l-2 [&_blockquote]:border-border-strong [&_blockquote]:pl-4 [&_blockquote]:text-text-secondary",
146
+ "[&_table]:w-full [&_table]:text-sm [&_table]:border-collapse [&_table]:my-2",
147
+ "[&_thead]:border-b [&_thead]:border-border",
148
+ "[&_th]:text-left [&_th]:px-2 [&_th]:py-1.5 [&_th]:font-semibold",
149
+ "[&_td]:px-2 [&_td]:py-1.5 [&_td]:border-t [&_td]:border-border/50",
145
150
  "[&_a]:text-accent [&_a]:underline-offset-2 [&_a]:hover:text-accent/80",
146
151
  className
147
152
  ),
@@ -149,6 +154,7 @@ function ResponseMessage({ content, className }) {
149
154
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
150
155
  import_react_markdown.default,
151
156
  {
157
+ remarkPlugins: [import_remark_gfm.default],
152
158
  rehypePlugins: [import_rehype_sanitize.default],
153
159
  components: {
154
160
  script: () => null,
@@ -174,7 +180,11 @@ function ResponseMessage({ content, className }) {
174
180
  h2: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: "text-sm font-bold mt-3 mb-1", children }),
175
181
  h3: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "text-sm font-semibold mt-2 mb-1", children }),
176
182
  hr: () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("hr", { className: "my-3 border-border" }),
177
- code: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: "bg-surface-sunken rounded px-1 py-0.5 text-xs font-mono", children })
183
+ code: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: "bg-surface-sunken rounded px-1 py-0.5 text-xs font-mono", children }),
184
+ table: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "overflow-x-auto my-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("table", { className: "w-full text-sm border-collapse", children }) }),
185
+ thead: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { className: "border-b border-border", children }),
186
+ th: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { className: "text-left px-2 py-1.5 font-semibold", children }),
187
+ td: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "px-2 py-1.5 border-t border-border/50", children })
178
188
  },
179
189
  children: normalizeMarkdownLists(content)
180
190
  }
@@ -194,9 +204,10 @@ var phaseLabels = {
194
204
  verifying: "Verifying..."
195
205
  };
196
206
  var CURSOR_STYLES = `
197
- .sk-streaming-cursor > :not(ul,ol,blockquote):last-child::after,
207
+ .sk-streaming-cursor > :not(ul,ol,blockquote,div:has(table)):last-child::after,
198
208
  .sk-streaming-cursor > :is(ul,ol):last-child > li:last-child::after,
199
- .sk-streaming-cursor > blockquote:last-child > p:last-child::after {
209
+ .sk-streaming-cursor > blockquote:last-child > p:last-child::after,
210
+ .sk-streaming-cursor > div:has(table):last-child table tbody tr:last-child td:last-child::after {
200
211
  content: "";
201
212
  display: inline-block;
202
213
  width: 2px;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/streaming/index.ts","../../src/streaming/StreamingMessage/StreamingMessage.tsx","../../src/hooks/useCharacterDrain.ts","../../src/response/ResponseMessage/ResponseMessage.tsx","../../src/streaming/ThinkingIndicator/ThinkingIndicator.tsx","../../src/streaming/ToolExecution/ToolExecution.tsx","../../src/streaming/RetrievalProgress/RetrievalProgress.tsx","../../src/streaming/VerificationProgress/VerificationProgress.tsx","../../src/streaming/TypewriterText/TypewriterText.tsx","../../src/streaming/TypingIndicator/TypingIndicator.tsx","../../src/streaming/TextGlimmer/TextGlimmer.tsx","../../src/streaming/StreamingList/StreamingList.tsx","../../src/streaming/StreamingStructure/StreamingStructure.tsx"],"sourcesContent":["export { StreamingMessage } from './StreamingMessage'\nexport type { StreamingMessageProps } from './StreamingMessage'\n\nexport { ThinkingIndicator } from './ThinkingIndicator'\nexport type { ThinkingIndicatorProps } from './ThinkingIndicator'\n\nexport { ToolExecution } from './ToolExecution'\nexport type { ToolExecutionProps } from './ToolExecution'\n\nexport { RetrievalProgress } from './RetrievalProgress'\nexport type { RetrievalProgressProps } from './RetrievalProgress'\n\nexport { VerificationProgress } from './VerificationProgress'\nexport type { VerificationProgressProps } from './VerificationProgress'\n\nexport { TypewriterText } from './TypewriterText'\nexport type { TypewriterTextProps } from './TypewriterText'\n\nexport { TypingIndicator } from './TypingIndicator'\nexport type { TypingIndicatorProps } from './TypingIndicator'\n\nexport { TextGlimmer } from './TextGlimmer'\nexport type { TextGlimmerProps } from './TextGlimmer'\n\nexport { StreamingList } from './StreamingList'\nexport type { StreamingListProps } from './StreamingList'\n\nexport { StreamingStructure } from './StreamingStructure'\nexport type { StreamingStructureProps } from './StreamingStructure'\n","'use client'\n\nimport React, { useEffect, useRef } from 'react'\nimport { twMerge } from 'tailwind-merge'\nimport { WaveLoader } from '@surf-kit/core'\nimport type { StreamState } from '../../types/streaming'\nimport { useCharacterDrain } from '../../hooks/useCharacterDrain'\nimport { ResponseMessage } from '../../response/ResponseMessage'\n\ntype StreamingMessageProps = {\n stream: StreamState\n onComplete?: () => void\n onDraining?: (isDraining: boolean) => void\n showPhases?: boolean\n className?: string\n}\n\nconst phaseLabels: Record<StreamState['phase'], string> = {\n idle: '',\n waiting: 'Waiting...',\n thinking: 'Thinking...',\n retrieving: 'Searching...',\n generating: 'Writing...',\n verifying: 'Verifying...',\n}\n\n// Cursor styles: a catch-all targets any last-child element, with overrides\n// for ul/ol (target last li) and blockquote (target last p) so the cursor\n// appears inline with the final text rather than on a new line.\n// Uses steps(1) for a crisp blink that won't smooth-fade to invisible.\nconst CURSOR_STYLES = `\n.sk-streaming-cursor > :not(ul,ol,blockquote):last-child::after,\n.sk-streaming-cursor > :is(ul,ol):last-child > li:last-child::after,\n.sk-streaming-cursor > blockquote:last-child > p:last-child::after {\n content: \"\";\n display: inline-block;\n width: 2px;\n height: 1em;\n background: var(--color-accent, #38bdf8);\n animation: sk-cursor-blink 0.8s steps(1) infinite;\n margin-left: 2px;\n vertical-align: text-bottom;\n}\n@keyframes sk-cursor-blink {\n 0%, 60% { opacity: 1; }\n 61%, 100% { opacity: 0; }\n}\n`\n\nfunction StreamingMessage({\n stream,\n onComplete,\n onDraining,\n showPhases = true,\n className,\n}: StreamingMessageProps) {\n const onCompleteRef = useRef(onComplete)\n onCompleteRef.current = onComplete\n const onDrainingRef = useRef(onDraining)\n onDrainingRef.current = onDraining\n const wasActiveRef = useRef(stream.active)\n\n useEffect(() => {\n if (wasActiveRef.current && !stream.active) {\n onCompleteRef.current?.()\n }\n wasActiveRef.current = stream.active\n }, [stream.active])\n\n const phaseLabel = phaseLabels[stream.phase]\n const { displayed: rawDisplayed, isDraining } = useCharacterDrain(stream.content)\n // Trim trailing whitespace so ReactMarkdown doesn't create empty trailing\n // block elements that the cursor would land inside.\n const displayedContent = (stream.active || isDraining) ? rawDisplayed.trimEnd() : rawDisplayed\n\n // Notify parent of draining state changes\n useEffect(() => {\n onDrainingRef.current?.(isDraining)\n }, [isDraining])\n\n // Format agent label from stream.agent (e.g. \"coordinator_agent\" → \"coordinator\")\n const agentLabel = stream.agent\n ? stream.agent.replace('_agent', '').replace('_', ' ')\n : null\n\n // Show phase indicator only when there's no displayed content yet\n const showPhaseIndicator = showPhases && stream.active && stream.phase !== 'idle' && !displayedContent\n const showCursor = (stream.active || isDraining) && !!displayedContent\n\n return (\n <div className={twMerge('flex w-full flex-col items-start', className)} data-testid=\"streaming-message\">\n {/* Screen reader announcements */}\n <div aria-live=\"assertive\" className=\"sr-only\">\n {stream.active && stream.phase !== 'idle' && 'Response started'}\n {!stream.active && stream.content && 'Response complete'}\n </div>\n\n {showCursor && <style>{CURSOR_STYLES}</style>}\n\n {/* Agent label */}\n {agentLabel && (\n <div className=\"text-[11px] font-display font-semibold uppercase tracking-[0.08em] text-text-muted px-1 mb-1.5\">\n {agentLabel}\n </div>\n )}\n\n <div className=\"max-w-[88%] px-4 py-3 rounded-[18px] rounded-tl-[4px] bg-surface border border-border motion-safe:animate-springFromLeft\">\n {showPhaseIndicator && (\n <div\n className=\"flex items-center gap-2 text-sm text-text-secondary\"\n data-testid=\"phase-indicator\"\n >\n <span aria-hidden=\"true\">\n <WaveLoader size=\"sm\" color=\"#38bdf8\" />\n </span>\n <span>{phaseLabel}</span>\n </div>\n )}\n\n {displayedContent && (\n <ResponseMessage\n content={displayedContent}\n className={showCursor ? 'sk-streaming-cursor' : undefined}\n />\n )}\n </div>\n </div>\n )\n}\n\nexport { StreamingMessage }\nexport type { StreamingMessageProps }\n","'use client'\n\nimport { useState, useRef, useEffect } from 'react'\n\nexport interface CharacterDrainResult {\n displayed: string\n isDraining: boolean\n}\n\n/**\n * Smoothly drains a growing `target` string character-by-character using\n * `requestAnimationFrame`, decoupling visual rendering from network packet\n * timing so text appears to type out instead of arriving in chunks.\n *\n * When `target` resets to empty (e.g. stream finished), the hook continues\n * draining the previous content to completion before resetting, so the\n * typing animation isn't cut short.\n *\n * Design: the RAF loop is long-lived — it does NOT restart on every delta.\n * The tick function is stored in a ref (updated each render) so the loop\n * always reads the latest drainTarget without being cancelled/restarted.\n * A separate kick-start effect re-fires the loop when it was idle and new\n * content arrives.\n */\nexport function useCharacterDrain(target: string, msPerChar = 15): CharacterDrainResult {\n const [displayed, setDisplayed] = useState('')\n const indexRef = useRef(0)\n const lastTimeRef = useRef(0)\n const rafRef = useRef<number | null>(null)\n // Holds the last non-empty target so we can finish draining after source resets\n const drainTargetRef = useRef('')\n const msPerCharRef = useRef(msPerChar)\n\n msPerCharRef.current = msPerChar\n\n // Update drain target when new content arrives; preserve old value on reset\n if (target !== '') {\n drainTargetRef.current = target\n }\n\n const drainTarget = drainTargetRef.current\n const isDraining = displayed.length < drainTarget.length\n\n // Tick function stored in ref so the long-lived RAF loop always reads the\n // latest drainTarget and msPerChar without being cancelled/recreated.\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n const tickRef = useRef<(now: number) => void>(() => {})\n tickRef.current = (now: number) => {\n const currentTarget = drainTargetRef.current\n if (currentTarget === '') {\n rafRef.current = null\n return\n }\n\n if (lastTimeRef.current === 0) lastTimeRef.current = now\n const elapsed = now - lastTimeRef.current\n const charsToAdvance = Math.floor(elapsed / msPerCharRef.current)\n\n if (charsToAdvance > 0 && indexRef.current < currentTarget.length) {\n let nextIndex = Math.min(indexRef.current + charsToAdvance, currentTarget.length)\n // When the slice would end with whitespace, advance past it to include\n // the next visible character. This prevents trimEnd() in the streaming\n // UI from stripping trailing newlines and causing ReactMarkdown to\n // \"jump\" between block structures in a single frame (e.g. a heading\n // suddenly gaining a following paragraph with no transition).\n while (\n nextIndex < currentTarget.length &&\n currentTarget[nextIndex - 1].trim() === ''\n ) {\n nextIndex++\n }\n indexRef.current = nextIndex\n lastTimeRef.current = now\n setDisplayed(currentTarget.slice(0, nextIndex))\n }\n\n if (indexRef.current < currentTarget.length) {\n rafRef.current = requestAnimationFrame((t) => tickRef.current(t))\n } else {\n rafRef.current = null\n }\n }\n\n // Kick-start the RAF loop when new content arrives and the loop is idle.\n // No cleanup here — we intentionally do NOT cancel the running loop when\n // drainTarget grows; the long-lived tick will pick up new chars automatically.\n useEffect(() => {\n if (\n drainTargetRef.current !== '' &&\n indexRef.current < drainTargetRef.current.length &&\n rafRef.current === null\n ) {\n rafRef.current = requestAnimationFrame((t) => tickRef.current(t))\n }\n }, [drainTarget]) // drainTarget change = new content; check if loop needs kicking\n\n // Once drain completes and source stream is already done, reset all state\n useEffect(() => {\n if (target === '' && !isDraining && displayed !== '') {\n indexRef.current = 0\n lastTimeRef.current = 0\n drainTargetRef.current = ''\n setDisplayed('')\n }\n }, [target, isDraining, displayed])\n\n // Cancel any pending RAF on unmount\n useEffect(() => {\n return () => {\n if (rafRef.current !== null) {\n cancelAnimationFrame(rafRef.current)\n rafRef.current = null\n }\n }\n }, [])\n\n return { displayed, isDraining }\n}\n","import React from 'react'\nimport ReactMarkdown from 'react-markdown'\nimport rehypeSanitize from 'rehype-sanitize'\nimport { twMerge } from 'tailwind-merge'\n\ntype ResponseMessageProps = {\n content: string\n className?: string\n}\n\nfunction normalizeMarkdownLists(content: string) {\n // Some providers emit compact list markdown like \"Intro: - item 1 - item 2\".\n // Insert a paragraph break before an inline list marker so markdown parses it as a list.\n return content.replace(/:\\s+-\\s+/g, ':\\n\\n- ')\n}\n\nfunction ResponseMessage({ content, className }: ResponseMessageProps) {\n return (\n <div\n className={twMerge(\n 'text-sm leading-relaxed text-text-primary',\n '[&_p]:my-2',\n '[&_ul]:my-2 [&_ul]:list-disc [&_ul]:pl-6',\n '[&_ol]:my-2 [&_ol]:list-decimal [&_ol]:pl-6',\n '[&_li]:my-1',\n '[&_strong]:text-text-primary [&_strong]:font-semibold',\n '[&_em]:text-text-secondary',\n '[&_h1]:text-lg [&_h1]:font-semibold [&_h1]:text-text-primary [&_h1]:mt-4 [&_h1]:mb-2',\n '[&_h2]:text-base [&_h2]:font-semibold [&_h2]:text-text-primary [&_h2]:mt-3 [&_h2]:mb-1.5',\n '[&_h3]:text-sm [&_h3]:font-semibold [&_h3]:text-accent [&_h3]:mt-2 [&_h3]:mb-1',\n '[&_code]:bg-surface-raised [&_code]:text-accent [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:rounded [&_code]:text-xs [&_code]:font-mono',\n '[&_pre]:bg-surface-raised [&_pre]:border [&_pre]:border-border [&_pre]:rounded-xl [&_pre]:p-4 [&_pre]:overflow-x-auto',\n '[&_hr]:my-3 [&_hr]:border-border',\n '[&_blockquote]:border-l-2 [&_blockquote]:border-border-strong [&_blockquote]:pl-4 [&_blockquote]:text-text-secondary',\n '[&_a]:text-accent [&_a]:underline-offset-2 [&_a]:hover:text-accent/80',\n className,\n )}\n data-testid=\"response-message\"\n >\n <ReactMarkdown\n rehypePlugins={[rehypeSanitize]}\n components={{\n script: () => null,\n iframe: () => null,\n p: ({ children }) => <p className=\"my-2\">{children}</p>,\n ul: ({ children }) => <ul className=\"my-2 list-disc pl-6\">{children}</ul>,\n ol: ({ children }) => <ol className=\"my-2 list-decimal pl-6\">{children}</ol>,\n li: ({ children, ...props }) => {\n let content = children\n // Strip leading \"N.\" or \"N)\" text that duplicates the ol counter\n if ((props as Record<string, unknown>).ordered) {\n content = React.Children.map(children, (child, i) => {\n if (i === 0 && typeof child === 'string') {\n return child.replace(/^\\d+[.)]\\s*/, '')\n }\n return child\n })\n }\n return <li className=\"my-1\">{content}</li>\n },\n strong: ({ children }) => <strong className=\"font-semibold\">{children}</strong>,\n em: ({ children }) => <em className=\"italic text-text-secondary\">{children}</em>,\n h1: ({ children }) => <h1 className=\"text-base font-bold mt-4 mb-2\">{children}</h1>,\n h2: ({ children }) => <h2 className=\"text-sm font-bold mt-3 mb-1\">{children}</h2>,\n h3: ({ children }) => <h3 className=\"text-sm font-semibold mt-2 mb-1\">{children}</h3>,\n hr: () => <hr className=\"my-3 border-border\" />,\n code: ({ children }) => <code className=\"bg-surface-sunken rounded px-1 py-0.5 text-xs font-mono\">{children}</code>,\n }}\n >\n {normalizeMarkdownLists(content)}\n </ReactMarkdown>\n </div>\n )\n}\n\nexport { ResponseMessage }\nexport type { ResponseMessageProps }\n","import React from 'react'\nimport { useReducedMotion } from '@surf-kit/hooks'\n\ntype ThinkingIndicatorProps = {\n label?: string\n className?: string\n}\n\nfunction ThinkingIndicator({ label = 'Thinking...', className }: ThinkingIndicatorProps) {\n const reducedMotion = useReducedMotion()\n\n return (\n <span\n role=\"status\"\n className={`inline-flex items-center gap-2 text-sm motion-safe:animate-fadeSlideUpSm ${className ?? ''}`}\n data-testid=\"thinking-indicator\"\n >\n <span className=\"text-text-secondary\">{label}</span>\n {!reducedMotion && (\n <span className=\"flex gap-1\" aria-hidden=\"true\" data-testid=\"animated-dots\">\n <span className=\"w-1.5 h-1.5 rounded-full bg-current animate-bounce [animation-delay:0ms]\" />\n <span className=\"w-1.5 h-1.5 rounded-full bg-current animate-bounce [animation-delay:150ms]\" />\n <span className=\"w-1.5 h-1.5 rounded-full bg-current animate-bounce [animation-delay:300ms]\" />\n </span>\n )}\n </span>\n )\n}\n\nexport { ThinkingIndicator }\nexport type { ThinkingIndicatorProps }\n","import React from 'react'\nimport { WaveLoader } from '@surf-kit/core'\n\ntype ToolExecutionProps = {\n tool: string\n label?: string\n className?: string\n}\n\nconst defaultLabels: Record<string, string> = {\n search: 'Searching knowledge base...',\n retrieve: 'Retrieving documents...',\n calculate: 'Calculating...',\n}\n\nfunction ToolExecution({ tool, label, className }: ToolExecutionProps) {\n const displayLabel = label ?? defaultLabels[tool] ?? `Running ${tool}...`\n\n return (\n <div\n className={`flex items-center gap-2 text-sm text-text-secondary ${className ?? ''}`}\n role=\"status\"\n data-testid=\"tool-execution\"\n >\n <span aria-hidden=\"true\">\n <WaveLoader size=\"sm\" color=\"#38bdf8\" />\n </span>\n <span>{displayLabel}</span>\n </div>\n )\n}\n\nexport { ToolExecution }\nexport type { ToolExecutionProps }\n","import React from 'react'\nimport { WaveLoader } from '@surf-kit/core'\nimport type { Source } from '../../types/agent'\n\ntype RetrievalProgressProps = {\n sources: Source[]\n isActive: boolean\n className?: string\n}\n\nfunction RetrievalProgress({ sources, isActive, className }: RetrievalProgressProps) {\n return (\n <div\n className={`space-y-2 ${className ?? ''}`}\n role=\"status\"\n aria-label={\n isActive\n ? `Retrieving sources, ${sources.length} found so far`\n : `${sources.length} sources found`\n }\n data-testid=\"retrieval-progress\"\n >\n {isActive && (\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <span aria-hidden=\"true\">\n <WaveLoader size=\"sm\" color=\"#38bdf8\" />\n </span>\n <span>Retrieving sources...</span>\n </div>\n )}\n\n {sources.length > 0 && (\n <ul className=\"space-y-1\" data-testid=\"source-list\">\n {sources.map((source, index) => (\n <li\n key={source.document_id}\n className=\"text-sm text-text-secondary flex items-center gap-2 animate-in fade-in slide-in-from-left-2\"\n style={{ animationDelay: `${index * 100}ms`, animationFillMode: 'both' }}\n data-testid=\"retrieval-source-item\"\n >\n <span className=\"w-1.5 h-1.5 rounded-full bg-accent flex-shrink-0\" aria-hidden=\"true\" />\n <span className=\"truncate\">{source.title}</span>\n </li>\n ))}\n </ul>\n )}\n </div>\n )\n}\n\nexport { RetrievalProgress }\nexport type { RetrievalProgressProps }\n","import React from 'react'\n\ntype VerificationProgressProps = {\n isActive: boolean\n label?: string\n className?: string\n}\n\nfunction VerificationProgress({\n isActive,\n label = 'Checking accuracy...',\n className,\n}: VerificationProgressProps) {\n if (!isActive) return null\n\n return (\n <div\n className={`flex items-center gap-2 text-sm ${className ?? ''}`}\n role=\"status\"\n data-testid=\"verification-progress\"\n >\n <svg\n className=\"w-4 h-4 animate-spin text-accent\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <circle className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\" />\n <path className=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z\" />\n </svg>\n <span className=\"text-accent animate-pulse\">{label}</span>\n </div>\n )\n}\n\nexport { VerificationProgress }\nexport type { VerificationProgressProps }\n","'use client'\n\nimport React, { useEffect, useState } from 'react'\n\nexport type TypewriterTextProps = {\n text: string\n speed?: number // ms per char, default 30\n delay?: number // ms to wait before starting, default 0\n onComplete?: () => void\n className?: string\n showCursor?: boolean // default true\n}\n\nexport function TypewriterText({\n text,\n speed = 30,\n delay = 0,\n onComplete,\n className = '',\n showCursor = true,\n}: TypewriterTextProps) {\n const [displayedText, setDisplayedText] = useState('')\n const [isComplete, setIsComplete] = useState(false)\n\n useEffect(() => {\n setDisplayedText('')\n setIsComplete(false)\n let index = 0\n let interval: ReturnType<typeof setInterval>\n const timeout = setTimeout(() => {\n interval = setInterval(() => {\n if (index < text.length) {\n setDisplayedText(text.slice(0, index + 1))\n index++\n } else {\n clearInterval(interval)\n setIsComplete(true)\n onComplete?.()\n }\n }, speed)\n }, delay)\n return () => {\n clearTimeout(timeout)\n clearInterval(interval)\n }\n }, [text, speed, delay, onComplete])\n\n return (\n <span className={className}>\n {displayedText}\n {showCursor && !isComplete && (\n <span className=\"typewriter-cursor\" aria-hidden=\"true\" />\n )}\n </span>\n )\n}\n","import React from 'react'\nimport { twMerge } from 'tailwind-merge'\nimport { useReducedMotion } from '@surf-kit/hooks'\n\ntype TypingIndicatorProps = {\n label?: string\n dotCount?: number\n className?: string\n}\n\nconst bounceKeyframes = `\n@keyframes typing-bounce {\n 0%, 80%, 100% { transform: translateY(0); }\n 40% { transform: translateY(-6px); }\n}\n`\n\nfunction TypingIndicator({\n label,\n dotCount = 3,\n className,\n}: TypingIndicatorProps) {\n const reducedMotion = useReducedMotion()\n\n return (\n <span\n role=\"status\"\n aria-label={label ?? 'typing'}\n className={twMerge('inline-flex items-center gap-2', className)}\n data-testid=\"typing-indicator\"\n >\n {!reducedMotion && <style>{bounceKeyframes}</style>}\n {label && (\n <span className=\"text-sm text-text-secondary\">{label}</span>\n )}\n <span className=\"flex gap-1\" data-testid=\"typing-dots\">\n {Array.from({ length: dotCount }, (_, i) => (\n <span\n key={i}\n className=\"w-2 h-2 rounded-full bg-text-secondary\"\n style={\n reducedMotion\n ? undefined\n : {\n animation: 'typing-bounce 1.4s infinite ease-in-out',\n animationDelay: `${i * 160}ms`,\n }\n }\n />\n ))}\n </span>\n </span>\n )\n}\n\nexport { TypingIndicator }\nexport type { TypingIndicatorProps }\n","import React from 'react'\nimport { twMerge } from 'tailwind-merge'\nimport { useReducedMotion } from '@surf-kit/hooks'\n\ntype TextGlimmerProps = {\n lines?: number\n className?: string\n}\n\nconst shimmerKeyframes = `\n@keyframes text-shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n}\n`\n\nconst widthPattern = ['100%', '90%', '60%']\n\nfunction TextGlimmer({ lines = 3, className }: TextGlimmerProps) {\n const reducedMotion = useReducedMotion()\n\n return (\n <div\n role=\"status\"\n aria-label=\"Loading\"\n className={twMerge('flex flex-col gap-2', className)}\n data-testid=\"text-glimmer\"\n >\n {!reducedMotion && <style>{shimmerKeyframes}</style>}\n {Array.from({ length: lines }, (_, i) => (\n <div\n key={i}\n className=\"h-3 rounded bg-surface-raised\"\n style={{\n width: widthPattern[i % widthPattern.length],\n ...(reducedMotion\n ? undefined\n : {\n backgroundImage:\n 'linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent)',\n backgroundSize: '200% 100%',\n animation: 'text-shimmer 1.5s infinite ease-in-out',\n }),\n }}\n data-testid=\"glimmer-line\"\n />\n ))}\n </div>\n )\n}\n\nexport { TextGlimmer }\nexport type { TextGlimmerProps }\n","import React from 'react'\nimport { twMerge } from 'tailwind-merge'\nimport { useReducedMotion } from '@surf-kit/hooks'\nimport { TypingIndicator } from '../TypingIndicator'\n\ntype StreamingListProps<T> = {\n items: T[]\n renderItem: (item: T, index: number) => React.ReactNode\n isStreaming?: boolean\n className?: string\n emptyMessage?: string\n}\n\nconst fadeSlideInKeyframes = `\n@keyframes fadeSlideIn {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n`\n\nfunction StreamingList<T>({\n items,\n renderItem,\n isStreaming = false,\n className,\n emptyMessage,\n}: StreamingListProps<T>) {\n const reducedMotion = useReducedMotion()\n\n if (items.length === 0 && !isStreaming) {\n return emptyMessage ? (\n <p className={twMerge('text-sm text-text-secondary', className)} data-testid=\"streaming-list-empty\">\n {emptyMessage}\n </p>\n ) : null\n }\n\n return (\n <ul\n aria-live=\"polite\"\n className={twMerge('list-none p-0 m-0', className)}\n data-testid=\"streaming-list\"\n >\n {!reducedMotion && <style>{fadeSlideInKeyframes}</style>}\n {items.map((item, index) => (\n <li\n key={index}\n style={\n reducedMotion\n ? undefined\n : { animation: 'fadeSlideIn 0.3s ease-out' }\n }\n data-testid=\"streaming-list-item\"\n >\n {renderItem(item, index)}\n </li>\n ))}\n {isStreaming && (\n <li data-testid=\"streaming-list-loading\">\n <TypingIndicator />\n </li>\n )}\n </ul>\n )\n}\n\nexport { StreamingList }\nexport type { StreamingListProps }\n","import React from 'react'\nimport { twMerge } from 'tailwind-merge'\nimport { useReducedMotion } from '@surf-kit/hooks'\nimport { TextGlimmer } from '../TextGlimmer'\n\ntype StreamingStructureProps = {\n data: Record<string, unknown>\n isStreaming?: boolean\n className?: string\n}\n\nconst fadeSlideInKeyframes = `\n@keyframes fadeSlideIn {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n`\n\nfunction renderValue(value: unknown, reducedMotion: boolean): React.ReactNode {\n if (value === null) {\n return <span className=\"italic text-text-secondary\">null</span>\n }\n if (value === undefined) {\n return <span className=\"italic text-text-secondary\">undefined</span>\n }\n if (Array.isArray(value)) {\n return (\n <ol className=\"list-decimal pl-4 m-0\">\n {value.map((item, i) => (\n <li key={i} className=\"text-text-secondary text-sm\">\n {renderValue(item, reducedMotion)}\n </li>\n ))}\n </ol>\n )\n }\n if (typeof value === 'object') {\n return renderNestedDl(value as Record<string, unknown>, reducedMotion)\n }\n return String(value)\n}\n\nfunction renderNestedDl(\n data: Record<string, unknown>,\n reducedMotion: boolean,\n): React.ReactNode {\n const entries = Object.entries(data)\n return (\n <dl className=\"pl-4 m-0\" data-testid=\"streaming-structure-nested\">\n {entries.map(([key, value]) => (\n <div\n key={key}\n style={\n reducedMotion\n ? undefined\n : { animation: 'fadeSlideIn 0.3s ease-out' }\n }\n >\n <dt className=\"font-medium text-text-primary text-sm\">{key}</dt>\n <dd className=\"text-text-secondary text-sm ml-0 mb-3\">\n {renderValue(value, reducedMotion)}\n </dd>\n </div>\n ))}\n </dl>\n )\n}\n\nfunction StreamingStructure({\n data,\n isStreaming = false,\n className,\n}: StreamingStructureProps) {\n const reducedMotion = useReducedMotion()\n const entries = Object.entries(data)\n\n return (\n <dl\n aria-live=\"polite\"\n className={twMerge('m-0', className)}\n data-testid=\"streaming-structure\"\n >\n {!reducedMotion && <style>{fadeSlideInKeyframes}</style>}\n {entries.map(([key, value]) => (\n <div\n key={key}\n style={\n reducedMotion\n ? undefined\n : { animation: 'fadeSlideIn 0.3s ease-out' }\n }\n data-testid=\"streaming-structure-entry\"\n >\n <dt className=\"font-medium text-text-primary text-sm\">{key}</dt>\n <dd className=\"text-text-secondary text-sm ml-0 mb-3\">\n {renderValue(value, reducedMotion)}\n </dd>\n </div>\n ))}\n {isStreaming && (\n <div data-testid=\"streaming-structure-loading\">\n <TextGlimmer lines={1} />\n </div>\n )}\n </dl>\n )\n}\n\nexport { StreamingStructure }\nexport type { StreamingStructureProps }\n"],"mappings":"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","names":["import_react","import_tailwind_merge","import_react","ReactMarkdown","rehypeSanitize","content","React","import_jsx_runtime","import_jsx_runtime","import_core","import_jsx_runtime","import_core","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime","import_tailwind_merge","import_hooks","import_jsx_runtime","import_tailwind_merge","import_hooks","import_jsx_runtime","import_tailwind_merge","import_hooks","import_jsx_runtime","import_tailwind_merge","import_hooks","import_jsx_runtime","fadeSlideInKeyframes"]}
1
+ {"version":3,"sources":["../../src/streaming/index.ts","../../src/streaming/StreamingMessage/StreamingMessage.tsx","../../src/hooks/useCharacterDrain.ts","../../src/response/ResponseMessage/ResponseMessage.tsx","../../src/streaming/ThinkingIndicator/ThinkingIndicator.tsx","../../src/streaming/ToolExecution/ToolExecution.tsx","../../src/streaming/RetrievalProgress/RetrievalProgress.tsx","../../src/streaming/VerificationProgress/VerificationProgress.tsx","../../src/streaming/TypewriterText/TypewriterText.tsx","../../src/streaming/TypingIndicator/TypingIndicator.tsx","../../src/streaming/TextGlimmer/TextGlimmer.tsx","../../src/streaming/StreamingList/StreamingList.tsx","../../src/streaming/StreamingStructure/StreamingStructure.tsx"],"sourcesContent":["export { StreamingMessage } from './StreamingMessage'\nexport type { StreamingMessageProps } from './StreamingMessage'\n\nexport { ThinkingIndicator } from './ThinkingIndicator'\nexport type { ThinkingIndicatorProps } from './ThinkingIndicator'\n\nexport { ToolExecution } from './ToolExecution'\nexport type { ToolExecutionProps } from './ToolExecution'\n\nexport { RetrievalProgress } from './RetrievalProgress'\nexport type { RetrievalProgressProps } from './RetrievalProgress'\n\nexport { VerificationProgress } from './VerificationProgress'\nexport type { VerificationProgressProps } from './VerificationProgress'\n\nexport { TypewriterText } from './TypewriterText'\nexport type { TypewriterTextProps } from './TypewriterText'\n\nexport { TypingIndicator } from './TypingIndicator'\nexport type { TypingIndicatorProps } from './TypingIndicator'\n\nexport { TextGlimmer } from './TextGlimmer'\nexport type { TextGlimmerProps } from './TextGlimmer'\n\nexport { StreamingList } from './StreamingList'\nexport type { StreamingListProps } from './StreamingList'\n\nexport { StreamingStructure } from './StreamingStructure'\nexport type { StreamingStructureProps } from './StreamingStructure'\n","'use client'\n\nimport React, { useEffect, useRef } from 'react'\nimport { twMerge } from 'tailwind-merge'\nimport { WaveLoader } from '@surf-kit/core'\nimport type { StreamState } from '../../types/streaming'\nimport { useCharacterDrain } from '../../hooks/useCharacterDrain'\nimport { ResponseMessage } from '../../response/ResponseMessage'\n\ntype StreamingMessageProps = {\n stream: StreamState\n onComplete?: () => void\n onDraining?: (isDraining: boolean) => void\n showPhases?: boolean\n className?: string\n}\n\nconst phaseLabels: Record<StreamState['phase'], string> = {\n idle: '',\n waiting: 'Waiting...',\n thinking: 'Thinking...',\n retrieving: 'Searching...',\n generating: 'Writing...',\n verifying: 'Verifying...',\n}\n\n// Cursor styles: a catch-all targets any last-child element, with overrides\n// for ul/ol (target last li) and blockquote (target last p) so the cursor\n// appears inline with the final text rather than on a new line.\n// Uses steps(1) for a crisp blink that won't smooth-fade to invisible.\nconst CURSOR_STYLES = `\n.sk-streaming-cursor > :not(ul,ol,blockquote,div:has(table)):last-child::after,\n.sk-streaming-cursor > :is(ul,ol):last-child > li:last-child::after,\n.sk-streaming-cursor > blockquote:last-child > p:last-child::after,\n.sk-streaming-cursor > div:has(table):last-child table tbody tr:last-child td:last-child::after {\n content: \"\";\n display: inline-block;\n width: 2px;\n height: 1em;\n background: var(--color-accent, #38bdf8);\n animation: sk-cursor-blink 0.8s steps(1) infinite;\n margin-left: 2px;\n vertical-align: text-bottom;\n}\n@keyframes sk-cursor-blink {\n 0%, 60% { opacity: 1; }\n 61%, 100% { opacity: 0; }\n}\n`\n\nfunction StreamingMessage({\n stream,\n onComplete,\n onDraining,\n showPhases = true,\n className,\n}: StreamingMessageProps) {\n const onCompleteRef = useRef(onComplete)\n onCompleteRef.current = onComplete\n const onDrainingRef = useRef(onDraining)\n onDrainingRef.current = onDraining\n const wasActiveRef = useRef(stream.active)\n\n useEffect(() => {\n if (wasActiveRef.current && !stream.active) {\n onCompleteRef.current?.()\n }\n wasActiveRef.current = stream.active\n }, [stream.active])\n\n const phaseLabel = phaseLabels[stream.phase]\n const { displayed: rawDisplayed, isDraining } = useCharacterDrain(stream.content)\n // Trim trailing whitespace so ReactMarkdown doesn't create empty trailing\n // block elements that the cursor would land inside.\n const displayedContent = (stream.active || isDraining) ? rawDisplayed.trimEnd() : rawDisplayed\n\n // Notify parent of draining state changes\n useEffect(() => {\n onDrainingRef.current?.(isDraining)\n }, [isDraining])\n\n // Format agent label from stream.agent (e.g. \"coordinator_agent\" → \"coordinator\")\n const agentLabel = stream.agent\n ? stream.agent.replace('_agent', '').replace('_', ' ')\n : null\n\n // Show phase indicator only when there's no displayed content yet\n const showPhaseIndicator = showPhases && stream.active && stream.phase !== 'idle' && !displayedContent\n const showCursor = (stream.active || isDraining) && !!displayedContent\n\n return (\n <div className={twMerge('flex w-full flex-col items-start', className)} data-testid=\"streaming-message\">\n {/* Screen reader announcements */}\n <div aria-live=\"assertive\" className=\"sr-only\">\n {stream.active && stream.phase !== 'idle' && 'Response started'}\n {!stream.active && stream.content && 'Response complete'}\n </div>\n\n {showCursor && <style>{CURSOR_STYLES}</style>}\n\n {/* Agent label */}\n {agentLabel && (\n <div className=\"text-[11px] font-display font-semibold uppercase tracking-[0.08em] text-text-muted px-1 mb-1.5\">\n {agentLabel}\n </div>\n )}\n\n <div className=\"max-w-[88%] px-4 py-3 rounded-[18px] rounded-tl-[4px] bg-surface border border-border motion-safe:animate-springFromLeft\">\n {showPhaseIndicator && (\n <div\n className=\"flex items-center gap-2 text-sm text-text-secondary\"\n data-testid=\"phase-indicator\"\n >\n <span aria-hidden=\"true\">\n <WaveLoader size=\"sm\" color=\"#38bdf8\" />\n </span>\n <span>{phaseLabel}</span>\n </div>\n )}\n\n {displayedContent && (\n <ResponseMessage\n content={displayedContent}\n className={showCursor ? 'sk-streaming-cursor' : undefined}\n />\n )}\n </div>\n </div>\n )\n}\n\nexport { StreamingMessage }\nexport type { StreamingMessageProps }\n","'use client'\n\nimport { useState, useRef, useEffect } from 'react'\n\nexport interface CharacterDrainResult {\n displayed: string\n isDraining: boolean\n}\n\n/**\n * Smoothly drains a growing `target` string character-by-character using\n * `requestAnimationFrame`, decoupling visual rendering from network packet\n * timing so text appears to type out instead of arriving in chunks.\n *\n * When `target` resets to empty (e.g. stream finished), the hook continues\n * draining the previous content to completion before resetting, so the\n * typing animation isn't cut short.\n *\n * Design: the RAF loop is long-lived — it does NOT restart on every delta.\n * The tick function is stored in a ref (updated each render) so the loop\n * always reads the latest drainTarget without being cancelled/restarted.\n * A separate kick-start effect re-fires the loop when it was idle and new\n * content arrives.\n */\nexport function useCharacterDrain(target: string, msPerChar = 15): CharacterDrainResult {\n const [displayed, setDisplayed] = useState('')\n const indexRef = useRef(0)\n const lastTimeRef = useRef(0)\n const rafRef = useRef<number | null>(null)\n // Holds the last non-empty target so we can finish draining after source resets\n const drainTargetRef = useRef('')\n const msPerCharRef = useRef(msPerChar)\n\n msPerCharRef.current = msPerChar\n\n // Update drain target when new content arrives; preserve old value on reset\n if (target !== '') {\n drainTargetRef.current = target\n }\n\n const drainTarget = drainTargetRef.current\n const isDraining = displayed.length < drainTarget.length\n\n // Tick function stored in ref so the long-lived RAF loop always reads the\n // latest drainTarget and msPerChar without being cancelled/recreated.\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n const tickRef = useRef<(now: number) => void>(() => {})\n tickRef.current = (now: number) => {\n const currentTarget = drainTargetRef.current\n if (currentTarget === '') {\n rafRef.current = null\n return\n }\n\n if (lastTimeRef.current === 0) lastTimeRef.current = now\n const elapsed = now - lastTimeRef.current\n const charsToAdvance = Math.floor(elapsed / msPerCharRef.current)\n\n if (charsToAdvance > 0 && indexRef.current < currentTarget.length) {\n let nextIndex = Math.min(indexRef.current + charsToAdvance, currentTarget.length)\n // When the slice would end with whitespace, advance past it to include\n // the next visible character. This prevents trimEnd() in the streaming\n // UI from stripping trailing newlines and causing ReactMarkdown to\n // \"jump\" between block structures in a single frame (e.g. a heading\n // suddenly gaining a following paragraph with no transition).\n while (\n nextIndex < currentTarget.length &&\n currentTarget[nextIndex - 1].trim() === ''\n ) {\n nextIndex++\n }\n indexRef.current = nextIndex\n lastTimeRef.current = now\n setDisplayed(currentTarget.slice(0, nextIndex))\n }\n\n if (indexRef.current < currentTarget.length) {\n rafRef.current = requestAnimationFrame((t) => tickRef.current(t))\n } else {\n rafRef.current = null\n }\n }\n\n // Kick-start the RAF loop when new content arrives and the loop is idle.\n // No cleanup here — we intentionally do NOT cancel the running loop when\n // drainTarget grows; the long-lived tick will pick up new chars automatically.\n useEffect(() => {\n if (\n drainTargetRef.current !== '' &&\n indexRef.current < drainTargetRef.current.length &&\n rafRef.current === null\n ) {\n rafRef.current = requestAnimationFrame((t) => tickRef.current(t))\n }\n }, [drainTarget]) // drainTarget change = new content; check if loop needs kicking\n\n // Once drain completes and source stream is already done, reset all state\n useEffect(() => {\n if (target === '' && !isDraining && displayed !== '') {\n indexRef.current = 0\n lastTimeRef.current = 0\n drainTargetRef.current = ''\n setDisplayed('')\n }\n }, [target, isDraining, displayed])\n\n // Cancel any pending RAF on unmount\n useEffect(() => {\n return () => {\n if (rafRef.current !== null) {\n cancelAnimationFrame(rafRef.current)\n rafRef.current = null\n }\n }\n }, [])\n\n return { displayed, isDraining }\n}\n","import React from 'react'\nimport ReactMarkdown from 'react-markdown'\nimport rehypeSanitize from 'rehype-sanitize'\nimport remarkGfm from 'remark-gfm'\nimport { twMerge } from 'tailwind-merge'\n\ntype ResponseMessageProps = {\n content: string\n className?: string\n}\n\nfunction normalizeMarkdownLists(content: string) {\n // Some providers emit compact list markdown like \"Intro: - item 1 - item 2\".\n // Insert a paragraph break before an inline list marker so markdown parses it as a list.\n return content.replace(/:\\s+-\\s+/g, ':\\n\\n- ')\n}\n\nfunction ResponseMessage({ content, className }: ResponseMessageProps) {\n return (\n <div\n className={twMerge(\n 'text-sm leading-relaxed text-text-primary',\n '[&_p]:my-2',\n '[&_ul]:my-2 [&_ul]:list-disc [&_ul]:pl-6',\n '[&_ol]:my-2 [&_ol]:list-decimal [&_ol]:pl-6',\n '[&_li]:my-1',\n '[&_strong]:text-text-primary [&_strong]:font-semibold',\n '[&_em]:text-text-secondary',\n '[&_h1]:text-lg [&_h1]:font-semibold [&_h1]:text-text-primary [&_h1]:mt-4 [&_h1]:mb-2',\n '[&_h2]:text-base [&_h2]:font-semibold [&_h2]:text-text-primary [&_h2]:mt-3 [&_h2]:mb-1.5',\n '[&_h3]:text-sm [&_h3]:font-semibold [&_h3]:text-accent [&_h3]:mt-2 [&_h3]:mb-1',\n '[&_code]:bg-surface-raised [&_code]:text-accent [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:rounded [&_code]:text-xs [&_code]:font-mono',\n '[&_pre]:bg-surface-raised [&_pre]:border [&_pre]:border-border [&_pre]:rounded-xl [&_pre]:p-4 [&_pre]:overflow-x-auto',\n '[&_hr]:my-3 [&_hr]:border-border',\n '[&_blockquote]:border-l-2 [&_blockquote]:border-border-strong [&_blockquote]:pl-4 [&_blockquote]:text-text-secondary',\n '[&_table]:w-full [&_table]:text-sm [&_table]:border-collapse [&_table]:my-2',\n '[&_thead]:border-b [&_thead]:border-border',\n '[&_th]:text-left [&_th]:px-2 [&_th]:py-1.5 [&_th]:font-semibold',\n '[&_td]:px-2 [&_td]:py-1.5 [&_td]:border-t [&_td]:border-border/50',\n '[&_a]:text-accent [&_a]:underline-offset-2 [&_a]:hover:text-accent/80',\n className,\n )}\n data-testid=\"response-message\"\n >\n <ReactMarkdown\n remarkPlugins={[remarkGfm]}\n rehypePlugins={[rehypeSanitize]}\n components={{\n script: () => null,\n iframe: () => null,\n p: ({ children }) => <p className=\"my-2\">{children}</p>,\n ul: ({ children }) => <ul className=\"my-2 list-disc pl-6\">{children}</ul>,\n ol: ({ children }) => <ol className=\"my-2 list-decimal pl-6\">{children}</ol>,\n li: ({ children, ...props }) => {\n let content = children\n // Strip leading \"N.\" or \"N)\" text that duplicates the ol counter\n if ((props as Record<string, unknown>).ordered) {\n content = React.Children.map(children, (child, i) => {\n if (i === 0 && typeof child === 'string') {\n return child.replace(/^\\d+[.)]\\s*/, '')\n }\n return child\n })\n }\n return <li className=\"my-1\">{content}</li>\n },\n strong: ({ children }) => <strong className=\"font-semibold\">{children}</strong>,\n em: ({ children }) => <em className=\"italic text-text-secondary\">{children}</em>,\n h1: ({ children }) => <h1 className=\"text-base font-bold mt-4 mb-2\">{children}</h1>,\n h2: ({ children }) => <h2 className=\"text-sm font-bold mt-3 mb-1\">{children}</h2>,\n h3: ({ children }) => <h3 className=\"text-sm font-semibold mt-2 mb-1\">{children}</h3>,\n hr: () => <hr className=\"my-3 border-border\" />,\n code: ({ children }) => <code className=\"bg-surface-sunken rounded px-1 py-0.5 text-xs font-mono\">{children}</code>,\n table: ({ children }) => (\n <div className=\"overflow-x-auto my-2\">\n <table className=\"w-full text-sm border-collapse\">{children}</table>\n </div>\n ),\n thead: ({ children }) => <thead className=\"border-b border-border\">{children}</thead>,\n th: ({ children }) => <th className=\"text-left px-2 py-1.5 font-semibold\">{children}</th>,\n td: ({ children }) => <td className=\"px-2 py-1.5 border-t border-border/50\">{children}</td>,\n }}\n >\n {normalizeMarkdownLists(content)}\n </ReactMarkdown>\n </div>\n )\n}\n\nexport { ResponseMessage }\nexport type { ResponseMessageProps }\n","import React from 'react'\nimport { useReducedMotion } from '@surf-kit/hooks'\n\ntype ThinkingIndicatorProps = {\n label?: string\n className?: string\n}\n\nfunction ThinkingIndicator({ label = 'Thinking...', className }: ThinkingIndicatorProps) {\n const reducedMotion = useReducedMotion()\n\n return (\n <span\n role=\"status\"\n className={`inline-flex items-center gap-2 text-sm motion-safe:animate-fadeSlideUpSm ${className ?? ''}`}\n data-testid=\"thinking-indicator\"\n >\n <span className=\"text-text-secondary\">{label}</span>\n {!reducedMotion && (\n <span className=\"flex gap-1\" aria-hidden=\"true\" data-testid=\"animated-dots\">\n <span className=\"w-1.5 h-1.5 rounded-full bg-current animate-bounce [animation-delay:0ms]\" />\n <span className=\"w-1.5 h-1.5 rounded-full bg-current animate-bounce [animation-delay:150ms]\" />\n <span className=\"w-1.5 h-1.5 rounded-full bg-current animate-bounce [animation-delay:300ms]\" />\n </span>\n )}\n </span>\n )\n}\n\nexport { ThinkingIndicator }\nexport type { ThinkingIndicatorProps }\n","import React from 'react'\nimport { WaveLoader } from '@surf-kit/core'\n\ntype ToolExecutionProps = {\n tool: string\n label?: string\n className?: string\n}\n\nconst defaultLabels: Record<string, string> = {\n search: 'Searching knowledge base...',\n retrieve: 'Retrieving documents...',\n calculate: 'Calculating...',\n}\n\nfunction ToolExecution({ tool, label, className }: ToolExecutionProps) {\n const displayLabel = label ?? defaultLabels[tool] ?? `Running ${tool}...`\n\n return (\n <div\n className={`flex items-center gap-2 text-sm text-text-secondary ${className ?? ''}`}\n role=\"status\"\n data-testid=\"tool-execution\"\n >\n <span aria-hidden=\"true\">\n <WaveLoader size=\"sm\" color=\"#38bdf8\" />\n </span>\n <span>{displayLabel}</span>\n </div>\n )\n}\n\nexport { ToolExecution }\nexport type { ToolExecutionProps }\n","import React from 'react'\nimport { WaveLoader } from '@surf-kit/core'\nimport type { Source } from '../../types/agent'\n\ntype RetrievalProgressProps = {\n sources: Source[]\n isActive: boolean\n className?: string\n}\n\nfunction RetrievalProgress({ sources, isActive, className }: RetrievalProgressProps) {\n return (\n <div\n className={`space-y-2 ${className ?? ''}`}\n role=\"status\"\n aria-label={\n isActive\n ? `Retrieving sources, ${sources.length} found so far`\n : `${sources.length} sources found`\n }\n data-testid=\"retrieval-progress\"\n >\n {isActive && (\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <span aria-hidden=\"true\">\n <WaveLoader size=\"sm\" color=\"#38bdf8\" />\n </span>\n <span>Retrieving sources...</span>\n </div>\n )}\n\n {sources.length > 0 && (\n <ul className=\"space-y-1\" data-testid=\"source-list\">\n {sources.map((source, index) => (\n <li\n key={source.document_id}\n className=\"text-sm text-text-secondary flex items-center gap-2 animate-in fade-in slide-in-from-left-2\"\n style={{ animationDelay: `${index * 100}ms`, animationFillMode: 'both' }}\n data-testid=\"retrieval-source-item\"\n >\n <span className=\"w-1.5 h-1.5 rounded-full bg-accent flex-shrink-0\" aria-hidden=\"true\" />\n <span className=\"truncate\">{source.title}</span>\n </li>\n ))}\n </ul>\n )}\n </div>\n )\n}\n\nexport { RetrievalProgress }\nexport type { RetrievalProgressProps }\n","import React from 'react'\n\ntype VerificationProgressProps = {\n isActive: boolean\n label?: string\n className?: string\n}\n\nfunction VerificationProgress({\n isActive,\n label = 'Checking accuracy...',\n className,\n}: VerificationProgressProps) {\n if (!isActive) return null\n\n return (\n <div\n className={`flex items-center gap-2 text-sm ${className ?? ''}`}\n role=\"status\"\n data-testid=\"verification-progress\"\n >\n <svg\n className=\"w-4 h-4 animate-spin text-accent\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <circle className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\" />\n <path className=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z\" />\n </svg>\n <span className=\"text-accent animate-pulse\">{label}</span>\n </div>\n )\n}\n\nexport { VerificationProgress }\nexport type { VerificationProgressProps }\n","'use client'\n\nimport React, { useEffect, useState } from 'react'\n\nexport type TypewriterTextProps = {\n text: string\n speed?: number // ms per char, default 30\n delay?: number // ms to wait before starting, default 0\n onComplete?: () => void\n className?: string\n showCursor?: boolean // default true\n}\n\nexport function TypewriterText({\n text,\n speed = 30,\n delay = 0,\n onComplete,\n className = '',\n showCursor = true,\n}: TypewriterTextProps) {\n const [displayedText, setDisplayedText] = useState('')\n const [isComplete, setIsComplete] = useState(false)\n\n useEffect(() => {\n setDisplayedText('')\n setIsComplete(false)\n let index = 0\n let interval: ReturnType<typeof setInterval>\n const timeout = setTimeout(() => {\n interval = setInterval(() => {\n if (index < text.length) {\n setDisplayedText(text.slice(0, index + 1))\n index++\n } else {\n clearInterval(interval)\n setIsComplete(true)\n onComplete?.()\n }\n }, speed)\n }, delay)\n return () => {\n clearTimeout(timeout)\n clearInterval(interval)\n }\n }, [text, speed, delay, onComplete])\n\n return (\n <span className={className}>\n {displayedText}\n {showCursor && !isComplete && (\n <span className=\"typewriter-cursor\" aria-hidden=\"true\" />\n )}\n </span>\n )\n}\n","import React from 'react'\nimport { twMerge } from 'tailwind-merge'\nimport { useReducedMotion } from '@surf-kit/hooks'\n\ntype TypingIndicatorProps = {\n label?: string\n dotCount?: number\n className?: string\n}\n\nconst bounceKeyframes = `\n@keyframes typing-bounce {\n 0%, 80%, 100% { transform: translateY(0); }\n 40% { transform: translateY(-6px); }\n}\n`\n\nfunction TypingIndicator({\n label,\n dotCount = 3,\n className,\n}: TypingIndicatorProps) {\n const reducedMotion = useReducedMotion()\n\n return (\n <span\n role=\"status\"\n aria-label={label ?? 'typing'}\n className={twMerge('inline-flex items-center gap-2', className)}\n data-testid=\"typing-indicator\"\n >\n {!reducedMotion && <style>{bounceKeyframes}</style>}\n {label && (\n <span className=\"text-sm text-text-secondary\">{label}</span>\n )}\n <span className=\"flex gap-1\" data-testid=\"typing-dots\">\n {Array.from({ length: dotCount }, (_, i) => (\n <span\n key={i}\n className=\"w-2 h-2 rounded-full bg-text-secondary\"\n style={\n reducedMotion\n ? undefined\n : {\n animation: 'typing-bounce 1.4s infinite ease-in-out',\n animationDelay: `${i * 160}ms`,\n }\n }\n />\n ))}\n </span>\n </span>\n )\n}\n\nexport { TypingIndicator }\nexport type { TypingIndicatorProps }\n","import React from 'react'\nimport { twMerge } from 'tailwind-merge'\nimport { useReducedMotion } from '@surf-kit/hooks'\n\ntype TextGlimmerProps = {\n lines?: number\n className?: string\n}\n\nconst shimmerKeyframes = `\n@keyframes text-shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n}\n`\n\nconst widthPattern = ['100%', '90%', '60%']\n\nfunction TextGlimmer({ lines = 3, className }: TextGlimmerProps) {\n const reducedMotion = useReducedMotion()\n\n return (\n <div\n role=\"status\"\n aria-label=\"Loading\"\n className={twMerge('flex flex-col gap-2', className)}\n data-testid=\"text-glimmer\"\n >\n {!reducedMotion && <style>{shimmerKeyframes}</style>}\n {Array.from({ length: lines }, (_, i) => (\n <div\n key={i}\n className=\"h-3 rounded bg-surface-raised\"\n style={{\n width: widthPattern[i % widthPattern.length],\n ...(reducedMotion\n ? undefined\n : {\n backgroundImage:\n 'linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent)',\n backgroundSize: '200% 100%',\n animation: 'text-shimmer 1.5s infinite ease-in-out',\n }),\n }}\n data-testid=\"glimmer-line\"\n />\n ))}\n </div>\n )\n}\n\nexport { TextGlimmer }\nexport type { TextGlimmerProps }\n","import React from 'react'\nimport { twMerge } from 'tailwind-merge'\nimport { useReducedMotion } from '@surf-kit/hooks'\nimport { TypingIndicator } from '../TypingIndicator'\n\ntype StreamingListProps<T> = {\n items: T[]\n renderItem: (item: T, index: number) => React.ReactNode\n isStreaming?: boolean\n className?: string\n emptyMessage?: string\n}\n\nconst fadeSlideInKeyframes = `\n@keyframes fadeSlideIn {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n`\n\nfunction StreamingList<T>({\n items,\n renderItem,\n isStreaming = false,\n className,\n emptyMessage,\n}: StreamingListProps<T>) {\n const reducedMotion = useReducedMotion()\n\n if (items.length === 0 && !isStreaming) {\n return emptyMessage ? (\n <p className={twMerge('text-sm text-text-secondary', className)} data-testid=\"streaming-list-empty\">\n {emptyMessage}\n </p>\n ) : null\n }\n\n return (\n <ul\n aria-live=\"polite\"\n className={twMerge('list-none p-0 m-0', className)}\n data-testid=\"streaming-list\"\n >\n {!reducedMotion && <style>{fadeSlideInKeyframes}</style>}\n {items.map((item, index) => (\n <li\n key={index}\n style={\n reducedMotion\n ? undefined\n : { animation: 'fadeSlideIn 0.3s ease-out' }\n }\n data-testid=\"streaming-list-item\"\n >\n {renderItem(item, index)}\n </li>\n ))}\n {isStreaming && (\n <li data-testid=\"streaming-list-loading\">\n <TypingIndicator />\n </li>\n )}\n </ul>\n )\n}\n\nexport { StreamingList }\nexport type { StreamingListProps }\n","import React from 'react'\nimport { twMerge } from 'tailwind-merge'\nimport { useReducedMotion } from '@surf-kit/hooks'\nimport { TextGlimmer } from '../TextGlimmer'\n\ntype StreamingStructureProps = {\n data: Record<string, unknown>\n isStreaming?: boolean\n className?: string\n}\n\nconst fadeSlideInKeyframes = `\n@keyframes fadeSlideIn {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n`\n\nfunction renderValue(value: unknown, reducedMotion: boolean): React.ReactNode {\n if (value === null) {\n return <span className=\"italic text-text-secondary\">null</span>\n }\n if (value === undefined) {\n return <span className=\"italic text-text-secondary\">undefined</span>\n }\n if (Array.isArray(value)) {\n return (\n <ol className=\"list-decimal pl-4 m-0\">\n {value.map((item, i) => (\n <li key={i} className=\"text-text-secondary text-sm\">\n {renderValue(item, reducedMotion)}\n </li>\n ))}\n </ol>\n )\n }\n if (typeof value === 'object') {\n return renderNestedDl(value as Record<string, unknown>, reducedMotion)\n }\n return String(value)\n}\n\nfunction renderNestedDl(\n data: Record<string, unknown>,\n reducedMotion: boolean,\n): React.ReactNode {\n const entries = Object.entries(data)\n return (\n <dl className=\"pl-4 m-0\" data-testid=\"streaming-structure-nested\">\n {entries.map(([key, value]) => (\n <div\n key={key}\n style={\n reducedMotion\n ? undefined\n : { animation: 'fadeSlideIn 0.3s ease-out' }\n }\n >\n <dt className=\"font-medium text-text-primary text-sm\">{key}</dt>\n <dd className=\"text-text-secondary text-sm ml-0 mb-3\">\n {renderValue(value, reducedMotion)}\n </dd>\n </div>\n ))}\n </dl>\n )\n}\n\nfunction StreamingStructure({\n data,\n isStreaming = false,\n className,\n}: StreamingStructureProps) {\n const reducedMotion = useReducedMotion()\n const entries = Object.entries(data)\n\n return (\n <dl\n aria-live=\"polite\"\n className={twMerge('m-0', className)}\n data-testid=\"streaming-structure\"\n >\n {!reducedMotion && <style>{fadeSlideInKeyframes}</style>}\n {entries.map(([key, value]) => (\n <div\n key={key}\n style={\n reducedMotion\n ? undefined\n : { animation: 'fadeSlideIn 0.3s ease-out' }\n }\n data-testid=\"streaming-structure-entry\"\n >\n <dt className=\"font-medium text-text-primary text-sm\">{key}</dt>\n <dd className=\"text-text-secondary text-sm ml-0 mb-3\">\n {renderValue(value, reducedMotion)}\n </dd>\n </div>\n ))}\n {isStreaming && (\n <div data-testid=\"streaming-structure-loading\">\n <TextGlimmer lines={1} />\n </div>\n )}\n </dl>\n )\n}\n\nexport { StreamingStructure }\nexport type { StreamingStructureProps }\n"],"mappings":"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","names":["import_react","import_tailwind_merge","import_react","ReactMarkdown","remarkGfm","rehypeSanitize","content","React","import_jsx_runtime","import_jsx_runtime","import_core","import_jsx_runtime","import_core","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime","import_tailwind_merge","import_hooks","import_jsx_runtime","import_tailwind_merge","import_hooks","import_jsx_runtime","import_tailwind_merge","import_hooks","import_jsx_runtime","import_tailwind_merge","import_hooks","import_jsx_runtime","fadeSlideInKeyframes"]}
@@ -1,8 +1,8 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { a as StreamState } from '../streaming-x7umFHoP.cjs';
2
+ import { a as StreamState } from '../streaming-BHPXnwwo.cjs';
3
3
  import { S as Source } from '../agent-BNSmiexZ.cjs';
4
4
  import React from 'react';
5
- import '../chat-CGamM7Mz.cjs';
5
+ import '../chat-BRY3xGg_.cjs';
6
6
 
7
7
  type StreamingMessageProps = {
8
8
  stream: StreamState;
@@ -1,8 +1,8 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { a as StreamState } from '../streaming-Bx-ff2tt.js';
2
+ import { a as StreamState } from '../streaming-C6mbU7My.js';
3
3
  import { S as Source } from '../agent-BNSmiexZ.js';
4
4
  import React from 'react';
5
- import '../chat-BIIDOGrD.js';
5
+ import '../chat-CcKc6OAR.js';
6
6
 
7
7
  type StreamingMessageProps = {
8
8
  stream: StreamState;
@@ -74,6 +74,7 @@ function useCharacterDrain(target, msPerChar = 15) {
74
74
  import React from "react";
75
75
  import ReactMarkdown from "react-markdown";
76
76
  import rehypeSanitize from "rehype-sanitize";
77
+ import remarkGfm from "remark-gfm";
77
78
  import { twMerge } from "tailwind-merge";
78
79
  import { jsx } from "react/jsx-runtime";
79
80
  function normalizeMarkdownLists(content) {
@@ -98,6 +99,10 @@ function ResponseMessage({ content, className }) {
98
99
  "[&_pre]:bg-surface-raised [&_pre]:border [&_pre]:border-border [&_pre]:rounded-xl [&_pre]:p-4 [&_pre]:overflow-x-auto",
99
100
  "[&_hr]:my-3 [&_hr]:border-border",
100
101
  "[&_blockquote]:border-l-2 [&_blockquote]:border-border-strong [&_blockquote]:pl-4 [&_blockquote]:text-text-secondary",
102
+ "[&_table]:w-full [&_table]:text-sm [&_table]:border-collapse [&_table]:my-2",
103
+ "[&_thead]:border-b [&_thead]:border-border",
104
+ "[&_th]:text-left [&_th]:px-2 [&_th]:py-1.5 [&_th]:font-semibold",
105
+ "[&_td]:px-2 [&_td]:py-1.5 [&_td]:border-t [&_td]:border-border/50",
101
106
  "[&_a]:text-accent [&_a]:underline-offset-2 [&_a]:hover:text-accent/80",
102
107
  className
103
108
  ),
@@ -105,6 +110,7 @@ function ResponseMessage({ content, className }) {
105
110
  children: /* @__PURE__ */ jsx(
106
111
  ReactMarkdown,
107
112
  {
113
+ remarkPlugins: [remarkGfm],
108
114
  rehypePlugins: [rehypeSanitize],
109
115
  components: {
110
116
  script: () => null,
@@ -130,7 +136,11 @@ function ResponseMessage({ content, className }) {
130
136
  h2: ({ children }) => /* @__PURE__ */ jsx("h2", { className: "text-sm font-bold mt-3 mb-1", children }),
131
137
  h3: ({ children }) => /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold mt-2 mb-1", children }),
132
138
  hr: () => /* @__PURE__ */ jsx("hr", { className: "my-3 border-border" }),
133
- code: ({ children }) => /* @__PURE__ */ jsx("code", { className: "bg-surface-sunken rounded px-1 py-0.5 text-xs font-mono", children })
139
+ code: ({ children }) => /* @__PURE__ */ jsx("code", { className: "bg-surface-sunken rounded px-1 py-0.5 text-xs font-mono", children }),
140
+ table: ({ children }) => /* @__PURE__ */ jsx("div", { className: "overflow-x-auto my-2", children: /* @__PURE__ */ jsx("table", { className: "w-full text-sm border-collapse", children }) }),
141
+ thead: ({ children }) => /* @__PURE__ */ jsx("thead", { className: "border-b border-border", children }),
142
+ th: ({ children }) => /* @__PURE__ */ jsx("th", { className: "text-left px-2 py-1.5 font-semibold", children }),
143
+ td: ({ children }) => /* @__PURE__ */ jsx("td", { className: "px-2 py-1.5 border-t border-border/50", children })
134
144
  },
135
145
  children: normalizeMarkdownLists(content)
136
146
  }
@@ -150,9 +160,10 @@ var phaseLabels = {
150
160
  verifying: "Verifying..."
151
161
  };
152
162
  var CURSOR_STYLES = `
153
- .sk-streaming-cursor > :not(ul,ol,blockquote):last-child::after,
163
+ .sk-streaming-cursor > :not(ul,ol,blockquote,div:has(table)):last-child::after,
154
164
  .sk-streaming-cursor > :is(ul,ol):last-child > li:last-child::after,
155
- .sk-streaming-cursor > blockquote:last-child > p:last-child::after {
165
+ .sk-streaming-cursor > blockquote:last-child > p:last-child::after,
166
+ .sk-streaming-cursor > div:has(table):last-child table tbody tr:last-child td:last-child::after {
156
167
  content: "";
157
168
  display: inline-block;
158
169
  width: 2px;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/streaming/StreamingMessage/StreamingMessage.tsx","../../src/hooks/useCharacterDrain.ts","../../src/response/ResponseMessage/ResponseMessage.tsx","../../src/streaming/ThinkingIndicator/ThinkingIndicator.tsx","../../src/streaming/ToolExecution/ToolExecution.tsx","../../src/streaming/RetrievalProgress/RetrievalProgress.tsx","../../src/streaming/VerificationProgress/VerificationProgress.tsx","../../src/streaming/TypewriterText/TypewriterText.tsx","../../src/streaming/TypingIndicator/TypingIndicator.tsx","../../src/streaming/TextGlimmer/TextGlimmer.tsx","../../src/streaming/StreamingList/StreamingList.tsx","../../src/streaming/StreamingStructure/StreamingStructure.tsx"],"sourcesContent":["'use client'\n\nimport React, { useEffect, useRef } from 'react'\nimport { twMerge } from 'tailwind-merge'\nimport { WaveLoader } from '@surf-kit/core'\nimport type { StreamState } from '../../types/streaming'\nimport { useCharacterDrain } from '../../hooks/useCharacterDrain'\nimport { ResponseMessage } from '../../response/ResponseMessage'\n\ntype StreamingMessageProps = {\n stream: StreamState\n onComplete?: () => void\n onDraining?: (isDraining: boolean) => void\n showPhases?: boolean\n className?: string\n}\n\nconst phaseLabels: Record<StreamState['phase'], string> = {\n idle: '',\n waiting: 'Waiting...',\n thinking: 'Thinking...',\n retrieving: 'Searching...',\n generating: 'Writing...',\n verifying: 'Verifying...',\n}\n\n// Cursor styles: a catch-all targets any last-child element, with overrides\n// for ul/ol (target last li) and blockquote (target last p) so the cursor\n// appears inline with the final text rather than on a new line.\n// Uses steps(1) for a crisp blink that won't smooth-fade to invisible.\nconst CURSOR_STYLES = `\n.sk-streaming-cursor > :not(ul,ol,blockquote):last-child::after,\n.sk-streaming-cursor > :is(ul,ol):last-child > li:last-child::after,\n.sk-streaming-cursor > blockquote:last-child > p:last-child::after {\n content: \"\";\n display: inline-block;\n width: 2px;\n height: 1em;\n background: var(--color-accent, #38bdf8);\n animation: sk-cursor-blink 0.8s steps(1) infinite;\n margin-left: 2px;\n vertical-align: text-bottom;\n}\n@keyframes sk-cursor-blink {\n 0%, 60% { opacity: 1; }\n 61%, 100% { opacity: 0; }\n}\n`\n\nfunction StreamingMessage({\n stream,\n onComplete,\n onDraining,\n showPhases = true,\n className,\n}: StreamingMessageProps) {\n const onCompleteRef = useRef(onComplete)\n onCompleteRef.current = onComplete\n const onDrainingRef = useRef(onDraining)\n onDrainingRef.current = onDraining\n const wasActiveRef = useRef(stream.active)\n\n useEffect(() => {\n if (wasActiveRef.current && !stream.active) {\n onCompleteRef.current?.()\n }\n wasActiveRef.current = stream.active\n }, [stream.active])\n\n const phaseLabel = phaseLabels[stream.phase]\n const { displayed: rawDisplayed, isDraining } = useCharacterDrain(stream.content)\n // Trim trailing whitespace so ReactMarkdown doesn't create empty trailing\n // block elements that the cursor would land inside.\n const displayedContent = (stream.active || isDraining) ? rawDisplayed.trimEnd() : rawDisplayed\n\n // Notify parent of draining state changes\n useEffect(() => {\n onDrainingRef.current?.(isDraining)\n }, [isDraining])\n\n // Format agent label from stream.agent (e.g. \"coordinator_agent\" → \"coordinator\")\n const agentLabel = stream.agent\n ? stream.agent.replace('_agent', '').replace('_', ' ')\n : null\n\n // Show phase indicator only when there's no displayed content yet\n const showPhaseIndicator = showPhases && stream.active && stream.phase !== 'idle' && !displayedContent\n const showCursor = (stream.active || isDraining) && !!displayedContent\n\n return (\n <div className={twMerge('flex w-full flex-col items-start', className)} data-testid=\"streaming-message\">\n {/* Screen reader announcements */}\n <div aria-live=\"assertive\" className=\"sr-only\">\n {stream.active && stream.phase !== 'idle' && 'Response started'}\n {!stream.active && stream.content && 'Response complete'}\n </div>\n\n {showCursor && <style>{CURSOR_STYLES}</style>}\n\n {/* Agent label */}\n {agentLabel && (\n <div className=\"text-[11px] font-display font-semibold uppercase tracking-[0.08em] text-text-muted px-1 mb-1.5\">\n {agentLabel}\n </div>\n )}\n\n <div className=\"max-w-[88%] px-4 py-3 rounded-[18px] rounded-tl-[4px] bg-surface border border-border motion-safe:animate-springFromLeft\">\n {showPhaseIndicator && (\n <div\n className=\"flex items-center gap-2 text-sm text-text-secondary\"\n data-testid=\"phase-indicator\"\n >\n <span aria-hidden=\"true\">\n <WaveLoader size=\"sm\" color=\"#38bdf8\" />\n </span>\n <span>{phaseLabel}</span>\n </div>\n )}\n\n {displayedContent && (\n <ResponseMessage\n content={displayedContent}\n className={showCursor ? 'sk-streaming-cursor' : undefined}\n />\n )}\n </div>\n </div>\n )\n}\n\nexport { StreamingMessage }\nexport type { StreamingMessageProps }\n","'use client'\n\nimport { useState, useRef, useEffect } from 'react'\n\nexport interface CharacterDrainResult {\n displayed: string\n isDraining: boolean\n}\n\n/**\n * Smoothly drains a growing `target` string character-by-character using\n * `requestAnimationFrame`, decoupling visual rendering from network packet\n * timing so text appears to type out instead of arriving in chunks.\n *\n * When `target` resets to empty (e.g. stream finished), the hook continues\n * draining the previous content to completion before resetting, so the\n * typing animation isn't cut short.\n *\n * Design: the RAF loop is long-lived — it does NOT restart on every delta.\n * The tick function is stored in a ref (updated each render) so the loop\n * always reads the latest drainTarget without being cancelled/restarted.\n * A separate kick-start effect re-fires the loop when it was idle and new\n * content arrives.\n */\nexport function useCharacterDrain(target: string, msPerChar = 15): CharacterDrainResult {\n const [displayed, setDisplayed] = useState('')\n const indexRef = useRef(0)\n const lastTimeRef = useRef(0)\n const rafRef = useRef<number | null>(null)\n // Holds the last non-empty target so we can finish draining after source resets\n const drainTargetRef = useRef('')\n const msPerCharRef = useRef(msPerChar)\n\n msPerCharRef.current = msPerChar\n\n // Update drain target when new content arrives; preserve old value on reset\n if (target !== '') {\n drainTargetRef.current = target\n }\n\n const drainTarget = drainTargetRef.current\n const isDraining = displayed.length < drainTarget.length\n\n // Tick function stored in ref so the long-lived RAF loop always reads the\n // latest drainTarget and msPerChar without being cancelled/recreated.\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n const tickRef = useRef<(now: number) => void>(() => {})\n tickRef.current = (now: number) => {\n const currentTarget = drainTargetRef.current\n if (currentTarget === '') {\n rafRef.current = null\n return\n }\n\n if (lastTimeRef.current === 0) lastTimeRef.current = now\n const elapsed = now - lastTimeRef.current\n const charsToAdvance = Math.floor(elapsed / msPerCharRef.current)\n\n if (charsToAdvance > 0 && indexRef.current < currentTarget.length) {\n let nextIndex = Math.min(indexRef.current + charsToAdvance, currentTarget.length)\n // When the slice would end with whitespace, advance past it to include\n // the next visible character. This prevents trimEnd() in the streaming\n // UI from stripping trailing newlines and causing ReactMarkdown to\n // \"jump\" between block structures in a single frame (e.g. a heading\n // suddenly gaining a following paragraph with no transition).\n while (\n nextIndex < currentTarget.length &&\n currentTarget[nextIndex - 1].trim() === ''\n ) {\n nextIndex++\n }\n indexRef.current = nextIndex\n lastTimeRef.current = now\n setDisplayed(currentTarget.slice(0, nextIndex))\n }\n\n if (indexRef.current < currentTarget.length) {\n rafRef.current = requestAnimationFrame((t) => tickRef.current(t))\n } else {\n rafRef.current = null\n }\n }\n\n // Kick-start the RAF loop when new content arrives and the loop is idle.\n // No cleanup here — we intentionally do NOT cancel the running loop when\n // drainTarget grows; the long-lived tick will pick up new chars automatically.\n useEffect(() => {\n if (\n drainTargetRef.current !== '' &&\n indexRef.current < drainTargetRef.current.length &&\n rafRef.current === null\n ) {\n rafRef.current = requestAnimationFrame((t) => tickRef.current(t))\n }\n }, [drainTarget]) // drainTarget change = new content; check if loop needs kicking\n\n // Once drain completes and source stream is already done, reset all state\n useEffect(() => {\n if (target === '' && !isDraining && displayed !== '') {\n indexRef.current = 0\n lastTimeRef.current = 0\n drainTargetRef.current = ''\n setDisplayed('')\n }\n }, [target, isDraining, displayed])\n\n // Cancel any pending RAF on unmount\n useEffect(() => {\n return () => {\n if (rafRef.current !== null) {\n cancelAnimationFrame(rafRef.current)\n rafRef.current = null\n }\n }\n }, [])\n\n return { displayed, isDraining }\n}\n","import React from 'react'\nimport ReactMarkdown from 'react-markdown'\nimport rehypeSanitize from 'rehype-sanitize'\nimport { twMerge } from 'tailwind-merge'\n\ntype ResponseMessageProps = {\n content: string\n className?: string\n}\n\nfunction normalizeMarkdownLists(content: string) {\n // Some providers emit compact list markdown like \"Intro: - item 1 - item 2\".\n // Insert a paragraph break before an inline list marker so markdown parses it as a list.\n return content.replace(/:\\s+-\\s+/g, ':\\n\\n- ')\n}\n\nfunction ResponseMessage({ content, className }: ResponseMessageProps) {\n return (\n <div\n className={twMerge(\n 'text-sm leading-relaxed text-text-primary',\n '[&_p]:my-2',\n '[&_ul]:my-2 [&_ul]:list-disc [&_ul]:pl-6',\n '[&_ol]:my-2 [&_ol]:list-decimal [&_ol]:pl-6',\n '[&_li]:my-1',\n '[&_strong]:text-text-primary [&_strong]:font-semibold',\n '[&_em]:text-text-secondary',\n '[&_h1]:text-lg [&_h1]:font-semibold [&_h1]:text-text-primary [&_h1]:mt-4 [&_h1]:mb-2',\n '[&_h2]:text-base [&_h2]:font-semibold [&_h2]:text-text-primary [&_h2]:mt-3 [&_h2]:mb-1.5',\n '[&_h3]:text-sm [&_h3]:font-semibold [&_h3]:text-accent [&_h3]:mt-2 [&_h3]:mb-1',\n '[&_code]:bg-surface-raised [&_code]:text-accent [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:rounded [&_code]:text-xs [&_code]:font-mono',\n '[&_pre]:bg-surface-raised [&_pre]:border [&_pre]:border-border [&_pre]:rounded-xl [&_pre]:p-4 [&_pre]:overflow-x-auto',\n '[&_hr]:my-3 [&_hr]:border-border',\n '[&_blockquote]:border-l-2 [&_blockquote]:border-border-strong [&_blockquote]:pl-4 [&_blockquote]:text-text-secondary',\n '[&_a]:text-accent [&_a]:underline-offset-2 [&_a]:hover:text-accent/80',\n className,\n )}\n data-testid=\"response-message\"\n >\n <ReactMarkdown\n rehypePlugins={[rehypeSanitize]}\n components={{\n script: () => null,\n iframe: () => null,\n p: ({ children }) => <p className=\"my-2\">{children}</p>,\n ul: ({ children }) => <ul className=\"my-2 list-disc pl-6\">{children}</ul>,\n ol: ({ children }) => <ol className=\"my-2 list-decimal pl-6\">{children}</ol>,\n li: ({ children, ...props }) => {\n let content = children\n // Strip leading \"N.\" or \"N)\" text that duplicates the ol counter\n if ((props as Record<string, unknown>).ordered) {\n content = React.Children.map(children, (child, i) => {\n if (i === 0 && typeof child === 'string') {\n return child.replace(/^\\d+[.)]\\s*/, '')\n }\n return child\n })\n }\n return <li className=\"my-1\">{content}</li>\n },\n strong: ({ children }) => <strong className=\"font-semibold\">{children}</strong>,\n em: ({ children }) => <em className=\"italic text-text-secondary\">{children}</em>,\n h1: ({ children }) => <h1 className=\"text-base font-bold mt-4 mb-2\">{children}</h1>,\n h2: ({ children }) => <h2 className=\"text-sm font-bold mt-3 mb-1\">{children}</h2>,\n h3: ({ children }) => <h3 className=\"text-sm font-semibold mt-2 mb-1\">{children}</h3>,\n hr: () => <hr className=\"my-3 border-border\" />,\n code: ({ children }) => <code className=\"bg-surface-sunken rounded px-1 py-0.5 text-xs font-mono\">{children}</code>,\n }}\n >\n {normalizeMarkdownLists(content)}\n </ReactMarkdown>\n </div>\n )\n}\n\nexport { ResponseMessage }\nexport type { ResponseMessageProps }\n","import React from 'react'\nimport { useReducedMotion } from '@surf-kit/hooks'\n\ntype ThinkingIndicatorProps = {\n label?: string\n className?: string\n}\n\nfunction ThinkingIndicator({ label = 'Thinking...', className }: ThinkingIndicatorProps) {\n const reducedMotion = useReducedMotion()\n\n return (\n <span\n role=\"status\"\n className={`inline-flex items-center gap-2 text-sm motion-safe:animate-fadeSlideUpSm ${className ?? ''}`}\n data-testid=\"thinking-indicator\"\n >\n <span className=\"text-text-secondary\">{label}</span>\n {!reducedMotion && (\n <span className=\"flex gap-1\" aria-hidden=\"true\" data-testid=\"animated-dots\">\n <span className=\"w-1.5 h-1.5 rounded-full bg-current animate-bounce [animation-delay:0ms]\" />\n <span className=\"w-1.5 h-1.5 rounded-full bg-current animate-bounce [animation-delay:150ms]\" />\n <span className=\"w-1.5 h-1.5 rounded-full bg-current animate-bounce [animation-delay:300ms]\" />\n </span>\n )}\n </span>\n )\n}\n\nexport { ThinkingIndicator }\nexport type { ThinkingIndicatorProps }\n","import React from 'react'\nimport { WaveLoader } from '@surf-kit/core'\n\ntype ToolExecutionProps = {\n tool: string\n label?: string\n className?: string\n}\n\nconst defaultLabels: Record<string, string> = {\n search: 'Searching knowledge base...',\n retrieve: 'Retrieving documents...',\n calculate: 'Calculating...',\n}\n\nfunction ToolExecution({ tool, label, className }: ToolExecutionProps) {\n const displayLabel = label ?? defaultLabels[tool] ?? `Running ${tool}...`\n\n return (\n <div\n className={`flex items-center gap-2 text-sm text-text-secondary ${className ?? ''}`}\n role=\"status\"\n data-testid=\"tool-execution\"\n >\n <span aria-hidden=\"true\">\n <WaveLoader size=\"sm\" color=\"#38bdf8\" />\n </span>\n <span>{displayLabel}</span>\n </div>\n )\n}\n\nexport { ToolExecution }\nexport type { ToolExecutionProps }\n","import React from 'react'\nimport { WaveLoader } from '@surf-kit/core'\nimport type { Source } from '../../types/agent'\n\ntype RetrievalProgressProps = {\n sources: Source[]\n isActive: boolean\n className?: string\n}\n\nfunction RetrievalProgress({ sources, isActive, className }: RetrievalProgressProps) {\n return (\n <div\n className={`space-y-2 ${className ?? ''}`}\n role=\"status\"\n aria-label={\n isActive\n ? `Retrieving sources, ${sources.length} found so far`\n : `${sources.length} sources found`\n }\n data-testid=\"retrieval-progress\"\n >\n {isActive && (\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <span aria-hidden=\"true\">\n <WaveLoader size=\"sm\" color=\"#38bdf8\" />\n </span>\n <span>Retrieving sources...</span>\n </div>\n )}\n\n {sources.length > 0 && (\n <ul className=\"space-y-1\" data-testid=\"source-list\">\n {sources.map((source, index) => (\n <li\n key={source.document_id}\n className=\"text-sm text-text-secondary flex items-center gap-2 animate-in fade-in slide-in-from-left-2\"\n style={{ animationDelay: `${index * 100}ms`, animationFillMode: 'both' }}\n data-testid=\"retrieval-source-item\"\n >\n <span className=\"w-1.5 h-1.5 rounded-full bg-accent flex-shrink-0\" aria-hidden=\"true\" />\n <span className=\"truncate\">{source.title}</span>\n </li>\n ))}\n </ul>\n )}\n </div>\n )\n}\n\nexport { RetrievalProgress }\nexport type { RetrievalProgressProps }\n","import React from 'react'\n\ntype VerificationProgressProps = {\n isActive: boolean\n label?: string\n className?: string\n}\n\nfunction VerificationProgress({\n isActive,\n label = 'Checking accuracy...',\n className,\n}: VerificationProgressProps) {\n if (!isActive) return null\n\n return (\n <div\n className={`flex items-center gap-2 text-sm ${className ?? ''}`}\n role=\"status\"\n data-testid=\"verification-progress\"\n >\n <svg\n className=\"w-4 h-4 animate-spin text-accent\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <circle className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\" />\n <path className=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z\" />\n </svg>\n <span className=\"text-accent animate-pulse\">{label}</span>\n </div>\n )\n}\n\nexport { VerificationProgress }\nexport type { VerificationProgressProps }\n","'use client'\n\nimport React, { useEffect, useState } from 'react'\n\nexport type TypewriterTextProps = {\n text: string\n speed?: number // ms per char, default 30\n delay?: number // ms to wait before starting, default 0\n onComplete?: () => void\n className?: string\n showCursor?: boolean // default true\n}\n\nexport function TypewriterText({\n text,\n speed = 30,\n delay = 0,\n onComplete,\n className = '',\n showCursor = true,\n}: TypewriterTextProps) {\n const [displayedText, setDisplayedText] = useState('')\n const [isComplete, setIsComplete] = useState(false)\n\n useEffect(() => {\n setDisplayedText('')\n setIsComplete(false)\n let index = 0\n let interval: ReturnType<typeof setInterval>\n const timeout = setTimeout(() => {\n interval = setInterval(() => {\n if (index < text.length) {\n setDisplayedText(text.slice(0, index + 1))\n index++\n } else {\n clearInterval(interval)\n setIsComplete(true)\n onComplete?.()\n }\n }, speed)\n }, delay)\n return () => {\n clearTimeout(timeout)\n clearInterval(interval)\n }\n }, [text, speed, delay, onComplete])\n\n return (\n <span className={className}>\n {displayedText}\n {showCursor && !isComplete && (\n <span className=\"typewriter-cursor\" aria-hidden=\"true\" />\n )}\n </span>\n )\n}\n","import React from 'react'\nimport { twMerge } from 'tailwind-merge'\nimport { useReducedMotion } from '@surf-kit/hooks'\n\ntype TypingIndicatorProps = {\n label?: string\n dotCount?: number\n className?: string\n}\n\nconst bounceKeyframes = `\n@keyframes typing-bounce {\n 0%, 80%, 100% { transform: translateY(0); }\n 40% { transform: translateY(-6px); }\n}\n`\n\nfunction TypingIndicator({\n label,\n dotCount = 3,\n className,\n}: TypingIndicatorProps) {\n const reducedMotion = useReducedMotion()\n\n return (\n <span\n role=\"status\"\n aria-label={label ?? 'typing'}\n className={twMerge('inline-flex items-center gap-2', className)}\n data-testid=\"typing-indicator\"\n >\n {!reducedMotion && <style>{bounceKeyframes}</style>}\n {label && (\n <span className=\"text-sm text-text-secondary\">{label}</span>\n )}\n <span className=\"flex gap-1\" data-testid=\"typing-dots\">\n {Array.from({ length: dotCount }, (_, i) => (\n <span\n key={i}\n className=\"w-2 h-2 rounded-full bg-text-secondary\"\n style={\n reducedMotion\n ? undefined\n : {\n animation: 'typing-bounce 1.4s infinite ease-in-out',\n animationDelay: `${i * 160}ms`,\n }\n }\n />\n ))}\n </span>\n </span>\n )\n}\n\nexport { TypingIndicator }\nexport type { TypingIndicatorProps }\n","import React from 'react'\nimport { twMerge } from 'tailwind-merge'\nimport { useReducedMotion } from '@surf-kit/hooks'\n\ntype TextGlimmerProps = {\n lines?: number\n className?: string\n}\n\nconst shimmerKeyframes = `\n@keyframes text-shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n}\n`\n\nconst widthPattern = ['100%', '90%', '60%']\n\nfunction TextGlimmer({ lines = 3, className }: TextGlimmerProps) {\n const reducedMotion = useReducedMotion()\n\n return (\n <div\n role=\"status\"\n aria-label=\"Loading\"\n className={twMerge('flex flex-col gap-2', className)}\n data-testid=\"text-glimmer\"\n >\n {!reducedMotion && <style>{shimmerKeyframes}</style>}\n {Array.from({ length: lines }, (_, i) => (\n <div\n key={i}\n className=\"h-3 rounded bg-surface-raised\"\n style={{\n width: widthPattern[i % widthPattern.length],\n ...(reducedMotion\n ? undefined\n : {\n backgroundImage:\n 'linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent)',\n backgroundSize: '200% 100%',\n animation: 'text-shimmer 1.5s infinite ease-in-out',\n }),\n }}\n data-testid=\"glimmer-line\"\n />\n ))}\n </div>\n )\n}\n\nexport { TextGlimmer }\nexport type { TextGlimmerProps }\n","import React from 'react'\nimport { twMerge } from 'tailwind-merge'\nimport { useReducedMotion } from '@surf-kit/hooks'\nimport { TypingIndicator } from '../TypingIndicator'\n\ntype StreamingListProps<T> = {\n items: T[]\n renderItem: (item: T, index: number) => React.ReactNode\n isStreaming?: boolean\n className?: string\n emptyMessage?: string\n}\n\nconst fadeSlideInKeyframes = `\n@keyframes fadeSlideIn {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n`\n\nfunction StreamingList<T>({\n items,\n renderItem,\n isStreaming = false,\n className,\n emptyMessage,\n}: StreamingListProps<T>) {\n const reducedMotion = useReducedMotion()\n\n if (items.length === 0 && !isStreaming) {\n return emptyMessage ? (\n <p className={twMerge('text-sm text-text-secondary', className)} data-testid=\"streaming-list-empty\">\n {emptyMessage}\n </p>\n ) : null\n }\n\n return (\n <ul\n aria-live=\"polite\"\n className={twMerge('list-none p-0 m-0', className)}\n data-testid=\"streaming-list\"\n >\n {!reducedMotion && <style>{fadeSlideInKeyframes}</style>}\n {items.map((item, index) => (\n <li\n key={index}\n style={\n reducedMotion\n ? undefined\n : { animation: 'fadeSlideIn 0.3s ease-out' }\n }\n data-testid=\"streaming-list-item\"\n >\n {renderItem(item, index)}\n </li>\n ))}\n {isStreaming && (\n <li data-testid=\"streaming-list-loading\">\n <TypingIndicator />\n </li>\n )}\n </ul>\n )\n}\n\nexport { StreamingList }\nexport type { StreamingListProps }\n","import React from 'react'\nimport { twMerge } from 'tailwind-merge'\nimport { useReducedMotion } from '@surf-kit/hooks'\nimport { TextGlimmer } from '../TextGlimmer'\n\ntype StreamingStructureProps = {\n data: Record<string, unknown>\n isStreaming?: boolean\n className?: string\n}\n\nconst fadeSlideInKeyframes = `\n@keyframes fadeSlideIn {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n`\n\nfunction renderValue(value: unknown, reducedMotion: boolean): React.ReactNode {\n if (value === null) {\n return <span className=\"italic text-text-secondary\">null</span>\n }\n if (value === undefined) {\n return <span className=\"italic text-text-secondary\">undefined</span>\n }\n if (Array.isArray(value)) {\n return (\n <ol className=\"list-decimal pl-4 m-0\">\n {value.map((item, i) => (\n <li key={i} className=\"text-text-secondary text-sm\">\n {renderValue(item, reducedMotion)}\n </li>\n ))}\n </ol>\n )\n }\n if (typeof value === 'object') {\n return renderNestedDl(value as Record<string, unknown>, reducedMotion)\n }\n return String(value)\n}\n\nfunction renderNestedDl(\n data: Record<string, unknown>,\n reducedMotion: boolean,\n): React.ReactNode {\n const entries = Object.entries(data)\n return (\n <dl className=\"pl-4 m-0\" data-testid=\"streaming-structure-nested\">\n {entries.map(([key, value]) => (\n <div\n key={key}\n style={\n reducedMotion\n ? undefined\n : { animation: 'fadeSlideIn 0.3s ease-out' }\n }\n >\n <dt className=\"font-medium text-text-primary text-sm\">{key}</dt>\n <dd className=\"text-text-secondary text-sm ml-0 mb-3\">\n {renderValue(value, reducedMotion)}\n </dd>\n </div>\n ))}\n </dl>\n )\n}\n\nfunction StreamingStructure({\n data,\n isStreaming = false,\n className,\n}: StreamingStructureProps) {\n const reducedMotion = useReducedMotion()\n const entries = Object.entries(data)\n\n return (\n <dl\n aria-live=\"polite\"\n className={twMerge('m-0', className)}\n data-testid=\"streaming-structure\"\n >\n {!reducedMotion && <style>{fadeSlideInKeyframes}</style>}\n {entries.map(([key, value]) => (\n <div\n key={key}\n style={\n reducedMotion\n ? undefined\n : { animation: 'fadeSlideIn 0.3s ease-out' }\n }\n data-testid=\"streaming-structure-entry\"\n >\n <dt className=\"font-medium text-text-primary text-sm\">{key}</dt>\n <dd className=\"text-text-secondary text-sm ml-0 mb-3\">\n {renderValue(value, reducedMotion)}\n </dd>\n </div>\n ))}\n {isStreaming && (\n <div data-testid=\"streaming-structure-loading\">\n <TextGlimmer lines={1} />\n </div>\n )}\n </dl>\n )\n}\n\nexport { StreamingStructure }\nexport type { StreamingStructureProps }\n"],"mappings":"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","names":["useEffect","useRef","twMerge","content","jsx","useRef","useEffect","twMerge","jsx","jsxs","WaveLoader","jsx","jsxs","WaveLoader","jsx","jsxs","jsx","jsxs","useEffect","useState","jsx","jsxs","twMerge","useReducedMotion","jsx","jsxs","twMerge","useReducedMotion","jsx","jsxs","twMerge","useReducedMotion","jsx","jsxs","useReducedMotion","twMerge","twMerge","useReducedMotion","jsx","jsxs","fadeSlideInKeyframes","useReducedMotion","twMerge"]}
1
+ {"version":3,"sources":["../../src/streaming/StreamingMessage/StreamingMessage.tsx","../../src/hooks/useCharacterDrain.ts","../../src/response/ResponseMessage/ResponseMessage.tsx","../../src/streaming/ThinkingIndicator/ThinkingIndicator.tsx","../../src/streaming/ToolExecution/ToolExecution.tsx","../../src/streaming/RetrievalProgress/RetrievalProgress.tsx","../../src/streaming/VerificationProgress/VerificationProgress.tsx","../../src/streaming/TypewriterText/TypewriterText.tsx","../../src/streaming/TypingIndicator/TypingIndicator.tsx","../../src/streaming/TextGlimmer/TextGlimmer.tsx","../../src/streaming/StreamingList/StreamingList.tsx","../../src/streaming/StreamingStructure/StreamingStructure.tsx"],"sourcesContent":["'use client'\n\nimport React, { useEffect, useRef } from 'react'\nimport { twMerge } from 'tailwind-merge'\nimport { WaveLoader } from '@surf-kit/core'\nimport type { StreamState } from '../../types/streaming'\nimport { useCharacterDrain } from '../../hooks/useCharacterDrain'\nimport { ResponseMessage } from '../../response/ResponseMessage'\n\ntype StreamingMessageProps = {\n stream: StreamState\n onComplete?: () => void\n onDraining?: (isDraining: boolean) => void\n showPhases?: boolean\n className?: string\n}\n\nconst phaseLabels: Record<StreamState['phase'], string> = {\n idle: '',\n waiting: 'Waiting...',\n thinking: 'Thinking...',\n retrieving: 'Searching...',\n generating: 'Writing...',\n verifying: 'Verifying...',\n}\n\n// Cursor styles: a catch-all targets any last-child element, with overrides\n// for ul/ol (target last li) and blockquote (target last p) so the cursor\n// appears inline with the final text rather than on a new line.\n// Uses steps(1) for a crisp blink that won't smooth-fade to invisible.\nconst CURSOR_STYLES = `\n.sk-streaming-cursor > :not(ul,ol,blockquote,div:has(table)):last-child::after,\n.sk-streaming-cursor > :is(ul,ol):last-child > li:last-child::after,\n.sk-streaming-cursor > blockquote:last-child > p:last-child::after,\n.sk-streaming-cursor > div:has(table):last-child table tbody tr:last-child td:last-child::after {\n content: \"\";\n display: inline-block;\n width: 2px;\n height: 1em;\n background: var(--color-accent, #38bdf8);\n animation: sk-cursor-blink 0.8s steps(1) infinite;\n margin-left: 2px;\n vertical-align: text-bottom;\n}\n@keyframes sk-cursor-blink {\n 0%, 60% { opacity: 1; }\n 61%, 100% { opacity: 0; }\n}\n`\n\nfunction StreamingMessage({\n stream,\n onComplete,\n onDraining,\n showPhases = true,\n className,\n}: StreamingMessageProps) {\n const onCompleteRef = useRef(onComplete)\n onCompleteRef.current = onComplete\n const onDrainingRef = useRef(onDraining)\n onDrainingRef.current = onDraining\n const wasActiveRef = useRef(stream.active)\n\n useEffect(() => {\n if (wasActiveRef.current && !stream.active) {\n onCompleteRef.current?.()\n }\n wasActiveRef.current = stream.active\n }, [stream.active])\n\n const phaseLabel = phaseLabels[stream.phase]\n const { displayed: rawDisplayed, isDraining } = useCharacterDrain(stream.content)\n // Trim trailing whitespace so ReactMarkdown doesn't create empty trailing\n // block elements that the cursor would land inside.\n const displayedContent = (stream.active || isDraining) ? rawDisplayed.trimEnd() : rawDisplayed\n\n // Notify parent of draining state changes\n useEffect(() => {\n onDrainingRef.current?.(isDraining)\n }, [isDraining])\n\n // Format agent label from stream.agent (e.g. \"coordinator_agent\" → \"coordinator\")\n const agentLabel = stream.agent\n ? stream.agent.replace('_agent', '').replace('_', ' ')\n : null\n\n // Show phase indicator only when there's no displayed content yet\n const showPhaseIndicator = showPhases && stream.active && stream.phase !== 'idle' && !displayedContent\n const showCursor = (stream.active || isDraining) && !!displayedContent\n\n return (\n <div className={twMerge('flex w-full flex-col items-start', className)} data-testid=\"streaming-message\">\n {/* Screen reader announcements */}\n <div aria-live=\"assertive\" className=\"sr-only\">\n {stream.active && stream.phase !== 'idle' && 'Response started'}\n {!stream.active && stream.content && 'Response complete'}\n </div>\n\n {showCursor && <style>{CURSOR_STYLES}</style>}\n\n {/* Agent label */}\n {agentLabel && (\n <div className=\"text-[11px] font-display font-semibold uppercase tracking-[0.08em] text-text-muted px-1 mb-1.5\">\n {agentLabel}\n </div>\n )}\n\n <div className=\"max-w-[88%] px-4 py-3 rounded-[18px] rounded-tl-[4px] bg-surface border border-border motion-safe:animate-springFromLeft\">\n {showPhaseIndicator && (\n <div\n className=\"flex items-center gap-2 text-sm text-text-secondary\"\n data-testid=\"phase-indicator\"\n >\n <span aria-hidden=\"true\">\n <WaveLoader size=\"sm\" color=\"#38bdf8\" />\n </span>\n <span>{phaseLabel}</span>\n </div>\n )}\n\n {displayedContent && (\n <ResponseMessage\n content={displayedContent}\n className={showCursor ? 'sk-streaming-cursor' : undefined}\n />\n )}\n </div>\n </div>\n )\n}\n\nexport { StreamingMessage }\nexport type { StreamingMessageProps }\n","'use client'\n\nimport { useState, useRef, useEffect } from 'react'\n\nexport interface CharacterDrainResult {\n displayed: string\n isDraining: boolean\n}\n\n/**\n * Smoothly drains a growing `target` string character-by-character using\n * `requestAnimationFrame`, decoupling visual rendering from network packet\n * timing so text appears to type out instead of arriving in chunks.\n *\n * When `target` resets to empty (e.g. stream finished), the hook continues\n * draining the previous content to completion before resetting, so the\n * typing animation isn't cut short.\n *\n * Design: the RAF loop is long-lived — it does NOT restart on every delta.\n * The tick function is stored in a ref (updated each render) so the loop\n * always reads the latest drainTarget without being cancelled/restarted.\n * A separate kick-start effect re-fires the loop when it was idle and new\n * content arrives.\n */\nexport function useCharacterDrain(target: string, msPerChar = 15): CharacterDrainResult {\n const [displayed, setDisplayed] = useState('')\n const indexRef = useRef(0)\n const lastTimeRef = useRef(0)\n const rafRef = useRef<number | null>(null)\n // Holds the last non-empty target so we can finish draining after source resets\n const drainTargetRef = useRef('')\n const msPerCharRef = useRef(msPerChar)\n\n msPerCharRef.current = msPerChar\n\n // Update drain target when new content arrives; preserve old value on reset\n if (target !== '') {\n drainTargetRef.current = target\n }\n\n const drainTarget = drainTargetRef.current\n const isDraining = displayed.length < drainTarget.length\n\n // Tick function stored in ref so the long-lived RAF loop always reads the\n // latest drainTarget and msPerChar without being cancelled/recreated.\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n const tickRef = useRef<(now: number) => void>(() => {})\n tickRef.current = (now: number) => {\n const currentTarget = drainTargetRef.current\n if (currentTarget === '') {\n rafRef.current = null\n return\n }\n\n if (lastTimeRef.current === 0) lastTimeRef.current = now\n const elapsed = now - lastTimeRef.current\n const charsToAdvance = Math.floor(elapsed / msPerCharRef.current)\n\n if (charsToAdvance > 0 && indexRef.current < currentTarget.length) {\n let nextIndex = Math.min(indexRef.current + charsToAdvance, currentTarget.length)\n // When the slice would end with whitespace, advance past it to include\n // the next visible character. This prevents trimEnd() in the streaming\n // UI from stripping trailing newlines and causing ReactMarkdown to\n // \"jump\" between block structures in a single frame (e.g. a heading\n // suddenly gaining a following paragraph with no transition).\n while (\n nextIndex < currentTarget.length &&\n currentTarget[nextIndex - 1].trim() === ''\n ) {\n nextIndex++\n }\n indexRef.current = nextIndex\n lastTimeRef.current = now\n setDisplayed(currentTarget.slice(0, nextIndex))\n }\n\n if (indexRef.current < currentTarget.length) {\n rafRef.current = requestAnimationFrame((t) => tickRef.current(t))\n } else {\n rafRef.current = null\n }\n }\n\n // Kick-start the RAF loop when new content arrives and the loop is idle.\n // No cleanup here — we intentionally do NOT cancel the running loop when\n // drainTarget grows; the long-lived tick will pick up new chars automatically.\n useEffect(() => {\n if (\n drainTargetRef.current !== '' &&\n indexRef.current < drainTargetRef.current.length &&\n rafRef.current === null\n ) {\n rafRef.current = requestAnimationFrame((t) => tickRef.current(t))\n }\n }, [drainTarget]) // drainTarget change = new content; check if loop needs kicking\n\n // Once drain completes and source stream is already done, reset all state\n useEffect(() => {\n if (target === '' && !isDraining && displayed !== '') {\n indexRef.current = 0\n lastTimeRef.current = 0\n drainTargetRef.current = ''\n setDisplayed('')\n }\n }, [target, isDraining, displayed])\n\n // Cancel any pending RAF on unmount\n useEffect(() => {\n return () => {\n if (rafRef.current !== null) {\n cancelAnimationFrame(rafRef.current)\n rafRef.current = null\n }\n }\n }, [])\n\n return { displayed, isDraining }\n}\n","import React from 'react'\nimport ReactMarkdown from 'react-markdown'\nimport rehypeSanitize from 'rehype-sanitize'\nimport remarkGfm from 'remark-gfm'\nimport { twMerge } from 'tailwind-merge'\n\ntype ResponseMessageProps = {\n content: string\n className?: string\n}\n\nfunction normalizeMarkdownLists(content: string) {\n // Some providers emit compact list markdown like \"Intro: - item 1 - item 2\".\n // Insert a paragraph break before an inline list marker so markdown parses it as a list.\n return content.replace(/:\\s+-\\s+/g, ':\\n\\n- ')\n}\n\nfunction ResponseMessage({ content, className }: ResponseMessageProps) {\n return (\n <div\n className={twMerge(\n 'text-sm leading-relaxed text-text-primary',\n '[&_p]:my-2',\n '[&_ul]:my-2 [&_ul]:list-disc [&_ul]:pl-6',\n '[&_ol]:my-2 [&_ol]:list-decimal [&_ol]:pl-6',\n '[&_li]:my-1',\n '[&_strong]:text-text-primary [&_strong]:font-semibold',\n '[&_em]:text-text-secondary',\n '[&_h1]:text-lg [&_h1]:font-semibold [&_h1]:text-text-primary [&_h1]:mt-4 [&_h1]:mb-2',\n '[&_h2]:text-base [&_h2]:font-semibold [&_h2]:text-text-primary [&_h2]:mt-3 [&_h2]:mb-1.5',\n '[&_h3]:text-sm [&_h3]:font-semibold [&_h3]:text-accent [&_h3]:mt-2 [&_h3]:mb-1',\n '[&_code]:bg-surface-raised [&_code]:text-accent [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:rounded [&_code]:text-xs [&_code]:font-mono',\n '[&_pre]:bg-surface-raised [&_pre]:border [&_pre]:border-border [&_pre]:rounded-xl [&_pre]:p-4 [&_pre]:overflow-x-auto',\n '[&_hr]:my-3 [&_hr]:border-border',\n '[&_blockquote]:border-l-2 [&_blockquote]:border-border-strong [&_blockquote]:pl-4 [&_blockquote]:text-text-secondary',\n '[&_table]:w-full [&_table]:text-sm [&_table]:border-collapse [&_table]:my-2',\n '[&_thead]:border-b [&_thead]:border-border',\n '[&_th]:text-left [&_th]:px-2 [&_th]:py-1.5 [&_th]:font-semibold',\n '[&_td]:px-2 [&_td]:py-1.5 [&_td]:border-t [&_td]:border-border/50',\n '[&_a]:text-accent [&_a]:underline-offset-2 [&_a]:hover:text-accent/80',\n className,\n )}\n data-testid=\"response-message\"\n >\n <ReactMarkdown\n remarkPlugins={[remarkGfm]}\n rehypePlugins={[rehypeSanitize]}\n components={{\n script: () => null,\n iframe: () => null,\n p: ({ children }) => <p className=\"my-2\">{children}</p>,\n ul: ({ children }) => <ul className=\"my-2 list-disc pl-6\">{children}</ul>,\n ol: ({ children }) => <ol className=\"my-2 list-decimal pl-6\">{children}</ol>,\n li: ({ children, ...props }) => {\n let content = children\n // Strip leading \"N.\" or \"N)\" text that duplicates the ol counter\n if ((props as Record<string, unknown>).ordered) {\n content = React.Children.map(children, (child, i) => {\n if (i === 0 && typeof child === 'string') {\n return child.replace(/^\\d+[.)]\\s*/, '')\n }\n return child\n })\n }\n return <li className=\"my-1\">{content}</li>\n },\n strong: ({ children }) => <strong className=\"font-semibold\">{children}</strong>,\n em: ({ children }) => <em className=\"italic text-text-secondary\">{children}</em>,\n h1: ({ children }) => <h1 className=\"text-base font-bold mt-4 mb-2\">{children}</h1>,\n h2: ({ children }) => <h2 className=\"text-sm font-bold mt-3 mb-1\">{children}</h2>,\n h3: ({ children }) => <h3 className=\"text-sm font-semibold mt-2 mb-1\">{children}</h3>,\n hr: () => <hr className=\"my-3 border-border\" />,\n code: ({ children }) => <code className=\"bg-surface-sunken rounded px-1 py-0.5 text-xs font-mono\">{children}</code>,\n table: ({ children }) => (\n <div className=\"overflow-x-auto my-2\">\n <table className=\"w-full text-sm border-collapse\">{children}</table>\n </div>\n ),\n thead: ({ children }) => <thead className=\"border-b border-border\">{children}</thead>,\n th: ({ children }) => <th className=\"text-left px-2 py-1.5 font-semibold\">{children}</th>,\n td: ({ children }) => <td className=\"px-2 py-1.5 border-t border-border/50\">{children}</td>,\n }}\n >\n {normalizeMarkdownLists(content)}\n </ReactMarkdown>\n </div>\n )\n}\n\nexport { ResponseMessage }\nexport type { ResponseMessageProps }\n","import React from 'react'\nimport { useReducedMotion } from '@surf-kit/hooks'\n\ntype ThinkingIndicatorProps = {\n label?: string\n className?: string\n}\n\nfunction ThinkingIndicator({ label = 'Thinking...', className }: ThinkingIndicatorProps) {\n const reducedMotion = useReducedMotion()\n\n return (\n <span\n role=\"status\"\n className={`inline-flex items-center gap-2 text-sm motion-safe:animate-fadeSlideUpSm ${className ?? ''}`}\n data-testid=\"thinking-indicator\"\n >\n <span className=\"text-text-secondary\">{label}</span>\n {!reducedMotion && (\n <span className=\"flex gap-1\" aria-hidden=\"true\" data-testid=\"animated-dots\">\n <span className=\"w-1.5 h-1.5 rounded-full bg-current animate-bounce [animation-delay:0ms]\" />\n <span className=\"w-1.5 h-1.5 rounded-full bg-current animate-bounce [animation-delay:150ms]\" />\n <span className=\"w-1.5 h-1.5 rounded-full bg-current animate-bounce [animation-delay:300ms]\" />\n </span>\n )}\n </span>\n )\n}\n\nexport { ThinkingIndicator }\nexport type { ThinkingIndicatorProps }\n","import React from 'react'\nimport { WaveLoader } from '@surf-kit/core'\n\ntype ToolExecutionProps = {\n tool: string\n label?: string\n className?: string\n}\n\nconst defaultLabels: Record<string, string> = {\n search: 'Searching knowledge base...',\n retrieve: 'Retrieving documents...',\n calculate: 'Calculating...',\n}\n\nfunction ToolExecution({ tool, label, className }: ToolExecutionProps) {\n const displayLabel = label ?? defaultLabels[tool] ?? `Running ${tool}...`\n\n return (\n <div\n className={`flex items-center gap-2 text-sm text-text-secondary ${className ?? ''}`}\n role=\"status\"\n data-testid=\"tool-execution\"\n >\n <span aria-hidden=\"true\">\n <WaveLoader size=\"sm\" color=\"#38bdf8\" />\n </span>\n <span>{displayLabel}</span>\n </div>\n )\n}\n\nexport { ToolExecution }\nexport type { ToolExecutionProps }\n","import React from 'react'\nimport { WaveLoader } from '@surf-kit/core'\nimport type { Source } from '../../types/agent'\n\ntype RetrievalProgressProps = {\n sources: Source[]\n isActive: boolean\n className?: string\n}\n\nfunction RetrievalProgress({ sources, isActive, className }: RetrievalProgressProps) {\n return (\n <div\n className={`space-y-2 ${className ?? ''}`}\n role=\"status\"\n aria-label={\n isActive\n ? `Retrieving sources, ${sources.length} found so far`\n : `${sources.length} sources found`\n }\n data-testid=\"retrieval-progress\"\n >\n {isActive && (\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n <span aria-hidden=\"true\">\n <WaveLoader size=\"sm\" color=\"#38bdf8\" />\n </span>\n <span>Retrieving sources...</span>\n </div>\n )}\n\n {sources.length > 0 && (\n <ul className=\"space-y-1\" data-testid=\"source-list\">\n {sources.map((source, index) => (\n <li\n key={source.document_id}\n className=\"text-sm text-text-secondary flex items-center gap-2 animate-in fade-in slide-in-from-left-2\"\n style={{ animationDelay: `${index * 100}ms`, animationFillMode: 'both' }}\n data-testid=\"retrieval-source-item\"\n >\n <span className=\"w-1.5 h-1.5 rounded-full bg-accent flex-shrink-0\" aria-hidden=\"true\" />\n <span className=\"truncate\">{source.title}</span>\n </li>\n ))}\n </ul>\n )}\n </div>\n )\n}\n\nexport { RetrievalProgress }\nexport type { RetrievalProgressProps }\n","import React from 'react'\n\ntype VerificationProgressProps = {\n isActive: boolean\n label?: string\n className?: string\n}\n\nfunction VerificationProgress({\n isActive,\n label = 'Checking accuracy...',\n className,\n}: VerificationProgressProps) {\n if (!isActive) return null\n\n return (\n <div\n className={`flex items-center gap-2 text-sm ${className ?? ''}`}\n role=\"status\"\n data-testid=\"verification-progress\"\n >\n <svg\n className=\"w-4 h-4 animate-spin text-accent\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n >\n <circle className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\" />\n <path className=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z\" />\n </svg>\n <span className=\"text-accent animate-pulse\">{label}</span>\n </div>\n )\n}\n\nexport { VerificationProgress }\nexport type { VerificationProgressProps }\n","'use client'\n\nimport React, { useEffect, useState } from 'react'\n\nexport type TypewriterTextProps = {\n text: string\n speed?: number // ms per char, default 30\n delay?: number // ms to wait before starting, default 0\n onComplete?: () => void\n className?: string\n showCursor?: boolean // default true\n}\n\nexport function TypewriterText({\n text,\n speed = 30,\n delay = 0,\n onComplete,\n className = '',\n showCursor = true,\n}: TypewriterTextProps) {\n const [displayedText, setDisplayedText] = useState('')\n const [isComplete, setIsComplete] = useState(false)\n\n useEffect(() => {\n setDisplayedText('')\n setIsComplete(false)\n let index = 0\n let interval: ReturnType<typeof setInterval>\n const timeout = setTimeout(() => {\n interval = setInterval(() => {\n if (index < text.length) {\n setDisplayedText(text.slice(0, index + 1))\n index++\n } else {\n clearInterval(interval)\n setIsComplete(true)\n onComplete?.()\n }\n }, speed)\n }, delay)\n return () => {\n clearTimeout(timeout)\n clearInterval(interval)\n }\n }, [text, speed, delay, onComplete])\n\n return (\n <span className={className}>\n {displayedText}\n {showCursor && !isComplete && (\n <span className=\"typewriter-cursor\" aria-hidden=\"true\" />\n )}\n </span>\n )\n}\n","import React from 'react'\nimport { twMerge } from 'tailwind-merge'\nimport { useReducedMotion } from '@surf-kit/hooks'\n\ntype TypingIndicatorProps = {\n label?: string\n dotCount?: number\n className?: string\n}\n\nconst bounceKeyframes = `\n@keyframes typing-bounce {\n 0%, 80%, 100% { transform: translateY(0); }\n 40% { transform: translateY(-6px); }\n}\n`\n\nfunction TypingIndicator({\n label,\n dotCount = 3,\n className,\n}: TypingIndicatorProps) {\n const reducedMotion = useReducedMotion()\n\n return (\n <span\n role=\"status\"\n aria-label={label ?? 'typing'}\n className={twMerge('inline-flex items-center gap-2', className)}\n data-testid=\"typing-indicator\"\n >\n {!reducedMotion && <style>{bounceKeyframes}</style>}\n {label && (\n <span className=\"text-sm text-text-secondary\">{label}</span>\n )}\n <span className=\"flex gap-1\" data-testid=\"typing-dots\">\n {Array.from({ length: dotCount }, (_, i) => (\n <span\n key={i}\n className=\"w-2 h-2 rounded-full bg-text-secondary\"\n style={\n reducedMotion\n ? undefined\n : {\n animation: 'typing-bounce 1.4s infinite ease-in-out',\n animationDelay: `${i * 160}ms`,\n }\n }\n />\n ))}\n </span>\n </span>\n )\n}\n\nexport { TypingIndicator }\nexport type { TypingIndicatorProps }\n","import React from 'react'\nimport { twMerge } from 'tailwind-merge'\nimport { useReducedMotion } from '@surf-kit/hooks'\n\ntype TextGlimmerProps = {\n lines?: number\n className?: string\n}\n\nconst shimmerKeyframes = `\n@keyframes text-shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n}\n`\n\nconst widthPattern = ['100%', '90%', '60%']\n\nfunction TextGlimmer({ lines = 3, className }: TextGlimmerProps) {\n const reducedMotion = useReducedMotion()\n\n return (\n <div\n role=\"status\"\n aria-label=\"Loading\"\n className={twMerge('flex flex-col gap-2', className)}\n data-testid=\"text-glimmer\"\n >\n {!reducedMotion && <style>{shimmerKeyframes}</style>}\n {Array.from({ length: lines }, (_, i) => (\n <div\n key={i}\n className=\"h-3 rounded bg-surface-raised\"\n style={{\n width: widthPattern[i % widthPattern.length],\n ...(reducedMotion\n ? undefined\n : {\n backgroundImage:\n 'linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent)',\n backgroundSize: '200% 100%',\n animation: 'text-shimmer 1.5s infinite ease-in-out',\n }),\n }}\n data-testid=\"glimmer-line\"\n />\n ))}\n </div>\n )\n}\n\nexport { TextGlimmer }\nexport type { TextGlimmerProps }\n","import React from 'react'\nimport { twMerge } from 'tailwind-merge'\nimport { useReducedMotion } from '@surf-kit/hooks'\nimport { TypingIndicator } from '../TypingIndicator'\n\ntype StreamingListProps<T> = {\n items: T[]\n renderItem: (item: T, index: number) => React.ReactNode\n isStreaming?: boolean\n className?: string\n emptyMessage?: string\n}\n\nconst fadeSlideInKeyframes = `\n@keyframes fadeSlideIn {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n`\n\nfunction StreamingList<T>({\n items,\n renderItem,\n isStreaming = false,\n className,\n emptyMessage,\n}: StreamingListProps<T>) {\n const reducedMotion = useReducedMotion()\n\n if (items.length === 0 && !isStreaming) {\n return emptyMessage ? (\n <p className={twMerge('text-sm text-text-secondary', className)} data-testid=\"streaming-list-empty\">\n {emptyMessage}\n </p>\n ) : null\n }\n\n return (\n <ul\n aria-live=\"polite\"\n className={twMerge('list-none p-0 m-0', className)}\n data-testid=\"streaming-list\"\n >\n {!reducedMotion && <style>{fadeSlideInKeyframes}</style>}\n {items.map((item, index) => (\n <li\n key={index}\n style={\n reducedMotion\n ? undefined\n : { animation: 'fadeSlideIn 0.3s ease-out' }\n }\n data-testid=\"streaming-list-item\"\n >\n {renderItem(item, index)}\n </li>\n ))}\n {isStreaming && (\n <li data-testid=\"streaming-list-loading\">\n <TypingIndicator />\n </li>\n )}\n </ul>\n )\n}\n\nexport { StreamingList }\nexport type { StreamingListProps }\n","import React from 'react'\nimport { twMerge } from 'tailwind-merge'\nimport { useReducedMotion } from '@surf-kit/hooks'\nimport { TextGlimmer } from '../TextGlimmer'\n\ntype StreamingStructureProps = {\n data: Record<string, unknown>\n isStreaming?: boolean\n className?: string\n}\n\nconst fadeSlideInKeyframes = `\n@keyframes fadeSlideIn {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n`\n\nfunction renderValue(value: unknown, reducedMotion: boolean): React.ReactNode {\n if (value === null) {\n return <span className=\"italic text-text-secondary\">null</span>\n }\n if (value === undefined) {\n return <span className=\"italic text-text-secondary\">undefined</span>\n }\n if (Array.isArray(value)) {\n return (\n <ol className=\"list-decimal pl-4 m-0\">\n {value.map((item, i) => (\n <li key={i} className=\"text-text-secondary text-sm\">\n {renderValue(item, reducedMotion)}\n </li>\n ))}\n </ol>\n )\n }\n if (typeof value === 'object') {\n return renderNestedDl(value as Record<string, unknown>, reducedMotion)\n }\n return String(value)\n}\n\nfunction renderNestedDl(\n data: Record<string, unknown>,\n reducedMotion: boolean,\n): React.ReactNode {\n const entries = Object.entries(data)\n return (\n <dl className=\"pl-4 m-0\" data-testid=\"streaming-structure-nested\">\n {entries.map(([key, value]) => (\n <div\n key={key}\n style={\n reducedMotion\n ? undefined\n : { animation: 'fadeSlideIn 0.3s ease-out' }\n }\n >\n <dt className=\"font-medium text-text-primary text-sm\">{key}</dt>\n <dd className=\"text-text-secondary text-sm ml-0 mb-3\">\n {renderValue(value, reducedMotion)}\n </dd>\n </div>\n ))}\n </dl>\n )\n}\n\nfunction StreamingStructure({\n data,\n isStreaming = false,\n className,\n}: StreamingStructureProps) {\n const reducedMotion = useReducedMotion()\n const entries = Object.entries(data)\n\n return (\n <dl\n aria-live=\"polite\"\n className={twMerge('m-0', className)}\n data-testid=\"streaming-structure\"\n >\n {!reducedMotion && <style>{fadeSlideInKeyframes}</style>}\n {entries.map(([key, value]) => (\n <div\n key={key}\n style={\n reducedMotion\n ? undefined\n : { animation: 'fadeSlideIn 0.3s ease-out' }\n }\n data-testid=\"streaming-structure-entry\"\n >\n <dt className=\"font-medium text-text-primary text-sm\">{key}</dt>\n <dd className=\"text-text-secondary text-sm ml-0 mb-3\">\n {renderValue(value, reducedMotion)}\n </dd>\n </div>\n ))}\n {isStreaming && (\n <div data-testid=\"streaming-structure-loading\">\n <TextGlimmer lines={1} />\n </div>\n )}\n </dl>\n )\n}\n\nexport { StreamingStructure }\nexport type { StreamingStructureProps }\n"],"mappings":";;;AAEA,SAAgB,aAAAA,YAAW,UAAAC,eAAc;AACzC,SAAS,WAAAC,gBAAe;AACxB,SAAS,kBAAkB;;;ACF3B,SAAS,UAAU,QAAQ,iBAAiB;AAsBrC,SAAS,kBAAkB,QAAgB,YAAY,IAA0B;AACtF,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,EAAE;AAC7C,QAAM,WAAW,OAAO,CAAC;AACzB,QAAM,cAAc,OAAO,CAAC;AAC5B,QAAM,SAAS,OAAsB,IAAI;AAEzC,QAAM,iBAAiB,OAAO,EAAE;AAChC,QAAM,eAAe,OAAO,SAAS;AAErC,eAAa,UAAU;AAGvB,MAAI,WAAW,IAAI;AACjB,mBAAe,UAAU;AAAA,EAC3B;AAEA,QAAM,cAAc,eAAe;AACnC,QAAM,aAAa,UAAU,SAAS,YAAY;AAKlD,QAAM,UAAU,OAA8B,MAAM;AAAA,EAAC,CAAC;AACtD,UAAQ,UAAU,CAAC,QAAgB;AACjC,UAAM,gBAAgB,eAAe;AACrC,QAAI,kBAAkB,IAAI;AACxB,aAAO,UAAU;AACjB;AAAA,IACF;AAEA,QAAI,YAAY,YAAY,EAAG,aAAY,UAAU;AACrD,UAAM,UAAU,MAAM,YAAY;AAClC,UAAM,iBAAiB,KAAK,MAAM,UAAU,aAAa,OAAO;AAEhE,QAAI,iBAAiB,KAAK,SAAS,UAAU,cAAc,QAAQ;AACjE,UAAI,YAAY,KAAK,IAAI,SAAS,UAAU,gBAAgB,cAAc,MAAM;AAMhF,aACE,YAAY,cAAc,UAC1B,cAAc,YAAY,CAAC,EAAE,KAAK,MAAM,IACxC;AACA;AAAA,MACF;AACA,eAAS,UAAU;AACnB,kBAAY,UAAU;AACtB,mBAAa,cAAc,MAAM,GAAG,SAAS,CAAC;AAAA,IAChD;AAEA,QAAI,SAAS,UAAU,cAAc,QAAQ;AAC3C,aAAO,UAAU,sBAAsB,CAAC,MAAM,QAAQ,QAAQ,CAAC,CAAC;AAAA,IAClE,OAAO;AACL,aAAO,UAAU;AAAA,IACnB;AAAA,EACF;AAKA,YAAU,MAAM;AACd,QACE,eAAe,YAAY,MAC3B,SAAS,UAAU,eAAe,QAAQ,UAC1C,OAAO,YAAY,MACnB;AACA,aAAO,UAAU,sBAAsB,CAAC,MAAM,QAAQ,QAAQ,CAAC,CAAC;AAAA,IAClE;AAAA,EACF,GAAG,CAAC,WAAW,CAAC;AAGhB,YAAU,MAAM;AACd,QAAI,WAAW,MAAM,CAAC,cAAc,cAAc,IAAI;AACpD,eAAS,UAAU;AACnB,kBAAY,UAAU;AACtB,qBAAe,UAAU;AACzB,mBAAa,EAAE;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,QAAQ,YAAY,SAAS,CAAC;AAGlC,YAAU,MAAM;AACd,WAAO,MAAM;AACX,UAAI,OAAO,YAAY,MAAM;AAC3B,6BAAqB,OAAO,OAAO;AACnC,eAAO,UAAU;AAAA,MACnB;AAAA,IACF;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SAAO,EAAE,WAAW,WAAW;AACjC;;;ACrHA,OAAO,WAAW;AAClB,OAAO,mBAAmB;AAC1B,OAAO,oBAAoB;AAC3B,OAAO,eAAe;AACtB,SAAS,eAAe;AA8CO;AAvC/B,SAAS,uBAAuB,SAAiB;AAG/C,SAAO,QAAQ,QAAQ,aAAa,SAAS;AAC/C;AAEA,SAAS,gBAAgB,EAAE,SAAS,UAAU,GAAyB;AACrE,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACA,eAAY;AAAA,MAEZ;AAAA,QAAC;AAAA;AAAA,UACC,eAAe,CAAC,SAAS;AAAA,UACzB,eAAe,CAAC,cAAc;AAAA,UAC9B,YAAY;AAAA,YACV,QAAQ,MAAM;AAAA,YACd,QAAQ,MAAM;AAAA,YACd,GAAG,CAAC,EAAE,SAAS,MAAM,oBAAC,OAAE,WAAU,QAAQ,UAAS;AAAA,YACnD,IAAI,CAAC,EAAE,SAAS,MAAM,oBAAC,QAAG,WAAU,uBAAuB,UAAS;AAAA,YACpE,IAAI,CAAC,EAAE,SAAS,MAAM,oBAAC,QAAG,WAAU,0BAA0B,UAAS;AAAA,YACvE,IAAI,CAAC,EAAE,UAAU,GAAG,MAAM,MAAM;AAC9B,kBAAIC,WAAU;AAEd,kBAAK,MAAkC,SAAS;AAC9C,gBAAAA,WAAU,MAAM,SAAS,IAAI,UAAU,CAAC,OAAO,MAAM;AACnD,sBAAI,MAAM,KAAK,OAAO,UAAU,UAAU;AACxC,2BAAO,MAAM,QAAQ,eAAe,EAAE;AAAA,kBACxC;AACA,yBAAO;AAAA,gBACT,CAAC;AAAA,cACH;AACA,qBAAO,oBAAC,QAAG,WAAU,QAAQ,UAAAA,UAAQ;AAAA,YACvC;AAAA,YACA,QAAQ,CAAC,EAAE,SAAS,MAAM,oBAAC,YAAO,WAAU,iBAAiB,UAAS;AAAA,YACtE,IAAI,CAAC,EAAE,SAAS,MAAM,oBAAC,QAAG,WAAU,8BAA8B,UAAS;AAAA,YAC3E,IAAI,CAAC,EAAE,SAAS,MAAM,oBAAC,QAAG,WAAU,iCAAiC,UAAS;AAAA,YAC9E,IAAI,CAAC,EAAE,SAAS,MAAM,oBAAC,QAAG,WAAU,+BAA+B,UAAS;AAAA,YAC5E,IAAI,CAAC,EAAE,SAAS,MAAM,oBAAC,QAAG,WAAU,mCAAmC,UAAS;AAAA,YAChF,IAAI,MAAM,oBAAC,QAAG,WAAU,sBAAqB;AAAA,YAC7C,MAAM,CAAC,EAAE,SAAS,MAAM,oBAAC,UAAK,WAAU,2DAA2D,UAAS;AAAA,YAC5G,OAAO,CAAC,EAAE,SAAS,MACjB,oBAAC,SAAI,WAAU,wBACb,8BAAC,WAAM,WAAU,kCAAkC,UAAS,GAC9D;AAAA,YAEF,OAAO,CAAC,EAAE,SAAS,MAAM,oBAAC,WAAM,WAAU,0BAA0B,UAAS;AAAA,YAC7E,IAAI,CAAC,EAAE,SAAS,MAAM,oBAAC,QAAG,WAAU,uCAAuC,UAAS;AAAA,YACpF,IAAI,CAAC,EAAE,SAAS,MAAM,oBAAC,QAAG,WAAU,yCAAyC,UAAS;AAAA,UACxF;AAAA,UAEC,iCAAuB,OAAO;AAAA;AAAA,MACjC;AAAA;AAAA,EACF;AAEJ;;;AFMM,SAKe,OAAAC,MALf;AA5EN,IAAM,cAAoD;AAAA,EACxD,MAAM;AAAA,EACN,SAAS;AAAA,EACT,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,WAAW;AACb;AAMA,IAAM,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoBtB,SAAS,iBAAiB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb;AACF,GAA0B;AACxB,QAAM,gBAAgBC,QAAO,UAAU;AACvC,gBAAc,UAAU;AACxB,QAAM,gBAAgBA,QAAO,UAAU;AACvC,gBAAc,UAAU;AACxB,QAAM,eAAeA,QAAO,OAAO,MAAM;AAEzC,EAAAC,WAAU,MAAM;AACd,QAAI,aAAa,WAAW,CAAC,OAAO,QAAQ;AAC1C,oBAAc,UAAU;AAAA,IAC1B;AACA,iBAAa,UAAU,OAAO;AAAA,EAChC,GAAG,CAAC,OAAO,MAAM,CAAC;AAElB,QAAM,aAAa,YAAY,OAAO,KAAK;AAC3C,QAAM,EAAE,WAAW,cAAc,WAAW,IAAI,kBAAkB,OAAO,OAAO;AAGhF,QAAM,mBAAoB,OAAO,UAAU,aAAc,aAAa,QAAQ,IAAI;AAGlF,EAAAA,WAAU,MAAM;AACd,kBAAc,UAAU,UAAU;AAAA,EACpC,GAAG,CAAC,UAAU,CAAC;AAGf,QAAM,aAAa,OAAO,QACtB,OAAO,MAAM,QAAQ,UAAU,EAAE,EAAE,QAAQ,KAAK,GAAG,IACnD;AAGJ,QAAM,qBAAqB,cAAc,OAAO,UAAU,OAAO,UAAU,UAAU,CAAC;AACtF,QAAM,cAAc,OAAO,UAAU,eAAe,CAAC,CAAC;AAEtD,SACE,qBAAC,SAAI,WAAWC,SAAQ,oCAAoC,SAAS,GAAG,eAAY,qBAElF;AAAA,yBAAC,SAAI,aAAU,aAAY,WAAU,WAClC;AAAA,aAAO,UAAU,OAAO,UAAU,UAAU;AAAA,MAC5C,CAAC,OAAO,UAAU,OAAO,WAAW;AAAA,OACvC;AAAA,IAEC,cAAc,gBAAAH,KAAC,WAAO,yBAAc;AAAA,IAGpC,cACC,gBAAAA,KAAC,SAAI,WAAU,kGACZ,sBACH;AAAA,IAGF,qBAAC,SAAI,WAAU,4HACZ;AAAA,4BACC;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,eAAY;AAAA,UAEZ;AAAA,4BAAAA,KAAC,UAAK,eAAY,QAChB,0BAAAA,KAAC,cAAW,MAAK,MAAK,OAAM,WAAU,GACxC;AAAA,YACA,gBAAAA,KAAC,UAAM,sBAAW;AAAA;AAAA;AAAA,MACpB;AAAA,MAGD,oBACC,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,WAAW,aAAa,wBAAwB;AAAA;AAAA,MAClD;AAAA,OAEJ;AAAA,KACF;AAEJ;;;AGhIA,SAAS,wBAAwB;AAgB3B,gBAAAI,MAEE,QAAAC,aAFF;AATN,SAAS,kBAAkB,EAAE,QAAQ,eAAe,UAAU,GAA2B;AACvF,QAAM,gBAAgB,iBAAiB;AAEvC,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,WAAW,4EAA4E,aAAa,EAAE;AAAA,MACtG,eAAY;AAAA,MAEZ;AAAA,wBAAAD,KAAC,UAAK,WAAU,uBAAuB,iBAAM;AAAA,QAC5C,CAAC,iBACA,gBAAAC,MAAC,UAAK,WAAU,cAAa,eAAY,QAAO,eAAY,iBAC1D;AAAA,0BAAAD,KAAC,UAAK,WAAU,4EAA2E;AAAA,UAC3F,gBAAAA,KAAC,UAAK,WAAU,8EAA6E;AAAA,UAC7F,gBAAAA,KAAC,UAAK,WAAU,8EAA6E;AAAA,WAC/F;AAAA;AAAA;AAAA,EAEJ;AAEJ;;;AC1BA,SAAS,cAAAE,mBAAkB;AAkBvB,SAMI,OAAAC,MANJ,QAAAC,aAAA;AAVJ,IAAM,gBAAwC;AAAA,EAC5C,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,WAAW;AACb;AAEA,SAAS,cAAc,EAAE,MAAM,OAAO,UAAU,GAAuB;AACrE,QAAM,eAAe,SAAS,cAAc,IAAI,KAAK,WAAW,IAAI;AAEpE,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,uDAAuD,aAAa,EAAE;AAAA,MACjF,MAAK;AAAA,MACL,eAAY;AAAA,MAEZ;AAAA,wBAAAD,KAAC,UAAK,eAAY,QAChB,0BAAAA,KAACD,aAAA,EAAW,MAAK,MAAK,OAAM,WAAU,GACxC;AAAA,QACA,gBAAAC,KAAC,UAAM,wBAAa;AAAA;AAAA;AAAA,EACtB;AAEJ;;;AC7BA,SAAS,cAAAE,mBAAkB;AAsBnB,SAEI,OAAAC,MAFJ,QAAAC,aAAA;AAbR,SAAS,kBAAkB,EAAE,SAAS,UAAU,UAAU,GAA2B;AACnF,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,aAAa,aAAa,EAAE;AAAA,MACvC,MAAK;AAAA,MACL,cACE,WACI,uBAAuB,QAAQ,MAAM,kBACrC,GAAG,QAAQ,MAAM;AAAA,MAEvB,eAAY;AAAA,MAEX;AAAA,oBACC,gBAAAA,MAAC,SAAI,WAAU,uDACb;AAAA,0BAAAD,KAAC,UAAK,eAAY,QAChB,0BAAAA,KAACD,aAAA,EAAW,MAAK,MAAK,OAAM,WAAU,GACxC;AAAA,UACA,gBAAAC,KAAC,UAAK,mCAAqB;AAAA,WAC7B;AAAA,QAGD,QAAQ,SAAS,KAChB,gBAAAA,KAAC,QAAG,WAAU,aAAY,eAAY,eACnC,kBAAQ,IAAI,CAAC,QAAQ,UACpB,gBAAAC;AAAA,UAAC;AAAA;AAAA,YAEC,WAAU;AAAA,YACV,OAAO,EAAE,gBAAgB,GAAG,QAAQ,GAAG,MAAM,mBAAmB,OAAO;AAAA,YACvE,eAAY;AAAA,YAEZ;AAAA,8BAAAD,KAAC,UAAK,WAAU,oDAAmD,eAAY,QAAO;AAAA,cACtF,gBAAAA,KAAC,UAAK,WAAU,YAAY,iBAAO,OAAM;AAAA;AAAA;AAAA,UANpC,OAAO;AAAA,QAOd,CACD,GACH;AAAA;AAAA;AAAA,EAEJ;AAEJ;;;AC3BM,SAOE,OAAAE,MAPF,QAAAC,aAAA;AAbN,SAAS,qBAAqB;AAAA,EAC5B;AAAA,EACA,QAAQ;AAAA,EACR;AACF,GAA8B;AAC5B,MAAI,CAAC,SAAU,QAAO;AAEtB,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,mCAAmC,aAAa,EAAE;AAAA,MAC7D,MAAK;AAAA,MACL,eAAY;AAAA,MAEZ;AAAA,wBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,OAAM;AAAA,YACN,MAAK;AAAA,YACL,SAAQ;AAAA,YACR,eAAY;AAAA,YAEZ;AAAA,8BAAAD,KAAC,YAAO,WAAU,cAAa,IAAG,MAAK,IAAG,MAAK,GAAE,MAAK,QAAO,gBAAe,aAAY,KAAI;AAAA,cAC5F,gBAAAA,KAAC,UAAK,WAAU,cAAa,MAAK,gBAAe,GAAE,+CAA8C;AAAA;AAAA;AAAA,QACnG;AAAA,QACA,gBAAAA,KAAC,UAAK,WAAU,6BAA6B,iBAAM;AAAA;AAAA;AAAA,EACrD;AAEJ;;;AChCA,SAAgB,aAAAE,YAAW,YAAAC,iBAAgB;AA8CvC,SAGI,OAAAC,MAHJ,QAAAC,aAAA;AAnCG,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR;AAAA,EACA,YAAY;AAAA,EACZ,aAAa;AACf,GAAwB;AACtB,QAAM,CAAC,eAAe,gBAAgB,IAAIF,UAAS,EAAE;AACrD,QAAM,CAAC,YAAY,aAAa,IAAIA,UAAS,KAAK;AAElD,EAAAD,WAAU,MAAM;AACd,qBAAiB,EAAE;AACnB,kBAAc,KAAK;AACnB,QAAI,QAAQ;AACZ,QAAI;AACJ,UAAM,UAAU,WAAW,MAAM;AAC/B,iBAAW,YAAY,MAAM;AAC3B,YAAI,QAAQ,KAAK,QAAQ;AACvB,2BAAiB,KAAK,MAAM,GAAG,QAAQ,CAAC,CAAC;AACzC;AAAA,QACF,OAAO;AACL,wBAAc,QAAQ;AACtB,wBAAc,IAAI;AAClB,uBAAa;AAAA,QACf;AAAA,MACF,GAAG,KAAK;AAAA,IACV,GAAG,KAAK;AACR,WAAO,MAAM;AACX,mBAAa,OAAO;AACpB,oBAAc,QAAQ;AAAA,IACxB;AAAA,EACF,GAAG,CAAC,MAAM,OAAO,OAAO,UAAU,CAAC;AAEnC,SACE,gBAAAG,MAAC,UAAK,WACH;AAAA;AAAA,IACA,cAAc,CAAC,cACd,gBAAAD,KAAC,UAAK,WAAU,qBAAoB,eAAY,QAAO;AAAA,KAE3D;AAEJ;;;ACtDA,SAAS,WAAAE,gBAAe;AACxB,SAAS,oBAAAC,yBAAwB;AAuB7B,SAMqB,OAAAC,MANrB,QAAAC,aAAA;AAfJ,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAOxB,SAAS,gBAAgB;AAAA,EACvB;AAAA,EACA,WAAW;AAAA,EACX;AACF,GAAyB;AACvB,QAAM,gBAAgBF,kBAAiB;AAEvC,SACE,gBAAAE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAY,SAAS;AAAA,MACrB,WAAWH,SAAQ,kCAAkC,SAAS;AAAA,MAC9D,eAAY;AAAA,MAEX;AAAA,SAAC,iBAAiB,gBAAAE,KAAC,WAAO,2BAAgB;AAAA,QAC1C,SACC,gBAAAA,KAAC,UAAK,WAAU,+BAA+B,iBAAM;AAAA,QAEvD,gBAAAA,KAAC,UAAK,WAAU,cAAa,eAAY,eACtC,gBAAM,KAAK,EAAE,QAAQ,SAAS,GAAG,CAAC,GAAG,MACpC,gBAAAA;AAAA,UAAC;AAAA;AAAA,YAEC,WAAU;AAAA,YACV,OACE,gBACI,SACA;AAAA,cACE,WAAW;AAAA,cACX,gBAAgB,GAAG,IAAI,GAAG;AAAA,YAC5B;AAAA;AAAA,UARD;AAAA,QAUP,CACD,GACH;AAAA;AAAA;AAAA,EACF;AAEJ;;;ACpDA,SAAS,WAAAE,gBAAe;AACxB,SAAS,oBAAAC,yBAAwB;AAoB7B,SAMqB,OAAAC,MANrB,QAAAC,aAAA;AAbJ,IAAM,mBAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAOzB,IAAM,eAAe,CAAC,QAAQ,OAAO,KAAK;AAE1C,SAAS,YAAY,EAAE,QAAQ,GAAG,UAAU,GAAqB;AAC/D,QAAM,gBAAgBF,kBAAiB;AAEvC,SACE,gBAAAE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAW;AAAA,MACX,WAAWH,SAAQ,uBAAuB,SAAS;AAAA,MACnD,eAAY;AAAA,MAEX;AAAA,SAAC,iBAAiB,gBAAAE,KAAC,WAAO,4BAAiB;AAAA,QAC3C,MAAM,KAAK,EAAE,QAAQ,MAAM,GAAG,CAAC,GAAG,MACjC,gBAAAA;AAAA,UAAC;AAAA;AAAA,YAEC,WAAU;AAAA,YACV,OAAO;AAAA,cACL,OAAO,aAAa,IAAI,aAAa,MAAM;AAAA,cAC3C,GAAI,gBACA,SACA;AAAA,gBACE,iBACE;AAAA,gBACF,gBAAgB;AAAA,gBAChB,WAAW;AAAA,cACb;AAAA,YACN;AAAA,YACA,eAAY;AAAA;AAAA,UAbP;AAAA,QAcP,CACD;AAAA;AAAA;AAAA,EACH;AAEJ;;;AChDA,SAAS,WAAAE,gBAAe;AACxB,SAAS,oBAAAC,yBAAwB;AA6B3B,gBAAAC,OAOF,QAAAC,aAPE;AAlBN,IAAM,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAO7B,SAAS,cAAiB;AAAA,EACxB;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AACF,GAA0B;AACxB,QAAM,gBAAgBC,kBAAiB;AAEvC,MAAI,MAAM,WAAW,KAAK,CAAC,aAAa;AACtC,WAAO,eACL,gBAAAF,MAAC,OAAE,WAAWG,SAAQ,+BAA+B,SAAS,GAAG,eAAY,wBAC1E,wBACH,IACE;AAAA,EACN;AAEA,SACE,gBAAAF;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAWE,SAAQ,qBAAqB,SAAS;AAAA,MACjD,eAAY;AAAA,MAEX;AAAA,SAAC,iBAAiB,gBAAAH,MAAC,WAAO,gCAAqB;AAAA,QAC/C,MAAM,IAAI,CAAC,MAAM,UAChB,gBAAAA;AAAA,UAAC;AAAA;AAAA,YAEC,OACE,gBACI,SACA,EAAE,WAAW,4BAA4B;AAAA,YAE/C,eAAY;AAAA,YAEX,qBAAW,MAAM,KAAK;AAAA;AAAA,UARlB;AAAA,QASP,CACD;AAAA,QACA,eACC,gBAAAA,MAAC,QAAG,eAAY,0BACd,0BAAAA,MAAC,mBAAgB,GACnB;AAAA;AAAA;AAAA,EAEJ;AAEJ;;;AC/DA,SAAS,WAAAI,gBAAe;AACxB,SAAS,oBAAAC,yBAAwB;AAkBtB,gBAAAC,OA8BH,QAAAC,cA9BG;AATX,IAAMC,wBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAO7B,SAAS,YAAY,OAAgB,eAAyC;AAC5E,MAAI,UAAU,MAAM;AAClB,WAAO,gBAAAF,MAAC,UAAK,WAAU,8BAA6B,kBAAI;AAAA,EAC1D;AACA,MAAI,UAAU,QAAW;AACvB,WAAO,gBAAAA,MAAC,UAAK,WAAU,8BAA6B,uBAAS;AAAA,EAC/D;AACA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WACE,gBAAAA,MAAC,QAAG,WAAU,yBACX,gBAAM,IAAI,CAAC,MAAM,MAChB,gBAAAA,MAAC,QAAW,WAAU,+BACnB,sBAAY,MAAM,aAAa,KADzB,CAET,CACD,GACH;AAAA,EAEJ;AACA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,eAAe,OAAkC,aAAa;AAAA,EACvE;AACA,SAAO,OAAO,KAAK;AACrB;AAEA,SAAS,eACP,MACA,eACiB;AACjB,QAAM,UAAU,OAAO,QAAQ,IAAI;AACnC,SACE,gBAAAA,MAAC,QAAG,WAAU,YAAW,eAAY,8BAClC,kBAAQ,IAAI,CAAC,CAAC,KAAK,KAAK,MACvB,gBAAAC;AAAA,IAAC;AAAA;AAAA,MAEC,OACE,gBACI,SACA,EAAE,WAAW,4BAA4B;AAAA,MAG/C;AAAA,wBAAAD,MAAC,QAAG,WAAU,yCAAyC,eAAI;AAAA,QAC3D,gBAAAA,MAAC,QAAG,WAAU,yCACX,sBAAY,OAAO,aAAa,GACnC;AAAA;AAAA;AAAA,IAVK;AAAA,EAWP,CACD,GACH;AAEJ;AAEA,SAAS,mBAAmB;AAAA,EAC1B;AAAA,EACA,cAAc;AAAA,EACd;AACF,GAA4B;AAC1B,QAAM,gBAAgBG,kBAAiB;AACvC,QAAM,UAAU,OAAO,QAAQ,IAAI;AAEnC,SACE,gBAAAF;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAWG,SAAQ,OAAO,SAAS;AAAA,MACnC,eAAY;AAAA,MAEX;AAAA,SAAC,iBAAiB,gBAAAJ,MAAC,WAAO,UAAAE,uBAAqB;AAAA,QAC/C,QAAQ,IAAI,CAAC,CAAC,KAAK,KAAK,MACvB,gBAAAD;AAAA,UAAC;AAAA;AAAA,YAEC,OACE,gBACI,SACA,EAAE,WAAW,4BAA4B;AAAA,YAE/C,eAAY;AAAA,YAEZ;AAAA,8BAAAD,MAAC,QAAG,WAAU,yCAAyC,eAAI;AAAA,cAC3D,gBAAAA,MAAC,QAAG,WAAU,yCACX,sBAAY,OAAO,aAAa,GACnC;AAAA;AAAA;AAAA,UAXK;AAAA,QAYP,CACD;AAAA,QACA,eACC,gBAAAA,MAAC,SAAI,eAAY,+BACf,0BAAAA,MAAC,eAAY,OAAO,GAAG,GACzB;AAAA;AAAA;AAAA,EAEJ;AAEJ;","names":["useEffect","useRef","twMerge","content","jsx","useRef","useEffect","twMerge","jsx","jsxs","WaveLoader","jsx","jsxs","WaveLoader","jsx","jsxs","jsx","jsxs","useEffect","useState","jsx","jsxs","twMerge","useReducedMotion","jsx","jsxs","twMerge","useReducedMotion","jsx","jsxs","twMerge","useReducedMotion","jsx","jsxs","useReducedMotion","twMerge","twMerge","useReducedMotion","jsx","jsxs","fadeSlideInKeyframes","useReducedMotion","twMerge"]}
@@ -1,5 +1,5 @@
1
1
  import { S as Source, V as VerificationResult, C as ConfidenceBreakdown, a as AgentResponse } from './agent-BNSmiexZ.cjs';
2
- import { C as ChatError } from './chat-CGamM7Mz.cjs';
2
+ import { C as ChatError } from './chat-BRY3xGg_.cjs';
3
3
 
4
4
  type StreamEvent = {
5
5
  type: 'phase';
@@ -7,6 +7,8 @@ type StreamEvent = {
7
7
  } | {
8
8
  type: 'delta';
9
9
  content: string;
10
+ } | {
11
+ type: 'delta_reset';
10
12
  } | {
11
13
  type: 'source';
12
14
  source: Source;
@@ -1,5 +1,5 @@
1
1
  import { S as Source, V as VerificationResult, C as ConfidenceBreakdown, a as AgentResponse } from './agent-BNSmiexZ.js';
2
- import { C as ChatError } from './chat-BIIDOGrD.js';
2
+ import { C as ChatError } from './chat-CcKc6OAR.js';
3
3
 
4
4
  type StreamEvent = {
5
5
  type: 'phase';
@@ -7,6 +7,8 @@ type StreamEvent = {
7
7
  } | {
8
8
  type: 'delta';
9
9
  content: string;
10
+ } | {
11
+ type: 'delta_reset';
10
12
  } | {
11
13
  type: 'source';
12
14
  source: Source;