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