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