@jazzmind/busibox-app 3.0.37 → 3.0.39
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/components/chat/ChatContainer.d.ts.map +1 -1
- package/dist/components/chat/ChatContainer.js +122 -602
- package/dist/components/chat/ChatContainer.js.map +1 -1
- package/dist/components/chat/ChatInterface.d.ts +3 -4
- package/dist/components/chat/ChatInterface.d.ts.map +1 -1
- package/dist/components/chat/ChatInterface.js +159 -361
- package/dist/components/chat/ChatInterface.js.map +1 -1
- package/dist/components/chat/MessageList.d.ts +2 -1
- package/dist/components/chat/MessageList.d.ts.map +1 -1
- package/dist/components/chat/MessageList.js +33 -17
- package/dist/components/chat/MessageList.js.map +1 -1
- package/dist/components/chat/StepTimeline.d.ts +9 -0
- package/dist/components/chat/StepTimeline.d.ts.map +1 -0
- package/dist/components/chat/StepTimeline.js +110 -0
- package/dist/components/chat/StepTimeline.js.map +1 -0
- package/dist/components/chat/ThinkingStream.d.ts +13 -0
- package/dist/components/chat/ThinkingStream.d.ts.map +1 -0
- package/dist/components/chat/ThinkingStream.js +41 -0
- package/dist/components/chat/ThinkingStream.js.map +1 -0
- package/dist/components/chat/ThinkingToggle.d.ts.map +1 -1
- package/dist/components/chat/ThinkingToggle.js +15 -1
- package/dist/components/chat/ThinkingToggle.js.map +1 -1
- package/dist/lib/agent/agent-api-base.d.ts.map +1 -1
- package/dist/lib/agent/agent-api-base.js +0 -1
- package/dist/lib/agent/agent-api-base.js.map +1 -1
- package/dist/lib/agent/chat-client.d.ts.map +1 -1
- package/dist/lib/agent/chat-client.js +0 -1
- package/dist/lib/agent/chat-client.js.map +1 -1
- package/dist/lib/agent/index.d.ts +2 -0
- package/dist/lib/agent/index.d.ts.map +1 -1
- package/dist/lib/agent/index.js +2 -0
- package/dist/lib/agent/index.js.map +1 -1
- package/dist/lib/agent/stream-event-processor.d.ts +37 -0
- package/dist/lib/agent/stream-event-processor.d.ts.map +1 -0
- package/dist/lib/agent/stream-event-processor.js +204 -0
- package/dist/lib/agent/stream-event-processor.js.map +1 -0
- package/dist/lib/hooks/useChatStream.d.ts +34 -0
- package/dist/lib/hooks/useChatStream.d.ts.map +1 -0
- package/dist/lib/hooks/useChatStream.js +105 -0
- package/dist/lib/hooks/useChatStream.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { jsx as _jsx,
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
/**
|
|
4
4
|
* ChatInterface - Core Chat Component
|
|
5
5
|
*
|
|
@@ -27,36 +27,19 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
27
27
|
* />
|
|
28
28
|
* ```
|
|
29
29
|
*/
|
|
30
|
-
import { useState, useRef, useEffect } from 'react';
|
|
31
|
-
import { Send, Bot, Loader2, Paperclip,
|
|
30
|
+
import { useState, useRef, useEffect, useCallback } from 'react';
|
|
31
|
+
import { Send, Bot, Loader2, Paperclip, Plus, Trash2, Volume2, X } from 'lucide-react';
|
|
32
32
|
import toast from 'react-hot-toast';
|
|
33
|
-
import ReactMarkdown from 'react-markdown';
|
|
34
|
-
import remarkGfm from 'remark-gfm';
|
|
35
|
-
import remarkMath from 'remark-math';
|
|
36
|
-
import rehypeKatex from 'rehype-katex';
|
|
37
|
-
import 'katex/dist/katex.min.css';
|
|
38
33
|
import { MessageList } from './MessageList';
|
|
39
|
-
import {
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
42
|
-
|
|
43
|
-
// Real-time execution status display (legacy, for non-agentic streaming)
|
|
44
|
-
function ExecutionStatus({ events, isActive }) {
|
|
45
|
-
if (events.length === 0)
|
|
46
|
-
return null;
|
|
47
|
-
// Only show the last few relevant events
|
|
48
|
-
const relevantEvents = events.filter(e => ['planning', 'tool_start', 'tool_result', 'agent_start', 'agent_result', 'synthesis_start'].includes(e.type)).slice(-4);
|
|
49
|
-
if (relevantEvents.length === 0)
|
|
50
|
-
return null;
|
|
51
|
-
return (_jsxs("div", { className: "flex gap-3 justify-start mb-4", children: [_jsx("div", { className: "bg-blue-600 dark:bg-blue-500 rounded-full p-2 h-8 w-8 flex items-center justify-center flex-shrink-0", children: _jsx(Bot, { className: "w-4 h-4 text-white" }) }), _jsx("div", { className: "max-w-[80%] rounded-lg p-3 bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-gray-100", children: _jsxs("div", { className: "space-y-1.5", children: [relevantEvents.map((event, idx) => (_jsxs("div", { className: "flex items-center gap-2 text-sm", children: [event.type === 'planning' && (_jsxs(_Fragment, { children: [_jsx(Brain, { className: "w-3.5 h-3.5 text-purple-500 flex-shrink-0" }), _jsx("span", { className: "text-purple-700 dark:text-purple-400", children: event.message || 'Analyzing...' })] })), event.type === 'tool_start' && (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "w-3.5 h-3.5 text-blue-500 animate-spin flex-shrink-0" }), _jsx("span", { className: "text-blue-700 dark:text-blue-400", children: event.message || `Running ${event.data?.display_name || event.data?.tool}...` })] })), event.type === 'tool_result' && (_jsxs(_Fragment, { children: [event.data?.success !== false ? (_jsx(CheckCircle, { className: "w-3.5 h-3.5 text-green-500 flex-shrink-0" })) : (_jsx(AlertCircle, { className: "w-3.5 h-3.5 text-red-500 flex-shrink-0" })), _jsx("span", { className: event.data?.success !== false ? 'text-green-700 dark:text-green-400' : 'text-red-700 dark:text-red-400', children: event.message || `${event.data?.display_name || event.data?.tool_name} done` })] })), event.type === 'agent_start' && (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "w-3.5 h-3.5 text-indigo-500 animate-spin flex-shrink-0" }), _jsx("span", { className: "text-indigo-700 dark:text-indigo-400", children: event.message || `Consulting ${event.data?.display_name || event.data?.agent}...` })] })), event.type === 'agent_result' && (_jsxs(_Fragment, { children: [event.data?.success !== false ? (_jsx(CheckCircle, { className: "w-3.5 h-3.5 text-green-500 flex-shrink-0" })) : (_jsx(AlertCircle, { className: "w-3.5 h-3.5 text-red-500 flex-shrink-0" })), _jsx("span", { className: event.data?.success !== false ? 'text-green-700 dark:text-green-400' : 'text-red-700 dark:text-red-400', children: event.message || 'Agent responded' })] })), event.type === 'synthesis_start' && (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "w-3.5 h-3.5 text-purple-500 animate-spin flex-shrink-0" }), _jsx("span", { className: "text-purple-700 dark:text-purple-400", children: event.message || 'Combining results...' })] }))] }, idx))), isActive && (_jsxs("div", { className: "flex items-center gap-2 text-xs text-gray-500 dark:text-gray-400 mt-1", children: [_jsx(Loader2, { className: "w-3 h-3 animate-spin" }), _jsx("span", { children: "Processing..." })] }))] }) })] }));
|
|
52
|
-
}
|
|
53
|
-
export function ChatInterface({ token, agentUrl, agentId, enableWebSearch = false, enableDocSearch = false, allowAttachments = false, placeholder = 'Type your message...', welcomeMessage, model = 'auto', useStreaming = true, useAgenticStreaming = false, className = '', onMessageSent, onResponseReceived, initialConversationId, metadata, onConversationDeleted, }) {
|
|
34
|
+
import { stripThinkTags } from './chat-utils';
|
|
35
|
+
import { sendChatMessage, streamChatMessageAgentic, getConversationHistory } from '../../lib/agent/chat-client';
|
|
36
|
+
import { createAccumulator, processStreamEvent } from '../../lib/agent/stream-event-processor';
|
|
37
|
+
export function ChatInterface({ token, agentUrl, agentId, enableWebSearch = false, enableDocSearch = false, allowAttachments = false, placeholder = 'Type your message...', welcomeMessage, model = 'auto', useStreaming: _useStreaming = true, useAgenticStreaming = true, className = '', onMessageSent, onResponseReceived, initialConversationId, metadata, onConversationDeleted, }) {
|
|
54
38
|
const [messages, setMessages] = useState([]);
|
|
55
39
|
const [input, setInput] = useState('');
|
|
56
40
|
const [isLoading, setIsLoading] = useState(false);
|
|
57
41
|
const [streamingContent, setStreamingContent] = useState('');
|
|
58
|
-
const [
|
|
59
|
-
const [thoughts, setThoughts] = useState([]); // For agentic streaming
|
|
42
|
+
const [thoughts, setThoughts] = useState([]);
|
|
60
43
|
const [interimMessages, setInterimMessages] = useState([]);
|
|
61
44
|
const [streamingAgentName, setStreamingAgentName] = useState(undefined);
|
|
62
45
|
const [conversationId, setConversationId] = useState(initialConversationId);
|
|
@@ -70,29 +53,31 @@ export function ChatInterface({ token, agentUrl, agentId, enableWebSearch = fals
|
|
|
70
53
|
const messagesEndRef = useRef(null);
|
|
71
54
|
const fileInputRef = useRef(null);
|
|
72
55
|
const textareaRef = useRef(null);
|
|
56
|
+
const tokenRef = useRef(token);
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
tokenRef.current = token;
|
|
59
|
+
}, [token]);
|
|
73
60
|
const scrollToBottom = () => {
|
|
74
61
|
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
75
62
|
};
|
|
76
63
|
useEffect(() => {
|
|
77
64
|
scrollToBottom();
|
|
78
65
|
}, [messages, streamingContent]);
|
|
79
|
-
|
|
80
|
-
useEffect(() => {
|
|
81
|
-
if (initialConversationId && token) {
|
|
82
|
-
loadConversationHistory(initialConversationId);
|
|
83
|
-
}
|
|
84
|
-
}, [initialConversationId, token]);
|
|
85
|
-
const loadConversationHistory = async (convId) => {
|
|
66
|
+
const loadConversationHistory = useCallback(async (convId) => {
|
|
86
67
|
setLoadingHistory(true);
|
|
87
68
|
try {
|
|
88
|
-
const history = await getConversationHistory(convId, { token, agentUrl });
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
69
|
+
const history = await getConversationHistory(convId, { token: tokenRef.current, agentUrl });
|
|
70
|
+
const displayMessages = history.map(msg => {
|
|
71
|
+
const raw = msg;
|
|
72
|
+
const thoughts = raw.routing_decision?.thoughts || raw.thoughts;
|
|
73
|
+
return {
|
|
74
|
+
id: msg.id,
|
|
75
|
+
role: msg.role,
|
|
76
|
+
content: msg.content,
|
|
77
|
+
createdAt: new Date(raw.created_at || raw.createdAt || Date.now()),
|
|
78
|
+
thoughts: thoughts?.length > 0 ? thoughts : undefined,
|
|
79
|
+
};
|
|
80
|
+
});
|
|
96
81
|
setMessages(displayMessages);
|
|
97
82
|
setConversationId(convId);
|
|
98
83
|
}
|
|
@@ -103,7 +88,13 @@ export function ChatInterface({ token, agentUrl, agentId, enableWebSearch = fals
|
|
|
103
88
|
finally {
|
|
104
89
|
setLoadingHistory(false);
|
|
105
90
|
}
|
|
106
|
-
};
|
|
91
|
+
}, [agentUrl]);
|
|
92
|
+
// Load conversation history when initialConversationId changes (not on token refresh)
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
if (initialConversationId && tokenRef.current) {
|
|
95
|
+
loadConversationHistory(initialConversationId);
|
|
96
|
+
}
|
|
97
|
+
}, [initialConversationId, loadConversationHistory]);
|
|
107
98
|
const handleFileSelect = async (e) => {
|
|
108
99
|
const files = e.target.files;
|
|
109
100
|
if (!files || files.length === 0)
|
|
@@ -129,11 +120,10 @@ export function ChatInterface({ token, agentUrl, agentId, enableWebSearch = fals
|
|
|
129
120
|
const handleSubmit = async (e, overrideMessage) => {
|
|
130
121
|
e?.preventDefault();
|
|
131
122
|
const messageText = overrideMessage ?? input.trim();
|
|
132
|
-
|
|
133
|
-
if (!messageText || (isLoading && !promptActive))
|
|
123
|
+
if (!messageText)
|
|
134
124
|
return;
|
|
135
|
-
//
|
|
136
|
-
if (isLoading &&
|
|
125
|
+
// Abort any in-flight stream so the new message can proceed
|
|
126
|
+
if (isLoading && abortController) {
|
|
137
127
|
abortController.abort();
|
|
138
128
|
}
|
|
139
129
|
const userMessage = messageText;
|
|
@@ -174,337 +164,149 @@ export function ChatInterface({ token, agentUrl, agentId, enableWebSearch = fals
|
|
|
174
164
|
setInterimMessages([]);
|
|
175
165
|
setStreamingAgentName(undefined);
|
|
176
166
|
setStreamingParts([]);
|
|
177
|
-
|
|
178
|
-
let collectedThoughts = [];
|
|
179
|
-
let collectedParts = [];
|
|
167
|
+
const accumulated = createAccumulator();
|
|
180
168
|
let hasAddedMessage = false;
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
collectedThoughts = [...collectedThoughts, newEvent];
|
|
202
|
-
setThoughts(collectedThoughts);
|
|
203
|
-
break;
|
|
204
|
-
case 'tool_start':
|
|
205
|
-
{
|
|
206
|
-
collectedThoughts = [...collectedThoughts, newEvent];
|
|
207
|
-
setThoughts(collectedThoughts);
|
|
208
|
-
const toolSource = event.data?.source || 'tool';
|
|
209
|
-
const toolName = String(event.data?.data?.tool_name || event.data?.data?.display_name || toolSource);
|
|
210
|
-
const toolPart = {
|
|
211
|
-
type: 'tool_call',
|
|
212
|
-
id: `tool-${Date.now()}-${toolName}`,
|
|
213
|
-
name: toolName,
|
|
214
|
-
displayName: String(event.data?.data?.display_name || event.data?.message || toolName),
|
|
215
|
-
status: 'running',
|
|
216
|
-
input: (event.data?.data || undefined),
|
|
217
|
-
startedAt: new Date(),
|
|
169
|
+
for await (const event of streamChatMessageAgentic(request, { token: tokenRef.current, agentUrl, signal: controller.signal })) {
|
|
170
|
+
const parsed = event.data;
|
|
171
|
+
// message_complete and error have ChatInterface-specific side effects
|
|
172
|
+
// (creating DisplayMessages, toasts) so they're handled inline.
|
|
173
|
+
if (event.type === 'message_complete') {
|
|
174
|
+
if (!hasAddedMessage) {
|
|
175
|
+
setConversationId(parsed.conversation_id);
|
|
176
|
+
const cleanedContent = stripThinkTags(accumulated.fullContent);
|
|
177
|
+
if (cleanedContent) {
|
|
178
|
+
const finalParts = [
|
|
179
|
+
...accumulated.parts,
|
|
180
|
+
{ type: 'text', content: cleanedContent },
|
|
181
|
+
];
|
|
182
|
+
const assistantMessage = {
|
|
183
|
+
role: 'assistant',
|
|
184
|
+
content: cleanedContent,
|
|
185
|
+
timestamp: new Date(),
|
|
186
|
+
thoughts: accumulated.thoughts.length > 0 ? accumulated.thoughts : undefined,
|
|
187
|
+
agentName: accumulated.agentName,
|
|
188
|
+
parts: finalParts,
|
|
218
189
|
};
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
setStreamingParts(collectedParts);
|
|
222
|
-
}
|
|
223
|
-
break;
|
|
224
|
-
case 'tool_result':
|
|
225
|
-
{
|
|
226
|
-
collectedThoughts = [...collectedThoughts, newEvent];
|
|
227
|
-
setThoughts(collectedThoughts);
|
|
228
|
-
const resultSource = event.data?.source || 'tool';
|
|
229
|
-
const idx = pendingTools.get(resultSource);
|
|
230
|
-
if (idx !== undefined && collectedParts[idx]?.type === 'tool_call') {
|
|
231
|
-
const existing = collectedParts[idx];
|
|
232
|
-
const updated = {
|
|
233
|
-
...existing,
|
|
234
|
-
status: event.data?.data?.success === false ? 'error' : 'completed',
|
|
235
|
-
output: event.data?.message || undefined,
|
|
236
|
-
error: event.data?.data?.success === false ? String(event.data?.message || 'Failed') : undefined,
|
|
237
|
-
completedAt: new Date(),
|
|
238
|
-
};
|
|
239
|
-
collectedParts = [...collectedParts];
|
|
240
|
-
collectedParts[idx] = updated;
|
|
241
|
-
pendingTools.delete(resultSource);
|
|
242
|
-
}
|
|
243
|
-
else {
|
|
244
|
-
// No matching tool_start; append as a standalone completed tool
|
|
245
|
-
const toolName = String(event.data?.data?.tool_name || event.data?.data?.display_name || resultSource);
|
|
246
|
-
collectedParts = [...collectedParts, {
|
|
247
|
-
type: 'tool_call',
|
|
248
|
-
id: `tool-${Date.now()}-${toolName}`,
|
|
249
|
-
name: toolName,
|
|
250
|
-
displayName: String(event.data?.data?.display_name || toolName),
|
|
251
|
-
status: event.data?.data?.success === false ? 'error' : 'completed',
|
|
252
|
-
output: event.data?.message || undefined,
|
|
253
|
-
completedAt: new Date(),
|
|
254
|
-
}];
|
|
255
|
-
}
|
|
256
|
-
setStreamingParts(collectedParts);
|
|
190
|
+
setMessages((prev) => [...prev, assistantMessage]);
|
|
191
|
+
hasAddedMessage = true;
|
|
257
192
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
? `${interimMessage || 'Voice output ready'} (${audioUrl})`
|
|
267
|
-
: interimMessage;
|
|
268
|
-
if (rendered) {
|
|
269
|
-
setInterimMessages(prev => [...prev, rendered]);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
break;
|
|
273
|
-
case 'content':
|
|
274
|
-
{
|
|
275
|
-
const contentData = event.data?.data || {};
|
|
276
|
-
const msgText = event.data?.message || '';
|
|
277
|
-
if (contentData.streaming && contentData.partial) {
|
|
278
|
-
fullContent += msgText;
|
|
279
|
-
}
|
|
280
|
-
else if (contentData.complete) {
|
|
281
|
-
// Final marker
|
|
282
|
-
}
|
|
283
|
-
else if (msgText) {
|
|
284
|
-
fullContent = msgText;
|
|
285
|
-
}
|
|
286
|
-
const thinkTexts = extractThinkContent(fullContent);
|
|
287
|
-
if (thinkTexts.length > 0) {
|
|
288
|
-
const thinkThought = {
|
|
289
|
-
type: 'thought',
|
|
290
|
-
source: 'model',
|
|
291
|
-
message: thinkTexts.join('\n\n'),
|
|
292
|
-
data: event.data,
|
|
293
|
-
timestamp: new Date(),
|
|
294
|
-
};
|
|
295
|
-
const hasModelThought = collectedThoughts.some(t => t.source === 'model' && t.type === 'thought');
|
|
296
|
-
if (hasModelThought) {
|
|
297
|
-
collectedThoughts = collectedThoughts.map(t => t.source === 'model' && t.type === 'thought' ? thinkThought : t);
|
|
298
|
-
}
|
|
299
|
-
else {
|
|
300
|
-
collectedThoughts = [...collectedThoughts, thinkThought];
|
|
301
|
-
}
|
|
302
|
-
setThoughts(collectedThoughts);
|
|
303
|
-
}
|
|
304
|
-
setStreamingContent(stripThinkTags(fullContent));
|
|
193
|
+
setStreamingContent('');
|
|
194
|
+
setThoughts([]);
|
|
195
|
+
setInterimMessages([]);
|
|
196
|
+
setStreamingAgentName(undefined);
|
|
197
|
+
setStreamingParts([]);
|
|
198
|
+
setIsLoading(false);
|
|
199
|
+
if (cleanedContent) {
|
|
200
|
+
onResponseReceived?.(cleanedContent);
|
|
305
201
|
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
hasAddedMessage = true;
|
|
330
|
-
}
|
|
331
|
-
setStreamingContent('');
|
|
332
|
-
setThoughts([]);
|
|
333
|
-
setInterimMessages([]);
|
|
334
|
-
setStreamingParts([]);
|
|
335
|
-
}
|
|
202
|
+
}
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (event.type === 'error') {
|
|
206
|
+
const errorMessage = parsed?.message || parsed?.error || 'An error occurred';
|
|
207
|
+
const errorSource = parsed?.source || parsed?.data?.source || '';
|
|
208
|
+
const isToolError = errorSource && !errorSource.includes('agent') && !errorSource.includes('dispatcher');
|
|
209
|
+
if (isToolError) {
|
|
210
|
+
accumulated.thoughts = [...accumulated.thoughts, {
|
|
211
|
+
type: 'error',
|
|
212
|
+
source: errorSource,
|
|
213
|
+
message: `Tool error (${errorSource}): ${errorMessage}`,
|
|
214
|
+
data: parsed,
|
|
215
|
+
timestamp: new Date(),
|
|
216
|
+
}];
|
|
217
|
+
setThoughts(accumulated.thoughts);
|
|
218
|
+
const errIdx = accumulated.pendingTools.get(errorSource);
|
|
219
|
+
if (errIdx !== undefined && accumulated.parts[errIdx]?.type === 'tool_call') {
|
|
220
|
+
const existing = accumulated.parts[errIdx];
|
|
221
|
+
accumulated.parts = [...accumulated.parts];
|
|
222
|
+
accumulated.parts[errIdx] = { ...existing, status: 'error', error: errorMessage, completedAt: new Date() };
|
|
223
|
+
accumulated.pendingTools.delete(errorSource);
|
|
224
|
+
setStreamingParts(accumulated.parts);
|
|
336
225
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
case 'message_complete':
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
toast.error(errorMessage);
|
|
341
229
|
if (!hasAddedMessage) {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
agentName: streamingAgentName,
|
|
356
|
-
parts: finalParts,
|
|
357
|
-
};
|
|
358
|
-
setMessages((prev) => [...prev, assistantMessage]);
|
|
359
|
-
hasAddedMessage = true;
|
|
360
|
-
}
|
|
361
|
-
setStreamingContent('');
|
|
362
|
-
setThoughts([]);
|
|
363
|
-
setInterimMessages([]);
|
|
364
|
-
setStreamingAgentName(undefined);
|
|
365
|
-
setStreamingParts([]);
|
|
366
|
-
setIsLoading(false);
|
|
367
|
-
if (cleanedContent) {
|
|
368
|
-
onResponseReceived?.(cleanedContent);
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
break;
|
|
372
|
-
case 'error':
|
|
373
|
-
{
|
|
374
|
-
const errorMessage = event.data?.message || event.data?.error || 'An error occurred';
|
|
375
|
-
const errorSource = event.data?.source || event.data?.data?.source || '';
|
|
376
|
-
const isToolError = errorSource && !errorSource.includes('agent') && !errorSource.includes('dispatcher');
|
|
377
|
-
if (isToolError) {
|
|
378
|
-
collectedThoughts = [...collectedThoughts, {
|
|
379
|
-
type: 'error',
|
|
380
|
-
source: errorSource,
|
|
381
|
-
message: `Tool error (${errorSource}): ${errorMessage}`,
|
|
382
|
-
data: event.data,
|
|
383
|
-
timestamp: new Date(),
|
|
384
|
-
}];
|
|
385
|
-
setThoughts(collectedThoughts);
|
|
386
|
-
// Update matching pending tool part to error status
|
|
387
|
-
const errIdx = pendingTools.get(errorSource);
|
|
388
|
-
if (errIdx !== undefined && collectedParts[errIdx]?.type === 'tool_call') {
|
|
389
|
-
const existing = collectedParts[errIdx];
|
|
390
|
-
collectedParts = [...collectedParts];
|
|
391
|
-
collectedParts[errIdx] = { ...existing, status: 'error', error: errorMessage, completedAt: new Date() };
|
|
392
|
-
pendingTools.delete(errorSource);
|
|
393
|
-
setStreamingParts(collectedParts);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
else {
|
|
397
|
-
toast.error(errorMessage);
|
|
398
|
-
if (!hasAddedMessage) {
|
|
399
|
-
const errorContent = fullContent.trim()
|
|
400
|
-
? `${fullContent}\n\n**Error:** ${errorMessage}`
|
|
401
|
-
: `**Error:** ${errorMessage}`;
|
|
402
|
-
const errorAssistantMessage = {
|
|
403
|
-
role: 'assistant',
|
|
404
|
-
content: errorContent,
|
|
405
|
-
timestamp: new Date(),
|
|
406
|
-
thoughts: collectedThoughts.length > 0 ? collectedThoughts : undefined,
|
|
407
|
-
agentName: streamingAgentName,
|
|
408
|
-
parts: collectedParts,
|
|
409
|
-
};
|
|
410
|
-
setMessages((prev) => [...prev, errorAssistantMessage]);
|
|
411
|
-
hasAddedMessage = true;
|
|
412
|
-
}
|
|
413
|
-
setStreamingContent('');
|
|
414
|
-
setThoughts([]);
|
|
415
|
-
setInterimMessages([]);
|
|
416
|
-
setStreamingAgentName(undefined);
|
|
417
|
-
setStreamingParts([]);
|
|
418
|
-
}
|
|
230
|
+
const errorContent = accumulated.fullContent.trim()
|
|
231
|
+
? `${accumulated.fullContent}\n\n**Error:** ${errorMessage}`
|
|
232
|
+
: `**Error:** ${errorMessage}`;
|
|
233
|
+
const errorAssistantMessage = {
|
|
234
|
+
role: 'assistant',
|
|
235
|
+
content: errorContent,
|
|
236
|
+
timestamp: new Date(),
|
|
237
|
+
thoughts: accumulated.thoughts.length > 0 ? accumulated.thoughts : undefined,
|
|
238
|
+
agentName: accumulated.agentName,
|
|
239
|
+
parts: accumulated.parts,
|
|
240
|
+
};
|
|
241
|
+
setMessages((prev) => [...prev, errorAssistantMessage]);
|
|
242
|
+
hasAddedMessage = true;
|
|
419
243
|
}
|
|
420
|
-
|
|
244
|
+
setStreamingContent('');
|
|
245
|
+
setThoughts([]);
|
|
246
|
+
setInterimMessages([]);
|
|
247
|
+
setStreamingAgentName(undefined);
|
|
248
|
+
setStreamingParts([]);
|
|
249
|
+
}
|
|
250
|
+
continue;
|
|
421
251
|
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
case 'model_selected':
|
|
453
|
-
collectedEvents = [...collectedEvents, newEvent];
|
|
454
|
-
setExecutionEvents(collectedEvents);
|
|
455
|
-
break;
|
|
456
|
-
case 'content_chunk':
|
|
457
|
-
// Only append if chunk is defined and not empty
|
|
458
|
-
if (event.data.chunk !== undefined && event.data.chunk !== null) {
|
|
459
|
-
fullContent += event.data.chunk;
|
|
460
|
-
setStreamingContent(fullContent);
|
|
461
|
-
}
|
|
462
|
-
break;
|
|
463
|
-
case 'execution_complete':
|
|
464
|
-
// Clear execution events when content starts flowing
|
|
465
|
-
break;
|
|
466
|
-
case 'message_complete':
|
|
467
|
-
setConversationId(event.data.conversation_id);
|
|
468
|
-
// Add assistant message to display
|
|
252
|
+
// All other events go through the shared processor
|
|
253
|
+
const result = processStreamEvent(event.type, parsed, accumulated);
|
|
254
|
+
if (result.conversationId && !result.titleUpdate) {
|
|
255
|
+
setConversationId(result.conversationId);
|
|
256
|
+
}
|
|
257
|
+
if (accumulated.agentName) {
|
|
258
|
+
setStreamingAgentName(accumulated.agentName);
|
|
259
|
+
}
|
|
260
|
+
if (result.content !== undefined) {
|
|
261
|
+
setStreamingContent(result.content);
|
|
262
|
+
}
|
|
263
|
+
if (result.thoughts) {
|
|
264
|
+
setThoughts(result.thoughts);
|
|
265
|
+
}
|
|
266
|
+
if (result.parts) {
|
|
267
|
+
setStreamingParts(result.parts);
|
|
268
|
+
}
|
|
269
|
+
if (result.interimMessages) {
|
|
270
|
+
setInterimMessages(result.interimMessages);
|
|
271
|
+
}
|
|
272
|
+
if (result.quickReplies) {
|
|
273
|
+
setQuickReplies(result.quickReplies);
|
|
274
|
+
}
|
|
275
|
+
if (result.promptActive !== undefined) {
|
|
276
|
+
setPromptActive(result.promptActive);
|
|
277
|
+
}
|
|
278
|
+
// prompt event: finalize accumulated content as a completed message
|
|
279
|
+
if (event.type === 'prompt' && result.promptActive) {
|
|
280
|
+
const cleanedSoFar = stripThinkTags(accumulated.fullContent);
|
|
281
|
+
if (cleanedSoFar && !hasAddedMessage) {
|
|
469
282
|
const assistantMessage = {
|
|
470
283
|
role: 'assistant',
|
|
471
|
-
content:
|
|
284
|
+
content: cleanedSoFar,
|
|
472
285
|
timestamp: new Date(),
|
|
286
|
+
thoughts: accumulated.thoughts.length > 0 ? accumulated.thoughts : undefined,
|
|
287
|
+
agentName: accumulated.agentName,
|
|
288
|
+
parts: accumulated.parts,
|
|
473
289
|
};
|
|
474
290
|
setMessages((prev) => [...prev, assistantMessage]);
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
const streamErrorMessage = event.data?.error || 'An error occurred';
|
|
482
|
-
toast.error(streamErrorMessage);
|
|
483
|
-
// Add error message to chat
|
|
484
|
-
const streamErrorAssistantMessage = {
|
|
485
|
-
role: 'assistant',
|
|
486
|
-
content: `⚠️ **Error:** ${streamErrorMessage}`,
|
|
487
|
-
timestamp: new Date(),
|
|
488
|
-
};
|
|
489
|
-
setMessages((prev) => [...prev, streamErrorAssistantMessage]);
|
|
490
|
-
setStreamingContent('');
|
|
491
|
-
setExecutionEvents([]);
|
|
492
|
-
break;
|
|
291
|
+
hasAddedMessage = true;
|
|
292
|
+
}
|
|
293
|
+
setStreamingContent('');
|
|
294
|
+
setThoughts([]);
|
|
295
|
+
setInterimMessages([]);
|
|
296
|
+
setStreamingParts([]);
|
|
493
297
|
}
|
|
494
298
|
}
|
|
495
299
|
}
|
|
496
300
|
else {
|
|
497
|
-
// Non-streaming
|
|
498
|
-
const response = await sendChatMessage(request, { token, agentUrl });
|
|
301
|
+
// Non-streaming fallback
|
|
302
|
+
const response = await sendChatMessage(request, { token: tokenRef.current, agentUrl });
|
|
499
303
|
setConversationId(response.conversation_id);
|
|
500
|
-
// Add assistant message to display
|
|
501
304
|
const assistantMessage = {
|
|
502
305
|
role: 'assistant',
|
|
503
306
|
content: response.content,
|
|
504
307
|
timestamp: new Date(),
|
|
505
308
|
};
|
|
506
309
|
setMessages((prev) => [...prev, assistantMessage]);
|
|
507
|
-
// Callback
|
|
508
310
|
onResponseReceived?.(response.content);
|
|
509
311
|
}
|
|
510
312
|
// Clear attachments after successful send
|
|
@@ -539,7 +341,6 @@ export function ChatInterface({ token, agentUrl, agentId, enableWebSearch = fals
|
|
|
539
341
|
finally {
|
|
540
342
|
setIsLoading(false);
|
|
541
343
|
setStreamingContent('');
|
|
542
|
-
setExecutionEvents([]);
|
|
543
344
|
setThoughts([]);
|
|
544
345
|
setInterimMessages([]);
|
|
545
346
|
setAbortController(null);
|
|
@@ -571,7 +372,6 @@ export function ChatInterface({ token, agentUrl, agentId, enableWebSearch = fals
|
|
|
571
372
|
setMessages([]);
|
|
572
373
|
setConversationId(undefined);
|
|
573
374
|
setStreamingContent('');
|
|
574
|
-
setExecutionEvents([]);
|
|
575
375
|
setThoughts([]);
|
|
576
376
|
setInterimMessages([]);
|
|
577
377
|
setIsLoading(false);
|
|
@@ -592,7 +392,7 @@ export function ChatInterface({ token, agentUrl, agentId, enableWebSearch = fals
|
|
|
592
392
|
const response = await fetch(`${agentUrl || ''}/chat/${conversationId}/messages/${messageId}`, {
|
|
593
393
|
method: 'DELETE',
|
|
594
394
|
headers: {
|
|
595
|
-
'Authorization': `Bearer ${
|
|
395
|
+
'Authorization': `Bearer ${tokenRef.current}`,
|
|
596
396
|
'Content-Type': 'application/json',
|
|
597
397
|
},
|
|
598
398
|
});
|
|
@@ -620,7 +420,7 @@ export function ChatInterface({ token, agentUrl, agentId, enableWebSearch = fals
|
|
|
620
420
|
const response = await fetch(`${agentUrl || ''}/conversations/${conversationId}`, {
|
|
621
421
|
method: 'DELETE',
|
|
622
422
|
headers: {
|
|
623
|
-
'Authorization': `Bearer ${
|
|
423
|
+
'Authorization': `Bearer ${tokenRef.current}`,
|
|
624
424
|
'Content-Type': 'application/json',
|
|
625
425
|
},
|
|
626
426
|
});
|
|
@@ -689,9 +489,7 @@ export function ChatInterface({ token, agentUrl, agentId, enableWebSearch = fals
|
|
|
689
489
|
});
|
|
690
490
|
}
|
|
691
491
|
return displayMessages;
|
|
692
|
-
})(), streamingContent:
|
|
693
|
-
.filter((p) => p.type === 'tool_call')
|
|
694
|
-
.map((part) => (_jsx(StreamingToolCard, { part: part }, part.id))) })), interimMessages.length > 0 && (_jsx("div", { className: "mb-3 space-y-1", children: interimMessages.map((msg, idx) => (_jsx("div", { className: "text-xs rounded border border-amber-200 dark:border-amber-800 bg-amber-50 dark:bg-amber-900/20 px-2 py-1 text-amber-800 dark:text-amber-200", children: msg }, `interim-${idx}`))) })), streamingContent ? (_jsxs("div", { className: "prose prose-sm dark:prose-invert max-w-none prose-headings:font-semibold prose-h1:text-xl prose-h1:mt-4 prose-h1:mb-3 prose-h2:text-lg prose-h2:mt-4 prose-h2:mb-2 prose-h3:text-base prose-h3:mt-3 prose-h3:mb-2 prose-p:my-2 prose-p:leading-relaxed prose-ul:my-3 prose-ol:my-3 prose-li:my-0.5 prose-hr:my-6 prose-strong:font-semibold prose-code:px-1 prose-code:py-0.5 prose-code:rounded prose-code:text-sm prose-pre:border prose-blockquote:border-l-4 prose-blockquote:pl-4 prose-blockquote:italic prose-a:text-blue-600 dark:prose-a:text-blue-400 prose-a:no-underline hover:prose-a:underline", children: [_jsx(ReactMarkdown, { remarkPlugins: [remarkGfm, remarkMath], rehypePlugins: [rehypeKatex], components: streamingMarkdownComponents, children: preprocessLatex(streamingContent) }), _jsx("span", { className: "inline-block w-2 h-4 bg-gray-400 dark:bg-gray-500 animate-pulse ml-1" })] })) : (_jsxs("div", { className: "flex items-center gap-2 text-gray-500 dark:text-gray-400", children: [_jsx(Loader2, { className: "w-4 h-4 animate-spin" }), _jsx("span", { children: "Thinking..." })] }))] }) })] })), !useAgenticStreaming && isLoading && executionEvents.length > 0 && !streamingContent && (_jsx(ExecutionStatus, { events: executionEvents, isActive: isLoading })), !useAgenticStreaming && isLoading && !streamingContent && executionEvents.length === 0 && (_jsxs("div", { className: "flex gap-3 justify-start", children: [_jsx("div", { className: "bg-blue-600 dark:bg-blue-500 rounded-full p-2 h-8 w-8 flex items-center justify-center flex-shrink-0", children: _jsx(Bot, { className: "w-4 h-4 text-white" }) }), _jsx("div", { className: "max-w-[80%] rounded-lg p-4 bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-gray-100", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Loader2, { className: "w-4 h-4 animate-spin" }), _jsx("span", { className: "text-sm", children: "Thinking..." })] }) })] })), _jsx("div", { ref: messagesEndRef })] }), attachments.length > 0 && (_jsx("div", { className: "flex-shrink-0 px-4 py-2 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800", children: _jsx("div", { className: "flex flex-wrap gap-2", children: attachments.map((attachment, index) => (_jsxs("div", { className: "flex items-center gap-2 bg-white dark:bg-gray-700 px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-600", children: [_jsx("span", { className: "text-sm text-gray-700 dark:text-gray-200", children: attachment.name }), _jsx("button", { onClick: () => handleRemoveAttachment(index), className: "text-gray-400 dark:text-gray-500 hover:text-red-600 dark:hover:text-red-400", children: "\u00D7" })] }, index))) }) })), quickReplies.length > 0 && (_jsx("div", { className: "flex-shrink-0 px-3 pt-2 pb-1 flex flex-wrap gap-2 justify-center border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900", children: quickReplies.map((reply) => (_jsx("button", { type: "button", onClick: () => handleQuickReply(reply), className: "px-4 py-1.5 text-sm font-medium rounded-full border border-blue-500 dark:border-blue-400 text-blue-600 dark:text-blue-400 hover:bg-blue-50 dark:hover:bg-blue-900/30 transition-colors", children: reply }, reply))) })), _jsx("form", { onSubmit: handleSubmit, className: "flex-shrink-0 p-3 border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900", children: _jsxs("div", { className: "flex gap-2 items-end", children: [allowAttachments && (_jsxs(_Fragment, { children: [_jsx("input", { ref: fileInputRef, type: "file", multiple: true, onChange: handleFileSelect, className: "hidden" }), _jsx("button", { type: "button", onClick: () => fileInputRef.current?.click(), disabled: isLoading, className: "p-2 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 disabled:opacity-50 disabled:cursor-not-allowed flex-shrink-0", title: "Attach files", children: _jsx(Paperclip, { className: "w-5 h-5" }) })] })), _jsx("textarea", { ref: textareaRef, value: input, onChange: (e) => {
|
|
492
|
+
})(), streamingContent: streamingContent || undefined, streamingAgentName: streamingAgentName, streamingThoughts: thoughts, streamingParts: streamingParts, isLoading: isLoading, onDeleteMessage: handleDeleteMessage, onRetryMessage: handleRetryMessage, onSuggestedAction: (action) => handleSubmit(null, action) })), _jsx("div", { ref: messagesEndRef })] }), attachments.length > 0 && (_jsx("div", { className: "flex-shrink-0 px-4 py-2 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800", children: _jsx("div", { className: "flex flex-wrap gap-2", children: attachments.map((attachment, index) => (_jsxs("div", { className: "flex items-center gap-2 bg-white dark:bg-gray-700 px-3 py-1.5 rounded-lg border border-gray-200 dark:border-gray-600", children: [_jsx("span", { className: "text-sm text-gray-700 dark:text-gray-200", children: attachment.name }), _jsx("button", { onClick: () => handleRemoveAttachment(index), className: "text-gray-400 dark:text-gray-500 hover:text-red-600 dark:hover:text-red-400", children: "\u00D7" })] }, index))) }) })), quickReplies.length > 0 && (_jsx("div", { className: "flex-shrink-0 px-3 pt-2 pb-1 flex flex-wrap gap-2 justify-center border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900", children: quickReplies.map((reply) => (_jsx("button", { type: "button", onClick: () => handleQuickReply(reply), className: "px-4 py-1.5 text-sm font-medium rounded-full border border-blue-500 dark:border-blue-400 text-blue-600 dark:text-blue-400 hover:bg-blue-50 dark:hover:bg-blue-900/30 transition-colors", children: reply }, reply))) })), _jsx("form", { onSubmit: handleSubmit, className: "flex-shrink-0 p-3 border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900", children: _jsxs("div", { className: "flex gap-2 items-end", children: [allowAttachments && (_jsxs(_Fragment, { children: [_jsx("input", { ref: fileInputRef, type: "file", multiple: true, onChange: handleFileSelect, className: "hidden" }), _jsx("button", { type: "button", onClick: () => fileInputRef.current?.click(), disabled: isLoading, className: "p-2 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 disabled:opacity-50 disabled:cursor-not-allowed flex-shrink-0", title: "Attach files", children: _jsx(Paperclip, { className: "w-5 h-5" }) })] })), _jsx("textarea", { ref: textareaRef, value: input, onChange: (e) => {
|
|
695
493
|
setInput(e.target.value);
|
|
696
494
|
if (textareaRef.current) {
|
|
697
495
|
textareaRef.current.style.height = 'auto';
|
|
@@ -700,10 +498,10 @@ export function ChatInterface({ token, agentUrl, agentId, enableWebSearch = fals
|
|
|
700
498
|
}, onKeyDown: (e) => {
|
|
701
499
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
702
500
|
e.preventDefault();
|
|
703
|
-
if (input.trim()
|
|
501
|
+
if (input.trim()) {
|
|
704
502
|
handleSubmit(e);
|
|
705
503
|
}
|
|
706
504
|
}
|
|
707
|
-
}, placeholder: promptActive ? 'Type your reply...' : placeholder, rows: 1, className: "flex-1 px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 focus:border-transparent resize-none", style: { minHeight: '40px', maxHeight: '200px' }, disabled:
|
|
505
|
+
}, placeholder: promptActive ? 'Type your reply...' : placeholder, rows: 1, className: "flex-1 px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 focus:border-transparent resize-none", style: { minHeight: '40px', maxHeight: '200px' }, disabled: false }), isLoading && !promptActive && (_jsx("button", { type: "button", onClick: handleCancel, className: "bg-red-600 dark:bg-red-500 hover:bg-red-700 dark:hover:bg-red-600 text-white px-4 py-2 rounded-lg transition-colors flex items-center gap-2 font-medium flex-shrink-0", title: "Cancel", children: _jsx("span", { className: "w-5 h-5 flex items-center justify-center", children: "\u23F9" }) })), _jsx("button", { type: "submit", disabled: !input.trim(), className: "bg-blue-600 dark:bg-blue-500 hover:bg-blue-700 dark:hover:bg-blue-600 disabled:bg-gray-400 dark:disabled:bg-gray-600 text-white px-4 py-2 rounded-lg transition-colors flex items-center gap-2 font-medium disabled:cursor-not-allowed flex-shrink-0", children: _jsx(Send, { className: "w-5 h-5" }) })] }) }), showVoiceComingSoon && (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 px-4", children: _jsxs("div", { className: "w-full max-w-sm rounded-xl bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 shadow-xl p-4", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsxs("div", { children: [_jsx("h4", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: "Voice Mode" }), _jsx("p", { className: "mt-2 text-sm text-gray-600 dark:text-gray-300", children: "Voice mode is coming soon. Chat remains text-only for now." })] }), _jsx("button", { type: "button", onClick: () => setShowVoiceComingSoon(false), className: "p-1 rounded-md text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700", "aria-label": "Close voice mode modal", children: _jsx(X, { className: "w-4 h-4" }) })] }), _jsx("div", { className: "mt-4 flex justify-end", children: _jsx("button", { type: "button", onClick: () => setShowVoiceComingSoon(false), className: "px-3 py-1.5 text-sm font-medium rounded-lg bg-blue-600 text-white hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600", children: "Got it" }) })] }) }))] }));
|
|
708
506
|
}
|
|
709
507
|
//# sourceMappingURL=ChatInterface.js.map
|