@surf-kit/agent 0.2.2 → 0.3.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 +625 -204
  2. package/dist/chat/index.cjs.map +1 -1
  3. package/dist/chat/index.d.cts +11 -6
  4. package/dist/chat/index.d.ts +11 -6
  5. package/dist/chat/index.js +606 -185
  6. package/dist/chat/index.js.map +1 -1
  7. package/dist/{chat--OifhIRe.d.ts → chat-BIIDOGrD.d.ts} +10 -1
  8. package/dist/{chat-ChYl2XjV.d.cts → chat-CGamM7Mz.d.cts} +10 -1
  9. package/dist/{hooks-DLfF18IU.d.cts → hooks-B1NYoLLs.d.cts} +21 -5
  10. package/dist/{hooks-BGs8-4GK.d.ts → hooks-CTeEqnBQ.d.ts} +21 -5
  11. package/dist/hooks.cjs +126 -81
  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 +126 -81
  16. package/dist/hooks.js.map +1 -1
  17. package/dist/index.cjs +686 -265
  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 +645 -224
  22. package/dist/index.js.map +1 -1
  23. package/dist/layouts/index.cjs +646 -225
  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 +622 -201
  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 +66 -12
  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 +64 -10
  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 +202 -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 +172 -73
  48. package/dist/streaming/index.js.map +1 -1
  49. package/dist/{streaming-DbQxScpi.d.ts → streaming-Bx-ff2tt.d.ts} +1 -1
  50. package/dist/{streaming-DfT22A0z.d.cts → streaming-x7umFHoP.d.cts} +1 -1
  51. package/package.json +15 -4
@@ -2,7 +2,8 @@
2
2
 
3
3
  // src/streaming/StreamingMessage/StreamingMessage.tsx
4
4
  import { useEffect as useEffect2, useRef as useRef2 } from "react";
5
- import { Spinner } from "@surf-kit/core";
5
+ import { twMerge as twMerge2 } from "tailwind-merge";
6
+ import { WaveLoader } from "@surf-kit/core";
6
7
 
7
8
  // src/hooks/useCharacterDrain.ts
8
9
  import { useState, useRef, useEffect } from "react";
@@ -31,7 +32,10 @@ function useCharacterDrain(target, msPerChar = 15) {
31
32
  const elapsed = now - lastTimeRef.current;
32
33
  const charsToAdvance = Math.floor(elapsed / msPerCharRef.current);
33
34
  if (charsToAdvance > 0 && indexRef.current < currentTarget.length) {
34
- const nextIndex = Math.min(indexRef.current + charsToAdvance, currentTarget.length);
35
+ let nextIndex = Math.min(indexRef.current + charsToAdvance, currentTarget.length);
36
+ while (nextIndex < currentTarget.length && currentTarget[nextIndex - 1].trim() === "") {
37
+ nextIndex++;
38
+ }
35
39
  indexRef.current = nextIndex;
36
40
  lastTimeRef.current = now;
37
41
  setDisplayed(currentTarget.slice(0, nextIndex));
@@ -66,8 +70,77 @@ function useCharacterDrain(target, msPerChar = 15) {
66
70
  return { displayed, isDraining };
67
71
  }
68
72
 
73
+ // src/response/ResponseMessage/ResponseMessage.tsx
74
+ import React from "react";
75
+ import ReactMarkdown from "react-markdown";
76
+ import rehypeSanitize from "rehype-sanitize";
77
+ import { twMerge } from "tailwind-merge";
78
+ import { jsx } from "react/jsx-runtime";
79
+ function normalizeMarkdownLists(content) {
80
+ return content.replace(/:\s+-\s+/g, ":\n\n- ");
81
+ }
82
+ function ResponseMessage({ content, className }) {
83
+ return /* @__PURE__ */ jsx(
84
+ "div",
85
+ {
86
+ className: twMerge(
87
+ "text-sm leading-relaxed text-text-primary",
88
+ "[&_p]:my-2",
89
+ "[&_ul]:my-2 [&_ul]:list-disc [&_ul]:pl-6",
90
+ "[&_ol]:my-2 [&_ol]:list-decimal [&_ol]:pl-6",
91
+ "[&_li]:my-1",
92
+ "[&_strong]:text-text-primary [&_strong]:font-semibold",
93
+ "[&_em]:text-text-secondary",
94
+ "[&_h1]:text-lg [&_h1]:font-semibold [&_h1]:text-text-primary [&_h1]:mt-4 [&_h1]:mb-2",
95
+ "[&_h2]:text-base [&_h2]:font-semibold [&_h2]:text-text-primary [&_h2]:mt-3 [&_h2]:mb-1.5",
96
+ "[&_h3]:text-sm [&_h3]:font-semibold [&_h3]:text-accent [&_h3]:mt-2 [&_h3]:mb-1",
97
+ "[&_code]:bg-surface-raised [&_code]:text-accent [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:rounded [&_code]:text-xs [&_code]:font-mono",
98
+ "[&_pre]:bg-surface-raised [&_pre]:border [&_pre]:border-border [&_pre]:rounded-xl [&_pre]:p-4 [&_pre]:overflow-x-auto",
99
+ "[&_hr]:my-3 [&_hr]:border-border",
100
+ "[&_blockquote]:border-l-2 [&_blockquote]:border-border-strong [&_blockquote]:pl-4 [&_blockquote]:text-text-secondary",
101
+ "[&_a]:text-accent [&_a]:underline-offset-2 [&_a]:hover:text-accent/80",
102
+ className
103
+ ),
104
+ "data-testid": "response-message",
105
+ children: /* @__PURE__ */ jsx(
106
+ ReactMarkdown,
107
+ {
108
+ rehypePlugins: [rehypeSanitize],
109
+ components: {
110
+ script: () => null,
111
+ iframe: () => null,
112
+ p: ({ children }) => /* @__PURE__ */ jsx("p", { className: "my-2", children }),
113
+ ul: ({ children }) => /* @__PURE__ */ jsx("ul", { className: "my-2 list-disc pl-6", children }),
114
+ ol: ({ children }) => /* @__PURE__ */ jsx("ol", { className: "my-2 list-decimal pl-6", children }),
115
+ li: ({ children, ...props }) => {
116
+ let content2 = children;
117
+ if (props.ordered) {
118
+ content2 = React.Children.map(children, (child, i) => {
119
+ if (i === 0 && typeof child === "string") {
120
+ return child.replace(/^\d+[.)]\s*/, "");
121
+ }
122
+ return child;
123
+ });
124
+ }
125
+ return /* @__PURE__ */ jsx("li", { className: "my-1", children: content2 });
126
+ },
127
+ strong: ({ children }) => /* @__PURE__ */ jsx("strong", { className: "font-semibold", children }),
128
+ em: ({ children }) => /* @__PURE__ */ jsx("em", { className: "italic text-text-secondary", children }),
129
+ h1: ({ children }) => /* @__PURE__ */ jsx("h1", { className: "text-base font-bold mt-4 mb-2", children }),
130
+ h2: ({ children }) => /* @__PURE__ */ jsx("h2", { className: "text-sm font-bold mt-3 mb-1", children }),
131
+ h3: ({ children }) => /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold mt-2 mb-1", children }),
132
+ 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 })
134
+ },
135
+ children: normalizeMarkdownLists(content)
136
+ }
137
+ )
138
+ }
139
+ );
140
+ }
141
+
69
142
  // src/streaming/StreamingMessage/StreamingMessage.tsx
70
- import { jsx, jsxs } from "react/jsx-runtime";
143
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
71
144
  var phaseLabels = {
72
145
  idle: "",
73
146
  waiting: "Waiting...",
@@ -76,14 +149,35 @@ var phaseLabels = {
76
149
  generating: "Writing...",
77
150
  verifying: "Verifying..."
78
151
  };
152
+ var CURSOR_STYLES = `
153
+ .sk-streaming-cursor > :not(ul,ol,blockquote):last-child::after,
154
+ .sk-streaming-cursor > :is(ul,ol):last-child > li:last-child::after,
155
+ .sk-streaming-cursor > blockquote:last-child > p:last-child::after {
156
+ content: "";
157
+ display: inline-block;
158
+ width: 2px;
159
+ height: 1em;
160
+ background: var(--color-accent, #38bdf8);
161
+ animation: sk-cursor-blink 0.8s steps(1) infinite;
162
+ margin-left: 2px;
163
+ vertical-align: text-bottom;
164
+ }
165
+ @keyframes sk-cursor-blink {
166
+ 0%, 60% { opacity: 1; }
167
+ 61%, 100% { opacity: 0; }
168
+ }
169
+ `;
79
170
  function StreamingMessage({
80
171
  stream,
81
172
  onComplete,
173
+ onDraining,
82
174
  showPhases = true,
83
175
  className
84
176
  }) {
85
177
  const onCompleteRef = useRef2(onComplete);
86
178
  onCompleteRef.current = onComplete;
179
+ const onDrainingRef = useRef2(onDraining);
180
+ onDrainingRef.current = onDraining;
87
181
  const wasActiveRef = useRef2(stream.active);
88
182
  useEffect2(() => {
89
183
  if (wasActiveRef.current && !stream.active) {
@@ -92,42 +186,47 @@ function StreamingMessage({
92
186
  wasActiveRef.current = stream.active;
93
187
  }, [stream.active]);
94
188
  const phaseLabel = phaseLabels[stream.phase];
95
- const { displayed: displayedContent } = useCharacterDrain(stream.content);
96
- return /* @__PURE__ */ jsxs("div", { className, "data-testid": "streaming-message", children: [
189
+ const { displayed: rawDisplayed, isDraining } = useCharacterDrain(stream.content);
190
+ const displayedContent = stream.active || isDraining ? rawDisplayed.trimEnd() : rawDisplayed;
191
+ useEffect2(() => {
192
+ onDrainingRef.current?.(isDraining);
193
+ }, [isDraining]);
194
+ const agentLabel = stream.agent ? stream.agent.replace("_agent", "").replace("_", " ") : null;
195
+ const showPhaseIndicator = showPhases && stream.active && stream.phase !== "idle" && !displayedContent;
196
+ const showCursor = (stream.active || isDraining) && !!displayedContent;
197
+ return /* @__PURE__ */ jsxs("div", { className: twMerge2("flex w-full flex-col items-start", className), "data-testid": "streaming-message", children: [
97
198
  /* @__PURE__ */ jsxs("div", { "aria-live": "assertive", className: "sr-only", children: [
98
199
  stream.active && stream.phase !== "idle" && "Response started",
99
200
  !stream.active && stream.content && "Response complete"
100
201
  ] }),
202
+ showCursor && /* @__PURE__ */ jsx2("style", { children: CURSOR_STYLES }),
203
+ agentLabel && /* @__PURE__ */ jsx2("div", { className: "text-[11px] font-display font-semibold uppercase tracking-[0.08em] text-text-muted px-1 mb-1.5", children: agentLabel }),
101
204
  /* @__PURE__ */ jsxs("div", { className: "max-w-[88%] px-4 py-3 rounded-[18px] rounded-tl-[4px] bg-surface border border-border motion-safe:animate-springFromLeft", children: [
102
- showPhases && stream.active && stream.phase !== "idle" && /* @__PURE__ */ jsxs(
205
+ showPhaseIndicator && /* @__PURE__ */ jsxs(
103
206
  "div",
104
207
  {
105
- className: "flex items-center gap-2 mb-2 text-sm text-text-secondary",
208
+ className: "flex items-center gap-2 text-sm text-text-secondary",
106
209
  "data-testid": "phase-indicator",
107
210
  children: [
108
- /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: /* @__PURE__ */ jsx(Spinner, { size: "sm" }) }),
109
- /* @__PURE__ */ jsx("span", { children: phaseLabel })
211
+ /* @__PURE__ */ jsx2("span", { "aria-hidden": "true", children: /* @__PURE__ */ jsx2(WaveLoader, { size: "sm", color: "#38bdf8" }) }),
212
+ /* @__PURE__ */ jsx2("span", { children: phaseLabel })
110
213
  ]
111
214
  }
112
215
  ),
113
- /* @__PURE__ */ jsxs("div", { className: "text-sm leading-relaxed text-text-primary whitespace-pre-wrap", children: [
114
- displayedContent,
115
- stream.active && /* @__PURE__ */ jsx(
116
- "span",
117
- {
118
- className: "inline-block w-0.5 h-4 bg-accent align-text-bottom animate-pulse ml-0.5",
119
- "aria-hidden": "true",
120
- "data-testid": "streaming-cursor"
121
- }
122
- )
123
- ] })
216
+ displayedContent && /* @__PURE__ */ jsx2(
217
+ ResponseMessage,
218
+ {
219
+ content: displayedContent,
220
+ className: showCursor ? "sk-streaming-cursor" : void 0
221
+ }
222
+ )
124
223
  ] })
125
224
  ] });
126
225
  }
127
226
 
128
227
  // src/streaming/ThinkingIndicator/ThinkingIndicator.tsx
129
228
  import { useReducedMotion } from "@surf-kit/hooks";
130
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
229
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
131
230
  function ThinkingIndicator({ label = "Thinking...", className }) {
132
231
  const reducedMotion = useReducedMotion();
133
232
  return /* @__PURE__ */ jsxs2(
@@ -137,11 +236,11 @@ function ThinkingIndicator({ label = "Thinking...", className }) {
137
236
  className: `inline-flex items-center gap-2 text-sm motion-safe:animate-fadeSlideUpSm ${className ?? ""}`,
138
237
  "data-testid": "thinking-indicator",
139
238
  children: [
140
- /* @__PURE__ */ jsx2("span", { className: "text-text-secondary", children: label }),
239
+ /* @__PURE__ */ jsx3("span", { className: "text-text-secondary", children: label }),
141
240
  !reducedMotion && /* @__PURE__ */ jsxs2("span", { className: "flex gap-1", "aria-hidden": "true", "data-testid": "animated-dots", children: [
142
- /* @__PURE__ */ jsx2("span", { className: "w-1.5 h-1.5 rounded-full bg-current animate-bounce [animation-delay:0ms]" }),
143
- /* @__PURE__ */ jsx2("span", { className: "w-1.5 h-1.5 rounded-full bg-current animate-bounce [animation-delay:150ms]" }),
144
- /* @__PURE__ */ jsx2("span", { className: "w-1.5 h-1.5 rounded-full bg-current animate-bounce [animation-delay:300ms]" })
241
+ /* @__PURE__ */ jsx3("span", { className: "w-1.5 h-1.5 rounded-full bg-current animate-bounce [animation-delay:0ms]" }),
242
+ /* @__PURE__ */ jsx3("span", { className: "w-1.5 h-1.5 rounded-full bg-current animate-bounce [animation-delay:150ms]" }),
243
+ /* @__PURE__ */ jsx3("span", { className: "w-1.5 h-1.5 rounded-full bg-current animate-bounce [animation-delay:300ms]" })
145
244
  ] })
146
245
  ]
147
246
  }
@@ -149,8 +248,8 @@ function ThinkingIndicator({ label = "Thinking...", className }) {
149
248
  }
150
249
 
151
250
  // src/streaming/ToolExecution/ToolExecution.tsx
152
- import { Spinner as Spinner2 } from "@surf-kit/core";
153
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
251
+ import { WaveLoader as WaveLoader2 } from "@surf-kit/core";
252
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
154
253
  var defaultLabels = {
155
254
  search: "Searching knowledge base...",
156
255
  retrieve: "Retrieving documents...",
@@ -165,16 +264,16 @@ function ToolExecution({ tool, label, className }) {
165
264
  role: "status",
166
265
  "data-testid": "tool-execution",
167
266
  children: [
168
- /* @__PURE__ */ jsx3("span", { "aria-hidden": "true", children: /* @__PURE__ */ jsx3(Spinner2, { size: "sm" }) }),
169
- /* @__PURE__ */ jsx3("span", { children: displayLabel })
267
+ /* @__PURE__ */ jsx4("span", { "aria-hidden": "true", children: /* @__PURE__ */ jsx4(WaveLoader2, { size: "sm", color: "#38bdf8" }) }),
268
+ /* @__PURE__ */ jsx4("span", { children: displayLabel })
170
269
  ]
171
270
  }
172
271
  );
173
272
  }
174
273
 
175
274
  // src/streaming/RetrievalProgress/RetrievalProgress.tsx
176
- import { Spinner as Spinner3 } from "@surf-kit/core";
177
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
275
+ import { WaveLoader as WaveLoader3 } from "@surf-kit/core";
276
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
178
277
  function RetrievalProgress({ sources, isActive, className }) {
179
278
  return /* @__PURE__ */ jsxs4(
180
279
  "div",
@@ -185,18 +284,18 @@ function RetrievalProgress({ sources, isActive, className }) {
185
284
  "data-testid": "retrieval-progress",
186
285
  children: [
187
286
  isActive && /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2 text-sm text-text-secondary", children: [
188
- /* @__PURE__ */ jsx4("span", { "aria-hidden": "true", children: /* @__PURE__ */ jsx4(Spinner3, { size: "sm" }) }),
189
- /* @__PURE__ */ jsx4("span", { children: "Retrieving sources..." })
287
+ /* @__PURE__ */ jsx5("span", { "aria-hidden": "true", children: /* @__PURE__ */ jsx5(WaveLoader3, { size: "sm", color: "#38bdf8" }) }),
288
+ /* @__PURE__ */ jsx5("span", { children: "Retrieving sources..." })
190
289
  ] }),
191
- sources.length > 0 && /* @__PURE__ */ jsx4("ul", { className: "space-y-1", "data-testid": "source-list", children: sources.map((source, index) => /* @__PURE__ */ jsxs4(
290
+ sources.length > 0 && /* @__PURE__ */ jsx5("ul", { className: "space-y-1", "data-testid": "source-list", children: sources.map((source, index) => /* @__PURE__ */ jsxs4(
192
291
  "li",
193
292
  {
194
293
  className: "text-sm text-text-secondary flex items-center gap-2 animate-in fade-in slide-in-from-left-2",
195
294
  style: { animationDelay: `${index * 100}ms`, animationFillMode: "both" },
196
295
  "data-testid": "retrieval-source-item",
197
296
  children: [
198
- /* @__PURE__ */ jsx4("span", { className: "w-1.5 h-1.5 rounded-full bg-accent flex-shrink-0", "aria-hidden": "true" }),
199
- /* @__PURE__ */ jsx4("span", { className: "truncate", children: source.title })
297
+ /* @__PURE__ */ jsx5("span", { className: "w-1.5 h-1.5 rounded-full bg-accent flex-shrink-0", "aria-hidden": "true" }),
298
+ /* @__PURE__ */ jsx5("span", { className: "truncate", children: source.title })
200
299
  ]
201
300
  },
202
301
  source.document_id
@@ -207,7 +306,7 @@ function RetrievalProgress({ sources, isActive, className }) {
207
306
  }
208
307
 
209
308
  // src/streaming/VerificationProgress/VerificationProgress.tsx
210
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
309
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
211
310
  function VerificationProgress({
212
311
  isActive,
213
312
  label = "Checking accuracy...",
@@ -230,12 +329,12 @@ function VerificationProgress({
230
329
  viewBox: "0 0 24 24",
231
330
  "aria-hidden": "true",
232
331
  children: [
233
- /* @__PURE__ */ jsx5("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
234
- /* @__PURE__ */ jsx5("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" })
332
+ /* @__PURE__ */ jsx6("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
333
+ /* @__PURE__ */ jsx6("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" })
235
334
  ]
236
335
  }
237
336
  ),
238
- /* @__PURE__ */ jsx5("span", { className: "text-accent animate-pulse", children: label })
337
+ /* @__PURE__ */ jsx6("span", { className: "text-accent animate-pulse", children: label })
239
338
  ]
240
339
  }
241
340
  );
@@ -243,7 +342,7 @@ function VerificationProgress({
243
342
 
244
343
  // src/streaming/TypewriterText/TypewriterText.tsx
245
344
  import { useEffect as useEffect3, useState as useState2 } from "react";
246
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
345
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
247
346
  function TypewriterText({
248
347
  text,
249
348
  speed = 30,
@@ -278,14 +377,14 @@ function TypewriterText({
278
377
  }, [text, speed, delay, onComplete]);
279
378
  return /* @__PURE__ */ jsxs6("span", { className, children: [
280
379
  displayedText,
281
- showCursor && !isComplete && /* @__PURE__ */ jsx6("span", { className: "typewriter-cursor", "aria-hidden": "true" })
380
+ showCursor && !isComplete && /* @__PURE__ */ jsx7("span", { className: "typewriter-cursor", "aria-hidden": "true" })
282
381
  ] });
283
382
  }
284
383
 
285
384
  // src/streaming/TypingIndicator/TypingIndicator.tsx
286
- import { twMerge } from "tailwind-merge";
385
+ import { twMerge as twMerge3 } from "tailwind-merge";
287
386
  import { useReducedMotion as useReducedMotion2 } from "@surf-kit/hooks";
288
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
387
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
289
388
  var bounceKeyframes = `
290
389
  @keyframes typing-bounce {
291
390
  0%, 80%, 100% { transform: translateY(0); }
@@ -303,12 +402,12 @@ function TypingIndicator({
303
402
  {
304
403
  role: "status",
305
404
  "aria-label": label ?? "typing",
306
- className: twMerge("inline-flex items-center gap-2", className),
405
+ className: twMerge3("inline-flex items-center gap-2", className),
307
406
  "data-testid": "typing-indicator",
308
407
  children: [
309
- !reducedMotion && /* @__PURE__ */ jsx7("style", { children: bounceKeyframes }),
310
- label && /* @__PURE__ */ jsx7("span", { className: "text-sm text-text-secondary", children: label }),
311
- /* @__PURE__ */ jsx7("span", { className: "flex gap-1", "data-testid": "typing-dots", children: Array.from({ length: dotCount }, (_, i) => /* @__PURE__ */ jsx7(
408
+ !reducedMotion && /* @__PURE__ */ jsx8("style", { children: bounceKeyframes }),
409
+ label && /* @__PURE__ */ jsx8("span", { className: "text-sm text-text-secondary", children: label }),
410
+ /* @__PURE__ */ jsx8("span", { className: "flex gap-1", "data-testid": "typing-dots", children: Array.from({ length: dotCount }, (_, i) => /* @__PURE__ */ jsx8(
312
411
  "span",
313
412
  {
314
413
  className: "w-2 h-2 rounded-full bg-text-secondary",
@@ -325,9 +424,9 @@ function TypingIndicator({
325
424
  }
326
425
 
327
426
  // src/streaming/TextGlimmer/TextGlimmer.tsx
328
- import { twMerge as twMerge2 } from "tailwind-merge";
427
+ import { twMerge as twMerge4 } from "tailwind-merge";
329
428
  import { useReducedMotion as useReducedMotion3 } from "@surf-kit/hooks";
330
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
429
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
331
430
  var shimmerKeyframes = `
332
431
  @keyframes text-shimmer {
333
432
  0% { background-position: 200% 0; }
@@ -342,11 +441,11 @@ function TextGlimmer({ lines = 3, className }) {
342
441
  {
343
442
  role: "status",
344
443
  "aria-label": "Loading",
345
- className: twMerge2("flex flex-col gap-2", className),
444
+ className: twMerge4("flex flex-col gap-2", className),
346
445
  "data-testid": "text-glimmer",
347
446
  children: [
348
- !reducedMotion && /* @__PURE__ */ jsx8("style", { children: shimmerKeyframes }),
349
- Array.from({ length: lines }, (_, i) => /* @__PURE__ */ jsx8(
447
+ !reducedMotion && /* @__PURE__ */ jsx9("style", { children: shimmerKeyframes }),
448
+ Array.from({ length: lines }, (_, i) => /* @__PURE__ */ jsx9(
350
449
  "div",
351
450
  {
352
451
  className: "h-3 rounded bg-surface-raised",
@@ -368,9 +467,9 @@ function TextGlimmer({ lines = 3, className }) {
368
467
  }
369
468
 
370
469
  // src/streaming/StreamingList/StreamingList.tsx
371
- import { twMerge as twMerge3 } from "tailwind-merge";
470
+ import { twMerge as twMerge5 } from "tailwind-merge";
372
471
  import { useReducedMotion as useReducedMotion4 } from "@surf-kit/hooks";
373
- import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
472
+ import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
374
473
  var fadeSlideInKeyframes = `
375
474
  @keyframes fadeSlideIn {
376
475
  from { opacity: 0; transform: translateY(8px); }
@@ -386,17 +485,17 @@ function StreamingList({
386
485
  }) {
387
486
  const reducedMotion = useReducedMotion4();
388
487
  if (items.length === 0 && !isStreaming) {
389
- return emptyMessage ? /* @__PURE__ */ jsx9("p", { className: twMerge3("text-sm text-text-secondary", className), "data-testid": "streaming-list-empty", children: emptyMessage }) : null;
488
+ return emptyMessage ? /* @__PURE__ */ jsx10("p", { className: twMerge5("text-sm text-text-secondary", className), "data-testid": "streaming-list-empty", children: emptyMessage }) : null;
390
489
  }
391
490
  return /* @__PURE__ */ jsxs9(
392
491
  "ul",
393
492
  {
394
493
  "aria-live": "polite",
395
- className: twMerge3("list-none p-0 m-0", className),
494
+ className: twMerge5("list-none p-0 m-0", className),
396
495
  "data-testid": "streaming-list",
397
496
  children: [
398
- !reducedMotion && /* @__PURE__ */ jsx9("style", { children: fadeSlideInKeyframes }),
399
- items.map((item, index) => /* @__PURE__ */ jsx9(
497
+ !reducedMotion && /* @__PURE__ */ jsx10("style", { children: fadeSlideInKeyframes }),
498
+ items.map((item, index) => /* @__PURE__ */ jsx10(
400
499
  "li",
401
500
  {
402
501
  style: reducedMotion ? void 0 : { animation: "fadeSlideIn 0.3s ease-out" },
@@ -405,16 +504,16 @@ function StreamingList({
405
504
  },
406
505
  index
407
506
  )),
408
- isStreaming && /* @__PURE__ */ jsx9("li", { "data-testid": "streaming-list-loading", children: /* @__PURE__ */ jsx9(TypingIndicator, {}) })
507
+ isStreaming && /* @__PURE__ */ jsx10("li", { "data-testid": "streaming-list-loading", children: /* @__PURE__ */ jsx10(TypingIndicator, {}) })
409
508
  ]
410
509
  }
411
510
  );
412
511
  }
413
512
 
414
513
  // src/streaming/StreamingStructure/StreamingStructure.tsx
415
- import { twMerge as twMerge4 } from "tailwind-merge";
514
+ import { twMerge as twMerge6 } from "tailwind-merge";
416
515
  import { useReducedMotion as useReducedMotion5 } from "@surf-kit/hooks";
417
- import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
516
+ import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
418
517
  var fadeSlideInKeyframes2 = `
419
518
  @keyframes fadeSlideIn {
420
519
  from { opacity: 0; transform: translateY(8px); }
@@ -423,13 +522,13 @@ var fadeSlideInKeyframes2 = `
423
522
  `;
424
523
  function renderValue(value, reducedMotion) {
425
524
  if (value === null) {
426
- return /* @__PURE__ */ jsx10("span", { className: "italic text-text-secondary", children: "null" });
525
+ return /* @__PURE__ */ jsx11("span", { className: "italic text-text-secondary", children: "null" });
427
526
  }
428
527
  if (value === void 0) {
429
- return /* @__PURE__ */ jsx10("span", { className: "italic text-text-secondary", children: "undefined" });
528
+ return /* @__PURE__ */ jsx11("span", { className: "italic text-text-secondary", children: "undefined" });
430
529
  }
431
530
  if (Array.isArray(value)) {
432
- return /* @__PURE__ */ jsx10("ol", { className: "list-decimal pl-4 m-0", children: value.map((item, i) => /* @__PURE__ */ jsx10("li", { className: "text-text-secondary text-sm", children: renderValue(item, reducedMotion) }, i)) });
531
+ return /* @__PURE__ */ jsx11("ol", { className: "list-decimal pl-4 m-0", children: value.map((item, i) => /* @__PURE__ */ jsx11("li", { className: "text-text-secondary text-sm", children: renderValue(item, reducedMotion) }, i)) });
433
532
  }
434
533
  if (typeof value === "object") {
435
534
  return renderNestedDl(value, reducedMotion);
@@ -438,13 +537,13 @@ function renderValue(value, reducedMotion) {
438
537
  }
439
538
  function renderNestedDl(data, reducedMotion) {
440
539
  const entries = Object.entries(data);
441
- return /* @__PURE__ */ jsx10("dl", { className: "pl-4 m-0", "data-testid": "streaming-structure-nested", children: entries.map(([key, value]) => /* @__PURE__ */ jsxs10(
540
+ return /* @__PURE__ */ jsx11("dl", { className: "pl-4 m-0", "data-testid": "streaming-structure-nested", children: entries.map(([key, value]) => /* @__PURE__ */ jsxs10(
442
541
  "div",
443
542
  {
444
543
  style: reducedMotion ? void 0 : { animation: "fadeSlideIn 0.3s ease-out" },
445
544
  children: [
446
- /* @__PURE__ */ jsx10("dt", { className: "font-medium text-text-primary text-sm", children: key }),
447
- /* @__PURE__ */ jsx10("dd", { className: "text-text-secondary text-sm ml-0 mb-3", children: renderValue(value, reducedMotion) })
545
+ /* @__PURE__ */ jsx11("dt", { className: "font-medium text-text-primary text-sm", children: key }),
546
+ /* @__PURE__ */ jsx11("dd", { className: "text-text-secondary text-sm ml-0 mb-3", children: renderValue(value, reducedMotion) })
448
547
  ]
449
548
  },
450
549
  key
@@ -461,23 +560,23 @@ function StreamingStructure({
461
560
  "dl",
462
561
  {
463
562
  "aria-live": "polite",
464
- className: twMerge4("m-0", className),
563
+ className: twMerge6("m-0", className),
465
564
  "data-testid": "streaming-structure",
466
565
  children: [
467
- !reducedMotion && /* @__PURE__ */ jsx10("style", { children: fadeSlideInKeyframes2 }),
566
+ !reducedMotion && /* @__PURE__ */ jsx11("style", { children: fadeSlideInKeyframes2 }),
468
567
  entries.map(([key, value]) => /* @__PURE__ */ jsxs10(
469
568
  "div",
470
569
  {
471
570
  style: reducedMotion ? void 0 : { animation: "fadeSlideIn 0.3s ease-out" },
472
571
  "data-testid": "streaming-structure-entry",
473
572
  children: [
474
- /* @__PURE__ */ jsx10("dt", { className: "font-medium text-text-primary text-sm", children: key }),
475
- /* @__PURE__ */ jsx10("dd", { className: "text-text-secondary text-sm ml-0 mb-3", children: renderValue(value, reducedMotion) })
573
+ /* @__PURE__ */ jsx11("dt", { className: "font-medium text-text-primary text-sm", children: key }),
574
+ /* @__PURE__ */ jsx11("dd", { className: "text-text-secondary text-sm ml-0 mb-3", children: renderValue(value, reducedMotion) })
476
575
  ]
477
576
  },
478
577
  key
479
578
  )),
480
- isStreaming && /* @__PURE__ */ jsx10("div", { "data-testid": "streaming-structure-loading", children: /* @__PURE__ */ jsx10(TextGlimmer, { lines: 1 }) })
579
+ isStreaming && /* @__PURE__ */ jsx11("div", { "data-testid": "streaming-structure-loading", children: /* @__PURE__ */ jsx11(TextGlimmer, { lines: 1 }) })
481
580
  ]
482
581
  }
483
582
  );