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