@chatwidgetai/chat-widget 0.2.3 → 0.2.5
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/actions/google-calendar-appointment/component.d.ts +6 -0
- package/dist/actions/google-calendar-appointment/component.d.ts.map +1 -0
- package/dist/actions/google-calendar-appointment/handler.d.ts +2 -0
- package/dist/actions/google-calendar-appointment/handler.d.ts.map +1 -0
- package/dist/actions/google-calendar-appointment/index.d.ts +3 -0
- package/dist/actions/google-calendar-appointment/index.d.ts.map +1 -0
- package/dist/actions/index.d.ts +3 -0
- package/dist/actions/index.d.ts.map +1 -0
- package/dist/actions/registry.d.ts +24 -0
- package/dist/actions/registry.d.ts.map +1 -0
- package/dist/ai-chat-widget.umd.js +803 -307
- package/dist/ai-chat-widget.umd.js.map +1 -1
- package/dist/api/client.d.ts +8 -27
- package/dist/api/client.d.ts.map +1 -1
- package/dist/components/ChatWidget.d.ts.map +1 -1
- package/dist/components/ChatWindow.d.ts.map +1 -1
- package/dist/components/Message.d.ts.map +1 -1
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/ToolMessageGroup.d.ts.map +1 -1
- package/dist/hooks/useChat.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +803 -307
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +803 -307
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +86 -40
- package/dist/types/index.d.ts.map +1 -1
- package/dist/umd.d.ts +2 -1
- package/dist/umd.d.ts.map +1 -1
- package/dist/utils/sse-parser.d.ts +2 -0
- package/dist/utils/sse-parser.d.ts.map +1 -0
- package/dist/utils/storage.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -1,13 +1,53 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
async function* parseSSEStream(response, validator) {
|
|
5
|
+
if (!response.body) {
|
|
6
|
+
throw new Error("Response body is null");
|
|
7
|
+
}
|
|
8
|
+
const reader = response.body.getReader();
|
|
9
|
+
const decoder = new TextDecoder();
|
|
10
|
+
let buffer = "";
|
|
11
|
+
try {
|
|
12
|
+
while (true) {
|
|
13
|
+
const { done, value } = await reader.read();
|
|
14
|
+
if (done)
|
|
15
|
+
break;
|
|
16
|
+
buffer += decoder.decode(value, { stream: true });
|
|
17
|
+
const chunks = buffer.split("\n\n");
|
|
18
|
+
buffer = chunks.pop() || "";
|
|
19
|
+
for (const chunk of chunks) {
|
|
20
|
+
const lines = chunk.split("\n");
|
|
21
|
+
for (const line of lines) {
|
|
22
|
+
if (line.startsWith("data: ")) {
|
|
23
|
+
try {
|
|
24
|
+
const data = JSON.parse(line.slice(6));
|
|
25
|
+
if (validator) {
|
|
26
|
+
if (validator(data)) {
|
|
27
|
+
yield data;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
console.warn("[SSE Parser] Data failed validation:", data);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
yield data;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
console.error("[SSE Parser] Failed to parse SSE data:", line, e);
|
|
39
|
+
throw e;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
reader.releaseLock();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
11
51
|
class ApiError extends Error {
|
|
12
52
|
constructor(message, status, options) {
|
|
13
53
|
super(message);
|
|
@@ -136,12 +176,8 @@ class WidgetApiClient {
|
|
|
136
176
|
const result = await response.json();
|
|
137
177
|
return result.file;
|
|
138
178
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
* Returns ConversationMessage[] array
|
|
142
|
-
*/
|
|
143
|
-
async sendMessage(conversationId, message, fileIds) {
|
|
144
|
-
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/chat`, {
|
|
179
|
+
async *sendAgentMessageStream(conversationId, message, fileIds) {
|
|
180
|
+
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/agent`, {
|
|
145
181
|
method: 'POST',
|
|
146
182
|
headers: {
|
|
147
183
|
'Content-Type': 'application/json',
|
|
@@ -154,167 +190,36 @@ class WidgetApiClient {
|
|
|
154
190
|
}),
|
|
155
191
|
});
|
|
156
192
|
if (!response.ok) {
|
|
157
|
-
throw await buildApiError(response, 'Failed to send message');
|
|
158
|
-
}
|
|
159
|
-
return response.json();
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* Send a message to agent (with actions support)
|
|
163
|
-
*/
|
|
164
|
-
async sendAgentMessage(conversationId, message, fileIds) {
|
|
165
|
-
try {
|
|
166
|
-
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/agent`, {
|
|
167
|
-
method: 'POST',
|
|
168
|
-
headers: {
|
|
169
|
-
'Content-Type': 'application/json',
|
|
170
|
-
},
|
|
171
|
-
body: JSON.stringify({
|
|
172
|
-
conversationId: conversationId,
|
|
173
|
-
message,
|
|
174
|
-
fileIds,
|
|
175
|
-
timeZone: this.getTimeZone(),
|
|
176
|
-
}),
|
|
177
|
-
});
|
|
178
|
-
if (!response.ok) {
|
|
179
|
-
throw await buildApiError(response, `Agent request failed with status ${response.status}`);
|
|
180
|
-
}
|
|
181
|
-
const data = await response.json();
|
|
182
|
-
// Check if response indicates an error
|
|
183
|
-
if (data.type === 'error') {
|
|
184
|
-
throw new Error(data.message || 'Agent encountered an error');
|
|
185
|
-
}
|
|
186
|
-
return data;
|
|
187
|
-
}
|
|
188
|
-
catch (error) {
|
|
189
|
-
// Enhance error messages
|
|
190
|
-
if (error instanceof TypeError && error.message.includes('fetch')) {
|
|
191
|
-
throw new ApiError('Network error: Unable to reach the server', 0);
|
|
192
|
-
}
|
|
193
|
-
throw error;
|
|
193
|
+
throw await buildApiError(response, 'Failed to send agent message');
|
|
194
194
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
* Stream chat message responses
|
|
198
|
-
*/
|
|
199
|
-
async *sendMessageStream(conversationId, message, fileIds) {
|
|
200
|
-
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/chat`, {
|
|
201
|
-
method: 'POST',
|
|
202
|
-
headers: {
|
|
203
|
-
'Content-Type': 'application/json',
|
|
204
|
-
},
|
|
205
|
-
body: JSON.stringify({
|
|
206
|
-
conversationId: conversationId,
|
|
207
|
-
message,
|
|
208
|
-
fileIds,
|
|
209
|
-
timeZone: this.getTimeZone(),
|
|
210
|
-
}),
|
|
195
|
+
yield* parseSSEStream(response, (data) => {
|
|
196
|
+
return typeof data === 'object' && data !== null && 'type' in data;
|
|
211
197
|
});
|
|
212
|
-
if (!response.ok) {
|
|
213
|
-
throw await buildApiError(response, 'Failed to send message');
|
|
214
|
-
}
|
|
215
|
-
if (!response.body) {
|
|
216
|
-
throw new Error('Response body is null');
|
|
217
|
-
}
|
|
218
|
-
const reader = response.body.getReader();
|
|
219
|
-
const decoder = new TextDecoder();
|
|
220
|
-
let buffer = '';
|
|
221
|
-
try {
|
|
222
|
-
while (true) {
|
|
223
|
-
const { done, value } = await reader.read();
|
|
224
|
-
if (done)
|
|
225
|
-
break;
|
|
226
|
-
buffer += decoder.decode(value, { stream: true });
|
|
227
|
-
const lines = buffer.split('\n');
|
|
228
|
-
buffer = lines.pop() || '';
|
|
229
|
-
for (const line of lines) {
|
|
230
|
-
if (line.startsWith('data: ')) {
|
|
231
|
-
try {
|
|
232
|
-
const data = JSON.parse(line.slice(6));
|
|
233
|
-
// Handle error events
|
|
234
|
-
if (data.type === 'error') {
|
|
235
|
-
throw new Error(data.error || 'Stream error');
|
|
236
|
-
}
|
|
237
|
-
// Yield ConversationMessage objects
|
|
238
|
-
if (data.id) {
|
|
239
|
-
yield data;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
catch (e) {
|
|
243
|
-
console.error('[Widget API Client] Failed to parse SSE data:', line, e);
|
|
244
|
-
throw e;
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
finally {
|
|
251
|
-
reader.releaseLock();
|
|
252
|
-
}
|
|
253
198
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
* Handles streaming events from backend and yields ConversationMessage updates
|
|
257
|
-
*/
|
|
258
|
-
async *sendAgentMessageStream(conversationId, message, fileIds) {
|
|
259
|
-
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/agent`, {
|
|
199
|
+
async *continueAgentMessageStream(conversationId, toolCallId, state) {
|
|
200
|
+
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/agent/continue`, {
|
|
260
201
|
method: 'POST',
|
|
261
202
|
headers: {
|
|
262
203
|
'Content-Type': 'application/json',
|
|
263
204
|
},
|
|
264
205
|
body: JSON.stringify({
|
|
265
206
|
conversationId: conversationId,
|
|
266
|
-
|
|
267
|
-
|
|
207
|
+
toolCallId,
|
|
208
|
+
state,
|
|
268
209
|
timeZone: this.getTimeZone(),
|
|
269
210
|
}),
|
|
270
211
|
});
|
|
271
212
|
if (!response.ok) {
|
|
272
|
-
throw await buildApiError(response, 'Failed to
|
|
273
|
-
}
|
|
274
|
-
if (!response.body) {
|
|
275
|
-
throw new Error('Response body is null');
|
|
276
|
-
}
|
|
277
|
-
const reader = response.body.getReader();
|
|
278
|
-
const decoder = new TextDecoder();
|
|
279
|
-
let buffer = '';
|
|
280
|
-
try {
|
|
281
|
-
while (true) {
|
|
282
|
-
const { done, value } = await reader.read();
|
|
283
|
-
if (done)
|
|
284
|
-
break;
|
|
285
|
-
buffer += decoder.decode(value, { stream: true });
|
|
286
|
-
const lines = buffer.split('\n');
|
|
287
|
-
buffer = lines.pop() || '';
|
|
288
|
-
for (const line of lines) {
|
|
289
|
-
if (line.startsWith('data: ')) {
|
|
290
|
-
try {
|
|
291
|
-
const data = JSON.parse(line.slice(6));
|
|
292
|
-
// Handle error events
|
|
293
|
-
if (data.type === 'error') {
|
|
294
|
-
throw new Error(data.error || 'Stream error');
|
|
295
|
-
}
|
|
296
|
-
// Yield ConversationMessage objects that come from the backend
|
|
297
|
-
// The backend yields full ConversationMessage objects during streaming
|
|
298
|
-
if (data.id) {
|
|
299
|
-
yield data;
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
catch (e) {
|
|
303
|
-
console.error('[Widget API Client] Failed to parse SSE data:', line, e);
|
|
304
|
-
throw e;
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
finally {
|
|
311
|
-
reader.releaseLock();
|
|
213
|
+
throw await buildApiError(response, 'Failed to continue agent');
|
|
312
214
|
}
|
|
215
|
+
yield* parseSSEStream(response, (data) => {
|
|
216
|
+
return typeof data === 'object' && data !== null && 'type' in data;
|
|
217
|
+
});
|
|
313
218
|
}
|
|
314
219
|
/**
|
|
315
220
|
* Submit feedback for a message
|
|
316
221
|
*/
|
|
317
|
-
async submitFeedback(sessionId, messageId, feedback) {
|
|
222
|
+
async submitFeedback(sessionId, messageId, feedback, meta) {
|
|
318
223
|
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/feedback`, {
|
|
319
224
|
method: 'POST',
|
|
320
225
|
headers: {
|
|
@@ -324,6 +229,9 @@ class WidgetApiClient {
|
|
|
324
229
|
conversationId: sessionId,
|
|
325
230
|
messageId: messageId,
|
|
326
231
|
feedback,
|
|
232
|
+
messageRole: meta?.role,
|
|
233
|
+
messageContent: meta?.content,
|
|
234
|
+
messageTimestamp: meta?.timestamp,
|
|
327
235
|
}),
|
|
328
236
|
});
|
|
329
237
|
if (!response.ok) {
|
|
@@ -374,6 +282,173 @@ function generateMessageId() {
|
|
|
374
282
|
return `msg_${timestamp}_${randomStr}`;
|
|
375
283
|
}
|
|
376
284
|
|
|
285
|
+
const pendingResolvers = new Map();
|
|
286
|
+
const resumeCallbacks = new Map();
|
|
287
|
+
const frontendActionHandlers = {};
|
|
288
|
+
const actionRenderers = {};
|
|
289
|
+
function getFrontendActionHandler(implementation) {
|
|
290
|
+
return frontendActionHandlers[implementation];
|
|
291
|
+
}
|
|
292
|
+
function getActionRenderer(implementation) {
|
|
293
|
+
return actionRenderers[implementation];
|
|
294
|
+
}
|
|
295
|
+
function getActionPrompt(implementation) {
|
|
296
|
+
if (implementation === "google-calendar-appointment") {
|
|
297
|
+
return "Select a date to continue.";
|
|
298
|
+
}
|
|
299
|
+
return "Action input required.";
|
|
300
|
+
}
|
|
301
|
+
function waitForActionState(toolCallId) {
|
|
302
|
+
return new Promise((resolve) => {
|
|
303
|
+
pendingResolvers.set(toolCallId, resolve);
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
function resolveActionState(toolCallId, state) {
|
|
307
|
+
const resolver = pendingResolvers.get(toolCallId);
|
|
308
|
+
if (resolver) {
|
|
309
|
+
pendingResolvers.delete(toolCallId);
|
|
310
|
+
resolver(state);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
// If no active resolver, check for a resume callback (for page reload scenario)
|
|
314
|
+
const resumeCallback = resumeCallbacks.get(toolCallId);
|
|
315
|
+
if (resumeCallback) {
|
|
316
|
+
resumeCallback(state).catch((error) => {
|
|
317
|
+
console.error("[Action] Failed to resume action:", error);
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
function registerActionResumeCallback(toolCallId, callback) {
|
|
322
|
+
resumeCallbacks.set(toolCallId, callback);
|
|
323
|
+
}
|
|
324
|
+
function unregisterActionResumeCallback(toolCallId) {
|
|
325
|
+
resumeCallbacks.delete(toolCallId);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function groupSlotsByDate(slots) {
|
|
329
|
+
const grouped = new Map();
|
|
330
|
+
for (const slot of slots) {
|
|
331
|
+
if (!slot || typeof slot !== 'object' || !slot.startTime || typeof slot.startTime !== 'string') {
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
const date = slot.startTime.slice(0, 10);
|
|
335
|
+
if (!grouped.has(date)) {
|
|
336
|
+
grouped.set(date, []);
|
|
337
|
+
}
|
|
338
|
+
grouped.get(date).push(slot);
|
|
339
|
+
}
|
|
340
|
+
return grouped;
|
|
341
|
+
}
|
|
342
|
+
function formatDate(dateStr) {
|
|
343
|
+
try {
|
|
344
|
+
const date = new Date(dateStr);
|
|
345
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
346
|
+
weekday: "short",
|
|
347
|
+
month: "short",
|
|
348
|
+
day: "numeric",
|
|
349
|
+
}).format(date);
|
|
350
|
+
}
|
|
351
|
+
catch {
|
|
352
|
+
return dateStr;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
function GoogleCalendarAppointmentCard({ message }) {
|
|
356
|
+
const action = message.action;
|
|
357
|
+
if (!action || action.implementation !== "google-calendar-appointment") {
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
const rawSlots = action.state.availableSlots;
|
|
361
|
+
const availableSlots = Array.isArray(rawSlots)
|
|
362
|
+
? rawSlots.filter((slot) => slot !== null &&
|
|
363
|
+
slot !== undefined &&
|
|
364
|
+
typeof slot === "object" &&
|
|
365
|
+
"startTime" in slot &&
|
|
366
|
+
"endTime" in slot &&
|
|
367
|
+
typeof slot.startTime === "string" &&
|
|
368
|
+
typeof slot.endTime === "string")
|
|
369
|
+
: [];
|
|
370
|
+
const allowTopic = action.state.allowTopic !== false;
|
|
371
|
+
const isBooked = action.state.status === "booked";
|
|
372
|
+
const slotsByDate = groupSlotsByDate(availableSlots);
|
|
373
|
+
const dates = Array.from(slotsByDate.keys()).sort();
|
|
374
|
+
const [selectedDate, setSelectedDate] = useState(dates[0] ?? "");
|
|
375
|
+
const [selectedSlot, setSelectedSlot] = useState(null);
|
|
376
|
+
const [topic, setTopic] = useState("");
|
|
377
|
+
const [error, setError] = useState(null);
|
|
378
|
+
const slotsForSelectedDate = selectedDate ? slotsByDate.get(selectedDate) ?? [] : [];
|
|
379
|
+
const onConfirm = () => {
|
|
380
|
+
if (!selectedSlot) {
|
|
381
|
+
setError("Please select a time slot.");
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
if (allowTopic && !topic.trim()) {
|
|
385
|
+
setError("Please enter a topic for the meeting.");
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
setError(null);
|
|
389
|
+
resolveActionState(action.toolCallId, {
|
|
390
|
+
...action.state,
|
|
391
|
+
selectedSlot: {
|
|
392
|
+
startTime: selectedSlot.startTime,
|
|
393
|
+
endTime: selectedSlot.endTime,
|
|
394
|
+
},
|
|
395
|
+
topic: allowTopic ? topic.trim() : null,
|
|
396
|
+
});
|
|
397
|
+
};
|
|
398
|
+
if (isBooked) {
|
|
399
|
+
const rawBookedSlot = action.state.selectedSlot;
|
|
400
|
+
const bookedSlot = rawBookedSlot &&
|
|
401
|
+
typeof rawBookedSlot === "object" &&
|
|
402
|
+
"startTime" in rawBookedSlot &&
|
|
403
|
+
"endTime" in rawBookedSlot
|
|
404
|
+
? rawBookedSlot
|
|
405
|
+
: null;
|
|
406
|
+
const bookedTopic = typeof action.state.topic === "string" ? action.state.topic : null;
|
|
407
|
+
const eventLink = typeof action.state.bookedEventLink === "string" ? action.state.bookedEventLink : null;
|
|
408
|
+
return (jsxs("div", { className: "ai-chat-action-card ai-chat-action-booked", children: [jsxs("div", { className: "ai-chat-action-header", children: [jsx("svg", { className: "ai-chat-action-icon-success", viewBox: "0 0 20 20", fill: "currentColor", children: jsx("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z", clipRule: "evenodd" }) }), "Appointment Confirmed"] }), jsxs("div", { className: "ai-chat-action-body", children: [bookedTopic && (jsxs("div", { className: "ai-chat-action-detail", children: [jsx("span", { className: "ai-chat-action-label", children: "Topic:" }), jsx("span", { className: "ai-chat-action-value", children: bookedTopic })] })), bookedSlot && bookedSlot.startTime && (jsxs("div", { className: "ai-chat-action-detail", children: [jsx("span", { className: "ai-chat-action-label", children: "Time:" }), jsx("span", { className: "ai-chat-action-value", children: bookedSlot.displayTime || new Date(bookedSlot.startTime).toLocaleString() })] })), eventLink && (jsx("a", { href: eventLink, target: "_blank", rel: "noopener noreferrer", className: "ai-chat-action-link", children: "View in Google Calendar \u2192" }))] })] }));
|
|
409
|
+
}
|
|
410
|
+
return (jsxs("div", { className: "ai-chat-action-card", children: [jsxs("div", { className: "ai-chat-action-header", children: [jsx("svg", { className: "ai-chat-action-icon", viewBox: "0 0 20 20", fill: "currentColor", children: jsx("path", { fillRule: "evenodd", d: "M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z", clipRule: "evenodd" }) }), "Schedule an Appointment"] }), jsxs("div", { className: "ai-chat-action-body", children: [allowTopic && (jsxs("div", { className: "ai-chat-action-field", children: [jsx("label", { htmlFor: `topic-${action.toolCallId}`, className: "ai-chat-action-label", children: "Meeting Topic" }), jsx("input", { id: `topic-${action.toolCallId}`, type: "text", className: "ai-chat-action-input", placeholder: "e.g., Product Demo", value: topic, onChange: (e) => setTopic(e.target.value) })] })), jsxs("div", { className: "ai-chat-action-field", children: [jsx("label", { className: "ai-chat-action-label", children: "Select Date" }), jsx("div", { className: "ai-chat-action-date-grid", children: dates.slice(0, 7).map((date) => (jsx("button", { type: "button", className: `ai-chat-action-date-btn ${selectedDate === date ? "active" : ""}`, onClick: () => {
|
|
411
|
+
setSelectedDate(date);
|
|
412
|
+
setSelectedSlot(null);
|
|
413
|
+
}, children: formatDate(date) }, date))) })] }), selectedDate && slotsForSelectedDate.length > 0 && (jsxs("div", { className: "ai-chat-action-field", children: [jsx("label", { className: "ai-chat-action-label", children: "Select Time" }), jsx("div", { className: "ai-chat-action-time-grid", children: slotsForSelectedDate.map((slot) => (jsx("button", { type: "button", className: `ai-chat-action-time-btn ${selectedSlot?.startTime === slot.startTime ? "active" : ""}`, onClick: () => setSelectedSlot(slot), children: slot.displayTime || new Date(slot.startTime).toLocaleTimeString([], { hour: "numeric", minute: "2-digit" }) }, slot.startTime))) })] })), error && jsx("div", { className: "ai-chat-action-error", children: error }), jsx("button", { className: "ai-chat-action-button", type: "button", onClick: onConfirm, disabled: !selectedSlot, children: "Confirm Appointment" }), availableSlots.length === 0 && (jsx("div", { className: "ai-chat-action-hint", children: "No available time slots found." }))] })] }));
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
frontendActionHandlers["google-calendar-appointment"] = async (_input, _state, context) => {
|
|
417
|
+
return waitForActionState(context.toolCallId);
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
function styleInject(css, ref) {
|
|
421
|
+
if ( ref === void 0 ) ref = {};
|
|
422
|
+
var insertAt = ref.insertAt;
|
|
423
|
+
|
|
424
|
+
if (!css || typeof document === 'undefined') { return; }
|
|
425
|
+
|
|
426
|
+
var head = document.head || document.getElementsByTagName('head')[0];
|
|
427
|
+
var style = document.createElement('style');
|
|
428
|
+
style.type = 'text/css';
|
|
429
|
+
|
|
430
|
+
if (insertAt === 'top') {
|
|
431
|
+
if (head.firstChild) {
|
|
432
|
+
head.insertBefore(style, head.firstChild);
|
|
433
|
+
} else {
|
|
434
|
+
head.appendChild(style);
|
|
435
|
+
}
|
|
436
|
+
} else {
|
|
437
|
+
head.appendChild(style);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (style.styleSheet) {
|
|
441
|
+
style.styleSheet.cssText = css;
|
|
442
|
+
} else {
|
|
443
|
+
style.appendChild(document.createTextNode(css));
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
var css_248z$1 = ".ai-chat-action-card{background:linear-gradient(135deg,#fff,#f9fafb);border:1px solid #e5e7eb;border-radius:12px;box-shadow:0 2px 8px rgba(0,0,0,.06);margin-top:12px;padding:20px;transition:all .2s ease}.ai-chat-action-card:hover{box-shadow:0 4px 12px rgba(0,0,0,.1)}.ai-chat-action-booked{background:linear-gradient(135deg,#ecfdf5,#d1fae5);border-color:#10b981}.ai-chat-action-header{align-items:center;color:#111827;display:flex;font-size:16px;font-weight:600;gap:8px;margin-bottom:16px}.ai-chat-action-icon{color:#6366f1;height:20px;width:20px}.ai-chat-action-icon-success{color:#10b981;height:24px;width:24px}.ai-chat-action-body{display:flex;flex-direction:column;gap:16px}.ai-chat-action-field{display:flex;flex-direction:column;gap:8px}.ai-chat-action-label{color:#374151;font-size:13px;font-weight:500}.ai-chat-action-input{background:#fff;border:1px solid #d1d5db;border-radius:8px;font-size:14px;padding:10px 12px;transition:all .2s ease}.ai-chat-action-input:focus{border-color:#6366f1;box-shadow:0 0 0 3px rgba(99,102,241,.1);outline:none}.ai-chat-action-date-grid{display:grid;gap:8px;grid-template-columns:repeat(auto-fill,minmax(110px,1fr))}.ai-chat-action-date-btn{background:#fff;border:1px solid #d1d5db;border-radius:8px;color:#374151;cursor:pointer;font-size:13px;font-weight:500;padding:10px 8px;transition:all .2s ease}.ai-chat-action-date-btn:hover{background:#f3f4f6;border-color:#9ca3af}.ai-chat-action-date-btn.active{background:#6366f1;border-color:#6366f1;color:#fff}.ai-chat-action-time-grid{display:grid;gap:8px;grid-template-columns:repeat(auto-fill,minmax(100px,1fr))}.ai-chat-action-time-btn{background:#fff;border:1px solid #d1d5db;border-radius:8px;color:#374151;cursor:pointer;font-size:13px;font-weight:500;padding:10px 8px;transition:all .2s ease}.ai-chat-action-time-btn:hover{background:#f3f4f6;border-color:#9ca3af}.ai-chat-action-time-btn.active{background:#6366f1;border-color:#6366f1;color:#fff}.ai-chat-action-button{background:linear-gradient(135deg,#6366f1,#4f46e5);border:none;border-radius:8px;box-shadow:0 2px 4px rgba(99,102,241,.2);color:#fff;cursor:pointer;font-size:14px;font-weight:600;padding:12px 20px;transition:all .2s ease}.ai-chat-action-button:hover:not(:disabled){background:linear-gradient(135deg,#4f46e5,#4338ca);box-shadow:0 4px 8px rgba(99,102,241,.3);transform:translateY(-1px)}.ai-chat-action-button:disabled{cursor:not-allowed;opacity:.5;transform:none}.ai-chat-action-error{background:#fef2f2;border:1px solid #fecaca;border-radius:8px;color:#dc2626;font-size:13px;font-weight:500;padding:10px 12px}.ai-chat-action-hint{color:#6b7280;font-size:12px;font-style:italic}.ai-chat-action-detail{background:#fff;border:1px solid #e5e7eb;border-radius:8px;display:flex;flex-direction:column;gap:4px;padding:12px}.ai-chat-action-detail .ai-chat-action-label{color:#6b7280;font-size:12px;letter-spacing:.5px;text-transform:uppercase}.ai-chat-action-detail .ai-chat-action-value{color:#111827;font-size:14px;font-weight:600}.ai-chat-action-link{align-items:center;background:#fff;border:1px solid #10b981;border-radius:8px;color:#10b981;display:inline-flex;font-size:14px;font-weight:600;gap:4px;padding:10px 16px;text-decoration:none;transition:all .2s ease}.ai-chat-action-link:hover{background:#10b981;color:#fff;transform:translateX(2px)}";
|
|
448
|
+
styleInject(css_248z$1);
|
|
449
|
+
|
|
450
|
+
actionRenderers["google-calendar-appointment"] = (message) => (jsx(GoogleCalendarAppointmentCard, { message: message }));
|
|
451
|
+
|
|
377
452
|
/**
|
|
378
453
|
* Local Storage Utilities
|
|
379
454
|
* Handles conversation persistence in browser localStorage
|
|
@@ -435,9 +510,12 @@ function setWidgetStorage(widgetId, storage) {
|
|
|
435
510
|
* Get preview text from messages
|
|
436
511
|
*/
|
|
437
512
|
function getConversationPreview(messages) {
|
|
438
|
-
const firstUserMessage = messages.find(m => m.message.
|
|
513
|
+
const firstUserMessage = messages.find(m => m.message.role === "user");
|
|
439
514
|
if (firstUserMessage) {
|
|
440
|
-
|
|
515
|
+
const content = typeof firstUserMessage.message.content === "string"
|
|
516
|
+
? firstUserMessage.message.content
|
|
517
|
+
: "";
|
|
518
|
+
return content.slice(0, 100);
|
|
441
519
|
}
|
|
442
520
|
return 'New conversation';
|
|
443
521
|
}
|
|
@@ -592,6 +670,473 @@ function isStorageAvailable() {
|
|
|
592
670
|
* useChat Hook
|
|
593
671
|
* Main state management for chat functionality
|
|
594
672
|
*/
|
|
673
|
+
function hydrateToolNames(messages) {
|
|
674
|
+
const toolCallNameById = new Map();
|
|
675
|
+
for (const entry of messages) {
|
|
676
|
+
if (entry.message.role !== "assistant") {
|
|
677
|
+
continue;
|
|
678
|
+
}
|
|
679
|
+
const toolCalls = entry.message.tool_calls;
|
|
680
|
+
if (!Array.isArray(toolCalls)) {
|
|
681
|
+
continue;
|
|
682
|
+
}
|
|
683
|
+
for (const call of toolCalls) {
|
|
684
|
+
const callId = call?.id;
|
|
685
|
+
const callName = call?.function?.name;
|
|
686
|
+
if (callId && callName) {
|
|
687
|
+
toolCallNameById.set(callId, callName);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
if (toolCallNameById.size === 0) {
|
|
692
|
+
return messages;
|
|
693
|
+
}
|
|
694
|
+
return messages.map((entry) => {
|
|
695
|
+
if (entry.message.role !== "tool") {
|
|
696
|
+
return entry;
|
|
697
|
+
}
|
|
698
|
+
const toolCallId = entry.message.tool_call_id;
|
|
699
|
+
const resolvedName = toolCallNameById.get(toolCallId);
|
|
700
|
+
if (!resolvedName) {
|
|
701
|
+
return entry;
|
|
702
|
+
}
|
|
703
|
+
const currentName = entry.message.name;
|
|
704
|
+
const nextName = currentName && currentName.trim().length > 0 ? currentName : resolvedName;
|
|
705
|
+
const nextToolExecuting = entry.toolExecuting || resolvedName;
|
|
706
|
+
if (nextName === currentName && nextToolExecuting === entry.toolExecuting) {
|
|
707
|
+
return entry;
|
|
708
|
+
}
|
|
709
|
+
return {
|
|
710
|
+
...entry,
|
|
711
|
+
message: { ...entry.message, name: nextName },
|
|
712
|
+
toolExecuting: nextToolExecuting,
|
|
713
|
+
};
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
function hydrateActionContent(messages) {
|
|
717
|
+
return messages.map((entry) => {
|
|
718
|
+
if (entry.message.role !== "assistant" || !entry.action) {
|
|
719
|
+
return entry;
|
|
720
|
+
}
|
|
721
|
+
const content = typeof entry.message.content === "string" ? entry.message.content : "";
|
|
722
|
+
if (content.trim().length > 0) {
|
|
723
|
+
return entry;
|
|
724
|
+
}
|
|
725
|
+
return {
|
|
726
|
+
...entry,
|
|
727
|
+
message: { ...entry.message, content: getActionPrompt(entry.action.implementation) },
|
|
728
|
+
};
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
function hydrateMessages(messages) {
|
|
732
|
+
return hydrateActionContent(hydrateToolNames(messages));
|
|
733
|
+
}
|
|
734
|
+
function setupActionResumeCallbacks(messages, client, conversationId, setState, onMessageUpdate) {
|
|
735
|
+
// Find all incomplete actions and register resume callbacks
|
|
736
|
+
for (const message of messages) {
|
|
737
|
+
if (message.action && !message.action.done) {
|
|
738
|
+
const toolCallId = message.action.toolCallId;
|
|
739
|
+
const toolName = message.message.name || message.toolExecuting || "tool";
|
|
740
|
+
registerActionResumeCallback(toolCallId, async (newState) => {
|
|
741
|
+
// When user interacts with the action after reload, continue the stream
|
|
742
|
+
try {
|
|
743
|
+
// Update the action message with the new state
|
|
744
|
+
setState(prev => ({
|
|
745
|
+
...prev,
|
|
746
|
+
messages: prev.messages.map(m => m.action?.toolCallId === toolCallId
|
|
747
|
+
? {
|
|
748
|
+
...m,
|
|
749
|
+
action: m.action ? { ...m.action, state: newState } : undefined,
|
|
750
|
+
}
|
|
751
|
+
: m),
|
|
752
|
+
isTyping: true,
|
|
753
|
+
}));
|
|
754
|
+
const streamState = createStreamState();
|
|
755
|
+
// Continue the agent stream with the new state
|
|
756
|
+
for await (const event of client.continueAgentMessageStream(conversationId, toolCallId, newState)) {
|
|
757
|
+
if (event.type === "done") {
|
|
758
|
+
finalizeToolMessage(streamState, setState, toolCallId, toolName);
|
|
759
|
+
streamState.sources = event.sources;
|
|
760
|
+
streamState.toolCallToActionId = event.tool_call_to_action_id;
|
|
761
|
+
finalizeStreamMessages(setState, streamState.newMessageIds, event.sources, event.tool_call_to_action_id);
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
if (event.type === "error") {
|
|
765
|
+
const errorMessage = {
|
|
766
|
+
id: generateMessageId(),
|
|
767
|
+
message: {
|
|
768
|
+
role: "assistant",
|
|
769
|
+
content: `Error: ${event.error}`,
|
|
770
|
+
},
|
|
771
|
+
timestamp: new Date().toISOString(),
|
|
772
|
+
sources: [],
|
|
773
|
+
isError: true,
|
|
774
|
+
};
|
|
775
|
+
upsertMessage(setState, errorMessage, false);
|
|
776
|
+
setState(prev => ({ ...prev, isTyping: false }));
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
handleStreamEvent(event, streamState, onMessageUpdate, setState);
|
|
780
|
+
}
|
|
781
|
+
setState(prev => ({ ...prev, isTyping: false }));
|
|
782
|
+
}
|
|
783
|
+
catch (error) {
|
|
784
|
+
console.error("[Action Resume] Failed to continue stream:", error);
|
|
785
|
+
setState(prev => ({ ...prev, isTyping: false, error: error instanceof Error ? error.message : "Failed to resume action" }));
|
|
786
|
+
throw error;
|
|
787
|
+
}
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
function createStreamState() {
|
|
793
|
+
return {
|
|
794
|
+
currentContent: "",
|
|
795
|
+
currentMessageId: generateMessageId(),
|
|
796
|
+
activeToolCallCount: 0,
|
|
797
|
+
newMessageIds: new Set(),
|
|
798
|
+
sources: [],
|
|
799
|
+
toolCallToActionId: {},
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
function upsertMessage(setState, message, isTyping) {
|
|
803
|
+
setState(prev => {
|
|
804
|
+
const existingIndex = prev.messages.findIndex(m => m.id === message.id);
|
|
805
|
+
if (existingIndex >= 0) {
|
|
806
|
+
const newMessages = [...prev.messages];
|
|
807
|
+
newMessages[existingIndex] = message;
|
|
808
|
+
return { ...prev, messages: newMessages, isTyping, isLoading: false };
|
|
809
|
+
}
|
|
810
|
+
return {
|
|
811
|
+
...prev,
|
|
812
|
+
messages: [...prev.messages, message],
|
|
813
|
+
isTyping,
|
|
814
|
+
isLoading: false,
|
|
815
|
+
};
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
function finalizeStreamMessages(setState, messageIds, sources, toolCallToActionId) {
|
|
819
|
+
setState(prev => ({
|
|
820
|
+
...prev,
|
|
821
|
+
messages: prev.messages.map(msg => (messageIds.has(msg.id)
|
|
822
|
+
? { ...msg, sources, toolCallToActionId }
|
|
823
|
+
: msg)),
|
|
824
|
+
isTyping: false,
|
|
825
|
+
}));
|
|
826
|
+
}
|
|
827
|
+
function handleContentEvent(event, streamState, onMessageUpdate, setState) {
|
|
828
|
+
streamState.currentContent += event.content;
|
|
829
|
+
const assistantMessage = {
|
|
830
|
+
id: streamState.currentMessageId,
|
|
831
|
+
message: { role: "assistant", content: streamState.currentContent },
|
|
832
|
+
timestamp: new Date().toISOString(),
|
|
833
|
+
sources: streamState.sources,
|
|
834
|
+
isStreaming: true,
|
|
835
|
+
};
|
|
836
|
+
streamState.newMessageIds.add(assistantMessage.id);
|
|
837
|
+
onMessageUpdate(assistantMessage);
|
|
838
|
+
const hasContent = assistantMessage.message.content?.trim().length || 0 > 0;
|
|
839
|
+
const isToolExecuting = streamState.activeToolCallCount > 0;
|
|
840
|
+
const isTyping = (!hasContent && assistantMessage.isStreaming) || isToolExecuting;
|
|
841
|
+
upsertMessage(setState, assistantMessage, isTyping);
|
|
842
|
+
}
|
|
843
|
+
function handleToolStartEvent(event, streamState, onMessageUpdate, setState) {
|
|
844
|
+
if (streamState.currentContent.trim()) {
|
|
845
|
+
const finalAssistant = {
|
|
846
|
+
id: streamState.currentMessageId,
|
|
847
|
+
message: { role: "assistant", content: streamState.currentContent },
|
|
848
|
+
timestamp: new Date().toISOString(),
|
|
849
|
+
sources: streamState.sources,
|
|
850
|
+
isStreaming: false,
|
|
851
|
+
};
|
|
852
|
+
streamState.newMessageIds.add(finalAssistant.id);
|
|
853
|
+
onMessageUpdate(finalAssistant);
|
|
854
|
+
upsertMessage(setState, finalAssistant, true);
|
|
855
|
+
streamState.currentContent = "";
|
|
856
|
+
streamState.currentMessageId = generateMessageId();
|
|
857
|
+
}
|
|
858
|
+
const toolMessageId = event.tool_call_id;
|
|
859
|
+
streamState.activeToolCallCount += 1;
|
|
860
|
+
streamState.newMessageIds.add(toolMessageId);
|
|
861
|
+
const toolMessage = {
|
|
862
|
+
id: toolMessageId,
|
|
863
|
+
sources: [],
|
|
864
|
+
message: { role: "tool", content: "", tool_call_id: event.tool_call_id, name: event.tool_name },
|
|
865
|
+
timestamp: new Date().toISOString(),
|
|
866
|
+
isStreaming: true,
|
|
867
|
+
toolExecuting: event.tool_name,
|
|
868
|
+
};
|
|
869
|
+
upsertMessage(setState, toolMessage, true);
|
|
870
|
+
}
|
|
871
|
+
function handleToolEndEvent(event, streamState, _onMessageUpdate, setState) {
|
|
872
|
+
// Update state and mark action as done in a single setState call
|
|
873
|
+
setState(prev => {
|
|
874
|
+
const messages = prev.messages.map((msg) => {
|
|
875
|
+
const matchesToolCall = msg.message.role === "tool" && msg.message.tool_call_id === event.tool_call_id;
|
|
876
|
+
if (!matchesToolCall) {
|
|
877
|
+
return msg;
|
|
878
|
+
}
|
|
879
|
+
const existingName = msg.message.name || event.tool_name;
|
|
880
|
+
return {
|
|
881
|
+
...msg,
|
|
882
|
+
message: {
|
|
883
|
+
role: "tool",
|
|
884
|
+
content: event.state ? JSON.stringify(event.state) : (typeof msg.message.content === "string" ? msg.message.content : ""),
|
|
885
|
+
tool_call_id: event.tool_call_id,
|
|
886
|
+
name: existingName,
|
|
887
|
+
},
|
|
888
|
+
isStreaming: false,
|
|
889
|
+
toolExecuting: existingName,
|
|
890
|
+
action: msg.action ? {
|
|
891
|
+
...msg.action,
|
|
892
|
+
state: event.state || msg.action.state,
|
|
893
|
+
done: true, // Mark action as completed
|
|
894
|
+
} : undefined,
|
|
895
|
+
};
|
|
896
|
+
});
|
|
897
|
+
return { ...prev, messages, isTyping: false, isLoading: false };
|
|
898
|
+
});
|
|
899
|
+
streamState.activeToolCallCount = Math.max(0, streamState.activeToolCallCount - 1);
|
|
900
|
+
}
|
|
901
|
+
function handleToolErrorEvent(event, streamState, _onMessageUpdate, setState) {
|
|
902
|
+
setState(prev => {
|
|
903
|
+
const messages = prev.messages.map((entry) => {
|
|
904
|
+
const matchesToolCall = entry.message.role === "tool" && entry.message.tool_call_id === event.tool_call_id;
|
|
905
|
+
if (!matchesToolCall) {
|
|
906
|
+
return entry;
|
|
907
|
+
}
|
|
908
|
+
const name = entry.message.name || "tool";
|
|
909
|
+
const toolMessage = {
|
|
910
|
+
...entry,
|
|
911
|
+
message: {
|
|
912
|
+
role: "tool",
|
|
913
|
+
content: event.error || "Tool execution failed",
|
|
914
|
+
tool_call_id: event.tool_call_id,
|
|
915
|
+
name,
|
|
916
|
+
},
|
|
917
|
+
timestamp: new Date().toISOString(),
|
|
918
|
+
isStreaming: false,
|
|
919
|
+
isError: true,
|
|
920
|
+
toolExecuting: name,
|
|
921
|
+
};
|
|
922
|
+
return toolMessage;
|
|
923
|
+
});
|
|
924
|
+
return { ...prev, messages, isTyping: false, isLoading: false };
|
|
925
|
+
});
|
|
926
|
+
streamState.activeToolCallCount = Math.max(0, streamState.activeToolCallCount - 1);
|
|
927
|
+
}
|
|
928
|
+
function finalizeToolMessage(streamState, setState, toolCallId, toolName) {
|
|
929
|
+
setState(prev => {
|
|
930
|
+
const messages = prev.messages.map((entry) => {
|
|
931
|
+
const matchesToolCall = entry.message.role === "tool" && entry.message.tool_call_id === toolCallId;
|
|
932
|
+
if (!matchesToolCall) {
|
|
933
|
+
return entry;
|
|
934
|
+
}
|
|
935
|
+
const existingName = entry.message.name || toolName;
|
|
936
|
+
return {
|
|
937
|
+
...entry,
|
|
938
|
+
message: {
|
|
939
|
+
role: "tool",
|
|
940
|
+
content: typeof entry.message.content === "string" ? entry.message.content : "",
|
|
941
|
+
tool_call_id: toolCallId,
|
|
942
|
+
name: existingName,
|
|
943
|
+
},
|
|
944
|
+
isStreaming: false,
|
|
945
|
+
toolExecuting: existingName,
|
|
946
|
+
action: entry.action ? {
|
|
947
|
+
...entry.action,
|
|
948
|
+
done: true, // Mark action as completed
|
|
949
|
+
} : undefined,
|
|
950
|
+
};
|
|
951
|
+
});
|
|
952
|
+
return { ...prev, messages, isTyping: false, isLoading: false };
|
|
953
|
+
});
|
|
954
|
+
streamState.activeToolCallCount = Math.max(0, streamState.activeToolCallCount - 1);
|
|
955
|
+
}
|
|
956
|
+
function handleDoneEvent(event, streamState, _onMessageUpdate, setState) {
|
|
957
|
+
streamState.sources = event.sources;
|
|
958
|
+
streamState.toolCallToActionId = event.tool_call_to_action_id;
|
|
959
|
+
finalizeStreamMessages(setState, streamState.newMessageIds, event.sources, event.tool_call_to_action_id);
|
|
960
|
+
}
|
|
961
|
+
function handleHaltEvent(event, _streamState, onMessageUpdate, setState) {
|
|
962
|
+
const toolNames = event.tool_calls.map(call => call.name).join(", ");
|
|
963
|
+
const notice = toolNames
|
|
964
|
+
? `Awaiting external tool results: ${toolNames}.`
|
|
965
|
+
: "Awaiting external tool results.";
|
|
966
|
+
const haltMessage = {
|
|
967
|
+
id: generateMessageId(),
|
|
968
|
+
message: { role: "assistant", content: notice },
|
|
969
|
+
timestamp: new Date().toISOString(),
|
|
970
|
+
sources: [],
|
|
971
|
+
isError: true,
|
|
972
|
+
};
|
|
973
|
+
onMessageUpdate(haltMessage);
|
|
974
|
+
upsertMessage(setState, haltMessage, false);
|
|
975
|
+
setState(prev => ({ ...prev, error: "Awaiting external tool handling." }));
|
|
976
|
+
}
|
|
977
|
+
function handleErrorEvent(event, _streamState, onMessageUpdate, setState) {
|
|
978
|
+
const errorMessage = {
|
|
979
|
+
id: generateMessageId(),
|
|
980
|
+
message: { role: "assistant", content: `⚠️ ${event.error}` },
|
|
981
|
+
timestamp: new Date().toISOString(),
|
|
982
|
+
sources: [],
|
|
983
|
+
isError: true,
|
|
984
|
+
};
|
|
985
|
+
onMessageUpdate(errorMessage);
|
|
986
|
+
upsertMessage(setState, errorMessage, false);
|
|
987
|
+
setState(prev => ({ ...prev, error: event.error }));
|
|
988
|
+
}
|
|
989
|
+
const eventHandlers = {
|
|
990
|
+
content: handleContentEvent,
|
|
991
|
+
tool_start: handleToolStartEvent,
|
|
992
|
+
tool_end: handleToolEndEvent,
|
|
993
|
+
tool_error: handleToolErrorEvent,
|
|
994
|
+
done: handleDoneEvent,
|
|
995
|
+
halt: handleHaltEvent,
|
|
996
|
+
error: handleErrorEvent,
|
|
997
|
+
action_request: () => { },
|
|
998
|
+
};
|
|
999
|
+
function handleStreamEvent(event, streamState, onMessageUpdate, setState) {
|
|
1000
|
+
if (event.type === "tool_start" || event.type === "tool_end" || event.type === "tool_error" || event.type === "action_request" || event.type === "done" || event.type === "halt") {
|
|
1001
|
+
console.log("[Widget] stream event:", {
|
|
1002
|
+
type: event.type,
|
|
1003
|
+
toolCallId: "tool_call_id" in event ? event.tool_call_id : undefined,
|
|
1004
|
+
toolName: "tool_name" in event ? event.tool_name : undefined,
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
const handler = eventHandlers[event.type];
|
|
1008
|
+
if (handler) {
|
|
1009
|
+
handler(event, streamState, onMessageUpdate, setState);
|
|
1010
|
+
}
|
|
1011
|
+
else {
|
|
1012
|
+
console.warn('[Chat] Unknown event type:', event.type);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
async function handleActionLoop(client, initialEvent, streamState, onMessageUpdate, setState, widgetId, conversationId, getMessages) {
|
|
1016
|
+
let pendingEvent = initialEvent;
|
|
1017
|
+
while (pendingEvent) {
|
|
1018
|
+
streamState.toolCallToActionId = pendingEvent.tool_call_to_action_id;
|
|
1019
|
+
const resumeToolCallId = pendingEvent.tool_call_id;
|
|
1020
|
+
const existingToolMessage = getMessages().find((entry) => entry.message.role === "tool" && entry.message.tool_call_id === resumeToolCallId);
|
|
1021
|
+
const toolMessageId = existingToolMessage?.id ?? resumeToolCallId;
|
|
1022
|
+
const toolName = existingToolMessage?.message.name ?? "tool";
|
|
1023
|
+
const toolMessage = {
|
|
1024
|
+
id: toolMessageId,
|
|
1025
|
+
sources: [],
|
|
1026
|
+
message: {
|
|
1027
|
+
role: "tool",
|
|
1028
|
+
content: "",
|
|
1029
|
+
tool_call_id: resumeToolCallId,
|
|
1030
|
+
name: toolName,
|
|
1031
|
+
},
|
|
1032
|
+
timestamp: new Date().toISOString(),
|
|
1033
|
+
isStreaming: true,
|
|
1034
|
+
toolExecuting: toolName,
|
|
1035
|
+
action: {
|
|
1036
|
+
implementation: pendingEvent.implementation,
|
|
1037
|
+
toolCallId: pendingEvent.tool_call_id,
|
|
1038
|
+
actionId: pendingEvent.action_id,
|
|
1039
|
+
input: pendingEvent.input,
|
|
1040
|
+
state: pendingEvent.state,
|
|
1041
|
+
done: false, // Action not yet completed
|
|
1042
|
+
},
|
|
1043
|
+
};
|
|
1044
|
+
if (streamState.activeToolCallCount === 0) {
|
|
1045
|
+
streamState.activeToolCallCount = 1;
|
|
1046
|
+
}
|
|
1047
|
+
onMessageUpdate(toolMessage);
|
|
1048
|
+
upsertMessage(setState, toolMessage, true);
|
|
1049
|
+
const handler = getFrontendActionHandler(pendingEvent.implementation);
|
|
1050
|
+
if (!handler) {
|
|
1051
|
+
const errorMessage = {
|
|
1052
|
+
id: generateMessageId(),
|
|
1053
|
+
message: {
|
|
1054
|
+
role: "assistant",
|
|
1055
|
+
content: `⚠️ No frontend handler for action '${pendingEvent.implementation}'.`,
|
|
1056
|
+
},
|
|
1057
|
+
timestamp: new Date().toISOString(),
|
|
1058
|
+
sources: [],
|
|
1059
|
+
isError: true,
|
|
1060
|
+
};
|
|
1061
|
+
onMessageUpdate(errorMessage);
|
|
1062
|
+
upsertMessage(setState, errorMessage, false);
|
|
1063
|
+
setState(prev => ({ ...prev, error: `Missing frontend handler: ${pendingEvent?.implementation}` }));
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
let nextState;
|
|
1067
|
+
try {
|
|
1068
|
+
nextState = await handler(pendingEvent.input, pendingEvent.state, {
|
|
1069
|
+
widgetId,
|
|
1070
|
+
conversationId,
|
|
1071
|
+
toolCallId: pendingEvent.tool_call_id,
|
|
1072
|
+
actionId: pendingEvent.action_id,
|
|
1073
|
+
implementation: pendingEvent.implementation,
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
1076
|
+
catch (error) {
|
|
1077
|
+
const errorMessage = error instanceof Error ? error.message : "Frontend action failed.";
|
|
1078
|
+
const errorMessageEntry = {
|
|
1079
|
+
id: generateMessageId(),
|
|
1080
|
+
message: { role: "assistant", content: `⚠️ ${errorMessage}` },
|
|
1081
|
+
timestamp: new Date().toISOString(),
|
|
1082
|
+
sources: [],
|
|
1083
|
+
isError: true,
|
|
1084
|
+
};
|
|
1085
|
+
onMessageUpdate(errorMessageEntry);
|
|
1086
|
+
upsertMessage(setState, errorMessageEntry, false);
|
|
1087
|
+
setState(prev => ({ ...prev, error: errorMessage }));
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
pendingEvent = null;
|
|
1091
|
+
const updatedToolMessage = {
|
|
1092
|
+
...toolMessage,
|
|
1093
|
+
action: toolMessage.action
|
|
1094
|
+
? {
|
|
1095
|
+
...toolMessage.action,
|
|
1096
|
+
state: nextState,
|
|
1097
|
+
}
|
|
1098
|
+
: undefined,
|
|
1099
|
+
};
|
|
1100
|
+
upsertMessage(setState, updatedToolMessage, true);
|
|
1101
|
+
let streamEnded = false;
|
|
1102
|
+
for await (const event of client.continueAgentMessageStream(conversationId, resumeToolCallId, nextState)) {
|
|
1103
|
+
if (event.type === "action_request") {
|
|
1104
|
+
pendingEvent = event;
|
|
1105
|
+
break;
|
|
1106
|
+
}
|
|
1107
|
+
if (event.type === "done") {
|
|
1108
|
+
// Don't extract and update state from done event - the state was already
|
|
1109
|
+
// updated by tool_end event or by the user's frontend action.
|
|
1110
|
+
finalizeToolMessage(streamState, setState, resumeToolCallId, toolName);
|
|
1111
|
+
// Handle the done event but skip the tool finalization part since we already did it
|
|
1112
|
+
streamState.sources = event.sources;
|
|
1113
|
+
streamState.toolCallToActionId = event.tool_call_to_action_id;
|
|
1114
|
+
finalizeStreamMessages(setState, streamState.newMessageIds, event.sources, event.tool_call_to_action_id);
|
|
1115
|
+
streamEnded = true;
|
|
1116
|
+
continue; // Skip handleStreamEvent for done events to avoid state conflicts
|
|
1117
|
+
}
|
|
1118
|
+
if (event.type === "error") {
|
|
1119
|
+
const errorMessage = {
|
|
1120
|
+
id: generateMessageId(),
|
|
1121
|
+
message: {
|
|
1122
|
+
role: "assistant",
|
|
1123
|
+
content: `Error occurred during action execution: ${event.error}`,
|
|
1124
|
+
},
|
|
1125
|
+
timestamp: new Date().toISOString(),
|
|
1126
|
+
sources: [],
|
|
1127
|
+
isError: true,
|
|
1128
|
+
};
|
|
1129
|
+
upsertMessage(setState, errorMessage, false);
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
handleStreamEvent(event, streamState, onMessageUpdate, setState);
|
|
1133
|
+
}
|
|
1134
|
+
// If stream ended without a done event (e.g., only tool_end was sent), finalize the tool message
|
|
1135
|
+
if (!streamEnded && !pendingEvent) {
|
|
1136
|
+
finalizeToolMessage(streamState, setState, resumeToolCallId, toolName);
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
595
1140
|
function deriveErrorInfo(error) {
|
|
596
1141
|
if (error instanceof ApiError) {
|
|
597
1142
|
const retryAfterSeconds = typeof error.retryAfterMs === 'number'
|
|
@@ -664,6 +1209,10 @@ function useChat(options) {
|
|
|
664
1209
|
conversationId: '', // Will be set after loading conversation
|
|
665
1210
|
config: null,
|
|
666
1211
|
});
|
|
1212
|
+
const stateRef = useRef(state);
|
|
1213
|
+
useEffect(() => {
|
|
1214
|
+
stateRef.current = state;
|
|
1215
|
+
}, [state]);
|
|
667
1216
|
// Chat history state
|
|
668
1217
|
const [conversations, setConversations] = useState([]);
|
|
669
1218
|
const apiClient = useRef(new WidgetApiClient({ widgetId, apiUrl }));
|
|
@@ -671,18 +1220,12 @@ function useChat(options) {
|
|
|
671
1220
|
useEffect(() => {
|
|
672
1221
|
// Skip initialization in preview mode
|
|
673
1222
|
if (skipInitialization) {
|
|
674
|
-
console.log('[useChat] Skipping initialization (preview mode)');
|
|
675
1223
|
return;
|
|
676
1224
|
}
|
|
677
1225
|
let isMounted = true;
|
|
678
|
-
console.log('[useChat] Effect running, mounting component');
|
|
679
1226
|
const initialize = async () => {
|
|
680
1227
|
try {
|
|
681
|
-
console.log('[useChat] Fetching config...');
|
|
682
1228
|
const config = await apiClient.current.getConfig();
|
|
683
|
-
console.log('[useChat] Config fetched successfully:', {
|
|
684
|
-
hasAppearance: !!config.appearance,
|
|
685
|
-
});
|
|
686
1229
|
const persistConversation = config.settings.persistConversation ?? true;
|
|
687
1230
|
let conversationId = '';
|
|
688
1231
|
let messages = [];
|
|
@@ -693,7 +1236,7 @@ function useChat(options) {
|
|
|
693
1236
|
try {
|
|
694
1237
|
const conversation = await apiClient.current.getOrCreateConversation(storedId);
|
|
695
1238
|
conversationId = conversation.id;
|
|
696
|
-
messages = conversation.messages;
|
|
1239
|
+
messages = hydrateMessages(conversation.messages);
|
|
697
1240
|
}
|
|
698
1241
|
catch (conversationError) {
|
|
699
1242
|
console.warn('Failed to load existing conversation:', conversationError);
|
|
@@ -701,16 +1244,19 @@ function useChat(options) {
|
|
|
701
1244
|
}
|
|
702
1245
|
}
|
|
703
1246
|
if (!isMounted) {
|
|
704
|
-
console.log('[useChat] Component unmounted, skipping state update');
|
|
705
1247
|
return;
|
|
706
1248
|
}
|
|
707
|
-
|
|
1249
|
+
const hydratedMessages = hydrateMessages(messages);
|
|
708
1250
|
setState(prev => ({
|
|
709
1251
|
...prev,
|
|
710
1252
|
config,
|
|
711
1253
|
conversationId,
|
|
712
|
-
messages,
|
|
1254
|
+
messages: hydratedMessages,
|
|
713
1255
|
}));
|
|
1256
|
+
// Setup resume callbacks for incomplete actions
|
|
1257
|
+
if (conversationId) {
|
|
1258
|
+
setupActionResumeCallbacks(hydratedMessages, apiClient.current, conversationId, setState, onMessage ?? (() => { }));
|
|
1259
|
+
}
|
|
714
1260
|
}
|
|
715
1261
|
catch (error) {
|
|
716
1262
|
console.error('[useChat] Error fetching config:', error);
|
|
@@ -725,8 +1271,13 @@ function useChat(options) {
|
|
|
725
1271
|
};
|
|
726
1272
|
initialize();
|
|
727
1273
|
return () => {
|
|
728
|
-
console.log('[useChat] Effect cleanup, unmounting component');
|
|
729
1274
|
isMounted = false;
|
|
1275
|
+
// Cleanup resume callbacks
|
|
1276
|
+
state.messages.forEach(message => {
|
|
1277
|
+
if (message.action?.toolCallId) {
|
|
1278
|
+
unregisterActionResumeCallback(message.action.toolCallId);
|
|
1279
|
+
}
|
|
1280
|
+
});
|
|
730
1281
|
};
|
|
731
1282
|
}, [widgetId, apiUrl, onError]);
|
|
732
1283
|
// Save conversation when messages change
|
|
@@ -739,9 +1290,6 @@ function useChat(options) {
|
|
|
739
1290
|
saveConversation(widgetId, state.conversationId, state.messages);
|
|
740
1291
|
}
|
|
741
1292
|
}, [widgetId, state.messages, state.conversationId, state.config?.settings.persistConversation]);
|
|
742
|
-
/**
|
|
743
|
-
* Send a message
|
|
744
|
-
*/
|
|
745
1293
|
const sendMessage = useCallback(async (content, files) => {
|
|
746
1294
|
const trimmedContent = content.trim();
|
|
747
1295
|
const hasFiles = !!files && files.length > 0;
|
|
@@ -750,7 +1298,7 @@ function useChat(options) {
|
|
|
750
1298
|
const userMessage = {
|
|
751
1299
|
id: generateMessageId(),
|
|
752
1300
|
message: {
|
|
753
|
-
|
|
1301
|
+
role: "user",
|
|
754
1302
|
content: trimmedContent,
|
|
755
1303
|
},
|
|
756
1304
|
timestamp: new Date().toISOString(),
|
|
@@ -771,7 +1319,7 @@ function useChat(options) {
|
|
|
771
1319
|
const conversation = await apiClient.current.getOrCreateConversation();
|
|
772
1320
|
conversationId = conversation.id;
|
|
773
1321
|
setState(prev => {
|
|
774
|
-
const serverMessages = conversation.messages ?? [];
|
|
1322
|
+
const serverMessages = hydrateMessages(conversation.messages ?? []);
|
|
775
1323
|
if (serverMessages.length === 0) {
|
|
776
1324
|
return {
|
|
777
1325
|
...prev,
|
|
@@ -805,48 +1353,20 @@ function useChat(options) {
|
|
|
805
1353
|
}
|
|
806
1354
|
}
|
|
807
1355
|
}
|
|
808
|
-
// Determine if widget has actions (use agent endpoint)
|
|
809
|
-
const useAgent = state.config?.behavior.agentic || (state.config?.actions && state.config.actions.length > 0);
|
|
810
1356
|
// Stream the response
|
|
811
1357
|
let lastStreamedMessage = null;
|
|
812
|
-
const
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
// Show typing indicator if:
|
|
825
|
-
// 1. Message is streaming AND has no content (waiting for first token or after tool call)
|
|
826
|
-
// 2. Message is a tool execution (shows loading spinner on the tool)
|
|
827
|
-
const isAIMessage = message.message.type === 'ai';
|
|
828
|
-
const hasContent = isAIMessage && message.message.content.trim().length > 0;
|
|
829
|
-
const isToolExecuting = message.toolExecuting !== undefined;
|
|
830
|
-
return {
|
|
831
|
-
...prev,
|
|
832
|
-
messages: newMessages,
|
|
833
|
-
isTyping: (message.isStreaming && !hasContent && isAIMessage) || isToolExecuting,
|
|
834
|
-
isLoading: false,
|
|
835
|
-
};
|
|
836
|
-
}
|
|
837
|
-
else {
|
|
838
|
-
// Add new streaming message
|
|
839
|
-
const isAIMessage = message.message.type === 'ai';
|
|
840
|
-
const hasContent = isAIMessage && message.message.content.trim().length > 0;
|
|
841
|
-
const isToolExecuting = message.toolExecuting !== undefined;
|
|
842
|
-
return {
|
|
843
|
-
...prev,
|
|
844
|
-
messages: [...prev.messages, message],
|
|
845
|
-
isTyping: (message.isStreaming && !hasContent && isAIMessage) || isToolExecuting,
|
|
846
|
-
isLoading: false,
|
|
847
|
-
};
|
|
848
|
-
}
|
|
849
|
-
});
|
|
1358
|
+
const streamState = createStreamState();
|
|
1359
|
+
const stream = apiClient.current.sendAgentMessageStream(conversationId, trimmedContent, fileIds);
|
|
1360
|
+
for await (const event of stream) {
|
|
1361
|
+
if (event.type === "action_request") {
|
|
1362
|
+
await handleActionLoop(apiClient.current, event, streamState, (message) => {
|
|
1363
|
+
lastStreamedMessage = message;
|
|
1364
|
+
}, setState, widgetId, conversationId, () => stateRef.current.messages);
|
|
1365
|
+
break;
|
|
1366
|
+
}
|
|
1367
|
+
handleStreamEvent(event, streamState, (message) => {
|
|
1368
|
+
lastStreamedMessage = message;
|
|
1369
|
+
}, setState);
|
|
850
1370
|
}
|
|
851
1371
|
// Stream completed - finalize state
|
|
852
1372
|
setState(prev => ({
|
|
@@ -867,7 +1387,7 @@ function useChat(options) {
|
|
|
867
1387
|
const fallbackAssistantMessage = {
|
|
868
1388
|
id: generateMessageId(),
|
|
869
1389
|
message: {
|
|
870
|
-
|
|
1390
|
+
role: "assistant",
|
|
871
1391
|
content: fallbackMessage,
|
|
872
1392
|
},
|
|
873
1393
|
timestamp: new Date().toISOString(),
|
|
@@ -886,7 +1406,7 @@ function useChat(options) {
|
|
|
886
1406
|
const errorAssistantMessage = {
|
|
887
1407
|
id: generateMessageId(),
|
|
888
1408
|
message: {
|
|
889
|
-
|
|
1409
|
+
role: "assistant",
|
|
890
1410
|
content: `⚠️ ${errorInfo.message}`,
|
|
891
1411
|
},
|
|
892
1412
|
timestamp: new Date().toISOString(),
|
|
@@ -924,8 +1444,13 @@ function useChat(options) {
|
|
|
924
1444
|
*/
|
|
925
1445
|
const submitFeedback = useCallback(async (messageId, feedback) => {
|
|
926
1446
|
try {
|
|
1447
|
+
const message = state.messages.find(msg => msg.id === messageId);
|
|
1448
|
+
const messageContent = typeof message?.message.content === "string" ? message.message.content : undefined;
|
|
1449
|
+
const meta = message
|
|
1450
|
+
? { role: message.message.role, content: messageContent, timestamp: message.timestamp }
|
|
1451
|
+
: undefined;
|
|
927
1452
|
console.log('Submitting feedback:', { conversationId: state.conversationId, messageId, feedback });
|
|
928
|
-
await apiClient.current.submitFeedback(state.conversationId, messageId, feedback);
|
|
1453
|
+
await apiClient.current.submitFeedback(state.conversationId, messageId, feedback, meta);
|
|
929
1454
|
// Update message with feedback
|
|
930
1455
|
setState(prev => ({
|
|
931
1456
|
...prev,
|
|
@@ -959,9 +1484,6 @@ function useChat(options) {
|
|
|
959
1484
|
startedAt: entry.lastUpdated,
|
|
960
1485
|
})));
|
|
961
1486
|
}, [widgetId, state.config?.settings.persistConversation]);
|
|
962
|
-
/**
|
|
963
|
-
* Switch to a different conversation (from localStorage first, then fetch from server if needed)
|
|
964
|
-
*/
|
|
965
1487
|
const switchConversation = useCallback(async (conversationId) => {
|
|
966
1488
|
const persistConversation = state.config?.settings.persistConversation ?? true;
|
|
967
1489
|
// First try to load from localStorage
|
|
@@ -971,25 +1493,33 @@ function useChat(options) {
|
|
|
971
1493
|
setState(prev => ({
|
|
972
1494
|
...prev,
|
|
973
1495
|
conversationId: stored.conversationId,
|
|
974
|
-
messages: stored.messages,
|
|
1496
|
+
messages: hydrateMessages(stored.messages),
|
|
975
1497
|
}));
|
|
976
1498
|
setActiveConversation(widgetId, conversationId);
|
|
977
1499
|
return;
|
|
978
1500
|
}
|
|
979
1501
|
}
|
|
980
|
-
// If not in localStorage, fetch from server
|
|
981
1502
|
setState(prev => ({ ...prev, isLoading: true, error: null }));
|
|
982
1503
|
try {
|
|
983
1504
|
const conversation = await apiClient.current.getOrCreateConversation(conversationId);
|
|
1505
|
+
const hydratedMessages = hydrateMessages(conversation.messages);
|
|
1506
|
+
// Clear old resume callbacks
|
|
1507
|
+
state.messages.forEach(message => {
|
|
1508
|
+
if (message.action?.toolCallId) {
|
|
1509
|
+
unregisterActionResumeCallback(message.action.toolCallId);
|
|
1510
|
+
}
|
|
1511
|
+
});
|
|
984
1512
|
setState(prev => ({
|
|
985
1513
|
...prev,
|
|
986
1514
|
conversationId: conversation.id,
|
|
987
|
-
messages:
|
|
1515
|
+
messages: hydratedMessages,
|
|
988
1516
|
isLoading: false,
|
|
989
1517
|
}));
|
|
1518
|
+
// Setup new resume callbacks
|
|
1519
|
+
setupActionResumeCallbacks(hydratedMessages, apiClient.current, conversation.id, setState, onMessage ?? (() => { }));
|
|
990
1520
|
// Save to local storage
|
|
991
1521
|
if (persistConversation && isStorageAvailable()) {
|
|
992
|
-
saveConversation(widgetId, conversation.id,
|
|
1522
|
+
saveConversation(widgetId, conversation.id, hydratedMessages);
|
|
993
1523
|
}
|
|
994
1524
|
}
|
|
995
1525
|
catch (error) {
|
|
@@ -997,9 +1527,6 @@ function useChat(options) {
|
|
|
997
1527
|
setState(prev => ({ ...prev, isLoading: false, error: errorInfo.message }));
|
|
998
1528
|
}
|
|
999
1529
|
}, [widgetId, state.config?.settings.persistConversation]);
|
|
1000
|
-
/**
|
|
1001
|
-
* Start a new conversation (keeps history)
|
|
1002
|
-
*/
|
|
1003
1530
|
const startNewConversation = useCallback(() => {
|
|
1004
1531
|
setState(prev => ({
|
|
1005
1532
|
...prev,
|
|
@@ -1007,15 +1534,11 @@ function useChat(options) {
|
|
|
1007
1534
|
conversationId: '',
|
|
1008
1535
|
error: null,
|
|
1009
1536
|
}));
|
|
1010
|
-
// Clear active conversation but keep history
|
|
1011
1537
|
const persistConversation = state.config?.settings.persistConversation ?? true;
|
|
1012
1538
|
if (persistConversation && isStorageAvailable()) {
|
|
1013
1539
|
clearConversation(widgetId);
|
|
1014
1540
|
}
|
|
1015
1541
|
}, [widgetId, state.config?.settings.persistConversation]);
|
|
1016
|
-
/**
|
|
1017
|
-
* Delete a conversation from history
|
|
1018
|
-
*/
|
|
1019
1542
|
const deleteConversation$1 = useCallback((conversationId) => {
|
|
1020
1543
|
const persistConversation = state.config?.settings.persistConversation ?? true;
|
|
1021
1544
|
if (!persistConversation || !isStorageAvailable()) {
|
|
@@ -27869,12 +28392,12 @@ const Message = ({ message, showTimestamp = true, enableFeedback = true, showSou
|
|
|
27869
28392
|
hour12: true,
|
|
27870
28393
|
});
|
|
27871
28394
|
};
|
|
27872
|
-
const
|
|
28395
|
+
const role = message.message.role;
|
|
27873
28396
|
const isError = message.isError || false;
|
|
27874
|
-
const isTool =
|
|
27875
|
-
const isAssistant =
|
|
27876
|
-
const isSystem =
|
|
27877
|
-
const isHuman =
|
|
28397
|
+
const isTool = role === "tool";
|
|
28398
|
+
const isAssistant = role === "assistant";
|
|
28399
|
+
const isSystem = role === "system";
|
|
28400
|
+
const isHuman = role === "user";
|
|
27878
28401
|
// Tool messages are now handled by ToolMessageGroup in MessageList
|
|
27879
28402
|
if (isTool) {
|
|
27880
28403
|
return null;
|
|
@@ -27885,7 +28408,10 @@ const Message = ({ message, showTimestamp = true, enableFeedback = true, showSou
|
|
|
27885
28408
|
const hasContent = aiContent.trim().length > 0;
|
|
27886
28409
|
if (!hasContent)
|
|
27887
28410
|
return null;
|
|
27888
|
-
|
|
28411
|
+
const actionRenderer = message.action
|
|
28412
|
+
? getActionRenderer(message.action.implementation)
|
|
28413
|
+
: undefined;
|
|
28414
|
+
return (jsxs("div", { className: `ai-chat-message assistant ${isError ? 'error' : ''}`, children: [jsxs("div", { className: "ai-chat-message-content", children: [isError && (jsxs("div", { className: "ai-chat-error-indicator", children: [jsx("span", { className: "error-icon", children: "\u26A0\uFE0F" }), jsx("span", { className: "error-text", children: "Error" })] })), jsx(Markdown, { remarkPlugins: [remarkGfm], children: aiContent })] }), actionRenderer && message.action && actionRenderer(message), showTimestamp && (jsxs("div", { className: "ai-chat-message-meta", children: [jsx("span", { className: "ai-chat-message-timestamp", children: formatTime(message.timestamp) }), enableFeedback && onFeedback && (jsx(FeedbackButtons, { messageId: message.id, currentFeedback: message.feedback, onFeedback: onFeedback }))] })), showSources && message.sources?.length > 0 && (jsx(Sources, { sources: message.sources, displayMode: sourceDisplayMode }))] }));
|
|
27889
28415
|
}
|
|
27890
28416
|
// System message rendering
|
|
27891
28417
|
if (isSystem) {
|
|
@@ -27893,7 +28419,7 @@ const Message = ({ message, showTimestamp = true, enableFeedback = true, showSou
|
|
|
27893
28419
|
}
|
|
27894
28420
|
// Human message rendering
|
|
27895
28421
|
if (isHuman) {
|
|
27896
|
-
const userContent = message.message.content.split('--- File Content ---')[0];
|
|
28422
|
+
const userContent = (message.message.content || '').split('--- File Content ---')[0];
|
|
27897
28423
|
return (jsxs("div", { className: "ai-chat-message user", children: [jsx("div", { className: "ai-chat-message-content", children: userContent }), showTimestamp && (jsx("div", { className: "ai-chat-message-meta", children: jsx("span", { className: "ai-chat-message-timestamp", children: formatTime(message.timestamp) }) }))] }));
|
|
27898
28424
|
}
|
|
27899
28425
|
return null;
|
|
@@ -27910,13 +28436,31 @@ const GearIcon = ({ spinning = false }) => (jsxs("svg", { className: `ai-chat-to
|
|
|
27910
28436
|
const CheckIcon = () => (jsx("svg", { className: "ai-chat-tool-check", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("polyline", { points: "20 6 9 17 4 12" }) }));
|
|
27911
28437
|
const ErrorIcon = () => (jsx("svg", { className: "ai-chat-tool-error", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("path", { d: "M18 6L6 18M6 6l12 12" }) }));
|
|
27912
28438
|
const ToolMessageGroup = ({ messages }) => {
|
|
27913
|
-
|
|
27914
|
-
|
|
27915
|
-
|
|
27916
|
-
|
|
27917
|
-
|
|
27918
|
-
|
|
27919
|
-
|
|
28439
|
+
// Check if any message is loading (for actions, check done flag; otherwise check isStreaming)
|
|
28440
|
+
const isAnyLoading = messages.some(m => {
|
|
28441
|
+
if (m.action) {
|
|
28442
|
+
return !(m.action.done ?? false);
|
|
28443
|
+
}
|
|
28444
|
+
return m.isStreaming;
|
|
28445
|
+
});
|
|
28446
|
+
const actionMessages = messages.filter(message => message.action);
|
|
28447
|
+
return (jsxs("div", { className: "ai-chat-message tool", children: [jsxs("div", { className: "ai-chat-tool-row", children: [jsx(GearIcon, { spinning: isAnyLoading }), jsx("div", { className: "ai-chat-tool-badges", children: messages.map((message) => {
|
|
28448
|
+
const toolName = message.toolExecuting || message.message.name || 'Tool';
|
|
28449
|
+
const hasError = message.isError || false;
|
|
28450
|
+
// For actions, check if done flag is set; otherwise fall back to isStreaming
|
|
28451
|
+
const isDone = message.action ? (message.action.done ?? false) : !message.isStreaming;
|
|
28452
|
+
const isLoading = !isDone;
|
|
28453
|
+
return (jsxs("div", { className: `ai-chat-tool-badge ${isLoading ? 'loading' : hasError ? 'error' : 'completed'}`, children: [!isLoading && (hasError ? jsx(ErrorIcon, {}) : jsx(CheckIcon, {})), jsx("span", { className: "tool-name", children: formatToolName(toolName) })] }, message.id));
|
|
28454
|
+
}) })] }), actionMessages.map((message) => {
|
|
28455
|
+
if (!message.action) {
|
|
28456
|
+
return null;
|
|
28457
|
+
}
|
|
28458
|
+
const renderer = getActionRenderer(message.action.implementation);
|
|
28459
|
+
if (!renderer) {
|
|
28460
|
+
return null;
|
|
28461
|
+
}
|
|
28462
|
+
return (jsx("div", { className: "ai-chat-tool-action", children: renderer(message) }, `action-${message.id}`));
|
|
28463
|
+
})] }));
|
|
27920
28464
|
};
|
|
27921
28465
|
|
|
27922
28466
|
const TypingIndicator = () => {
|
|
@@ -27941,6 +28485,14 @@ const SuggestedQuestions = ({ questions, onQuestionClick, }) => {
|
|
|
27941
28485
|
const MessageList = ({ messages, isTyping = false, showTypingIndicator = true, showTimestamps = true, enableFeedback = true, showSources = true, sourceDisplayMode = 'with-score', welcomeTitle, welcomeMessage, suggestedQuestions, onSuggestedQuestionClick, onFeedback, }) => {
|
|
27942
28486
|
const containerRef = useRef(null);
|
|
27943
28487
|
const messagesEndRef = useRef(null);
|
|
28488
|
+
// Check if there's an active action awaiting user input
|
|
28489
|
+
const hasActiveAction = useMemo(() => {
|
|
28490
|
+
return messages.some(msg => msg.action &&
|
|
28491
|
+
msg.action.state &&
|
|
28492
|
+
msg.action.state.status !== 'completed' &&
|
|
28493
|
+
msg.action.state.status !== 'booked' &&
|
|
28494
|
+
msg.action.state.status !== 'failed');
|
|
28495
|
+
}, [messages]);
|
|
27944
28496
|
// Auto-scroll to bottom only on initial mount/load
|
|
27945
28497
|
useEffect(() => {
|
|
27946
28498
|
const container = containerRef.current;
|
|
@@ -27969,16 +28521,16 @@ const MessageList = ({ messages, isTyping = false, showTypingIndicator = true, s
|
|
|
27969
28521
|
}
|
|
27970
28522
|
};
|
|
27971
28523
|
for (const message of messages) {
|
|
27972
|
-
if (message.message.
|
|
28524
|
+
if (message.message.role === "tool") {
|
|
27973
28525
|
// Add to current tool group
|
|
27974
28526
|
currentToolGroup.push(message);
|
|
27975
28527
|
}
|
|
27976
|
-
else if (message.message.
|
|
28528
|
+
else if (message.message.role === "user") {
|
|
27977
28529
|
// Human message breaks tool grouping
|
|
27978
28530
|
flushToolGroup();
|
|
27979
28531
|
result.push({ type: 'message', message });
|
|
27980
28532
|
}
|
|
27981
|
-
else if (message.message.
|
|
28533
|
+
else if (message.message.role === "assistant") {
|
|
27982
28534
|
// Skip empty AI messages
|
|
27983
28535
|
const content = (message.message.content || '').trim();
|
|
27984
28536
|
if (!content) {
|
|
@@ -28004,7 +28556,7 @@ const MessageList = ({ messages, isTyping = false, showTypingIndicator = true, s
|
|
|
28004
28556
|
return (jsx(ToolMessageGroup, { messages: item.messages }, `tool-group-${index}`));
|
|
28005
28557
|
}
|
|
28006
28558
|
return (jsx(Message, { message: item.message, showTimestamp: showTimestamps, enableFeedback: enableFeedback, showSources: showSources, sourceDisplayMode: sourceDisplayMode, onFeedback: onFeedback }, item.message.id));
|
|
28007
|
-
}), isTyping && showTypingIndicator && jsx(TypingIndicator, {}), jsx("div", { ref: messagesEndRef })] }));
|
|
28559
|
+
}), isTyping && showTypingIndicator && !hasActiveAction && jsx(TypingIndicator, {}), jsx("div", { ref: messagesEndRef })] }));
|
|
28008
28560
|
};
|
|
28009
28561
|
|
|
28010
28562
|
// Allowed file types
|
|
@@ -28094,7 +28646,6 @@ const ChatWindow = ({ messages, isLoading, isTyping, error, config, onSendMessag
|
|
|
28094
28646
|
conversations = [], onLoadConversations, onSwitchConversation, onStartNewConversation, onDeleteConversation, currentConversationId,
|
|
28095
28647
|
// Override props for live preview
|
|
28096
28648
|
headerTitleOverride, welcomeTitleOverride, welcomeMessageOverride, placeholderOverride, suggestedQuestionsOverride, }) => {
|
|
28097
|
-
console.log('[ChatWindow] Rendering ChatWindow component');
|
|
28098
28649
|
const appearance = config?.appearance;
|
|
28099
28650
|
const settings = config?.settings;
|
|
28100
28651
|
// Check if chat history should be shown (requires both persistConversation AND showChatHistory)
|
|
@@ -28105,13 +28656,6 @@ headerTitleOverride, welcomeTitleOverride, welcomeMessageOverride, placeholderOv
|
|
|
28105
28656
|
const welcomeTitle = welcomeTitleOverride ?? appearance?.welcomeTitle ?? '';
|
|
28106
28657
|
const welcomeMessage = welcomeMessageOverride ?? appearance?.welcomeMessage ?? '';
|
|
28107
28658
|
const inputPlaceholder = placeholderOverride ?? appearance?.placeholder ?? 'Ask me anything...';
|
|
28108
|
-
console.log('[ChatWindow] Appearance values:', {
|
|
28109
|
-
size,
|
|
28110
|
-
headerTitle,
|
|
28111
|
-
welcomeTitle,
|
|
28112
|
-
welcomeMessage,
|
|
28113
|
-
inputPlaceholder,
|
|
28114
|
-
});
|
|
28115
28659
|
// Track if history panel is open
|
|
28116
28660
|
const [showHistory, setShowHistory] = useState(false);
|
|
28117
28661
|
// History exit animation when starting a new chat from overview
|
|
@@ -28156,7 +28700,7 @@ headerTitleOverride, welcomeTitleOverride, welcomeMessageOverride, placeholderOv
|
|
|
28156
28700
|
};
|
|
28157
28701
|
// Check if message limit is reached
|
|
28158
28702
|
const maxMessages = settings?.maxMessagesPerSession;
|
|
28159
|
-
const userMessageCount = messages.filter(m => m.message.
|
|
28703
|
+
const userMessageCount = messages.filter(m => m.message.role === "user").length;
|
|
28160
28704
|
const isLimitReached = maxMessages ? userMessageCount >= maxMessages : false;
|
|
28161
28705
|
const handleQuestionClick = (question) => {
|
|
28162
28706
|
onSendMessage(question);
|
|
@@ -28566,34 +29110,7 @@ function createThemeObserver(element, callback) {
|
|
|
28566
29110
|
return observer;
|
|
28567
29111
|
}
|
|
28568
29112
|
|
|
28569
|
-
function styleInject(css, ref) {
|
|
28570
|
-
if ( ref === void 0 ) ref = {};
|
|
28571
|
-
var insertAt = ref.insertAt;
|
|
28572
|
-
|
|
28573
|
-
if (typeof document === 'undefined') { return; }
|
|
28574
|
-
|
|
28575
|
-
var head = document.head || document.getElementsByTagName('head')[0];
|
|
28576
|
-
var style = document.createElement('style');
|
|
28577
|
-
style.type = 'text/css';
|
|
28578
|
-
|
|
28579
|
-
if (insertAt === 'top') {
|
|
28580
|
-
if (head.firstChild) {
|
|
28581
|
-
head.insertBefore(style, head.firstChild);
|
|
28582
|
-
} else {
|
|
28583
|
-
head.appendChild(style);
|
|
28584
|
-
}
|
|
28585
|
-
} else {
|
|
28586
|
-
head.appendChild(style);
|
|
28587
|
-
}
|
|
28588
|
-
|
|
28589
|
-
if (style.styleSheet) {
|
|
28590
|
-
style.styleSheet.cssText = css;
|
|
28591
|
-
} else {
|
|
28592
|
-
style.appendChild(document.createTextNode(css));
|
|
28593
|
-
}
|
|
28594
|
-
}
|
|
28595
|
-
|
|
28596
|
-
var css_248z = ".ai-chat-widget{--radius-sm:4px;--radius-md:8px;--radius-lg:12px;--radius-xl:16px;--radius-2xl:18px;--radius-pill:9999px;--radius-window-top:22px;--radius-window-bottom:44px;--radius-window-gutter:16px;--radius-chat-bubble:14px;--radius-preset-badge:13px;--radius-history-item:14px;--radius-action-badge:8px;--radius-input:62px;--space-xs:4px;--space-sm:8px;--space-md:16px;--space-lg:24px;--space-xl:32px;--text-xs:12px;--text-sm:14px;--text-md:15px;--text-lg:18px;--text-xl:22px;--text-2xl:28px;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--line-height-tight:1.3;--line-height-normal:1.4;--line-height-relaxed:1.6;--bg-primary:#fff;--bg-secondary:#f4f4f4;--bg-tertiary:#e5e7eb;--bg-hover:#e5e7eb;--text-primary:#3e3e3e;--text-secondary:#000;--text-muted:#71717a;--text-placeholder:#a1a1aa;--border-default:#d3d3d3;--border-subtle:#e5e7eb;--border-muted:#f4f4f5;--user-bg:#f4f3f0;--user-text:#000;--user-bg-hover:#e8e7e4;--agent-bg:transparent;--agent-text:#000;--input-bg:#f4f4f4;--input-border:#d3d3d3;--input-text:#000;--btn-primary-bg:#151515;--btn-primary-text:#f4f4f4;--btn-secondary-bg:transparent;--btn-secondary-text:#71717a;--spring-bounce:cubic-bezier(0.34,1.56,0.64,1);--spring-smooth:cubic-bezier(0.4,0,0.2,1);--spring-snappy:cubic-bezier(0.2,0,0,1);--duration-fast:0.15s;--duration-normal:0.25s;--duration-slow:0.35s;--shadow-sm:0 1px 2px rgba(0,0,0,.05);--shadow-md:0 2px 8px rgba(0,0,0,.1);--shadow-lg:0 4px 16px rgba(0,0,0,.12);--shadow-window:0px 0px 15px 9px rgba(0,0,0,.1);--shadow-button:0px 0px 15px 9px rgba(0,0,0,.03)}.ai-chat-widget.dark{--bg-primary:#282625;--bg-secondary:#4a4846;--bg-tertiary:#484848;--bg-hover:#484848;--text-primary:#fff;--text-secondary:#fff;--text-muted:#a1a1aa;--text-placeholder:#71717a;--border-default:#5d5b5b;--border-subtle:#5d5b5b;--border-muted:#5d5b5b;--user-bg:#484848;--user-text:#fff;--user-bg-hover:#5a5a5a;--agent-bg:transparent;--agent-text:#fff;--input-bg:#4a4846;--input-border:#5d5b5b;--input-text:#fff;--btn-primary-bg:#fff;--btn-primary-text:#312f2d;--btn-secondary-bg:transparent;--btn-secondary-text:#a1a1aa;--shadow-window:0px 0px 15px 9px rgba(0,0,0,.2);--shadow-button:0px 0px 15px 9px rgba(0,0,0,.03);--shadow-input:0px 0px 10px rgba(0,0,0,.15)}.ai-chat-widget{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}.ai-chat-widget-container{font-size:var(--text-sm);line-height:1.5;position:fixed;z-index:var(--widget-z-index,9999)}.ai-chat-widget-container.bottom-right{bottom:20px;right:20px}.ai-chat-widget-container.bottom-left{bottom:20px;left:20px}.ai-chat-widget-container.top-right{right:20px;top:20px}.ai-chat-widget-container.top-left{left:20px;top:20px}@keyframes windowOpen{0%{opacity:0;transform:scale(.9) translateY(20px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes windowClose{0%{opacity:1;transform:scale(1) translateY(0)}to{opacity:0;transform:scale(.9) translateY(20px)}}@keyframes messageSlideIn{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}@keyframes welcomeFadeIn{0%{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}@keyframes typingPulse{0%,60%,to{opacity:.4;transform:translateY(0) scale(1)}30%{opacity:1;transform:translateY(-4px) scale(1.1)}}@keyframes ai-chat-tool-active{0%,to{background:var(--bg-secondary);opacity:1}50%{background:var(--bg-tertiary);opacity:1}}@keyframes feedbackMorph{0%{opacity:.5;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@keyframes checkmarkPop{0%{opacity:0;transform:scale(0) rotate(-45deg)}50%{transform:scale(1.3) rotate(0deg)}to{opacity:1;transform:scale(1) rotate(0deg)}}@keyframes ai-chat-history-exit{to{opacity:0;transform:translateY(-18px)}}@media (max-width:480px){.ai-chat-window{animation:mobileSlideUp var(--duration-normal) var(--spring-smooth);border-radius:0!important;bottom:0!important;height:100%!important;left:0!important;position:fixed!important;right:0!important;top:0!important;width:100%!important}@keyframes mobileSlideUp{0%{transform:translateY(100%)}to{transform:translateY(0)}}.ai-chat-header{padding-top:max(16px,env(safe-area-inset-top))}.ai-chat-input-container{padding-bottom:max(20px,env(safe-area-inset-bottom))}}.ai-chat-button{align-items:center;background:var(--button-color,var(--btn-primary-bg));border:1px solid var(--button-border-color,var(--button-color,var(--btn-primary-bg)));border-radius:50%;box-shadow:var(--shadow-button,0 0 15px 9px rgba(0,0,0,.03));color:var(--button-icon-color,var(--btn-primary-text));cursor:pointer;display:flex;height:var(--button-size,56px);justify-content:center;overflow:hidden;position:relative;transition:filter var(--duration-fast) ease,transform var(--duration-fast) ease;width:var(--button-size,56px);z-index:1}.ai-chat-button:hover{transform:scale(1.02)}.ai-chat-button:active{transform:scale(.98)}.ai-chat-button-svg{height:50%;min-height:24px;min-width:24px;transition:transform var(--duration-fast) ease;width:50%}.ai-chat-button.is-open .ai-chat-button-svg{transform:rotate(0deg)}.ai-chat-button-icon{font-size:1.5em;line-height:1}.ai-chat-window{animation:windowOpen var(--duration-slow,.35s) var(--spring-bounce,cubic-bezier(.34,1.56,.64,1));background:var(--bg-primary,#fff);border:1px solid var(--border-default,#d3d3d3);border-radius:var(--radius-window-top,22px) var(--radius-window-top,22px) var(--radius-window-bottom,44px) var(--radius-window-bottom,44px);box-shadow:var(--shadow-window,0 0 15px 5px rgba(0,0,0,.08));display:flex;flex-direction:column;overflow:hidden;position:absolute;transform-origin:bottom right;z-index:2}.ai-chat-widget.dark .ai-chat-window{background:var(--bg-primary,#282625);border-color:var(--border-default,#5d5b5b);border-width:.7px}.ai-chat-window.closing{animation:windowClose var(--duration-normal) var(--spring-smooth) forwards}.ai-chat-window.size-small{height:var(--window-height,580px);width:var(--window-width,380px)}.ai-chat-window.size-medium{height:var(--window-height,720px);width:var(--window-width,440px)}.ai-chat-window.size-large{height:var(--window-height,820px);width:var(--window-width,520px)}.ai-chat-widget-container.bottom-right .ai-chat-window{bottom:calc(var(--button-size, 56px) + 8px);right:0}.ai-chat-widget-container.bottom-left .ai-chat-window{bottom:calc(var(--button-size, 56px) + 8px);left:0}.ai-chat-widget-container.top-right .ai-chat-window{right:0;top:calc(var(--button-size, 56px) + 8px)}.ai-chat-widget-container.top-left .ai-chat-window{left:0;top:calc(var(--button-size, 56px) + 8px)}.ai-chat-header{align-items:center;background:var(--bg-primary,#fff);border-bottom:1px solid var(--border-default,#d3d3d3);border-radius:var(--radius-window-top,22px) var(--radius-window-top,22px) 0 0;display:flex;justify-content:space-between;padding:18px var(--space-md,16px);position:relative;z-index:10}.ai-chat-widget.dark .ai-chat-header{background:var(--bg-primary,#282625);border-bottom-color:var(--border-default,#5d5b5b);border-bottom-width:.7px}.ai-chat-header.is-history{padding-left:var(--space-md)}.ai-chat-header.is-history .ai-chat-title{flex:1;min-width:0;overflow:hidden;padding-right:var(--space-lg);text-overflow:ellipsis;white-space:nowrap}.ai-chat-header-content{align-items:center;display:flex;flex:1;gap:var(--space-lg)}.ai-chat-header-actions{align-items:center;display:flex;gap:var(--space-sm)}.ai-chat-logo{border-radius:10px;height:36px;object-fit:cover;width:36px}.ai-chat-title{color:var(--text-primary,#3e3e3e);font-size:var(--text-xl,22px);font-weight:var(--font-weight-bold,700);letter-spacing:-.02em}.ai-chat-widget.dark .ai-chat-title{color:var(--text-primary,#fff)}.ai-chat-close-button{align-items:center;background:transparent;border:none;border-radius:var(--radius-md);color:var(--text-primary);cursor:pointer;display:flex;height:32px;justify-content:center;padding:0;transition:color var(--duration-fast) ease;width:32px}.ai-chat-close-button:hover{color:var(--text-muted)}.ai-chat-close-button:active{transform:scale(.95)}.ai-chat-header-button{align-items:center;background:transparent;border:none;border-radius:var(--radius-md);color:var(--text-muted);cursor:pointer;display:flex;height:32px;justify-content:center;padding:0;transition:all var(--duration-fast) ease;width:32px}.ai-chat-header-button:hover{background:var(--bg-secondary);color:var(--text-secondary)}.ai-chat-header-button svg{height:18px;width:18px}.ai-chat-messages{-webkit-overflow-scrolling:touch;-ms-overflow-style:none;align-items:stretch;background:var(--bg-primary,#fff);display:flex;flex:1;flex-direction:column;gap:var(--space-md,16px);justify-content:flex-start;overflow-x:hidden;overflow-y:auto;padding:var(--space-lg,24px) var(--space-md,16px) 100px;position:relative;scroll-behavior:smooth;scrollbar-width:none}.ai-chat-widget.dark .ai-chat-messages{background:var(--bg-primary,#18181b)}.ai-chat-messages::-webkit-scrollbar{display:none}.ai-chat-message{animation:messageSlideIn var(--duration-normal) var(--spring-bounce);display:flex;flex-direction:column;gap:6px}.ai-chat-message-content{word-wrap:break-word;font-size:var(--text-md);line-height:var(--line-height-relaxed);max-width:85%}.ai-chat-message.user{align-items:flex-end}.ai-chat-message.user .ai-chat-message-content{background:var(--user-bg,#f4f3f0);border:none;border-radius:var(--radius-chat-bubble,15px);box-shadow:none;color:var(--user-text,#000);padding:var(--space-sm,8px) var(--space-md,16px)}.ai-chat-widget.dark .ai-chat-message.user .ai-chat-message-content{background:var(--user-bg,#484848);color:var(--user-text,#fff)}.ai-chat-message.user .ai-chat-message-meta{justify-content:flex-end;padding-right:var(--space-xs)}.ai-chat-message.assistant{align-items:flex-start}.ai-chat-message.assistant .ai-chat-message-content{background:var(--agent-bg,transparent);border:none;color:var(--agent-text,#18181b);padding:0}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content{color:var(--agent-text,#fafafa)}.ai-chat-message.system{align-items:center}.ai-chat-message.system .ai-chat-message-content{background:hsla(48,96%,89%,.8);border-radius:var(--radius-md);color:#92400e;font-size:var(--text-xs);font-style:italic;max-width:90%;padding:var(--space-sm) var(--space-md);text-align:center}.ai-chat-widget.dark .ai-chat-message.system .ai-chat-message-content{background:rgba(120,53,15,.5);color:#fef3c7}.ai-chat-message.tool{align-items:flex-start}.ai-chat-message.tool .ai-chat-message-content{background:rgba(219,234,254,.8);border-radius:var(--radius-chat-bubble);border-bottom-left-radius:var(--radius-xs);color:#1e40af;font-family:Courier New,monospace;font-size:var(--text-sm);padding:var(--space-sm) var(--space-md)}.ai-chat-widget.dark .ai-chat-message.tool .ai-chat-message-content{background:rgba(30,58,138,.5);color:#dbeafe}.ai-chat-message-meta{align-items:center;color:var(--text-muted);display:flex;font-size:var(--text-xs);gap:var(--space-sm);padding-left:var(--space-xs)}.ai-chat-message-timestamp{font-size:var(--text-xs);line-height:1}.ai-chat-typing{align-items:center;animation:messageSlideIn var(--duration-normal) var(--spring-bounce);background:transparent;display:flex;gap:5px;padding:0}.ai-chat-typing-dot{animation:typingPulse 1.4s ease-in-out infinite;background:var(--text-muted);border-radius:50%;height:6px;width:6px}.ai-chat-typing-dot:nth-child(2){animation-delay:.15s}.ai-chat-typing-dot:nth-child(3){animation-delay:.3s}.ai-chat-welcome{align-items:stretch;animation:welcomeFadeIn var(--duration-slow) var(--spring-smooth);display:flex;flex-direction:column;justify-content:flex-start;padding:0;text-align:left}.ai-chat-welcome-text,.ai-chat-welcome-title{align-self:flex-start}.ai-chat-welcome-title{color:var(--text-primary);font-size:var(--text-2xl);font-weight:var(--font-weight-semibold);letter-spacing:-.02em;margin-bottom:var(--space-md)}.ai-chat-welcome-text{color:var(--text-secondary);font-size:var(--text-md);line-height:var(--line-height-relaxed);max-width:100%}.ai-chat-error{align-items:flex-start;align-self:center;background:var(--bg-secondary);border:none;border-radius:var(--radius-chat-bubble);color:var(--text-primary);display:flex;font-size:var(--text-md);font-weight:var(--font-weight-normal);gap:10px;line-height:1.5;margin:0 auto;max-width:90%;padding:10px var(--space-md)}.ai-chat-error:before{align-items:center;background:rgba(239,68,68,.15);border-radius:50%;color:#ef4444;content:\"⚠\";display:flex;flex-shrink:0;font-size:var(--text-xs);font-weight:700;height:18px;justify-content:center;margin-top:2px;width:18px}.ai-chat-widget.dark .ai-chat-error:before{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-message.assistant .ai-chat-message-content p{margin:0 0 12px}.ai-chat-message.assistant .ai-chat-message-content p:last-child{margin-bottom:0}.ai-chat-message.assistant .ai-chat-message-content ol,.ai-chat-message.assistant .ai-chat-message-content ul{margin:8px 0 12px;padding-left:24px}.ai-chat-message.assistant .ai-chat-message-content li{line-height:1.5;margin:6px 0}.ai-chat-message.assistant .ai-chat-message-content strong{font-weight:var(--font-weight-semibold)}.ai-chat-message.assistant .ai-chat-message-content em{font-style:italic}.ai-chat-message.assistant .ai-chat-message-content code{background:rgba(0,0,0,.06);border-radius:var(--radius-sm);font-family:SF Mono,Consolas,Monaco,monospace;font-size:.9em;padding:2px 6px}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content code{background:hsla(0,0%,100%,.1)}.ai-chat-message.assistant .ai-chat-message-content pre{background:rgba(0,0,0,.06);border-radius:var(--radius-md);margin:8px 0 12px;overflow-x:auto;padding:var(--space-sm)}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content pre{background:hsla(0,0%,100%,.08)}.ai-chat-message.assistant .ai-chat-message-content pre code{background:transparent;border-radius:0;padding:0}.ai-chat-message.assistant .ai-chat-message-content blockquote{border-left:3px solid var(--btn-primary-bg);color:var(--text-muted);margin:8px 0 12px;padding:4px 0 4px 12px}.ai-chat-message.assistant .ai-chat-message-content a{color:var(--btn-primary-bg);text-decoration:underline}.ai-chat-message.assistant .ai-chat-message-content a:hover{opacity:.8}.ai-chat-message.assistant .ai-chat-message-content h1,.ai-chat-message.assistant .ai-chat-message-content h2,.ai-chat-message.assistant .ai-chat-message-content h3,.ai-chat-message.assistant .ai-chat-message-content h4,.ai-chat-message.assistant .ai-chat-message-content h5,.ai-chat-message.assistant .ai-chat-message-content h6{font-weight:var(--font-weight-semibold);line-height:var(--line-height-tight);margin:16px 0 8px}.ai-chat-message.assistant .ai-chat-message-content h1:first-child,.ai-chat-message.assistant .ai-chat-message-content h2:first-child,.ai-chat-message.assistant .ai-chat-message-content h3:first-child{margin-top:0}.ai-chat-message.assistant .ai-chat-message-content hr{border:none;border-top:1px solid var(--border-subtle);margin:12px 0}.ai-chat-input-container{background:linear-gradient(to bottom,transparent 0,var(--bg-primary,#fff) 50%,var(--bg-primary,#fff) 100%);bottom:0;left:0;padding-top:30px;position:absolute;right:0;z-index:10}.ai-chat-widget.dark .ai-chat-input-container{background:linear-gradient(to bottom,transparent 0,var(--bg-primary,#282625) 50%,var(--bg-primary,#282625) 100%)}.ai-chat-input-container.separate{padding:0 var(--radius-window-gutter,16px) var(--radius-window-gutter,16px);padding-top:30px}.ai-chat-input-wrapper{align-items:flex-start;background:var(--input-bg,#f4f4f4);border:1px solid var(--input-border,#d3d3d3);border-radius:var(--radius-input,62px);display:flex;gap:0;height:52px;padding:6px 6px 6px 12px;position:relative;transition:all var(--duration-fast,.15s) ease;z-index:5}.ai-chat-widget.dark .ai-chat-input-wrapper{background:var(--input-bg,#4a4846);border-color:var(--input-border,#5d5b5b);border-width:.7px;box-shadow:var(--shadow-input,0 0 10px rgba(0,0,0,.15))}.ai-chat-input-wrapper:focus-within{border-color:var(--text-muted,#a1a1aa)}.ai-chat-input{word-wrap:break-word;background:transparent;border:none;box-sizing:border-box;color:var(--input-text,#000);flex:1;font-family:inherit;font-size:var(--text-md,15px);height:40px;line-height:20px;max-height:40px;min-height:40px;min-width:0;outline:none;overflow-wrap:anywhere;overflow-x:hidden;overflow-y:auto;padding:10px var(--space-sm,8px);resize:none;white-space:pre-wrap;width:0;word-break:break-word}.ai-chat-widget.dark .ai-chat-input{color:var(--input-text,#fff)}.ai-chat-input::placeholder{color:var(--text-placeholder,#a1a1aa)}.ai-chat-widget.dark .ai-chat-input::placeholder{color:var(--text-placeholder,#52525b)}.ai-chat-file-button{align-items:center;align-self:center;background:transparent;border:none;color:var(--text-placeholder);cursor:pointer;display:flex;flex-shrink:0;height:28px;justify-content:center;padding:0;transition:color var(--duration-fast) ease;width:28px}.ai-chat-file-button:hover{color:var(--text-secondary)}.ai-chat-file-button:disabled{cursor:not-allowed;opacity:.5}.ai-chat-send-button{align-items:center;align-self:center;background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#151515))));border:none;border-radius:50%;color:var(--button-icon-color,var(--btn-primary-text,#f4f4f4));cursor:pointer;display:flex;flex-shrink:0;height:40px;justify-content:center;padding:0;transition:all var(--duration-fast,.15s) ease;width:40px}.ai-chat-widget.dark .ai-chat-send-button{background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#fff))));color:var(--button-icon-color,var(--btn-primary-text,#312f2d))}.ai-chat-send-button.active{background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#151515))));color:var(--button-icon-color,var(--btn-primary-text,#f4f4f4))}.ai-chat-widget.dark .ai-chat-send-button.active{background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#fff))));color:var(--button-icon-color,var(--btn-primary-text,#312f2d))}.ai-chat-send-button:hover:not(:disabled){opacity:.8}.ai-chat-send-button:active:not(:disabled){transform:scale(.95)}.ai-chat-send-button:disabled{cursor:not-allowed;opacity:.3}.ai-chat-file-list{display:flex;flex-wrap:wrap;gap:var(--space-sm);padding:var(--space-sm) var(--space-sm)}.ai-chat-file-item{align-items:center;background:rgba(0,0,0,.05);border-radius:6px;display:flex;font-size:var(--text-xs);gap:var(--space-sm);padding:6px 10px}.ai-chat-file-extension{background:var(--btn-primary-bg);border-radius:3px;color:var(--btn-primary-text);display:inline-block;font-size:10px;font-weight:var(--font-weight-semibold);min-width:40px;padding:2px 6px;text-align:center;text-transform:uppercase}.ai-chat-file-info{display:flex;flex:1;flex-direction:column;gap:2px;min-width:0}.ai-chat-file-name{font-weight:var(--font-weight-medium);max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ai-chat-file-size{color:var(--text-muted);font-size:10px;opacity:.7}.ai-chat-file-remove{align-items:center;background:none;border:none;color:inherit;cursor:pointer;display:flex;justify-content:center;opacity:.5;padding:var(--space-xs);transition:opacity var(--duration-fast) ease}.ai-chat-file-remove:hover{opacity:1}.ai-chat-suggested-questions{align-self:flex-end;margin:0;padding:16px 0 0;width:100%}.ai-chat-suggested-questions-list{align-items:center;display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end}.ai-chat-suggested-question{background:var(--primary-color,var(--button-color,var(--user-bg,#f4f3f0)));border:none;border-radius:var(--radius-preset-badge,13px);color:var(--button-icon-color,var(--user-text,#000));cursor:pointer;font-size:14px;font-weight:400;line-height:1.3;max-width:100%;overflow:hidden;padding:8px 14px;text-overflow:ellipsis;transition:background .15s ease,opacity .15s ease;white-space:nowrap}.ai-chat-widget.dark .ai-chat-suggested-question{background:var(--primary-color,var(--button-color,var(--user-bg,#484848)));color:var(--button-icon-color,var(--user-text,#fff))}.ai-chat-suggested-question-text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ai-chat-suggested-question:hover{filter:brightness(.9)}.ai-chat-widget.dark .ai-chat-suggested-question:hover{filter:brightness(1.15)}.ai-chat-suggested-question:active{transform:scale(.98)}.ai-chat-suggested-question-icon{display:none}.ai-chat-feedback-buttons{align-items:center;display:flex;gap:var(--space-xs)}.ai-chat-feedback{align-items:center;display:inline-flex;gap:0;height:20px}.ai-chat-feedback-button{align-items:center;background:transparent!important;border:none;border-radius:var(--radius-sm);color:var(--text-placeholder);cursor:pointer;display:flex;font-size:var(--text-sm);height:20px;justify-content:center;padding:var(--space-xs);transition:all var(--duration-fast) var(--spring-bounce)}.ai-chat-feedback-button:hover:not(:disabled){background:none!important;color:var(--text-secondary)}.ai-chat-feedback-button:active:not(:disabled){transform:scale(.9)}.ai-chat-feedback-button:disabled{cursor:not-allowed;opacity:.4}.ai-chat-feedback-button.active{background:none!important;color:var(--text-primary)}.ai-chat-feedback-submitted{align-items:center;animation:feedbackMorph .3s var(--spring-bounce);display:flex;gap:6px}.ai-chat-feedback-checkmark{animation:checkmarkPop .3s var(--spring-bounce);color:#10b981;font-size:var(--text-md);font-weight:700}.ai-chat-feedback-text{color:#10b981;font-size:var(--text-sm);font-weight:var(--font-weight-medium)}.ai-chat-history-panel{background:var(--bg-primary,#fff);display:flex;flex:1;flex-direction:column;overflow:hidden}.ai-chat-widget.dark .ai-chat-history-panel{background:var(--bg-primary,#18181b)}.ai-chat-history-empty,.ai-chat-history-loading{align-items:center;color:var(--text-muted);display:flex;flex:1;font-size:var(--text-sm);justify-content:center;padding:var(--space-lg);text-align:center}.ai-chat-history-list{-ms-overflow-style:none;display:flex;flex:1;flex-direction:column;gap:var(--space-sm);overflow-y:auto;padding:var(--space-md) var(--space-md) 120px;scrollbar-width:none}.ai-chat-history-list::-webkit-scrollbar{display:none}.ai-chat-history-list.exiting{animation:ai-chat-history-exit .22s var(--spring-smooth) forwards}.ai-chat-history-item{align-items:center;background:var(--user-bg,#f4f4f5);border-radius:var(--radius-history-item,15px);display:flex;flex-direction:row;margin:0;overflow:hidden;transition:background var(--duration-fast,.15s) ease;width:100%}.ai-chat-history-item-content{align-items:center;background:transparent;border:none;cursor:pointer;display:flex;flex:1;flex-direction:row;height:36px;min-width:0;padding:0 3px 0 16px;text-align:left}.ai-chat-widget.dark .ai-chat-history-item{background:var(--user-bg,#27272a)}.ai-chat-history-item:hover{background:var(--user-bg-hover,#e5e7eb)}.ai-chat-widget.dark .ai-chat-history-item:hover{background:var(--user-bg-hover,#3f3f46)}.ai-chat-history-item.active{background:var(--user-bg-hover,#e5e7eb)}.ai-chat-widget.dark .ai-chat-history-item.active{background:var(--user-bg-hover,#3f3f46)}.ai-chat-history-item-preview{color:var(--text-primary,#18181b);flex:1;font-size:var(--text-sm,14px);font-weight:var(--font-weight-medium,500);line-height:var(--line-height-normal,1.4);margin-bottom:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ai-chat-widget.dark .ai-chat-history-item-preview{color:var(--text-primary,#fafafa)}.ai-chat-history-item.active .ai-chat-history-item-preview{font-weight:var(--font-weight-medium)}.ai-chat-history-item-meta{display:none}.ai-chat-history-item-delete{align-items:center;background:transparent;border:none;color:var(--text-muted,#71717a);cursor:pointer;display:flex;flex-shrink:0;height:32px;justify-content:center;margin-right:4px;opacity:0;transition:opacity var(--duration-fast,.15s) ease,color var(--duration-fast,.15s) ease;width:32px}.ai-chat-history-item:hover .ai-chat-history-item-delete{opacity:1}.ai-chat-history-item-delete:hover{color:var(--text-primary,#18181b)}.ai-chat-widget.dark .ai-chat-history-item-delete:hover{color:var(--text-primary,#fafafa)}.ai-chat-tool-row{align-items:center;display:flex;gap:10px;margin:2px 0}.ai-chat-tool-gear{color:var(--text-primary);flex-shrink:0;height:20px;width:20px}.ai-chat-tool-gear.spinning{animation:ai-chat-gear-spin 1.5s linear infinite}.ai-chat-tool-badges{align-items:center;display:flex;flex-wrap:wrap;gap:8px}.ai-chat-tool-badge{align-items:center;border-radius:var(--radius-action-badge,3px);display:inline-flex;font-size:12px;font-weight:500;gap:4px;line-height:1.2;padding:5px 12px;transition:all .2s ease;white-space:nowrap}.ai-chat-tool-badge.loading{animation:ai-chat-tool-gradient 2s linear infinite;background:linear-gradient(90deg,var(--tool-loading-bg-1,#e0e0e0) 0,var(--tool-loading-bg-2,#f0f0f0) 25%,var(--tool-loading-bg-3,#fff) 50%,var(--tool-loading-bg-2,#f0f0f0) 75%,var(--tool-loading-bg-1,#e0e0e0) 100%);background-size:200% 100%;color:var(--tool-loading-text,#1a1a1a);position:relative}.ai-chat-widget:not(.dark) .ai-chat-tool-badge.loading{--tool-loading-bg-1:#2a2a2a;--tool-loading-bg-2:#3a3a3a;--tool-loading-bg-3:#4a4a4a;--tool-loading-text:#fff}.ai-chat-tool-badge.completed{background:var(--tool-completed-bg,hsla(0,0%,100%,.12));color:var(--tool-completed-text,hsla(0,0%,100%,.9))}.ai-chat-widget:not(.dark) .ai-chat-tool-badge.completed{--tool-completed-bg:rgba(0,0,0,.08);--tool-completed-text:rgba(0,0,0,.8)}.ai-chat-tool-badge.error{background:var(--tool-error-bg,rgba(239,68,68,.15));color:var(--tool-error-text,#ef4444)}.ai-chat-tool-badge .ai-chat-tool-check{color:#22c55e;flex-shrink:0}.ai-chat-tool-badge .ai-chat-tool-error{color:#ef4444;flex-shrink:0}.tool-name{font-weight:500;line-height:1.2;white-space:nowrap}@keyframes ai-chat-gear-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@keyframes ai-chat-tool-gradient{0%{background-position:200% 0}to{background-position:-200% 0}}@keyframes ai-chat-tool-check-appear{0%{opacity:0;transform:scale(.5)}to{opacity:.7;transform:scale(1)}}.ai-chat-sources{background:rgba(0,0,0,.02);border-radius:6px;font-size:var(--text-xs);margin-top:var(--space-sm);overflow:hidden}.ai-chat-sources-toggle{align-items:center;background:none;border:none;cursor:pointer;display:flex;gap:6px;padding:var(--space-sm) 10px;text-align:left;transition:background var(--duration-fast) ease;width:100%}.ai-chat-sources-toggle:hover{background:rgba(0,0,0,.03)}.ai-chat-sources-icon{color:var(--text-muted);font-size:10px;transition:transform var(--duration-fast) ease}.ai-chat-sources-title{color:var(--text-primary);flex:1;font-size:11px;font-weight:var(--font-weight-semibold);letter-spacing:.5px;text-transform:uppercase}.ai-chat-source-item{border-top:1px solid rgba(0,0,0,.05);color:var(--text-muted);display:flex;gap:var(--space-sm);padding:var(--space-sm) 10px}.ai-chat-source-item:last-child{border-bottom:none}.ai-chat-source-number{color:var(--btn-primary-bg);flex-shrink:0;font-weight:var(--font-weight-semibold)}.ai-chat-source-details{display:flex;flex:1;flex-direction:column;gap:var(--space-xs)}.ai-chat-source-score{color:var(--text-placeholder);font-size:11px}.ai-chat-source-content{color:var(--text-muted);font-size:11px;font-style:italic;line-height:var(--line-height-normal)}.ai-chat-source-metadata{display:flex;flex-wrap:wrap;gap:6px;margin-top:2px}.ai-chat-source-meta-item{background:rgba(0,0,0,.05);border-radius:3px;color:var(--text-muted);font-size:10px;padding:2px 6px}";
|
|
29113
|
+
var css_248z = ".ai-chat-widget{--radius-sm:4px;--radius-md:8px;--radius-lg:12px;--radius-xl:16px;--radius-2xl:18px;--radius-pill:9999px;--radius-window-top:22px;--radius-window-bottom:44px;--radius-window-gutter:16px;--radius-chat-bubble:14px;--radius-preset-badge:13px;--radius-history-item:14px;--radius-action-badge:8px;--radius-input:62px;--space-xs:4px;--space-sm:8px;--space-md:16px;--space-lg:24px;--space-xl:32px;--text-xs:12px;--text-sm:14px;--text-md:15px;--text-lg:18px;--text-xl:22px;--text-2xl:28px;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--line-height-tight:1.3;--line-height-normal:1.4;--line-height-relaxed:1.6;--bg-primary:#fff;--bg-secondary:#f4f4f4;--bg-tertiary:#e5e7eb;--bg-hover:#e5e7eb;--text-primary:#3e3e3e;--text-secondary:#000;--text-muted:#71717a;--text-placeholder:#a1a1aa;--border-default:#d3d3d3;--border-subtle:#e5e7eb;--border-muted:#f4f4f5;--user-bg:#f4f3f0;--user-text:#000;--user-bg-hover:#e8e7e4;--agent-bg:transparent;--agent-text:#000;--input-bg:#f4f4f4;--input-border:#d3d3d3;--input-text:#000;--btn-primary-bg:#151515;--btn-primary-text:#f4f4f4;--btn-secondary-bg:transparent;--btn-secondary-text:#71717a;--spring-bounce:cubic-bezier(0.34,1.56,0.64,1);--spring-smooth:cubic-bezier(0.4,0,0.2,1);--spring-snappy:cubic-bezier(0.2,0,0,1);--duration-fast:0.15s;--duration-normal:0.25s;--duration-slow:0.35s;--shadow-sm:0 1px 2px rgba(0,0,0,.05);--shadow-md:0 2px 8px rgba(0,0,0,.1);--shadow-lg:0 4px 16px rgba(0,0,0,.12);--shadow-window:0px 0px 15px 9px rgba(0,0,0,.1);--shadow-button:0px 0px 15px 9px rgba(0,0,0,.03)}.ai-chat-widget.dark{--bg-primary:#282625;--bg-secondary:#4a4846;--bg-tertiary:#484848;--bg-hover:#484848;--text-primary:#fff;--text-secondary:#fff;--text-muted:#a1a1aa;--text-placeholder:#71717a;--border-default:#5d5b5b;--border-subtle:#5d5b5b;--border-muted:#5d5b5b;--user-bg:#484848;--user-text:#fff;--user-bg-hover:#5a5a5a;--agent-bg:transparent;--agent-text:#fff;--input-bg:#4a4846;--input-border:#5d5b5b;--input-text:#fff;--btn-primary-bg:#fff;--btn-primary-text:#312f2d;--btn-secondary-bg:transparent;--btn-secondary-text:#a1a1aa;--shadow-window:0px 0px 15px 9px rgba(0,0,0,.2);--shadow-button:0px 0px 15px 9px rgba(0,0,0,.03);--shadow-input:0px 0px 10px rgba(0,0,0,.15)}.ai-chat-widget{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}.ai-chat-widget-container{font-size:var(--text-sm);line-height:1.5;position:fixed;z-index:var(--widget-z-index,9999)}.ai-chat-widget-container.bottom-right{bottom:20px;right:20px}.ai-chat-widget-container.bottom-left{bottom:20px;left:20px}.ai-chat-widget-container.top-right{right:20px;top:20px}.ai-chat-widget-container.top-left{left:20px;top:20px}@keyframes windowOpen{0%{opacity:0;transform:scale(.9) translateY(20px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes windowClose{0%{opacity:1;transform:scale(1) translateY(0)}to{opacity:0;transform:scale(.9) translateY(20px)}}@keyframes messageSlideIn{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}@keyframes welcomeFadeIn{0%{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}@keyframes typingPulse{0%,60%,to{opacity:.4;transform:translateY(0) scale(1)}30%{opacity:1;transform:translateY(-4px) scale(1.1)}}@keyframes ai-chat-tool-active{0%,to{background:var(--bg-secondary);opacity:1}50%{background:var(--bg-tertiary);opacity:1}}@keyframes feedbackMorph{0%{opacity:.5;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@keyframes checkmarkPop{0%{opacity:0;transform:scale(0) rotate(-45deg)}50%{transform:scale(1.3) rotate(0deg)}to{opacity:1;transform:scale(1) rotate(0deg)}}@keyframes ai-chat-history-exit{to{opacity:0;transform:translateY(-18px)}}@media (max-width:480px){.ai-chat-window{animation:mobileSlideUp var(--duration-normal) var(--spring-smooth);border-radius:0!important;bottom:0!important;height:100%!important;left:0!important;position:fixed!important;right:0!important;top:0!important;width:100%!important}@keyframes mobileSlideUp{0%{transform:translateY(100%)}to{transform:translateY(0)}}.ai-chat-header{padding-top:max(16px,env(safe-area-inset-top))}.ai-chat-input-container{padding-bottom:max(20px,env(safe-area-inset-bottom))}}.ai-chat-button{align-items:center;background:var(--button-color,var(--btn-primary-bg));border:1px solid var(--button-border-color,var(--button-color,var(--btn-primary-bg)));border-radius:50%;box-shadow:var(--shadow-button,0 0 15px 9px rgba(0,0,0,.03));color:var(--button-icon-color,var(--btn-primary-text));cursor:pointer;display:flex;height:var(--button-size,56px);justify-content:center;overflow:hidden;position:relative;transition:filter var(--duration-fast) ease,transform var(--duration-fast) ease;width:var(--button-size,56px);z-index:1}.ai-chat-button:hover{transform:scale(1.02)}.ai-chat-button:active{transform:scale(.98)}.ai-chat-button-svg{height:50%;min-height:24px;min-width:24px;transition:transform var(--duration-fast) ease;width:50%}.ai-chat-button.is-open .ai-chat-button-svg{transform:rotate(0deg)}.ai-chat-button-icon{font-size:1.5em;line-height:1}.ai-chat-window{animation:windowOpen var(--duration-slow,.35s) var(--spring-bounce,cubic-bezier(.34,1.56,.64,1));background:var(--bg-primary,#fff);border:1px solid var(--border-default,#d3d3d3);border-radius:var(--radius-window-top,22px) var(--radius-window-top,22px) var(--radius-window-bottom,44px) var(--radius-window-bottom,44px);box-shadow:var(--shadow-window,0 0 15px 5px rgba(0,0,0,.08));display:flex;flex-direction:column;overflow:hidden;position:absolute;transform-origin:bottom right;z-index:2}.ai-chat-widget.dark .ai-chat-window{background:var(--bg-primary,#282625);border-color:var(--border-default,#5d5b5b);border-width:.7px}.ai-chat-window.closing{animation:windowClose var(--duration-normal) var(--spring-smooth) forwards}.ai-chat-window.size-small{height:var(--window-height,580px);width:var(--window-width,380px)}.ai-chat-window.size-medium{height:var(--window-height,720px);width:var(--window-width,440px)}.ai-chat-window.size-large{height:var(--window-height,820px);width:var(--window-width,520px)}.ai-chat-widget-container.bottom-right .ai-chat-window{bottom:calc(var(--button-size, 56px) + 8px);right:0}.ai-chat-widget-container.bottom-left .ai-chat-window{bottom:calc(var(--button-size, 56px) + 8px);left:0}.ai-chat-widget-container.top-right .ai-chat-window{right:0;top:calc(var(--button-size, 56px) + 8px)}.ai-chat-widget-container.top-left .ai-chat-window{left:0;top:calc(var(--button-size, 56px) + 8px)}.ai-chat-header{align-items:center;background:var(--bg-primary,#fff);border-bottom:1px solid var(--border-default,#d3d3d3);border-radius:var(--radius-window-top,22px) var(--radius-window-top,22px) 0 0;display:flex;justify-content:space-between;padding:18px var(--space-md,16px);position:relative;z-index:10}.ai-chat-widget.dark .ai-chat-header{background:var(--bg-primary,#282625);border-bottom-color:var(--border-default,#5d5b5b);border-bottom-width:.7px}.ai-chat-header.is-history{padding-left:var(--space-md)}.ai-chat-header.is-history .ai-chat-title{flex:1;min-width:0;overflow:hidden;padding-right:var(--space-lg);text-overflow:ellipsis;white-space:nowrap}.ai-chat-header-content{align-items:center;display:flex;flex:1;gap:var(--space-lg)}.ai-chat-header-actions{align-items:center;display:flex;gap:var(--space-sm)}.ai-chat-logo{border-radius:10px;height:36px;object-fit:cover;width:36px}.ai-chat-title{color:var(--text-primary,#3e3e3e);font-size:var(--text-xl,22px);font-weight:var(--font-weight-bold,700);letter-spacing:-.02em}.ai-chat-widget.dark .ai-chat-title{color:var(--text-primary,#fff)}.ai-chat-close-button{align-items:center;background:transparent;border:none;border-radius:var(--radius-md);color:var(--text-primary);cursor:pointer;display:flex;height:32px;justify-content:center;padding:0;transition:color var(--duration-fast) ease;width:32px}.ai-chat-close-button:hover{color:var(--text-muted)}.ai-chat-close-button:active{transform:scale(.95)}.ai-chat-header-button{align-items:center;background:transparent;border:none;border-radius:var(--radius-md);color:var(--text-muted);cursor:pointer;display:flex;height:32px;justify-content:center;padding:0;transition:all var(--duration-fast) ease;width:32px}.ai-chat-header-button:hover{background:var(--bg-secondary);color:var(--text-secondary)}.ai-chat-header-button svg{height:18px;width:18px}.ai-chat-messages{-webkit-overflow-scrolling:touch;-ms-overflow-style:none;align-items:stretch;background:var(--bg-primary,#fff);display:flex;flex:1;flex-direction:column;gap:var(--space-md,16px);justify-content:flex-start;overflow-x:hidden;overflow-y:auto;padding:var(--space-lg,24px) var(--space-md,16px) 100px;position:relative;scroll-behavior:smooth;scrollbar-width:none}.ai-chat-widget.dark .ai-chat-messages{background:var(--bg-primary,#18181b)}.ai-chat-messages::-webkit-scrollbar{display:none}.ai-chat-message{animation:messageSlideIn var(--duration-normal) var(--spring-bounce);display:flex;flex-direction:column;gap:6px}.ai-chat-message-content{word-wrap:break-word;font-size:var(--text-md);line-height:var(--line-height-relaxed);max-width:85%}.ai-chat-message.user{align-items:flex-end}.ai-chat-message.user .ai-chat-message-content{background:var(--user-bg,#f4f3f0);border:none;border-radius:var(--radius-chat-bubble,15px);box-shadow:none;color:var(--user-text,#000);padding:var(--space-sm,8px) var(--space-md,16px)}.ai-chat-widget.dark .ai-chat-message.user .ai-chat-message-content{background:var(--user-bg,#484848);color:var(--user-text,#fff)}.ai-chat-message.user .ai-chat-message-meta{justify-content:flex-end;padding-right:var(--space-xs)}.ai-chat-message.assistant{align-items:flex-start}.ai-chat-message.assistant .ai-chat-message-content{background:var(--agent-bg,transparent);border:none;color:var(--agent-text,#18181b);padding:0}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content{color:var(--agent-text,#fafafa)}.ai-chat-message.system{align-items:center}.ai-chat-message.system .ai-chat-message-content{background:hsla(48,96%,89%,.8);border-radius:var(--radius-md);color:#92400e;font-size:var(--text-xs);font-style:italic;max-width:90%;padding:var(--space-sm) var(--space-md);text-align:center}.ai-chat-widget.dark .ai-chat-message.system .ai-chat-message-content{background:rgba(120,53,15,.5);color:#fef3c7}.ai-chat-message.tool{align-items:flex-start}.ai-chat-message.tool .ai-chat-message-content{background:rgba(219,234,254,.8);border-radius:var(--radius-chat-bubble);border-bottom-left-radius:var(--radius-xs);color:#1e40af;font-family:Courier New,monospace;font-size:var(--text-sm);padding:var(--space-sm) var(--space-md)}.ai-chat-widget.dark .ai-chat-message.tool .ai-chat-message-content{background:rgba(30,58,138,.5);color:#dbeafe}.ai-chat-message-meta{align-items:center;color:var(--text-muted);display:flex;font-size:var(--text-xs);gap:var(--space-sm);padding-left:var(--space-xs)}.ai-chat-message-timestamp{font-size:var(--text-xs);line-height:1}.ai-chat-typing{align-items:center;animation:messageSlideIn var(--duration-normal) var(--spring-bounce);background:transparent;display:flex;gap:5px;padding:0}.ai-chat-typing-dot{animation:typingPulse 1.4s ease-in-out infinite;background:var(--text-muted);border-radius:50%;height:6px;width:6px}.ai-chat-typing-dot:nth-child(2){animation-delay:.15s}.ai-chat-typing-dot:nth-child(3){animation-delay:.3s}.ai-chat-welcome{align-items:stretch;animation:welcomeFadeIn var(--duration-slow) var(--spring-smooth);display:flex;flex-direction:column;justify-content:flex-start;padding:0;text-align:left}.ai-chat-welcome-text,.ai-chat-welcome-title{align-self:flex-start}.ai-chat-welcome-title{color:var(--text-primary);font-size:var(--text-2xl);font-weight:var(--font-weight-semibold);letter-spacing:-.02em;margin-bottom:var(--space-md)}.ai-chat-welcome-text{color:var(--text-secondary);font-size:var(--text-md);line-height:var(--line-height-relaxed);max-width:100%}.ai-chat-error{align-items:flex-start;align-self:center;background:var(--bg-secondary);border:none;border-radius:var(--radius-chat-bubble);color:var(--text-primary);display:flex;font-size:var(--text-md);font-weight:var(--font-weight-normal);gap:10px;line-height:1.5;margin:0 auto;max-width:90%;padding:10px var(--space-md)}.ai-chat-error:before{align-items:center;background:rgba(239,68,68,.15);border-radius:50%;color:#ef4444;content:\"⚠\";display:flex;flex-shrink:0;font-size:var(--text-xs);font-weight:700;height:18px;justify-content:center;margin-top:2px;width:18px}.ai-chat-widget.dark .ai-chat-error:before{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-message.assistant .ai-chat-message-content p{margin:0 0 12px}.ai-chat-message.assistant .ai-chat-message-content p:last-child{margin-bottom:0}.ai-chat-message.assistant .ai-chat-message-content ol,.ai-chat-message.assistant .ai-chat-message-content ul{margin:8px 0 12px;padding-left:24px}.ai-chat-message.assistant .ai-chat-message-content li{line-height:1.5;margin:6px 0}.ai-chat-message.assistant .ai-chat-message-content strong{font-weight:var(--font-weight-semibold)}.ai-chat-message.assistant .ai-chat-message-content em{font-style:italic}.ai-chat-message.assistant .ai-chat-message-content code{background:rgba(0,0,0,.06);border-radius:var(--radius-sm);font-family:SF Mono,Consolas,Monaco,monospace;font-size:.9em;padding:2px 6px}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content code{background:hsla(0,0%,100%,.1)}.ai-chat-message.assistant .ai-chat-message-content pre{background:rgba(0,0,0,.06);border-radius:var(--radius-md);margin:8px 0 12px;overflow-x:auto;padding:var(--space-sm)}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content pre{background:hsla(0,0%,100%,.08)}.ai-chat-message.assistant .ai-chat-message-content pre code{background:transparent;border-radius:0;padding:0}.ai-chat-message.assistant .ai-chat-message-content blockquote{border-left:3px solid var(--btn-primary-bg);color:var(--text-muted);margin:8px 0 12px;padding:4px 0 4px 12px}.ai-chat-message.assistant .ai-chat-message-content a{color:var(--btn-primary-bg);text-decoration:underline}.ai-chat-message.assistant .ai-chat-message-content a:hover{opacity:.8}.ai-chat-message.assistant .ai-chat-message-content h1,.ai-chat-message.assistant .ai-chat-message-content h2,.ai-chat-message.assistant .ai-chat-message-content h3,.ai-chat-message.assistant .ai-chat-message-content h4,.ai-chat-message.assistant .ai-chat-message-content h5,.ai-chat-message.assistant .ai-chat-message-content h6{font-weight:var(--font-weight-semibold);line-height:var(--line-height-tight);margin:16px 0 8px}.ai-chat-message.assistant .ai-chat-message-content h1:first-child,.ai-chat-message.assistant .ai-chat-message-content h2:first-child,.ai-chat-message.assistant .ai-chat-message-content h3:first-child{margin-top:0}.ai-chat-message.assistant .ai-chat-message-content hr{border:none;border-top:1px solid var(--border-subtle);margin:12px 0}.ai-chat-input-container{background:linear-gradient(to bottom,transparent 0,var(--bg-primary,#fff) 50%,var(--bg-primary,#fff) 100%);bottom:0;left:0;padding-top:30px;position:absolute;right:0;z-index:10}.ai-chat-widget.dark .ai-chat-input-container{background:linear-gradient(to bottom,transparent 0,var(--bg-primary,#282625) 50%,var(--bg-primary,#282625) 100%)}.ai-chat-input-container.separate{padding:0 var(--radius-window-gutter,16px) var(--radius-window-gutter,16px);padding-top:30px}.ai-chat-input-wrapper{align-items:flex-start;background:var(--input-bg,#f4f4f4);border:1px solid var(--input-border,#d3d3d3);border-radius:var(--radius-input,62px);display:flex;gap:0;height:52px;padding:6px 6px 6px 12px;position:relative;transition:all var(--duration-fast,.15s) ease;z-index:5}.ai-chat-widget.dark .ai-chat-input-wrapper{background:var(--input-bg,#4a4846);border-color:var(--input-border,#5d5b5b);border-width:.7px;box-shadow:var(--shadow-input,0 0 10px rgba(0,0,0,.15))}.ai-chat-input-wrapper:focus-within{border-color:var(--text-muted,#a1a1aa)}.ai-chat-input{word-wrap:break-word;background:transparent;border:none;box-sizing:border-box;color:var(--input-text,#000);flex:1;font-family:inherit;font-size:var(--text-md,15px);height:40px;line-height:20px;max-height:40px;min-height:40px;min-width:0;outline:none;overflow-wrap:anywhere;overflow-x:hidden;overflow-y:auto;padding:10px var(--space-sm,8px);resize:none;white-space:pre-wrap;width:0;word-break:break-word}.ai-chat-widget.dark .ai-chat-input{color:var(--input-text,#fff)}.ai-chat-input::placeholder{color:var(--text-placeholder,#a1a1aa)}.ai-chat-widget.dark .ai-chat-input::placeholder{color:var(--text-placeholder,#52525b)}.ai-chat-file-button{align-items:center;align-self:center;background:transparent;border:none;color:var(--text-placeholder);cursor:pointer;display:flex;flex-shrink:0;height:28px;justify-content:center;padding:0;transition:color var(--duration-fast) ease;width:28px}.ai-chat-file-button:hover{color:var(--text-secondary)}.ai-chat-file-button:disabled{cursor:not-allowed;opacity:.5}.ai-chat-send-button{align-items:center;align-self:center;background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#151515))));border:none;border-radius:50%;color:var(--button-icon-color,var(--btn-primary-text,#f4f4f4));cursor:pointer;display:flex;flex-shrink:0;height:40px;justify-content:center;padding:0;transition:all var(--duration-fast,.15s) ease;width:40px}.ai-chat-widget.dark .ai-chat-send-button{background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#fff))));color:var(--button-icon-color,var(--btn-primary-text,#312f2d))}.ai-chat-send-button.active{background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#151515))));color:var(--button-icon-color,var(--btn-primary-text,#f4f4f4))}.ai-chat-widget.dark .ai-chat-send-button.active{background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#fff))));color:var(--button-icon-color,var(--btn-primary-text,#312f2d))}.ai-chat-send-button:hover:not(:disabled){opacity:.8}.ai-chat-send-button:active:not(:disabled){transform:scale(.95)}.ai-chat-send-button:disabled{cursor:not-allowed;opacity:.3}.ai-chat-file-list{display:flex;flex-wrap:wrap;gap:var(--space-sm);padding:var(--space-sm) var(--space-sm)}.ai-chat-file-item{align-items:center;background:rgba(0,0,0,.05);border-radius:6px;display:flex;font-size:var(--text-xs);gap:var(--space-sm);padding:6px 10px}.ai-chat-file-extension{background:var(--btn-primary-bg);border-radius:3px;color:var(--btn-primary-text);display:inline-block;font-size:10px;font-weight:var(--font-weight-semibold);min-width:40px;padding:2px 6px;text-align:center;text-transform:uppercase}.ai-chat-file-info{display:flex;flex:1;flex-direction:column;gap:2px;min-width:0}.ai-chat-file-name{font-weight:var(--font-weight-medium);max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ai-chat-file-size{color:var(--text-muted);font-size:10px;opacity:.7}.ai-chat-file-remove{align-items:center;background:none;border:none;color:inherit;cursor:pointer;display:flex;justify-content:center;opacity:.5;padding:var(--space-xs);transition:opacity var(--duration-fast) ease}.ai-chat-file-remove:hover{opacity:1}.ai-chat-suggested-questions{align-self:flex-end;margin:0;padding:16px 0 0;width:100%}.ai-chat-suggested-questions-list{align-items:center;display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end}.ai-chat-suggested-question{background:var(--primary-color,var(--button-color,var(--user-bg,#f4f3f0)));border:none;border-radius:var(--radius-preset-badge,13px);color:var(--button-icon-color,var(--user-text,#000));cursor:pointer;font-size:14px;font-weight:400;line-height:1.3;max-width:100%;overflow:hidden;padding:8px 14px;text-overflow:ellipsis;transition:background .15s ease,opacity .15s ease;white-space:nowrap}.ai-chat-widget.dark .ai-chat-suggested-question{background:var(--primary-color,var(--button-color,var(--user-bg,#484848)));color:var(--button-icon-color,var(--user-text,#fff))}.ai-chat-suggested-question-text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ai-chat-suggested-question:hover{filter:brightness(.9)}.ai-chat-widget.dark .ai-chat-suggested-question:hover{filter:brightness(1.15)}.ai-chat-suggested-question:active{transform:scale(.98)}.ai-chat-suggested-question-icon{display:none}.ai-chat-feedback-buttons{align-items:center;display:flex;gap:var(--space-xs)}.ai-chat-feedback{align-items:center;display:inline-flex;gap:0;height:20px}.ai-chat-feedback-button{align-items:center;background:transparent!important;border:none;border-radius:var(--radius-sm);color:var(--text-placeholder);cursor:pointer;display:flex;font-size:var(--text-sm);height:20px;justify-content:center;padding:var(--space-xs);transition:all var(--duration-fast) var(--spring-bounce)}.ai-chat-feedback-button:hover:not(:disabled){background:none!important;color:var(--text-secondary)}.ai-chat-feedback-button:active:not(:disabled){transform:scale(.9)}.ai-chat-feedback-button:disabled{cursor:not-allowed;opacity:.4}.ai-chat-feedback-button.active{background:none!important;color:var(--text-primary)}.ai-chat-feedback-submitted{align-items:center;animation:feedbackMorph .3s var(--spring-bounce);display:flex;gap:6px}.ai-chat-feedback-checkmark{animation:checkmarkPop .3s var(--spring-bounce);color:#10b981;font-size:var(--text-md);font-weight:700}.ai-chat-feedback-text{color:#10b981;font-size:var(--text-sm);font-weight:var(--font-weight-medium)}.ai-chat-history-panel{background:var(--bg-primary,#fff);display:flex;flex:1;flex-direction:column;overflow:hidden}.ai-chat-widget.dark .ai-chat-history-panel{background:var(--bg-primary,#18181b)}.ai-chat-history-empty,.ai-chat-history-loading{align-items:center;color:var(--text-muted);display:flex;flex:1;font-size:var(--text-sm);justify-content:center;padding:var(--space-lg);text-align:center}.ai-chat-history-list{-ms-overflow-style:none;display:flex;flex:1;flex-direction:column;gap:var(--space-sm);overflow-y:auto;padding:var(--space-md) var(--space-md) 120px;scrollbar-width:none}.ai-chat-history-list::-webkit-scrollbar{display:none}.ai-chat-history-list.exiting{animation:ai-chat-history-exit .22s var(--spring-smooth) forwards}.ai-chat-history-item{align-items:center;background:var(--user-bg,#f4f4f5);border-radius:var(--radius-history-item,15px);display:flex;flex:0 0 auto;flex-direction:row;height:var(--history-item-height,44px);margin:0;overflow:hidden;transition:background var(--duration-fast,.15s) ease;width:100%}.ai-chat-history-item-content{align-items:center;background:transparent;border:none;cursor:pointer;display:flex;flex:1;flex-direction:row;height:100%;min-width:0;padding:0 3px 0 16px;text-align:left}.ai-chat-widget.dark .ai-chat-history-item{background:var(--user-bg,#27272a)}.ai-chat-history-item:hover{background:var(--user-bg-hover,#e5e7eb)}.ai-chat-widget.dark .ai-chat-history-item:hover{background:var(--user-bg-hover,#3f3f46)}.ai-chat-history-item.active{background:var(--user-bg-hover,#e5e7eb)}.ai-chat-widget.dark .ai-chat-history-item.active{background:var(--user-bg-hover,#3f3f46)}.ai-chat-history-item-preview{color:var(--text-primary,#18181b);flex:1;font-size:var(--text-sm,14px);font-weight:var(--font-weight-medium,500);line-height:var(--line-height-normal,1.4);margin-bottom:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ai-chat-widget.dark .ai-chat-history-item-preview{color:var(--text-primary,#fafafa)}.ai-chat-history-item.active .ai-chat-history-item-preview{font-weight:var(--font-weight-medium)}.ai-chat-history-item-meta{display:none}.ai-chat-history-item-delete{align-items:center;background:transparent;border:none;color:var(--text-muted,#71717a);cursor:pointer;display:flex;flex-shrink:0;height:32px;justify-content:center;margin-right:4px;opacity:0;transition:opacity var(--duration-fast,.15s) ease,color var(--duration-fast,.15s) ease;width:32px}.ai-chat-history-item:hover .ai-chat-history-item-delete{opacity:1}.ai-chat-history-item-delete:hover{color:var(--text-primary,#18181b)}.ai-chat-widget.dark .ai-chat-history-item-delete:hover{color:var(--text-primary,#fafafa)}.ai-chat-tool-row{align-items:center;display:flex;gap:10px;margin:2px 0}.ai-chat-tool-gear{color:var(--text-primary);flex-shrink:0;height:20px;width:20px}.ai-chat-tool-gear.spinning{animation:ai-chat-gear-spin 1.5s linear infinite}.ai-chat-tool-badges{align-items:center;display:flex;flex-wrap:wrap;gap:8px}.ai-chat-tool-badge{align-items:center;border-radius:var(--radius-action-badge,3px);display:inline-flex;font-size:12px;font-weight:500;gap:4px;line-height:1.2;padding:5px 12px;transition:all .2s ease;white-space:nowrap}.ai-chat-tool-badge.loading{animation:ai-chat-tool-gradient 2s linear infinite;background:linear-gradient(90deg,var(--tool-loading-bg-1,#e0e0e0) 0,var(--tool-loading-bg-2,#f0f0f0) 25%,var(--tool-loading-bg-3,#fff) 50%,var(--tool-loading-bg-2,#f0f0f0) 75%,var(--tool-loading-bg-1,#e0e0e0) 100%);background-size:200% 100%;color:var(--tool-loading-text,#1a1a1a);position:relative}.ai-chat-widget:not(.dark) .ai-chat-tool-badge.loading{--tool-loading-bg-1:#2a2a2a;--tool-loading-bg-2:#3a3a3a;--tool-loading-bg-3:#4a4a4a;--tool-loading-text:#fff}.ai-chat-tool-badge.completed{background:var(--tool-completed-bg,hsla(0,0%,100%,.12));color:var(--tool-completed-text,hsla(0,0%,100%,.9))}.ai-chat-widget:not(.dark) .ai-chat-tool-badge.completed{--tool-completed-bg:rgba(0,0,0,.08);--tool-completed-text:rgba(0,0,0,.8)}.ai-chat-tool-badge.error{background:var(--tool-error-bg,rgba(239,68,68,.15));color:var(--tool-error-text,#ef4444)}.ai-chat-tool-badge .ai-chat-tool-check{color:#22c55e;flex-shrink:0}.ai-chat-tool-badge .ai-chat-tool-error{color:#ef4444;flex-shrink:0}.tool-name{font-weight:500;line-height:1.2;white-space:nowrap}@keyframes ai-chat-gear-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@keyframes ai-chat-tool-gradient{0%{background-position:200% 0}to{background-position:-200% 0}}@keyframes ai-chat-tool-check-appear{0%{opacity:0;transform:scale(.5)}to{opacity:.7;transform:scale(1)}}.ai-chat-sources{background:rgba(0,0,0,.02);border-radius:6px;font-size:var(--text-xs);margin-top:var(--space-sm);overflow:hidden}.ai-chat-sources-toggle{align-items:center;background:none;border:none;cursor:pointer;display:flex;gap:6px;padding:var(--space-sm) 10px;text-align:left;transition:background var(--duration-fast) ease;width:100%}.ai-chat-sources-toggle:hover{background:rgba(0,0,0,.03)}.ai-chat-sources-icon{color:var(--text-muted);font-size:10px;transition:transform var(--duration-fast) ease}.ai-chat-sources-title{color:var(--text-primary);flex:1;font-size:11px;font-weight:var(--font-weight-semibold);letter-spacing:.5px;text-transform:uppercase}.ai-chat-source-item{border-top:1px solid rgba(0,0,0,.05);color:var(--text-muted);display:flex;gap:var(--space-sm);padding:var(--space-sm) 10px}.ai-chat-source-item:last-child{border-bottom:none}.ai-chat-source-number{color:var(--btn-primary-bg);flex-shrink:0;font-weight:var(--font-weight-semibold)}.ai-chat-source-details{display:flex;flex:1;flex-direction:column;gap:var(--space-xs)}.ai-chat-source-score{color:var(--text-placeholder);font-size:11px}.ai-chat-source-content{color:var(--text-muted);font-size:11px;font-style:italic;line-height:var(--line-height-normal)}.ai-chat-source-metadata{display:flex;flex-wrap:wrap;gap:6px;margin-top:2px}.ai-chat-source-meta-item{background:rgba(0,0,0,.05);border-radius:3px;color:var(--text-muted);font-size:10px;padding:2px 6px}";
|
|
28597
29114
|
styleInject(css_248z);
|
|
28598
29115
|
|
|
28599
29116
|
// Icon components mapping
|
|
@@ -28697,16 +29214,6 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
28697
29214
|
mediaQuery.removeEventListener('change', handleMediaChange);
|
|
28698
29215
|
};
|
|
28699
29216
|
}, [config]);
|
|
28700
|
-
// Debug logging
|
|
28701
|
-
useEffect(() => {
|
|
28702
|
-
console.log('[ChatWidget] Config loaded:', config ? 'YES' : 'NO');
|
|
28703
|
-
if (config) {
|
|
28704
|
-
console.log('[ChatWidget] Config details:', {
|
|
28705
|
-
accentColor: config.appearance?.primaryColor,
|
|
28706
|
-
autoDetectedTheme,
|
|
28707
|
-
});
|
|
28708
|
-
}
|
|
28709
|
-
}, [config, autoDetectedTheme]);
|
|
28710
29217
|
// Handle auto-open
|
|
28711
29218
|
useEffect(() => {
|
|
28712
29219
|
if (config?.settings.autoOpen) {
|
|
@@ -28761,17 +29268,8 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
28761
29268
|
...customStyles,
|
|
28762
29269
|
...(zIndex !== undefined ? { '--widget-z-index': String(zIndex) } : {}),
|
|
28763
29270
|
};
|
|
28764
|
-
// Debug logging for theme and styles
|
|
28765
|
-
useEffect(() => {
|
|
28766
|
-
console.log('[ChatWidget] Theme info:', {
|
|
28767
|
-
effectiveTheme,
|
|
28768
|
-
autoDetectedTheme,
|
|
28769
|
-
accentColor,
|
|
28770
|
-
});
|
|
28771
|
-
}, [effectiveTheme, autoDetectedTheme, accentColor]);
|
|
28772
29271
|
const handleToggle = () => {
|
|
28773
29272
|
const newState = !isOpen;
|
|
28774
|
-
console.log('[ChatWidget] handleToggle called, setting isOpen to:', newState);
|
|
28775
29273
|
setIsOpen(newState);
|
|
28776
29274
|
if (newState) {
|
|
28777
29275
|
onOpen?.();
|
|
@@ -28786,10 +29284,8 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
28786
29284
|
// Don't render until config is loaded to avoid flash of unstyled content
|
|
28787
29285
|
// In preview mode, config is always available
|
|
28788
29286
|
if (!config && !previewMode) {
|
|
28789
|
-
console.log('[ChatWidget] Not rendering - config not loaded yet');
|
|
28790
29287
|
return null;
|
|
28791
29288
|
}
|
|
28792
|
-
console.log('[ChatWidget] Rendering widget', { isOpen, hasConfig: !!config });
|
|
28793
29289
|
// Get button icon based on state
|
|
28794
29290
|
const IconComponent = isOpen ? iconComponents.FiChevronDown : iconComponents.FiMessageCircle;
|
|
28795
29291
|
return (jsx("div", { ref: containerRef, className: `ai-chat-widget ${effectiveTheme}`, style: mergedStyles, children: jsxs("div", { ref: widgetRef, className: `ai-chat-widget-container ${effectivePosition}`, children: [isOpen && (jsx(ChatWindow, { messages: messages, isLoading: isLoading, isTyping: isTyping, error: error, config: config, onSendMessage: sendMessage, onClose: handleToggle, onFeedback: handleFeedback,
|