@surf-kit/agent 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,2358 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ AgentAvatar: () => AgentAvatar,
34
+ AgentChat: () => AgentChat,
35
+ AgentEmbed: () => AgentEmbed,
36
+ AgentFullPage: () => AgentFullPage,
37
+ AgentHandoff: () => AgentHandoff,
38
+ AgentLabel: () => AgentLabel,
39
+ AgentPanel: () => AgentPanel,
40
+ AgentResponseView: () => AgentResponse,
41
+ AgentWidget: () => AgentWidget,
42
+ ConfidenceBadge: () => ConfidenceBadge,
43
+ ConfidenceBreakdownView: () => ConfidenceBreakdown,
44
+ ConfidenceMeter: () => ConfidenceMeter,
45
+ ConversationList: () => ConversationList,
46
+ ErrorResponse: () => ErrorResponse,
47
+ FeedbackConfirmation: () => FeedbackConfirmation,
48
+ FeedbackDialog: () => FeedbackDialog,
49
+ FollowUpChips: () => FollowUpChips,
50
+ MessageBubble: () => MessageBubble,
51
+ MessageComposer: () => MessageComposer,
52
+ MessageThread: () => MessageThread,
53
+ ResponseMessage: () => ResponseMessage,
54
+ RetrievalProgress: () => RetrievalProgress,
55
+ RoutingIndicator: () => RoutingIndicator,
56
+ SourceBadge: () => SourceBadge,
57
+ SourceCard: () => SourceCard,
58
+ SourceDrawer: () => SourceDrawer,
59
+ SourceInline: () => SourceInline,
60
+ SourceList: () => SourceList,
61
+ StreamingMessage: () => StreamingMessage,
62
+ StructuredResponse: () => StructuredResponse,
63
+ ThinkingIndicator: () => ThinkingIndicator,
64
+ ThumbsFeedback: () => ThumbsFeedback,
65
+ ToolExecution: () => ToolExecution,
66
+ TypewriterText: () => TypewriterText,
67
+ VerificationBadge: () => VerificationBadge,
68
+ VerificationDetail: () => VerificationDetail,
69
+ VerificationProgress: () => VerificationProgress,
70
+ WelcomeScreen: () => WelcomeScreen
71
+ });
72
+ module.exports = __toCommonJS(index_exports);
73
+
74
+ // src/chat/AgentChat/AgentChat.tsx
75
+ var import_tailwind_merge8 = require("tailwind-merge");
76
+
77
+ // src/hooks/useAgentChat.ts
78
+ var import_react = require("react");
79
+ var initialState = {
80
+ messages: [],
81
+ conversationId: null,
82
+ isLoading: false,
83
+ error: null,
84
+ inputValue: "",
85
+ streamPhase: "idle",
86
+ streamingContent: ""
87
+ };
88
+ function reducer(state, action) {
89
+ switch (action.type) {
90
+ case "SET_INPUT":
91
+ return { ...state, inputValue: action.value };
92
+ case "SEND_START":
93
+ return {
94
+ ...state,
95
+ messages: [...state.messages, action.message],
96
+ isLoading: true,
97
+ error: null,
98
+ inputValue: "",
99
+ streamPhase: "thinking",
100
+ streamingContent: ""
101
+ };
102
+ case "STREAM_PHASE":
103
+ return { ...state, streamPhase: action.phase };
104
+ case "STREAM_CONTENT":
105
+ return { ...state, streamingContent: state.streamingContent + action.content };
106
+ case "SEND_SUCCESS":
107
+ return {
108
+ ...state,
109
+ messages: [...state.messages, action.message],
110
+ conversationId: action.conversationId ?? state.conversationId,
111
+ isLoading: false,
112
+ streamPhase: "idle",
113
+ streamingContent: ""
114
+ };
115
+ case "SEND_ERROR":
116
+ return {
117
+ ...state,
118
+ isLoading: false,
119
+ error: action.error,
120
+ streamPhase: "idle",
121
+ streamingContent: ""
122
+ };
123
+ case "LOAD_CONVERSATION":
124
+ return {
125
+ ...state,
126
+ conversationId: action.conversationId,
127
+ messages: action.messages,
128
+ error: null
129
+ };
130
+ case "RESET":
131
+ return { ...initialState };
132
+ case "CLEAR_ERROR":
133
+ return { ...state, error: null };
134
+ default:
135
+ return state;
136
+ }
137
+ }
138
+ var msgIdCounter = 0;
139
+ function generateMessageId() {
140
+ return `msg-${Date.now()}-${++msgIdCounter}`;
141
+ }
142
+ function useAgentChat(config2) {
143
+ const [state, dispatch] = (0, import_react.useReducer)(reducer, initialState);
144
+ const configRef = (0, import_react.useRef)(config2);
145
+ configRef.current = config2;
146
+ const lastUserMessageRef = (0, import_react.useRef)(null);
147
+ const sendMessage = (0, import_react.useCallback)(
148
+ async (content) => {
149
+ const { apiUrl, streamPath = "/chat/stream", headers = {}, timeout = 3e4 } = configRef.current;
150
+ lastUserMessageRef.current = content;
151
+ const userMessage = {
152
+ id: generateMessageId(),
153
+ role: "user",
154
+ content,
155
+ timestamp: /* @__PURE__ */ new Date()
156
+ };
157
+ dispatch({ type: "SEND_START", message: userMessage });
158
+ const controller = new AbortController();
159
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
160
+ try {
161
+ const response = await fetch(`${apiUrl}${streamPath}`, {
162
+ method: "POST",
163
+ headers: {
164
+ "Content-Type": "application/json",
165
+ Accept: "text/event-stream",
166
+ ...headers
167
+ },
168
+ body: JSON.stringify({
169
+ message: content,
170
+ conversation_id: state.conversationId
171
+ }),
172
+ signal: controller.signal
173
+ });
174
+ clearTimeout(timeoutId);
175
+ if (!response.ok) {
176
+ dispatch({
177
+ type: "SEND_ERROR",
178
+ error: {
179
+ code: "API_ERROR",
180
+ message: `HTTP ${response.status}: ${response.statusText}`,
181
+ retryable: response.status >= 500
182
+ }
183
+ });
184
+ return;
185
+ }
186
+ const reader = response.body?.getReader();
187
+ if (!reader) {
188
+ dispatch({
189
+ type: "SEND_ERROR",
190
+ error: { code: "STREAM_ERROR", message: "No response body", retryable: true }
191
+ });
192
+ return;
193
+ }
194
+ const decoder = new TextDecoder();
195
+ let buffer = "";
196
+ let accumulatedContent = "";
197
+ let agentResponse = null;
198
+ let capturedAgent = null;
199
+ let capturedConversationId = null;
200
+ while (true) {
201
+ const { done, value } = await reader.read();
202
+ if (done) break;
203
+ buffer += decoder.decode(value, { stream: true });
204
+ const lines = buffer.split("\n");
205
+ buffer = lines.pop() ?? "";
206
+ for (const line of lines) {
207
+ if (!line.startsWith("data: ")) continue;
208
+ const data = line.slice(6).trim();
209
+ if (data === "[DONE]") continue;
210
+ try {
211
+ const event = JSON.parse(data);
212
+ switch (event.type) {
213
+ case "agent":
214
+ capturedAgent = event.agent;
215
+ break;
216
+ case "phase":
217
+ dispatch({ type: "STREAM_PHASE", phase: event.phase });
218
+ break;
219
+ case "delta":
220
+ accumulatedContent += event.content;
221
+ dispatch({ type: "STREAM_CONTENT", content: event.content });
222
+ break;
223
+ case "done":
224
+ agentResponse = event.response;
225
+ capturedConversationId = event.conversation_id ?? null;
226
+ break;
227
+ case "error":
228
+ dispatch({ type: "SEND_ERROR", error: event.error });
229
+ return;
230
+ }
231
+ } catch {
232
+ }
233
+ }
234
+ }
235
+ const assistantMessage = {
236
+ id: generateMessageId(),
237
+ role: "assistant",
238
+ content: agentResponse?.message ?? accumulatedContent,
239
+ response: agentResponse ?? void 0,
240
+ agent: capturedAgent ?? void 0,
241
+ timestamp: /* @__PURE__ */ new Date()
242
+ };
243
+ dispatch({
244
+ type: "SEND_SUCCESS",
245
+ message: assistantMessage,
246
+ streamingContent: accumulatedContent,
247
+ conversationId: capturedConversationId
248
+ });
249
+ } catch (err) {
250
+ clearTimeout(timeoutId);
251
+ if (err.name === "AbortError") {
252
+ dispatch({
253
+ type: "SEND_ERROR",
254
+ error: { code: "TIMEOUT", message: "Request timed out", retryable: true }
255
+ });
256
+ } else {
257
+ dispatch({
258
+ type: "SEND_ERROR",
259
+ error: {
260
+ code: "NETWORK_ERROR",
261
+ message: err.message ?? "Network error",
262
+ retryable: true
263
+ }
264
+ });
265
+ }
266
+ }
267
+ },
268
+ [state.conversationId]
269
+ );
270
+ const setInputValue = (0, import_react.useCallback)((value) => {
271
+ dispatch({ type: "SET_INPUT", value });
272
+ }, []);
273
+ const loadConversation = (0, import_react.useCallback)((conversationId, messages) => {
274
+ dispatch({ type: "LOAD_CONVERSATION", conversationId, messages });
275
+ }, []);
276
+ const submitFeedback = (0, import_react.useCallback)(
277
+ async (messageId, rating, comment) => {
278
+ const { apiUrl, feedbackPath = "/feedback", headers = {} } = configRef.current;
279
+ await fetch(`${apiUrl}${feedbackPath}`, {
280
+ method: "POST",
281
+ headers: { "Content-Type": "application/json", ...headers },
282
+ body: JSON.stringify({ messageId, rating, comment })
283
+ });
284
+ },
285
+ []
286
+ );
287
+ const retry = (0, import_react.useCallback)(async () => {
288
+ if (lastUserMessageRef.current) {
289
+ await sendMessage(lastUserMessageRef.current);
290
+ }
291
+ }, [sendMessage]);
292
+ const reset = (0, import_react.useCallback)(() => {
293
+ dispatch({ type: "RESET" });
294
+ lastUserMessageRef.current = null;
295
+ }, []);
296
+ const actions = {
297
+ sendMessage,
298
+ setInputValue,
299
+ loadConversation,
300
+ submitFeedback,
301
+ retry,
302
+ reset
303
+ };
304
+ return { state, actions };
305
+ }
306
+
307
+ // src/chat/MessageThread/MessageThread.tsx
308
+ var import_tailwind_merge5 = require("tailwind-merge");
309
+ var import_react3 = require("react");
310
+
311
+ // src/chat/MessageBubble/MessageBubble.tsx
312
+ var import_tailwind_merge4 = require("tailwind-merge");
313
+
314
+ // src/response/AgentResponse/AgentResponse.tsx
315
+ var import_core2 = require("@surf-kit/core");
316
+
317
+ // src/response/ResponseMessage/ResponseMessage.tsx
318
+ var import_react_markdown = __toESM(require("react-markdown"), 1);
319
+ var import_rehype_sanitize = __toESM(require("rehype-sanitize"), 1);
320
+ var import_tailwind_merge = require("tailwind-merge");
321
+ var import_jsx_runtime = require("react/jsx-runtime");
322
+ function normalizeMarkdownLists(content) {
323
+ return content.replace(/:\s+-\s+/g, ":\n\n- ");
324
+ }
325
+ function ResponseMessage({ content, className }) {
326
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
327
+ "div",
328
+ {
329
+ className: (0, import_tailwind_merge.twMerge)(
330
+ "text-sm leading-relaxed text-text-primary",
331
+ "[&_p]:my-2",
332
+ "[&_ul]:my-2 [&_ul]:list-disc [&_ul]:pl-6",
333
+ "[&_ol]:my-2 [&_ol]:list-decimal [&_ol]:pl-6",
334
+ "[&_li]:my-1",
335
+ "[&_strong]:text-text-primary [&_strong]:font-semibold",
336
+ "[&_em]:text-text-secondary",
337
+ "[&_h1]:text-lg [&_h1]:font-semibold [&_h1]:text-text-primary [&_h1]:mt-4 [&_h1]:mb-2",
338
+ "[&_h2]:text-base [&_h2]:font-semibold [&_h2]:text-text-primary [&_h2]:mt-3 [&_h2]:mb-1.5",
339
+ "[&_h3]:text-sm [&_h3]:font-semibold [&_h3]:text-accent [&_h3]:mt-2 [&_h3]:mb-1",
340
+ "[&_code]:bg-surface-raised [&_code]:text-accent [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:rounded [&_code]:text-xs [&_code]:font-mono",
341
+ "[&_pre]:bg-surface-raised [&_pre]:border [&_pre]:border-border [&_pre]:rounded-xl [&_pre]:p-4 [&_pre]:overflow-x-auto",
342
+ "[&_blockquote]:border-l-2 [&_blockquote]:border-border-strong [&_blockquote]:pl-4 [&_blockquote]:text-text-secondary",
343
+ "[&_a]:text-accent [&_a]:underline-offset-2 [&_a]:hover:text-accent/80",
344
+ className
345
+ ),
346
+ "data-testid": "response-message",
347
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
348
+ import_react_markdown.default,
349
+ {
350
+ rehypePlugins: [import_rehype_sanitize.default],
351
+ components: {
352
+ script: () => null,
353
+ iframe: () => null,
354
+ p: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "my-2", children }),
355
+ ul: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "my-2 list-disc pl-6", children }),
356
+ ol: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ol", { className: "my-2 list-decimal pl-6", children }),
357
+ li: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { className: "my-1", children }),
358
+ strong: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { className: "font-semibold", children }),
359
+ h1: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", { className: "text-base font-bold mt-4 mb-2", children }),
360
+ h2: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: "text-sm font-bold mt-3 mb-1", children }),
361
+ h3: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { className: "text-sm font-semibold mt-2 mb-1", children }),
362
+ code: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { className: "bg-surface-sunken rounded px-1 py-0.5 text-xs font-mono", children })
363
+ },
364
+ children: normalizeMarkdownLists(content)
365
+ }
366
+ )
367
+ }
368
+ );
369
+ }
370
+
371
+ // src/response/StructuredResponse/StructuredResponse.tsx
372
+ var import_jsx_runtime2 = require("react/jsx-runtime");
373
+ function tryParse(value) {
374
+ if (value === void 0 || value === null) return null;
375
+ if (typeof value === "string") {
376
+ try {
377
+ return JSON.parse(value);
378
+ } catch {
379
+ return null;
380
+ }
381
+ }
382
+ return value;
383
+ }
384
+ function renderSteps(data) {
385
+ const steps = tryParse(data.steps);
386
+ if (!steps || !Array.isArray(steps)) return null;
387
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ol", { className: "flex flex-col gap-2", "data-testid": "structured-steps", children: steps.map((step, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { className: "flex items-start gap-3", children: [
388
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
389
+ "span",
390
+ {
391
+ className: "mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-accent text-[11px] font-semibold text-white",
392
+ "aria-hidden": "true",
393
+ children: i + 1
394
+ }
395
+ ),
396
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-sm text-text-primary leading-relaxed", children: step })
397
+ ] }, i)) });
398
+ }
399
+ function renderTable(data) {
400
+ const columns = tryParse(data.columns);
401
+ const rawRows = tryParse(data.rows);
402
+ if (columns && rawRows && Array.isArray(columns) && Array.isArray(rawRows)) {
403
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "overflow-x-auto rounded-lg border border-border", "data-testid": "structured-table", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("table", { role: "table", className: "w-full border-collapse text-sm", children: [
404
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("thead", { className: "bg-surface-raised", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tr", { children: columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
405
+ "th",
406
+ {
407
+ className: "text-left px-4 py-2.5 font-semibold text-text-primary border-b border-border",
408
+ children: col
409
+ },
410
+ col
411
+ )) }) }),
412
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tbody", { children: rawRows.map((row, i) => {
413
+ const cells = Array.isArray(row) ? row : columns.map((col) => row[col]);
414
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tr", { className: "even:bg-surface-raised/40", children: cells.map((cell, j) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { className: "px-4 py-2 text-text-secondary border-b border-border", children: String(cell ?? "") }, j)) }, i);
415
+ }) })
416
+ ] }) });
417
+ }
418
+ const entries = Object.entries(data);
419
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "overflow-x-auto rounded-lg border border-border", "data-testid": "structured-table", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("table", { role: "table", className: "w-full border-collapse text-sm", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tbody", { children: entries.map(([key, value]) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("tr", { className: "even:bg-surface-raised/40", children: [
420
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { className: "px-4 py-2 text-text-primary font-medium border-b border-border w-1/3", children: key }),
421
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { className: "px-4 py-2 text-text-secondary border-b border-border", children: typeof value === "object" ? JSON.stringify(value) : String(value ?? "") })
422
+ ] }, key)) }) }) });
423
+ }
424
+ function renderCard(data) {
425
+ const title = typeof data.title === "string" ? data.title : null;
426
+ const body = typeof data.body === "string" ? data.body : null;
427
+ const link = typeof data.link === "string" ? data.link : null;
428
+ const linkLabel = typeof data.link_label === "string" ? data.link_label : "Learn more";
429
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
430
+ "div",
431
+ {
432
+ className: "rounded-xl border border-border bg-surface-raised p-4 flex flex-col gap-2",
433
+ "data-testid": "structured-card",
434
+ children: [
435
+ title && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-sm font-semibold text-text-primary", children: title }),
436
+ body && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-sm text-text-secondary leading-relaxed", children: body }),
437
+ link && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
438
+ "a",
439
+ {
440
+ href: link,
441
+ target: "_blank",
442
+ rel: "noopener noreferrer",
443
+ className: "mt-1 inline-flex items-center gap-1 text-xs font-medium text-accent hover:text-accent/80 underline-offset-2 hover:underline transition-colors",
444
+ children: [
445
+ linkLabel,
446
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { className: "h-3 w-3", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: 2, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" }) })
447
+ ]
448
+ }
449
+ )
450
+ ]
451
+ }
452
+ );
453
+ }
454
+ function renderList(data) {
455
+ const items = tryParse(data.items);
456
+ const title = typeof data.title === "string" ? data.title : null;
457
+ if (!items || !Array.isArray(items)) return null;
458
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col gap-1.5", "data-testid": "structured-list", children: [
459
+ title && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs font-semibold uppercase tracking-wider text-text-secondary mb-1", children: title }),
460
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { className: "flex flex-col gap-1.5", children: items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("li", { className: "flex items-start gap-2.5", children: [
461
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "mt-1.5 h-1.5 w-1.5 shrink-0 rounded-full bg-accent", "aria-hidden": "true" }),
462
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-sm text-text-primary leading-relaxed", children: item })
463
+ ] }, i)) })
464
+ ] });
465
+ }
466
+ function renderWarning(data) {
467
+ const severity = typeof data.severity === "string" ? data.severity : "medium";
468
+ const action = typeof data.action === "string" ? data.action : null;
469
+ const details = typeof data.details === "string" ? data.details : null;
470
+ const isHigh = severity === "high";
471
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
472
+ "div",
473
+ {
474
+ role: "alert",
475
+ className: `rounded-xl border p-4 flex gap-3 ${isHigh ? "border-red-200 bg-red-50 dark:border-red-900/50 dark:bg-red-950/30" : "border-amber-200 bg-amber-50 dark:border-amber-900/50 dark:bg-amber-950/30"}`,
476
+ "data-testid": "structured-warning",
477
+ children: [
478
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
479
+ "svg",
480
+ {
481
+ className: `mt-0.5 h-5 w-5 shrink-0 ${isHigh ? "text-red-500" : "text-amber-500"}`,
482
+ fill: "none",
483
+ viewBox: "0 0 24 24",
484
+ stroke: "currentColor",
485
+ strokeWidth: 2,
486
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" })
487
+ }
488
+ ),
489
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex flex-col gap-1", children: [
490
+ action && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: `text-sm font-semibold ${isHigh ? "text-red-700 dark:text-red-300" : "text-amber-700 dark:text-amber-300"}`, children: action }),
491
+ details && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: `text-sm ${isHigh ? "text-red-600 dark:text-red-400" : "text-amber-600 dark:text-amber-400"}`, children: details })
492
+ ] })
493
+ ]
494
+ }
495
+ );
496
+ }
497
+ function StructuredResponse({ uiHint, data, className }) {
498
+ if (!data) return null;
499
+ let content;
500
+ switch (uiHint) {
501
+ case "steps":
502
+ content = renderSteps(data);
503
+ break;
504
+ case "table":
505
+ content = renderTable(data);
506
+ break;
507
+ case "card":
508
+ content = renderCard(data);
509
+ break;
510
+ case "list":
511
+ content = renderList(data);
512
+ break;
513
+ case "warning":
514
+ content = renderWarning(data);
515
+ break;
516
+ case "text":
517
+ content = typeof data.text === "string" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { "data-testid": "structured-text", children: data.text }) : null;
518
+ break;
519
+ default:
520
+ content = null;
521
+ }
522
+ if (!content) return null;
523
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className, "data-testid": "structured-response", children: content });
524
+ }
525
+
526
+ // src/sources/SourceList/SourceList.tsx
527
+ var import_react2 = require("react");
528
+
529
+ // src/sources/SourceCard/SourceCard.tsx
530
+ var import_core = require("@surf-kit/core");
531
+ var import_tailwind_merge2 = require("tailwind-merge");
532
+ var import_jsx_runtime3 = require("react/jsx-runtime");
533
+ function getConfidenceIntent(confidence) {
534
+ if (confidence >= 0.8) return "success";
535
+ if (confidence >= 0.5) return "warning";
536
+ return "error";
537
+ }
538
+ function getConfidenceLabel(confidence) {
539
+ if (confidence >= 0.8) return "High";
540
+ if (confidence >= 0.5) return "Medium";
541
+ return "Low";
542
+ }
543
+ function SourceCard({ source, variant = "compact", onNavigate, className }) {
544
+ const handleClick = () => {
545
+ if (onNavigate) {
546
+ onNavigate(source);
547
+ }
548
+ };
549
+ const isCompact = variant === "compact";
550
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
551
+ "div",
552
+ {
553
+ className: (0, import_tailwind_merge2.twMerge)(
554
+ "rounded-xl border transition-all duration-200",
555
+ "bg-surface border-border",
556
+ onNavigate && "cursor-pointer hover:border-border-strong",
557
+ className
558
+ ),
559
+ "data-document-id": source.document_id,
560
+ "data-testid": "source-card",
561
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
562
+ "div",
563
+ {
564
+ className: isCompact ? "px-4 py-3" : "px-6 py-4",
565
+ onClick: handleClick,
566
+ role: onNavigate ? "button" : void 0,
567
+ tabIndex: onNavigate ? 0 : void 0,
568
+ onKeyDown: onNavigate ? (e) => {
569
+ if (e.key === "Enter" || e.key === " ") {
570
+ e.preventDefault();
571
+ handleClick();
572
+ }
573
+ } : void 0,
574
+ children: [
575
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-start justify-between gap-2", children: [
576
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex-1 min-w-0", children: [
577
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-sm font-medium text-text-primary truncate", children: source.title }),
578
+ 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 })
579
+ ] }),
580
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
581
+ import_core.Badge,
582
+ {
583
+ intent: getConfidenceIntent(source.confidence),
584
+ size: "sm",
585
+ children: getConfidenceLabel(source.confidence)
586
+ }
587
+ )
588
+ ] }),
589
+ !isCompact && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-xs text-text-secondary mt-2 line-clamp-3 leading-relaxed", children: source.snippet })
590
+ ]
591
+ }
592
+ )
593
+ }
594
+ );
595
+ }
596
+
597
+ // src/sources/SourceList/SourceList.tsx
598
+ var import_jsx_runtime4 = require("react/jsx-runtime");
599
+ function SourceList({
600
+ sources,
601
+ variant = "compact",
602
+ collapsible = false,
603
+ defaultExpanded = true,
604
+ onNavigate,
605
+ className
606
+ }) {
607
+ const [isExpanded, setIsExpanded] = (0, import_react2.useState)(defaultExpanded);
608
+ if (sources.length === 0) return null;
609
+ 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)(
610
+ SourceCard,
611
+ {
612
+ source,
613
+ variant,
614
+ onNavigate
615
+ },
616
+ source.document_id
617
+ )) });
618
+ if (!collapsible) {
619
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className, "data-testid": "source-list", children: content });
620
+ }
621
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className, "data-testid": "source-list", children: [
622
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
623
+ "button",
624
+ {
625
+ type: "button",
626
+ onClick: () => setIsExpanded((prev) => !prev),
627
+ "aria-expanded": isExpanded,
628
+ className: "flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wider text-text-secondary hover:text-accent mb-2 transition-colors duration-200",
629
+ children: [
630
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
631
+ "svg",
632
+ {
633
+ className: `w-4 h-4 transition-transform ${isExpanded ? "rotate-180" : ""}`,
634
+ fill: "none",
635
+ viewBox: "0 0 24 24",
636
+ stroke: "currentColor",
637
+ strokeWidth: 2,
638
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M19 9l-7 7-7-7" })
639
+ }
640
+ ),
641
+ "Sources (",
642
+ sources.length,
643
+ ")"
644
+ ]
645
+ }
646
+ ),
647
+ isExpanded && content
648
+ ] });
649
+ }
650
+
651
+ // src/response/FollowUpChips/FollowUpChips.tsx
652
+ var import_tailwind_merge3 = require("tailwind-merge");
653
+ var import_jsx_runtime5 = require("react/jsx-runtime");
654
+ function FollowUpChips({ suggestions, onSelect, className }) {
655
+ if (suggestions.length === 0) return null;
656
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
657
+ "div",
658
+ {
659
+ className: `flex gap-2 overflow-x-auto py-1 ${className ?? ""}`,
660
+ role: "group",
661
+ "aria-label": "Follow-up suggestions",
662
+ "data-testid": "follow-up-chips",
663
+ children: suggestions.map((suggestion) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
664
+ "button",
665
+ {
666
+ type: "button",
667
+ onClick: () => onSelect(suggestion),
668
+ className: (0, import_tailwind_merge3.twMerge)(
669
+ "px-4 py-1.5 rounded-full text-sm shrink-0 whitespace-nowrap",
670
+ "border border-border bg-surface text-text-primary",
671
+ "hover:bg-surface-raised hover:border-interactive hover:text-text-primary",
672
+ "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
673
+ "transition-all duration-200"
674
+ ),
675
+ children: suggestion
676
+ },
677
+ suggestion
678
+ ))
679
+ }
680
+ );
681
+ }
682
+
683
+ // src/response/AgentResponse/AgentResponse.tsx
684
+ var import_jsx_runtime6 = require("react/jsx-runtime");
685
+ function getConfidenceIntent2(overall) {
686
+ if (overall === "high") return "success";
687
+ if (overall === "medium") return "warning";
688
+ return "error";
689
+ }
690
+ function getVerificationIntent(status) {
691
+ if (status === "passed") return "success";
692
+ if (status === "flagged") return "warning";
693
+ return "error";
694
+ }
695
+ function getVerificationLabel(status) {
696
+ if (status === "passed") return "Verified";
697
+ if (status === "flagged") return "Flagged";
698
+ return "Failed";
699
+ }
700
+ function AgentResponse({
701
+ response,
702
+ showSources = true,
703
+ showConfidence = false,
704
+ showVerification = false,
705
+ onFollowUp,
706
+ onNavigateSource,
707
+ className
708
+ }) {
709
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: `flex flex-col gap-4 ${className ?? ""}`, "data-testid": "agent-response", children: [
710
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ResponseMessage, { content: response.message }),
711
+ response.ui_hint !== "text" && response.structured_data && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
712
+ StructuredResponse,
713
+ {
714
+ uiHint: response.ui_hint,
715
+ data: response.structured_data
716
+ }
717
+ ),
718
+ (showConfidence || showVerification) && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-wrap items-center gap-2 mt-1", "data-testid": "response-meta", children: [
719
+ showConfidence && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
720
+ import_core2.Badge,
721
+ {
722
+ intent: getConfidenceIntent2(response.confidence.overall),
723
+ size: "sm",
724
+ children: [
725
+ response.confidence.overall,
726
+ " confidence"
727
+ ]
728
+ }
729
+ ),
730
+ showVerification && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
731
+ import_core2.Badge,
732
+ {
733
+ intent: getVerificationIntent(response.verification.status),
734
+ size: "sm",
735
+ children: [
736
+ getVerificationLabel(response.verification.status),
737
+ " (",
738
+ response.verification.claims_verified,
739
+ "/",
740
+ response.verification.claims_checked,
741
+ ")"
742
+ ]
743
+ }
744
+ )
745
+ ] }),
746
+ showSources && response.sources.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
747
+ SourceList,
748
+ {
749
+ sources: response.sources,
750
+ variant: "compact",
751
+ collapsible: true,
752
+ defaultExpanded: false,
753
+ onNavigate: onNavigateSource
754
+ }
755
+ ),
756
+ response.follow_up_suggestions.length > 0 && onFollowUp && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
757
+ FollowUpChips,
758
+ {
759
+ suggestions: response.follow_up_suggestions,
760
+ onSelect: onFollowUp
761
+ }
762
+ )
763
+ ] });
764
+ }
765
+
766
+ // src/chat/MessageBubble/MessageBubble.tsx
767
+ var import_jsx_runtime7 = require("react/jsx-runtime");
768
+ function MessageBubble({
769
+ message,
770
+ showAgent,
771
+ showSources = true,
772
+ showConfidence = true,
773
+ showVerification = true,
774
+ className
775
+ }) {
776
+ const isUser = message.role === "user";
777
+ if (isUser) {
778
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
779
+ "div",
780
+ {
781
+ "data-message-id": message.id,
782
+ className: (0, import_tailwind_merge4.twMerge)("flex w-full justify-end", className),
783
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "max-w-[75%] rounded-[18px] rounded-br-[4px] px-4 py-2.5 bg-accent text-white whitespace-pre-wrap text-sm leading-relaxed", children: message.content })
784
+ }
785
+ );
786
+ }
787
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
788
+ "div",
789
+ {
790
+ "data-message-id": message.id,
791
+ className: (0, import_tailwind_merge4.twMerge)("flex w-full flex-col items-start gap-1.5", className),
792
+ children: [
793
+ showAgent && message.agent && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "text-[11px] font-medium uppercase tracking-[0.08em] text-text-secondary px-1", children: message.agent.replace("_agent", "").replace("_", " ") }),
794
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "max-w-[88%] rounded-[18px] rounded-tl-[4px] px-4 py-3 bg-surface-raised border border-border", children: message.response ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
795
+ AgentResponse,
796
+ {
797
+ response: message.response,
798
+ showSources,
799
+ showConfidence,
800
+ showVerification
801
+ }
802
+ ) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ResponseMessage, { content: message.content }) })
803
+ ]
804
+ }
805
+ );
806
+ }
807
+
808
+ // src/chat/MessageThread/MessageThread.tsx
809
+ var import_jsx_runtime8 = require("react/jsx-runtime");
810
+ function MessageThread({ messages, streamingSlot, showSources, showConfidence, showVerification, className }) {
811
+ const bottomRef = (0, import_react3.useRef)(null);
812
+ (0, import_react3.useEffect)(() => {
813
+ bottomRef.current?.scrollIntoView?.({ behavior: "smooth" });
814
+ }, [messages.length, streamingSlot]);
815
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
816
+ "div",
817
+ {
818
+ role: "log",
819
+ "aria-live": "polite",
820
+ "aria-label": "Message thread",
821
+ className: (0, import_tailwind_merge5.twMerge)(
822
+ "flex flex-col gap-4 overflow-y-auto flex-1 px-4 py-6",
823
+ className
824
+ ),
825
+ children: [
826
+ messages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
827
+ MessageBubble,
828
+ {
829
+ message,
830
+ showSources,
831
+ showConfidence,
832
+ showVerification
833
+ },
834
+ message.id
835
+ )),
836
+ streamingSlot,
837
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { ref: bottomRef })
838
+ ]
839
+ }
840
+ );
841
+ }
842
+
843
+ // src/chat/MessageComposer/MessageComposer.tsx
844
+ var import_tailwind_merge6 = require("tailwind-merge");
845
+ var import_react4 = require("react");
846
+ var import_jsx_runtime9 = require("react/jsx-runtime");
847
+ function MessageComposer({
848
+ onSend,
849
+ isLoading = false,
850
+ placeholder = "Type a message...",
851
+ className
852
+ }) {
853
+ const [value, setValue] = (0, import_react4.useState)("");
854
+ const textareaRef = (0, import_react4.useRef)(null);
855
+ const canSend = value.trim().length > 0 && !isLoading;
856
+ const handleSend = (0, import_react4.useCallback)(() => {
857
+ if (!canSend) return;
858
+ onSend(value.trim());
859
+ setValue("");
860
+ textareaRef.current?.focus();
861
+ }, [canSend, onSend, value]);
862
+ const handleKeyDown = (0, import_react4.useCallback)(
863
+ (e) => {
864
+ if (e.key === "Enter" && !e.shiftKey) {
865
+ e.preventDefault();
866
+ handleSend();
867
+ }
868
+ },
869
+ [handleSend]
870
+ );
871
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
872
+ "div",
873
+ {
874
+ className: (0, import_tailwind_merge6.twMerge)(
875
+ "flex items-end gap-3 border-t border-border px-4 py-3 bg-canvas",
876
+ className
877
+ ),
878
+ children: [
879
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
880
+ "textarea",
881
+ {
882
+ ref: textareaRef,
883
+ value,
884
+ onChange: (e) => setValue(e.target.value),
885
+ onKeyDown: handleKeyDown,
886
+ placeholder,
887
+ rows: 1,
888
+ disabled: isLoading,
889
+ className: (0, import_tailwind_merge6.twMerge)(
890
+ "flex-1 resize-none rounded-xl border border-border bg-surface",
891
+ "px-4 py-2.5 text-sm text-text-primary placeholder:text-text-muted",
892
+ "focus:border-transparent focus:ring-2 focus:ring-accent/40 focus:outline-none",
893
+ "disabled:opacity-50 disabled:cursor-not-allowed",
894
+ "transition-all duration-200"
895
+ ),
896
+ style: { colorScheme: "dark" },
897
+ "aria-label": "Message input"
898
+ }
899
+ ),
900
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
901
+ "button",
902
+ {
903
+ type: "button",
904
+ onClick: handleSend,
905
+ disabled: !value.trim() || isLoading,
906
+ "aria-label": "Send message",
907
+ className: (0, import_tailwind_merge6.twMerge)(
908
+ "inline-flex items-center justify-center rounded-xl px-5 py-2.5",
909
+ "text-sm font-semibold text-white shrink-0",
910
+ "transition-all duration-200",
911
+ "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
912
+ value.trim() && !isLoading ? "bg-accent hover:bg-accent-hover hover:scale-[1.02] active:scale-[0.98]" : "bg-accent/30 text-text-muted cursor-not-allowed"
913
+ ),
914
+ children: "Send"
915
+ }
916
+ )
917
+ ]
918
+ }
919
+ );
920
+ }
921
+
922
+ // src/chat/WelcomeScreen/WelcomeScreen.tsx
923
+ var import_tailwind_merge7 = require("tailwind-merge");
924
+ var import_jsx_runtime10 = require("react/jsx-runtime");
925
+ function WelcomeScreen({
926
+ title = "Welcome",
927
+ message = "How can I help you today?",
928
+ suggestedQuestions = [],
929
+ onQuestionSelect,
930
+ className
931
+ }) {
932
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
933
+ "div",
934
+ {
935
+ className: (0, import_tailwind_merge7.twMerge)(
936
+ "flex flex-1 flex-col items-center justify-center gap-8 p-8 text-center",
937
+ className
938
+ ),
939
+ children: [
940
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
941
+ "div",
942
+ {
943
+ className: "w-14 h-14 rounded-2xl bg-accent/10 border border-border flex items-center justify-center pulse-glow",
944
+ "aria-hidden": "true",
945
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "text-2xl", children: "\u2726" })
946
+ }
947
+ ),
948
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex flex-col gap-2", children: [
949
+ title && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { className: "text-2xl font-semibold text-text-primary", children: title }),
950
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-text-secondary text-base leading-relaxed max-w-sm", children: message })
951
+ ] }),
952
+ suggestedQuestions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
953
+ "div",
954
+ {
955
+ className: "flex flex-wrap justify-center gap-2 max-w-md",
956
+ role: "group",
957
+ "aria-label": "Suggested questions",
958
+ children: suggestedQuestions.map((question) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
959
+ "button",
960
+ {
961
+ type: "button",
962
+ onClick: () => onQuestionSelect?.(question),
963
+ className: (0, import_tailwind_merge7.twMerge)(
964
+ "px-4 py-2 rounded-full text-sm",
965
+ "border border-border bg-surface text-text-primary",
966
+ "hover:bg-surface-raised hover:border-interactive hover:text-text-primary",
967
+ "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
968
+ "transition-colors duration-200"
969
+ ),
970
+ children: question
971
+ },
972
+ question
973
+ ))
974
+ }
975
+ )
976
+ ]
977
+ }
978
+ );
979
+ }
980
+
981
+ // src/streaming/StreamingMessage/StreamingMessage.tsx
982
+ var import_react6 = require("react");
983
+ var import_core3 = require("@surf-kit/core");
984
+
985
+ // src/hooks/useCharacterDrain.ts
986
+ var import_react5 = require("react");
987
+ function useCharacterDrain(target, msPerChar = 15) {
988
+ const [displayed, setDisplayed] = (0, import_react5.useState)("");
989
+ const indexRef = (0, import_react5.useRef)(0);
990
+ const lastTimeRef = (0, import_react5.useRef)(0);
991
+ const rafRef = (0, import_react5.useRef)(null);
992
+ const drainTargetRef = (0, import_react5.useRef)("");
993
+ const msPerCharRef = (0, import_react5.useRef)(msPerChar);
994
+ msPerCharRef.current = msPerChar;
995
+ if (target !== "") {
996
+ drainTargetRef.current = target;
997
+ }
998
+ const drainTarget = drainTargetRef.current;
999
+ const isDraining = displayed.length < drainTarget.length;
1000
+ const tickRef = (0, import_react5.useRef)(() => {
1001
+ });
1002
+ tickRef.current = (now) => {
1003
+ const currentTarget = drainTargetRef.current;
1004
+ if (currentTarget === "") {
1005
+ rafRef.current = null;
1006
+ return;
1007
+ }
1008
+ if (lastTimeRef.current === 0) lastTimeRef.current = now;
1009
+ const elapsed = now - lastTimeRef.current;
1010
+ const charsToAdvance = Math.floor(elapsed / msPerCharRef.current);
1011
+ if (charsToAdvance > 0 && indexRef.current < currentTarget.length) {
1012
+ const nextIndex = Math.min(indexRef.current + charsToAdvance, currentTarget.length);
1013
+ indexRef.current = nextIndex;
1014
+ lastTimeRef.current = now;
1015
+ setDisplayed(currentTarget.slice(0, nextIndex));
1016
+ }
1017
+ if (indexRef.current < currentTarget.length) {
1018
+ rafRef.current = requestAnimationFrame((t) => tickRef.current(t));
1019
+ } else {
1020
+ rafRef.current = null;
1021
+ }
1022
+ };
1023
+ (0, import_react5.useEffect)(() => {
1024
+ if (drainTargetRef.current !== "" && indexRef.current < drainTargetRef.current.length && rafRef.current === null) {
1025
+ rafRef.current = requestAnimationFrame((t) => tickRef.current(t));
1026
+ }
1027
+ }, [drainTarget]);
1028
+ (0, import_react5.useEffect)(() => {
1029
+ if (target === "" && !isDraining && displayed !== "") {
1030
+ indexRef.current = 0;
1031
+ lastTimeRef.current = 0;
1032
+ drainTargetRef.current = "";
1033
+ setDisplayed("");
1034
+ }
1035
+ }, [target, isDraining, displayed]);
1036
+ (0, import_react5.useEffect)(() => {
1037
+ return () => {
1038
+ if (rafRef.current !== null) {
1039
+ cancelAnimationFrame(rafRef.current);
1040
+ rafRef.current = null;
1041
+ }
1042
+ };
1043
+ }, []);
1044
+ return { displayed, isDraining };
1045
+ }
1046
+
1047
+ // src/streaming/StreamingMessage/StreamingMessage.tsx
1048
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1049
+ var phaseLabels = {
1050
+ idle: "",
1051
+ waiting: "Waiting",
1052
+ thinking: "Thinking",
1053
+ retrieving: "Searching",
1054
+ generating: "Writing",
1055
+ verifying: "Verifying"
1056
+ };
1057
+ function StreamingMessage({
1058
+ stream,
1059
+ onComplete,
1060
+ showPhases = true,
1061
+ className
1062
+ }) {
1063
+ const onCompleteRef = (0, import_react6.useRef)(onComplete);
1064
+ onCompleteRef.current = onComplete;
1065
+ const wasActiveRef = (0, import_react6.useRef)(stream.active);
1066
+ (0, import_react6.useEffect)(() => {
1067
+ if (wasActiveRef.current && !stream.active) {
1068
+ onCompleteRef.current?.();
1069
+ }
1070
+ wasActiveRef.current = stream.active;
1071
+ }, [stream.active]);
1072
+ const phaseLabel = phaseLabels[stream.phase];
1073
+ const { displayed: displayedContent } = useCharacterDrain(stream.content);
1074
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className, "data-testid": "streaming-message", children: [
1075
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { "aria-live": "assertive", className: "sr-only", children: [
1076
+ stream.active && stream.phase !== "idle" && "Response started",
1077
+ !stream.active && stream.content && "Response complete"
1078
+ ] }),
1079
+ showPhases && stream.active && stream.phase !== "idle" && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1080
+ "div",
1081
+ {
1082
+ className: "flex items-center gap-2 mb-2 text-sm text-text-secondary",
1083
+ "data-testid": "phase-indicator",
1084
+ children: [
1085
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_core3.Spinner, { size: "sm" }) }),
1086
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: phaseLabel })
1087
+ ]
1088
+ }
1089
+ ),
1090
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "text-text-primary whitespace-pre-wrap", children: [
1091
+ displayedContent,
1092
+ stream.active && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1093
+ "span",
1094
+ {
1095
+ className: "inline-block w-0.5 h-4 bg-accent align-text-bottom animate-pulse ml-0.5",
1096
+ "aria-hidden": "true",
1097
+ "data-testid": "streaming-cursor"
1098
+ }
1099
+ )
1100
+ ] })
1101
+ ] });
1102
+ }
1103
+
1104
+ // src/chat/AgentChat/AgentChat.tsx
1105
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1106
+ function AgentChat({
1107
+ endpoint,
1108
+ title = "Chat",
1109
+ welcomeTitle,
1110
+ welcomeMessage = "How can I help you today?",
1111
+ suggestedQuestions = [],
1112
+ showHeader = true,
1113
+ showWelcomeTitle = true,
1114
+ showSources,
1115
+ showConfidence,
1116
+ showVerification,
1117
+ className
1118
+ }) {
1119
+ const { state, actions } = useAgentChat({ apiUrl: endpoint });
1120
+ const hasMessages = state.messages.length > 0;
1121
+ const handleSend = (content) => {
1122
+ void actions.sendMessage(content);
1123
+ };
1124
+ const handleQuestionSelect = (question) => {
1125
+ void actions.sendMessage(question);
1126
+ };
1127
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1128
+ "div",
1129
+ {
1130
+ className: (0, import_tailwind_merge8.twMerge)(
1131
+ "flex flex-col h-full bg-canvas border border-border rounded-xl overflow-hidden",
1132
+ className
1133
+ ),
1134
+ children: [
1135
+ showHeader && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex items-center justify-between border-b border-border px-4 py-3 bg-surface-raised shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h1", { className: "text-base font-semibold text-text-primary", children: title }) }),
1136
+ hasMessages ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1137
+ MessageThread,
1138
+ {
1139
+ messages: state.messages,
1140
+ streamingSlot: state.isLoading ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1141
+ StreamingMessage,
1142
+ {
1143
+ stream: {
1144
+ active: state.isLoading,
1145
+ phase: state.streamPhase,
1146
+ content: state.streamingContent,
1147
+ sources: [],
1148
+ agent: null,
1149
+ agentLabel: null
1150
+ }
1151
+ }
1152
+ ) : void 0,
1153
+ showSources,
1154
+ showConfidence,
1155
+ showVerification
1156
+ }
1157
+ ) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1158
+ WelcomeScreen,
1159
+ {
1160
+ title: showWelcomeTitle ? welcomeTitle ?? title : "",
1161
+ message: welcomeMessage,
1162
+ suggestedQuestions,
1163
+ onQuestionSelect: handleQuestionSelect
1164
+ }
1165
+ ),
1166
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(MessageComposer, { onSend: handleSend, isLoading: state.isLoading })
1167
+ ]
1168
+ }
1169
+ );
1170
+ }
1171
+
1172
+ // src/response/ErrorResponse/ErrorResponse.tsx
1173
+ var import_core4 = require("@surf-kit/core");
1174
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1175
+ function ErrorResponse({ error, onRetry, className }) {
1176
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { role: "alert", className, "data-testid": "error-response", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_core4.Alert, { intent: "error", title: "Error", children: [
1177
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("p", { children: error.message }),
1178
+ error.retryable && onRetry && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "mt-3", children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1179
+ import_core4.Button,
1180
+ {
1181
+ intent: "secondary",
1182
+ size: "sm",
1183
+ onPress: onRetry,
1184
+ "aria-label": "Retry",
1185
+ children: "Retry"
1186
+ }
1187
+ ) })
1188
+ ] }) });
1189
+ }
1190
+
1191
+ // src/sources/SourceInline/SourceInline.tsx
1192
+ var import_core5 = require("@surf-kit/core");
1193
+ var import_jsx_runtime14 = require("react/jsx-runtime");
1194
+ function SourceInline({ source, index, className }) {
1195
+ const tooltipContent = `${source.title}${source.section ? ` - ${source.section}` : ""}: ${source.snippet.slice(0, 120)}${source.snippet.length > 120 ? "..." : ""}`;
1196
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_core5.Tooltip, { content: tooltipContent, placement: "top", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
1197
+ "span",
1198
+ {
1199
+ className: `inline-flex items-center justify-center text-xs text-accent font-medium cursor-help ${className ?? ""}`,
1200
+ "data-testid": "source-inline",
1201
+ "data-document-id": source.document_id,
1202
+ "aria-label": `Source ${index}: ${source.title}`,
1203
+ children: [
1204
+ "[",
1205
+ index,
1206
+ "]"
1207
+ ]
1208
+ }
1209
+ ) });
1210
+ }
1211
+
1212
+ // src/sources/SourceDrawer/SourceDrawer.tsx
1213
+ var import_core6 = require("@surf-kit/core");
1214
+ var import_jsx_runtime15 = require("react/jsx-runtime");
1215
+ function getConfidenceIntent3(confidence) {
1216
+ if (confidence >= 0.8) return "success";
1217
+ if (confidence >= 0.5) return "warning";
1218
+ return "error";
1219
+ }
1220
+ function SourceDrawer({ source, isOpen, onClose, className }) {
1221
+ if (!source) return null;
1222
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1223
+ import_core6.Sheet,
1224
+ {
1225
+ isOpen,
1226
+ onClose,
1227
+ title: source.title,
1228
+ size: "md",
1229
+ className,
1230
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { "data-testid": "source-drawer", "data-document-id": source.document_id, children: [
1231
+ source.section && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm text-text-secondary mb-4", children: source.section }),
1232
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2 mb-4", children: [
1233
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-sm text-text-secondary", children: "Confidence:" }),
1234
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_core6.Badge, { intent: getConfidenceIntent3(source.confidence), size: "sm", children: [
1235
+ Math.round(source.confidence * 100),
1236
+ "%"
1237
+ ] })
1238
+ ] }),
1239
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "mb-4", children: [
1240
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h3", { className: "text-sm font-medium text-text-primary mb-2", children: "Snippet" }),
1241
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-sm text-text-secondary bg-surface-raised p-4 rounded-lg", children: source.snippet })
1242
+ ] }),
1243
+ source.url && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
1244
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("h3", { className: "text-sm font-medium text-text-primary mb-2", children: "Source URL" }),
1245
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
1246
+ "a",
1247
+ {
1248
+ href: source.url,
1249
+ target: "_blank",
1250
+ rel: "noopener noreferrer",
1251
+ className: "text-sm text-accent hover:underline break-all",
1252
+ children: source.url
1253
+ }
1254
+ )
1255
+ ] })
1256
+ ] })
1257
+ }
1258
+ );
1259
+ }
1260
+
1261
+ // src/sources/SourceBadge/SourceBadge.tsx
1262
+ var import_core7 = require("@surf-kit/core");
1263
+ var import_jsx_runtime16 = require("react/jsx-runtime");
1264
+ function SourceBadge({ count, className }) {
1265
+ if (count === 0) return null;
1266
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_core7.Badge, { intent: "info", size: "sm", className, "data-testid": "source-badge", children: [
1267
+ count,
1268
+ " ",
1269
+ count === 1 ? "source" : "sources"
1270
+ ] });
1271
+ }
1272
+
1273
+ // src/confidence/ConfidenceBadge/ConfidenceBadge.tsx
1274
+ var import_core8 = require("@surf-kit/core");
1275
+ var import_jsx_runtime17 = require("react/jsx-runtime");
1276
+ var intentMap = {
1277
+ high: "success",
1278
+ medium: "warning",
1279
+ low: "error"
1280
+ };
1281
+ var labelMap = {
1282
+ high: "High confidence",
1283
+ medium: "Medium confidence",
1284
+ low: "Low confidence"
1285
+ };
1286
+ function ConfidenceBadge({ confidence, className }) {
1287
+ const level = confidence.overall;
1288
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1289
+ import_core8.Badge,
1290
+ {
1291
+ intent: intentMap[level],
1292
+ size: "sm",
1293
+ role: "status",
1294
+ "aria-label": labelMap[level],
1295
+ className,
1296
+ children: level.charAt(0).toUpperCase() + level.slice(1)
1297
+ }
1298
+ );
1299
+ }
1300
+
1301
+ // src/confidence/ConfidenceBreakdown/ConfidenceBreakdown.tsx
1302
+ var import_react7 = require("react");
1303
+
1304
+ // src/confidence/ConfidenceMeter/ConfidenceMeter.tsx
1305
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1306
+ function getBarColor(value) {
1307
+ if (value >= 0.8) return "bg-green-500";
1308
+ if (value >= 0.5) return "bg-yellow-500";
1309
+ return "bg-red-500";
1310
+ }
1311
+ function ConfidenceMeter({ value, label, className }) {
1312
+ const clamped = Math.max(0, Math.min(1, value));
1313
+ const pct = Math.round(clamped * 100);
1314
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className, "data-testid": "confidence-meter", children: [
1315
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex items-center justify-between mb-1", children: [
1316
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-xs font-medium text-text-secondary", children: label }),
1317
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "text-xs text-text-secondary", children: [
1318
+ pct,
1319
+ "%"
1320
+ ] })
1321
+ ] }),
1322
+ /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1323
+ "div",
1324
+ {
1325
+ className: "h-2 w-full rounded-full bg-surface-secondary overflow-hidden",
1326
+ role: "progressbar",
1327
+ "aria-valuenow": pct,
1328
+ "aria-valuemin": 0,
1329
+ "aria-valuemax": 100,
1330
+ "aria-label": `${label}: ${pct}%`,
1331
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1332
+ "div",
1333
+ {
1334
+ className: `h-full rounded-full transition-all duration-300 ${getBarColor(clamped)}`,
1335
+ style: { width: `${pct}%` }
1336
+ }
1337
+ )
1338
+ }
1339
+ )
1340
+ ] });
1341
+ }
1342
+
1343
+ // src/confidence/ConfidenceBreakdown/ConfidenceBreakdown.tsx
1344
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1345
+ var dimensions = [
1346
+ { key: "retrieval_quality", label: "Retrieval Quality" },
1347
+ { key: "source_authority", label: "Source Authority" },
1348
+ { key: "answer_groundedness", label: "Answer Groundedness" },
1349
+ { key: "recency", label: "Recency" }
1350
+ ];
1351
+ function ConfidenceBreakdown({
1352
+ confidence,
1353
+ expandable = true,
1354
+ defaultExpanded = false,
1355
+ className
1356
+ }) {
1357
+ const [expanded, setExpanded] = (0, import_react7.useState)(defaultExpanded);
1358
+ const isExpanded = expandable ? expanded : true;
1359
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: `rounded-xl border border-border bg-surface ${className ?? ""}`, "data-testid": "confidence-breakdown", children: [
1360
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
1361
+ "button",
1362
+ {
1363
+ type: "button",
1364
+ className: "flex w-full items-center justify-between px-4 py-3 text-left",
1365
+ onClick: () => expandable && setExpanded((prev) => !prev),
1366
+ "aria-expanded": isExpanded,
1367
+ disabled: !expandable,
1368
+ children: [
1369
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "text-sm font-medium text-text-primary", children: "Confidence" }),
1370
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ConfidenceBadge, { confidence })
1371
+ ]
1372
+ }
1373
+ ),
1374
+ isExpanded && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
1375
+ "div",
1376
+ {
1377
+ className: "border-t border-border px-4 py-3 space-y-3 animate-in fade-in slide-in-from-top-1 duration-200",
1378
+ "data-testid": "confidence-breakdown-details",
1379
+ children: [
1380
+ dimensions.map((dim) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1381
+ ConfidenceMeter,
1382
+ {
1383
+ value: confidence[dim.key],
1384
+ label: dim.label
1385
+ },
1386
+ dim.key
1387
+ )),
1388
+ confidence.reasoning && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-xs text-text-secondary mt-2", children: confidence.reasoning })
1389
+ ]
1390
+ }
1391
+ )
1392
+ ] });
1393
+ }
1394
+
1395
+ // src/confidence/VerificationBadge/VerificationBadge.tsx
1396
+ var import_core9 = require("@surf-kit/core");
1397
+ var import_jsx_runtime20 = require("react/jsx-runtime");
1398
+ var config = {
1399
+ passed: {
1400
+ intent: "success",
1401
+ label: "Verified",
1402
+ icon: "\u2713",
1403
+ ariaLabel: "Verification passed"
1404
+ },
1405
+ flagged: {
1406
+ intent: "warning",
1407
+ label: "Flagged",
1408
+ icon: "\u26A0",
1409
+ ariaLabel: "Verification flagged"
1410
+ },
1411
+ failed: {
1412
+ intent: "error",
1413
+ label: "Failed",
1414
+ icon: "\u2717",
1415
+ ariaLabel: "Verification failed"
1416
+ }
1417
+ };
1418
+ function VerificationBadge({ verification, className }) {
1419
+ const { intent, label, icon, ariaLabel } = config[verification.status];
1420
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
1421
+ import_core9.Badge,
1422
+ {
1423
+ intent,
1424
+ size: "sm",
1425
+ role: "status",
1426
+ "aria-label": ariaLabel,
1427
+ className,
1428
+ children: [
1429
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { "aria-hidden": "true", className: "mr-1", children: icon }),
1430
+ label
1431
+ ]
1432
+ }
1433
+ );
1434
+ }
1435
+
1436
+ // src/confidence/VerificationDetail/VerificationDetail.tsx
1437
+ var import_react8 = require("react");
1438
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1439
+ function VerificationDetail({
1440
+ verification,
1441
+ expandable = true,
1442
+ defaultExpanded = false,
1443
+ className
1444
+ }) {
1445
+ const [expanded, setExpanded] = (0, import_react8.useState)(defaultExpanded);
1446
+ const isExpanded = expandable ? expanded : true;
1447
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: `rounded-xl border border-border bg-surface ${className ?? ""}`, "data-testid": "verification-detail", children: [
1448
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
1449
+ "button",
1450
+ {
1451
+ type: "button",
1452
+ className: "flex w-full items-center justify-between px-4 py-3 text-left",
1453
+ onClick: () => expandable && setExpanded((prev) => !prev),
1454
+ "aria-expanded": isExpanded,
1455
+ disabled: !expandable,
1456
+ children: [
1457
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-sm font-medium text-text-primary", children: "Verification" }),
1458
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(VerificationBadge, { verification })
1459
+ ]
1460
+ }
1461
+ ),
1462
+ isExpanded && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
1463
+ "div",
1464
+ {
1465
+ className: "border-t border-border px-4 py-3 space-y-2 animate-in fade-in slide-in-from-top-1 duration-200",
1466
+ "data-testid": "verification-detail-content",
1467
+ children: [
1468
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex gap-4 text-xs text-text-secondary", children: [
1469
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { "data-testid": "claims-checked", children: [
1470
+ "Claims checked: ",
1471
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("strong", { className: "text-text-primary", children: verification.claims_checked })
1472
+ ] }),
1473
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { "data-testid": "claims-verified", children: [
1474
+ "Claims verified: ",
1475
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("strong", { className: "text-text-primary", children: verification.claims_verified })
1476
+ ] })
1477
+ ] }),
1478
+ verification.flags.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("ul", { className: "mt-2 space-y-1", "data-testid": "verification-flags", children: verification.flags.map((flag, i) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("li", { className: "flex items-start gap-2 text-xs text-text-secondary", children: [
1479
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-yellow-500 shrink-0", "aria-hidden": "true", children: "\u26A0" }),
1480
+ flag
1481
+ ] }, i)) })
1482
+ ]
1483
+ }
1484
+ )
1485
+ ] });
1486
+ }
1487
+
1488
+ // src/hooks/useAgentTheme.ts
1489
+ var import_react9 = require("react");
1490
+ var DEFAULT_ACCENT = "#6366f1";
1491
+ var DEFAULT_LABEL = "Agent";
1492
+ function useAgentTheme(agentId, agentThemes) {
1493
+ return (0, import_react9.useMemo)(() => {
1494
+ if (!agentId) {
1495
+ return { accent: DEFAULT_ACCENT, icon: null, label: DEFAULT_LABEL };
1496
+ }
1497
+ const theme = agentThemes?.[agentId];
1498
+ if (!theme) {
1499
+ return { accent: DEFAULT_ACCENT, icon: null, label: agentId };
1500
+ }
1501
+ return {
1502
+ accent: theme.accent ?? DEFAULT_ACCENT,
1503
+ icon: theme.icon ?? null,
1504
+ label: theme.label
1505
+ };
1506
+ }, [agentId, agentThemes]);
1507
+ }
1508
+
1509
+ // src/agent-identity/AgentAvatar/AgentAvatar.tsx
1510
+ var import_jsx_runtime22 = require("react/jsx-runtime");
1511
+ var sizeMap = {
1512
+ sm: "h-6 w-6 text-xs",
1513
+ md: "h-8 w-8 text-sm",
1514
+ lg: "h-10 w-10 text-base"
1515
+ };
1516
+ var iconSizeMap = {
1517
+ sm: 14,
1518
+ md: 18,
1519
+ lg: 22
1520
+ };
1521
+ function AgentAvatar({ agentId, agent, size = "md", agentThemes, className }) {
1522
+ const resolvedId = agent?.id ?? agentId ?? null;
1523
+ const themes = agent ? { ...agentThemes, [agent.id]: agent } : agentThemes;
1524
+ const { accent, icon: Icon, label } = useAgentTheme(resolvedId, themes);
1525
+ const initial = label.charAt(0).toUpperCase();
1526
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1527
+ "div",
1528
+ {
1529
+ className: `inline-flex items-center justify-center rounded-full shrink-0 font-medium text-white ${sizeMap[size]} ${className ?? ""}`,
1530
+ style: { backgroundColor: accent },
1531
+ role: "img",
1532
+ "aria-label": `${label} avatar`,
1533
+ "data-testid": "agent-avatar",
1534
+ children: Icon ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Icon, { size: iconSizeMap[size], className: "text-white" }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { "aria-hidden": "true", children: initial })
1535
+ }
1536
+ );
1537
+ }
1538
+
1539
+ // src/agent-identity/AgentLabel/AgentLabel.tsx
1540
+ var import_jsx_runtime23 = require("react/jsx-runtime");
1541
+ function AgentLabel({ agent, className }) {
1542
+ const accent = agent.accent ?? "#6366f1";
1543
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
1544
+ "span",
1545
+ {
1546
+ className: `text-xs font-medium ${className ?? ""}`,
1547
+ "data-testid": "agent-label",
1548
+ children: [
1549
+ "Answered by",
1550
+ " ",
1551
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { style: { color: accent }, children: agent.label })
1552
+ ]
1553
+ }
1554
+ );
1555
+ }
1556
+
1557
+ // src/agent-identity/AgentHandoff/AgentHandoff.tsx
1558
+ var import_jsx_runtime24 = require("react/jsx-runtime");
1559
+ function AgentHandoff({ from, to, className }) {
1560
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
1561
+ "div",
1562
+ {
1563
+ className: `flex items-center gap-3 px-4 py-3 rounded-xl border border-border bg-surface ${className ?? ""}`,
1564
+ "data-testid": "agent-handoff",
1565
+ children: [
1566
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex items-center gap-2 animate-in fade-in slide-in-from-left-2 duration-300", children: [
1567
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(AgentAvatar, { agent: from, size: "sm" }),
1568
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "text-xs text-text-secondary", children: from.label })
1569
+ ] }),
1570
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "text-text-secondary text-xs", "aria-hidden": "true", children: "\u2192" }),
1571
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex items-center gap-2 animate-in fade-in slide-in-from-right-2 duration-300", children: [
1572
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(AgentAvatar, { agent: to, size: "sm" }),
1573
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "text-xs text-text-secondary", children: to.label })
1574
+ ] }),
1575
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "sr-only", role: "status", "aria-live": "polite", children: [
1576
+ "Handing off from ",
1577
+ from.label,
1578
+ " to ",
1579
+ to.label
1580
+ ] })
1581
+ ]
1582
+ }
1583
+ );
1584
+ }
1585
+
1586
+ // src/agent-identity/RoutingIndicator/RoutingIndicator.tsx
1587
+ var import_jsx_runtime25 = require("react/jsx-runtime");
1588
+ function RoutingIndicator({ from, to, reason, className }) {
1589
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
1590
+ "div",
1591
+ {
1592
+ className: `inline-flex items-center gap-1.5 rounded-md border border-border/50 bg-surface-secondary/50 px-2.5 py-1 font-mono text-[11px] text-text-secondary ${className ?? ""}`,
1593
+ "data-testid": "routing-indicator",
1594
+ children: [
1595
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "opacity-60", children: "Routed:" }),
1596
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: from }),
1597
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "opacity-40", "aria-hidden": "true", children: "\u2192" }),
1598
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: to }),
1599
+ reason && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "opacity-50 ml-1", title: reason, children: [
1600
+ "(",
1601
+ reason,
1602
+ ")"
1603
+ ] })
1604
+ ]
1605
+ }
1606
+ );
1607
+ }
1608
+
1609
+ // src/streaming/ThinkingIndicator/ThinkingIndicator.tsx
1610
+ var import_hooks = require("@surf-kit/hooks");
1611
+ var import_jsx_runtime26 = require("react/jsx-runtime");
1612
+ function ThinkingIndicator({ label = "Thinking...", className }) {
1613
+ const reducedMotion = (0, import_hooks.useReducedMotion)();
1614
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
1615
+ "span",
1616
+ {
1617
+ role: "status",
1618
+ className: `inline-flex items-center gap-2 text-sm ${className ?? ""}`,
1619
+ "data-testid": "thinking-indicator",
1620
+ children: [
1621
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "text-text-secondary", children: label }),
1622
+ !reducedMotion && /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("span", { className: "flex gap-1", "aria-hidden": "true", "data-testid": "animated-dots", children: [
1623
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "w-1.5 h-1.5 rounded-full bg-current animate-bounce [animation-delay:0ms]" }),
1624
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "w-1.5 h-1.5 rounded-full bg-current animate-bounce [animation-delay:150ms]" }),
1625
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "w-1.5 h-1.5 rounded-full bg-current animate-bounce [animation-delay:300ms]" })
1626
+ ] })
1627
+ ]
1628
+ }
1629
+ );
1630
+ }
1631
+
1632
+ // src/streaming/ToolExecution/ToolExecution.tsx
1633
+ var import_core10 = require("@surf-kit/core");
1634
+ var import_jsx_runtime27 = require("react/jsx-runtime");
1635
+ var defaultLabels = {
1636
+ search: "Searching knowledge base...",
1637
+ retrieve: "Retrieving documents...",
1638
+ calculate: "Calculating..."
1639
+ };
1640
+ function ToolExecution({ tool, label, className }) {
1641
+ const displayLabel = label ?? defaultLabels[tool] ?? `Running ${tool}...`;
1642
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
1643
+ "div",
1644
+ {
1645
+ className: `flex items-center gap-2 text-sm text-text-secondary ${className ?? ""}`,
1646
+ role: "status",
1647
+ "data-testid": "tool-execution",
1648
+ children: [
1649
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_core10.Spinner, { size: "sm" }) }),
1650
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { children: displayLabel })
1651
+ ]
1652
+ }
1653
+ );
1654
+ }
1655
+
1656
+ // src/streaming/RetrievalProgress/RetrievalProgress.tsx
1657
+ var import_core11 = require("@surf-kit/core");
1658
+ var import_jsx_runtime28 = require("react/jsx-runtime");
1659
+ function RetrievalProgress({ sources, isActive, className }) {
1660
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
1661
+ "div",
1662
+ {
1663
+ className: `space-y-2 ${className ?? ""}`,
1664
+ role: "status",
1665
+ "aria-label": isActive ? `Retrieving sources, ${sources.length} found so far` : `${sources.length} sources found`,
1666
+ "data-testid": "retrieval-progress",
1667
+ children: [
1668
+ isActive && /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex items-center gap-2 text-sm text-text-secondary", children: [
1669
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_core11.Spinner, { size: "sm" }) }),
1670
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { children: "Retrieving sources..." })
1671
+ ] }),
1672
+ 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)(
1673
+ "li",
1674
+ {
1675
+ className: "text-sm text-text-secondary flex items-center gap-2 animate-in fade-in slide-in-from-left-2",
1676
+ style: { animationDelay: `${index * 100}ms`, animationFillMode: "both" },
1677
+ "data-testid": "retrieval-source-item",
1678
+ children: [
1679
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "w-1.5 h-1.5 rounded-full bg-accent flex-shrink-0", "aria-hidden": "true" }),
1680
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "truncate", children: source.title })
1681
+ ]
1682
+ },
1683
+ source.document_id
1684
+ )) })
1685
+ ]
1686
+ }
1687
+ );
1688
+ }
1689
+
1690
+ // src/streaming/VerificationProgress/VerificationProgress.tsx
1691
+ var import_jsx_runtime29 = require("react/jsx-runtime");
1692
+ function VerificationProgress({
1693
+ isActive,
1694
+ label = "Checking accuracy...",
1695
+ className
1696
+ }) {
1697
+ if (!isActive) return null;
1698
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
1699
+ "div",
1700
+ {
1701
+ className: `flex items-center gap-2 text-sm ${className ?? ""}`,
1702
+ role: "status",
1703
+ "data-testid": "verification-progress",
1704
+ children: [
1705
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
1706
+ "svg",
1707
+ {
1708
+ className: "w-4 h-4 animate-spin text-accent",
1709
+ xmlns: "http://www.w3.org/2000/svg",
1710
+ fill: "none",
1711
+ viewBox: "0 0 24 24",
1712
+ "aria-hidden": "true",
1713
+ children: [
1714
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
1715
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" })
1716
+ ]
1717
+ }
1718
+ ),
1719
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: "text-accent animate-pulse", children: label })
1720
+ ]
1721
+ }
1722
+ );
1723
+ }
1724
+
1725
+ // src/streaming/TypewriterText/TypewriterText.tsx
1726
+ var import_react10 = require("react");
1727
+ var import_jsx_runtime30 = require("react/jsx-runtime");
1728
+ function TypewriterText({
1729
+ text,
1730
+ speed = 30,
1731
+ delay = 0,
1732
+ onComplete,
1733
+ className = "",
1734
+ showCursor = true
1735
+ }) {
1736
+ const [displayedText, setDisplayedText] = (0, import_react10.useState)("");
1737
+ const [isComplete, setIsComplete] = (0, import_react10.useState)(false);
1738
+ (0, import_react10.useEffect)(() => {
1739
+ setDisplayedText("");
1740
+ setIsComplete(false);
1741
+ let index = 0;
1742
+ let interval;
1743
+ const timeout = setTimeout(() => {
1744
+ interval = setInterval(() => {
1745
+ if (index < text.length) {
1746
+ setDisplayedText(text.slice(0, index + 1));
1747
+ index++;
1748
+ } else {
1749
+ clearInterval(interval);
1750
+ setIsComplete(true);
1751
+ onComplete?.();
1752
+ }
1753
+ }, speed);
1754
+ }, delay);
1755
+ return () => {
1756
+ clearTimeout(timeout);
1757
+ clearInterval(interval);
1758
+ };
1759
+ }, [text, speed, delay, onComplete]);
1760
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("span", { className, children: [
1761
+ displayedText,
1762
+ showCursor && !isComplete && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "typewriter-cursor", "aria-hidden": "true" })
1763
+ ] });
1764
+ }
1765
+
1766
+ // src/chat/ConversationList/ConversationList.tsx
1767
+ var import_tailwind_merge9 = require("tailwind-merge");
1768
+ var import_jsx_runtime31 = require("react/jsx-runtime");
1769
+ function ConversationList({
1770
+ conversations,
1771
+ activeId,
1772
+ onSelect,
1773
+ onDelete,
1774
+ onNew,
1775
+ className
1776
+ }) {
1777
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
1778
+ "nav",
1779
+ {
1780
+ "aria-label": "Conversation list",
1781
+ className: (0, import_tailwind_merge9.twMerge)("flex flex-col h-full bg-canvas", className),
1782
+ children: [
1783
+ onNew && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "p-3 border-b border-border", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1784
+ "button",
1785
+ {
1786
+ type: "button",
1787
+ onClick: onNew,
1788
+ className: "w-full px-4 py-2.5 rounded-xl text-sm font-semibold bg-accent text-white hover:bg-accent-hover transition-all duration-200",
1789
+ children: "New conversation"
1790
+ }
1791
+ ) }),
1792
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("ul", { role: "list", className: "flex-1 overflow-y-auto", children: [
1793
+ conversations.map((conversation) => {
1794
+ const isActive = conversation.id === activeId;
1795
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
1796
+ "li",
1797
+ {
1798
+ className: (0, import_tailwind_merge9.twMerge)(
1799
+ "flex items-start border-b border-border transition-colors duration-200",
1800
+ "hover:bg-surface",
1801
+ isActive && "bg-surface-raised border-l-2 border-l-accent"
1802
+ ),
1803
+ children: [
1804
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
1805
+ "button",
1806
+ {
1807
+ type: "button",
1808
+ onClick: () => onSelect(conversation.id),
1809
+ "aria-current": isActive ? "true" : void 0,
1810
+ className: "flex-1 min-w-0 text-left px-4 py-3",
1811
+ children: [
1812
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "text-sm font-medium text-brand-cream truncate", children: conversation.title }),
1813
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "text-xs text-brand-cream/40 truncate mt-0.5 leading-relaxed", children: conversation.lastMessage })
1814
+ ]
1815
+ }
1816
+ ),
1817
+ onDelete && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
1818
+ "button",
1819
+ {
1820
+ type: "button",
1821
+ onClick: () => onDelete(conversation.id),
1822
+ "aria-label": `Delete ${conversation.title}`,
1823
+ className: "shrink-0 p-1.5 m-2 rounded-lg text-brand-cream/25 hover:text-brand-watermelon hover:bg-brand-watermelon/10 transition-colors duration-200",
1824
+ children: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
1825
+ "svg",
1826
+ {
1827
+ xmlns: "http://www.w3.org/2000/svg",
1828
+ width: "14",
1829
+ height: "14",
1830
+ viewBox: "0 0 24 24",
1831
+ fill: "none",
1832
+ stroke: "currentColor",
1833
+ strokeWidth: "2",
1834
+ strokeLinecap: "round",
1835
+ strokeLinejoin: "round",
1836
+ "aria-hidden": "true",
1837
+ children: [
1838
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("polyline", { points: "3 6 5 6 21 6" }),
1839
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
1840
+ ]
1841
+ }
1842
+ )
1843
+ }
1844
+ )
1845
+ ]
1846
+ },
1847
+ conversation.id
1848
+ );
1849
+ }),
1850
+ conversations.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("li", { className: "px-4 py-8 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "text-sm text-brand-cream/30 font-body", children: "No conversations yet" }) })
1851
+ ] })
1852
+ ]
1853
+ }
1854
+ );
1855
+ }
1856
+
1857
+ // src/layouts/AgentFullPage/AgentFullPage.tsx
1858
+ var import_tailwind_merge10 = require("tailwind-merge");
1859
+ var import_react11 = require("react");
1860
+ var import_jsx_runtime32 = require("react/jsx-runtime");
1861
+ function AgentFullPage({
1862
+ endpoint,
1863
+ title = "Chat",
1864
+ showConversationList = false,
1865
+ conversations = [],
1866
+ activeConversationId,
1867
+ onConversationSelect,
1868
+ onConversationDelete,
1869
+ onNewConversation,
1870
+ className
1871
+ }) {
1872
+ const [sidebarOpen, setSidebarOpen] = (0, import_react11.useState)(false);
1873
+ const handleSelect = (0, import_react11.useCallback)(
1874
+ (id) => {
1875
+ onConversationSelect?.(id);
1876
+ setSidebarOpen(false);
1877
+ },
1878
+ [onConversationSelect]
1879
+ );
1880
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
1881
+ "div",
1882
+ {
1883
+ className: (0, import_tailwind_merge10.twMerge)("flex h-screen w-full overflow-hidden bg-brand-dark", className),
1884
+ "data-testid": "agent-full-page",
1885
+ children: [
1886
+ showConversationList && /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_jsx_runtime32.Fragment, { children: [
1887
+ sidebarOpen && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1888
+ "div",
1889
+ {
1890
+ className: "fixed inset-0 bg-brand-dark/80 backdrop-blur-sm z-30 md:hidden",
1891
+ onClick: () => setSidebarOpen(false),
1892
+ "data-testid": "sidebar-overlay"
1893
+ }
1894
+ ),
1895
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1896
+ "aside",
1897
+ {
1898
+ className: (0, import_tailwind_merge10.twMerge)(
1899
+ "bg-brand-dark border-r border-brand-gold/15 w-72 shrink-0 flex-col z-40",
1900
+ // Desktop: always visible
1901
+ "hidden md:flex",
1902
+ // Mobile: overlay when open
1903
+ sidebarOpen && "fixed inset-y-0 left-0 flex md:relative"
1904
+ ),
1905
+ "aria-label": "Conversations sidebar",
1906
+ children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1907
+ ConversationList,
1908
+ {
1909
+ conversations,
1910
+ activeId: activeConversationId,
1911
+ onSelect: handleSelect,
1912
+ onDelete: onConversationDelete,
1913
+ onNew: onNewConversation
1914
+ }
1915
+ )
1916
+ }
1917
+ )
1918
+ ] }),
1919
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex-1 flex flex-col min-w-0 bg-brand-dark", children: [
1920
+ showConversationList && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "md:hidden flex items-center border-b border-brand-gold/15 px-3 py-2 bg-brand-dark-panel/60 backdrop-blur-sm", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1921
+ "button",
1922
+ {
1923
+ type: "button",
1924
+ onClick: () => setSidebarOpen(true),
1925
+ "aria-label": "Open conversations sidebar",
1926
+ className: "p-2 rounded-xl text-brand-cream/60 hover:text-brand-cream hover:bg-brand-dark-panel transition-colors duration-200",
1927
+ children: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
1928
+ "svg",
1929
+ {
1930
+ xmlns: "http://www.w3.org/2000/svg",
1931
+ width: "20",
1932
+ height: "20",
1933
+ viewBox: "0 0 24 24",
1934
+ fill: "none",
1935
+ stroke: "currentColor",
1936
+ strokeWidth: "2",
1937
+ strokeLinecap: "round",
1938
+ strokeLinejoin: "round",
1939
+ "aria-hidden": "true",
1940
+ children: [
1941
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("line", { x1: "3", y1: "12", x2: "21", y2: "12" }),
1942
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("line", { x1: "3", y1: "6", x2: "21", y2: "6" }),
1943
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("line", { x1: "3", y1: "18", x2: "21", y2: "18" })
1944
+ ]
1945
+ }
1946
+ )
1947
+ }
1948
+ ) }),
1949
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
1950
+ AgentChat,
1951
+ {
1952
+ endpoint,
1953
+ title,
1954
+ className: "flex-1 rounded-none border-0"
1955
+ }
1956
+ )
1957
+ ] })
1958
+ ]
1959
+ }
1960
+ );
1961
+ }
1962
+
1963
+ // src/layouts/AgentPanel/AgentPanel.tsx
1964
+ var import_tailwind_merge11 = require("tailwind-merge");
1965
+ var import_react12 = require("react");
1966
+ var import_jsx_runtime33 = require("react/jsx-runtime");
1967
+ function AgentPanel({
1968
+ endpoint,
1969
+ isOpen,
1970
+ onClose,
1971
+ side = "right",
1972
+ width = 400,
1973
+ title = "Chat",
1974
+ className
1975
+ }) {
1976
+ const panelRef = (0, import_react12.useRef)(null);
1977
+ (0, import_react12.useEffect)(() => {
1978
+ if (!isOpen) return;
1979
+ const handleKeyDown = (e) => {
1980
+ if (e.key === "Escape") onClose();
1981
+ };
1982
+ document.addEventListener("keydown", handleKeyDown);
1983
+ return () => document.removeEventListener("keydown", handleKeyDown);
1984
+ }, [isOpen, onClose]);
1985
+ const widthStyle = typeof width === "number" ? `${width}px` : width;
1986
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
1987
+ "div",
1988
+ {
1989
+ className: (0, import_tailwind_merge11.twMerge)("fixed inset-0 z-50", !isOpen && "pointer-events-none"),
1990
+ "aria-hidden": !isOpen,
1991
+ children: [
1992
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
1993
+ "div",
1994
+ {
1995
+ className: (0, import_tailwind_merge11.twMerge)(
1996
+ "fixed inset-0 transition-opacity duration-300",
1997
+ isOpen ? "opacity-100 bg-brand-dark/70 backdrop-blur-sm pointer-events-auto" : "opacity-0 pointer-events-none"
1998
+ ),
1999
+ onClick: isOpen ? onClose : void 0,
2000
+ "data-testid": "panel-backdrop"
2001
+ }
2002
+ ),
2003
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
2004
+ "div",
2005
+ {
2006
+ ref: panelRef,
2007
+ role: "dialog",
2008
+ "aria-label": title,
2009
+ "aria-modal": isOpen ? "true" : void 0,
2010
+ style: { width: widthStyle, maxWidth: "100vw" },
2011
+ className: (0, import_tailwind_merge11.twMerge)(
2012
+ "fixed top-0 h-full flex flex-col z-50 bg-brand-dark shadow-card",
2013
+ "transition-transform duration-300 ease-in-out",
2014
+ 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"}`,
2015
+ className
2016
+ ),
2017
+ children: [
2018
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex items-center justify-between border-b border-brand-gold/15 px-5 py-3.5 bg-brand-dark-panel/60 backdrop-blur-sm shrink-0", children: [
2019
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("h2", { className: "text-base font-display font-semibold text-brand-cream", children: title }),
2020
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
2021
+ "button",
2022
+ {
2023
+ type: "button",
2024
+ onClick: onClose,
2025
+ "aria-label": "Close panel",
2026
+ className: "rounded-xl p-2 text-brand-cream/40 hover:text-brand-cream/80 hover:bg-brand-cream/5 transition-colors duration-200",
2027
+ children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
2028
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
2029
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
2030
+ ] })
2031
+ }
2032
+ )
2033
+ ] }),
2034
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
2035
+ AgentChat,
2036
+ {
2037
+ endpoint,
2038
+ title,
2039
+ showHeader: false,
2040
+ showWelcomeTitle: false,
2041
+ className: "flex-1 rounded-none border-0"
2042
+ }
2043
+ )
2044
+ ]
2045
+ }
2046
+ )
2047
+ ]
2048
+ }
2049
+ );
2050
+ }
2051
+
2052
+ // src/layouts/AgentWidget/AgentWidget.tsx
2053
+ var import_tailwind_merge12 = require("tailwind-merge");
2054
+ var import_react13 = require("react");
2055
+ var import_jsx_runtime34 = require("react/jsx-runtime");
2056
+ function AgentWidget({
2057
+ endpoint,
2058
+ position = "bottom-right",
2059
+ triggerLabel = "Chat",
2060
+ title = "Chat",
2061
+ className
2062
+ }) {
2063
+ const [isOpen, setIsOpen] = (0, import_react13.useState)(false);
2064
+ const toggle = (0, import_react13.useCallback)(() => {
2065
+ setIsOpen((prev) => !prev);
2066
+ }, []);
2067
+ const positionClasses = position === "bottom-left" ? "left-4 bottom-4" : "right-4 bottom-4";
2068
+ const popoverPositionClasses = position === "bottom-left" ? "left-4 bottom-20" : "right-4 bottom-20";
2069
+ const popoverOrigin = position === "bottom-left" ? "origin-bottom-left" : "origin-bottom-right";
2070
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className, children: [
2071
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
2072
+ "div",
2073
+ {
2074
+ role: "dialog",
2075
+ "aria-label": title,
2076
+ "aria-hidden": !isOpen,
2077
+ className: (0, import_tailwind_merge12.twMerge)(
2078
+ "fixed z-50 flex flex-col",
2079
+ "w-[min(400px,calc(100vw-2rem))] h-[min(600px,calc(100vh-6rem))]",
2080
+ "rounded-2xl overflow-hidden border border-brand-gold/15",
2081
+ "bg-brand-dark/95 backdrop-blur-[12px] shadow-card",
2082
+ popoverPositionClasses,
2083
+ popoverOrigin,
2084
+ "transition-all duration-200 ease-out",
2085
+ isOpen ? "opacity-100 scale-100 pointer-events-auto translate-y-0" : "opacity-0 scale-95 pointer-events-none translate-y-2"
2086
+ ),
2087
+ children: [
2088
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex items-center justify-between px-4 py-2.5 bg-brand-dark-panel/80 border-b border-brand-gold/15 shrink-0", children: [
2089
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("h2", { className: "text-sm font-display font-semibold text-brand-cream", children: title }),
2090
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2091
+ "button",
2092
+ {
2093
+ type: "button",
2094
+ onClick: () => setIsOpen(false),
2095
+ "aria-label": "Minimize chat",
2096
+ className: "rounded-lg p-1.5 text-brand-cream/40 hover:text-brand-cream/70 transition-colors duration-200",
2097
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
2098
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
2099
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
2100
+ ] })
2101
+ }
2102
+ )
2103
+ ] }),
2104
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2105
+ AgentChat,
2106
+ {
2107
+ endpoint,
2108
+ title,
2109
+ showHeader: false,
2110
+ showWelcomeTitle: false,
2111
+ className: "flex-1 rounded-none border-0 min-h-0"
2112
+ }
2113
+ )
2114
+ ]
2115
+ }
2116
+ ),
2117
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
2118
+ "button",
2119
+ {
2120
+ type: "button",
2121
+ onClick: toggle,
2122
+ "aria-label": isOpen ? "Close chat" : triggerLabel,
2123
+ "aria-expanded": isOpen,
2124
+ className: (0, import_tailwind_merge12.twMerge)(
2125
+ "fixed z-50 flex items-center justify-center w-14 h-14 rounded-full",
2126
+ "bg-brand-blue text-brand-cream shadow-glow-cyan",
2127
+ "hover:bg-brand-cyan hover:shadow-glow-cyan hover:scale-105",
2128
+ "active:scale-95",
2129
+ "transition-all duration-200",
2130
+ positionClasses
2131
+ ),
2132
+ children: isOpen ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
2133
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
2134
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
2135
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) })
2136
+ }
2137
+ )
2138
+ ] });
2139
+ }
2140
+
2141
+ // src/layouts/AgentEmbed/AgentEmbed.tsx
2142
+ var import_tailwind_merge13 = require("tailwind-merge");
2143
+ var import_jsx_runtime35 = require("react/jsx-runtime");
2144
+ function AgentEmbed({
2145
+ endpoint,
2146
+ title = "Chat",
2147
+ className
2148
+ }) {
2149
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2150
+ "div",
2151
+ {
2152
+ className: (0, import_tailwind_merge13.twMerge)("w-full h-full min-h-0", className),
2153
+ "data-testid": "agent-embed",
2154
+ children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2155
+ AgentChat,
2156
+ {
2157
+ endpoint,
2158
+ title,
2159
+ className: "h-full rounded-none border-0"
2160
+ }
2161
+ )
2162
+ }
2163
+ );
2164
+ }
2165
+
2166
+ // src/feedback/ThumbsFeedback/ThumbsFeedback.tsx
2167
+ var import_react14 = require("react");
2168
+ var import_jsx_runtime36 = require("react/jsx-runtime");
2169
+ function ThumbsFeedback({
2170
+ messageId,
2171
+ onFeedback,
2172
+ state = null,
2173
+ onNegative,
2174
+ className
2175
+ }) {
2176
+ const [selected, setSelected] = (0, import_react14.useState)(state);
2177
+ const handleClick = (rating) => {
2178
+ setSelected(rating);
2179
+ onFeedback(messageId, rating);
2180
+ if (rating === "negative" && onNegative) {
2181
+ onNegative();
2182
+ }
2183
+ };
2184
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
2185
+ "div",
2186
+ {
2187
+ className: `inline-flex items-center gap-0.5 ${className ?? ""}`,
2188
+ role: "group",
2189
+ "aria-label": "Rate this response",
2190
+ "data-testid": "thumbs-feedback",
2191
+ children: [
2192
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
2193
+ "button",
2194
+ {
2195
+ type: "button",
2196
+ onClick: () => handleClick("positive"),
2197
+ "aria-label": "Thumbs up",
2198
+ "aria-pressed": selected === "positive",
2199
+ className: `p-1.5 rounded-md transition-colors duration-200 ${selected === "positive" ? "text-brand-cyan bg-brand-cyan/15" : "text-brand-cream/30 hover:text-brand-cyan hover:bg-brand-cyan/10"}`,
2200
+ "data-testid": "thumbs-up",
2201
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
2202
+ "svg",
2203
+ {
2204
+ width: "16",
2205
+ height: "16",
2206
+ viewBox: "0 0 24 24",
2207
+ fill: "none",
2208
+ stroke: "currentColor",
2209
+ strokeWidth: "2",
2210
+ strokeLinecap: "round",
2211
+ strokeLinejoin: "round",
2212
+ "aria-hidden": "true",
2213
+ children: [
2214
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("path", { d: "M7 10v12" }),
2215
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("path", { d: "M15 5.88 14 10h5.83a2 2 0 0 1 1.92 2.56l-2.33 8A2 2 0 0 1 17.5 22H4a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2h2.76a2 2 0 0 0 1.79-1.11L12 2h0a3.13 3.13 0 0 1 3 3.88Z" })
2216
+ ]
2217
+ }
2218
+ )
2219
+ }
2220
+ ),
2221
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
2222
+ "button",
2223
+ {
2224
+ type: "button",
2225
+ onClick: () => handleClick("negative"),
2226
+ "aria-label": "Thumbs down",
2227
+ "aria-pressed": selected === "negative",
2228
+ className: `p-1.5 rounded-md transition-colors duration-200 ${selected === "negative" ? "text-brand-watermelon bg-brand-watermelon/15" : "text-brand-cream/30 hover:text-brand-watermelon hover:bg-brand-watermelon/10"}`,
2229
+ "data-testid": "thumbs-down",
2230
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
2231
+ "svg",
2232
+ {
2233
+ width: "16",
2234
+ height: "16",
2235
+ viewBox: "0 0 24 24",
2236
+ fill: "none",
2237
+ stroke: "currentColor",
2238
+ strokeWidth: "2",
2239
+ strokeLinecap: "round",
2240
+ strokeLinejoin: "round",
2241
+ "aria-hidden": "true",
2242
+ children: [
2243
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("path", { d: "M17 14V2" }),
2244
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("path", { d: "M9 18.12 10 14H4.17a2 2 0 0 1-1.92-2.56l2.33-8A2 2 0 0 1 6.5 2H20a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-2.76a2 2 0 0 0-1.79 1.11L12 22h0a3.13 3.13 0 0 1-3-3.88Z" })
2245
+ ]
2246
+ }
2247
+ )
2248
+ }
2249
+ )
2250
+ ]
2251
+ }
2252
+ );
2253
+ }
2254
+
2255
+ // src/feedback/FeedbackDialog/FeedbackDialog.tsx
2256
+ var import_react15 = require("react");
2257
+ var import_core12 = require("@surf-kit/core");
2258
+ var import_jsx_runtime37 = require("react/jsx-runtime");
2259
+ function FeedbackDialog({ isOpen, onClose, onSubmit, className }) {
2260
+ const [comment, setComment] = (0, import_react15.useState)("");
2261
+ const handleSubmit = () => {
2262
+ onSubmit(comment);
2263
+ setComment("");
2264
+ onClose();
2265
+ };
2266
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
2267
+ import_core12.Dialog,
2268
+ {
2269
+ isOpen,
2270
+ onClose,
2271
+ title: "Share your feedback",
2272
+ size: "sm",
2273
+ className,
2274
+ footer: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
2275
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_core12.Button, { intent: "ghost", onPress: onClose, children: "Cancel" }),
2276
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_core12.Button, { intent: "primary", onPress: handleSubmit, isDisabled: comment.trim().length === 0, children: "Submit" })
2277
+ ] }),
2278
+ children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
2279
+ import_core12.TextArea,
2280
+ {
2281
+ label: "What could be improved?",
2282
+ value: comment,
2283
+ onChange: setComment,
2284
+ placeholder: "Tell us what went wrong or how this response could be better...",
2285
+ rows: 4,
2286
+ "data-testid": "feedback-textarea"
2287
+ }
2288
+ )
2289
+ }
2290
+ );
2291
+ }
2292
+
2293
+ // src/feedback/FeedbackConfirmation/FeedbackConfirmation.tsx
2294
+ var import_jsx_runtime38 = require("react/jsx-runtime");
2295
+ function FeedbackConfirmation({ variant = "inline", className }) {
2296
+ if (variant === "toast") {
2297
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
2298
+ "div",
2299
+ {
2300
+ role: "status",
2301
+ className: `fixed bottom-4 right-4 bg-surface border border-border rounded-lg px-4 py-3 shadow-lg text-sm text-text-primary ${className ?? ""}`,
2302
+ "data-testid": "feedback-confirmation",
2303
+ children: "Thanks for your feedback"
2304
+ }
2305
+ );
2306
+ }
2307
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
2308
+ "span",
2309
+ {
2310
+ role: "status",
2311
+ className: `text-sm text-text-secondary ${className ?? ""}`,
2312
+ "data-testid": "feedback-confirmation",
2313
+ children: "Thanks for your feedback"
2314
+ }
2315
+ );
2316
+ }
2317
+ // Annotate the CommonJS export names for ESM import in node:
2318
+ 0 && (module.exports = {
2319
+ AgentAvatar,
2320
+ AgentChat,
2321
+ AgentEmbed,
2322
+ AgentFullPage,
2323
+ AgentHandoff,
2324
+ AgentLabel,
2325
+ AgentPanel,
2326
+ AgentResponseView,
2327
+ AgentWidget,
2328
+ ConfidenceBadge,
2329
+ ConfidenceBreakdownView,
2330
+ ConfidenceMeter,
2331
+ ConversationList,
2332
+ ErrorResponse,
2333
+ FeedbackConfirmation,
2334
+ FeedbackDialog,
2335
+ FollowUpChips,
2336
+ MessageBubble,
2337
+ MessageComposer,
2338
+ MessageThread,
2339
+ ResponseMessage,
2340
+ RetrievalProgress,
2341
+ RoutingIndicator,
2342
+ SourceBadge,
2343
+ SourceCard,
2344
+ SourceDrawer,
2345
+ SourceInline,
2346
+ SourceList,
2347
+ StreamingMessage,
2348
+ StructuredResponse,
2349
+ ThinkingIndicator,
2350
+ ThumbsFeedback,
2351
+ ToolExecution,
2352
+ TypewriterText,
2353
+ VerificationBadge,
2354
+ VerificationDetail,
2355
+ VerificationProgress,
2356
+ WelcomeScreen
2357
+ });
2358
+ //# sourceMappingURL=index.cjs.map