@surf-kit/agent 0.2.1 → 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 (65) hide show
  1. package/LICENSE +184 -12
  2. package/README.md +1 -1
  3. package/dist/agent-identity/index.cjs +1 -0
  4. package/dist/agent-identity/index.cjs.map +1 -1
  5. package/dist/agent-identity/index.js +2 -0
  6. package/dist/agent-identity/index.js.map +1 -1
  7. package/dist/chat/index.cjs +626 -204
  8. package/dist/chat/index.cjs.map +1 -1
  9. package/dist/chat/index.d.cts +11 -6
  10. package/dist/chat/index.d.ts +11 -6
  11. package/dist/chat/index.js +608 -185
  12. package/dist/chat/index.js.map +1 -1
  13. package/dist/{chat--OifhIRe.d.ts → chat-BIIDOGrD.d.ts} +10 -1
  14. package/dist/{chat-ChYl2XjV.d.cts → chat-CGamM7Mz.d.cts} +10 -1
  15. package/dist/confidence/index.cjs +1 -0
  16. package/dist/confidence/index.cjs.map +1 -1
  17. package/dist/confidence/index.js +2 -0
  18. package/dist/confidence/index.js.map +1 -1
  19. package/dist/feedback/index.cjs +1 -0
  20. package/dist/feedback/index.cjs.map +1 -1
  21. package/dist/feedback/index.js +2 -0
  22. package/dist/feedback/index.js.map +1 -1
  23. package/dist/{hooks-DLfF18IU.d.cts → hooks-B1NYoLLs.d.cts} +21 -5
  24. package/dist/{hooks-BGs8-4GK.d.ts → hooks-CTeEqnBQ.d.ts} +21 -5
  25. package/dist/hooks.cjs +127 -81
  26. package/dist/hooks.cjs.map +1 -1
  27. package/dist/hooks.d.cts +3 -3
  28. package/dist/hooks.d.ts +3 -3
  29. package/dist/hooks.js +128 -81
  30. package/dist/hooks.js.map +1 -1
  31. package/dist/index.cjs +687 -265
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.d.cts +3 -3
  34. package/dist/index.d.ts +3 -3
  35. package/dist/index.js +647 -224
  36. package/dist/index.js.map +1 -1
  37. package/dist/layouts/index.cjs +647 -225
  38. package/dist/layouts/index.cjs.map +1 -1
  39. package/dist/layouts/index.d.cts +1 -1
  40. package/dist/layouts/index.d.ts +1 -1
  41. package/dist/layouts/index.js +624 -201
  42. package/dist/layouts/index.js.map +1 -1
  43. package/dist/mcp/index.cjs +2 -1
  44. package/dist/mcp/index.cjs.map +1 -1
  45. package/dist/mcp/index.js +4 -2
  46. package/dist/mcp/index.js.map +1 -1
  47. package/dist/response/index.cjs +67 -12
  48. package/dist/response/index.cjs.map +1 -1
  49. package/dist/response/index.d.cts +2 -2
  50. package/dist/response/index.d.ts +2 -2
  51. package/dist/response/index.js +66 -10
  52. package/dist/response/index.js.map +1 -1
  53. package/dist/sources/index.cjs +31 -1
  54. package/dist/sources/index.cjs.map +1 -1
  55. package/dist/sources/index.js +32 -1
  56. package/dist/sources/index.js.map +1 -1
  57. package/dist/streaming/index.cjs +203 -93
  58. package/dist/streaming/index.cjs.map +1 -1
  59. package/dist/streaming/index.d.cts +4 -3
  60. package/dist/streaming/index.d.ts +4 -3
  61. package/dist/streaming/index.js +174 -73
  62. package/dist/streaming/index.js.map +1 -1
  63. package/dist/{streaming-DbQxScpi.d.ts → streaming-Bx-ff2tt.d.ts} +1 -1
  64. package/dist/{streaming-DfT22A0z.d.cts → streaming-x7umFHoP.d.cts} +1 -1
  65. package/package.json +17 -6
package/dist/index.cjs CHANGED
@@ -1,3 +1,4 @@
1
+ 'use client';
1
2
  "use strict";
2
3
  var __create = Object.create;
3
4
  var __defProp = Object.defineProperty;
@@ -80,7 +81,7 @@ __export(index_exports, {
80
81
  module.exports = __toCommonJS(index_exports);
81
82
 
82
83
  // src/chat/AgentChat/AgentChat.tsx
83
- var import_tailwind_merge8 = require("tailwind-merge");
84
+ var import_tailwind_merge9 = require("tailwind-merge");
84
85
 
85
86
  // src/hooks/useAgentChat.ts
86
87
  var import_react = require("react");
@@ -91,7 +92,8 @@ var initialState = {
91
92
  error: null,
92
93
  inputValue: "",
93
94
  streamPhase: "idle",
94
- streamingContent: ""
95
+ streamingContent: "",
96
+ streamingAgent: null
95
97
  };
96
98
  function reducer(state, action) {
97
99
  switch (action.type) {
@@ -105,12 +107,15 @@ function reducer(state, action) {
105
107
  error: null,
106
108
  inputValue: "",
107
109
  streamPhase: "thinking",
108
- streamingContent: ""
110
+ streamingContent: "",
111
+ streamingAgent: null
109
112
  };
110
113
  case "STREAM_PHASE":
111
114
  return { ...state, streamPhase: action.phase };
112
115
  case "STREAM_CONTENT":
113
116
  return { ...state, streamingContent: state.streamingContent + action.content };
117
+ case "STREAM_AGENT":
118
+ return { ...state, streamingAgent: action.agent };
114
119
  case "SEND_SUCCESS":
115
120
  return {
116
121
  ...state,
@@ -126,7 +131,8 @@ function reducer(state, action) {
126
131
  isLoading: false,
127
132
  error: action.error,
128
133
  streamPhase: "idle",
129
- streamingContent: ""
134
+ streamingContent: "",
135
+ streamingAgent: null
130
136
  };
131
137
  case "LOAD_CONVERSATION":
132
138
  return {
@@ -152,107 +158,142 @@ function useAgentChat(config2) {
152
158
  const configRef = (0, import_react.useRef)(config2);
153
159
  configRef.current = config2;
154
160
  const lastUserMessageRef = (0, import_react.useRef)(null);
161
+ const lastUserAttachmentsRef = (0, import_react.useRef)(void 0);
155
162
  const sendMessage = (0, import_react.useCallback)(
156
- async (content) => {
157
- 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 ?? {};
158
166
  lastUserMessageRef.current = content;
167
+ lastUserAttachmentsRef.current = attachments;
159
168
  const userMessage = {
160
169
  id: generateMessageId(),
161
170
  role: "user",
162
171
  content,
172
+ attachments,
163
173
  timestamp: /* @__PURE__ */ new Date()
164
174
  };
165
175
  dispatch({ type: "SEND_START", message: userMessage });
166
176
  const controller = new AbortController();
167
177
  const timeoutId = setTimeout(() => controller.abort(), timeout);
168
178
  try {
169
- const response = await fetch(`${apiUrl}${streamPath}`, {
170
- method: "POST",
171
- headers: {
172
- "Content-Type": "application/json",
173
- Accept: "text/event-stream",
174
- ...headers
175
- },
176
- body: JSON.stringify({
177
- message: content,
178
- conversation_id: state.conversationId
179
- }),
180
- signal: controller.signal
181
- });
182
- clearTimeout(timeoutId);
183
- if (!response.ok) {
184
- dispatch({
185
- type: "SEND_ERROR",
186
- error: {
187
- code: "API_ERROR",
188
- message: `HTTP ${response.status}: ${response.statusText}`,
189
- retryable: response.status >= 500
190
- }
191
- });
192
- 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
+ }));
193
196
  }
194
- const reader = response.body?.getReader();
195
- if (!reader) {
196
- dispatch({
197
- type: "SEND_ERROR",
198
- 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
199
242
  });
200
- return;
201
- }
202
- const decoder = new TextDecoder();
203
- let buffer = "";
204
- let accumulatedContent = "";
205
- let agentResponse = null;
206
- let capturedAgent = null;
207
- let capturedConversationId = null;
208
- while (true) {
209
- const { done, value } = await reader.read();
210
- if (done) break;
211
- buffer += decoder.decode(value, { stream: true });
212
- const lines = buffer.split("\n");
213
- buffer = lines.pop() ?? "";
214
- for (const line of lines) {
215
- if (!line.startsWith("data: ")) continue;
216
- const data = line.slice(6).trim();
217
- if (data === "[DONE]") continue;
218
- try {
219
- const event = JSON.parse(data);
220
- switch (event.type) {
221
- case "agent":
222
- capturedAgent = event.agent;
223
- break;
224
- case "phase":
225
- dispatch({ type: "STREAM_PHASE", phase: event.phase });
226
- break;
227
- case "delta":
228
- accumulatedContent += event.content;
229
- dispatch({ type: "STREAM_CONTENT", content: event.content });
230
- break;
231
- case "done":
232
- agentResponse = event.response;
233
- capturedConversationId = event.conversation_id ?? null;
234
- break;
235
- case "error":
236
- dispatch({ type: "SEND_ERROR", error: event.error });
237
- 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 {
238
279
  }
239
- } catch {
240
280
  }
241
281
  }
242
282
  }
283
+ if (ctx.hadStreamError) return;
243
284
  const assistantMessage = {
244
285
  id: generateMessageId(),
245
286
  role: "assistant",
246
- content: agentResponse?.message ?? accumulatedContent,
247
- response: agentResponse ?? void 0,
248
- agent: capturedAgent ?? void 0,
287
+ content: ctx.agentResponse?.message ?? ctx.accumulatedContent,
288
+ response: ctx.agentResponse ?? void 0,
289
+ agent: ctx.capturedAgent ?? void 0,
249
290
  timestamp: /* @__PURE__ */ new Date()
250
291
  };
251
292
  dispatch({
252
293
  type: "SEND_SUCCESS",
253
294
  message: assistantMessage,
254
- streamingContent: accumulatedContent,
255
- conversationId: capturedConversationId
295
+ streamingContent: ctx.accumulatedContent,
296
+ conversationId: ctx.capturedConversationId
256
297
  });
257
298
  } catch (err) {
258
299
  clearTimeout(timeoutId);
@@ -283,7 +324,8 @@ function useAgentChat(config2) {
283
324
  }, []);
284
325
  const submitFeedback = (0, import_react.useCallback)(
285
326
  async (messageId, rating, comment) => {
286
- 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 ?? {};
287
329
  await fetch(`${apiUrl}${feedbackPath}`, {
288
330
  method: "POST",
289
331
  headers: { "Content-Type": "application/json", ...headers },
@@ -294,12 +336,13 @@ function useAgentChat(config2) {
294
336
  );
295
337
  const retry = (0, import_react.useCallback)(async () => {
296
338
  if (lastUserMessageRef.current) {
297
- await sendMessage(lastUserMessageRef.current);
339
+ await sendMessage(lastUserMessageRef.current, lastUserAttachmentsRef.current);
298
340
  }
299
341
  }, [sendMessage]);
300
342
  const reset = (0, import_react.useCallback)(() => {
301
343
  dispatch({ type: "RESET" });
302
344
  lastUserMessageRef.current = null;
345
+ lastUserAttachmentsRef.current = void 0;
303
346
  }, []);
304
347
  const actions = {
305
348
  sendMessage,
@@ -314,7 +357,7 @@ function useAgentChat(config2) {
314
357
 
315
358
  // src/chat/MessageThread/MessageThread.tsx
316
359
  var import_tailwind_merge5 = require("tailwind-merge");
317
- var import_react3 = require("react");
360
+ var import_react4 = require("react");
318
361
 
319
362
  // src/chat/MessageBubble/MessageBubble.tsx
320
363
  var import_tailwind_merge4 = require("tailwind-merge");
@@ -323,6 +366,7 @@ var import_tailwind_merge4 = require("tailwind-merge");
323
366
  var import_core2 = require("@surf-kit/core");
324
367
 
325
368
  // src/response/ResponseMessage/ResponseMessage.tsx
369
+ var import_react2 = __toESM(require("react"), 1);
326
370
  var import_react_markdown = __toESM(require("react-markdown"), 1);
327
371
  var import_rehype_sanitize = __toESM(require("rehype-sanitize"), 1);
328
372
  var import_tailwind_merge = require("tailwind-merge");
@@ -347,6 +391,7 @@ function ResponseMessage({ content, className }) {
347
391
  "[&_h3]:text-sm [&_h3]:font-semibold [&_h3]:text-accent [&_h3]:mt-2 [&_h3]:mb-1",
348
392
  "[&_code]:bg-surface-raised [&_code]:text-accent [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:rounded [&_code]:text-xs [&_code]:font-mono",
349
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",
350
395
  "[&_blockquote]:border-l-2 [&_blockquote]:border-border-strong [&_blockquote]:pl-4 [&_blockquote]:text-text-secondary",
351
396
  "[&_a]:text-accent [&_a]:underline-offset-2 [&_a]:hover:text-accent/80",
352
397
  className
@@ -362,11 +407,24 @@ function ResponseMessage({ content, className }) {
362
407
  p: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "my-2", children }),
363
408
  ul: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "my-2 list-disc pl-6", children }),
364
409
  ol: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "my-2 list-decimal pl-6", children }),
365
- 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
+ },
366
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 }),
367
424
  h1: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { className: "text-base font-bold mt-4 mb-2", children }),
368
425
  h2: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: "text-sm font-bold mt-3 mb-1", children }),
369
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" }),
370
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 })
371
429
  },
372
430
  children: normalizeMarkdownLists(content)
@@ -502,7 +560,14 @@ function renderWarning(data) {
502
560
  }
503
561
  );
504
562
  }
505
- 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;
506
571
  if (!data) return null;
507
572
  let content;
508
573
  switch (uiHint) {
@@ -532,7 +597,7 @@ function StructuredResponse({ uiHint, data, className }) {
532
597
  }
533
598
 
534
599
  // src/sources/SourceList/SourceList.tsx
535
- var import_react2 = require("react");
600
+ var import_react3 = require("react");
536
601
 
537
602
  // src/sources/SourceCard/SourceCard.tsx
538
603
  var import_core = require("@surf-kit/core");
@@ -582,7 +647,36 @@ function SourceCard({ source, variant = "compact", onNavigate, className }) {
582
647
  children: [
583
648
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-start justify-between gap-2", children: [
584
649
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex-1 min-w-0", children: [
585
- /* @__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 }),
586
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 })
587
681
  ] }),
588
682
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
@@ -612,7 +706,7 @@ function SourceList({
612
706
  onNavigate,
613
707
  className
614
708
  }) {
615
- const [isExpanded, setIsExpanded] = (0, import_react2.useState)(defaultExpanded);
709
+ const [isExpanded, setIsExpanded] = (0, import_react3.useState)(defaultExpanded);
616
710
  if (sources.length === 0) return null;
617
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)(
618
712
  SourceCard,
@@ -716,13 +810,16 @@ function AgentResponse({
716
810
  }) {
717
811
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: `flex flex-col gap-4 ${className ?? ""}`, "data-testid": "agent-response", children: [
718
812
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ResponseMessage, { content: response.message }),
719
- response.ui_hint !== "text" && response.structured_data && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
720
- StructuredResponse,
721
- {
722
- uiHint: response.ui_hint,
723
- data: response.structured_data
724
- }
725
- ),
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
+ })(),
726
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: [
727
824
  showConfidence && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
728
825
  import_core2.Badge,
@@ -773,6 +870,31 @@ function AgentResponse({
773
870
 
774
871
  // src/chat/MessageBubble/MessageBubble.tsx
775
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
+ }
776
898
  function MessageBubble({
777
899
  message,
778
900
  showAgent,
@@ -780,23 +902,29 @@ function MessageBubble({
780
902
  showConfidence = true,
781
903
  showVerification = true,
782
904
  animated = true,
905
+ userBubbleClassName,
783
906
  className
784
907
  }) {
785
908
  const isUser = message.role === "user";
909
+ const hasAttachments = message.attachments && message.attachments.length > 0;
786
910
  if (isUser) {
787
911
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
788
912
  "div",
789
913
  {
790
914
  "data-message-id": message.id,
791
915
  className: (0, import_tailwind_merge4.twMerge)("flex w-full justify-end", className),
792
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
916
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
793
917
  "div",
794
918
  {
795
919
  className: (0, import_tailwind_merge4.twMerge)(
796
- "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",
797
- 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
798
923
  ),
799
- 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
+ ]
800
928
  }
801
929
  )
802
930
  }
@@ -808,7 +936,7 @@ function MessageBubble({
808
936
  "data-message-id": message.id,
809
937
  className: (0, import_tailwind_merge4.twMerge)("flex w-full flex-col items-start gap-1.5", className),
810
938
  children: [
811
- 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("_", " ") }),
812
940
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
813
941
  "div",
814
942
  {
@@ -834,34 +962,70 @@ function MessageBubble({
834
962
 
835
963
  // src/chat/MessageThread/MessageThread.tsx
836
964
  var import_jsx_runtime8 = require("react/jsx-runtime");
837
- function MessageThread({ messages, streamingSlot, showSources, showConfidence, showVerification, className }) {
838
- const bottomRef = (0, import_react3.useRef)(null);
839
- (0, import_react3.useEffect)(() => {
840
- bottomRef.current?.scrollIntoView?.({ behavior: "smooth" });
841
- }, [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]);
842
997
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
843
998
  "div",
844
999
  {
1000
+ ref: scrollRef,
845
1001
  role: "log",
846
1002
  "aria-live": "polite",
847
1003
  "aria-label": "Message thread",
1004
+ onScroll: handleScroll,
848
1005
  className: (0, import_tailwind_merge5.twMerge)(
849
1006
  "flex flex-col gap-4 overflow-y-auto flex-1 px-4 py-6",
850
1007
  className
851
1008
  ),
852
1009
  children: [
853
- messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
854
- MessageBubble,
855
- {
856
- message,
857
- showSources,
858
- showConfidence,
859
- showVerification
860
- },
861
- message.id
862
- )),
863
- streamingSlot,
864
- /* @__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
865
1029
  ]
866
1030
  }
867
1031
  );
@@ -869,32 +1033,126 @@ function MessageThread({ messages, streamingSlot, showSources, showConfidence, s
869
1033
 
870
1034
  // src/chat/MessageComposer/MessageComposer.tsx
871
1035
  var import_tailwind_merge6 = require("tailwind-merge");
872
- var import_react4 = require("react");
1036
+ var import_react5 = require("react");
873
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
+ }
874
1126
  function MessageComposer({
875
1127
  onSend,
876
1128
  isLoading = false,
877
1129
  placeholder = "Type a message...",
878
1130
  className
879
1131
  }) {
880
- const [value, setValue] = (0, import_react4.useState)("");
881
- const textareaRef = (0, import_react4.useRef)(null);
882
- const canSend = value.trim().length > 0 && !isLoading;
883
- 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)(() => {
884
1139
  const el = textareaRef.current;
885
1140
  if (el) {
886
1141
  el.style.height = "auto";
887
1142
  el.style.overflowY = "hidden";
888
1143
  }
889
1144
  }, []);
890
- const handleSend = (0, import_react4.useCallback)(() => {
1145
+ const handleSend = (0, import_react5.useCallback)(() => {
891
1146
  if (!canSend) return;
892
- 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);
893
1150
  setValue("");
1151
+ setAttachments([]);
894
1152
  resetHeight();
895
1153
  textareaRef.current?.focus();
896
- }, [canSend, onSend, value, resetHeight]);
897
- const handleKeyDown = (0, import_react4.useCallback)(
1154
+ }, [canSend, onSend, value, attachments, resetHeight]);
1155
+ const handleKeyDown = (0, import_react5.useCallback)(
898
1156
  (e) => {
899
1157
  if (e.key === "Enter" && !e.shiftKey) {
900
1158
  e.preventDefault();
@@ -903,64 +1161,194 @@ function MessageComposer({
903
1161
  },
904
1162
  [handleSend]
905
1163
  );
906
- const handleChange = (0, import_react4.useCallback)(
1164
+ const handleChange = (0, import_react5.useCallback)(
907
1165
  (e) => {
908
1166
  setValue(e.target.value);
909
1167
  const el = e.target;
910
1168
  el.style.height = "auto";
911
- const capped = Math.min(el.scrollHeight, 128);
1169
+ const capped = Math.min(el.scrollHeight, 200);
912
1170
  el.style.height = `${capped}px`;
913
- el.style.overflowY = el.scrollHeight > 128 ? "auto" : "hidden";
1171
+ el.style.overflowY = el.scrollHeight > 200 ? "auto" : "hidden";
914
1172
  },
915
1173
  []
916
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
+ );
917
1254
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
918
1255
  "div",
919
1256
  {
920
1257
  className: (0, import_tailwind_merge6.twMerge)(
921
- "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",
922
1263
  className
923
1264
  ),
1265
+ onDragOver: handleDragOver,
1266
+ onDragLeave: handleDragLeave,
1267
+ onDrop: handleDrop,
924
1268
  children: [
925
1269
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
926
- "textarea",
1270
+ "input",
927
1271
  {
928
- ref: textareaRef,
929
- value,
930
- onChange: handleChange,
931
- onKeyDown: handleKeyDown,
932
- placeholder,
933
- rows: 1,
934
- disabled: isLoading,
935
- className: (0, import_tailwind_merge6.twMerge)(
936
- "flex-1 resize-none rounded-xl border border-border bg-surface/80",
937
- "px-4 py-2.5 text-sm text-text-primary placeholder:text-text-muted",
938
- "focus:border-transparent focus:ring-2 focus:ring-accent/40 focus:outline-none",
939
- "disabled:opacity-50 disabled:cursor-not-allowed",
940
- "overflow-hidden",
941
- "transition-all duration-200"
942
- ),
943
- style: { colorScheme: "dark" },
944
- "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"
945
1279
  }
946
1280
  ),
947
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
948
- "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,
949
1283
  {
950
- type: "button",
951
- onClick: handleSend,
952
- disabled: !value.trim() || isLoading,
953
- "aria-label": "Send message",
954
- className: (0, import_tailwind_merge6.twMerge)(
955
- "inline-flex items-center justify-center rounded-xl px-5 py-2.5",
956
- "text-sm font-semibold text-white shrink-0",
957
- "transition-all duration-200",
958
- "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
959
- 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"
960
- ),
961
- children: "Send"
962
- }
963
- )
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
+ ] })
964
1352
  ]
965
1353
  }
966
1354
  );
@@ -973,6 +1361,7 @@ function WelcomeScreen({
973
1361
  title = "Welcome",
974
1362
  message = "How can I help you today?",
975
1363
  icon,
1364
+ iconClassName,
976
1365
  suggestedQuestions = [],
977
1366
  onQuestionSelect,
978
1367
  className
@@ -985,12 +1374,15 @@ function WelcomeScreen({
985
1374
  className
986
1375
  ),
987
1376
  children: [
988
- /* @__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)(
989
1378
  "div",
990
1379
  {
991
- 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
+ ),
992
1384
  "aria-hidden": "true",
993
- 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" })
994
1386
  }
995
1387
  ),
996
1388
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-2", children: [
@@ -1000,7 +1392,7 @@ function WelcomeScreen({
1000
1392
  suggestedQuestions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1001
1393
  "div",
1002
1394
  {
1003
- className: "flex flex-wrap justify-center gap-2 max-w-md",
1395
+ className: "flex flex-wrap justify-center gap-2 max-w-xl",
1004
1396
  role: "group",
1005
1397
  "aria-label": "Suggested questions",
1006
1398
  children: suggestedQuestions.map((question) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
@@ -1009,7 +1401,7 @@ function WelcomeScreen({
1009
1401
  type: "button",
1010
1402
  onClick: () => onQuestionSelect?.(question),
1011
1403
  className: (0, import_tailwind_merge7.twMerge)(
1012
- "px-4 py-2 rounded-full text-sm",
1404
+ "px-3.5 py-1.5 rounded-full text-[12px]",
1013
1405
  "border border-border bg-transparent text-text-secondary",
1014
1406
  "hover:bg-accent/10 hover:border-interactive hover:text-text-primary",
1015
1407
  "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
@@ -1027,25 +1419,26 @@ function WelcomeScreen({
1027
1419
  }
1028
1420
 
1029
1421
  // src/streaming/StreamingMessage/StreamingMessage.tsx
1030
- var import_react6 = require("react");
1422
+ var import_react7 = require("react");
1423
+ var import_tailwind_merge8 = require("tailwind-merge");
1031
1424
  var import_core3 = require("@surf-kit/core");
1032
1425
 
1033
1426
  // src/hooks/useCharacterDrain.ts
1034
- var import_react5 = require("react");
1427
+ var import_react6 = require("react");
1035
1428
  function useCharacterDrain(target, msPerChar = 15) {
1036
- const [displayed, setDisplayed] = (0, import_react5.useState)("");
1037
- const indexRef = (0, import_react5.useRef)(0);
1038
- const lastTimeRef = (0, import_react5.useRef)(0);
1039
- const rafRef = (0, import_react5.useRef)(null);
1040
- const drainTargetRef = (0, import_react5.useRef)("");
1041
- 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);
1042
1435
  msPerCharRef.current = msPerChar;
1043
1436
  if (target !== "") {
1044
1437
  drainTargetRef.current = target;
1045
1438
  }
1046
1439
  const drainTarget = drainTargetRef.current;
1047
1440
  const isDraining = displayed.length < drainTarget.length;
1048
- const tickRef = (0, import_react5.useRef)(() => {
1441
+ const tickRef = (0, import_react6.useRef)(() => {
1049
1442
  });
1050
1443
  tickRef.current = (now) => {
1051
1444
  const currentTarget = drainTargetRef.current;
@@ -1057,7 +1450,10 @@ function useCharacterDrain(target, msPerChar = 15) {
1057
1450
  const elapsed = now - lastTimeRef.current;
1058
1451
  const charsToAdvance = Math.floor(elapsed / msPerCharRef.current);
1059
1452
  if (charsToAdvance > 0 && indexRef.current < currentTarget.length) {
1060
- 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
+ }
1061
1457
  indexRef.current = nextIndex;
1062
1458
  lastTimeRef.current = now;
1063
1459
  setDisplayed(currentTarget.slice(0, nextIndex));
@@ -1068,12 +1464,12 @@ function useCharacterDrain(target, msPerChar = 15) {
1068
1464
  rafRef.current = null;
1069
1465
  }
1070
1466
  };
1071
- (0, import_react5.useEffect)(() => {
1467
+ (0, import_react6.useEffect)(() => {
1072
1468
  if (drainTargetRef.current !== "" && indexRef.current < drainTargetRef.current.length && rafRef.current === null) {
1073
1469
  rafRef.current = requestAnimationFrame((t) => tickRef.current(t));
1074
1470
  }
1075
1471
  }, [drainTarget]);
1076
- (0, import_react5.useEffect)(() => {
1472
+ (0, import_react6.useEffect)(() => {
1077
1473
  if (target === "" && !isDraining && displayed !== "") {
1078
1474
  indexRef.current = 0;
1079
1475
  lastTimeRef.current = 0;
@@ -1081,7 +1477,7 @@ function useCharacterDrain(target, msPerChar = 15) {
1081
1477
  setDisplayed("");
1082
1478
  }
1083
1479
  }, [target, isDraining, displayed]);
1084
- (0, import_react5.useEffect)(() => {
1480
+ (0, import_react6.useEffect)(() => {
1085
1481
  return () => {
1086
1482
  if (rafRef.current !== null) {
1087
1483
  cancelAnimationFrame(rafRef.current);
@@ -1102,51 +1498,77 @@ var phaseLabels = {
1102
1498
  generating: "Writing...",
1103
1499
  verifying: "Verifying..."
1104
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
+ `;
1105
1519
  function StreamingMessage({
1106
1520
  stream,
1107
1521
  onComplete,
1522
+ onDraining,
1108
1523
  showPhases = true,
1109
1524
  className
1110
1525
  }) {
1111
- const onCompleteRef = (0, import_react6.useRef)(onComplete);
1526
+ const onCompleteRef = (0, import_react7.useRef)(onComplete);
1112
1527
  onCompleteRef.current = onComplete;
1113
- const wasActiveRef = (0, import_react6.useRef)(stream.active);
1114
- (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)(() => {
1115
1532
  if (wasActiveRef.current && !stream.active) {
1116
1533
  onCompleteRef.current?.();
1117
1534
  }
1118
1535
  wasActiveRef.current = stream.active;
1119
1536
  }, [stream.active]);
1120
1537
  const phaseLabel = phaseLabels[stream.phase];
1121
- const { displayed: displayedContent } = useCharacterDrain(stream.content);
1122
- 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: [
1123
1547
  /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { "aria-live": "assertive", className: "sr-only", children: [
1124
1548
  stream.active && stream.phase !== "idle" && "Response started",
1125
1549
  !stream.active && stream.content && "Response complete"
1126
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 }),
1127
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: [
1128
- showPhases && stream.active && stream.phase !== "idle" && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1554
+ showPhaseIndicator && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1129
1555
  "div",
1130
1556
  {
1131
- 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",
1132
1558
  "data-testid": "phase-indicator",
1133
1559
  children: [
1134
- /* @__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" }) }),
1135
1561
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: phaseLabel })
1136
1562
  ]
1137
1563
  }
1138
1564
  ),
1139
- /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "text-sm leading-relaxed text-text-primary whitespace-pre-wrap", children: [
1140
- displayedContent,
1141
- stream.active && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1142
- "span",
1143
- {
1144
- className: "inline-block w-0.5 h-4 bg-accent align-text-bottom animate-pulse ml-0.5",
1145
- "aria-hidden": "true",
1146
- "data-testid": "streaming-cursor"
1147
- }
1148
- )
1149
- ] })
1565
+ displayedContent && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1566
+ ResponseMessage,
1567
+ {
1568
+ content: displayedContent,
1569
+ className: showCursor ? "sk-streaming-cursor" : void 0
1570
+ }
1571
+ )
1150
1572
  ] })
1151
1573
  ] });
1152
1574
  }
@@ -1177,7 +1599,7 @@ function AgentChat({
1177
1599
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1178
1600
  "div",
1179
1601
  {
1180
- className: (0, import_tailwind_merge8.twMerge)(
1602
+ className: (0, import_tailwind_merge9.twMerge)(
1181
1603
  "flex flex-col h-full bg-canvas border border-border rounded-xl overflow-hidden",
1182
1604
  className
1183
1605
  ),
@@ -1349,7 +1771,7 @@ function ConfidenceBadge({ confidence, className }) {
1349
1771
  }
1350
1772
 
1351
1773
  // src/confidence/ConfidenceBreakdown/ConfidenceBreakdown.tsx
1352
- var import_react7 = require("react");
1774
+ var import_react8 = require("react");
1353
1775
 
1354
1776
  // src/confidence/ConfidenceMeter/ConfidenceMeter.tsx
1355
1777
  var import_jsx_runtime18 = require("react/jsx-runtime");
@@ -1404,7 +1826,7 @@ function ConfidenceBreakdown({
1404
1826
  defaultExpanded = false,
1405
1827
  className
1406
1828
  }) {
1407
- const [expanded, setExpanded] = (0, import_react7.useState)(defaultExpanded);
1829
+ const [expanded, setExpanded] = (0, import_react8.useState)(defaultExpanded);
1408
1830
  const isExpanded = expandable ? expanded : true;
1409
1831
  return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: `rounded-xl border border-border bg-surface ${className ?? ""}`, "data-testid": "confidence-breakdown", children: [
1410
1832
  /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
@@ -1484,7 +1906,7 @@ function VerificationBadge({ verification, className }) {
1484
1906
  }
1485
1907
 
1486
1908
  // src/confidence/VerificationDetail/VerificationDetail.tsx
1487
- var import_react8 = require("react");
1909
+ var import_react9 = require("react");
1488
1910
  var import_jsx_runtime21 = require("react/jsx-runtime");
1489
1911
  function VerificationDetail({
1490
1912
  verification,
@@ -1492,7 +1914,7 @@ function VerificationDetail({
1492
1914
  defaultExpanded = false,
1493
1915
  className
1494
1916
  }) {
1495
- const [expanded, setExpanded] = (0, import_react8.useState)(defaultExpanded);
1917
+ const [expanded, setExpanded] = (0, import_react9.useState)(defaultExpanded);
1496
1918
  const isExpanded = expandable ? expanded : true;
1497
1919
  return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: `rounded-xl border border-border bg-surface ${className ?? ""}`, "data-testid": "verification-detail", children: [
1498
1920
  /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
@@ -1536,11 +1958,11 @@ function VerificationDetail({
1536
1958
  }
1537
1959
 
1538
1960
  // src/hooks/useAgentTheme.ts
1539
- var import_react9 = require("react");
1961
+ var import_react10 = require("react");
1540
1962
  var DEFAULT_ACCENT = "#6366f1";
1541
1963
  var DEFAULT_LABEL = "Agent";
1542
1964
  function useAgentTheme(agentId, agentThemes) {
1543
- return (0, import_react9.useMemo)(() => {
1965
+ return (0, import_react10.useMemo)(() => {
1544
1966
  if (!agentId) {
1545
1967
  return { accent: DEFAULT_ACCENT, icon: null, label: DEFAULT_LABEL };
1546
1968
  }
@@ -1696,7 +2118,7 @@ function ToolExecution({ tool, label, className }) {
1696
2118
  role: "status",
1697
2119
  "data-testid": "tool-execution",
1698
2120
  children: [
1699
- /* @__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" }) }),
1700
2122
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { children: displayLabel })
1701
2123
  ]
1702
2124
  }
@@ -1716,7 +2138,7 @@ function RetrievalProgress({ sources, isActive, className }) {
1716
2138
  "data-testid": "retrieval-progress",
1717
2139
  children: [
1718
2140
  isActive && /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex items-center gap-2 text-sm text-text-secondary", children: [
1719
- /* @__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" }) }),
1720
2142
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { children: "Retrieving sources..." })
1721
2143
  ] }),
1722
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)(
@@ -1773,7 +2195,7 @@ function VerificationProgress({
1773
2195
  }
1774
2196
 
1775
2197
  // src/streaming/TypewriterText/TypewriterText.tsx
1776
- var import_react10 = require("react");
2198
+ var import_react11 = require("react");
1777
2199
  var import_jsx_runtime30 = require("react/jsx-runtime");
1778
2200
  function TypewriterText({
1779
2201
  text,
@@ -1783,9 +2205,9 @@ function TypewriterText({
1783
2205
  className = "",
1784
2206
  showCursor = true
1785
2207
  }) {
1786
- const [displayedText, setDisplayedText] = (0, import_react10.useState)("");
1787
- const [isComplete, setIsComplete] = (0, import_react10.useState)(false);
1788
- (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)(() => {
1789
2211
  setDisplayedText("");
1790
2212
  setIsComplete(false);
1791
2213
  let index = 0;
@@ -1814,7 +2236,7 @@ function TypewriterText({
1814
2236
  }
1815
2237
 
1816
2238
  // src/streaming/TypingIndicator/TypingIndicator.tsx
1817
- var import_tailwind_merge9 = require("tailwind-merge");
2239
+ var import_tailwind_merge10 = require("tailwind-merge");
1818
2240
  var import_hooks2 = require("@surf-kit/hooks");
1819
2241
  var import_jsx_runtime31 = require("react/jsx-runtime");
1820
2242
  var bounceKeyframes = `
@@ -1834,7 +2256,7 @@ function TypingIndicator({
1834
2256
  {
1835
2257
  role: "status",
1836
2258
  "aria-label": label ?? "typing",
1837
- 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),
1838
2260
  "data-testid": "typing-indicator",
1839
2261
  children: [
1840
2262
  !reducedMotion && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("style", { children: bounceKeyframes }),
@@ -1856,7 +2278,7 @@ function TypingIndicator({
1856
2278
  }
1857
2279
 
1858
2280
  // src/streaming/TextGlimmer/TextGlimmer.tsx
1859
- var import_tailwind_merge10 = require("tailwind-merge");
2281
+ var import_tailwind_merge11 = require("tailwind-merge");
1860
2282
  var import_hooks3 = require("@surf-kit/hooks");
1861
2283
  var import_jsx_runtime32 = require("react/jsx-runtime");
1862
2284
  var shimmerKeyframes = `
@@ -1873,7 +2295,7 @@ function TextGlimmer({ lines = 3, className }) {
1873
2295
  {
1874
2296
  role: "status",
1875
2297
  "aria-label": "Loading",
1876
- 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),
1877
2299
  "data-testid": "text-glimmer",
1878
2300
  children: [
1879
2301
  !reducedMotion && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("style", { children: shimmerKeyframes }),
@@ -1899,7 +2321,7 @@ function TextGlimmer({ lines = 3, className }) {
1899
2321
  }
1900
2322
 
1901
2323
  // src/streaming/StreamingList/StreamingList.tsx
1902
- var import_tailwind_merge11 = require("tailwind-merge");
2324
+ var import_tailwind_merge12 = require("tailwind-merge");
1903
2325
  var import_hooks4 = require("@surf-kit/hooks");
1904
2326
  var import_jsx_runtime33 = require("react/jsx-runtime");
1905
2327
  var fadeSlideInKeyframes = `
@@ -1917,13 +2339,13 @@ function StreamingList({
1917
2339
  }) {
1918
2340
  const reducedMotion = (0, import_hooks4.useReducedMotion)();
1919
2341
  if (items.length === 0 && !isStreaming) {
1920
- 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;
1921
2343
  }
1922
2344
  return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
1923
2345
  "ul",
1924
2346
  {
1925
2347
  "aria-live": "polite",
1926
- 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),
1927
2349
  "data-testid": "streaming-list",
1928
2350
  children: [
1929
2351
  !reducedMotion && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("style", { children: fadeSlideInKeyframes }),
@@ -1943,7 +2365,7 @@ function StreamingList({
1943
2365
  }
1944
2366
 
1945
2367
  // src/streaming/StreamingStructure/StreamingStructure.tsx
1946
- var import_tailwind_merge12 = require("tailwind-merge");
2368
+ var import_tailwind_merge13 = require("tailwind-merge");
1947
2369
  var import_hooks5 = require("@surf-kit/hooks");
1948
2370
  var import_jsx_runtime34 = require("react/jsx-runtime");
1949
2371
  var fadeSlideInKeyframes2 = `
@@ -1992,7 +2414,7 @@ function StreamingStructure({
1992
2414
  "dl",
1993
2415
  {
1994
2416
  "aria-live": "polite",
1995
- className: (0, import_tailwind_merge12.twMerge)("m-0", className),
2417
+ className: (0, import_tailwind_merge13.twMerge)("m-0", className),
1996
2418
  "data-testid": "streaming-structure",
1997
2419
  children: [
1998
2420
  !reducedMotion && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("style", { children: fadeSlideInKeyframes2 }),
@@ -2015,7 +2437,7 @@ function StreamingStructure({
2015
2437
  }
2016
2438
 
2017
2439
  // src/chat/ConversationList/ConversationList.tsx
2018
- var import_tailwind_merge13 = require("tailwind-merge");
2440
+ var import_tailwind_merge14 = require("tailwind-merge");
2019
2441
  var import_jsx_runtime35 = require("react/jsx-runtime");
2020
2442
  function ConversationList({
2021
2443
  conversations,
@@ -2029,7 +2451,7 @@ function ConversationList({
2029
2451
  "nav",
2030
2452
  {
2031
2453
  "aria-label": "Conversation list",
2032
- 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),
2033
2455
  children: [
2034
2456
  onNew && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "p-3 border-b border-border", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2035
2457
  "button",
@@ -2046,7 +2468,7 @@ function ConversationList({
2046
2468
  return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
2047
2469
  "li",
2048
2470
  {
2049
- className: (0, import_tailwind_merge13.twMerge)(
2471
+ className: (0, import_tailwind_merge14.twMerge)(
2050
2472
  "flex items-start border-b border-border transition-colors duration-200",
2051
2473
  "hover:bg-surface",
2052
2474
  isActive && "bg-surface-raised border-l-2 border-l-accent"
@@ -2106,8 +2528,8 @@ function ConversationList({
2106
2528
  }
2107
2529
 
2108
2530
  // src/layouts/AgentFullPage/AgentFullPage.tsx
2109
- var import_tailwind_merge14 = require("tailwind-merge");
2110
- var import_react11 = require("react");
2531
+ var import_tailwind_merge15 = require("tailwind-merge");
2532
+ var import_react12 = require("react");
2111
2533
  var import_jsx_runtime36 = require("react/jsx-runtime");
2112
2534
  function AgentFullPage({
2113
2535
  endpoint,
@@ -2120,8 +2542,8 @@ function AgentFullPage({
2120
2542
  onNewConversation,
2121
2543
  className
2122
2544
  }) {
2123
- const [sidebarOpen, setSidebarOpen] = (0, import_react11.useState)(false);
2124
- const handleSelect = (0, import_react11.useCallback)(
2545
+ const [sidebarOpen, setSidebarOpen] = (0, import_react12.useState)(false);
2546
+ const handleSelect = (0, import_react12.useCallback)(
2125
2547
  (id) => {
2126
2548
  onConversationSelect?.(id);
2127
2549
  setSidebarOpen(false);
@@ -2131,7 +2553,7 @@ function AgentFullPage({
2131
2553
  return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
2132
2554
  "div",
2133
2555
  {
2134
- 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),
2135
2557
  "data-testid": "agent-full-page",
2136
2558
  children: [
2137
2559
  showConversationList && /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
@@ -2146,7 +2568,7 @@ function AgentFullPage({
2146
2568
  /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
2147
2569
  "aside",
2148
2570
  {
2149
- className: (0, import_tailwind_merge14.twMerge)(
2571
+ className: (0, import_tailwind_merge15.twMerge)(
2150
2572
  "bg-brand-dark border-r border-brand-gold/15 w-72 shrink-0 flex-col z-40",
2151
2573
  // Desktop: always visible
2152
2574
  "hidden md:flex",
@@ -2212,8 +2634,8 @@ function AgentFullPage({
2212
2634
  }
2213
2635
 
2214
2636
  // src/layouts/AgentPanel/AgentPanel.tsx
2215
- var import_tailwind_merge15 = require("tailwind-merge");
2216
- var import_react12 = require("react");
2637
+ var import_tailwind_merge16 = require("tailwind-merge");
2638
+ var import_react13 = require("react");
2217
2639
  var import_jsx_runtime37 = require("react/jsx-runtime");
2218
2640
  function AgentPanel({
2219
2641
  endpoint,
@@ -2224,8 +2646,8 @@ function AgentPanel({
2224
2646
  title = "Chat",
2225
2647
  className
2226
2648
  }) {
2227
- const panelRef = (0, import_react12.useRef)(null);
2228
- (0, import_react12.useEffect)(() => {
2649
+ const panelRef = (0, import_react13.useRef)(null);
2650
+ (0, import_react13.useEffect)(() => {
2229
2651
  if (!isOpen) return;
2230
2652
  const handleKeyDown = (e) => {
2231
2653
  if (e.key === "Escape") onClose();
@@ -2237,13 +2659,13 @@ function AgentPanel({
2237
2659
  return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
2238
2660
  "div",
2239
2661
  {
2240
- 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"),
2241
2663
  "aria-hidden": !isOpen,
2242
2664
  children: [
2243
2665
  /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
2244
2666
  "div",
2245
2667
  {
2246
- className: (0, import_tailwind_merge15.twMerge)(
2668
+ className: (0, import_tailwind_merge16.twMerge)(
2247
2669
  "fixed inset-0 transition-opacity duration-300",
2248
2670
  isOpen ? "opacity-100 bg-brand-dark/70 backdrop-blur-sm pointer-events-auto" : "opacity-0 pointer-events-none"
2249
2671
  ),
@@ -2259,7 +2681,7 @@ function AgentPanel({
2259
2681
  "aria-label": title,
2260
2682
  "aria-modal": isOpen ? "true" : void 0,
2261
2683
  style: { width: widthStyle, maxWidth: "100vw" },
2262
- className: (0, import_tailwind_merge15.twMerge)(
2684
+ className: (0, import_tailwind_merge16.twMerge)(
2263
2685
  "fixed top-0 h-full flex flex-col z-50 bg-brand-dark shadow-card",
2264
2686
  "transition-transform duration-300 ease-in-out",
2265
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"}`,
@@ -2301,8 +2723,8 @@ function AgentPanel({
2301
2723
  }
2302
2724
 
2303
2725
  // src/layouts/AgentWidget/AgentWidget.tsx
2304
- var import_tailwind_merge16 = require("tailwind-merge");
2305
- var import_react13 = require("react");
2726
+ var import_tailwind_merge17 = require("tailwind-merge");
2727
+ var import_react14 = require("react");
2306
2728
  var import_jsx_runtime38 = require("react/jsx-runtime");
2307
2729
  function AgentWidget({
2308
2730
  endpoint,
@@ -2311,8 +2733,8 @@ function AgentWidget({
2311
2733
  title = "Chat",
2312
2734
  className
2313
2735
  }) {
2314
- const [isOpen, setIsOpen] = (0, import_react13.useState)(false);
2315
- const toggle = (0, import_react13.useCallback)(() => {
2736
+ const [isOpen, setIsOpen] = (0, import_react14.useState)(false);
2737
+ const toggle = (0, import_react14.useCallback)(() => {
2316
2738
  setIsOpen((prev) => !prev);
2317
2739
  }, []);
2318
2740
  const positionClasses = position === "bottom-left" ? "left-4 bottom-4" : "right-4 bottom-4";
@@ -2325,7 +2747,7 @@ function AgentWidget({
2325
2747
  role: "dialog",
2326
2748
  "aria-label": title,
2327
2749
  "aria-hidden": !isOpen,
2328
- className: (0, import_tailwind_merge16.twMerge)(
2750
+ className: (0, import_tailwind_merge17.twMerge)(
2329
2751
  "fixed z-50 flex flex-col",
2330
2752
  "w-[min(400px,calc(100vw-2rem))] h-[min(600px,calc(100vh-6rem))]",
2331
2753
  "rounded-2xl overflow-hidden border border-brand-gold/15",
@@ -2372,7 +2794,7 @@ function AgentWidget({
2372
2794
  onClick: toggle,
2373
2795
  "aria-label": isOpen ? "Close chat" : triggerLabel,
2374
2796
  "aria-expanded": isOpen,
2375
- className: (0, import_tailwind_merge16.twMerge)(
2797
+ className: (0, import_tailwind_merge17.twMerge)(
2376
2798
  "fixed z-50 flex items-center justify-center w-14 h-14 rounded-full",
2377
2799
  "bg-brand-blue text-brand-cream shadow-glow-cyan",
2378
2800
  "hover:bg-brand-cyan hover:shadow-glow-cyan hover:scale-105",
@@ -2390,7 +2812,7 @@ function AgentWidget({
2390
2812
  }
2391
2813
 
2392
2814
  // src/layouts/AgentEmbed/AgentEmbed.tsx
2393
- var import_tailwind_merge17 = require("tailwind-merge");
2815
+ var import_tailwind_merge18 = require("tailwind-merge");
2394
2816
  var import_jsx_runtime39 = require("react/jsx-runtime");
2395
2817
  function AgentEmbed({
2396
2818
  endpoint,
@@ -2400,7 +2822,7 @@ function AgentEmbed({
2400
2822
  return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2401
2823
  "div",
2402
2824
  {
2403
- 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),
2404
2826
  "data-testid": "agent-embed",
2405
2827
  children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
2406
2828
  AgentChat,
@@ -2416,7 +2838,7 @@ function AgentEmbed({
2416
2838
 
2417
2839
  // src/mcp/MCPToolCall/MCPToolCall.tsx
2418
2840
  var import_class_variance_authority = require("class-variance-authority");
2419
- var import_tailwind_merge18 = require("tailwind-merge");
2841
+ var import_tailwind_merge19 = require("tailwind-merge");
2420
2842
  var import_core12 = require("@surf-kit/core");
2421
2843
  var import_jsx_runtime40 = require("react/jsx-runtime");
2422
2844
  var statusBadgeIntent = {
@@ -2460,7 +2882,7 @@ function MCPToolCall({ call, isExpanded = false, onToggleExpand, className }) {
2460
2882
  return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
2461
2883
  "div",
2462
2884
  {
2463
- className: (0, import_tailwind_merge18.twMerge)(container({ status: call.status }), className),
2885
+ className: (0, import_tailwind_merge19.twMerge)(container({ status: call.status }), className),
2464
2886
  "data-testid": "mcp-tool-call",
2465
2887
  children: [
2466
2888
  /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
@@ -2477,7 +2899,7 @@ function MCPToolCall({ call, isExpanded = false, onToggleExpand, className }) {
2477
2899
  call.serverName && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "text-xs text-text-secondary truncate", children: call.serverName })
2478
2900
  ] }),
2479
2901
  /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "flex items-center gap-2 shrink-0", children: [
2480
- 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" }) }),
2481
2903
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2482
2904
  import_core12.Badge,
2483
2905
  {
@@ -2551,7 +2973,7 @@ function MCPToolCall({ call, isExpanded = false, onToggleExpand, className }) {
2551
2973
  }
2552
2974
 
2553
2975
  // src/mcp/MCPResourceView/MCPResourceView.tsx
2554
- var import_tailwind_merge19 = require("tailwind-merge");
2976
+ var import_tailwind_merge20 = require("tailwind-merge");
2555
2977
  var import_jsx_runtime41 = require("react/jsx-runtime");
2556
2978
  function isImageMime(mime) {
2557
2979
  return !!mime && mime.startsWith("image/");
@@ -2569,7 +2991,7 @@ function MCPResourceView({ resource, className }) {
2569
2991
  return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
2570
2992
  "div",
2571
2993
  {
2572
- 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),
2573
2995
  "data-testid": "mcp-resource-view",
2574
2996
  children: [
2575
2997
  /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "mb-2", children: [
@@ -2619,9 +3041,9 @@ function MCPResourceView({ resource, className }) {
2619
3041
  }
2620
3042
 
2621
3043
  // src/mcp/MCPServerStatus/MCPServerStatus.tsx
2622
- var import_react14 = require("react");
3044
+ var import_react15 = require("react");
2623
3045
  var import_class_variance_authority2 = require("class-variance-authority");
2624
- var import_tailwind_merge20 = require("tailwind-merge");
3046
+ var import_tailwind_merge21 = require("tailwind-merge");
2625
3047
  var import_jsx_runtime42 = require("react/jsx-runtime");
2626
3048
  var statusDot = (0, import_class_variance_authority2.cva)("inline-block h-2 w-2 rounded-full shrink-0", {
2627
3049
  variants: {
@@ -2643,13 +3065,13 @@ function formatLastPing(date) {
2643
3065
  return date.toLocaleTimeString();
2644
3066
  }
2645
3067
  function MCPServerStatus({ server, className }) {
2646
- const [showTools, setShowTools] = (0, import_react14.useState)(false);
2647
- 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);
2648
3070
  const lastPing = formatLastPing(server.lastPing);
2649
3071
  return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
2650
3072
  "div",
2651
3073
  {
2652
- 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),
2653
3075
  "data-testid": "mcp-server-status",
2654
3076
  children: [
2655
3077
  /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex items-center gap-2 mb-1", children: [
@@ -2761,9 +3183,9 @@ function MCPServerStatus({ server, className }) {
2761
3183
  }
2762
3184
 
2763
3185
  // src/mcp/MCPApprovalDialog/MCPApprovalDialog.tsx
2764
- var import_react15 = require("react");
3186
+ var import_react16 = require("react");
2765
3187
  var import_class_variance_authority3 = require("class-variance-authority");
2766
- var import_tailwind_merge21 = require("tailwind-merge");
3188
+ var import_tailwind_merge22 = require("tailwind-merge");
2767
3189
  var import_react_aria = require("react-aria");
2768
3190
  var import_core13 = require("@surf-kit/core");
2769
3191
  var import_jsx_runtime43 = require("react/jsx-runtime");
@@ -2799,9 +3221,9 @@ function MCPApprovalDialog({
2799
3221
  isOpen,
2800
3222
  className
2801
3223
  }) {
2802
- const ref = (0, import_react15.useRef)(null);
3224
+ const ref = (0, import_react16.useRef)(null);
2803
3225
  const { dialogProps, titleProps } = (0, import_react_aria.useDialog)({ role: "alertdialog" }, ref);
2804
- (0, import_react15.useEffect)(() => {
3226
+ (0, import_react16.useEffect)(() => {
2805
3227
  if (!isOpen) return;
2806
3228
  const handleKeyDown = (e) => {
2807
3229
  if (e.key === "Escape") {
@@ -2823,7 +3245,7 @@ function MCPApprovalDialog({
2823
3245
  {
2824
3246
  ...dialogProps,
2825
3247
  ref,
2826
- className: (0, import_tailwind_merge21.twMerge)(riskBorder({ risk: riskLevel }), className),
3248
+ className: (0, import_tailwind_merge22.twMerge)(riskBorder({ risk: riskLevel }), className),
2827
3249
  "data-testid": "mcp-approval-dialog",
2828
3250
  children: [
2829
3251
  /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "flex items-center justify-between mb-4", children: [
@@ -2901,7 +3323,7 @@ function MCPApprovalDialog({
2901
3323
  }
2902
3324
 
2903
3325
  // src/feedback/ThumbsFeedback/ThumbsFeedback.tsx
2904
- var import_react16 = require("react");
3326
+ var import_react17 = require("react");
2905
3327
  var import_jsx_runtime44 = require("react/jsx-runtime");
2906
3328
  function ThumbsFeedback({
2907
3329
  messageId,
@@ -2910,7 +3332,7 @@ function ThumbsFeedback({
2910
3332
  onNegative,
2911
3333
  className
2912
3334
  }) {
2913
- const [selected, setSelected] = (0, import_react16.useState)(state);
3335
+ const [selected, setSelected] = (0, import_react17.useState)(state);
2914
3336
  const handleClick = (rating) => {
2915
3337
  setSelected(rating);
2916
3338
  onFeedback(messageId, rating);
@@ -2990,11 +3412,11 @@ function ThumbsFeedback({
2990
3412
  }
2991
3413
 
2992
3414
  // src/feedback/FeedbackDialog/FeedbackDialog.tsx
2993
- var import_react17 = require("react");
3415
+ var import_react18 = require("react");
2994
3416
  var import_core14 = require("@surf-kit/core");
2995
3417
  var import_jsx_runtime45 = require("react/jsx-runtime");
2996
3418
  function FeedbackDialog({ isOpen, onClose, onSubmit, className }) {
2997
- const [comment, setComment] = (0, import_react17.useState)("");
3419
+ const [comment, setComment] = (0, import_react18.useState)("");
2998
3420
  const handleSubmit = () => {
2999
3421
  onSubmit(comment);
3000
3422
  setComment("");