@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
package/dist/index.cjs CHANGED
@@ -81,7 +81,7 @@ __export(index_exports, {
81
81
  module.exports = __toCommonJS(index_exports);
82
82
 
83
83
  // src/chat/AgentChat/AgentChat.tsx
84
- var import_tailwind_merge8 = require("tailwind-merge");
84
+ var import_tailwind_merge9 = require("tailwind-merge");
85
85
 
86
86
  // src/hooks/useAgentChat.ts
87
87
  var import_react = require("react");
@@ -92,7 +92,8 @@ var initialState = {
92
92
  error: null,
93
93
  inputValue: "",
94
94
  streamPhase: "idle",
95
- streamingContent: ""
95
+ streamingContent: "",
96
+ streamingAgent: null
96
97
  };
97
98
  function reducer(state, action) {
98
99
  switch (action.type) {
@@ -106,12 +107,15 @@ function reducer(state, action) {
106
107
  error: null,
107
108
  inputValue: "",
108
109
  streamPhase: "thinking",
109
- streamingContent: ""
110
+ streamingContent: "",
111
+ streamingAgent: null
110
112
  };
111
113
  case "STREAM_PHASE":
112
114
  return { ...state, streamPhase: action.phase };
113
115
  case "STREAM_CONTENT":
114
116
  return { ...state, streamingContent: state.streamingContent + action.content };
117
+ case "STREAM_AGENT":
118
+ return { ...state, streamingAgent: action.agent };
115
119
  case "SEND_SUCCESS":
116
120
  return {
117
121
  ...state,
@@ -127,7 +131,8 @@ function reducer(state, action) {
127
131
  isLoading: false,
128
132
  error: action.error,
129
133
  streamPhase: "idle",
130
- streamingContent: ""
134
+ streamingContent: "",
135
+ streamingAgent: null
131
136
  };
132
137
  case "LOAD_CONVERSATION":
133
138
  return {
@@ -153,107 +158,142 @@ function useAgentChat(config2) {
153
158
  const configRef = (0, import_react.useRef)(config2);
154
159
  configRef.current = config2;
155
160
  const lastUserMessageRef = (0, import_react.useRef)(null);
161
+ const lastUserAttachmentsRef = (0, import_react.useRef)(void 0);
156
162
  const sendMessage = (0, import_react.useCallback)(
157
- async (content) => {
158
- const { apiUrl, streamPath = "/chat/stream", headers = {}, timeout = 3e4 } = configRef.current;
163
+ async (content, attachments) => {
164
+ const { apiUrl, streamPath = "/chat/stream", headers: headersOrFn, timeout = 3e4, bodyExtra } = configRef.current;
165
+ const headers = typeof headersOrFn === "function" ? await headersOrFn() : headersOrFn ?? {};
159
166
  lastUserMessageRef.current = content;
167
+ lastUserAttachmentsRef.current = attachments;
160
168
  const userMessage = {
161
169
  id: generateMessageId(),
162
170
  role: "user",
163
171
  content,
172
+ attachments,
164
173
  timestamp: /* @__PURE__ */ new Date()
165
174
  };
166
175
  dispatch({ type: "SEND_START", message: userMessage });
167
176
  const controller = new AbortController();
168
177
  const timeoutId = setTimeout(() => controller.abort(), timeout);
169
178
  try {
170
- const response = await fetch(`${apiUrl}${streamPath}`, {
171
- method: "POST",
172
- headers: {
173
- "Content-Type": "application/json",
174
- Accept: "text/event-stream",
175
- ...headers
176
- },
177
- body: JSON.stringify({
178
- message: content,
179
- conversation_id: state.conversationId
180
- }),
181
- signal: controller.signal
182
- });
183
- clearTimeout(timeoutId);
184
- if (!response.ok) {
185
- dispatch({
186
- type: "SEND_ERROR",
187
- error: {
188
- code: "API_ERROR",
189
- message: `HTTP ${response.status}: ${response.statusText}`,
190
- retryable: response.status >= 500
191
- }
192
- });
193
- return;
179
+ const url = `${apiUrl}${streamPath}`;
180
+ const mergedHeaders = {
181
+ "Content-Type": "application/json",
182
+ Accept: "text/event-stream",
183
+ ...headers
184
+ };
185
+ const requestBody = {
186
+ message: content,
187
+ conversation_id: state.conversationId,
188
+ ...bodyExtra
189
+ };
190
+ if (attachments && attachments.length > 0) {
191
+ requestBody.attachments = attachments.map((a) => ({
192
+ filename: a.filename,
193
+ content_type: a.content_type,
194
+ data: a.data
195
+ }));
194
196
  }
195
- const reader = response.body?.getReader();
196
- if (!reader) {
197
- dispatch({
198
- type: "SEND_ERROR",
199
- error: { code: "STREAM_ERROR", message: "No response body", retryable: true }
197
+ const body = JSON.stringify(requestBody);
198
+ const ctx = {
199
+ accumulatedContent: "",
200
+ agentResponse: null,
201
+ capturedAgent: null,
202
+ capturedConversationId: null,
203
+ hadStreamError: false
204
+ };
205
+ const handleEvent = (event) => {
206
+ switch (event.type) {
207
+ case "agent":
208
+ ctx.capturedAgent = event.agent;
209
+ dispatch({ type: "STREAM_AGENT", agent: ctx.capturedAgent });
210
+ break;
211
+ case "phase":
212
+ dispatch({ type: "STREAM_PHASE", phase: event.phase });
213
+ break;
214
+ case "delta":
215
+ ctx.accumulatedContent += event.content;
216
+ dispatch({ type: "STREAM_CONTENT", content: event.content });
217
+ break;
218
+ case "done":
219
+ ctx.agentResponse = event.response;
220
+ ctx.capturedConversationId = event.conversation_id ?? null;
221
+ break;
222
+ case "error":
223
+ ctx.hadStreamError = true;
224
+ dispatch({ type: "SEND_ERROR", error: event.error });
225
+ break;
226
+ }
227
+ };
228
+ const { streamAdapter } = configRef.current;
229
+ if (streamAdapter) {
230
+ await streamAdapter(
231
+ url,
232
+ { method: "POST", headers: mergedHeaders, body, signal: controller.signal },
233
+ handleEvent
234
+ );
235
+ clearTimeout(timeoutId);
236
+ } else {
237
+ const response = await fetch(url, {
238
+ method: "POST",
239
+ headers: mergedHeaders,
240
+ body,
241
+ signal: controller.signal
200
242
  });
201
- return;
202
- }
203
- const decoder = new TextDecoder();
204
- let buffer = "";
205
- let accumulatedContent = "";
206
- let agentResponse = null;
207
- let capturedAgent = null;
208
- let capturedConversationId = null;
209
- while (true) {
210
- const { done, value } = await reader.read();
211
- if (done) break;
212
- buffer += decoder.decode(value, { stream: true });
213
- const lines = buffer.split("\n");
214
- buffer = lines.pop() ?? "";
215
- for (const line of lines) {
216
- if (!line.startsWith("data: ")) continue;
217
- const data = line.slice(6).trim();
218
- if (data === "[DONE]") continue;
219
- try {
220
- const event = JSON.parse(data);
221
- switch (event.type) {
222
- case "agent":
223
- capturedAgent = event.agent;
224
- break;
225
- case "phase":
226
- dispatch({ type: "STREAM_PHASE", phase: event.phase });
227
- break;
228
- case "delta":
229
- accumulatedContent += event.content;
230
- dispatch({ type: "STREAM_CONTENT", content: event.content });
231
- break;
232
- case "done":
233
- agentResponse = event.response;
234
- capturedConversationId = event.conversation_id ?? null;
235
- break;
236
- case "error":
237
- dispatch({ type: "SEND_ERROR", error: event.error });
238
- return;
243
+ clearTimeout(timeoutId);
244
+ if (!response.ok) {
245
+ dispatch({
246
+ type: "SEND_ERROR",
247
+ error: {
248
+ code: "API_ERROR",
249
+ message: `HTTP ${response.status}: ${response.statusText}`,
250
+ retryable: response.status >= 500
251
+ }
252
+ });
253
+ return;
254
+ }
255
+ const reader = response.body?.getReader();
256
+ if (!reader) {
257
+ dispatch({
258
+ type: "SEND_ERROR",
259
+ error: { code: "STREAM_ERROR", message: "No response body", retryable: true }
260
+ });
261
+ return;
262
+ }
263
+ const decoder = new TextDecoder();
264
+ let buffer = "";
265
+ while (true) {
266
+ const { done, value } = await reader.read();
267
+ if (done) break;
268
+ buffer += decoder.decode(value, { stream: true });
269
+ const lines = buffer.split("\n");
270
+ buffer = lines.pop() ?? "";
271
+ for (const line of lines) {
272
+ if (!line.startsWith("data: ")) continue;
273
+ const data = line.slice(6).trim();
274
+ if (data === "[DONE]") continue;
275
+ try {
276
+ const event = JSON.parse(data);
277
+ handleEvent(event);
278
+ } catch {
239
279
  }
240
- } catch {
241
280
  }
242
281
  }
243
282
  }
283
+ if (ctx.hadStreamError) return;
244
284
  const assistantMessage = {
245
285
  id: generateMessageId(),
246
286
  role: "assistant",
247
- content: agentResponse?.message ?? accumulatedContent,
248
- response: agentResponse ?? void 0,
249
- agent: capturedAgent ?? void 0,
287
+ content: ctx.agentResponse?.message ?? ctx.accumulatedContent,
288
+ response: ctx.agentResponse ?? void 0,
289
+ agent: ctx.capturedAgent ?? void 0,
250
290
  timestamp: /* @__PURE__ */ new Date()
251
291
  };
252
292
  dispatch({
253
293
  type: "SEND_SUCCESS",
254
294
  message: assistantMessage,
255
- streamingContent: accumulatedContent,
256
- conversationId: capturedConversationId
295
+ streamingContent: ctx.accumulatedContent,
296
+ conversationId: ctx.capturedConversationId
257
297
  });
258
298
  } catch (err) {
259
299
  clearTimeout(timeoutId);
@@ -284,7 +324,8 @@ function useAgentChat(config2) {
284
324
  }, []);
285
325
  const submitFeedback = (0, import_react.useCallback)(
286
326
  async (messageId, rating, comment) => {
287
- const { apiUrl, feedbackPath = "/feedback", headers = {} } = configRef.current;
327
+ const { apiUrl, feedbackPath = "/feedback", headers: headersOrFn } = configRef.current;
328
+ const headers = typeof headersOrFn === "function" ? await headersOrFn() : headersOrFn ?? {};
288
329
  await fetch(`${apiUrl}${feedbackPath}`, {
289
330
  method: "POST",
290
331
  headers: { "Content-Type": "application/json", ...headers },
@@ -295,12 +336,13 @@ function useAgentChat(config2) {
295
336
  );
296
337
  const retry = (0, import_react.useCallback)(async () => {
297
338
  if (lastUserMessageRef.current) {
298
- await sendMessage(lastUserMessageRef.current);
339
+ await sendMessage(lastUserMessageRef.current, lastUserAttachmentsRef.current);
299
340
  }
300
341
  }, [sendMessage]);
301
342
  const reset = (0, import_react.useCallback)(() => {
302
343
  dispatch({ type: "RESET" });
303
344
  lastUserMessageRef.current = null;
345
+ lastUserAttachmentsRef.current = void 0;
304
346
  }, []);
305
347
  const actions = {
306
348
  sendMessage,
@@ -315,7 +357,7 @@ function useAgentChat(config2) {
315
357
 
316
358
  // src/chat/MessageThread/MessageThread.tsx
317
359
  var import_tailwind_merge5 = require("tailwind-merge");
318
- var import_react3 = require("react");
360
+ var import_react4 = require("react");
319
361
 
320
362
  // src/chat/MessageBubble/MessageBubble.tsx
321
363
  var import_tailwind_merge4 = require("tailwind-merge");
@@ -324,6 +366,7 @@ var import_tailwind_merge4 = require("tailwind-merge");
324
366
  var import_core2 = require("@surf-kit/core");
325
367
 
326
368
  // src/response/ResponseMessage/ResponseMessage.tsx
369
+ var import_react2 = __toESM(require("react"), 1);
327
370
  var import_react_markdown = __toESM(require("react-markdown"), 1);
328
371
  var import_rehype_sanitize = __toESM(require("rehype-sanitize"), 1);
329
372
  var import_tailwind_merge = require("tailwind-merge");
@@ -348,6 +391,7 @@ function ResponseMessage({ content, className }) {
348
391
  "[&_h3]:text-sm [&_h3]:font-semibold [&_h3]:text-accent [&_h3]:mt-2 [&_h3]:mb-1",
349
392
  "[&_code]:bg-surface-raised [&_code]:text-accent [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:rounded [&_code]:text-xs [&_code]:font-mono",
350
393
  "[&_pre]:bg-surface-raised [&_pre]:border [&_pre]:border-border [&_pre]:rounded-xl [&_pre]:p-4 [&_pre]:overflow-x-auto",
394
+ "[&_hr]:my-3 [&_hr]:border-border",
351
395
  "[&_blockquote]:border-l-2 [&_blockquote]:border-border-strong [&_blockquote]:pl-4 [&_blockquote]:text-text-secondary",
352
396
  "[&_a]:text-accent [&_a]:underline-offset-2 [&_a]:hover:text-accent/80",
353
397
  className
@@ -363,11 +407,24 @@ function ResponseMessage({ content, className }) {
363
407
  p: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "my-2", children }),
364
408
  ul: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "my-2 list-disc pl-6", children }),
365
409
  ol: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "my-2 list-decimal pl-6", children }),
366
- li: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { className: "my-1", children }),
410
+ li: ({ children, ...props }) => {
411
+ let content2 = children;
412
+ if (props.ordered) {
413
+ content2 = import_react2.default.Children.map(children, (child, i) => {
414
+ if (i === 0 && typeof child === "string") {
415
+ return child.replace(/^\d+[.)]\s*/, "");
416
+ }
417
+ return child;
418
+ });
419
+ }
420
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { className: "my-1", children: content2 });
421
+ },
367
422
  strong: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { className: "font-semibold", children }),
423
+ em: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("em", { className: "italic text-text-secondary", children }),
368
424
  h1: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { className: "text-base font-bold mt-4 mb-2", children }),
369
425
  h2: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: "text-sm font-bold mt-3 mb-1", children }),
370
426
  h3: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "text-sm font-semibold mt-2 mb-1", children }),
427
+ hr: () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("hr", { className: "my-3 border-border" }),
371
428
  code: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: "bg-surface-sunken rounded px-1 py-0.5 text-xs font-mono", children })
372
429
  },
373
430
  children: normalizeMarkdownLists(content)
@@ -503,7 +560,14 @@ function renderWarning(data) {
503
560
  }
504
561
  );
505
562
  }
506
- function StructuredResponse({ uiHint, data, className }) {
563
+ function StructuredResponse({ uiHint, data: rawData, className }) {
564
+ const data = typeof rawData === "string" ? (() => {
565
+ try {
566
+ return JSON.parse(rawData);
567
+ } catch {
568
+ return null;
569
+ }
570
+ })() : rawData;
507
571
  if (!data) return null;
508
572
  let content;
509
573
  switch (uiHint) {
@@ -533,7 +597,7 @@ function StructuredResponse({ uiHint, data, className }) {
533
597
  }
534
598
 
535
599
  // src/sources/SourceList/SourceList.tsx
536
- var import_react2 = require("react");
600
+ var import_react3 = require("react");
537
601
 
538
602
  // src/sources/SourceCard/SourceCard.tsx
539
603
  var import_core = require("@surf-kit/core");
@@ -583,7 +647,36 @@ function SourceCard({ source, variant = "compact", onNavigate, className }) {
583
647
  children: [
584
648
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-start justify-between gap-2", children: [
585
649
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex-1 min-w-0", children: [
586
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-sm font-medium text-text-primary truncate", children: source.title }),
650
+ source.url ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
651
+ "a",
652
+ {
653
+ href: source.url,
654
+ target: "_blank",
655
+ rel: "noopener noreferrer",
656
+ className: "text-sm font-medium text-accent hover:underline truncate block",
657
+ onClick: (e) => e.stopPropagation(),
658
+ children: [
659
+ source.title,
660
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
661
+ "svg",
662
+ {
663
+ className: "inline-block ml-1 w-3 h-3 opacity-60",
664
+ viewBox: "0 0 24 24",
665
+ fill: "none",
666
+ stroke: "currentColor",
667
+ strokeWidth: "2",
668
+ strokeLinecap: "round",
669
+ strokeLinejoin: "round",
670
+ children: [
671
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6" }),
672
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { points: "15 3 21 3 21 9" }),
673
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "10", y1: "14", x2: "21", y2: "3" })
674
+ ]
675
+ }
676
+ )
677
+ ]
678
+ }
679
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-sm font-medium text-text-primary truncate", children: source.title }),
587
680
  source.section && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-[11px] font-semibold uppercase tracking-wider text-text-secondary truncate mt-0.5", children: source.section })
588
681
  ] }),
589
682
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
@@ -613,7 +706,7 @@ function SourceList({
613
706
  onNavigate,
614
707
  className
615
708
  }) {
616
- const [isExpanded, setIsExpanded] = (0, import_react2.useState)(defaultExpanded);
709
+ const [isExpanded, setIsExpanded] = (0, import_react3.useState)(defaultExpanded);
617
710
  if (sources.length === 0) return null;
618
711
  const content = /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "flex flex-col gap-1.5", "data-testid": "source-list-items", children: sources.map((source) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
619
712
  SourceCard,
@@ -717,13 +810,16 @@ function AgentResponse({
717
810
  }) {
718
811
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: `flex flex-col gap-4 ${className ?? ""}`, "data-testid": "agent-response", children: [
719
812
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ResponseMessage, { content: response.message }),
720
- response.ui_hint !== "text" && response.structured_data && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
721
- StructuredResponse,
722
- {
723
- uiHint: response.ui_hint,
724
- data: response.structured_data
725
- }
726
- ),
813
+ response.ui_hint !== "text" && response.structured_data && (() => {
814
+ const parsed = typeof response.structured_data === "string" ? (() => {
815
+ try {
816
+ return JSON.parse(response.structured_data);
817
+ } catch {
818
+ return null;
819
+ }
820
+ })() : response.structured_data;
821
+ return parsed ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StructuredResponse, { uiHint: response.ui_hint, data: parsed }) : null;
822
+ })(),
727
823
  (showConfidence || showVerification) && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-wrap items-center gap-2 mt-1", "data-testid": "response-meta", children: [
728
824
  showConfidence && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
729
825
  import_core2.Badge,
@@ -774,6 +870,31 @@ function AgentResponse({
774
870
 
775
871
  // src/chat/MessageBubble/MessageBubble.tsx
776
872
  var import_jsx_runtime7 = require("react/jsx-runtime");
873
+ function DocumentIcon() {
874
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
875
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("path", { d: "M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z" }),
876
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("polyline", { points: "14 2 14 8 20 8" }),
877
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: "16", y1: "13", x2: "8", y2: "13" }),
878
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: "16", y1: "17", x2: "8", y2: "17" })
879
+ ] });
880
+ }
881
+ function AttachmentThumbnail({ attachment }) {
882
+ const isImage = attachment.content_type.startsWith("image/");
883
+ if (isImage) {
884
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "rounded-lg overflow-hidden border border-black/10 max-w-[240px]", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
885
+ "img",
886
+ {
887
+ src: attachment.preview_url ?? `data:${attachment.content_type};base64,${attachment.data}`,
888
+ alt: attachment.filename,
889
+ className: "max-w-full max-h-[200px] object-contain"
890
+ }
891
+ ) });
892
+ }
893
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex items-center gap-2 px-3 py-2 rounded-lg border border-black/10 bg-black/5", children: [
894
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(DocumentIcon, {}),
895
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-xs truncate max-w-[160px]", children: attachment.filename })
896
+ ] });
897
+ }
777
898
  function MessageBubble({
778
899
  message,
779
900
  showAgent,
@@ -781,23 +902,29 @@ function MessageBubble({
781
902
  showConfidence = true,
782
903
  showVerification = true,
783
904
  animated = true,
905
+ userBubbleClassName,
784
906
  className
785
907
  }) {
786
908
  const isUser = message.role === "user";
909
+ const hasAttachments = message.attachments && message.attachments.length > 0;
787
910
  if (isUser) {
788
911
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
789
912
  "div",
790
913
  {
791
914
  "data-message-id": message.id,
792
915
  className: (0, import_tailwind_merge4.twMerge)("flex w-full justify-end", className),
793
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
916
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
794
917
  "div",
795
918
  {
796
919
  className: (0, import_tailwind_merge4.twMerge)(
797
- "max-w-[70%] rounded-[18px] rounded-br-[4px] px-4 py-2.5 bg-accent text-brand-cream break-words whitespace-pre-wrap text-sm leading-relaxed",
798
- animated && "motion-safe:animate-slideFromRight"
920
+ "max-w-[70%] rounded-[18px] rounded-br-[4px] px-4 py-2.5 bg-[#e8e8e8] text-[#1a1a1a] break-words whitespace-pre-wrap text-sm leading-relaxed",
921
+ animated && "motion-safe:animate-slideFromRight",
922
+ userBubbleClassName
799
923
  ),
800
- children: message.content
924
+ children: [
925
+ hasAttachments && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex flex-wrap gap-2 mb-2", children: message.attachments.map((att, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(AttachmentThumbnail, { attachment: att }, `${att.filename}-${i}`)) }),
926
+ message.content
927
+ ]
801
928
  }
802
929
  )
803
930
  }
@@ -809,7 +936,7 @@ function MessageBubble({
809
936
  "data-message-id": message.id,
810
937
  className: (0, import_tailwind_merge4.twMerge)("flex w-full flex-col items-start gap-1.5", className),
811
938
  children: [
812
- showAgent && message.agent && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-[11px] font-semibold uppercase tracking-[0.08em] text-text-muted px-1", children: message.agent.replace("_agent", "").replace("_", " ") }),
939
+ showAgent && message.agent && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-[11px] font-display font-semibold uppercase tracking-[0.08em] text-text-muted px-1", children: message.agent.replace("_agent", "").replace("_", " ") }),
813
940
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
814
941
  "div",
815
942
  {
@@ -835,34 +962,70 @@ function MessageBubble({
835
962
 
836
963
  // src/chat/MessageThread/MessageThread.tsx
837
964
  var import_jsx_runtime8 = require("react/jsx-runtime");
838
- function MessageThread({ messages, streamingSlot, showSources, showConfidence, showVerification, className }) {
839
- const bottomRef = (0, import_react3.useRef)(null);
840
- (0, import_react3.useEffect)(() => {
841
- bottomRef.current?.scrollIntoView?.({ behavior: "smooth" });
842
- }, [messages.length, streamingSlot]);
965
+ function MessageThread({ messages, streamingSlot, showAgent, showSources, showConfidence, showVerification, hideLastAssistant, userBubbleClassName, className }) {
966
+ const scrollRef = (0, import_react4.useRef)(null);
967
+ const isNearBottom = (0, import_react4.useRef)(true);
968
+ const isProgrammaticScroll = (0, import_react4.useRef)(false);
969
+ const hasStreaming = !!streamingSlot;
970
+ const scrollToBottom = (0, import_react4.useCallback)(() => {
971
+ const el = scrollRef.current;
972
+ if (el && isNearBottom.current) {
973
+ isProgrammaticScroll.current = true;
974
+ el.scrollTop = el.scrollHeight;
975
+ }
976
+ }, []);
977
+ const handleScroll = (0, import_react4.useCallback)(() => {
978
+ if (isProgrammaticScroll.current) {
979
+ isProgrammaticScroll.current = false;
980
+ return;
981
+ }
982
+ const el = scrollRef.current;
983
+ if (!el) return;
984
+ isNearBottom.current = el.scrollHeight - el.scrollTop - el.clientHeight < 80;
985
+ }, []);
986
+ (0, import_react4.useEffect)(scrollToBottom, [messages.length, scrollToBottom]);
987
+ (0, import_react4.useEffect)(() => {
988
+ if (!hasStreaming) return;
989
+ let raf;
990
+ const tick = () => {
991
+ scrollToBottom();
992
+ raf = requestAnimationFrame(tick);
993
+ };
994
+ raf = requestAnimationFrame(tick);
995
+ return () => cancelAnimationFrame(raf);
996
+ }, [hasStreaming, scrollToBottom]);
843
997
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
844
998
  "div",
845
999
  {
1000
+ ref: scrollRef,
846
1001
  role: "log",
847
1002
  "aria-live": "polite",
848
1003
  "aria-label": "Message thread",
1004
+ onScroll: handleScroll,
849
1005
  className: (0, import_tailwind_merge5.twMerge)(
850
1006
  "flex flex-col gap-4 overflow-y-auto flex-1 px-4 py-6",
851
1007
  className
852
1008
  ),
853
1009
  children: [
854
- messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
855
- MessageBubble,
856
- {
857
- message,
858
- showSources,
859
- showConfidence,
860
- showVerification
861
- },
862
- message.id
863
- )),
864
- streamingSlot,
865
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { ref: bottomRef })
1010
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex-1 shrink-0" }),
1011
+ messages.map((message, i) => {
1012
+ if (hideLastAssistant && i === messages.length - 1 && message.role === "assistant") {
1013
+ return null;
1014
+ }
1015
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1016
+ MessageBubble,
1017
+ {
1018
+ message,
1019
+ showAgent,
1020
+ showSources,
1021
+ showConfidence,
1022
+ showVerification,
1023
+ userBubbleClassName
1024
+ },
1025
+ message.id
1026
+ );
1027
+ }),
1028
+ streamingSlot
866
1029
  ]
867
1030
  }
868
1031
  );
@@ -870,32 +1033,126 @@ function MessageThread({ messages, streamingSlot, showSources, showConfidence, s
870
1033
 
871
1034
  // src/chat/MessageComposer/MessageComposer.tsx
872
1035
  var import_tailwind_merge6 = require("tailwind-merge");
873
- var import_react4 = require("react");
1036
+ var import_react5 = require("react");
874
1037
  var import_jsx_runtime9 = require("react/jsx-runtime");
1038
+ var ALLOWED_TYPES = /* @__PURE__ */ new Set([
1039
+ "image/png",
1040
+ "image/jpeg",
1041
+ "image/gif",
1042
+ "image/webp",
1043
+ "application/pdf"
1044
+ ]);
1045
+ var MAX_FILE_SIZE = 10 * 1024 * 1024;
1046
+ var MAX_ATTACHMENTS = 5;
1047
+ function ArrowUpIcon() {
1048
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
1049
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M10 16V4" }),
1050
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M4 10l6-6 6 6" })
1051
+ ] });
1052
+ }
1053
+ function StopIcon() {
1054
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("rect", { x: "3", y: "3", width: "10", height: "10", rx: "2" }) });
1055
+ }
1056
+ function PaperclipIcon() {
1057
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48" }) });
1058
+ }
1059
+ function XIcon({ size = 14 }) {
1060
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
1061
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M18 6L6 18" }),
1062
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M6 6l12 12" })
1063
+ ] });
1064
+ }
1065
+ function DocumentIcon2() {
1066
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
1067
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z" }),
1068
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("polyline", { points: "14 2 14 8 20 8" }),
1069
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: "16", y1: "13", x2: "8", y2: "13" }),
1070
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("line", { x1: "16", y1: "17", x2: "8", y2: "17" }),
1071
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("polyline", { points: "10 9 9 9 8 9" })
1072
+ ] });
1073
+ }
1074
+ function fileToBase64(file) {
1075
+ return new Promise((resolve, reject) => {
1076
+ const reader = new FileReader();
1077
+ reader.onload = () => {
1078
+ const result = reader.result;
1079
+ const base64 = result.split(",")[1];
1080
+ resolve(base64);
1081
+ };
1082
+ reader.onerror = reject;
1083
+ reader.readAsDataURL(file);
1084
+ });
1085
+ }
1086
+ function AttachmentPreview({
1087
+ attachment,
1088
+ onRemove
1089
+ }) {
1090
+ const isImage = attachment.content_type.startsWith("image/");
1091
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "relative group flex-shrink-0", children: [
1092
+ isImage ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "w-16 h-16 rounded-lg overflow-hidden border border-border/60 bg-surface-alt", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1093
+ "img",
1094
+ {
1095
+ src: attachment.preview_url ?? `data:${attachment.content_type};base64,${attachment.data}`,
1096
+ alt: attachment.filename,
1097
+ className: "w-full h-full object-cover"
1098
+ }
1099
+ ) }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "h-16 px-3 rounded-lg border border-border/60 bg-surface-alt flex items-center gap-2", children: [
1100
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "text-text-muted", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(DocumentIcon2, {}) }),
1101
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex flex-col min-w-0", children: [
1102
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-xs text-text-primary truncate max-w-[120px]", children: attachment.filename }),
1103
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-[10px] text-text-muted", children: "PDF" })
1104
+ ] })
1105
+ ] }),
1106
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1107
+ "button",
1108
+ {
1109
+ type: "button",
1110
+ onClick: onRemove,
1111
+ className: (0, import_tailwind_merge6.twMerge)(
1112
+ "absolute -top-1.5 -right-1.5",
1113
+ "w-5 h-5 rounded-full",
1114
+ "bg-text-muted/80 text-white",
1115
+ "flex items-center justify-center",
1116
+ "opacity-0 group-hover:opacity-100",
1117
+ "transition-opacity duration-150",
1118
+ "hover:bg-text-primary"
1119
+ ),
1120
+ "aria-label": `Remove ${attachment.filename}`,
1121
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(XIcon, { size: 10 })
1122
+ }
1123
+ )
1124
+ ] });
1125
+ }
875
1126
  function MessageComposer({
876
1127
  onSend,
877
1128
  isLoading = false,
878
1129
  placeholder = "Type a message...",
879
1130
  className
880
1131
  }) {
881
- const [value, setValue] = (0, import_react4.useState)("");
882
- const textareaRef = (0, import_react4.useRef)(null);
883
- const canSend = value.trim().length > 0 && !isLoading;
884
- const resetHeight = (0, import_react4.useCallback)(() => {
1132
+ const [value, setValue] = (0, import_react5.useState)("");
1133
+ const [attachments, setAttachments] = (0, import_react5.useState)([]);
1134
+ const [dragOver, setDragOver] = (0, import_react5.useState)(false);
1135
+ const textareaRef = (0, import_react5.useRef)(null);
1136
+ const fileInputRef = (0, import_react5.useRef)(null);
1137
+ const canSend = (value.trim().length > 0 || attachments.length > 0) && !isLoading;
1138
+ const resetHeight = (0, import_react5.useCallback)(() => {
885
1139
  const el = textareaRef.current;
886
1140
  if (el) {
887
1141
  el.style.height = "auto";
888
1142
  el.style.overflowY = "hidden";
889
1143
  }
890
1144
  }, []);
891
- const handleSend = (0, import_react4.useCallback)(() => {
1145
+ const handleSend = (0, import_react5.useCallback)(() => {
892
1146
  if (!canSend) return;
893
- onSend(value.trim());
1147
+ const message = value.trim() || (attachments.length > 0 ? "Please analyse the attached file(s)." : "");
1148
+ if (!message && attachments.length === 0) return;
1149
+ onSend(message, attachments.length > 0 ? attachments : void 0);
894
1150
  setValue("");
1151
+ setAttachments([]);
895
1152
  resetHeight();
896
1153
  textareaRef.current?.focus();
897
- }, [canSend, onSend, value, resetHeight]);
898
- const handleKeyDown = (0, import_react4.useCallback)(
1154
+ }, [canSend, onSend, value, attachments, resetHeight]);
1155
+ const handleKeyDown = (0, import_react5.useCallback)(
899
1156
  (e) => {
900
1157
  if (e.key === "Enter" && !e.shiftKey) {
901
1158
  e.preventDefault();
@@ -904,64 +1161,194 @@ function MessageComposer({
904
1161
  },
905
1162
  [handleSend]
906
1163
  );
907
- const handleChange = (0, import_react4.useCallback)(
1164
+ const handleChange = (0, import_react5.useCallback)(
908
1165
  (e) => {
909
1166
  setValue(e.target.value);
910
1167
  const el = e.target;
911
1168
  el.style.height = "auto";
912
- const capped = Math.min(el.scrollHeight, 128);
1169
+ const capped = Math.min(el.scrollHeight, 200);
913
1170
  el.style.height = `${capped}px`;
914
- el.style.overflowY = el.scrollHeight > 128 ? "auto" : "hidden";
1171
+ el.style.overflowY = el.scrollHeight > 200 ? "auto" : "hidden";
915
1172
  },
916
1173
  []
917
1174
  );
1175
+ const addFiles = (0, import_react5.useCallback)(async (files) => {
1176
+ const fileArray = Array.from(files);
1177
+ for (const file of fileArray) {
1178
+ if (attachments.length >= MAX_ATTACHMENTS) break;
1179
+ if (!ALLOWED_TYPES.has(file.type)) continue;
1180
+ if (file.size > MAX_FILE_SIZE) continue;
1181
+ try {
1182
+ const data = await fileToBase64(file);
1183
+ const previewUrl = file.type.startsWith("image/") ? URL.createObjectURL(file) : void 0;
1184
+ const attachment = {
1185
+ filename: file.name,
1186
+ content_type: file.type,
1187
+ data,
1188
+ preview_url: previewUrl
1189
+ };
1190
+ setAttachments((prev) => {
1191
+ if (prev.length >= MAX_ATTACHMENTS) return prev;
1192
+ return [...prev, attachment];
1193
+ });
1194
+ } catch {
1195
+ }
1196
+ }
1197
+ }, [attachments.length]);
1198
+ const handleFileSelect = (0, import_react5.useCallback)(() => {
1199
+ fileInputRef.current?.click();
1200
+ }, []);
1201
+ const handleFileInputChange = (0, import_react5.useCallback)(
1202
+ (e) => {
1203
+ if (e.target.files) {
1204
+ void addFiles(e.target.files);
1205
+ e.target.value = "";
1206
+ }
1207
+ },
1208
+ [addFiles]
1209
+ );
1210
+ const removeAttachment = (0, import_react5.useCallback)((index) => {
1211
+ setAttachments((prev) => {
1212
+ const removed = prev[index];
1213
+ if (removed?.preview_url) URL.revokeObjectURL(removed.preview_url);
1214
+ return prev.filter((_, i) => i !== index);
1215
+ });
1216
+ }, []);
1217
+ const handlePaste = (0, import_react5.useCallback)(
1218
+ (e) => {
1219
+ const items = e.clipboardData.items;
1220
+ const files = [];
1221
+ for (const item of items) {
1222
+ if (item.kind === "file" && ALLOWED_TYPES.has(item.type)) {
1223
+ const file = item.getAsFile();
1224
+ if (file) files.push(file);
1225
+ }
1226
+ }
1227
+ if (files.length > 0) {
1228
+ void addFiles(files);
1229
+ }
1230
+ },
1231
+ [addFiles]
1232
+ );
1233
+ const handleDragOver = (0, import_react5.useCallback)((e) => {
1234
+ e.preventDefault();
1235
+ e.stopPropagation();
1236
+ setDragOver(true);
1237
+ }, []);
1238
+ const handleDragLeave = (0, import_react5.useCallback)((e) => {
1239
+ e.preventDefault();
1240
+ e.stopPropagation();
1241
+ setDragOver(false);
1242
+ }, []);
1243
+ const handleDrop = (0, import_react5.useCallback)(
1244
+ (e) => {
1245
+ e.preventDefault();
1246
+ e.stopPropagation();
1247
+ setDragOver(false);
1248
+ if (e.dataTransfer.files.length > 0) {
1249
+ void addFiles(e.dataTransfer.files);
1250
+ }
1251
+ },
1252
+ [addFiles]
1253
+ );
918
1254
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
919
1255
  "div",
920
1256
  {
921
1257
  className: (0, import_tailwind_merge6.twMerge)(
922
- "flex items-end gap-3 shrink-0 border-t border-border px-4 py-3",
1258
+ "relative shrink-0 rounded-3xl border bg-surface",
1259
+ "shadow-lg shadow-black/10",
1260
+ "transition-all duration-200",
1261
+ "focus-within:border-accent/40 focus-within:shadow-accent/5",
1262
+ dragOver ? "border-accent/60 bg-accent/5" : "border-border/60",
923
1263
  className
924
1264
  ),
1265
+ onDragOver: handleDragOver,
1266
+ onDragLeave: handleDragLeave,
1267
+ onDrop: handleDrop,
925
1268
  children: [
926
1269
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
927
- "textarea",
1270
+ "input",
928
1271
  {
929
- ref: textareaRef,
930
- value,
931
- onChange: handleChange,
932
- onKeyDown: handleKeyDown,
933
- placeholder,
934
- rows: 1,
935
- disabled: isLoading,
936
- className: (0, import_tailwind_merge6.twMerge)(
937
- "flex-1 resize-none rounded-xl border border-border bg-surface/80",
938
- "px-4 py-2.5 text-sm text-text-primary placeholder:text-text-muted",
939
- "focus:border-transparent focus:ring-2 focus:ring-accent/40 focus:outline-none",
940
- "disabled:opacity-50 disabled:cursor-not-allowed",
941
- "overflow-hidden",
942
- "transition-all duration-200"
943
- ),
944
- style: { colorScheme: "dark" },
945
- "aria-label": "Message input"
1272
+ ref: fileInputRef,
1273
+ type: "file",
1274
+ multiple: true,
1275
+ accept: "image/png,image/jpeg,image/gif,image/webp,application/pdf",
1276
+ onChange: handleFileInputChange,
1277
+ className: "hidden",
1278
+ "aria-hidden": "true"
946
1279
  }
947
1280
  ),
948
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
949
- "button",
1281
+ attachments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex gap-2 px-4 pt-3 pb-1 overflow-x-auto", children: attachments.map((att, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1282
+ AttachmentPreview,
950
1283
  {
951
- type: "button",
952
- onClick: handleSend,
953
- disabled: !value.trim() || isLoading,
954
- "aria-label": "Send message",
955
- className: (0, import_tailwind_merge6.twMerge)(
956
- "inline-flex items-center justify-center rounded-xl px-5 py-2.5",
957
- "text-sm font-semibold text-white shrink-0",
958
- "transition-all duration-200",
959
- "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
960
- value.trim() && !isLoading ? "bg-accent hover:bg-accent-hover hover:scale-[1.02] hover:shadow-glow-cyan active:scale-[0.98]" : "bg-accent/30 text-text-muted cursor-not-allowed"
961
- ),
962
- children: "Send"
963
- }
964
- )
1284
+ attachment: att,
1285
+ onRemove: () => removeAttachment(i)
1286
+ },
1287
+ `${att.filename}-${i}`
1288
+ )) }),
1289
+ dragOver && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "absolute inset-0 rounded-3xl flex items-center justify-center bg-accent/10 border-2 border-dashed border-accent/40 z-10 pointer-events-none", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-sm font-display font-semibold text-accent", children: "Drop files here" }) }),
1290
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-end", children: [
1291
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1292
+ "button",
1293
+ {
1294
+ type: "button",
1295
+ onClick: handleFileSelect,
1296
+ disabled: isLoading || attachments.length >= MAX_ATTACHMENTS,
1297
+ "aria-label": "Attach file",
1298
+ className: (0, import_tailwind_merge6.twMerge)(
1299
+ "flex-shrink-0 ml-2 mb-3",
1300
+ "inline-flex items-center justify-center",
1301
+ "w-9 h-9 rounded-full",
1302
+ "transition-all duration-200",
1303
+ "text-text-muted/60 hover:text-text-secondary hover:bg-text-muted/10",
1304
+ "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
1305
+ "disabled:opacity-30 disabled:cursor-not-allowed disabled:hover:bg-transparent"
1306
+ ),
1307
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PaperclipIcon, {})
1308
+ }
1309
+ ),
1310
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1311
+ "textarea",
1312
+ {
1313
+ ref: textareaRef,
1314
+ value,
1315
+ onChange: handleChange,
1316
+ onKeyDown: handleKeyDown,
1317
+ onPaste: handlePaste,
1318
+ placeholder,
1319
+ rows: 1,
1320
+ disabled: isLoading,
1321
+ className: (0, import_tailwind_merge6.twMerge)(
1322
+ "flex-1 resize-none bg-transparent",
1323
+ "pl-2 pr-14 pt-4 pb-4 text-[15px] leading-relaxed",
1324
+ "text-text-primary placeholder:text-text-muted/70",
1325
+ "focus:outline-none",
1326
+ "disabled:opacity-50 disabled:cursor-not-allowed",
1327
+ "overflow-hidden"
1328
+ ),
1329
+ style: { colorScheme: "dark" },
1330
+ "aria-label": "Message input"
1331
+ }
1332
+ ),
1333
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1334
+ "button",
1335
+ {
1336
+ type: "button",
1337
+ onClick: handleSend,
1338
+ disabled: !canSend,
1339
+ "aria-label": "Send message",
1340
+ className: (0, import_tailwind_merge6.twMerge)(
1341
+ "absolute bottom-3 right-3",
1342
+ "inline-flex items-center justify-center",
1343
+ "w-9 h-9 rounded-full",
1344
+ "transition-all duration-200",
1345
+ "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
1346
+ canSend ? "bg-accent text-white hover:bg-accent-hover active:scale-90 shadow-md shadow-accent/25" : isLoading ? "bg-text-muted/20 text-text-secondary hover:bg-text-muted/30" : "bg-transparent text-text-muted/40 cursor-default"
1347
+ ),
1348
+ children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StopIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ArrowUpIcon, {})
1349
+ }
1350
+ )
1351
+ ] })
965
1352
  ]
966
1353
  }
967
1354
  );
@@ -974,6 +1361,7 @@ function WelcomeScreen({
974
1361
  title = "Welcome",
975
1362
  message = "How can I help you today?",
976
1363
  icon,
1364
+ iconClassName,
977
1365
  suggestedQuestions = [],
978
1366
  onQuestionSelect,
979
1367
  className
@@ -986,12 +1374,15 @@ function WelcomeScreen({
986
1374
  className
987
1375
  ),
988
1376
  children: [
989
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1377
+ icon ? iconClassName ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: iconClassName, "aria-hidden": "true", children: icon }) : icon : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
990
1378
  "div",
991
1379
  {
992
- className: "w-14 h-14 rounded-2xl bg-accent/10 border border-border flex items-center justify-center pulse-glow",
1380
+ className: (0, import_tailwind_merge7.twMerge)(
1381
+ "w-14 h-14 rounded-2xl bg-accent/10 border border-border flex items-center justify-center pulse-glow",
1382
+ iconClassName
1383
+ ),
993
1384
  "aria-hidden": "true",
994
- children: icon ?? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-2xl", children: "\u2726" })
1385
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-2xl", children: "\u2726" })
995
1386
  }
996
1387
  ),
997
1388
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-2", children: [
@@ -1001,7 +1392,7 @@ function WelcomeScreen({
1001
1392
  suggestedQuestions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1002
1393
  "div",
1003
1394
  {
1004
- className: "flex flex-wrap justify-center gap-2 max-w-md",
1395
+ className: "flex flex-wrap justify-center gap-2 max-w-xl",
1005
1396
  role: "group",
1006
1397
  "aria-label": "Suggested questions",
1007
1398
  children: suggestedQuestions.map((question) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
@@ -1010,7 +1401,7 @@ function WelcomeScreen({
1010
1401
  type: "button",
1011
1402
  onClick: () => onQuestionSelect?.(question),
1012
1403
  className: (0, import_tailwind_merge7.twMerge)(
1013
- "px-4 py-2 rounded-full text-sm",
1404
+ "px-3.5 py-1.5 rounded-full text-[12px]",
1014
1405
  "border border-border bg-transparent text-text-secondary",
1015
1406
  "hover:bg-accent/10 hover:border-interactive hover:text-text-primary",
1016
1407
  "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
@@ -1028,25 +1419,26 @@ function WelcomeScreen({
1028
1419
  }
1029
1420
 
1030
1421
  // src/streaming/StreamingMessage/StreamingMessage.tsx
1031
- var import_react6 = require("react");
1422
+ var import_react7 = require("react");
1423
+ var import_tailwind_merge8 = require("tailwind-merge");
1032
1424
  var import_core3 = require("@surf-kit/core");
1033
1425
 
1034
1426
  // src/hooks/useCharacterDrain.ts
1035
- var import_react5 = require("react");
1427
+ var import_react6 = require("react");
1036
1428
  function useCharacterDrain(target, msPerChar = 15) {
1037
- const [displayed, setDisplayed] = (0, import_react5.useState)("");
1038
- const indexRef = (0, import_react5.useRef)(0);
1039
- const lastTimeRef = (0, import_react5.useRef)(0);
1040
- const rafRef = (0, import_react5.useRef)(null);
1041
- const drainTargetRef = (0, import_react5.useRef)("");
1042
- const msPerCharRef = (0, import_react5.useRef)(msPerChar);
1429
+ const [displayed, setDisplayed] = (0, import_react6.useState)("");
1430
+ const indexRef = (0, import_react6.useRef)(0);
1431
+ const lastTimeRef = (0, import_react6.useRef)(0);
1432
+ const rafRef = (0, import_react6.useRef)(null);
1433
+ const drainTargetRef = (0, import_react6.useRef)("");
1434
+ const msPerCharRef = (0, import_react6.useRef)(msPerChar);
1043
1435
  msPerCharRef.current = msPerChar;
1044
1436
  if (target !== "") {
1045
1437
  drainTargetRef.current = target;
1046
1438
  }
1047
1439
  const drainTarget = drainTargetRef.current;
1048
1440
  const isDraining = displayed.length < drainTarget.length;
1049
- const tickRef = (0, import_react5.useRef)(() => {
1441
+ const tickRef = (0, import_react6.useRef)(() => {
1050
1442
  });
1051
1443
  tickRef.current = (now) => {
1052
1444
  const currentTarget = drainTargetRef.current;
@@ -1058,7 +1450,10 @@ function useCharacterDrain(target, msPerChar = 15) {
1058
1450
  const elapsed = now - lastTimeRef.current;
1059
1451
  const charsToAdvance = Math.floor(elapsed / msPerCharRef.current);
1060
1452
  if (charsToAdvance > 0 && indexRef.current < currentTarget.length) {
1061
- const nextIndex = Math.min(indexRef.current + charsToAdvance, currentTarget.length);
1453
+ let nextIndex = Math.min(indexRef.current + charsToAdvance, currentTarget.length);
1454
+ while (nextIndex < currentTarget.length && currentTarget[nextIndex - 1].trim() === "") {
1455
+ nextIndex++;
1456
+ }
1062
1457
  indexRef.current = nextIndex;
1063
1458
  lastTimeRef.current = now;
1064
1459
  setDisplayed(currentTarget.slice(0, nextIndex));
@@ -1069,12 +1464,12 @@ function useCharacterDrain(target, msPerChar = 15) {
1069
1464
  rafRef.current = null;
1070
1465
  }
1071
1466
  };
1072
- (0, import_react5.useEffect)(() => {
1467
+ (0, import_react6.useEffect)(() => {
1073
1468
  if (drainTargetRef.current !== "" && indexRef.current < drainTargetRef.current.length && rafRef.current === null) {
1074
1469
  rafRef.current = requestAnimationFrame((t) => tickRef.current(t));
1075
1470
  }
1076
1471
  }, [drainTarget]);
1077
- (0, import_react5.useEffect)(() => {
1472
+ (0, import_react6.useEffect)(() => {
1078
1473
  if (target === "" && !isDraining && displayed !== "") {
1079
1474
  indexRef.current = 0;
1080
1475
  lastTimeRef.current = 0;
@@ -1082,7 +1477,7 @@ function useCharacterDrain(target, msPerChar = 15) {
1082
1477
  setDisplayed("");
1083
1478
  }
1084
1479
  }, [target, isDraining, displayed]);
1085
- (0, import_react5.useEffect)(() => {
1480
+ (0, import_react6.useEffect)(() => {
1086
1481
  return () => {
1087
1482
  if (rafRef.current !== null) {
1088
1483
  cancelAnimationFrame(rafRef.current);
@@ -1103,51 +1498,77 @@ var phaseLabels = {
1103
1498
  generating: "Writing...",
1104
1499
  verifying: "Verifying..."
1105
1500
  };
1501
+ var CURSOR_STYLES = `
1502
+ .sk-streaming-cursor > :not(ul,ol,blockquote):last-child::after,
1503
+ .sk-streaming-cursor > :is(ul,ol):last-child > li:last-child::after,
1504
+ .sk-streaming-cursor > blockquote:last-child > p:last-child::after {
1505
+ content: "";
1506
+ display: inline-block;
1507
+ width: 2px;
1508
+ height: 1em;
1509
+ background: var(--color-accent, #38bdf8);
1510
+ animation: sk-cursor-blink 0.8s steps(1) infinite;
1511
+ margin-left: 2px;
1512
+ vertical-align: text-bottom;
1513
+ }
1514
+ @keyframes sk-cursor-blink {
1515
+ 0%, 60% { opacity: 1; }
1516
+ 61%, 100% { opacity: 0; }
1517
+ }
1518
+ `;
1106
1519
  function StreamingMessage({
1107
1520
  stream,
1108
1521
  onComplete,
1522
+ onDraining,
1109
1523
  showPhases = true,
1110
1524
  className
1111
1525
  }) {
1112
- const onCompleteRef = (0, import_react6.useRef)(onComplete);
1526
+ const onCompleteRef = (0, import_react7.useRef)(onComplete);
1113
1527
  onCompleteRef.current = onComplete;
1114
- const wasActiveRef = (0, import_react6.useRef)(stream.active);
1115
- (0, import_react6.useEffect)(() => {
1528
+ const onDrainingRef = (0, import_react7.useRef)(onDraining);
1529
+ onDrainingRef.current = onDraining;
1530
+ const wasActiveRef = (0, import_react7.useRef)(stream.active);
1531
+ (0, import_react7.useEffect)(() => {
1116
1532
  if (wasActiveRef.current && !stream.active) {
1117
1533
  onCompleteRef.current?.();
1118
1534
  }
1119
1535
  wasActiveRef.current = stream.active;
1120
1536
  }, [stream.active]);
1121
1537
  const phaseLabel = phaseLabels[stream.phase];
1122
- const { displayed: displayedContent } = useCharacterDrain(stream.content);
1123
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className, "data-testid": "streaming-message", children: [
1538
+ const { displayed: rawDisplayed, isDraining } = useCharacterDrain(stream.content);
1539
+ const displayedContent = stream.active || isDraining ? rawDisplayed.trimEnd() : rawDisplayed;
1540
+ (0, import_react7.useEffect)(() => {
1541
+ onDrainingRef.current?.(isDraining);
1542
+ }, [isDraining]);
1543
+ const agentLabel = stream.agent ? stream.agent.replace("_agent", "").replace("_", " ") : null;
1544
+ const showPhaseIndicator = showPhases && stream.active && stream.phase !== "idle" && !displayedContent;
1545
+ const showCursor = (stream.active || isDraining) && !!displayedContent;
1546
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: (0, import_tailwind_merge8.twMerge)("flex w-full flex-col items-start", className), "data-testid": "streaming-message", children: [
1124
1547
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { "aria-live": "assertive", className: "sr-only", children: [
1125
1548
  stream.active && stream.phase !== "idle" && "Response started",
1126
1549
  !stream.active && stream.content && "Response complete"
1127
1550
  ] }),
1551
+ showCursor && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("style", { children: CURSOR_STYLES }),
1552
+ agentLabel && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "text-[11px] font-display font-semibold uppercase tracking-[0.08em] text-text-muted px-1 mb-1.5", children: agentLabel }),
1128
1553
  /* @__PURE__ */ (0, import_jsx_runtime11.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: [
1129
- showPhases && stream.active && stream.phase !== "idle" && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1554
+ showPhaseIndicator && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1130
1555
  "div",
1131
1556
  {
1132
- className: "flex items-center gap-2 mb-2 text-sm text-text-secondary",
1557
+ className: "flex items-center gap-2 text-sm text-text-secondary",
1133
1558
  "data-testid": "phase-indicator",
1134
1559
  children: [
1135
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_core3.Spinner, { size: "sm" }) }),
1560
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_core3.WaveLoader, { size: "sm", color: "#38bdf8" }) }),
1136
1561
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: phaseLabel })
1137
1562
  ]
1138
1563
  }
1139
1564
  ),
1140
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "text-sm leading-relaxed text-text-primary whitespace-pre-wrap", children: [
1141
- displayedContent,
1142
- stream.active && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1143
- "span",
1144
- {
1145
- className: "inline-block w-0.5 h-4 bg-accent align-text-bottom animate-pulse ml-0.5",
1146
- "aria-hidden": "true",
1147
- "data-testid": "streaming-cursor"
1148
- }
1149
- )
1150
- ] })
1565
+ displayedContent && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1566
+ ResponseMessage,
1567
+ {
1568
+ content: displayedContent,
1569
+ className: showCursor ? "sk-streaming-cursor" : void 0
1570
+ }
1571
+ )
1151
1572
  ] })
1152
1573
  ] });
1153
1574
  }
@@ -1178,7 +1599,7 @@ function AgentChat({
1178
1599
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1179
1600
  "div",
1180
1601
  {
1181
- className: (0, import_tailwind_merge8.twMerge)(
1602
+ className: (0, import_tailwind_merge9.twMerge)(
1182
1603
  "flex flex-col h-full bg-canvas border border-border rounded-xl overflow-hidden",
1183
1604
  className
1184
1605
  ),
@@ -1350,7 +1771,7 @@ function ConfidenceBadge({ confidence, className }) {
1350
1771
  }
1351
1772
 
1352
1773
  // src/confidence/ConfidenceBreakdown/ConfidenceBreakdown.tsx
1353
- var import_react7 = require("react");
1774
+ var import_react8 = require("react");
1354
1775
 
1355
1776
  // src/confidence/ConfidenceMeter/ConfidenceMeter.tsx
1356
1777
  var import_jsx_runtime18 = require("react/jsx-runtime");
@@ -1405,7 +1826,7 @@ function ConfidenceBreakdown({
1405
1826
  defaultExpanded = false,
1406
1827
  className
1407
1828
  }) {
1408
- const [expanded, setExpanded] = (0, import_react7.useState)(defaultExpanded);
1829
+ const [expanded, setExpanded] = (0, import_react8.useState)(defaultExpanded);
1409
1830
  const isExpanded = expandable ? expanded : true;
1410
1831
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: `rounded-xl border border-border bg-surface ${className ?? ""}`, "data-testid": "confidence-breakdown", children: [
1411
1832
  /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
@@ -1485,7 +1906,7 @@ function VerificationBadge({ verification, className }) {
1485
1906
  }
1486
1907
 
1487
1908
  // src/confidence/VerificationDetail/VerificationDetail.tsx
1488
- var import_react8 = require("react");
1909
+ var import_react9 = require("react");
1489
1910
  var import_jsx_runtime21 = require("react/jsx-runtime");
1490
1911
  function VerificationDetail({
1491
1912
  verification,
@@ -1493,7 +1914,7 @@ function VerificationDetail({
1493
1914
  defaultExpanded = false,
1494
1915
  className
1495
1916
  }) {
1496
- const [expanded, setExpanded] = (0, import_react8.useState)(defaultExpanded);
1917
+ const [expanded, setExpanded] = (0, import_react9.useState)(defaultExpanded);
1497
1918
  const isExpanded = expandable ? expanded : true;
1498
1919
  return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: `rounded-xl border border-border bg-surface ${className ?? ""}`, "data-testid": "verification-detail", children: [
1499
1920
  /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
@@ -1537,11 +1958,11 @@ function VerificationDetail({
1537
1958
  }
1538
1959
 
1539
1960
  // src/hooks/useAgentTheme.ts
1540
- var import_react9 = require("react");
1961
+ var import_react10 = require("react");
1541
1962
  var DEFAULT_ACCENT = "#6366f1";
1542
1963
  var DEFAULT_LABEL = "Agent";
1543
1964
  function useAgentTheme(agentId, agentThemes) {
1544
- return (0, import_react9.useMemo)(() => {
1965
+ return (0, import_react10.useMemo)(() => {
1545
1966
  if (!agentId) {
1546
1967
  return { accent: DEFAULT_ACCENT, icon: null, label: DEFAULT_LABEL };
1547
1968
  }
@@ -1697,7 +2118,7 @@ function ToolExecution({ tool, label, className }) {
1697
2118
  role: "status",
1698
2119
  "data-testid": "tool-execution",
1699
2120
  children: [
1700
- /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_core10.Spinner, { size: "sm" }) }),
2121
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_core10.WaveLoader, { size: "sm", color: "#38bdf8" }) }),
1701
2122
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { children: displayLabel })
1702
2123
  ]
1703
2124
  }
@@ -1717,7 +2138,7 @@ function RetrievalProgress({ sources, isActive, className }) {
1717
2138
  "data-testid": "retrieval-progress",
1718
2139
  children: [
1719
2140
  isActive && /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex items-center gap-2 text-sm text-text-secondary", children: [
1720
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_core11.Spinner, { size: "sm" }) }),
2141
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_core11.WaveLoader, { size: "sm", color: "#38bdf8" }) }),
1721
2142
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { children: "Retrieving sources..." })
1722
2143
  ] }),
1723
2144
  sources.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("ul", { className: "space-y-1", "data-testid": "source-list", children: sources.map((source, index) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
@@ -1774,7 +2195,7 @@ function VerificationProgress({
1774
2195
  }
1775
2196
 
1776
2197
  // src/streaming/TypewriterText/TypewriterText.tsx
1777
- var import_react10 = require("react");
2198
+ var import_react11 = require("react");
1778
2199
  var import_jsx_runtime30 = require("react/jsx-runtime");
1779
2200
  function TypewriterText({
1780
2201
  text,
@@ -1784,9 +2205,9 @@ function TypewriterText({
1784
2205
  className = "",
1785
2206
  showCursor = true
1786
2207
  }) {
1787
- const [displayedText, setDisplayedText] = (0, import_react10.useState)("");
1788
- const [isComplete, setIsComplete] = (0, import_react10.useState)(false);
1789
- (0, import_react10.useEffect)(() => {
2208
+ const [displayedText, setDisplayedText] = (0, import_react11.useState)("");
2209
+ const [isComplete, setIsComplete] = (0, import_react11.useState)(false);
2210
+ (0, import_react11.useEffect)(() => {
1790
2211
  setDisplayedText("");
1791
2212
  setIsComplete(false);
1792
2213
  let index = 0;
@@ -1815,7 +2236,7 @@ function TypewriterText({
1815
2236
  }
1816
2237
 
1817
2238
  // src/streaming/TypingIndicator/TypingIndicator.tsx
1818
- var import_tailwind_merge9 = require("tailwind-merge");
2239
+ var import_tailwind_merge10 = require("tailwind-merge");
1819
2240
  var import_hooks2 = require("@surf-kit/hooks");
1820
2241
  var import_jsx_runtime31 = require("react/jsx-runtime");
1821
2242
  var bounceKeyframes = `
@@ -1835,7 +2256,7 @@ function TypingIndicator({
1835
2256
  {
1836
2257
  role: "status",
1837
2258
  "aria-label": label ?? "typing",
1838
- className: (0, import_tailwind_merge9.twMerge)("inline-flex items-center gap-2", className),
2259
+ className: (0, import_tailwind_merge10.twMerge)("inline-flex items-center gap-2", className),
1839
2260
  "data-testid": "typing-indicator",
1840
2261
  children: [
1841
2262
  !reducedMotion && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("style", { children: bounceKeyframes }),
@@ -1857,7 +2278,7 @@ function TypingIndicator({
1857
2278
  }
1858
2279
 
1859
2280
  // src/streaming/TextGlimmer/TextGlimmer.tsx
1860
- var import_tailwind_merge10 = require("tailwind-merge");
2281
+ var import_tailwind_merge11 = require("tailwind-merge");
1861
2282
  var import_hooks3 = require("@surf-kit/hooks");
1862
2283
  var import_jsx_runtime32 = require("react/jsx-runtime");
1863
2284
  var shimmerKeyframes = `
@@ -1874,7 +2295,7 @@ function TextGlimmer({ lines = 3, className }) {
1874
2295
  {
1875
2296
  role: "status",
1876
2297
  "aria-label": "Loading",
1877
- className: (0, import_tailwind_merge10.twMerge)("flex flex-col gap-2", className),
2298
+ className: (0, import_tailwind_merge11.twMerge)("flex flex-col gap-2", className),
1878
2299
  "data-testid": "text-glimmer",
1879
2300
  children: [
1880
2301
  !reducedMotion && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("style", { children: shimmerKeyframes }),
@@ -1900,7 +2321,7 @@ function TextGlimmer({ lines = 3, className }) {
1900
2321
  }
1901
2322
 
1902
2323
  // src/streaming/StreamingList/StreamingList.tsx
1903
- var import_tailwind_merge11 = require("tailwind-merge");
2324
+ var import_tailwind_merge12 = require("tailwind-merge");
1904
2325
  var import_hooks4 = require("@surf-kit/hooks");
1905
2326
  var import_jsx_runtime33 = require("react/jsx-runtime");
1906
2327
  var fadeSlideInKeyframes = `
@@ -1918,13 +2339,13 @@ function StreamingList({
1918
2339
  }) {
1919
2340
  const reducedMotion = (0, import_hooks4.useReducedMotion)();
1920
2341
  if (items.length === 0 && !isStreaming) {
1921
- return emptyMessage ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { className: (0, import_tailwind_merge11.twMerge)("text-sm text-text-secondary", className), "data-testid": "streaming-list-empty", children: emptyMessage }) : null;
2342
+ return emptyMessage ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { className: (0, import_tailwind_merge12.twMerge)("text-sm text-text-secondary", className), "data-testid": "streaming-list-empty", children: emptyMessage }) : null;
1922
2343
  }
1923
2344
  return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
1924
2345
  "ul",
1925
2346
  {
1926
2347
  "aria-live": "polite",
1927
- className: (0, import_tailwind_merge11.twMerge)("list-none p-0 m-0", className),
2348
+ className: (0, import_tailwind_merge12.twMerge)("list-none p-0 m-0", className),
1928
2349
  "data-testid": "streaming-list",
1929
2350
  children: [
1930
2351
  !reducedMotion && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("style", { children: fadeSlideInKeyframes }),
@@ -1944,7 +2365,7 @@ function StreamingList({
1944
2365
  }
1945
2366
 
1946
2367
  // src/streaming/StreamingStructure/StreamingStructure.tsx
1947
- var import_tailwind_merge12 = require("tailwind-merge");
2368
+ var import_tailwind_merge13 = require("tailwind-merge");
1948
2369
  var import_hooks5 = require("@surf-kit/hooks");
1949
2370
  var import_jsx_runtime34 = require("react/jsx-runtime");
1950
2371
  var fadeSlideInKeyframes2 = `
@@ -1993,7 +2414,7 @@ function StreamingStructure({
1993
2414
  "dl",
1994
2415
  {
1995
2416
  "aria-live": "polite",
1996
- className: (0, import_tailwind_merge12.twMerge)("m-0", className),
2417
+ className: (0, import_tailwind_merge13.twMerge)("m-0", className),
1997
2418
  "data-testid": "streaming-structure",
1998
2419
  children: [
1999
2420
  !reducedMotion && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("style", { children: fadeSlideInKeyframes2 }),
@@ -2016,7 +2437,7 @@ function StreamingStructure({
2016
2437
  }
2017
2438
 
2018
2439
  // src/chat/ConversationList/ConversationList.tsx
2019
- var import_tailwind_merge13 = require("tailwind-merge");
2440
+ var import_tailwind_merge14 = require("tailwind-merge");
2020
2441
  var import_jsx_runtime35 = require("react/jsx-runtime");
2021
2442
  function ConversationList({
2022
2443
  conversations,
@@ -2030,7 +2451,7 @@ function ConversationList({
2030
2451
  "nav",
2031
2452
  {
2032
2453
  "aria-label": "Conversation list",
2033
- className: (0, import_tailwind_merge13.twMerge)("flex flex-col h-full bg-canvas", className),
2454
+ className: (0, import_tailwind_merge14.twMerge)("flex flex-col h-full bg-canvas", className),
2034
2455
  children: [
2035
2456
  onNew && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "p-3 border-b border-border", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2036
2457
  "button",
@@ -2047,7 +2468,7 @@ function ConversationList({
2047
2468
  return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
2048
2469
  "li",
2049
2470
  {
2050
- className: (0, import_tailwind_merge13.twMerge)(
2471
+ className: (0, import_tailwind_merge14.twMerge)(
2051
2472
  "flex items-start border-b border-border transition-colors duration-200",
2052
2473
  "hover:bg-surface",
2053
2474
  isActive && "bg-surface-raised border-l-2 border-l-accent"
@@ -2107,8 +2528,8 @@ function ConversationList({
2107
2528
  }
2108
2529
 
2109
2530
  // src/layouts/AgentFullPage/AgentFullPage.tsx
2110
- var import_tailwind_merge14 = require("tailwind-merge");
2111
- var import_react11 = require("react");
2531
+ var import_tailwind_merge15 = require("tailwind-merge");
2532
+ var import_react12 = require("react");
2112
2533
  var import_jsx_runtime36 = require("react/jsx-runtime");
2113
2534
  function AgentFullPage({
2114
2535
  endpoint,
@@ -2121,8 +2542,8 @@ function AgentFullPage({
2121
2542
  onNewConversation,
2122
2543
  className
2123
2544
  }) {
2124
- const [sidebarOpen, setSidebarOpen] = (0, import_react11.useState)(false);
2125
- const handleSelect = (0, import_react11.useCallback)(
2545
+ const [sidebarOpen, setSidebarOpen] = (0, import_react12.useState)(false);
2546
+ const handleSelect = (0, import_react12.useCallback)(
2126
2547
  (id) => {
2127
2548
  onConversationSelect?.(id);
2128
2549
  setSidebarOpen(false);
@@ -2132,7 +2553,7 @@ function AgentFullPage({
2132
2553
  return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
2133
2554
  "div",
2134
2555
  {
2135
- className: (0, import_tailwind_merge14.twMerge)("flex h-screen w-full overflow-hidden bg-brand-dark", className),
2556
+ className: (0, import_tailwind_merge15.twMerge)("flex h-screen w-full overflow-hidden bg-brand-dark", className),
2136
2557
  "data-testid": "agent-full-page",
2137
2558
  children: [
2138
2559
  showConversationList && /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
@@ -2147,7 +2568,7 @@ function AgentFullPage({
2147
2568
  /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
2148
2569
  "aside",
2149
2570
  {
2150
- className: (0, import_tailwind_merge14.twMerge)(
2571
+ className: (0, import_tailwind_merge15.twMerge)(
2151
2572
  "bg-brand-dark border-r border-brand-gold/15 w-72 shrink-0 flex-col z-40",
2152
2573
  // Desktop: always visible
2153
2574
  "hidden md:flex",
@@ -2213,8 +2634,8 @@ function AgentFullPage({
2213
2634
  }
2214
2635
 
2215
2636
  // src/layouts/AgentPanel/AgentPanel.tsx
2216
- var import_tailwind_merge15 = require("tailwind-merge");
2217
- var import_react12 = require("react");
2637
+ var import_tailwind_merge16 = require("tailwind-merge");
2638
+ var import_react13 = require("react");
2218
2639
  var import_jsx_runtime37 = require("react/jsx-runtime");
2219
2640
  function AgentPanel({
2220
2641
  endpoint,
@@ -2225,8 +2646,8 @@ function AgentPanel({
2225
2646
  title = "Chat",
2226
2647
  className
2227
2648
  }) {
2228
- const panelRef = (0, import_react12.useRef)(null);
2229
- (0, import_react12.useEffect)(() => {
2649
+ const panelRef = (0, import_react13.useRef)(null);
2650
+ (0, import_react13.useEffect)(() => {
2230
2651
  if (!isOpen) return;
2231
2652
  const handleKeyDown = (e) => {
2232
2653
  if (e.key === "Escape") onClose();
@@ -2238,13 +2659,13 @@ function AgentPanel({
2238
2659
  return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
2239
2660
  "div",
2240
2661
  {
2241
- className: (0, import_tailwind_merge15.twMerge)("fixed inset-0 z-50", !isOpen && "pointer-events-none"),
2662
+ className: (0, import_tailwind_merge16.twMerge)("fixed inset-0 z-50", !isOpen && "pointer-events-none"),
2242
2663
  "aria-hidden": !isOpen,
2243
2664
  children: [
2244
2665
  /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
2245
2666
  "div",
2246
2667
  {
2247
- className: (0, import_tailwind_merge15.twMerge)(
2668
+ className: (0, import_tailwind_merge16.twMerge)(
2248
2669
  "fixed inset-0 transition-opacity duration-300",
2249
2670
  isOpen ? "opacity-100 bg-brand-dark/70 backdrop-blur-sm pointer-events-auto" : "opacity-0 pointer-events-none"
2250
2671
  ),
@@ -2260,7 +2681,7 @@ function AgentPanel({
2260
2681
  "aria-label": title,
2261
2682
  "aria-modal": isOpen ? "true" : void 0,
2262
2683
  style: { width: widthStyle, maxWidth: "100vw" },
2263
- className: (0, import_tailwind_merge15.twMerge)(
2684
+ className: (0, import_tailwind_merge16.twMerge)(
2264
2685
  "fixed top-0 h-full flex flex-col z-50 bg-brand-dark shadow-card",
2265
2686
  "transition-transform duration-300 ease-in-out",
2266
2687
  side === "left" ? `left-0 border-r border-brand-gold/15 ${isOpen ? "translate-x-0" : "-translate-x-full"}` : `right-0 border-l border-brand-gold/15 ${isOpen ? "translate-x-0" : "translate-x-full"}`,
@@ -2302,8 +2723,8 @@ function AgentPanel({
2302
2723
  }
2303
2724
 
2304
2725
  // src/layouts/AgentWidget/AgentWidget.tsx
2305
- var import_tailwind_merge16 = require("tailwind-merge");
2306
- var import_react13 = require("react");
2726
+ var import_tailwind_merge17 = require("tailwind-merge");
2727
+ var import_react14 = require("react");
2307
2728
  var import_jsx_runtime38 = require("react/jsx-runtime");
2308
2729
  function AgentWidget({
2309
2730
  endpoint,
@@ -2312,8 +2733,8 @@ function AgentWidget({
2312
2733
  title = "Chat",
2313
2734
  className
2314
2735
  }) {
2315
- const [isOpen, setIsOpen] = (0, import_react13.useState)(false);
2316
- const toggle = (0, import_react13.useCallback)(() => {
2736
+ const [isOpen, setIsOpen] = (0, import_react14.useState)(false);
2737
+ const toggle = (0, import_react14.useCallback)(() => {
2317
2738
  setIsOpen((prev) => !prev);
2318
2739
  }, []);
2319
2740
  const positionClasses = position === "bottom-left" ? "left-4 bottom-4" : "right-4 bottom-4";
@@ -2326,7 +2747,7 @@ function AgentWidget({
2326
2747
  role: "dialog",
2327
2748
  "aria-label": title,
2328
2749
  "aria-hidden": !isOpen,
2329
- className: (0, import_tailwind_merge16.twMerge)(
2750
+ className: (0, import_tailwind_merge17.twMerge)(
2330
2751
  "fixed z-50 flex flex-col",
2331
2752
  "w-[min(400px,calc(100vw-2rem))] h-[min(600px,calc(100vh-6rem))]",
2332
2753
  "rounded-2xl overflow-hidden border border-brand-gold/15",
@@ -2373,7 +2794,7 @@ function AgentWidget({
2373
2794
  onClick: toggle,
2374
2795
  "aria-label": isOpen ? "Close chat" : triggerLabel,
2375
2796
  "aria-expanded": isOpen,
2376
- className: (0, import_tailwind_merge16.twMerge)(
2797
+ className: (0, import_tailwind_merge17.twMerge)(
2377
2798
  "fixed z-50 flex items-center justify-center w-14 h-14 rounded-full",
2378
2799
  "bg-brand-blue text-brand-cream shadow-glow-cyan",
2379
2800
  "hover:bg-brand-cyan hover:shadow-glow-cyan hover:scale-105",
@@ -2391,7 +2812,7 @@ function AgentWidget({
2391
2812
  }
2392
2813
 
2393
2814
  // src/layouts/AgentEmbed/AgentEmbed.tsx
2394
- var import_tailwind_merge17 = require("tailwind-merge");
2815
+ var import_tailwind_merge18 = require("tailwind-merge");
2395
2816
  var import_jsx_runtime39 = require("react/jsx-runtime");
2396
2817
  function AgentEmbed({
2397
2818
  endpoint,
@@ -2401,7 +2822,7 @@ function AgentEmbed({
2401
2822
  return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2402
2823
  "div",
2403
2824
  {
2404
- className: (0, import_tailwind_merge17.twMerge)("w-full h-full min-h-0", className),
2825
+ className: (0, import_tailwind_merge18.twMerge)("w-full h-full min-h-0", className),
2405
2826
  "data-testid": "agent-embed",
2406
2827
  children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2407
2828
  AgentChat,
@@ -2417,7 +2838,7 @@ function AgentEmbed({
2417
2838
 
2418
2839
  // src/mcp/MCPToolCall/MCPToolCall.tsx
2419
2840
  var import_class_variance_authority = require("class-variance-authority");
2420
- var import_tailwind_merge18 = require("tailwind-merge");
2841
+ var import_tailwind_merge19 = require("tailwind-merge");
2421
2842
  var import_core12 = require("@surf-kit/core");
2422
2843
  var import_jsx_runtime40 = require("react/jsx-runtime");
2423
2844
  var statusBadgeIntent = {
@@ -2461,7 +2882,7 @@ function MCPToolCall({ call, isExpanded = false, onToggleExpand, className }) {
2461
2882
  return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
2462
2883
  "div",
2463
2884
  {
2464
- className: (0, import_tailwind_merge18.twMerge)(container({ status: call.status }), className),
2885
+ className: (0, import_tailwind_merge19.twMerge)(container({ status: call.status }), className),
2465
2886
  "data-testid": "mcp-tool-call",
2466
2887
  children: [
2467
2888
  /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
@@ -2478,7 +2899,7 @@ function MCPToolCall({ call, isExpanded = false, onToggleExpand, className }) {
2478
2899
  call.serverName && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "text-xs text-text-secondary truncate", children: call.serverName })
2479
2900
  ] }),
2480
2901
  /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "flex items-center gap-2 shrink-0", children: [
2481
- call.status === "running" && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_core12.Spinner, { size: "sm" }) }),
2902
+ call.status === "running" && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_core12.WaveLoader, { size: "sm", color: "#38bdf8" }) }),
2482
2903
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2483
2904
  import_core12.Badge,
2484
2905
  {
@@ -2552,7 +2973,7 @@ function MCPToolCall({ call, isExpanded = false, onToggleExpand, className }) {
2552
2973
  }
2553
2974
 
2554
2975
  // src/mcp/MCPResourceView/MCPResourceView.tsx
2555
- var import_tailwind_merge19 = require("tailwind-merge");
2976
+ var import_tailwind_merge20 = require("tailwind-merge");
2556
2977
  var import_jsx_runtime41 = require("react/jsx-runtime");
2557
2978
  function isImageMime(mime) {
2558
2979
  return !!mime && mime.startsWith("image/");
@@ -2570,7 +2991,7 @@ function MCPResourceView({ resource, className }) {
2570
2991
  return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
2571
2992
  "div",
2572
2993
  {
2573
- className: (0, import_tailwind_merge19.twMerge)("rounded-lg border border-border bg-surface p-3 text-sm", className),
2994
+ className: (0, import_tailwind_merge20.twMerge)("rounded-lg border border-border bg-surface p-3 text-sm", className),
2574
2995
  "data-testid": "mcp-resource-view",
2575
2996
  children: [
2576
2997
  /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "mb-2", children: [
@@ -2620,9 +3041,9 @@ function MCPResourceView({ resource, className }) {
2620
3041
  }
2621
3042
 
2622
3043
  // src/mcp/MCPServerStatus/MCPServerStatus.tsx
2623
- var import_react14 = require("react");
3044
+ var import_react15 = require("react");
2624
3045
  var import_class_variance_authority2 = require("class-variance-authority");
2625
- var import_tailwind_merge20 = require("tailwind-merge");
3046
+ var import_tailwind_merge21 = require("tailwind-merge");
2626
3047
  var import_jsx_runtime42 = require("react/jsx-runtime");
2627
3048
  var statusDot = (0, import_class_variance_authority2.cva)("inline-block h-2 w-2 rounded-full shrink-0", {
2628
3049
  variants: {
@@ -2644,13 +3065,13 @@ function formatLastPing(date) {
2644
3065
  return date.toLocaleTimeString();
2645
3066
  }
2646
3067
  function MCPServerStatus({ server, className }) {
2647
- const [showTools, setShowTools] = (0, import_react14.useState)(false);
2648
- const [showResources, setShowResources] = (0, import_react14.useState)(false);
3068
+ const [showTools, setShowTools] = (0, import_react15.useState)(false);
3069
+ const [showResources, setShowResources] = (0, import_react15.useState)(false);
2649
3070
  const lastPing = formatLastPing(server.lastPing);
2650
3071
  return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
2651
3072
  "div",
2652
3073
  {
2653
- className: (0, import_tailwind_merge20.twMerge)("rounded-lg border border-border bg-surface p-3 text-sm", className),
3074
+ className: (0, import_tailwind_merge21.twMerge)("rounded-lg border border-border bg-surface p-3 text-sm", className),
2654
3075
  "data-testid": "mcp-server-status",
2655
3076
  children: [
2656
3077
  /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex items-center gap-2 mb-1", children: [
@@ -2762,9 +3183,9 @@ function MCPServerStatus({ server, className }) {
2762
3183
  }
2763
3184
 
2764
3185
  // src/mcp/MCPApprovalDialog/MCPApprovalDialog.tsx
2765
- var import_react15 = require("react");
3186
+ var import_react16 = require("react");
2766
3187
  var import_class_variance_authority3 = require("class-variance-authority");
2767
- var import_tailwind_merge21 = require("tailwind-merge");
3188
+ var import_tailwind_merge22 = require("tailwind-merge");
2768
3189
  var import_react_aria = require("react-aria");
2769
3190
  var import_core13 = require("@surf-kit/core");
2770
3191
  var import_jsx_runtime43 = require("react/jsx-runtime");
@@ -2800,9 +3221,9 @@ function MCPApprovalDialog({
2800
3221
  isOpen,
2801
3222
  className
2802
3223
  }) {
2803
- const ref = (0, import_react15.useRef)(null);
3224
+ const ref = (0, import_react16.useRef)(null);
2804
3225
  const { dialogProps, titleProps } = (0, import_react_aria.useDialog)({ role: "alertdialog" }, ref);
2805
- (0, import_react15.useEffect)(() => {
3226
+ (0, import_react16.useEffect)(() => {
2806
3227
  if (!isOpen) return;
2807
3228
  const handleKeyDown = (e) => {
2808
3229
  if (e.key === "Escape") {
@@ -2824,7 +3245,7 @@ function MCPApprovalDialog({
2824
3245
  {
2825
3246
  ...dialogProps,
2826
3247
  ref,
2827
- className: (0, import_tailwind_merge21.twMerge)(riskBorder({ risk: riskLevel }), className),
3248
+ className: (0, import_tailwind_merge22.twMerge)(riskBorder({ risk: riskLevel }), className),
2828
3249
  "data-testid": "mcp-approval-dialog",
2829
3250
  children: [
2830
3251
  /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "flex items-center justify-between mb-4", children: [
@@ -2902,7 +3323,7 @@ function MCPApprovalDialog({
2902
3323
  }
2903
3324
 
2904
3325
  // src/feedback/ThumbsFeedback/ThumbsFeedback.tsx
2905
- var import_react16 = require("react");
3326
+ var import_react17 = require("react");
2906
3327
  var import_jsx_runtime44 = require("react/jsx-runtime");
2907
3328
  function ThumbsFeedback({
2908
3329
  messageId,
@@ -2911,7 +3332,7 @@ function ThumbsFeedback({
2911
3332
  onNegative,
2912
3333
  className
2913
3334
  }) {
2914
- const [selected, setSelected] = (0, import_react16.useState)(state);
3335
+ const [selected, setSelected] = (0, import_react17.useState)(state);
2915
3336
  const handleClick = (rating) => {
2916
3337
  setSelected(rating);
2917
3338
  onFeedback(messageId, rating);
@@ -2991,11 +3412,11 @@ function ThumbsFeedback({
2991
3412
  }
2992
3413
 
2993
3414
  // src/feedback/FeedbackDialog/FeedbackDialog.tsx
2994
- var import_react17 = require("react");
3415
+ var import_react18 = require("react");
2995
3416
  var import_core14 = require("@surf-kit/core");
2996
3417
  var import_jsx_runtime45 = require("react/jsx-runtime");
2997
3418
  function FeedbackDialog({ isOpen, onClose, onSubmit, className }) {
2998
- const [comment, setComment] = (0, import_react17.useState)("");
3419
+ const [comment, setComment] = (0, import_react18.useState)("");
2999
3420
  const handleSubmit = () => {
3000
3421
  onSubmit(comment);
3001
3422
  setComment("");