@surf-kit/agent 0.2.2 → 0.4.0

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 (51) hide show
  1. package/dist/chat/index.cjs +733 -222
  2. package/dist/chat/index.cjs.map +1 -1
  3. package/dist/chat/index.d.cts +13 -7
  4. package/dist/chat/index.d.ts +13 -7
  5. package/dist/chat/index.js +715 -204
  6. package/dist/chat/index.js.map +1 -1
  7. package/dist/{chat-ChYl2XjV.d.cts → chat-BRY3xGg_.d.cts} +11 -2
  8. package/dist/{chat--OifhIRe.d.ts → chat-CcKc6OAR.d.ts} +11 -2
  9. package/dist/{hooks-BGs8-4GK.d.ts → hooks-BLeiVk-x.d.ts} +22 -5
  10. package/dist/{hooks-DLfF18IU.d.cts → hooks-CSGGLd7j.d.cts} +22 -5
  11. package/dist/hooks.cjs +160 -85
  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 +160 -85
  16. package/dist/hooks.js.map +1 -1
  17. package/dist/index.cjs +794 -283
  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 +758 -247
  22. package/dist/index.js.map +1 -1
  23. package/dist/layouts/index.cjs +754 -243
  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 +733 -222
  28. package/dist/layouts/index.js.map +1 -1
  29. package/dist/mcp/index.cjs +1 -1
  30. package/dist/mcp/index.cjs.map +1 -1
  31. package/dist/mcp/index.js +2 -2
  32. package/dist/mcp/index.js.map +1 -1
  33. package/dist/response/index.cjs +100 -15
  34. package/dist/response/index.cjs.map +1 -1
  35. package/dist/response/index.d.cts +2 -2
  36. package/dist/response/index.d.ts +2 -2
  37. package/dist/response/index.js +99 -14
  38. package/dist/response/index.js.map +1 -1
  39. package/dist/sources/index.cjs +30 -1
  40. package/dist/sources/index.cjs.map +1 -1
  41. package/dist/sources/index.js +30 -1
  42. package/dist/sources/index.js.map +1 -1
  43. package/dist/streaming/index.cjs +213 -93
  44. package/dist/streaming/index.cjs.map +1 -1
  45. package/dist/streaming/index.d.cts +4 -3
  46. package/dist/streaming/index.d.ts +4 -3
  47. package/dist/streaming/index.js +183 -73
  48. package/dist/streaming/index.js.map +1 -1
  49. package/dist/{streaming-DfT22A0z.d.cts → streaming-BHPXnwwo.d.cts} +3 -1
  50. package/dist/{streaming-DbQxScpi.d.ts → streaming-C6mbU7My.d.ts} +3 -1
  51. package/package.json +17 -5
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/streaming/index.ts","../../src/streaming/StreamingMessage/StreamingMessage.tsx","../../src/hooks/useCharacterDrain.ts","../../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 { Spinner } from '@surf-kit/core'\nimport type { StreamState } from '../../types/streaming'\nimport { useCharacterDrain } from '../../hooks/useCharacterDrain'\n\ntype StreamingMessageProps = {\n stream: StreamState\n onComplete?: () => 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\nfunction StreamingMessage({\n stream,\n onComplete,\n showPhases = true,\n className,\n}: StreamingMessageProps) {\n const onCompleteRef = useRef(onComplete)\n onCompleteRef.current = onComplete\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: displayedContent } = useCharacterDrain(stream.content)\n\n return (\n <div className={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 <div className=\"max-w-[88%] px-4 py-3 rounded-[18px] rounded-tl-[4px] bg-surface border border-border motion-safe:animate-springFromLeft\">\n {showPhases && stream.active && stream.phase !== 'idle' && (\n <div\n className=\"flex items-center gap-2 mb-2 text-sm text-text-secondary\"\n data-testid=\"phase-indicator\"\n >\n <span aria-hidden=\"true\">\n <Spinner size=\"sm\" />\n </span>\n <span>{phaseLabel}</span>\n </div>\n )}\n\n <div className=\"text-sm leading-relaxed text-text-primary whitespace-pre-wrap\">\n {displayedContent}\n {stream.active && (\n <span\n className=\"inline-block w-0.5 h-4 bg-accent align-text-bottom animate-pulse ml-0.5\"\n aria-hidden=\"true\"\n data-testid=\"streaming-cursor\"\n />\n )}\n </div>\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 const nextIndex = Math.min(indexRef.current + charsToAdvance, currentTarget.length)\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 { 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 { Spinner } 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 <Spinner size=\"sm\" />\n </span>\n <span>{displayLabel}</span>\n </div>\n )\n}\n\nexport { ToolExecution }\nexport type { ToolExecutionProps }\n","import React from 'react'\nimport { Spinner } 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 <Spinner size=\"sm\" />\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_jsx_runtime","import_core","import_jsx_runtime","import_core","import_jsx_runtime","import_jsx_runtime","import_react","import_jsx_runtime","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,16 +1,17 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { a as StreamState } from '../streaming-DfT22A0z.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-ChYl2XjV.cjs';
5
+ import '../chat-BRY3xGg_.cjs';
6
6
 
7
7
  type StreamingMessageProps = {
8
8
  stream: StreamState;
9
9
  onComplete?: () => void;
10
+ onDraining?: (isDraining: boolean) => void;
10
11
  showPhases?: boolean;
11
12
  className?: string;
12
13
  };
13
- declare function StreamingMessage({ stream, onComplete, showPhases, className, }: StreamingMessageProps): react_jsx_runtime.JSX.Element;
14
+ declare function StreamingMessage({ stream, onComplete, onDraining, showPhases, className, }: StreamingMessageProps): react_jsx_runtime.JSX.Element;
14
15
 
15
16
  type ThinkingIndicatorProps = {
16
17
  label?: string;
@@ -1,16 +1,17 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { a as StreamState } from '../streaming-DbQxScpi.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--OifhIRe.js';
5
+ import '../chat-CcKc6OAR.js';
6
6
 
7
7
  type StreamingMessageProps = {
8
8
  stream: StreamState;
9
9
  onComplete?: () => void;
10
+ onDraining?: (isDraining: boolean) => void;
10
11
  showPhases?: boolean;
11
12
  className?: string;
12
13
  };
13
- declare function StreamingMessage({ stream, onComplete, showPhases, className, }: StreamingMessageProps): react_jsx_runtime.JSX.Element;
14
+ declare function StreamingMessage({ stream, onComplete, onDraining, showPhases, className, }: StreamingMessageProps): react_jsx_runtime.JSX.Element;
14
15
 
15
16
  type ThinkingIndicatorProps = {
16
17
  label?: string;