@surf-kit/agent 0.2.2 → 0.4.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 +733 -222
- package/dist/chat/index.cjs.map +1 -1
- package/dist/chat/index.d.cts +13 -7
- package/dist/chat/index.d.ts +13 -7
- package/dist/chat/index.js +715 -204
- package/dist/chat/index.js.map +1 -1
- package/dist/{chat-ChYl2XjV.d.cts → chat-BRY3xGg_.d.cts} +11 -2
- package/dist/{chat--OifhIRe.d.ts → chat-CcKc6OAR.d.ts} +11 -2
- package/dist/{hooks-BGs8-4GK.d.ts → hooks-BLeiVk-x.d.ts} +22 -5
- package/dist/{hooks-DLfF18IU.d.cts → hooks-CSGGLd7j.d.cts} +22 -5
- package/dist/hooks.cjs +160 -85
- 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 +160 -85
- package/dist/hooks.js.map +1 -1
- package/dist/index.cjs +794 -283
- 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 +758 -247
- package/dist/index.js.map +1 -1
- package/dist/layouts/index.cjs +754 -243
- 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 +733 -222
- 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 +100 -15
- 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 +99 -14
- 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 +213 -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 +183 -73
- package/dist/streaming/index.js.map +1 -1
- package/dist/{streaming-DfT22A0z.d.cts → streaming-BHPXnwwo.d.cts} +3 -1
- package/dist/{streaming-DbQxScpi.d.ts → streaming-C6mbU7My.d.ts} +3 -1
- package/package.json +17 -5
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
// src/chat/AgentChat/AgentChat.tsx
|
|
4
|
-
import { twMerge as
|
|
4
|
+
import { twMerge as twMerge9 } from "tailwind-merge";
|
|
5
5
|
|
|
6
6
|
// src/hooks/useAgentChat.ts
|
|
7
7
|
import { useReducer, useCallback, useRef } from "react";
|
|
@@ -12,7 +12,8 @@ var initialState = {
|
|
|
12
12
|
error: null,
|
|
13
13
|
inputValue: "",
|
|
14
14
|
streamPhase: "idle",
|
|
15
|
-
streamingContent: ""
|
|
15
|
+
streamingContent: "",
|
|
16
|
+
streamingAgent: null
|
|
16
17
|
};
|
|
17
18
|
function reducer(state, action) {
|
|
18
19
|
switch (action.type) {
|
|
@@ -26,12 +27,17 @@ function reducer(state, action) {
|
|
|
26
27
|
error: null,
|
|
27
28
|
inputValue: "",
|
|
28
29
|
streamPhase: "thinking",
|
|
29
|
-
streamingContent: ""
|
|
30
|
+
streamingContent: "",
|
|
31
|
+
streamingAgent: null
|
|
30
32
|
};
|
|
31
33
|
case "STREAM_PHASE":
|
|
32
34
|
return { ...state, streamPhase: action.phase };
|
|
33
35
|
case "STREAM_CONTENT":
|
|
34
36
|
return { ...state, streamingContent: state.streamingContent + action.content };
|
|
37
|
+
case "STREAM_CONTENT_RESET":
|
|
38
|
+
return { ...state, streamingContent: "" };
|
|
39
|
+
case "STREAM_AGENT":
|
|
40
|
+
return { ...state, streamingAgent: action.agent };
|
|
35
41
|
case "SEND_SUCCESS":
|
|
36
42
|
return {
|
|
37
43
|
...state,
|
|
@@ -47,7 +53,8 @@ function reducer(state, action) {
|
|
|
47
53
|
isLoading: false,
|
|
48
54
|
error: action.error,
|
|
49
55
|
streamPhase: "idle",
|
|
50
|
-
streamingContent: ""
|
|
56
|
+
streamingContent: "",
|
|
57
|
+
streamingAgent: null
|
|
51
58
|
};
|
|
52
59
|
case "LOAD_CONVERSATION":
|
|
53
60
|
return {
|
|
@@ -73,115 +80,172 @@ function useAgentChat(config2) {
|
|
|
73
80
|
const configRef = useRef(config2);
|
|
74
81
|
configRef.current = config2;
|
|
75
82
|
const lastUserMessageRef = useRef(null);
|
|
83
|
+
const lastUserAttachmentsRef = useRef(void 0);
|
|
84
|
+
const abortControllerRef = useRef(null);
|
|
76
85
|
const sendMessage = useCallback(
|
|
77
|
-
async (content) => {
|
|
78
|
-
const { apiUrl, streamPath = "/chat/stream", headers
|
|
86
|
+
async (content, attachments) => {
|
|
87
|
+
const { apiUrl, streamPath = "/chat/stream", headers: headersOrFn, timeout = 3e4, bodyExtra } = configRef.current;
|
|
88
|
+
const headers = typeof headersOrFn === "function" ? await headersOrFn() : headersOrFn ?? {};
|
|
79
89
|
lastUserMessageRef.current = content;
|
|
90
|
+
lastUserAttachmentsRef.current = attachments;
|
|
80
91
|
const userMessage = {
|
|
81
92
|
id: generateMessageId(),
|
|
82
93
|
role: "user",
|
|
83
94
|
content,
|
|
95
|
+
attachments,
|
|
84
96
|
timestamp: /* @__PURE__ */ new Date()
|
|
85
97
|
};
|
|
86
98
|
dispatch({ type: "SEND_START", message: userMessage });
|
|
87
99
|
const controller = new AbortController();
|
|
100
|
+
abortControllerRef.current = controller;
|
|
88
101
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
102
|
+
const ctx = {
|
|
103
|
+
accumulatedContent: "",
|
|
104
|
+
agentResponse: null,
|
|
105
|
+
capturedAgent: null,
|
|
106
|
+
capturedConversationId: null,
|
|
107
|
+
hadStreamError: false
|
|
108
|
+
};
|
|
89
109
|
try {
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
error: {
|
|
108
|
-
code: "API_ERROR",
|
|
109
|
-
message: `HTTP ${response.status}: ${response.statusText}`,
|
|
110
|
-
retryable: response.status >= 500
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
return;
|
|
110
|
+
const url = `${apiUrl}${streamPath}`;
|
|
111
|
+
const mergedHeaders = {
|
|
112
|
+
"Content-Type": "application/json",
|
|
113
|
+
Accept: "text/event-stream",
|
|
114
|
+
...headers
|
|
115
|
+
};
|
|
116
|
+
const requestBody = {
|
|
117
|
+
message: content,
|
|
118
|
+
conversation_id: state.conversationId,
|
|
119
|
+
...bodyExtra
|
|
120
|
+
};
|
|
121
|
+
if (attachments && attachments.length > 0) {
|
|
122
|
+
requestBody.attachments = attachments.map((a) => ({
|
|
123
|
+
filename: a.filename,
|
|
124
|
+
content_type: a.content_type,
|
|
125
|
+
data: a.data
|
|
126
|
+
}));
|
|
114
127
|
}
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
128
|
+
const body = JSON.stringify(requestBody);
|
|
129
|
+
const handleEvent = (event) => {
|
|
130
|
+
switch (event.type) {
|
|
131
|
+
case "agent":
|
|
132
|
+
ctx.capturedAgent = event.agent;
|
|
133
|
+
dispatch({ type: "STREAM_AGENT", agent: ctx.capturedAgent });
|
|
134
|
+
break;
|
|
135
|
+
case "phase":
|
|
136
|
+
dispatch({ type: "STREAM_PHASE", phase: event.phase });
|
|
137
|
+
break;
|
|
138
|
+
case "delta":
|
|
139
|
+
ctx.accumulatedContent += event.content;
|
|
140
|
+
dispatch({ type: "STREAM_CONTENT", content: event.content });
|
|
141
|
+
break;
|
|
142
|
+
case "delta_reset":
|
|
143
|
+
ctx.accumulatedContent = "";
|
|
144
|
+
dispatch({ type: "STREAM_CONTENT_RESET" });
|
|
145
|
+
break;
|
|
146
|
+
case "done":
|
|
147
|
+
ctx.agentResponse = event.response;
|
|
148
|
+
ctx.capturedConversationId = event.conversation_id ?? null;
|
|
149
|
+
break;
|
|
150
|
+
case "error":
|
|
151
|
+
ctx.hadStreamError = true;
|
|
152
|
+
dispatch({ type: "SEND_ERROR", error: event.error });
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
const { streamAdapter } = configRef.current;
|
|
157
|
+
if (streamAdapter) {
|
|
158
|
+
await streamAdapter(
|
|
159
|
+
url,
|
|
160
|
+
{ method: "POST", headers: mergedHeaders, body, signal: controller.signal },
|
|
161
|
+
handleEvent
|
|
162
|
+
);
|
|
163
|
+
clearTimeout(timeoutId);
|
|
164
|
+
} else {
|
|
165
|
+
const response = await fetch(url, {
|
|
166
|
+
method: "POST",
|
|
167
|
+
headers: mergedHeaders,
|
|
168
|
+
body,
|
|
169
|
+
signal: controller.signal
|
|
120
170
|
});
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
dispatch({ type: "SEND_ERROR", error: event.error });
|
|
158
|
-
return;
|
|
171
|
+
clearTimeout(timeoutId);
|
|
172
|
+
if (!response.ok) {
|
|
173
|
+
dispatch({
|
|
174
|
+
type: "SEND_ERROR",
|
|
175
|
+
error: {
|
|
176
|
+
code: "API_ERROR",
|
|
177
|
+
message: `HTTP ${response.status}: ${response.statusText}`,
|
|
178
|
+
retryable: response.status >= 500
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const reader = response.body?.getReader();
|
|
184
|
+
if (!reader) {
|
|
185
|
+
dispatch({
|
|
186
|
+
type: "SEND_ERROR",
|
|
187
|
+
error: { code: "STREAM_ERROR", message: "No response body", retryable: true }
|
|
188
|
+
});
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const decoder = new TextDecoder();
|
|
192
|
+
let buffer = "";
|
|
193
|
+
while (true) {
|
|
194
|
+
const { done, value } = await reader.read();
|
|
195
|
+
if (done) break;
|
|
196
|
+
buffer += decoder.decode(value, { stream: true });
|
|
197
|
+
const lines = buffer.split("\n");
|
|
198
|
+
buffer = lines.pop() ?? "";
|
|
199
|
+
for (const line of lines) {
|
|
200
|
+
if (!line.startsWith("data: ")) continue;
|
|
201
|
+
const data = line.slice(6).trim();
|
|
202
|
+
if (data === "[DONE]") continue;
|
|
203
|
+
try {
|
|
204
|
+
const event = JSON.parse(data);
|
|
205
|
+
handleEvent(event);
|
|
206
|
+
} catch {
|
|
159
207
|
}
|
|
160
|
-
} catch {
|
|
161
208
|
}
|
|
162
209
|
}
|
|
163
210
|
}
|
|
211
|
+
if (ctx.hadStreamError) return;
|
|
164
212
|
const assistantMessage = {
|
|
165
213
|
id: generateMessageId(),
|
|
166
214
|
role: "assistant",
|
|
167
|
-
content: agentResponse?.message ?? accumulatedContent,
|
|
168
|
-
response: agentResponse ?? void 0,
|
|
169
|
-
agent: capturedAgent ?? void 0,
|
|
215
|
+
content: ctx.agentResponse?.message ?? ctx.accumulatedContent,
|
|
216
|
+
response: ctx.agentResponse ?? void 0,
|
|
217
|
+
agent: ctx.capturedAgent ?? void 0,
|
|
170
218
|
timestamp: /* @__PURE__ */ new Date()
|
|
171
219
|
};
|
|
172
220
|
dispatch({
|
|
173
221
|
type: "SEND_SUCCESS",
|
|
174
222
|
message: assistantMessage,
|
|
175
|
-
streamingContent: accumulatedContent,
|
|
176
|
-
conversationId: capturedConversationId
|
|
223
|
+
streamingContent: ctx.accumulatedContent,
|
|
224
|
+
conversationId: ctx.capturedConversationId
|
|
177
225
|
});
|
|
178
226
|
} catch (err) {
|
|
179
227
|
clearTimeout(timeoutId);
|
|
180
228
|
if (err.name === "AbortError") {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
229
|
+
if (ctx.accumulatedContent) {
|
|
230
|
+
const partialMessage = {
|
|
231
|
+
id: generateMessageId(),
|
|
232
|
+
role: "assistant",
|
|
233
|
+
content: ctx.accumulatedContent,
|
|
234
|
+
agent: ctx.capturedAgent ?? void 0,
|
|
235
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
236
|
+
};
|
|
237
|
+
dispatch({
|
|
238
|
+
type: "SEND_SUCCESS",
|
|
239
|
+
message: partialMessage,
|
|
240
|
+
streamingContent: ctx.accumulatedContent,
|
|
241
|
+
conversationId: ctx.capturedConversationId
|
|
242
|
+
});
|
|
243
|
+
} else {
|
|
244
|
+
dispatch({
|
|
245
|
+
type: "SEND_ERROR",
|
|
246
|
+
error: { code: "ABORTED", message: "Request stopped", retryable: true }
|
|
247
|
+
});
|
|
248
|
+
}
|
|
185
249
|
} else {
|
|
186
250
|
dispatch({
|
|
187
251
|
type: "SEND_ERROR",
|
|
@@ -192,6 +256,8 @@ function useAgentChat(config2) {
|
|
|
192
256
|
}
|
|
193
257
|
});
|
|
194
258
|
}
|
|
259
|
+
} finally {
|
|
260
|
+
abortControllerRef.current = null;
|
|
195
261
|
}
|
|
196
262
|
},
|
|
197
263
|
[state.conversationId]
|
|
@@ -204,7 +270,8 @@ function useAgentChat(config2) {
|
|
|
204
270
|
}, []);
|
|
205
271
|
const submitFeedback = useCallback(
|
|
206
272
|
async (messageId, rating, comment) => {
|
|
207
|
-
const { apiUrl, feedbackPath = "/feedback", headers
|
|
273
|
+
const { apiUrl, feedbackPath = "/feedback", headers: headersOrFn } = configRef.current;
|
|
274
|
+
const headers = typeof headersOrFn === "function" ? await headersOrFn() : headersOrFn ?? {};
|
|
208
275
|
await fetch(`${apiUrl}${feedbackPath}`, {
|
|
209
276
|
method: "POST",
|
|
210
277
|
headers: { "Content-Type": "application/json", ...headers },
|
|
@@ -215,12 +282,16 @@ function useAgentChat(config2) {
|
|
|
215
282
|
);
|
|
216
283
|
const retry = useCallback(async () => {
|
|
217
284
|
if (lastUserMessageRef.current) {
|
|
218
|
-
await sendMessage(lastUserMessageRef.current);
|
|
285
|
+
await sendMessage(lastUserMessageRef.current, lastUserAttachmentsRef.current);
|
|
219
286
|
}
|
|
220
287
|
}, [sendMessage]);
|
|
288
|
+
const stop = useCallback(() => {
|
|
289
|
+
abortControllerRef.current?.abort();
|
|
290
|
+
}, []);
|
|
221
291
|
const reset = useCallback(() => {
|
|
222
292
|
dispatch({ type: "RESET" });
|
|
223
293
|
lastUserMessageRef.current = null;
|
|
294
|
+
lastUserAttachmentsRef.current = void 0;
|
|
224
295
|
}, []);
|
|
225
296
|
const actions = {
|
|
226
297
|
sendMessage,
|
|
@@ -228,6 +299,7 @@ function useAgentChat(config2) {
|
|
|
228
299
|
loadConversation,
|
|
229
300
|
submitFeedback,
|
|
230
301
|
retry,
|
|
302
|
+
stop,
|
|
231
303
|
reset
|
|
232
304
|
};
|
|
233
305
|
return { state, actions };
|
|
@@ -235,7 +307,7 @@ function useAgentChat(config2) {
|
|
|
235
307
|
|
|
236
308
|
// src/chat/MessageThread/MessageThread.tsx
|
|
237
309
|
import { twMerge as twMerge5 } from "tailwind-merge";
|
|
238
|
-
import { useEffect, useRef as useRef2 } from "react";
|
|
310
|
+
import { useCallback as useCallback2, useEffect, useRef as useRef2 } from "react";
|
|
239
311
|
|
|
240
312
|
// src/chat/MessageBubble/MessageBubble.tsx
|
|
241
313
|
import { twMerge as twMerge4 } from "tailwind-merge";
|
|
@@ -244,8 +316,10 @@ import { twMerge as twMerge4 } from "tailwind-merge";
|
|
|
244
316
|
import { Badge as Badge2 } from "@surf-kit/core";
|
|
245
317
|
|
|
246
318
|
// src/response/ResponseMessage/ResponseMessage.tsx
|
|
319
|
+
import React from "react";
|
|
247
320
|
import ReactMarkdown from "react-markdown";
|
|
248
321
|
import rehypeSanitize from "rehype-sanitize";
|
|
322
|
+
import remarkGfm from "remark-gfm";
|
|
249
323
|
import { twMerge } from "tailwind-merge";
|
|
250
324
|
import { jsx } from "react/jsx-runtime";
|
|
251
325
|
function normalizeMarkdownLists(content) {
|
|
@@ -268,7 +342,12 @@ function ResponseMessage({ content, className }) {
|
|
|
268
342
|
"[&_h3]:text-sm [&_h3]:font-semibold [&_h3]:text-accent [&_h3]:mt-2 [&_h3]:mb-1",
|
|
269
343
|
"[&_code]:bg-surface-raised [&_code]:text-accent [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:rounded [&_code]:text-xs [&_code]:font-mono",
|
|
270
344
|
"[&_pre]:bg-surface-raised [&_pre]:border [&_pre]:border-border [&_pre]:rounded-xl [&_pre]:p-4 [&_pre]:overflow-x-auto",
|
|
345
|
+
"[&_hr]:my-3 [&_hr]:border-border",
|
|
271
346
|
"[&_blockquote]:border-l-2 [&_blockquote]:border-border-strong [&_blockquote]:pl-4 [&_blockquote]:text-text-secondary",
|
|
347
|
+
"[&_table]:w-full [&_table]:text-sm [&_table]:border-collapse [&_table]:my-2",
|
|
348
|
+
"[&_thead]:border-b [&_thead]:border-border",
|
|
349
|
+
"[&_th]:text-left [&_th]:px-2 [&_th]:py-1.5 [&_th]:font-semibold",
|
|
350
|
+
"[&_td]:px-2 [&_td]:py-1.5 [&_td]:border-t [&_td]:border-border/50",
|
|
272
351
|
"[&_a]:text-accent [&_a]:underline-offset-2 [&_a]:hover:text-accent/80",
|
|
273
352
|
className
|
|
274
353
|
),
|
|
@@ -276,6 +355,7 @@ function ResponseMessage({ content, className }) {
|
|
|
276
355
|
children: /* @__PURE__ */ jsx(
|
|
277
356
|
ReactMarkdown,
|
|
278
357
|
{
|
|
358
|
+
remarkPlugins: [remarkGfm],
|
|
279
359
|
rehypePlugins: [rehypeSanitize],
|
|
280
360
|
components: {
|
|
281
361
|
script: () => null,
|
|
@@ -283,12 +363,29 @@ function ResponseMessage({ content, className }) {
|
|
|
283
363
|
p: ({ children }) => /* @__PURE__ */ jsx("p", { className: "my-2", children }),
|
|
284
364
|
ul: ({ children }) => /* @__PURE__ */ jsx("ul", { className: "my-2 list-disc pl-6", children }),
|
|
285
365
|
ol: ({ children }) => /* @__PURE__ */ jsx("ol", { className: "my-2 list-decimal pl-6", children }),
|
|
286
|
-
li: ({ children }) =>
|
|
366
|
+
li: ({ children, ...props }) => {
|
|
367
|
+
let content2 = children;
|
|
368
|
+
if (props.ordered) {
|
|
369
|
+
content2 = React.Children.map(children, (child, i) => {
|
|
370
|
+
if (i === 0 && typeof child === "string") {
|
|
371
|
+
return child.replace(/^\d+[.)]\s*/, "");
|
|
372
|
+
}
|
|
373
|
+
return child;
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
return /* @__PURE__ */ jsx("li", { className: "my-1", children: content2 });
|
|
377
|
+
},
|
|
287
378
|
strong: ({ children }) => /* @__PURE__ */ jsx("strong", { className: "font-semibold", children }),
|
|
379
|
+
em: ({ children }) => /* @__PURE__ */ jsx("em", { className: "italic text-text-secondary", children }),
|
|
288
380
|
h1: ({ children }) => /* @__PURE__ */ jsx("h1", { className: "text-base font-bold mt-4 mb-2", children }),
|
|
289
381
|
h2: ({ children }) => /* @__PURE__ */ jsx("h2", { className: "text-sm font-bold mt-3 mb-1", children }),
|
|
290
382
|
h3: ({ children }) => /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold mt-2 mb-1", children }),
|
|
291
|
-
|
|
383
|
+
hr: () => /* @__PURE__ */ jsx("hr", { className: "my-3 border-border" }),
|
|
384
|
+
code: ({ children }) => /* @__PURE__ */ jsx("code", { className: "bg-surface-sunken rounded px-1 py-0.5 text-xs font-mono", children }),
|
|
385
|
+
table: ({ children }) => /* @__PURE__ */ jsx("div", { className: "overflow-x-auto my-2", children: /* @__PURE__ */ jsx("table", { className: "w-full text-sm border-collapse", children }) }),
|
|
386
|
+
thead: ({ children }) => /* @__PURE__ */ jsx("thead", { className: "border-b border-border", children }),
|
|
387
|
+
th: ({ children }) => /* @__PURE__ */ jsx("th", { className: "text-left px-2 py-1.5 font-semibold", children }),
|
|
388
|
+
td: ({ children }) => /* @__PURE__ */ jsx("td", { className: "px-2 py-1.5 border-t border-border/50", children })
|
|
292
389
|
},
|
|
293
390
|
children: normalizeMarkdownLists(content)
|
|
294
391
|
}
|
|
@@ -298,7 +395,9 @@ function ResponseMessage({ content, className }) {
|
|
|
298
395
|
}
|
|
299
396
|
|
|
300
397
|
// src/response/StructuredResponse/StructuredResponse.tsx
|
|
301
|
-
import
|
|
398
|
+
import ReactMarkdown2 from "react-markdown";
|
|
399
|
+
import rehypeSanitize2 from "rehype-sanitize";
|
|
400
|
+
import { Fragment, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
302
401
|
function tryParse(value) {
|
|
303
402
|
if (value === void 0 || value === null) return null;
|
|
304
403
|
if (typeof value === "string") {
|
|
@@ -310,6 +409,25 @@ function tryParse(value) {
|
|
|
310
409
|
}
|
|
311
410
|
return value;
|
|
312
411
|
}
|
|
412
|
+
function InlineMarkdown({ text }) {
|
|
413
|
+
return /* @__PURE__ */ jsx2(
|
|
414
|
+
ReactMarkdown2,
|
|
415
|
+
{
|
|
416
|
+
rehypePlugins: [rehypeSanitize2],
|
|
417
|
+
components: {
|
|
418
|
+
// Unwrap block-level <p> so content stays inline within its parent
|
|
419
|
+
p: ({ children }) => /* @__PURE__ */ jsx2(Fragment, { children }),
|
|
420
|
+
strong: ({ children }) => /* @__PURE__ */ jsx2("strong", { className: "font-semibold", children }),
|
|
421
|
+
em: ({ children }) => /* @__PURE__ */ jsx2("em", { className: "italic", children }),
|
|
422
|
+
code: ({ children }) => /* @__PURE__ */ jsx2("code", { className: "bg-surface-sunken rounded px-1 py-0.5 text-xs font-mono", children }),
|
|
423
|
+
// Prevent block elements that would break layout
|
|
424
|
+
script: () => null,
|
|
425
|
+
iframe: () => null
|
|
426
|
+
},
|
|
427
|
+
children: text
|
|
428
|
+
}
|
|
429
|
+
);
|
|
430
|
+
}
|
|
313
431
|
function renderSteps(data) {
|
|
314
432
|
const steps = tryParse(data.steps);
|
|
315
433
|
if (!steps || !Array.isArray(steps)) return null;
|
|
@@ -322,7 +440,7 @@ function renderSteps(data) {
|
|
|
322
440
|
children: i + 1
|
|
323
441
|
}
|
|
324
442
|
),
|
|
325
|
-
/* @__PURE__ */ jsx2("span", { className: "text-sm text-text-primary leading-relaxed", children: step })
|
|
443
|
+
/* @__PURE__ */ jsx2("span", { className: "text-sm text-text-primary leading-relaxed", children: /* @__PURE__ */ jsx2(InlineMarkdown, { text: step }) })
|
|
326
444
|
] }, i)) });
|
|
327
445
|
}
|
|
328
446
|
function renderTable(data) {
|
|
@@ -388,7 +506,7 @@ function renderList(data) {
|
|
|
388
506
|
title && /* @__PURE__ */ jsx2("p", { className: "text-xs font-semibold uppercase tracking-wider text-text-secondary mb-1", children: title }),
|
|
389
507
|
/* @__PURE__ */ jsx2("ul", { className: "flex flex-col gap-1.5", children: items.map((item, i) => /* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2.5", children: [
|
|
390
508
|
/* @__PURE__ */ jsx2("span", { className: "mt-1.5 h-1.5 w-1.5 shrink-0 rounded-full bg-accent", "aria-hidden": "true" }),
|
|
391
|
-
/* @__PURE__ */ jsx2("span", { className: "text-sm text-text-primary leading-relaxed", children: item })
|
|
509
|
+
/* @__PURE__ */ jsx2("span", { className: "text-sm text-text-primary leading-relaxed", children: /* @__PURE__ */ jsx2(InlineMarkdown, { text: item }) })
|
|
392
510
|
] }, i)) })
|
|
393
511
|
] });
|
|
394
512
|
}
|
|
@@ -423,7 +541,14 @@ function renderWarning(data) {
|
|
|
423
541
|
}
|
|
424
542
|
);
|
|
425
543
|
}
|
|
426
|
-
function StructuredResponse({ uiHint, data, className }) {
|
|
544
|
+
function StructuredResponse({ uiHint, data: rawData, className }) {
|
|
545
|
+
const data = typeof rawData === "string" ? (() => {
|
|
546
|
+
try {
|
|
547
|
+
return JSON.parse(rawData);
|
|
548
|
+
} catch {
|
|
549
|
+
return null;
|
|
550
|
+
}
|
|
551
|
+
})() : rawData;
|
|
427
552
|
if (!data) return null;
|
|
428
553
|
let content;
|
|
429
554
|
switch (uiHint) {
|
|
@@ -503,7 +628,36 @@ function SourceCard({ source, variant = "compact", onNavigate, className }) {
|
|
|
503
628
|
children: [
|
|
504
629
|
/* @__PURE__ */ jsxs2("div", { className: "flex items-start justify-between gap-2", children: [
|
|
505
630
|
/* @__PURE__ */ jsxs2("div", { className: "flex-1 min-w-0", children: [
|
|
506
|
-
/* @__PURE__ */
|
|
631
|
+
source.url ? /* @__PURE__ */ jsxs2(
|
|
632
|
+
"a",
|
|
633
|
+
{
|
|
634
|
+
href: source.url,
|
|
635
|
+
target: "_blank",
|
|
636
|
+
rel: "noopener noreferrer",
|
|
637
|
+
className: "text-sm font-medium text-accent hover:underline truncate block",
|
|
638
|
+
onClick: (e) => e.stopPropagation(),
|
|
639
|
+
children: [
|
|
640
|
+
source.title,
|
|
641
|
+
/* @__PURE__ */ jsxs2(
|
|
642
|
+
"svg",
|
|
643
|
+
{
|
|
644
|
+
className: "inline-block ml-1 w-3 h-3 opacity-60",
|
|
645
|
+
viewBox: "0 0 24 24",
|
|
646
|
+
fill: "none",
|
|
647
|
+
stroke: "currentColor",
|
|
648
|
+
strokeWidth: "2",
|
|
649
|
+
strokeLinecap: "round",
|
|
650
|
+
strokeLinejoin: "round",
|
|
651
|
+
children: [
|
|
652
|
+
/* @__PURE__ */ jsx3("path", { d: "M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6" }),
|
|
653
|
+
/* @__PURE__ */ jsx3("polyline", { points: "15 3 21 3 21 9" }),
|
|
654
|
+
/* @__PURE__ */ jsx3("line", { x1: "10", y1: "14", x2: "21", y2: "3" })
|
|
655
|
+
]
|
|
656
|
+
}
|
|
657
|
+
)
|
|
658
|
+
]
|
|
659
|
+
}
|
|
660
|
+
) : /* @__PURE__ */ jsx3("p", { className: "text-sm font-medium text-text-primary truncate", children: source.title }),
|
|
507
661
|
source.section && /* @__PURE__ */ jsx3("p", { className: "text-[11px] font-semibold uppercase tracking-wider text-text-secondary truncate mt-0.5", children: source.section })
|
|
508
662
|
] }),
|
|
509
663
|
/* @__PURE__ */ jsx3(
|
|
@@ -637,13 +791,16 @@ function AgentResponse({
|
|
|
637
791
|
}) {
|
|
638
792
|
return /* @__PURE__ */ jsxs4("div", { className: `flex flex-col gap-4 ${className ?? ""}`, "data-testid": "agent-response", children: [
|
|
639
793
|
/* @__PURE__ */ jsx6(ResponseMessage, { content: response.message }),
|
|
640
|
-
response.ui_hint !== "text" && response.structured_data &&
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
794
|
+
response.ui_hint !== "text" && response.structured_data && (() => {
|
|
795
|
+
const parsed = typeof response.structured_data === "string" ? (() => {
|
|
796
|
+
try {
|
|
797
|
+
return JSON.parse(response.structured_data);
|
|
798
|
+
} catch {
|
|
799
|
+
return null;
|
|
800
|
+
}
|
|
801
|
+
})() : response.structured_data;
|
|
802
|
+
return parsed ? /* @__PURE__ */ jsx6(StructuredResponse, { uiHint: response.ui_hint, data: parsed }) : null;
|
|
803
|
+
})(),
|
|
647
804
|
(showConfidence || showVerification) && /* @__PURE__ */ jsxs4("div", { className: "flex flex-wrap items-center gap-2 mt-1", "data-testid": "response-meta", children: [
|
|
648
805
|
showConfidence && /* @__PURE__ */ jsxs4(
|
|
649
806
|
Badge2,
|
|
@@ -694,6 +851,31 @@ function AgentResponse({
|
|
|
694
851
|
|
|
695
852
|
// src/chat/MessageBubble/MessageBubble.tsx
|
|
696
853
|
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
854
|
+
function DocumentIcon() {
|
|
855
|
+
return /* @__PURE__ */ jsxs5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
856
|
+
/* @__PURE__ */ jsx7("path", { d: "M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z" }),
|
|
857
|
+
/* @__PURE__ */ jsx7("polyline", { points: "14 2 14 8 20 8" }),
|
|
858
|
+
/* @__PURE__ */ jsx7("line", { x1: "16", y1: "13", x2: "8", y2: "13" }),
|
|
859
|
+
/* @__PURE__ */ jsx7("line", { x1: "16", y1: "17", x2: "8", y2: "17" })
|
|
860
|
+
] });
|
|
861
|
+
}
|
|
862
|
+
function AttachmentThumbnail({ attachment }) {
|
|
863
|
+
const isImage = attachment.content_type.startsWith("image/");
|
|
864
|
+
if (isImage) {
|
|
865
|
+
return /* @__PURE__ */ jsx7("div", { className: "rounded-lg overflow-hidden border border-black/10 max-w-[240px]", children: /* @__PURE__ */ jsx7(
|
|
866
|
+
"img",
|
|
867
|
+
{
|
|
868
|
+
src: attachment.preview_url ?? `data:${attachment.content_type};base64,${attachment.data}`,
|
|
869
|
+
alt: attachment.filename,
|
|
870
|
+
className: "max-w-full max-h-[200px] object-contain"
|
|
871
|
+
}
|
|
872
|
+
) });
|
|
873
|
+
}
|
|
874
|
+
return /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2 px-3 py-2 rounded-lg border border-black/10 bg-black/5", children: [
|
|
875
|
+
/* @__PURE__ */ jsx7(DocumentIcon, {}),
|
|
876
|
+
/* @__PURE__ */ jsx7("span", { className: "text-xs truncate max-w-[160px]", children: attachment.filename })
|
|
877
|
+
] });
|
|
878
|
+
}
|
|
697
879
|
function MessageBubble({
|
|
698
880
|
message,
|
|
699
881
|
showAgent,
|
|
@@ -701,23 +883,29 @@ function MessageBubble({
|
|
|
701
883
|
showConfidence = true,
|
|
702
884
|
showVerification = true,
|
|
703
885
|
animated = true,
|
|
886
|
+
userBubbleClassName,
|
|
704
887
|
className
|
|
705
888
|
}) {
|
|
706
889
|
const isUser = message.role === "user";
|
|
890
|
+
const hasAttachments = message.attachments && message.attachments.length > 0;
|
|
707
891
|
if (isUser) {
|
|
708
892
|
return /* @__PURE__ */ jsx7(
|
|
709
893
|
"div",
|
|
710
894
|
{
|
|
711
895
|
"data-message-id": message.id,
|
|
712
896
|
className: twMerge4("flex w-full justify-end", className),
|
|
713
|
-
children: /* @__PURE__ */
|
|
897
|
+
children: /* @__PURE__ */ jsxs5(
|
|
714
898
|
"div",
|
|
715
899
|
{
|
|
716
900
|
className: twMerge4(
|
|
717
|
-
"max-w-[70%] rounded-[18px] rounded-br-[4px] px-4 py-2.5 bg-
|
|
718
|
-
animated && "motion-safe:animate-slideFromRight"
|
|
901
|
+
"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",
|
|
902
|
+
animated && "motion-safe:animate-slideFromRight",
|
|
903
|
+
userBubbleClassName
|
|
719
904
|
),
|
|
720
|
-
children:
|
|
905
|
+
children: [
|
|
906
|
+
hasAttachments && /* @__PURE__ */ jsx7("div", { className: "flex flex-wrap gap-2 mb-2", children: message.attachments.map((att, i) => /* @__PURE__ */ jsx7(AttachmentThumbnail, { attachment: att }, `${att.filename}-${i}`)) }),
|
|
907
|
+
message.content
|
|
908
|
+
]
|
|
721
909
|
}
|
|
722
910
|
)
|
|
723
911
|
}
|
|
@@ -729,7 +917,7 @@ function MessageBubble({
|
|
|
729
917
|
"data-message-id": message.id,
|
|
730
918
|
className: twMerge4("flex w-full flex-col items-start gap-1.5", className),
|
|
731
919
|
children: [
|
|
732
|
-
showAgent && message.agent && /* @__PURE__ */ jsx7("div", { className: "text-[11px] font-semibold uppercase tracking-[0.08em] text-text-muted px-1", children: message.agent.replace("_agent", "").replace("_", " ") }),
|
|
920
|
+
showAgent && message.agent && /* @__PURE__ */ jsx7("div", { className: "text-[11px] font-display font-semibold uppercase tracking-[0.08em] text-text-muted px-1", children: message.agent.replace("_agent", "").replace("_", " ") }),
|
|
733
921
|
/* @__PURE__ */ jsx7(
|
|
734
922
|
"div",
|
|
735
923
|
{
|
|
@@ -755,34 +943,97 @@ function MessageBubble({
|
|
|
755
943
|
|
|
756
944
|
// src/chat/MessageThread/MessageThread.tsx
|
|
757
945
|
import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
758
|
-
function MessageThread({ messages, streamingSlot, showSources, showConfidence, showVerification, className }) {
|
|
759
|
-
const
|
|
946
|
+
function MessageThread({ messages, streamingSlot, showAgent, showSources, showConfidence, showVerification, hideLastAssistant, userBubbleClassName, className }) {
|
|
947
|
+
const scrollRef = useRef2(null);
|
|
948
|
+
const shouldAutoScroll = useRef2(true);
|
|
949
|
+
const hasStreaming = !!streamingSlot;
|
|
950
|
+
const scrollToBottom = useCallback2(() => {
|
|
951
|
+
const el = scrollRef.current;
|
|
952
|
+
if (el && shouldAutoScroll.current) {
|
|
953
|
+
el.scrollTop = el.scrollHeight;
|
|
954
|
+
}
|
|
955
|
+
}, []);
|
|
760
956
|
useEffect(() => {
|
|
761
|
-
|
|
762
|
-
|
|
957
|
+
const el = scrollRef.current;
|
|
958
|
+
if (!el) return;
|
|
959
|
+
const onWheel = (e) => {
|
|
960
|
+
if (e.deltaY < 0) {
|
|
961
|
+
shouldAutoScroll.current = false;
|
|
962
|
+
}
|
|
963
|
+
};
|
|
964
|
+
const onPointerDown = () => {
|
|
965
|
+
el.dataset.userPointer = "1";
|
|
966
|
+
};
|
|
967
|
+
const onPointerUp = () => {
|
|
968
|
+
delete el.dataset.userPointer;
|
|
969
|
+
};
|
|
970
|
+
el.addEventListener("wheel", onWheel, { passive: true });
|
|
971
|
+
el.addEventListener("pointerdown", onPointerDown);
|
|
972
|
+
window.addEventListener("pointerup", onPointerUp);
|
|
973
|
+
return () => {
|
|
974
|
+
el.removeEventListener("wheel", onWheel);
|
|
975
|
+
el.removeEventListener("pointerdown", onPointerDown);
|
|
976
|
+
window.removeEventListener("pointerup", onPointerUp);
|
|
977
|
+
};
|
|
978
|
+
}, []);
|
|
979
|
+
const handleScroll = useCallback2(() => {
|
|
980
|
+
const el = scrollRef.current;
|
|
981
|
+
if (!el) return;
|
|
982
|
+
const nearBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 80;
|
|
983
|
+
if (nearBottom) {
|
|
984
|
+
shouldAutoScroll.current = true;
|
|
985
|
+
} else if (el.dataset.userPointer) {
|
|
986
|
+
shouldAutoScroll.current = false;
|
|
987
|
+
}
|
|
988
|
+
}, []);
|
|
989
|
+
useEffect(scrollToBottom, [messages.length, scrollToBottom]);
|
|
990
|
+
useEffect(() => {
|
|
991
|
+
if (!hasStreaming) return;
|
|
992
|
+
let raf;
|
|
993
|
+
const tick = () => {
|
|
994
|
+
scrollToBottom();
|
|
995
|
+
raf = requestAnimationFrame(tick);
|
|
996
|
+
};
|
|
997
|
+
raf = requestAnimationFrame(tick);
|
|
998
|
+
return () => cancelAnimationFrame(raf);
|
|
999
|
+
}, [hasStreaming, scrollToBottom]);
|
|
1000
|
+
useEffect(() => {
|
|
1001
|
+
if (!hasStreaming) {
|
|
1002
|
+
shouldAutoScroll.current = true;
|
|
1003
|
+
}
|
|
1004
|
+
}, [hasStreaming]);
|
|
763
1005
|
return /* @__PURE__ */ jsxs6(
|
|
764
1006
|
"div",
|
|
765
1007
|
{
|
|
1008
|
+
ref: scrollRef,
|
|
766
1009
|
role: "log",
|
|
767
1010
|
"aria-live": "polite",
|
|
768
1011
|
"aria-label": "Message thread",
|
|
1012
|
+
onScroll: handleScroll,
|
|
769
1013
|
className: twMerge5(
|
|
770
1014
|
"flex flex-col gap-4 overflow-y-auto flex-1 px-4 py-6",
|
|
771
1015
|
className
|
|
772
1016
|
),
|
|
773
1017
|
children: [
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
{
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
1018
|
+
/* @__PURE__ */ jsx8("div", { className: "flex-1 shrink-0" }),
|
|
1019
|
+
messages.map((message, i) => {
|
|
1020
|
+
if (hideLastAssistant && i === messages.length - 1 && message.role === "assistant") {
|
|
1021
|
+
return null;
|
|
1022
|
+
}
|
|
1023
|
+
return /* @__PURE__ */ jsx8(
|
|
1024
|
+
MessageBubble,
|
|
1025
|
+
{
|
|
1026
|
+
message,
|
|
1027
|
+
showAgent,
|
|
1028
|
+
showSources,
|
|
1029
|
+
showConfidence,
|
|
1030
|
+
showVerification,
|
|
1031
|
+
userBubbleClassName
|
|
1032
|
+
},
|
|
1033
|
+
message.id
|
|
1034
|
+
);
|
|
1035
|
+
}),
|
|
1036
|
+
streamingSlot
|
|
786
1037
|
]
|
|
787
1038
|
}
|
|
788
1039
|
);
|
|
@@ -790,32 +1041,127 @@ function MessageThread({ messages, streamingSlot, showSources, showConfidence, s
|
|
|
790
1041
|
|
|
791
1042
|
// src/chat/MessageComposer/MessageComposer.tsx
|
|
792
1043
|
import { twMerge as twMerge6 } from "tailwind-merge";
|
|
793
|
-
import { useState as useState2, useRef as useRef3, useCallback as
|
|
1044
|
+
import { useState as useState2, useRef as useRef3, useCallback as useCallback3 } from "react";
|
|
794
1045
|
import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1046
|
+
var ALLOWED_TYPES = /* @__PURE__ */ new Set([
|
|
1047
|
+
"image/png",
|
|
1048
|
+
"image/jpeg",
|
|
1049
|
+
"image/gif",
|
|
1050
|
+
"image/webp",
|
|
1051
|
+
"application/pdf"
|
|
1052
|
+
]);
|
|
1053
|
+
var MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
1054
|
+
var MAX_ATTACHMENTS = 5;
|
|
1055
|
+
function ArrowUpIcon() {
|
|
1056
|
+
return /* @__PURE__ */ jsxs7("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1057
|
+
/* @__PURE__ */ jsx9("path", { d: "M10 16V4" }),
|
|
1058
|
+
/* @__PURE__ */ jsx9("path", { d: "M4 10l6-6 6 6" })
|
|
1059
|
+
] });
|
|
1060
|
+
}
|
|
1061
|
+
function StopIcon() {
|
|
1062
|
+
return /* @__PURE__ */ jsx9("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", children: /* @__PURE__ */ jsx9("rect", { x: "3", y: "3", width: "10", height: "10", rx: "2" }) });
|
|
1063
|
+
}
|
|
1064
|
+
function PaperclipIcon() {
|
|
1065
|
+
return /* @__PURE__ */ jsx9("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx9("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" }) });
|
|
1066
|
+
}
|
|
1067
|
+
function XIcon({ size = 14 }) {
|
|
1068
|
+
return /* @__PURE__ */ jsxs7("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1069
|
+
/* @__PURE__ */ jsx9("path", { d: "M18 6L6 18" }),
|
|
1070
|
+
/* @__PURE__ */ jsx9("path", { d: "M6 6l12 12" })
|
|
1071
|
+
] });
|
|
1072
|
+
}
|
|
1073
|
+
function DocumentIcon2() {
|
|
1074
|
+
return /* @__PURE__ */ jsxs7("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1075
|
+
/* @__PURE__ */ jsx9("path", { d: "M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z" }),
|
|
1076
|
+
/* @__PURE__ */ jsx9("polyline", { points: "14 2 14 8 20 8" }),
|
|
1077
|
+
/* @__PURE__ */ jsx9("line", { x1: "16", y1: "13", x2: "8", y2: "13" }),
|
|
1078
|
+
/* @__PURE__ */ jsx9("line", { x1: "16", y1: "17", x2: "8", y2: "17" }),
|
|
1079
|
+
/* @__PURE__ */ jsx9("polyline", { points: "10 9 9 9 8 9" })
|
|
1080
|
+
] });
|
|
1081
|
+
}
|
|
1082
|
+
function fileToBase64(file) {
|
|
1083
|
+
return new Promise((resolve, reject) => {
|
|
1084
|
+
const reader = new FileReader();
|
|
1085
|
+
reader.onload = () => {
|
|
1086
|
+
const result = reader.result;
|
|
1087
|
+
const base64 = result.split(",")[1];
|
|
1088
|
+
resolve(base64);
|
|
1089
|
+
};
|
|
1090
|
+
reader.onerror = reject;
|
|
1091
|
+
reader.readAsDataURL(file);
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
1094
|
+
function AttachmentPreview({
|
|
1095
|
+
attachment,
|
|
1096
|
+
onRemove
|
|
1097
|
+
}) {
|
|
1098
|
+
const isImage = attachment.content_type.startsWith("image/");
|
|
1099
|
+
return /* @__PURE__ */ jsxs7("div", { className: "relative group flex-shrink-0", children: [
|
|
1100
|
+
isImage ? /* @__PURE__ */ jsx9("div", { className: "w-16 h-16 rounded-lg overflow-hidden border border-border/60 bg-surface-alt", children: /* @__PURE__ */ jsx9(
|
|
1101
|
+
"img",
|
|
1102
|
+
{
|
|
1103
|
+
src: attachment.preview_url ?? `data:${attachment.content_type};base64,${attachment.data}`,
|
|
1104
|
+
alt: attachment.filename,
|
|
1105
|
+
className: "w-full h-full object-cover"
|
|
1106
|
+
}
|
|
1107
|
+
) }) : /* @__PURE__ */ jsxs7("div", { className: "h-16 px-3 rounded-lg border border-border/60 bg-surface-alt flex items-center gap-2", children: [
|
|
1108
|
+
/* @__PURE__ */ jsx9("div", { className: "text-text-muted", children: /* @__PURE__ */ jsx9(DocumentIcon2, {}) }),
|
|
1109
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex flex-col min-w-0", children: [
|
|
1110
|
+
/* @__PURE__ */ jsx9("span", { className: "text-xs text-text-primary truncate max-w-[120px]", children: attachment.filename }),
|
|
1111
|
+
/* @__PURE__ */ jsx9("span", { className: "text-[10px] text-text-muted", children: "PDF" })
|
|
1112
|
+
] })
|
|
1113
|
+
] }),
|
|
1114
|
+
/* @__PURE__ */ jsx9(
|
|
1115
|
+
"button",
|
|
1116
|
+
{
|
|
1117
|
+
type: "button",
|
|
1118
|
+
onClick: onRemove,
|
|
1119
|
+
className: twMerge6(
|
|
1120
|
+
"absolute -top-1.5 -right-1.5",
|
|
1121
|
+
"w-5 h-5 rounded-full",
|
|
1122
|
+
"bg-text-muted/80 text-white",
|
|
1123
|
+
"flex items-center justify-center",
|
|
1124
|
+
"opacity-0 group-hover:opacity-100",
|
|
1125
|
+
"transition-opacity duration-150",
|
|
1126
|
+
"hover:bg-text-primary"
|
|
1127
|
+
),
|
|
1128
|
+
"aria-label": `Remove ${attachment.filename}`,
|
|
1129
|
+
children: /* @__PURE__ */ jsx9(XIcon, { size: 10 })
|
|
1130
|
+
}
|
|
1131
|
+
)
|
|
1132
|
+
] });
|
|
1133
|
+
}
|
|
795
1134
|
function MessageComposer({
|
|
796
1135
|
onSend,
|
|
1136
|
+
onStop,
|
|
797
1137
|
isLoading = false,
|
|
798
1138
|
placeholder = "Type a message...",
|
|
799
1139
|
className
|
|
800
1140
|
}) {
|
|
801
1141
|
const [value, setValue] = useState2("");
|
|
1142
|
+
const [attachments, setAttachments] = useState2([]);
|
|
1143
|
+
const [dragOver, setDragOver] = useState2(false);
|
|
802
1144
|
const textareaRef = useRef3(null);
|
|
803
|
-
const
|
|
804
|
-
const
|
|
1145
|
+
const fileInputRef = useRef3(null);
|
|
1146
|
+
const canSend = (value.trim().length > 0 || attachments.length > 0) && !isLoading;
|
|
1147
|
+
const resetHeight = useCallback3(() => {
|
|
805
1148
|
const el = textareaRef.current;
|
|
806
1149
|
if (el) {
|
|
807
1150
|
el.style.height = "auto";
|
|
808
1151
|
el.style.overflowY = "hidden";
|
|
809
1152
|
}
|
|
810
1153
|
}, []);
|
|
811
|
-
const handleSend =
|
|
1154
|
+
const handleSend = useCallback3(() => {
|
|
812
1155
|
if (!canSend) return;
|
|
813
|
-
|
|
1156
|
+
const message = value.trim() || (attachments.length > 0 ? "Please analyse the attached file(s)." : "");
|
|
1157
|
+
if (!message && attachments.length === 0) return;
|
|
1158
|
+
onSend(message, attachments.length > 0 ? attachments : void 0);
|
|
814
1159
|
setValue("");
|
|
1160
|
+
setAttachments([]);
|
|
815
1161
|
resetHeight();
|
|
816
1162
|
textareaRef.current?.focus();
|
|
817
|
-
}, [canSend, onSend, value, resetHeight]);
|
|
818
|
-
const handleKeyDown =
|
|
1163
|
+
}, [canSend, onSend, value, attachments, resetHeight]);
|
|
1164
|
+
const handleKeyDown = useCallback3(
|
|
819
1165
|
(e) => {
|
|
820
1166
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
821
1167
|
e.preventDefault();
|
|
@@ -824,64 +1170,194 @@ function MessageComposer({
|
|
|
824
1170
|
},
|
|
825
1171
|
[handleSend]
|
|
826
1172
|
);
|
|
827
|
-
const handleChange =
|
|
1173
|
+
const handleChange = useCallback3(
|
|
828
1174
|
(e) => {
|
|
829
1175
|
setValue(e.target.value);
|
|
830
1176
|
const el = e.target;
|
|
831
1177
|
el.style.height = "auto";
|
|
832
|
-
const capped = Math.min(el.scrollHeight,
|
|
1178
|
+
const capped = Math.min(el.scrollHeight, 200);
|
|
833
1179
|
el.style.height = `${capped}px`;
|
|
834
|
-
el.style.overflowY = el.scrollHeight >
|
|
1180
|
+
el.style.overflowY = el.scrollHeight > 200 ? "auto" : "hidden";
|
|
835
1181
|
},
|
|
836
1182
|
[]
|
|
837
1183
|
);
|
|
1184
|
+
const addFiles = useCallback3(async (files) => {
|
|
1185
|
+
const fileArray = Array.from(files);
|
|
1186
|
+
for (const file of fileArray) {
|
|
1187
|
+
if (attachments.length >= MAX_ATTACHMENTS) break;
|
|
1188
|
+
if (!ALLOWED_TYPES.has(file.type)) continue;
|
|
1189
|
+
if (file.size > MAX_FILE_SIZE) continue;
|
|
1190
|
+
try {
|
|
1191
|
+
const data = await fileToBase64(file);
|
|
1192
|
+
const previewUrl = file.type.startsWith("image/") ? URL.createObjectURL(file) : void 0;
|
|
1193
|
+
const attachment = {
|
|
1194
|
+
filename: file.name,
|
|
1195
|
+
content_type: file.type,
|
|
1196
|
+
data,
|
|
1197
|
+
preview_url: previewUrl
|
|
1198
|
+
};
|
|
1199
|
+
setAttachments((prev) => {
|
|
1200
|
+
if (prev.length >= MAX_ATTACHMENTS) return prev;
|
|
1201
|
+
return [...prev, attachment];
|
|
1202
|
+
});
|
|
1203
|
+
} catch {
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
}, [attachments.length]);
|
|
1207
|
+
const handleFileSelect = useCallback3(() => {
|
|
1208
|
+
fileInputRef.current?.click();
|
|
1209
|
+
}, []);
|
|
1210
|
+
const handleFileInputChange = useCallback3(
|
|
1211
|
+
(e) => {
|
|
1212
|
+
if (e.target.files) {
|
|
1213
|
+
void addFiles(e.target.files);
|
|
1214
|
+
e.target.value = "";
|
|
1215
|
+
}
|
|
1216
|
+
},
|
|
1217
|
+
[addFiles]
|
|
1218
|
+
);
|
|
1219
|
+
const removeAttachment = useCallback3((index) => {
|
|
1220
|
+
setAttachments((prev) => {
|
|
1221
|
+
const removed = prev[index];
|
|
1222
|
+
if (removed?.preview_url) URL.revokeObjectURL(removed.preview_url);
|
|
1223
|
+
return prev.filter((_, i) => i !== index);
|
|
1224
|
+
});
|
|
1225
|
+
}, []);
|
|
1226
|
+
const handlePaste = useCallback3(
|
|
1227
|
+
(e) => {
|
|
1228
|
+
const items = e.clipboardData.items;
|
|
1229
|
+
const files = [];
|
|
1230
|
+
for (const item of items) {
|
|
1231
|
+
if (item.kind === "file" && ALLOWED_TYPES.has(item.type)) {
|
|
1232
|
+
const file = item.getAsFile();
|
|
1233
|
+
if (file) files.push(file);
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
if (files.length > 0) {
|
|
1237
|
+
void addFiles(files);
|
|
1238
|
+
}
|
|
1239
|
+
},
|
|
1240
|
+
[addFiles]
|
|
1241
|
+
);
|
|
1242
|
+
const handleDragOver = useCallback3((e) => {
|
|
1243
|
+
e.preventDefault();
|
|
1244
|
+
e.stopPropagation();
|
|
1245
|
+
setDragOver(true);
|
|
1246
|
+
}, []);
|
|
1247
|
+
const handleDragLeave = useCallback3((e) => {
|
|
1248
|
+
e.preventDefault();
|
|
1249
|
+
e.stopPropagation();
|
|
1250
|
+
setDragOver(false);
|
|
1251
|
+
}, []);
|
|
1252
|
+
const handleDrop = useCallback3(
|
|
1253
|
+
(e) => {
|
|
1254
|
+
e.preventDefault();
|
|
1255
|
+
e.stopPropagation();
|
|
1256
|
+
setDragOver(false);
|
|
1257
|
+
if (e.dataTransfer.files.length > 0) {
|
|
1258
|
+
void addFiles(e.dataTransfer.files);
|
|
1259
|
+
}
|
|
1260
|
+
},
|
|
1261
|
+
[addFiles]
|
|
1262
|
+
);
|
|
838
1263
|
return /* @__PURE__ */ jsxs7(
|
|
839
1264
|
"div",
|
|
840
1265
|
{
|
|
841
1266
|
className: twMerge6(
|
|
842
|
-
"
|
|
1267
|
+
"relative shrink-0 rounded-3xl border bg-surface",
|
|
1268
|
+
"shadow-lg shadow-black/10",
|
|
1269
|
+
"transition-all duration-200",
|
|
1270
|
+
"focus-within:border-accent/40 focus-within:shadow-accent/5",
|
|
1271
|
+
dragOver ? "border-accent/60 bg-accent/5" : "border-border/60",
|
|
843
1272
|
className
|
|
844
1273
|
),
|
|
1274
|
+
onDragOver: handleDragOver,
|
|
1275
|
+
onDragLeave: handleDragLeave,
|
|
1276
|
+
onDrop: handleDrop,
|
|
845
1277
|
children: [
|
|
846
1278
|
/* @__PURE__ */ jsx9(
|
|
847
|
-
"
|
|
1279
|
+
"input",
|
|
848
1280
|
{
|
|
849
|
-
ref:
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
className: twMerge6(
|
|
857
|
-
"flex-1 resize-none rounded-xl border border-border bg-surface/80",
|
|
858
|
-
"px-4 py-2.5 text-sm text-text-primary placeholder:text-text-muted",
|
|
859
|
-
"focus:border-transparent focus:ring-2 focus:ring-accent/40 focus:outline-none",
|
|
860
|
-
"disabled:opacity-50 disabled:cursor-not-allowed",
|
|
861
|
-
"overflow-hidden",
|
|
862
|
-
"transition-all duration-200"
|
|
863
|
-
),
|
|
864
|
-
style: { colorScheme: "dark" },
|
|
865
|
-
"aria-label": "Message input"
|
|
1281
|
+
ref: fileInputRef,
|
|
1282
|
+
type: "file",
|
|
1283
|
+
multiple: true,
|
|
1284
|
+
accept: "image/png,image/jpeg,image/gif,image/webp,application/pdf",
|
|
1285
|
+
onChange: handleFileInputChange,
|
|
1286
|
+
className: "hidden",
|
|
1287
|
+
"aria-hidden": "true"
|
|
866
1288
|
}
|
|
867
1289
|
),
|
|
868
|
-
/* @__PURE__ */ jsx9(
|
|
869
|
-
|
|
1290
|
+
attachments.length > 0 && /* @__PURE__ */ jsx9("div", { className: "flex gap-2 px-4 pt-3 pb-1 overflow-x-auto", children: attachments.map((att, i) => /* @__PURE__ */ jsx9(
|
|
1291
|
+
AttachmentPreview,
|
|
870
1292
|
{
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
1293
|
+
attachment: att,
|
|
1294
|
+
onRemove: () => removeAttachment(i)
|
|
1295
|
+
},
|
|
1296
|
+
`${att.filename}-${i}`
|
|
1297
|
+
)) }),
|
|
1298
|
+
dragOver && /* @__PURE__ */ jsx9("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__ */ jsx9("span", { className: "text-sm font-display font-semibold text-accent", children: "Drop files here" }) }),
|
|
1299
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex items-end", children: [
|
|
1300
|
+
/* @__PURE__ */ jsx9(
|
|
1301
|
+
"button",
|
|
1302
|
+
{
|
|
1303
|
+
type: "button",
|
|
1304
|
+
onClick: handleFileSelect,
|
|
1305
|
+
disabled: isLoading || attachments.length >= MAX_ATTACHMENTS,
|
|
1306
|
+
"aria-label": "Attach file",
|
|
1307
|
+
className: twMerge6(
|
|
1308
|
+
"flex-shrink-0 ml-2 mb-3",
|
|
1309
|
+
"inline-flex items-center justify-center",
|
|
1310
|
+
"w-9 h-9 rounded-full",
|
|
1311
|
+
"transition-all duration-200",
|
|
1312
|
+
"text-text-muted/60 hover:text-text-secondary hover:bg-text-muted/10",
|
|
1313
|
+
"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
|
|
1314
|
+
"disabled:opacity-30 disabled:cursor-not-allowed disabled:hover:bg-transparent"
|
|
1315
|
+
),
|
|
1316
|
+
children: /* @__PURE__ */ jsx9(PaperclipIcon, {})
|
|
1317
|
+
}
|
|
1318
|
+
),
|
|
1319
|
+
/* @__PURE__ */ jsx9(
|
|
1320
|
+
"textarea",
|
|
1321
|
+
{
|
|
1322
|
+
ref: textareaRef,
|
|
1323
|
+
value,
|
|
1324
|
+
onChange: handleChange,
|
|
1325
|
+
onKeyDown: handleKeyDown,
|
|
1326
|
+
onPaste: handlePaste,
|
|
1327
|
+
placeholder,
|
|
1328
|
+
rows: 1,
|
|
1329
|
+
disabled: isLoading,
|
|
1330
|
+
className: twMerge6(
|
|
1331
|
+
"flex-1 resize-none bg-transparent",
|
|
1332
|
+
"pl-2 pr-14 pt-4 pb-4 text-[15px] leading-relaxed",
|
|
1333
|
+
"text-text-primary placeholder:text-text-muted/70",
|
|
1334
|
+
"focus:outline-none",
|
|
1335
|
+
"disabled:opacity-50 disabled:cursor-not-allowed",
|
|
1336
|
+
"overflow-hidden"
|
|
1337
|
+
),
|
|
1338
|
+
style: { colorScheme: "dark" },
|
|
1339
|
+
"aria-label": "Message input"
|
|
1340
|
+
}
|
|
1341
|
+
),
|
|
1342
|
+
/* @__PURE__ */ jsx9(
|
|
1343
|
+
"button",
|
|
1344
|
+
{
|
|
1345
|
+
type: "button",
|
|
1346
|
+
onClick: isLoading && onStop ? onStop : handleSend,
|
|
1347
|
+
disabled: !canSend && !isLoading,
|
|
1348
|
+
"aria-label": isLoading ? "Stop generating" : "Send message",
|
|
1349
|
+
className: twMerge6(
|
|
1350
|
+
"absolute bottom-3 right-3",
|
|
1351
|
+
"inline-flex items-center justify-center",
|
|
1352
|
+
"w-9 h-9 rounded-full",
|
|
1353
|
+
"transition-all duration-200",
|
|
1354
|
+
"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
|
|
1355
|
+
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 cursor-pointer" : "bg-transparent text-text-muted/40 cursor-default"
|
|
1356
|
+
),
|
|
1357
|
+
children: isLoading ? /* @__PURE__ */ jsx9(StopIcon, {}) : /* @__PURE__ */ jsx9(ArrowUpIcon, {})
|
|
1358
|
+
}
|
|
1359
|
+
)
|
|
1360
|
+
] })
|
|
885
1361
|
]
|
|
886
1362
|
}
|
|
887
1363
|
);
|
|
@@ -894,6 +1370,7 @@ function WelcomeScreen({
|
|
|
894
1370
|
title = "Welcome",
|
|
895
1371
|
message = "How can I help you today?",
|
|
896
1372
|
icon,
|
|
1373
|
+
iconClassName,
|
|
897
1374
|
suggestedQuestions = [],
|
|
898
1375
|
onQuestionSelect,
|
|
899
1376
|
className
|
|
@@ -906,12 +1383,15 @@ function WelcomeScreen({
|
|
|
906
1383
|
className
|
|
907
1384
|
),
|
|
908
1385
|
children: [
|
|
909
|
-
/* @__PURE__ */ jsx10(
|
|
1386
|
+
icon ? iconClassName ? /* @__PURE__ */ jsx10("div", { className: iconClassName, "aria-hidden": "true", children: icon }) : icon : /* @__PURE__ */ jsx10(
|
|
910
1387
|
"div",
|
|
911
1388
|
{
|
|
912
|
-
className:
|
|
1389
|
+
className: twMerge7(
|
|
1390
|
+
"w-14 h-14 rounded-2xl bg-accent/10 border border-border flex items-center justify-center pulse-glow",
|
|
1391
|
+
iconClassName
|
|
1392
|
+
),
|
|
913
1393
|
"aria-hidden": "true",
|
|
914
|
-
children:
|
|
1394
|
+
children: /* @__PURE__ */ jsx10("span", { className: "text-2xl", children: "\u2726" })
|
|
915
1395
|
}
|
|
916
1396
|
),
|
|
917
1397
|
/* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-2", children: [
|
|
@@ -921,7 +1401,7 @@ function WelcomeScreen({
|
|
|
921
1401
|
suggestedQuestions.length > 0 && /* @__PURE__ */ jsx10(
|
|
922
1402
|
"div",
|
|
923
1403
|
{
|
|
924
|
-
className: "flex flex-wrap justify-center gap-2 max-w-
|
|
1404
|
+
className: "flex flex-wrap justify-center gap-2 max-w-xl",
|
|
925
1405
|
role: "group",
|
|
926
1406
|
"aria-label": "Suggested questions",
|
|
927
1407
|
children: suggestedQuestions.map((question) => /* @__PURE__ */ jsx10(
|
|
@@ -930,7 +1410,7 @@ function WelcomeScreen({
|
|
|
930
1410
|
type: "button",
|
|
931
1411
|
onClick: () => onQuestionSelect?.(question),
|
|
932
1412
|
className: twMerge7(
|
|
933
|
-
"px-
|
|
1413
|
+
"px-3.5 py-1.5 rounded-full text-[12px]",
|
|
934
1414
|
"border border-border bg-transparent text-text-secondary",
|
|
935
1415
|
"hover:bg-accent/10 hover:border-interactive hover:text-text-primary",
|
|
936
1416
|
"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
|
|
@@ -949,7 +1429,8 @@ function WelcomeScreen({
|
|
|
949
1429
|
|
|
950
1430
|
// src/streaming/StreamingMessage/StreamingMessage.tsx
|
|
951
1431
|
import { useEffect as useEffect3, useRef as useRef5 } from "react";
|
|
952
|
-
import {
|
|
1432
|
+
import { twMerge as twMerge8 } from "tailwind-merge";
|
|
1433
|
+
import { WaveLoader } from "@surf-kit/core";
|
|
953
1434
|
|
|
954
1435
|
// src/hooks/useCharacterDrain.ts
|
|
955
1436
|
import { useState as useState3, useRef as useRef4, useEffect as useEffect2 } from "react";
|
|
@@ -978,7 +1459,10 @@ function useCharacterDrain(target, msPerChar = 15) {
|
|
|
978
1459
|
const elapsed = now - lastTimeRef.current;
|
|
979
1460
|
const charsToAdvance = Math.floor(elapsed / msPerCharRef.current);
|
|
980
1461
|
if (charsToAdvance > 0 && indexRef.current < currentTarget.length) {
|
|
981
|
-
|
|
1462
|
+
let nextIndex = Math.min(indexRef.current + charsToAdvance, currentTarget.length);
|
|
1463
|
+
while (nextIndex < currentTarget.length && currentTarget[nextIndex - 1].trim() === "") {
|
|
1464
|
+
nextIndex++;
|
|
1465
|
+
}
|
|
982
1466
|
indexRef.current = nextIndex;
|
|
983
1467
|
lastTimeRef.current = now;
|
|
984
1468
|
setDisplayed(currentTarget.slice(0, nextIndex));
|
|
@@ -1023,14 +1507,36 @@ var phaseLabels = {
|
|
|
1023
1507
|
generating: "Writing...",
|
|
1024
1508
|
verifying: "Verifying..."
|
|
1025
1509
|
};
|
|
1510
|
+
var CURSOR_STYLES = `
|
|
1511
|
+
.sk-streaming-cursor > :not(ul,ol,blockquote,div:has(table)):last-child::after,
|
|
1512
|
+
.sk-streaming-cursor > :is(ul,ol):last-child > li:last-child::after,
|
|
1513
|
+
.sk-streaming-cursor > blockquote:last-child > p:last-child::after,
|
|
1514
|
+
.sk-streaming-cursor > div:has(table):last-child table tbody tr:last-child td:last-child::after {
|
|
1515
|
+
content: "";
|
|
1516
|
+
display: inline-block;
|
|
1517
|
+
width: 2px;
|
|
1518
|
+
height: 1em;
|
|
1519
|
+
background: var(--color-accent, #38bdf8);
|
|
1520
|
+
animation: sk-cursor-blink 0.8s steps(1) infinite;
|
|
1521
|
+
margin-left: 2px;
|
|
1522
|
+
vertical-align: text-bottom;
|
|
1523
|
+
}
|
|
1524
|
+
@keyframes sk-cursor-blink {
|
|
1525
|
+
0%, 60% { opacity: 1; }
|
|
1526
|
+
61%, 100% { opacity: 0; }
|
|
1527
|
+
}
|
|
1528
|
+
`;
|
|
1026
1529
|
function StreamingMessage({
|
|
1027
1530
|
stream,
|
|
1028
1531
|
onComplete,
|
|
1532
|
+
onDraining,
|
|
1029
1533
|
showPhases = true,
|
|
1030
1534
|
className
|
|
1031
1535
|
}) {
|
|
1032
1536
|
const onCompleteRef = useRef5(onComplete);
|
|
1033
1537
|
onCompleteRef.current = onComplete;
|
|
1538
|
+
const onDrainingRef = useRef5(onDraining);
|
|
1539
|
+
onDrainingRef.current = onDraining;
|
|
1034
1540
|
const wasActiveRef = useRef5(stream.active);
|
|
1035
1541
|
useEffect3(() => {
|
|
1036
1542
|
if (wasActiveRef.current && !stream.active) {
|
|
@@ -1039,35 +1545,40 @@ function StreamingMessage({
|
|
|
1039
1545
|
wasActiveRef.current = stream.active;
|
|
1040
1546
|
}, [stream.active]);
|
|
1041
1547
|
const phaseLabel = phaseLabels[stream.phase];
|
|
1042
|
-
const { displayed:
|
|
1043
|
-
|
|
1548
|
+
const { displayed: rawDisplayed, isDraining } = useCharacterDrain(stream.content);
|
|
1549
|
+
const displayedContent = stream.active || isDraining ? rawDisplayed.trimEnd() : rawDisplayed;
|
|
1550
|
+
useEffect3(() => {
|
|
1551
|
+
onDrainingRef.current?.(isDraining);
|
|
1552
|
+
}, [isDraining]);
|
|
1553
|
+
const agentLabel = stream.agent ? stream.agent.replace("_agent", "").replace("_", " ") : null;
|
|
1554
|
+
const showPhaseIndicator = showPhases && stream.active && stream.phase !== "idle" && !displayedContent;
|
|
1555
|
+
const showCursor = (stream.active || isDraining) && !!displayedContent;
|
|
1556
|
+
return /* @__PURE__ */ jsxs9("div", { className: twMerge8("flex w-full flex-col items-start", className), "data-testid": "streaming-message", children: [
|
|
1044
1557
|
/* @__PURE__ */ jsxs9("div", { "aria-live": "assertive", className: "sr-only", children: [
|
|
1045
1558
|
stream.active && stream.phase !== "idle" && "Response started",
|
|
1046
1559
|
!stream.active && stream.content && "Response complete"
|
|
1047
1560
|
] }),
|
|
1561
|
+
showCursor && /* @__PURE__ */ jsx11("style", { children: CURSOR_STYLES }),
|
|
1562
|
+
agentLabel && /* @__PURE__ */ jsx11("div", { className: "text-[11px] font-display font-semibold uppercase tracking-[0.08em] text-text-muted px-1 mb-1.5", children: agentLabel }),
|
|
1048
1563
|
/* @__PURE__ */ jsxs9("div", { className: "max-w-[88%] px-4 py-3 rounded-[18px] rounded-tl-[4px] bg-surface border border-border motion-safe:animate-springFromLeft", children: [
|
|
1049
|
-
|
|
1564
|
+
showPhaseIndicator && /* @__PURE__ */ jsxs9(
|
|
1050
1565
|
"div",
|
|
1051
1566
|
{
|
|
1052
|
-
className: "flex items-center gap-2
|
|
1567
|
+
className: "flex items-center gap-2 text-sm text-text-secondary",
|
|
1053
1568
|
"data-testid": "phase-indicator",
|
|
1054
1569
|
children: [
|
|
1055
|
-
/* @__PURE__ */ jsx11("span", { "aria-hidden": "true", children: /* @__PURE__ */ jsx11(
|
|
1570
|
+
/* @__PURE__ */ jsx11("span", { "aria-hidden": "true", children: /* @__PURE__ */ jsx11(WaveLoader, { size: "sm", color: "#38bdf8" }) }),
|
|
1056
1571
|
/* @__PURE__ */ jsx11("span", { children: phaseLabel })
|
|
1057
1572
|
]
|
|
1058
1573
|
}
|
|
1059
1574
|
),
|
|
1060
|
-
/* @__PURE__ */
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
"data-testid": "streaming-cursor"
|
|
1068
|
-
}
|
|
1069
|
-
)
|
|
1070
|
-
] })
|
|
1575
|
+
displayedContent && /* @__PURE__ */ jsx11(
|
|
1576
|
+
ResponseMessage,
|
|
1577
|
+
{
|
|
1578
|
+
content: displayedContent,
|
|
1579
|
+
className: showCursor ? "sk-streaming-cursor" : void 0
|
|
1580
|
+
}
|
|
1581
|
+
)
|
|
1071
1582
|
] })
|
|
1072
1583
|
] });
|
|
1073
1584
|
}
|
|
@@ -1098,7 +1609,7 @@ function AgentChat({
|
|
|
1098
1609
|
return /* @__PURE__ */ jsxs10(
|
|
1099
1610
|
"div",
|
|
1100
1611
|
{
|
|
1101
|
-
className:
|
|
1612
|
+
className: twMerge9(
|
|
1102
1613
|
"flex flex-col h-full bg-canvas border border-border rounded-xl overflow-hidden",
|
|
1103
1614
|
className
|
|
1104
1615
|
),
|
|
@@ -1134,7 +1645,7 @@ function AgentChat({
|
|
|
1134
1645
|
onQuestionSelect: handleQuestionSelect
|
|
1135
1646
|
}
|
|
1136
1647
|
),
|
|
1137
|
-
/* @__PURE__ */ jsx12(MessageComposer, { onSend: handleSend, isLoading: state.isLoading })
|
|
1648
|
+
/* @__PURE__ */ jsx12(MessageComposer, { onSend: handleSend, onStop: actions.stop, isLoading: state.isLoading })
|
|
1138
1649
|
]
|
|
1139
1650
|
}
|
|
1140
1651
|
);
|
|
@@ -1601,7 +2112,7 @@ function ThinkingIndicator({ label = "Thinking...", className }) {
|
|
|
1601
2112
|
}
|
|
1602
2113
|
|
|
1603
2114
|
// src/streaming/ToolExecution/ToolExecution.tsx
|
|
1604
|
-
import {
|
|
2115
|
+
import { WaveLoader as WaveLoader2 } from "@surf-kit/core";
|
|
1605
2116
|
import { jsx as jsx26, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
1606
2117
|
var defaultLabels = {
|
|
1607
2118
|
search: "Searching knowledge base...",
|
|
@@ -1617,7 +2128,7 @@ function ToolExecution({ tool, label, className }) {
|
|
|
1617
2128
|
role: "status",
|
|
1618
2129
|
"data-testid": "tool-execution",
|
|
1619
2130
|
children: [
|
|
1620
|
-
/* @__PURE__ */ jsx26("span", { "aria-hidden": "true", children: /* @__PURE__ */ jsx26(
|
|
2131
|
+
/* @__PURE__ */ jsx26("span", { "aria-hidden": "true", children: /* @__PURE__ */ jsx26(WaveLoader2, { size: "sm", color: "#38bdf8" }) }),
|
|
1621
2132
|
/* @__PURE__ */ jsx26("span", { children: displayLabel })
|
|
1622
2133
|
]
|
|
1623
2134
|
}
|
|
@@ -1625,7 +2136,7 @@ function ToolExecution({ tool, label, className }) {
|
|
|
1625
2136
|
}
|
|
1626
2137
|
|
|
1627
2138
|
// src/streaming/RetrievalProgress/RetrievalProgress.tsx
|
|
1628
|
-
import {
|
|
2139
|
+
import { WaveLoader as WaveLoader3 } from "@surf-kit/core";
|
|
1629
2140
|
import { jsx as jsx27, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
1630
2141
|
function RetrievalProgress({ sources, isActive, className }) {
|
|
1631
2142
|
return /* @__PURE__ */ jsxs24(
|
|
@@ -1637,7 +2148,7 @@ function RetrievalProgress({ sources, isActive, className }) {
|
|
|
1637
2148
|
"data-testid": "retrieval-progress",
|
|
1638
2149
|
children: [
|
|
1639
2150
|
isActive && /* @__PURE__ */ jsxs24("div", { className: "flex items-center gap-2 text-sm text-text-secondary", children: [
|
|
1640
|
-
/* @__PURE__ */ jsx27("span", { "aria-hidden": "true", children: /* @__PURE__ */ jsx27(
|
|
2151
|
+
/* @__PURE__ */ jsx27("span", { "aria-hidden": "true", children: /* @__PURE__ */ jsx27(WaveLoader3, { size: "sm", color: "#38bdf8" }) }),
|
|
1641
2152
|
/* @__PURE__ */ jsx27("span", { children: "Retrieving sources..." })
|
|
1642
2153
|
] }),
|
|
1643
2154
|
sources.length > 0 && /* @__PURE__ */ jsx27("ul", { className: "space-y-1", "data-testid": "source-list", children: sources.map((source, index) => /* @__PURE__ */ jsxs24(
|
|
@@ -1735,7 +2246,7 @@ function TypewriterText({
|
|
|
1735
2246
|
}
|
|
1736
2247
|
|
|
1737
2248
|
// src/streaming/TypingIndicator/TypingIndicator.tsx
|
|
1738
|
-
import { twMerge as
|
|
2249
|
+
import { twMerge as twMerge10 } from "tailwind-merge";
|
|
1739
2250
|
import { useReducedMotion as useReducedMotion2 } from "@surf-kit/hooks";
|
|
1740
2251
|
import { jsx as jsx30, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
1741
2252
|
var bounceKeyframes = `
|
|
@@ -1755,7 +2266,7 @@ function TypingIndicator({
|
|
|
1755
2266
|
{
|
|
1756
2267
|
role: "status",
|
|
1757
2268
|
"aria-label": label ?? "typing",
|
|
1758
|
-
className:
|
|
2269
|
+
className: twMerge10("inline-flex items-center gap-2", className),
|
|
1759
2270
|
"data-testid": "typing-indicator",
|
|
1760
2271
|
children: [
|
|
1761
2272
|
!reducedMotion && /* @__PURE__ */ jsx30("style", { children: bounceKeyframes }),
|
|
@@ -1777,7 +2288,7 @@ function TypingIndicator({
|
|
|
1777
2288
|
}
|
|
1778
2289
|
|
|
1779
2290
|
// src/streaming/TextGlimmer/TextGlimmer.tsx
|
|
1780
|
-
import { twMerge as
|
|
2291
|
+
import { twMerge as twMerge11 } from "tailwind-merge";
|
|
1781
2292
|
import { useReducedMotion as useReducedMotion3 } from "@surf-kit/hooks";
|
|
1782
2293
|
import { jsx as jsx31, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
1783
2294
|
var shimmerKeyframes = `
|
|
@@ -1794,7 +2305,7 @@ function TextGlimmer({ lines = 3, className }) {
|
|
|
1794
2305
|
{
|
|
1795
2306
|
role: "status",
|
|
1796
2307
|
"aria-label": "Loading",
|
|
1797
|
-
className:
|
|
2308
|
+
className: twMerge11("flex flex-col gap-2", className),
|
|
1798
2309
|
"data-testid": "text-glimmer",
|
|
1799
2310
|
children: [
|
|
1800
2311
|
!reducedMotion && /* @__PURE__ */ jsx31("style", { children: shimmerKeyframes }),
|
|
@@ -1820,7 +2331,7 @@ function TextGlimmer({ lines = 3, className }) {
|
|
|
1820
2331
|
}
|
|
1821
2332
|
|
|
1822
2333
|
// src/streaming/StreamingList/StreamingList.tsx
|
|
1823
|
-
import { twMerge as
|
|
2334
|
+
import { twMerge as twMerge12 } from "tailwind-merge";
|
|
1824
2335
|
import { useReducedMotion as useReducedMotion4 } from "@surf-kit/hooks";
|
|
1825
2336
|
import { jsx as jsx32, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
1826
2337
|
var fadeSlideInKeyframes = `
|
|
@@ -1838,13 +2349,13 @@ function StreamingList({
|
|
|
1838
2349
|
}) {
|
|
1839
2350
|
const reducedMotion = useReducedMotion4();
|
|
1840
2351
|
if (items.length === 0 && !isStreaming) {
|
|
1841
|
-
return emptyMessage ? /* @__PURE__ */ jsx32("p", { className:
|
|
2352
|
+
return emptyMessage ? /* @__PURE__ */ jsx32("p", { className: twMerge12("text-sm text-text-secondary", className), "data-testid": "streaming-list-empty", children: emptyMessage }) : null;
|
|
1842
2353
|
}
|
|
1843
2354
|
return /* @__PURE__ */ jsxs29(
|
|
1844
2355
|
"ul",
|
|
1845
2356
|
{
|
|
1846
2357
|
"aria-live": "polite",
|
|
1847
|
-
className:
|
|
2358
|
+
className: twMerge12("list-none p-0 m-0", className),
|
|
1848
2359
|
"data-testid": "streaming-list",
|
|
1849
2360
|
children: [
|
|
1850
2361
|
!reducedMotion && /* @__PURE__ */ jsx32("style", { children: fadeSlideInKeyframes }),
|
|
@@ -1864,7 +2375,7 @@ function StreamingList({
|
|
|
1864
2375
|
}
|
|
1865
2376
|
|
|
1866
2377
|
// src/streaming/StreamingStructure/StreamingStructure.tsx
|
|
1867
|
-
import { twMerge as
|
|
2378
|
+
import { twMerge as twMerge13 } from "tailwind-merge";
|
|
1868
2379
|
import { useReducedMotion as useReducedMotion5 } from "@surf-kit/hooks";
|
|
1869
2380
|
import { jsx as jsx33, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
1870
2381
|
var fadeSlideInKeyframes2 = `
|
|
@@ -1913,7 +2424,7 @@ function StreamingStructure({
|
|
|
1913
2424
|
"dl",
|
|
1914
2425
|
{
|
|
1915
2426
|
"aria-live": "polite",
|
|
1916
|
-
className:
|
|
2427
|
+
className: twMerge13("m-0", className),
|
|
1917
2428
|
"data-testid": "streaming-structure",
|
|
1918
2429
|
children: [
|
|
1919
2430
|
!reducedMotion && /* @__PURE__ */ jsx33("style", { children: fadeSlideInKeyframes2 }),
|
|
@@ -1936,7 +2447,7 @@ function StreamingStructure({
|
|
|
1936
2447
|
}
|
|
1937
2448
|
|
|
1938
2449
|
// src/chat/ConversationList/ConversationList.tsx
|
|
1939
|
-
import { twMerge as
|
|
2450
|
+
import { twMerge as twMerge14 } from "tailwind-merge";
|
|
1940
2451
|
import { jsx as jsx34, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
1941
2452
|
function ConversationList({
|
|
1942
2453
|
conversations,
|
|
@@ -1950,14 +2461,14 @@ function ConversationList({
|
|
|
1950
2461
|
"nav",
|
|
1951
2462
|
{
|
|
1952
2463
|
"aria-label": "Conversation list",
|
|
1953
|
-
className:
|
|
2464
|
+
className: twMerge14("flex flex-col flex-1 min-h-0", className),
|
|
1954
2465
|
children: [
|
|
1955
|
-
onNew && /* @__PURE__ */ jsx34("div", { className: "
|
|
2466
|
+
onNew && /* @__PURE__ */ jsx34("div", { className: "px-5 pt-1 pb-3 border-b border-border", children: /* @__PURE__ */ jsx34(
|
|
1956
2467
|
"button",
|
|
1957
2468
|
{
|
|
1958
2469
|
type: "button",
|
|
1959
2470
|
onClick: onNew,
|
|
1960
|
-
className: "w-full px-4 py-2
|
|
2471
|
+
className: "w-full px-4 py-2 rounded-lg text-sm font-medium border border-border text-text-secondary hover:text-text-primary hover:bg-surface hover:border-border-strong transition-colors duration-150",
|
|
1961
2472
|
children: "New conversation"
|
|
1962
2473
|
}
|
|
1963
2474
|
) }),
|
|
@@ -1967,10 +2478,10 @@ function ConversationList({
|
|
|
1967
2478
|
return /* @__PURE__ */ jsxs31(
|
|
1968
2479
|
"li",
|
|
1969
2480
|
{
|
|
1970
|
-
className:
|
|
1971
|
-
"flex items-start
|
|
1972
|
-
"hover:bg-surface",
|
|
1973
|
-
isActive
|
|
2481
|
+
className: twMerge14(
|
|
2482
|
+
"flex items-start transition-colors duration-150",
|
|
2483
|
+
"hover:bg-surface-raised",
|
|
2484
|
+
isActive ? "bg-accent-subtlest border-l-[3px] border-l-accent" : "border-l-[3px] border-l-transparent"
|
|
1974
2485
|
),
|
|
1975
2486
|
children: [
|
|
1976
2487
|
/* @__PURE__ */ jsxs31(
|
|
@@ -1979,10 +2490,10 @@ function ConversationList({
|
|
|
1979
2490
|
type: "button",
|
|
1980
2491
|
onClick: () => onSelect(conversation.id),
|
|
1981
2492
|
"aria-current": isActive ? "true" : void 0,
|
|
1982
|
-
className: "flex-1 min-w-0 text-left px-
|
|
2493
|
+
className: "flex-1 min-w-0 text-left px-5 py-2.5",
|
|
1983
2494
|
children: [
|
|
1984
|
-
/* @__PURE__ */ jsx34("div", { className: "text-sm font-medium text-
|
|
1985
|
-
/* @__PURE__ */ jsx34("div", { className: "text-xs text-
|
|
2495
|
+
/* @__PURE__ */ jsx34("div", { className: "text-sm font-medium text-text-primary truncate", children: conversation.title }),
|
|
2496
|
+
/* @__PURE__ */ jsx34("div", { className: "text-xs text-text-muted truncate mt-0.5 leading-relaxed", children: conversation.lastMessage })
|
|
1986
2497
|
]
|
|
1987
2498
|
}
|
|
1988
2499
|
),
|
|
@@ -1992,7 +2503,7 @@ function ConversationList({
|
|
|
1992
2503
|
type: "button",
|
|
1993
2504
|
onClick: () => onDelete(conversation.id),
|
|
1994
2505
|
"aria-label": `Delete ${conversation.title}`,
|
|
1995
|
-
className: "shrink-0 p-1.5 m-2 rounded-lg text-
|
|
2506
|
+
className: "shrink-0 p-1.5 m-2 rounded-lg text-text-muted hover:text-status-error hover:bg-status-error/10 transition-colors duration-150",
|
|
1996
2507
|
children: /* @__PURE__ */ jsxs31(
|
|
1997
2508
|
"svg",
|
|
1998
2509
|
{
|
|
@@ -2019,7 +2530,7 @@ function ConversationList({
|
|
|
2019
2530
|
conversation.id
|
|
2020
2531
|
);
|
|
2021
2532
|
}),
|
|
2022
|
-
conversations.length === 0 && /* @__PURE__ */ jsx34("li", { className: "px-
|
|
2533
|
+
conversations.length === 0 && /* @__PURE__ */ jsx34("li", { className: "px-5 py-12 text-center", children: /* @__PURE__ */ jsx34("span", { className: "text-sm text-text-muted font-body", children: "No conversations yet" }) })
|
|
2023
2534
|
] })
|
|
2024
2535
|
]
|
|
2025
2536
|
}
|
|
@@ -2027,9 +2538,9 @@ function ConversationList({
|
|
|
2027
2538
|
}
|
|
2028
2539
|
|
|
2029
2540
|
// src/layouts/AgentFullPage/AgentFullPage.tsx
|
|
2030
|
-
import { twMerge as
|
|
2031
|
-
import { useState as useState7, useCallback as
|
|
2032
|
-
import { Fragment, jsx as jsx35, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
2541
|
+
import { twMerge as twMerge15 } from "tailwind-merge";
|
|
2542
|
+
import { useState as useState7, useCallback as useCallback4 } from "react";
|
|
2543
|
+
import { Fragment as Fragment2, jsx as jsx35, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
2033
2544
|
function AgentFullPage({
|
|
2034
2545
|
endpoint,
|
|
2035
2546
|
title = "Chat",
|
|
@@ -2042,7 +2553,7 @@ function AgentFullPage({
|
|
|
2042
2553
|
className
|
|
2043
2554
|
}) {
|
|
2044
2555
|
const [sidebarOpen, setSidebarOpen] = useState7(false);
|
|
2045
|
-
const handleSelect =
|
|
2556
|
+
const handleSelect = useCallback4(
|
|
2046
2557
|
(id) => {
|
|
2047
2558
|
onConversationSelect?.(id);
|
|
2048
2559
|
setSidebarOpen(false);
|
|
@@ -2052,10 +2563,10 @@ function AgentFullPage({
|
|
|
2052
2563
|
return /* @__PURE__ */ jsxs32(
|
|
2053
2564
|
"div",
|
|
2054
2565
|
{
|
|
2055
|
-
className:
|
|
2566
|
+
className: twMerge15("flex h-screen w-full overflow-hidden bg-brand-dark", className),
|
|
2056
2567
|
"data-testid": "agent-full-page",
|
|
2057
2568
|
children: [
|
|
2058
|
-
showConversationList && /* @__PURE__ */ jsxs32(
|
|
2569
|
+
showConversationList && /* @__PURE__ */ jsxs32(Fragment2, { children: [
|
|
2059
2570
|
sidebarOpen && /* @__PURE__ */ jsx35(
|
|
2060
2571
|
"div",
|
|
2061
2572
|
{
|
|
@@ -2067,7 +2578,7 @@ function AgentFullPage({
|
|
|
2067
2578
|
/* @__PURE__ */ jsx35(
|
|
2068
2579
|
"aside",
|
|
2069
2580
|
{
|
|
2070
|
-
className:
|
|
2581
|
+
className: twMerge15(
|
|
2071
2582
|
"bg-brand-dark border-r border-brand-gold/15 w-72 shrink-0 flex-col z-40",
|
|
2072
2583
|
// Desktop: always visible
|
|
2073
2584
|
"hidden md:flex",
|
|
@@ -2133,7 +2644,7 @@ function AgentFullPage({
|
|
|
2133
2644
|
}
|
|
2134
2645
|
|
|
2135
2646
|
// src/layouts/AgentPanel/AgentPanel.tsx
|
|
2136
|
-
import { twMerge as
|
|
2647
|
+
import { twMerge as twMerge16 } from "tailwind-merge";
|
|
2137
2648
|
import { useRef as useRef6, useEffect as useEffect5 } from "react";
|
|
2138
2649
|
import { jsx as jsx36, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
2139
2650
|
function AgentPanel({
|
|
@@ -2158,13 +2669,13 @@ function AgentPanel({
|
|
|
2158
2669
|
return /* @__PURE__ */ jsxs33(
|
|
2159
2670
|
"div",
|
|
2160
2671
|
{
|
|
2161
|
-
className:
|
|
2672
|
+
className: twMerge16("fixed inset-0 z-50", !isOpen && "pointer-events-none"),
|
|
2162
2673
|
"aria-hidden": !isOpen,
|
|
2163
2674
|
children: [
|
|
2164
2675
|
/* @__PURE__ */ jsx36(
|
|
2165
2676
|
"div",
|
|
2166
2677
|
{
|
|
2167
|
-
className:
|
|
2678
|
+
className: twMerge16(
|
|
2168
2679
|
"fixed inset-0 transition-opacity duration-300",
|
|
2169
2680
|
isOpen ? "opacity-100 bg-brand-dark/70 backdrop-blur-sm pointer-events-auto" : "opacity-0 pointer-events-none"
|
|
2170
2681
|
),
|
|
@@ -2180,7 +2691,7 @@ function AgentPanel({
|
|
|
2180
2691
|
"aria-label": title,
|
|
2181
2692
|
"aria-modal": isOpen ? "true" : void 0,
|
|
2182
2693
|
style: { width: widthStyle, maxWidth: "100vw" },
|
|
2183
|
-
className:
|
|
2694
|
+
className: twMerge16(
|
|
2184
2695
|
"fixed top-0 h-full flex flex-col z-50 bg-brand-dark shadow-card",
|
|
2185
2696
|
"transition-transform duration-300 ease-in-out",
|
|
2186
2697
|
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"}`,
|
|
@@ -2222,8 +2733,8 @@ function AgentPanel({
|
|
|
2222
2733
|
}
|
|
2223
2734
|
|
|
2224
2735
|
// src/layouts/AgentWidget/AgentWidget.tsx
|
|
2225
|
-
import { twMerge as
|
|
2226
|
-
import { useState as useState8, useCallback as
|
|
2736
|
+
import { twMerge as twMerge17 } from "tailwind-merge";
|
|
2737
|
+
import { useState as useState8, useCallback as useCallback5 } from "react";
|
|
2227
2738
|
import { jsx as jsx37, jsxs as jsxs34 } from "react/jsx-runtime";
|
|
2228
2739
|
function AgentWidget({
|
|
2229
2740
|
endpoint,
|
|
@@ -2233,7 +2744,7 @@ function AgentWidget({
|
|
|
2233
2744
|
className
|
|
2234
2745
|
}) {
|
|
2235
2746
|
const [isOpen, setIsOpen] = useState8(false);
|
|
2236
|
-
const toggle =
|
|
2747
|
+
const toggle = useCallback5(() => {
|
|
2237
2748
|
setIsOpen((prev) => !prev);
|
|
2238
2749
|
}, []);
|
|
2239
2750
|
const positionClasses = position === "bottom-left" ? "left-4 bottom-4" : "right-4 bottom-4";
|
|
@@ -2246,7 +2757,7 @@ function AgentWidget({
|
|
|
2246
2757
|
role: "dialog",
|
|
2247
2758
|
"aria-label": title,
|
|
2248
2759
|
"aria-hidden": !isOpen,
|
|
2249
|
-
className:
|
|
2760
|
+
className: twMerge17(
|
|
2250
2761
|
"fixed z-50 flex flex-col",
|
|
2251
2762
|
"w-[min(400px,calc(100vw-2rem))] h-[min(600px,calc(100vh-6rem))]",
|
|
2252
2763
|
"rounded-2xl overflow-hidden border border-brand-gold/15",
|
|
@@ -2293,7 +2804,7 @@ function AgentWidget({
|
|
|
2293
2804
|
onClick: toggle,
|
|
2294
2805
|
"aria-label": isOpen ? "Close chat" : triggerLabel,
|
|
2295
2806
|
"aria-expanded": isOpen,
|
|
2296
|
-
className:
|
|
2807
|
+
className: twMerge17(
|
|
2297
2808
|
"fixed z-50 flex items-center justify-center w-14 h-14 rounded-full",
|
|
2298
2809
|
"bg-brand-blue text-brand-cream shadow-glow-cyan",
|
|
2299
2810
|
"hover:bg-brand-cyan hover:shadow-glow-cyan hover:scale-105",
|
|
@@ -2311,7 +2822,7 @@ function AgentWidget({
|
|
|
2311
2822
|
}
|
|
2312
2823
|
|
|
2313
2824
|
// src/layouts/AgentEmbed/AgentEmbed.tsx
|
|
2314
|
-
import { twMerge as
|
|
2825
|
+
import { twMerge as twMerge18 } from "tailwind-merge";
|
|
2315
2826
|
import { jsx as jsx38 } from "react/jsx-runtime";
|
|
2316
2827
|
function AgentEmbed({
|
|
2317
2828
|
endpoint,
|
|
@@ -2321,7 +2832,7 @@ function AgentEmbed({
|
|
|
2321
2832
|
return /* @__PURE__ */ jsx38(
|
|
2322
2833
|
"div",
|
|
2323
2834
|
{
|
|
2324
|
-
className:
|
|
2835
|
+
className: twMerge18("w-full h-full min-h-0", className),
|
|
2325
2836
|
"data-testid": "agent-embed",
|
|
2326
2837
|
children: /* @__PURE__ */ jsx38(
|
|
2327
2838
|
AgentChat,
|
|
@@ -2337,8 +2848,8 @@ function AgentEmbed({
|
|
|
2337
2848
|
|
|
2338
2849
|
// src/mcp/MCPToolCall/MCPToolCall.tsx
|
|
2339
2850
|
import { cva } from "class-variance-authority";
|
|
2340
|
-
import { twMerge as
|
|
2341
|
-
import { Badge as Badge7,
|
|
2851
|
+
import { twMerge as twMerge19 } from "tailwind-merge";
|
|
2852
|
+
import { Badge as Badge7, WaveLoader as WaveLoader4 } from "@surf-kit/core";
|
|
2342
2853
|
import { jsx as jsx39, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
2343
2854
|
var statusBadgeIntent = {
|
|
2344
2855
|
pending: "default",
|
|
@@ -2381,7 +2892,7 @@ function MCPToolCall({ call, isExpanded = false, onToggleExpand, className }) {
|
|
|
2381
2892
|
return /* @__PURE__ */ jsxs35(
|
|
2382
2893
|
"div",
|
|
2383
2894
|
{
|
|
2384
|
-
className:
|
|
2895
|
+
className: twMerge19(container({ status: call.status }), className),
|
|
2385
2896
|
"data-testid": "mcp-tool-call",
|
|
2386
2897
|
children: [
|
|
2387
2898
|
/* @__PURE__ */ jsxs35(
|
|
@@ -2398,7 +2909,7 @@ function MCPToolCall({ call, isExpanded = false, onToggleExpand, className }) {
|
|
|
2398
2909
|
call.serverName && /* @__PURE__ */ jsx39("span", { className: "text-xs text-text-secondary truncate", children: call.serverName })
|
|
2399
2910
|
] }),
|
|
2400
2911
|
/* @__PURE__ */ jsxs35("div", { className: "flex items-center gap-2 shrink-0", children: [
|
|
2401
|
-
call.status === "running" && /* @__PURE__ */ jsx39("span", { "aria-hidden": "true", children: /* @__PURE__ */ jsx39(
|
|
2912
|
+
call.status === "running" && /* @__PURE__ */ jsx39("span", { "aria-hidden": "true", children: /* @__PURE__ */ jsx39(WaveLoader4, { size: "sm", color: "#38bdf8" }) }),
|
|
2402
2913
|
/* @__PURE__ */ jsx39(
|
|
2403
2914
|
Badge7,
|
|
2404
2915
|
{
|
|
@@ -2472,7 +2983,7 @@ function MCPToolCall({ call, isExpanded = false, onToggleExpand, className }) {
|
|
|
2472
2983
|
}
|
|
2473
2984
|
|
|
2474
2985
|
// src/mcp/MCPResourceView/MCPResourceView.tsx
|
|
2475
|
-
import { twMerge as
|
|
2986
|
+
import { twMerge as twMerge20 } from "tailwind-merge";
|
|
2476
2987
|
import { jsx as jsx40, jsxs as jsxs36 } from "react/jsx-runtime";
|
|
2477
2988
|
function isImageMime(mime) {
|
|
2478
2989
|
return !!mime && mime.startsWith("image/");
|
|
@@ -2490,7 +3001,7 @@ function MCPResourceView({ resource, className }) {
|
|
|
2490
3001
|
return /* @__PURE__ */ jsxs36(
|
|
2491
3002
|
"div",
|
|
2492
3003
|
{
|
|
2493
|
-
className:
|
|
3004
|
+
className: twMerge20("rounded-lg border border-border bg-surface p-3 text-sm", className),
|
|
2494
3005
|
"data-testid": "mcp-resource-view",
|
|
2495
3006
|
children: [
|
|
2496
3007
|
/* @__PURE__ */ jsxs36("div", { className: "mb-2", children: [
|
|
@@ -2542,7 +3053,7 @@ function MCPResourceView({ resource, className }) {
|
|
|
2542
3053
|
// src/mcp/MCPServerStatus/MCPServerStatus.tsx
|
|
2543
3054
|
import { useState as useState9 } from "react";
|
|
2544
3055
|
import { cva as cva2 } from "class-variance-authority";
|
|
2545
|
-
import { twMerge as
|
|
3056
|
+
import { twMerge as twMerge21 } from "tailwind-merge";
|
|
2546
3057
|
import { jsx as jsx41, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
2547
3058
|
var statusDot = cva2("inline-block h-2 w-2 rounded-full shrink-0", {
|
|
2548
3059
|
variants: {
|
|
@@ -2570,7 +3081,7 @@ function MCPServerStatus({ server, className }) {
|
|
|
2570
3081
|
return /* @__PURE__ */ jsxs37(
|
|
2571
3082
|
"div",
|
|
2572
3083
|
{
|
|
2573
|
-
className:
|
|
3084
|
+
className: twMerge21("rounded-lg border border-border bg-surface p-3 text-sm", className),
|
|
2574
3085
|
"data-testid": "mcp-server-status",
|
|
2575
3086
|
children: [
|
|
2576
3087
|
/* @__PURE__ */ jsxs37("div", { className: "flex items-center gap-2 mb-1", children: [
|
|
@@ -2684,7 +3195,7 @@ function MCPServerStatus({ server, className }) {
|
|
|
2684
3195
|
// src/mcp/MCPApprovalDialog/MCPApprovalDialog.tsx
|
|
2685
3196
|
import { useRef as useRef7, useEffect as useEffect6 } from "react";
|
|
2686
3197
|
import { cva as cva3 } from "class-variance-authority";
|
|
2687
|
-
import { twMerge as
|
|
3198
|
+
import { twMerge as twMerge22 } from "tailwind-merge";
|
|
2688
3199
|
import { useDialog, FocusScope } from "react-aria";
|
|
2689
3200
|
import { Button as Button2, Badge as Badge8 } from "@surf-kit/core";
|
|
2690
3201
|
import { jsx as jsx42, jsxs as jsxs38 } from "react/jsx-runtime";
|
|
@@ -2744,7 +3255,7 @@ function MCPApprovalDialog({
|
|
|
2744
3255
|
{
|
|
2745
3256
|
...dialogProps,
|
|
2746
3257
|
ref,
|
|
2747
|
-
className:
|
|
3258
|
+
className: twMerge22(riskBorder({ risk: riskLevel }), className),
|
|
2748
3259
|
"data-testid": "mcp-approval-dialog",
|
|
2749
3260
|
children: [
|
|
2750
3261
|
/* @__PURE__ */ jsxs38("div", { className: "flex items-center justify-between mb-4", children: [
|
|
@@ -2913,7 +3424,7 @@ function ThumbsFeedback({
|
|
|
2913
3424
|
// src/feedback/FeedbackDialog/FeedbackDialog.tsx
|
|
2914
3425
|
import { useState as useState11 } from "react";
|
|
2915
3426
|
import { Dialog, Button as Button3, TextArea } from "@surf-kit/core";
|
|
2916
|
-
import { Fragment as
|
|
3427
|
+
import { Fragment as Fragment3, jsx as jsx44, jsxs as jsxs40 } from "react/jsx-runtime";
|
|
2917
3428
|
function FeedbackDialog({ isOpen, onClose, onSubmit, className }) {
|
|
2918
3429
|
const [comment, setComment] = useState11("");
|
|
2919
3430
|
const handleSubmit = () => {
|
|
@@ -2929,7 +3440,7 @@ function FeedbackDialog({ isOpen, onClose, onSubmit, className }) {
|
|
|
2929
3440
|
title: "Share your feedback",
|
|
2930
3441
|
size: "sm",
|
|
2931
3442
|
className,
|
|
2932
|
-
footer: /* @__PURE__ */ jsxs40(
|
|
3443
|
+
footer: /* @__PURE__ */ jsxs40(Fragment3, { children: [
|
|
2933
3444
|
/* @__PURE__ */ jsx44(Button3, { intent: "ghost", onPress: onClose, children: "Cancel" }),
|
|
2934
3445
|
/* @__PURE__ */ jsx44(Button3, { intent: "primary", onPress: handleSubmit, isDisabled: comment.trim().length === 0, children: "Submit" })
|
|
2935
3446
|
] }),
|