@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.js
CHANGED
|
@@ -3,13 +3,53 @@
|
|
|
3
3
|
var jsxRuntime = require('react/jsx-runtime');
|
|
4
4
|
var react = require('react');
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
async function* parseSSEStream(response, validator) {
|
|
7
|
+
if (!response.body) {
|
|
8
|
+
throw new Error("Response body is null");
|
|
9
|
+
}
|
|
10
|
+
const reader = response.body.getReader();
|
|
11
|
+
const decoder = new TextDecoder();
|
|
12
|
+
let buffer = "";
|
|
13
|
+
try {
|
|
14
|
+
while (true) {
|
|
15
|
+
const { done, value } = await reader.read();
|
|
16
|
+
if (done)
|
|
17
|
+
break;
|
|
18
|
+
buffer += decoder.decode(value, { stream: true });
|
|
19
|
+
const chunks = buffer.split("\n\n");
|
|
20
|
+
buffer = chunks.pop() || "";
|
|
21
|
+
for (const chunk of chunks) {
|
|
22
|
+
const lines = chunk.split("\n");
|
|
23
|
+
for (const line of lines) {
|
|
24
|
+
if (line.startsWith("data: ")) {
|
|
25
|
+
try {
|
|
26
|
+
const data = JSON.parse(line.slice(6));
|
|
27
|
+
if (validator) {
|
|
28
|
+
if (validator(data)) {
|
|
29
|
+
yield data;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
console.warn("[SSE Parser] Data failed validation:", data);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
yield data;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
console.error("[SSE Parser] Failed to parse SSE data:", line, e);
|
|
41
|
+
throw e;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
reader.releaseLock();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
13
53
|
class ApiError extends Error {
|
|
14
54
|
constructor(message, status, options) {
|
|
15
55
|
super(message);
|
|
@@ -138,12 +178,8 @@ class WidgetApiClient {
|
|
|
138
178
|
const result = await response.json();
|
|
139
179
|
return result.file;
|
|
140
180
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
* Returns ConversationMessage[] array
|
|
144
|
-
*/
|
|
145
|
-
async sendMessage(conversationId, message, fileIds) {
|
|
146
|
-
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/chat`, {
|
|
181
|
+
async *sendAgentMessageStream(conversationId, message, fileIds) {
|
|
182
|
+
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/agent`, {
|
|
147
183
|
method: 'POST',
|
|
148
184
|
headers: {
|
|
149
185
|
'Content-Type': 'application/json',
|
|
@@ -156,167 +192,36 @@ class WidgetApiClient {
|
|
|
156
192
|
}),
|
|
157
193
|
});
|
|
158
194
|
if (!response.ok) {
|
|
159
|
-
throw await buildApiError(response, 'Failed to send message');
|
|
160
|
-
}
|
|
161
|
-
return response.json();
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* Send a message to agent (with actions support)
|
|
165
|
-
*/
|
|
166
|
-
async sendAgentMessage(conversationId, message, fileIds) {
|
|
167
|
-
try {
|
|
168
|
-
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/agent`, {
|
|
169
|
-
method: 'POST',
|
|
170
|
-
headers: {
|
|
171
|
-
'Content-Type': 'application/json',
|
|
172
|
-
},
|
|
173
|
-
body: JSON.stringify({
|
|
174
|
-
conversationId: conversationId,
|
|
175
|
-
message,
|
|
176
|
-
fileIds,
|
|
177
|
-
timeZone: this.getTimeZone(),
|
|
178
|
-
}),
|
|
179
|
-
});
|
|
180
|
-
if (!response.ok) {
|
|
181
|
-
throw await buildApiError(response, `Agent request failed with status ${response.status}`);
|
|
182
|
-
}
|
|
183
|
-
const data = await response.json();
|
|
184
|
-
// Check if response indicates an error
|
|
185
|
-
if (data.type === 'error') {
|
|
186
|
-
throw new Error(data.message || 'Agent encountered an error');
|
|
187
|
-
}
|
|
188
|
-
return data;
|
|
189
|
-
}
|
|
190
|
-
catch (error) {
|
|
191
|
-
// Enhance error messages
|
|
192
|
-
if (error instanceof TypeError && error.message.includes('fetch')) {
|
|
193
|
-
throw new ApiError('Network error: Unable to reach the server', 0);
|
|
194
|
-
}
|
|
195
|
-
throw error;
|
|
195
|
+
throw await buildApiError(response, 'Failed to send agent message');
|
|
196
196
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
* Stream chat message responses
|
|
200
|
-
*/
|
|
201
|
-
async *sendMessageStream(conversationId, message, fileIds) {
|
|
202
|
-
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/chat`, {
|
|
203
|
-
method: 'POST',
|
|
204
|
-
headers: {
|
|
205
|
-
'Content-Type': 'application/json',
|
|
206
|
-
},
|
|
207
|
-
body: JSON.stringify({
|
|
208
|
-
conversationId: conversationId,
|
|
209
|
-
message,
|
|
210
|
-
fileIds,
|
|
211
|
-
timeZone: this.getTimeZone(),
|
|
212
|
-
}),
|
|
197
|
+
yield* parseSSEStream(response, (data) => {
|
|
198
|
+
return typeof data === 'object' && data !== null && 'type' in data;
|
|
213
199
|
});
|
|
214
|
-
if (!response.ok) {
|
|
215
|
-
throw await buildApiError(response, 'Failed to send message');
|
|
216
|
-
}
|
|
217
|
-
if (!response.body) {
|
|
218
|
-
throw new Error('Response body is null');
|
|
219
|
-
}
|
|
220
|
-
const reader = response.body.getReader();
|
|
221
|
-
const decoder = new TextDecoder();
|
|
222
|
-
let buffer = '';
|
|
223
|
-
try {
|
|
224
|
-
while (true) {
|
|
225
|
-
const { done, value } = await reader.read();
|
|
226
|
-
if (done)
|
|
227
|
-
break;
|
|
228
|
-
buffer += decoder.decode(value, { stream: true });
|
|
229
|
-
const lines = buffer.split('\n');
|
|
230
|
-
buffer = lines.pop() || '';
|
|
231
|
-
for (const line of lines) {
|
|
232
|
-
if (line.startsWith('data: ')) {
|
|
233
|
-
try {
|
|
234
|
-
const data = JSON.parse(line.slice(6));
|
|
235
|
-
// Handle error events
|
|
236
|
-
if (data.type === 'error') {
|
|
237
|
-
throw new Error(data.error || 'Stream error');
|
|
238
|
-
}
|
|
239
|
-
// Yield ConversationMessage objects
|
|
240
|
-
if (data.id) {
|
|
241
|
-
yield data;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
catch (e) {
|
|
245
|
-
console.error('[Widget API Client] Failed to parse SSE data:', line, e);
|
|
246
|
-
throw e;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
finally {
|
|
253
|
-
reader.releaseLock();
|
|
254
|
-
}
|
|
255
200
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
* Handles streaming events from backend and yields ConversationMessage updates
|
|
259
|
-
*/
|
|
260
|
-
async *sendAgentMessageStream(conversationId, message, fileIds) {
|
|
261
|
-
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/agent`, {
|
|
201
|
+
async *continueAgentMessageStream(conversationId, toolCallId, state) {
|
|
202
|
+
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/agent/continue`, {
|
|
262
203
|
method: 'POST',
|
|
263
204
|
headers: {
|
|
264
205
|
'Content-Type': 'application/json',
|
|
265
206
|
},
|
|
266
207
|
body: JSON.stringify({
|
|
267
208
|
conversationId: conversationId,
|
|
268
|
-
|
|
269
|
-
|
|
209
|
+
toolCallId,
|
|
210
|
+
state,
|
|
270
211
|
timeZone: this.getTimeZone(),
|
|
271
212
|
}),
|
|
272
213
|
});
|
|
273
214
|
if (!response.ok) {
|
|
274
|
-
throw await buildApiError(response, 'Failed to
|
|
275
|
-
}
|
|
276
|
-
if (!response.body) {
|
|
277
|
-
throw new Error('Response body is null');
|
|
278
|
-
}
|
|
279
|
-
const reader = response.body.getReader();
|
|
280
|
-
const decoder = new TextDecoder();
|
|
281
|
-
let buffer = '';
|
|
282
|
-
try {
|
|
283
|
-
while (true) {
|
|
284
|
-
const { done, value } = await reader.read();
|
|
285
|
-
if (done)
|
|
286
|
-
break;
|
|
287
|
-
buffer += decoder.decode(value, { stream: true });
|
|
288
|
-
const lines = buffer.split('\n');
|
|
289
|
-
buffer = lines.pop() || '';
|
|
290
|
-
for (const line of lines) {
|
|
291
|
-
if (line.startsWith('data: ')) {
|
|
292
|
-
try {
|
|
293
|
-
const data = JSON.parse(line.slice(6));
|
|
294
|
-
// Handle error events
|
|
295
|
-
if (data.type === 'error') {
|
|
296
|
-
throw new Error(data.error || 'Stream error');
|
|
297
|
-
}
|
|
298
|
-
// Yield ConversationMessage objects that come from the backend
|
|
299
|
-
// The backend yields full ConversationMessage objects during streaming
|
|
300
|
-
if (data.id) {
|
|
301
|
-
yield data;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
catch (e) {
|
|
305
|
-
console.error('[Widget API Client] Failed to parse SSE data:', line, e);
|
|
306
|
-
throw e;
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
finally {
|
|
313
|
-
reader.releaseLock();
|
|
215
|
+
throw await buildApiError(response, 'Failed to continue agent');
|
|
314
216
|
}
|
|
217
|
+
yield* parseSSEStream(response, (data) => {
|
|
218
|
+
return typeof data === 'object' && data !== null && 'type' in data;
|
|
219
|
+
});
|
|
315
220
|
}
|
|
316
221
|
/**
|
|
317
222
|
* Submit feedback for a message
|
|
318
223
|
*/
|
|
319
|
-
async submitFeedback(sessionId, messageId, feedback) {
|
|
224
|
+
async submitFeedback(sessionId, messageId, feedback, meta) {
|
|
320
225
|
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/feedback`, {
|
|
321
226
|
method: 'POST',
|
|
322
227
|
headers: {
|
|
@@ -326,6 +231,9 @@ class WidgetApiClient {
|
|
|
326
231
|
conversationId: sessionId,
|
|
327
232
|
messageId: messageId,
|
|
328
233
|
feedback,
|
|
234
|
+
messageRole: meta?.role,
|
|
235
|
+
messageContent: meta?.content,
|
|
236
|
+
messageTimestamp: meta?.timestamp,
|
|
329
237
|
}),
|
|
330
238
|
});
|
|
331
239
|
if (!response.ok) {
|
|
@@ -376,6 +284,173 @@ function generateMessageId() {
|
|
|
376
284
|
return `msg_${timestamp}_${randomStr}`;
|
|
377
285
|
}
|
|
378
286
|
|
|
287
|
+
const pendingResolvers = new Map();
|
|
288
|
+
const resumeCallbacks = new Map();
|
|
289
|
+
const frontendActionHandlers = {};
|
|
290
|
+
const actionRenderers = {};
|
|
291
|
+
function getFrontendActionHandler(implementation) {
|
|
292
|
+
return frontendActionHandlers[implementation];
|
|
293
|
+
}
|
|
294
|
+
function getActionRenderer(implementation) {
|
|
295
|
+
return actionRenderers[implementation];
|
|
296
|
+
}
|
|
297
|
+
function getActionPrompt(implementation) {
|
|
298
|
+
if (implementation === "google-calendar-appointment") {
|
|
299
|
+
return "Select a date to continue.";
|
|
300
|
+
}
|
|
301
|
+
return "Action input required.";
|
|
302
|
+
}
|
|
303
|
+
function waitForActionState(toolCallId) {
|
|
304
|
+
return new Promise((resolve) => {
|
|
305
|
+
pendingResolvers.set(toolCallId, resolve);
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
function resolveActionState(toolCallId, state) {
|
|
309
|
+
const resolver = pendingResolvers.get(toolCallId);
|
|
310
|
+
if (resolver) {
|
|
311
|
+
pendingResolvers.delete(toolCallId);
|
|
312
|
+
resolver(state);
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
// If no active resolver, check for a resume callback (for page reload scenario)
|
|
316
|
+
const resumeCallback = resumeCallbacks.get(toolCallId);
|
|
317
|
+
if (resumeCallback) {
|
|
318
|
+
resumeCallback(state).catch((error) => {
|
|
319
|
+
console.error("[Action] Failed to resume action:", error);
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
function registerActionResumeCallback(toolCallId, callback) {
|
|
324
|
+
resumeCallbacks.set(toolCallId, callback);
|
|
325
|
+
}
|
|
326
|
+
function unregisterActionResumeCallback(toolCallId) {
|
|
327
|
+
resumeCallbacks.delete(toolCallId);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function groupSlotsByDate(slots) {
|
|
331
|
+
const grouped = new Map();
|
|
332
|
+
for (const slot of slots) {
|
|
333
|
+
if (!slot || typeof slot !== 'object' || !slot.startTime || typeof slot.startTime !== 'string') {
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
const date = slot.startTime.slice(0, 10);
|
|
337
|
+
if (!grouped.has(date)) {
|
|
338
|
+
grouped.set(date, []);
|
|
339
|
+
}
|
|
340
|
+
grouped.get(date).push(slot);
|
|
341
|
+
}
|
|
342
|
+
return grouped;
|
|
343
|
+
}
|
|
344
|
+
function formatDate(dateStr) {
|
|
345
|
+
try {
|
|
346
|
+
const date = new Date(dateStr);
|
|
347
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
348
|
+
weekday: "short",
|
|
349
|
+
month: "short",
|
|
350
|
+
day: "numeric",
|
|
351
|
+
}).format(date);
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
return dateStr;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
function GoogleCalendarAppointmentCard({ message }) {
|
|
358
|
+
const action = message.action;
|
|
359
|
+
if (!action || action.implementation !== "google-calendar-appointment") {
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
const rawSlots = action.state.availableSlots;
|
|
363
|
+
const availableSlots = Array.isArray(rawSlots)
|
|
364
|
+
? rawSlots.filter((slot) => slot !== null &&
|
|
365
|
+
slot !== undefined &&
|
|
366
|
+
typeof slot === "object" &&
|
|
367
|
+
"startTime" in slot &&
|
|
368
|
+
"endTime" in slot &&
|
|
369
|
+
typeof slot.startTime === "string" &&
|
|
370
|
+
typeof slot.endTime === "string")
|
|
371
|
+
: [];
|
|
372
|
+
const allowTopic = action.state.allowTopic !== false;
|
|
373
|
+
const isBooked = action.state.status === "booked";
|
|
374
|
+
const slotsByDate = groupSlotsByDate(availableSlots);
|
|
375
|
+
const dates = Array.from(slotsByDate.keys()).sort();
|
|
376
|
+
const [selectedDate, setSelectedDate] = react.useState(dates[0] ?? "");
|
|
377
|
+
const [selectedSlot, setSelectedSlot] = react.useState(null);
|
|
378
|
+
const [topic, setTopic] = react.useState("");
|
|
379
|
+
const [error, setError] = react.useState(null);
|
|
380
|
+
const slotsForSelectedDate = selectedDate ? slotsByDate.get(selectedDate) ?? [] : [];
|
|
381
|
+
const onConfirm = () => {
|
|
382
|
+
if (!selectedSlot) {
|
|
383
|
+
setError("Please select a time slot.");
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
if (allowTopic && !topic.trim()) {
|
|
387
|
+
setError("Please enter a topic for the meeting.");
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
setError(null);
|
|
391
|
+
resolveActionState(action.toolCallId, {
|
|
392
|
+
...action.state,
|
|
393
|
+
selectedSlot: {
|
|
394
|
+
startTime: selectedSlot.startTime,
|
|
395
|
+
endTime: selectedSlot.endTime,
|
|
396
|
+
},
|
|
397
|
+
topic: allowTopic ? topic.trim() : null,
|
|
398
|
+
});
|
|
399
|
+
};
|
|
400
|
+
if (isBooked) {
|
|
401
|
+
const rawBookedSlot = action.state.selectedSlot;
|
|
402
|
+
const bookedSlot = rawBookedSlot &&
|
|
403
|
+
typeof rawBookedSlot === "object" &&
|
|
404
|
+
"startTime" in rawBookedSlot &&
|
|
405
|
+
"endTime" in rawBookedSlot
|
|
406
|
+
? rawBookedSlot
|
|
407
|
+
: null;
|
|
408
|
+
const bookedTopic = typeof action.state.topic === "string" ? action.state.topic : null;
|
|
409
|
+
const eventLink = typeof action.state.bookedEventLink === "string" ? action.state.bookedEventLink : null;
|
|
410
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-action-card ai-chat-action-booked", children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-header", children: [jsxRuntime.jsx("svg", { className: "ai-chat-action-icon-success", viewBox: "0 0 20 20", fill: "currentColor", children: jsxRuntime.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"] }), jsxRuntime.jsxs("div", { className: "ai-chat-action-body", children: [bookedTopic && (jsxRuntime.jsxs("div", { className: "ai-chat-action-detail", children: [jsxRuntime.jsx("span", { className: "ai-chat-action-label", children: "Topic:" }), jsxRuntime.jsx("span", { className: "ai-chat-action-value", children: bookedTopic })] })), bookedSlot && bookedSlot.startTime && (jsxRuntime.jsxs("div", { className: "ai-chat-action-detail", children: [jsxRuntime.jsx("span", { className: "ai-chat-action-label", children: "Time:" }), jsxRuntime.jsx("span", { className: "ai-chat-action-value", children: bookedSlot.displayTime || new Date(bookedSlot.startTime).toLocaleString() })] })), eventLink && (jsxRuntime.jsx("a", { href: eventLink, target: "_blank", rel: "noopener noreferrer", className: "ai-chat-action-link", children: "View in Google Calendar \u2192" }))] })] }));
|
|
411
|
+
}
|
|
412
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-action-card", children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-header", children: [jsxRuntime.jsx("svg", { className: "ai-chat-action-icon", viewBox: "0 0 20 20", fill: "currentColor", children: jsxRuntime.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"] }), jsxRuntime.jsxs("div", { className: "ai-chat-action-body", children: [allowTopic && (jsxRuntime.jsxs("div", { className: "ai-chat-action-field", children: [jsxRuntime.jsx("label", { htmlFor: `topic-${action.toolCallId}`, className: "ai-chat-action-label", children: "Meeting Topic" }), jsxRuntime.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) })] })), jsxRuntime.jsxs("div", { className: "ai-chat-action-field", children: [jsxRuntime.jsx("label", { className: "ai-chat-action-label", children: "Select Date" }), jsxRuntime.jsx("div", { className: "ai-chat-action-date-grid", children: dates.slice(0, 7).map((date) => (jsxRuntime.jsx("button", { type: "button", className: `ai-chat-action-date-btn ${selectedDate === date ? "active" : ""}`, onClick: () => {
|
|
413
|
+
setSelectedDate(date);
|
|
414
|
+
setSelectedSlot(null);
|
|
415
|
+
}, children: formatDate(date) }, date))) })] }), selectedDate && slotsForSelectedDate.length > 0 && (jsxRuntime.jsxs("div", { className: "ai-chat-action-field", children: [jsxRuntime.jsx("label", { className: "ai-chat-action-label", children: "Select Time" }), jsxRuntime.jsx("div", { className: "ai-chat-action-time-grid", children: slotsForSelectedDate.map((slot) => (jsxRuntime.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 && jsxRuntime.jsx("div", { className: "ai-chat-action-error", children: error }), jsxRuntime.jsx("button", { className: "ai-chat-action-button", type: "button", onClick: onConfirm, disabled: !selectedSlot, children: "Confirm Appointment" }), availableSlots.length === 0 && (jsxRuntime.jsx("div", { className: "ai-chat-action-hint", children: "No available time slots found." }))] })] }));
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
frontendActionHandlers["google-calendar-appointment"] = async (_input, _state, context) => {
|
|
419
|
+
return waitForActionState(context.toolCallId);
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
function styleInject(css, ref) {
|
|
423
|
+
if ( ref === void 0 ) ref = {};
|
|
424
|
+
var insertAt = ref.insertAt;
|
|
425
|
+
|
|
426
|
+
if (!css || typeof document === 'undefined') { return; }
|
|
427
|
+
|
|
428
|
+
var head = document.head || document.getElementsByTagName('head')[0];
|
|
429
|
+
var style = document.createElement('style');
|
|
430
|
+
style.type = 'text/css';
|
|
431
|
+
|
|
432
|
+
if (insertAt === 'top') {
|
|
433
|
+
if (head.firstChild) {
|
|
434
|
+
head.insertBefore(style, head.firstChild);
|
|
435
|
+
} else {
|
|
436
|
+
head.appendChild(style);
|
|
437
|
+
}
|
|
438
|
+
} else {
|
|
439
|
+
head.appendChild(style);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (style.styleSheet) {
|
|
443
|
+
style.styleSheet.cssText = css;
|
|
444
|
+
} else {
|
|
445
|
+
style.appendChild(document.createTextNode(css));
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
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)}";
|
|
450
|
+
styleInject(css_248z$1);
|
|
451
|
+
|
|
452
|
+
actionRenderers["google-calendar-appointment"] = (message) => (jsxRuntime.jsx(GoogleCalendarAppointmentCard, { message: message }));
|
|
453
|
+
|
|
379
454
|
/**
|
|
380
455
|
* Local Storage Utilities
|
|
381
456
|
* Handles conversation persistence in browser localStorage
|
|
@@ -437,9 +512,12 @@ function setWidgetStorage(widgetId, storage) {
|
|
|
437
512
|
* Get preview text from messages
|
|
438
513
|
*/
|
|
439
514
|
function getConversationPreview(messages) {
|
|
440
|
-
const firstUserMessage = messages.find(m => m.message.
|
|
515
|
+
const firstUserMessage = messages.find(m => m.message.role === "user");
|
|
441
516
|
if (firstUserMessage) {
|
|
442
|
-
|
|
517
|
+
const content = typeof firstUserMessage.message.content === "string"
|
|
518
|
+
? firstUserMessage.message.content
|
|
519
|
+
: "";
|
|
520
|
+
return content.slice(0, 100);
|
|
443
521
|
}
|
|
444
522
|
return 'New conversation';
|
|
445
523
|
}
|
|
@@ -594,6 +672,473 @@ function isStorageAvailable() {
|
|
|
594
672
|
* useChat Hook
|
|
595
673
|
* Main state management for chat functionality
|
|
596
674
|
*/
|
|
675
|
+
function hydrateToolNames(messages) {
|
|
676
|
+
const toolCallNameById = new Map();
|
|
677
|
+
for (const entry of messages) {
|
|
678
|
+
if (entry.message.role !== "assistant") {
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
const toolCalls = entry.message.tool_calls;
|
|
682
|
+
if (!Array.isArray(toolCalls)) {
|
|
683
|
+
continue;
|
|
684
|
+
}
|
|
685
|
+
for (const call of toolCalls) {
|
|
686
|
+
const callId = call?.id;
|
|
687
|
+
const callName = call?.function?.name;
|
|
688
|
+
if (callId && callName) {
|
|
689
|
+
toolCallNameById.set(callId, callName);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
if (toolCallNameById.size === 0) {
|
|
694
|
+
return messages;
|
|
695
|
+
}
|
|
696
|
+
return messages.map((entry) => {
|
|
697
|
+
if (entry.message.role !== "tool") {
|
|
698
|
+
return entry;
|
|
699
|
+
}
|
|
700
|
+
const toolCallId = entry.message.tool_call_id;
|
|
701
|
+
const resolvedName = toolCallNameById.get(toolCallId);
|
|
702
|
+
if (!resolvedName) {
|
|
703
|
+
return entry;
|
|
704
|
+
}
|
|
705
|
+
const currentName = entry.message.name;
|
|
706
|
+
const nextName = currentName && currentName.trim().length > 0 ? currentName : resolvedName;
|
|
707
|
+
const nextToolExecuting = entry.toolExecuting || resolvedName;
|
|
708
|
+
if (nextName === currentName && nextToolExecuting === entry.toolExecuting) {
|
|
709
|
+
return entry;
|
|
710
|
+
}
|
|
711
|
+
return {
|
|
712
|
+
...entry,
|
|
713
|
+
message: { ...entry.message, name: nextName },
|
|
714
|
+
toolExecuting: nextToolExecuting,
|
|
715
|
+
};
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
function hydrateActionContent(messages) {
|
|
719
|
+
return messages.map((entry) => {
|
|
720
|
+
if (entry.message.role !== "assistant" || !entry.action) {
|
|
721
|
+
return entry;
|
|
722
|
+
}
|
|
723
|
+
const content = typeof entry.message.content === "string" ? entry.message.content : "";
|
|
724
|
+
if (content.trim().length > 0) {
|
|
725
|
+
return entry;
|
|
726
|
+
}
|
|
727
|
+
return {
|
|
728
|
+
...entry,
|
|
729
|
+
message: { ...entry.message, content: getActionPrompt(entry.action.implementation) },
|
|
730
|
+
};
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
function hydrateMessages(messages) {
|
|
734
|
+
return hydrateActionContent(hydrateToolNames(messages));
|
|
735
|
+
}
|
|
736
|
+
function setupActionResumeCallbacks(messages, client, conversationId, setState, onMessageUpdate) {
|
|
737
|
+
// Find all incomplete actions and register resume callbacks
|
|
738
|
+
for (const message of messages) {
|
|
739
|
+
if (message.action && !message.action.done) {
|
|
740
|
+
const toolCallId = message.action.toolCallId;
|
|
741
|
+
const toolName = message.message.name || message.toolExecuting || "tool";
|
|
742
|
+
registerActionResumeCallback(toolCallId, async (newState) => {
|
|
743
|
+
// When user interacts with the action after reload, continue the stream
|
|
744
|
+
try {
|
|
745
|
+
// Update the action message with the new state
|
|
746
|
+
setState(prev => ({
|
|
747
|
+
...prev,
|
|
748
|
+
messages: prev.messages.map(m => m.action?.toolCallId === toolCallId
|
|
749
|
+
? {
|
|
750
|
+
...m,
|
|
751
|
+
action: m.action ? { ...m.action, state: newState } : undefined,
|
|
752
|
+
}
|
|
753
|
+
: m),
|
|
754
|
+
isTyping: true,
|
|
755
|
+
}));
|
|
756
|
+
const streamState = createStreamState();
|
|
757
|
+
// Continue the agent stream with the new state
|
|
758
|
+
for await (const event of client.continueAgentMessageStream(conversationId, toolCallId, newState)) {
|
|
759
|
+
if (event.type === "done") {
|
|
760
|
+
finalizeToolMessage(streamState, setState, toolCallId, toolName);
|
|
761
|
+
streamState.sources = event.sources;
|
|
762
|
+
streamState.toolCallToActionId = event.tool_call_to_action_id;
|
|
763
|
+
finalizeStreamMessages(setState, streamState.newMessageIds, event.sources, event.tool_call_to_action_id);
|
|
764
|
+
continue;
|
|
765
|
+
}
|
|
766
|
+
if (event.type === "error") {
|
|
767
|
+
const errorMessage = {
|
|
768
|
+
id: generateMessageId(),
|
|
769
|
+
message: {
|
|
770
|
+
role: "assistant",
|
|
771
|
+
content: `Error: ${event.error}`,
|
|
772
|
+
},
|
|
773
|
+
timestamp: new Date().toISOString(),
|
|
774
|
+
sources: [],
|
|
775
|
+
isError: true,
|
|
776
|
+
};
|
|
777
|
+
upsertMessage(setState, errorMessage, false);
|
|
778
|
+
setState(prev => ({ ...prev, isTyping: false }));
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
handleStreamEvent(event, streamState, onMessageUpdate, setState);
|
|
782
|
+
}
|
|
783
|
+
setState(prev => ({ ...prev, isTyping: false }));
|
|
784
|
+
}
|
|
785
|
+
catch (error) {
|
|
786
|
+
console.error("[Action Resume] Failed to continue stream:", error);
|
|
787
|
+
setState(prev => ({ ...prev, isTyping: false, error: error instanceof Error ? error.message : "Failed to resume action" }));
|
|
788
|
+
throw error;
|
|
789
|
+
}
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
function createStreamState() {
|
|
795
|
+
return {
|
|
796
|
+
currentContent: "",
|
|
797
|
+
currentMessageId: generateMessageId(),
|
|
798
|
+
activeToolCallCount: 0,
|
|
799
|
+
newMessageIds: new Set(),
|
|
800
|
+
sources: [],
|
|
801
|
+
toolCallToActionId: {},
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
function upsertMessage(setState, message, isTyping) {
|
|
805
|
+
setState(prev => {
|
|
806
|
+
const existingIndex = prev.messages.findIndex(m => m.id === message.id);
|
|
807
|
+
if (existingIndex >= 0) {
|
|
808
|
+
const newMessages = [...prev.messages];
|
|
809
|
+
newMessages[existingIndex] = message;
|
|
810
|
+
return { ...prev, messages: newMessages, isTyping, isLoading: false };
|
|
811
|
+
}
|
|
812
|
+
return {
|
|
813
|
+
...prev,
|
|
814
|
+
messages: [...prev.messages, message],
|
|
815
|
+
isTyping,
|
|
816
|
+
isLoading: false,
|
|
817
|
+
};
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
function finalizeStreamMessages(setState, messageIds, sources, toolCallToActionId) {
|
|
821
|
+
setState(prev => ({
|
|
822
|
+
...prev,
|
|
823
|
+
messages: prev.messages.map(msg => (messageIds.has(msg.id)
|
|
824
|
+
? { ...msg, sources, toolCallToActionId }
|
|
825
|
+
: msg)),
|
|
826
|
+
isTyping: false,
|
|
827
|
+
}));
|
|
828
|
+
}
|
|
829
|
+
function handleContentEvent(event, streamState, onMessageUpdate, setState) {
|
|
830
|
+
streamState.currentContent += event.content;
|
|
831
|
+
const assistantMessage = {
|
|
832
|
+
id: streamState.currentMessageId,
|
|
833
|
+
message: { role: "assistant", content: streamState.currentContent },
|
|
834
|
+
timestamp: new Date().toISOString(),
|
|
835
|
+
sources: streamState.sources,
|
|
836
|
+
isStreaming: true,
|
|
837
|
+
};
|
|
838
|
+
streamState.newMessageIds.add(assistantMessage.id);
|
|
839
|
+
onMessageUpdate(assistantMessage);
|
|
840
|
+
const hasContent = assistantMessage.message.content?.trim().length || 0 > 0;
|
|
841
|
+
const isToolExecuting = streamState.activeToolCallCount > 0;
|
|
842
|
+
const isTyping = (!hasContent && assistantMessage.isStreaming) || isToolExecuting;
|
|
843
|
+
upsertMessage(setState, assistantMessage, isTyping);
|
|
844
|
+
}
|
|
845
|
+
function handleToolStartEvent(event, streamState, onMessageUpdate, setState) {
|
|
846
|
+
if (streamState.currentContent.trim()) {
|
|
847
|
+
const finalAssistant = {
|
|
848
|
+
id: streamState.currentMessageId,
|
|
849
|
+
message: { role: "assistant", content: streamState.currentContent },
|
|
850
|
+
timestamp: new Date().toISOString(),
|
|
851
|
+
sources: streamState.sources,
|
|
852
|
+
isStreaming: false,
|
|
853
|
+
};
|
|
854
|
+
streamState.newMessageIds.add(finalAssistant.id);
|
|
855
|
+
onMessageUpdate(finalAssistant);
|
|
856
|
+
upsertMessage(setState, finalAssistant, true);
|
|
857
|
+
streamState.currentContent = "";
|
|
858
|
+
streamState.currentMessageId = generateMessageId();
|
|
859
|
+
}
|
|
860
|
+
const toolMessageId = event.tool_call_id;
|
|
861
|
+
streamState.activeToolCallCount += 1;
|
|
862
|
+
streamState.newMessageIds.add(toolMessageId);
|
|
863
|
+
const toolMessage = {
|
|
864
|
+
id: toolMessageId,
|
|
865
|
+
sources: [],
|
|
866
|
+
message: { role: "tool", content: "", tool_call_id: event.tool_call_id, name: event.tool_name },
|
|
867
|
+
timestamp: new Date().toISOString(),
|
|
868
|
+
isStreaming: true,
|
|
869
|
+
toolExecuting: event.tool_name,
|
|
870
|
+
};
|
|
871
|
+
upsertMessage(setState, toolMessage, true);
|
|
872
|
+
}
|
|
873
|
+
function handleToolEndEvent(event, streamState, _onMessageUpdate, setState) {
|
|
874
|
+
// Update state and mark action as done in a single setState call
|
|
875
|
+
setState(prev => {
|
|
876
|
+
const messages = prev.messages.map((msg) => {
|
|
877
|
+
const matchesToolCall = msg.message.role === "tool" && msg.message.tool_call_id === event.tool_call_id;
|
|
878
|
+
if (!matchesToolCall) {
|
|
879
|
+
return msg;
|
|
880
|
+
}
|
|
881
|
+
const existingName = msg.message.name || event.tool_name;
|
|
882
|
+
return {
|
|
883
|
+
...msg,
|
|
884
|
+
message: {
|
|
885
|
+
role: "tool",
|
|
886
|
+
content: event.state ? JSON.stringify(event.state) : (typeof msg.message.content === "string" ? msg.message.content : ""),
|
|
887
|
+
tool_call_id: event.tool_call_id,
|
|
888
|
+
name: existingName,
|
|
889
|
+
},
|
|
890
|
+
isStreaming: false,
|
|
891
|
+
toolExecuting: existingName,
|
|
892
|
+
action: msg.action ? {
|
|
893
|
+
...msg.action,
|
|
894
|
+
state: event.state || msg.action.state,
|
|
895
|
+
done: true, // Mark action as completed
|
|
896
|
+
} : undefined,
|
|
897
|
+
};
|
|
898
|
+
});
|
|
899
|
+
return { ...prev, messages, isTyping: false, isLoading: false };
|
|
900
|
+
});
|
|
901
|
+
streamState.activeToolCallCount = Math.max(0, streamState.activeToolCallCount - 1);
|
|
902
|
+
}
|
|
903
|
+
function handleToolErrorEvent(event, streamState, _onMessageUpdate, setState) {
|
|
904
|
+
setState(prev => {
|
|
905
|
+
const messages = prev.messages.map((entry) => {
|
|
906
|
+
const matchesToolCall = entry.message.role === "tool" && entry.message.tool_call_id === event.tool_call_id;
|
|
907
|
+
if (!matchesToolCall) {
|
|
908
|
+
return entry;
|
|
909
|
+
}
|
|
910
|
+
const name = entry.message.name || "tool";
|
|
911
|
+
const toolMessage = {
|
|
912
|
+
...entry,
|
|
913
|
+
message: {
|
|
914
|
+
role: "tool",
|
|
915
|
+
content: event.error || "Tool execution failed",
|
|
916
|
+
tool_call_id: event.tool_call_id,
|
|
917
|
+
name,
|
|
918
|
+
},
|
|
919
|
+
timestamp: new Date().toISOString(),
|
|
920
|
+
isStreaming: false,
|
|
921
|
+
isError: true,
|
|
922
|
+
toolExecuting: name,
|
|
923
|
+
};
|
|
924
|
+
return toolMessage;
|
|
925
|
+
});
|
|
926
|
+
return { ...prev, messages, isTyping: false, isLoading: false };
|
|
927
|
+
});
|
|
928
|
+
streamState.activeToolCallCount = Math.max(0, streamState.activeToolCallCount - 1);
|
|
929
|
+
}
|
|
930
|
+
function finalizeToolMessage(streamState, setState, toolCallId, toolName) {
|
|
931
|
+
setState(prev => {
|
|
932
|
+
const messages = prev.messages.map((entry) => {
|
|
933
|
+
const matchesToolCall = entry.message.role === "tool" && entry.message.tool_call_id === toolCallId;
|
|
934
|
+
if (!matchesToolCall) {
|
|
935
|
+
return entry;
|
|
936
|
+
}
|
|
937
|
+
const existingName = entry.message.name || toolName;
|
|
938
|
+
return {
|
|
939
|
+
...entry,
|
|
940
|
+
message: {
|
|
941
|
+
role: "tool",
|
|
942
|
+
content: typeof entry.message.content === "string" ? entry.message.content : "",
|
|
943
|
+
tool_call_id: toolCallId,
|
|
944
|
+
name: existingName,
|
|
945
|
+
},
|
|
946
|
+
isStreaming: false,
|
|
947
|
+
toolExecuting: existingName,
|
|
948
|
+
action: entry.action ? {
|
|
949
|
+
...entry.action,
|
|
950
|
+
done: true, // Mark action as completed
|
|
951
|
+
} : undefined,
|
|
952
|
+
};
|
|
953
|
+
});
|
|
954
|
+
return { ...prev, messages, isTyping: false, isLoading: false };
|
|
955
|
+
});
|
|
956
|
+
streamState.activeToolCallCount = Math.max(0, streamState.activeToolCallCount - 1);
|
|
957
|
+
}
|
|
958
|
+
function handleDoneEvent(event, streamState, _onMessageUpdate, setState) {
|
|
959
|
+
streamState.sources = event.sources;
|
|
960
|
+
streamState.toolCallToActionId = event.tool_call_to_action_id;
|
|
961
|
+
finalizeStreamMessages(setState, streamState.newMessageIds, event.sources, event.tool_call_to_action_id);
|
|
962
|
+
}
|
|
963
|
+
function handleHaltEvent(event, _streamState, onMessageUpdate, setState) {
|
|
964
|
+
const toolNames = event.tool_calls.map(call => call.name).join(", ");
|
|
965
|
+
const notice = toolNames
|
|
966
|
+
? `Awaiting external tool results: ${toolNames}.`
|
|
967
|
+
: "Awaiting external tool results.";
|
|
968
|
+
const haltMessage = {
|
|
969
|
+
id: generateMessageId(),
|
|
970
|
+
message: { role: "assistant", content: notice },
|
|
971
|
+
timestamp: new Date().toISOString(),
|
|
972
|
+
sources: [],
|
|
973
|
+
isError: true,
|
|
974
|
+
};
|
|
975
|
+
onMessageUpdate(haltMessage);
|
|
976
|
+
upsertMessage(setState, haltMessage, false);
|
|
977
|
+
setState(prev => ({ ...prev, error: "Awaiting external tool handling." }));
|
|
978
|
+
}
|
|
979
|
+
function handleErrorEvent(event, _streamState, onMessageUpdate, setState) {
|
|
980
|
+
const errorMessage = {
|
|
981
|
+
id: generateMessageId(),
|
|
982
|
+
message: { role: "assistant", content: `⚠️ ${event.error}` },
|
|
983
|
+
timestamp: new Date().toISOString(),
|
|
984
|
+
sources: [],
|
|
985
|
+
isError: true,
|
|
986
|
+
};
|
|
987
|
+
onMessageUpdate(errorMessage);
|
|
988
|
+
upsertMessage(setState, errorMessage, false);
|
|
989
|
+
setState(prev => ({ ...prev, error: event.error }));
|
|
990
|
+
}
|
|
991
|
+
const eventHandlers = {
|
|
992
|
+
content: handleContentEvent,
|
|
993
|
+
tool_start: handleToolStartEvent,
|
|
994
|
+
tool_end: handleToolEndEvent,
|
|
995
|
+
tool_error: handleToolErrorEvent,
|
|
996
|
+
done: handleDoneEvent,
|
|
997
|
+
halt: handleHaltEvent,
|
|
998
|
+
error: handleErrorEvent,
|
|
999
|
+
action_request: () => { },
|
|
1000
|
+
};
|
|
1001
|
+
function handleStreamEvent(event, streamState, onMessageUpdate, setState) {
|
|
1002
|
+
if (event.type === "tool_start" || event.type === "tool_end" || event.type === "tool_error" || event.type === "action_request" || event.type === "done" || event.type === "halt") {
|
|
1003
|
+
console.log("[Widget] stream event:", {
|
|
1004
|
+
type: event.type,
|
|
1005
|
+
toolCallId: "tool_call_id" in event ? event.tool_call_id : undefined,
|
|
1006
|
+
toolName: "tool_name" in event ? event.tool_name : undefined,
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
const handler = eventHandlers[event.type];
|
|
1010
|
+
if (handler) {
|
|
1011
|
+
handler(event, streamState, onMessageUpdate, setState);
|
|
1012
|
+
}
|
|
1013
|
+
else {
|
|
1014
|
+
console.warn('[Chat] Unknown event type:', event.type);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
async function handleActionLoop(client, initialEvent, streamState, onMessageUpdate, setState, widgetId, conversationId, getMessages) {
|
|
1018
|
+
let pendingEvent = initialEvent;
|
|
1019
|
+
while (pendingEvent) {
|
|
1020
|
+
streamState.toolCallToActionId = pendingEvent.tool_call_to_action_id;
|
|
1021
|
+
const resumeToolCallId = pendingEvent.tool_call_id;
|
|
1022
|
+
const existingToolMessage = getMessages().find((entry) => entry.message.role === "tool" && entry.message.tool_call_id === resumeToolCallId);
|
|
1023
|
+
const toolMessageId = existingToolMessage?.id ?? resumeToolCallId;
|
|
1024
|
+
const toolName = existingToolMessage?.message.name ?? "tool";
|
|
1025
|
+
const toolMessage = {
|
|
1026
|
+
id: toolMessageId,
|
|
1027
|
+
sources: [],
|
|
1028
|
+
message: {
|
|
1029
|
+
role: "tool",
|
|
1030
|
+
content: "",
|
|
1031
|
+
tool_call_id: resumeToolCallId,
|
|
1032
|
+
name: toolName,
|
|
1033
|
+
},
|
|
1034
|
+
timestamp: new Date().toISOString(),
|
|
1035
|
+
isStreaming: true,
|
|
1036
|
+
toolExecuting: toolName,
|
|
1037
|
+
action: {
|
|
1038
|
+
implementation: pendingEvent.implementation,
|
|
1039
|
+
toolCallId: pendingEvent.tool_call_id,
|
|
1040
|
+
actionId: pendingEvent.action_id,
|
|
1041
|
+
input: pendingEvent.input,
|
|
1042
|
+
state: pendingEvent.state,
|
|
1043
|
+
done: false, // Action not yet completed
|
|
1044
|
+
},
|
|
1045
|
+
};
|
|
1046
|
+
if (streamState.activeToolCallCount === 0) {
|
|
1047
|
+
streamState.activeToolCallCount = 1;
|
|
1048
|
+
}
|
|
1049
|
+
onMessageUpdate(toolMessage);
|
|
1050
|
+
upsertMessage(setState, toolMessage, true);
|
|
1051
|
+
const handler = getFrontendActionHandler(pendingEvent.implementation);
|
|
1052
|
+
if (!handler) {
|
|
1053
|
+
const errorMessage = {
|
|
1054
|
+
id: generateMessageId(),
|
|
1055
|
+
message: {
|
|
1056
|
+
role: "assistant",
|
|
1057
|
+
content: `⚠️ No frontend handler for action '${pendingEvent.implementation}'.`,
|
|
1058
|
+
},
|
|
1059
|
+
timestamp: new Date().toISOString(),
|
|
1060
|
+
sources: [],
|
|
1061
|
+
isError: true,
|
|
1062
|
+
};
|
|
1063
|
+
onMessageUpdate(errorMessage);
|
|
1064
|
+
upsertMessage(setState, errorMessage, false);
|
|
1065
|
+
setState(prev => ({ ...prev, error: `Missing frontend handler: ${pendingEvent?.implementation}` }));
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1068
|
+
let nextState;
|
|
1069
|
+
try {
|
|
1070
|
+
nextState = await handler(pendingEvent.input, pendingEvent.state, {
|
|
1071
|
+
widgetId,
|
|
1072
|
+
conversationId,
|
|
1073
|
+
toolCallId: pendingEvent.tool_call_id,
|
|
1074
|
+
actionId: pendingEvent.action_id,
|
|
1075
|
+
implementation: pendingEvent.implementation,
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
catch (error) {
|
|
1079
|
+
const errorMessage = error instanceof Error ? error.message : "Frontend action failed.";
|
|
1080
|
+
const errorMessageEntry = {
|
|
1081
|
+
id: generateMessageId(),
|
|
1082
|
+
message: { role: "assistant", content: `⚠️ ${errorMessage}` },
|
|
1083
|
+
timestamp: new Date().toISOString(),
|
|
1084
|
+
sources: [],
|
|
1085
|
+
isError: true,
|
|
1086
|
+
};
|
|
1087
|
+
onMessageUpdate(errorMessageEntry);
|
|
1088
|
+
upsertMessage(setState, errorMessageEntry, false);
|
|
1089
|
+
setState(prev => ({ ...prev, error: errorMessage }));
|
|
1090
|
+
return;
|
|
1091
|
+
}
|
|
1092
|
+
pendingEvent = null;
|
|
1093
|
+
const updatedToolMessage = {
|
|
1094
|
+
...toolMessage,
|
|
1095
|
+
action: toolMessage.action
|
|
1096
|
+
? {
|
|
1097
|
+
...toolMessage.action,
|
|
1098
|
+
state: nextState,
|
|
1099
|
+
}
|
|
1100
|
+
: undefined,
|
|
1101
|
+
};
|
|
1102
|
+
upsertMessage(setState, updatedToolMessage, true);
|
|
1103
|
+
let streamEnded = false;
|
|
1104
|
+
for await (const event of client.continueAgentMessageStream(conversationId, resumeToolCallId, nextState)) {
|
|
1105
|
+
if (event.type === "action_request") {
|
|
1106
|
+
pendingEvent = event;
|
|
1107
|
+
break;
|
|
1108
|
+
}
|
|
1109
|
+
if (event.type === "done") {
|
|
1110
|
+
// Don't extract and update state from done event - the state was already
|
|
1111
|
+
// updated by tool_end event or by the user's frontend action.
|
|
1112
|
+
finalizeToolMessage(streamState, setState, resumeToolCallId, toolName);
|
|
1113
|
+
// Handle the done event but skip the tool finalization part since we already did it
|
|
1114
|
+
streamState.sources = event.sources;
|
|
1115
|
+
streamState.toolCallToActionId = event.tool_call_to_action_id;
|
|
1116
|
+
finalizeStreamMessages(setState, streamState.newMessageIds, event.sources, event.tool_call_to_action_id);
|
|
1117
|
+
streamEnded = true;
|
|
1118
|
+
continue; // Skip handleStreamEvent for done events to avoid state conflicts
|
|
1119
|
+
}
|
|
1120
|
+
if (event.type === "error") {
|
|
1121
|
+
const errorMessage = {
|
|
1122
|
+
id: generateMessageId(),
|
|
1123
|
+
message: {
|
|
1124
|
+
role: "assistant",
|
|
1125
|
+
content: `Error occurred during action execution: ${event.error}`,
|
|
1126
|
+
},
|
|
1127
|
+
timestamp: new Date().toISOString(),
|
|
1128
|
+
sources: [],
|
|
1129
|
+
isError: true,
|
|
1130
|
+
};
|
|
1131
|
+
upsertMessage(setState, errorMessage, false);
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
1134
|
+
handleStreamEvent(event, streamState, onMessageUpdate, setState);
|
|
1135
|
+
}
|
|
1136
|
+
// If stream ended without a done event (e.g., only tool_end was sent), finalize the tool message
|
|
1137
|
+
if (!streamEnded && !pendingEvent) {
|
|
1138
|
+
finalizeToolMessage(streamState, setState, resumeToolCallId, toolName);
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
597
1142
|
function deriveErrorInfo(error) {
|
|
598
1143
|
if (error instanceof ApiError) {
|
|
599
1144
|
const retryAfterSeconds = typeof error.retryAfterMs === 'number'
|
|
@@ -666,6 +1211,10 @@ function useChat(options) {
|
|
|
666
1211
|
conversationId: '', // Will be set after loading conversation
|
|
667
1212
|
config: null,
|
|
668
1213
|
});
|
|
1214
|
+
const stateRef = react.useRef(state);
|
|
1215
|
+
react.useEffect(() => {
|
|
1216
|
+
stateRef.current = state;
|
|
1217
|
+
}, [state]);
|
|
669
1218
|
// Chat history state
|
|
670
1219
|
const [conversations, setConversations] = react.useState([]);
|
|
671
1220
|
const apiClient = react.useRef(new WidgetApiClient({ widgetId, apiUrl }));
|
|
@@ -673,18 +1222,12 @@ function useChat(options) {
|
|
|
673
1222
|
react.useEffect(() => {
|
|
674
1223
|
// Skip initialization in preview mode
|
|
675
1224
|
if (skipInitialization) {
|
|
676
|
-
console.log('[useChat] Skipping initialization (preview mode)');
|
|
677
1225
|
return;
|
|
678
1226
|
}
|
|
679
1227
|
let isMounted = true;
|
|
680
|
-
console.log('[useChat] Effect running, mounting component');
|
|
681
1228
|
const initialize = async () => {
|
|
682
1229
|
try {
|
|
683
|
-
console.log('[useChat] Fetching config...');
|
|
684
1230
|
const config = await apiClient.current.getConfig();
|
|
685
|
-
console.log('[useChat] Config fetched successfully:', {
|
|
686
|
-
hasAppearance: !!config.appearance,
|
|
687
|
-
});
|
|
688
1231
|
const persistConversation = config.settings.persistConversation ?? true;
|
|
689
1232
|
let conversationId = '';
|
|
690
1233
|
let messages = [];
|
|
@@ -695,7 +1238,7 @@ function useChat(options) {
|
|
|
695
1238
|
try {
|
|
696
1239
|
const conversation = await apiClient.current.getOrCreateConversation(storedId);
|
|
697
1240
|
conversationId = conversation.id;
|
|
698
|
-
messages = conversation.messages;
|
|
1241
|
+
messages = hydrateMessages(conversation.messages);
|
|
699
1242
|
}
|
|
700
1243
|
catch (conversationError) {
|
|
701
1244
|
console.warn('Failed to load existing conversation:', conversationError);
|
|
@@ -703,16 +1246,19 @@ function useChat(options) {
|
|
|
703
1246
|
}
|
|
704
1247
|
}
|
|
705
1248
|
if (!isMounted) {
|
|
706
|
-
console.log('[useChat] Component unmounted, skipping state update');
|
|
707
1249
|
return;
|
|
708
1250
|
}
|
|
709
|
-
|
|
1251
|
+
const hydratedMessages = hydrateMessages(messages);
|
|
710
1252
|
setState(prev => ({
|
|
711
1253
|
...prev,
|
|
712
1254
|
config,
|
|
713
1255
|
conversationId,
|
|
714
|
-
messages,
|
|
1256
|
+
messages: hydratedMessages,
|
|
715
1257
|
}));
|
|
1258
|
+
// Setup resume callbacks for incomplete actions
|
|
1259
|
+
if (conversationId) {
|
|
1260
|
+
setupActionResumeCallbacks(hydratedMessages, apiClient.current, conversationId, setState, onMessage ?? (() => { }));
|
|
1261
|
+
}
|
|
716
1262
|
}
|
|
717
1263
|
catch (error) {
|
|
718
1264
|
console.error('[useChat] Error fetching config:', error);
|
|
@@ -727,8 +1273,13 @@ function useChat(options) {
|
|
|
727
1273
|
};
|
|
728
1274
|
initialize();
|
|
729
1275
|
return () => {
|
|
730
|
-
console.log('[useChat] Effect cleanup, unmounting component');
|
|
731
1276
|
isMounted = false;
|
|
1277
|
+
// Cleanup resume callbacks
|
|
1278
|
+
state.messages.forEach(message => {
|
|
1279
|
+
if (message.action?.toolCallId) {
|
|
1280
|
+
unregisterActionResumeCallback(message.action.toolCallId);
|
|
1281
|
+
}
|
|
1282
|
+
});
|
|
732
1283
|
};
|
|
733
1284
|
}, [widgetId, apiUrl, onError]);
|
|
734
1285
|
// Save conversation when messages change
|
|
@@ -741,9 +1292,6 @@ function useChat(options) {
|
|
|
741
1292
|
saveConversation(widgetId, state.conversationId, state.messages);
|
|
742
1293
|
}
|
|
743
1294
|
}, [widgetId, state.messages, state.conversationId, state.config?.settings.persistConversation]);
|
|
744
|
-
/**
|
|
745
|
-
* Send a message
|
|
746
|
-
*/
|
|
747
1295
|
const sendMessage = react.useCallback(async (content, files) => {
|
|
748
1296
|
const trimmedContent = content.trim();
|
|
749
1297
|
const hasFiles = !!files && files.length > 0;
|
|
@@ -752,7 +1300,7 @@ function useChat(options) {
|
|
|
752
1300
|
const userMessage = {
|
|
753
1301
|
id: generateMessageId(),
|
|
754
1302
|
message: {
|
|
755
|
-
|
|
1303
|
+
role: "user",
|
|
756
1304
|
content: trimmedContent,
|
|
757
1305
|
},
|
|
758
1306
|
timestamp: new Date().toISOString(),
|
|
@@ -773,7 +1321,7 @@ function useChat(options) {
|
|
|
773
1321
|
const conversation = await apiClient.current.getOrCreateConversation();
|
|
774
1322
|
conversationId = conversation.id;
|
|
775
1323
|
setState(prev => {
|
|
776
|
-
const serverMessages = conversation.messages ?? [];
|
|
1324
|
+
const serverMessages = hydrateMessages(conversation.messages ?? []);
|
|
777
1325
|
if (serverMessages.length === 0) {
|
|
778
1326
|
return {
|
|
779
1327
|
...prev,
|
|
@@ -807,48 +1355,20 @@ function useChat(options) {
|
|
|
807
1355
|
}
|
|
808
1356
|
}
|
|
809
1357
|
}
|
|
810
|
-
// Determine if widget has actions (use agent endpoint)
|
|
811
|
-
const useAgent = state.config?.behavior.agentic || (state.config?.actions && state.config.actions.length > 0);
|
|
812
1358
|
// Stream the response
|
|
813
1359
|
let lastStreamedMessage = null;
|
|
814
|
-
const
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
// Show typing indicator if:
|
|
827
|
-
// 1. Message is streaming AND has no content (waiting for first token or after tool call)
|
|
828
|
-
// 2. Message is a tool execution (shows loading spinner on the tool)
|
|
829
|
-
const isAIMessage = message.message.type === 'ai';
|
|
830
|
-
const hasContent = isAIMessage && message.message.content.trim().length > 0;
|
|
831
|
-
const isToolExecuting = message.toolExecuting !== undefined;
|
|
832
|
-
return {
|
|
833
|
-
...prev,
|
|
834
|
-
messages: newMessages,
|
|
835
|
-
isTyping: (message.isStreaming && !hasContent && isAIMessage) || isToolExecuting,
|
|
836
|
-
isLoading: false,
|
|
837
|
-
};
|
|
838
|
-
}
|
|
839
|
-
else {
|
|
840
|
-
// Add new streaming message
|
|
841
|
-
const isAIMessage = message.message.type === 'ai';
|
|
842
|
-
const hasContent = isAIMessage && message.message.content.trim().length > 0;
|
|
843
|
-
const isToolExecuting = message.toolExecuting !== undefined;
|
|
844
|
-
return {
|
|
845
|
-
...prev,
|
|
846
|
-
messages: [...prev.messages, message],
|
|
847
|
-
isTyping: (message.isStreaming && !hasContent && isAIMessage) || isToolExecuting,
|
|
848
|
-
isLoading: false,
|
|
849
|
-
};
|
|
850
|
-
}
|
|
851
|
-
});
|
|
1360
|
+
const streamState = createStreamState();
|
|
1361
|
+
const stream = apiClient.current.sendAgentMessageStream(conversationId, trimmedContent, fileIds);
|
|
1362
|
+
for await (const event of stream) {
|
|
1363
|
+
if (event.type === "action_request") {
|
|
1364
|
+
await handleActionLoop(apiClient.current, event, streamState, (message) => {
|
|
1365
|
+
lastStreamedMessage = message;
|
|
1366
|
+
}, setState, widgetId, conversationId, () => stateRef.current.messages);
|
|
1367
|
+
break;
|
|
1368
|
+
}
|
|
1369
|
+
handleStreamEvent(event, streamState, (message) => {
|
|
1370
|
+
lastStreamedMessage = message;
|
|
1371
|
+
}, setState);
|
|
852
1372
|
}
|
|
853
1373
|
// Stream completed - finalize state
|
|
854
1374
|
setState(prev => ({
|
|
@@ -869,7 +1389,7 @@ function useChat(options) {
|
|
|
869
1389
|
const fallbackAssistantMessage = {
|
|
870
1390
|
id: generateMessageId(),
|
|
871
1391
|
message: {
|
|
872
|
-
|
|
1392
|
+
role: "assistant",
|
|
873
1393
|
content: fallbackMessage,
|
|
874
1394
|
},
|
|
875
1395
|
timestamp: new Date().toISOString(),
|
|
@@ -888,7 +1408,7 @@ function useChat(options) {
|
|
|
888
1408
|
const errorAssistantMessage = {
|
|
889
1409
|
id: generateMessageId(),
|
|
890
1410
|
message: {
|
|
891
|
-
|
|
1411
|
+
role: "assistant",
|
|
892
1412
|
content: `⚠️ ${errorInfo.message}`,
|
|
893
1413
|
},
|
|
894
1414
|
timestamp: new Date().toISOString(),
|
|
@@ -926,8 +1446,13 @@ function useChat(options) {
|
|
|
926
1446
|
*/
|
|
927
1447
|
const submitFeedback = react.useCallback(async (messageId, feedback) => {
|
|
928
1448
|
try {
|
|
1449
|
+
const message = state.messages.find(msg => msg.id === messageId);
|
|
1450
|
+
const messageContent = typeof message?.message.content === "string" ? message.message.content : undefined;
|
|
1451
|
+
const meta = message
|
|
1452
|
+
? { role: message.message.role, content: messageContent, timestamp: message.timestamp }
|
|
1453
|
+
: undefined;
|
|
929
1454
|
console.log('Submitting feedback:', { conversationId: state.conversationId, messageId, feedback });
|
|
930
|
-
await apiClient.current.submitFeedback(state.conversationId, messageId, feedback);
|
|
1455
|
+
await apiClient.current.submitFeedback(state.conversationId, messageId, feedback, meta);
|
|
931
1456
|
// Update message with feedback
|
|
932
1457
|
setState(prev => ({
|
|
933
1458
|
...prev,
|
|
@@ -961,9 +1486,6 @@ function useChat(options) {
|
|
|
961
1486
|
startedAt: entry.lastUpdated,
|
|
962
1487
|
})));
|
|
963
1488
|
}, [widgetId, state.config?.settings.persistConversation]);
|
|
964
|
-
/**
|
|
965
|
-
* Switch to a different conversation (from localStorage first, then fetch from server if needed)
|
|
966
|
-
*/
|
|
967
1489
|
const switchConversation = react.useCallback(async (conversationId) => {
|
|
968
1490
|
const persistConversation = state.config?.settings.persistConversation ?? true;
|
|
969
1491
|
// First try to load from localStorage
|
|
@@ -973,25 +1495,33 @@ function useChat(options) {
|
|
|
973
1495
|
setState(prev => ({
|
|
974
1496
|
...prev,
|
|
975
1497
|
conversationId: stored.conversationId,
|
|
976
|
-
messages: stored.messages,
|
|
1498
|
+
messages: hydrateMessages(stored.messages),
|
|
977
1499
|
}));
|
|
978
1500
|
setActiveConversation(widgetId, conversationId);
|
|
979
1501
|
return;
|
|
980
1502
|
}
|
|
981
1503
|
}
|
|
982
|
-
// If not in localStorage, fetch from server
|
|
983
1504
|
setState(prev => ({ ...prev, isLoading: true, error: null }));
|
|
984
1505
|
try {
|
|
985
1506
|
const conversation = await apiClient.current.getOrCreateConversation(conversationId);
|
|
1507
|
+
const hydratedMessages = hydrateMessages(conversation.messages);
|
|
1508
|
+
// Clear old resume callbacks
|
|
1509
|
+
state.messages.forEach(message => {
|
|
1510
|
+
if (message.action?.toolCallId) {
|
|
1511
|
+
unregisterActionResumeCallback(message.action.toolCallId);
|
|
1512
|
+
}
|
|
1513
|
+
});
|
|
986
1514
|
setState(prev => ({
|
|
987
1515
|
...prev,
|
|
988
1516
|
conversationId: conversation.id,
|
|
989
|
-
messages:
|
|
1517
|
+
messages: hydratedMessages,
|
|
990
1518
|
isLoading: false,
|
|
991
1519
|
}));
|
|
1520
|
+
// Setup new resume callbacks
|
|
1521
|
+
setupActionResumeCallbacks(hydratedMessages, apiClient.current, conversation.id, setState, onMessage ?? (() => { }));
|
|
992
1522
|
// Save to local storage
|
|
993
1523
|
if (persistConversation && isStorageAvailable()) {
|
|
994
|
-
saveConversation(widgetId, conversation.id,
|
|
1524
|
+
saveConversation(widgetId, conversation.id, hydratedMessages);
|
|
995
1525
|
}
|
|
996
1526
|
}
|
|
997
1527
|
catch (error) {
|
|
@@ -999,9 +1529,6 @@ function useChat(options) {
|
|
|
999
1529
|
setState(prev => ({ ...prev, isLoading: false, error: errorInfo.message }));
|
|
1000
1530
|
}
|
|
1001
1531
|
}, [widgetId, state.config?.settings.persistConversation]);
|
|
1002
|
-
/**
|
|
1003
|
-
* Start a new conversation (keeps history)
|
|
1004
|
-
*/
|
|
1005
1532
|
const startNewConversation = react.useCallback(() => {
|
|
1006
1533
|
setState(prev => ({
|
|
1007
1534
|
...prev,
|
|
@@ -1009,15 +1536,11 @@ function useChat(options) {
|
|
|
1009
1536
|
conversationId: '',
|
|
1010
1537
|
error: null,
|
|
1011
1538
|
}));
|
|
1012
|
-
// Clear active conversation but keep history
|
|
1013
1539
|
const persistConversation = state.config?.settings.persistConversation ?? true;
|
|
1014
1540
|
if (persistConversation && isStorageAvailable()) {
|
|
1015
1541
|
clearConversation(widgetId);
|
|
1016
1542
|
}
|
|
1017
1543
|
}, [widgetId, state.config?.settings.persistConversation]);
|
|
1018
|
-
/**
|
|
1019
|
-
* Delete a conversation from history
|
|
1020
|
-
*/
|
|
1021
1544
|
const deleteConversation$1 = react.useCallback((conversationId) => {
|
|
1022
1545
|
const persistConversation = state.config?.settings.persistConversation ?? true;
|
|
1023
1546
|
if (!persistConversation || !isStorageAvailable()) {
|
|
@@ -27871,12 +28394,12 @@ const Message = ({ message, showTimestamp = true, enableFeedback = true, showSou
|
|
|
27871
28394
|
hour12: true,
|
|
27872
28395
|
});
|
|
27873
28396
|
};
|
|
27874
|
-
const
|
|
28397
|
+
const role = message.message.role;
|
|
27875
28398
|
const isError = message.isError || false;
|
|
27876
|
-
const isTool =
|
|
27877
|
-
const isAssistant =
|
|
27878
|
-
const isSystem =
|
|
27879
|
-
const isHuman =
|
|
28399
|
+
const isTool = role === "tool";
|
|
28400
|
+
const isAssistant = role === "assistant";
|
|
28401
|
+
const isSystem = role === "system";
|
|
28402
|
+
const isHuman = role === "user";
|
|
27880
28403
|
// Tool messages are now handled by ToolMessageGroup in MessageList
|
|
27881
28404
|
if (isTool) {
|
|
27882
28405
|
return null;
|
|
@@ -27887,7 +28410,10 @@ const Message = ({ message, showTimestamp = true, enableFeedback = true, showSou
|
|
|
27887
28410
|
const hasContent = aiContent.trim().length > 0;
|
|
27888
28411
|
if (!hasContent)
|
|
27889
28412
|
return null;
|
|
27890
|
-
|
|
28413
|
+
const actionRenderer = message.action
|
|
28414
|
+
? getActionRenderer(message.action.implementation)
|
|
28415
|
+
: undefined;
|
|
28416
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-message assistant ${isError ? 'error' : ''}`, children: [jsxRuntime.jsxs("div", { className: "ai-chat-message-content", children: [isError && (jsxRuntime.jsxs("div", { className: "ai-chat-error-indicator", children: [jsxRuntime.jsx("span", { className: "error-icon", children: "\u26A0\uFE0F" }), jsxRuntime.jsx("span", { className: "error-text", children: "Error" })] })), jsxRuntime.jsx(Markdown, { remarkPlugins: [remarkGfm], children: aiContent })] }), actionRenderer && message.action && actionRenderer(message), showTimestamp && (jsxRuntime.jsxs("div", { className: "ai-chat-message-meta", children: [jsxRuntime.jsx("span", { className: "ai-chat-message-timestamp", children: formatTime(message.timestamp) }), enableFeedback && onFeedback && (jsxRuntime.jsx(FeedbackButtons, { messageId: message.id, currentFeedback: message.feedback, onFeedback: onFeedback }))] })), showSources && message.sources?.length > 0 && (jsxRuntime.jsx(Sources, { sources: message.sources, displayMode: sourceDisplayMode }))] }));
|
|
27891
28417
|
}
|
|
27892
28418
|
// System message rendering
|
|
27893
28419
|
if (isSystem) {
|
|
@@ -27895,7 +28421,7 @@ const Message = ({ message, showTimestamp = true, enableFeedback = true, showSou
|
|
|
27895
28421
|
}
|
|
27896
28422
|
// Human message rendering
|
|
27897
28423
|
if (isHuman) {
|
|
27898
|
-
const userContent = message.message.content.split('--- File Content ---')[0];
|
|
28424
|
+
const userContent = (message.message.content || '').split('--- File Content ---')[0];
|
|
27899
28425
|
return (jsxRuntime.jsxs("div", { className: "ai-chat-message user", children: [jsxRuntime.jsx("div", { className: "ai-chat-message-content", children: userContent }), showTimestamp && (jsxRuntime.jsx("div", { className: "ai-chat-message-meta", children: jsxRuntime.jsx("span", { className: "ai-chat-message-timestamp", children: formatTime(message.timestamp) }) }))] }));
|
|
27900
28426
|
}
|
|
27901
28427
|
return null;
|
|
@@ -27912,13 +28438,31 @@ const GearIcon = ({ spinning = false }) => (jsxRuntime.jsxs("svg", { className:
|
|
|
27912
28438
|
const CheckIcon = () => (jsxRuntime.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: jsxRuntime.jsx("polyline", { points: "20 6 9 17 4 12" }) }));
|
|
27913
28439
|
const ErrorIcon = () => (jsxRuntime.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: jsxRuntime.jsx("path", { d: "M18 6L6 18M6 6l12 12" }) }));
|
|
27914
28440
|
const ToolMessageGroup = ({ messages }) => {
|
|
27915
|
-
|
|
27916
|
-
|
|
27917
|
-
|
|
27918
|
-
|
|
27919
|
-
|
|
27920
|
-
|
|
27921
|
-
|
|
28441
|
+
// Check if any message is loading (for actions, check done flag; otherwise check isStreaming)
|
|
28442
|
+
const isAnyLoading = messages.some(m => {
|
|
28443
|
+
if (m.action) {
|
|
28444
|
+
return !(m.action.done ?? false);
|
|
28445
|
+
}
|
|
28446
|
+
return m.isStreaming;
|
|
28447
|
+
});
|
|
28448
|
+
const actionMessages = messages.filter(message => message.action);
|
|
28449
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-message tool", children: [jsxRuntime.jsxs("div", { className: "ai-chat-tool-row", children: [jsxRuntime.jsx(GearIcon, { spinning: isAnyLoading }), jsxRuntime.jsx("div", { className: "ai-chat-tool-badges", children: messages.map((message) => {
|
|
28450
|
+
const toolName = message.toolExecuting || message.message.name || 'Tool';
|
|
28451
|
+
const hasError = message.isError || false;
|
|
28452
|
+
// For actions, check if done flag is set; otherwise fall back to isStreaming
|
|
28453
|
+
const isDone = message.action ? (message.action.done ?? false) : !message.isStreaming;
|
|
28454
|
+
const isLoading = !isDone;
|
|
28455
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-tool-badge ${isLoading ? 'loading' : hasError ? 'error' : 'completed'}`, children: [!isLoading && (hasError ? jsxRuntime.jsx(ErrorIcon, {}) : jsxRuntime.jsx(CheckIcon, {})), jsxRuntime.jsx("span", { className: "tool-name", children: formatToolName(toolName) })] }, message.id));
|
|
28456
|
+
}) })] }), actionMessages.map((message) => {
|
|
28457
|
+
if (!message.action) {
|
|
28458
|
+
return null;
|
|
28459
|
+
}
|
|
28460
|
+
const renderer = getActionRenderer(message.action.implementation);
|
|
28461
|
+
if (!renderer) {
|
|
28462
|
+
return null;
|
|
28463
|
+
}
|
|
28464
|
+
return (jsxRuntime.jsx("div", { className: "ai-chat-tool-action", children: renderer(message) }, `action-${message.id}`));
|
|
28465
|
+
})] }));
|
|
27922
28466
|
};
|
|
27923
28467
|
|
|
27924
28468
|
const TypingIndicator = () => {
|
|
@@ -27943,6 +28487,14 @@ const SuggestedQuestions = ({ questions, onQuestionClick, }) => {
|
|
|
27943
28487
|
const MessageList = ({ messages, isTyping = false, showTypingIndicator = true, showTimestamps = true, enableFeedback = true, showSources = true, sourceDisplayMode = 'with-score', welcomeTitle, welcomeMessage, suggestedQuestions, onSuggestedQuestionClick, onFeedback, }) => {
|
|
27944
28488
|
const containerRef = react.useRef(null);
|
|
27945
28489
|
const messagesEndRef = react.useRef(null);
|
|
28490
|
+
// Check if there's an active action awaiting user input
|
|
28491
|
+
const hasActiveAction = react.useMemo(() => {
|
|
28492
|
+
return messages.some(msg => msg.action &&
|
|
28493
|
+
msg.action.state &&
|
|
28494
|
+
msg.action.state.status !== 'completed' &&
|
|
28495
|
+
msg.action.state.status !== 'booked' &&
|
|
28496
|
+
msg.action.state.status !== 'failed');
|
|
28497
|
+
}, [messages]);
|
|
27946
28498
|
// Auto-scroll to bottom only on initial mount/load
|
|
27947
28499
|
react.useEffect(() => {
|
|
27948
28500
|
const container = containerRef.current;
|
|
@@ -27971,16 +28523,16 @@ const MessageList = ({ messages, isTyping = false, showTypingIndicator = true, s
|
|
|
27971
28523
|
}
|
|
27972
28524
|
};
|
|
27973
28525
|
for (const message of messages) {
|
|
27974
|
-
if (message.message.
|
|
28526
|
+
if (message.message.role === "tool") {
|
|
27975
28527
|
// Add to current tool group
|
|
27976
28528
|
currentToolGroup.push(message);
|
|
27977
28529
|
}
|
|
27978
|
-
else if (message.message.
|
|
28530
|
+
else if (message.message.role === "user") {
|
|
27979
28531
|
// Human message breaks tool grouping
|
|
27980
28532
|
flushToolGroup();
|
|
27981
28533
|
result.push({ type: 'message', message });
|
|
27982
28534
|
}
|
|
27983
|
-
else if (message.message.
|
|
28535
|
+
else if (message.message.role === "assistant") {
|
|
27984
28536
|
// Skip empty AI messages
|
|
27985
28537
|
const content = (message.message.content || '').trim();
|
|
27986
28538
|
if (!content) {
|
|
@@ -28006,7 +28558,7 @@ const MessageList = ({ messages, isTyping = false, showTypingIndicator = true, s
|
|
|
28006
28558
|
return (jsxRuntime.jsx(ToolMessageGroup, { messages: item.messages }, `tool-group-${index}`));
|
|
28007
28559
|
}
|
|
28008
28560
|
return (jsxRuntime.jsx(Message, { message: item.message, showTimestamp: showTimestamps, enableFeedback: enableFeedback, showSources: showSources, sourceDisplayMode: sourceDisplayMode, onFeedback: onFeedback }, item.message.id));
|
|
28009
|
-
}), isTyping && showTypingIndicator && jsxRuntime.jsx(TypingIndicator, {}), jsxRuntime.jsx("div", { ref: messagesEndRef })] }));
|
|
28561
|
+
}), isTyping && showTypingIndicator && !hasActiveAction && jsxRuntime.jsx(TypingIndicator, {}), jsxRuntime.jsx("div", { ref: messagesEndRef })] }));
|
|
28010
28562
|
};
|
|
28011
28563
|
|
|
28012
28564
|
// Allowed file types
|
|
@@ -28096,7 +28648,6 @@ const ChatWindow = ({ messages, isLoading, isTyping, error, config, onSendMessag
|
|
|
28096
28648
|
conversations = [], onLoadConversations, onSwitchConversation, onStartNewConversation, onDeleteConversation, currentConversationId,
|
|
28097
28649
|
// Override props for live preview
|
|
28098
28650
|
headerTitleOverride, welcomeTitleOverride, welcomeMessageOverride, placeholderOverride, suggestedQuestionsOverride, }) => {
|
|
28099
|
-
console.log('[ChatWindow] Rendering ChatWindow component');
|
|
28100
28651
|
const appearance = config?.appearance;
|
|
28101
28652
|
const settings = config?.settings;
|
|
28102
28653
|
// Check if chat history should be shown (requires both persistConversation AND showChatHistory)
|
|
@@ -28107,13 +28658,6 @@ headerTitleOverride, welcomeTitleOverride, welcomeMessageOverride, placeholderOv
|
|
|
28107
28658
|
const welcomeTitle = welcomeTitleOverride ?? appearance?.welcomeTitle ?? '';
|
|
28108
28659
|
const welcomeMessage = welcomeMessageOverride ?? appearance?.welcomeMessage ?? '';
|
|
28109
28660
|
const inputPlaceholder = placeholderOverride ?? appearance?.placeholder ?? 'Ask me anything...';
|
|
28110
|
-
console.log('[ChatWindow] Appearance values:', {
|
|
28111
|
-
size,
|
|
28112
|
-
headerTitle,
|
|
28113
|
-
welcomeTitle,
|
|
28114
|
-
welcomeMessage,
|
|
28115
|
-
inputPlaceholder,
|
|
28116
|
-
});
|
|
28117
28661
|
// Track if history panel is open
|
|
28118
28662
|
const [showHistory, setShowHistory] = react.useState(false);
|
|
28119
28663
|
// History exit animation when starting a new chat from overview
|
|
@@ -28158,7 +28702,7 @@ headerTitleOverride, welcomeTitleOverride, welcomeMessageOverride, placeholderOv
|
|
|
28158
28702
|
};
|
|
28159
28703
|
// Check if message limit is reached
|
|
28160
28704
|
const maxMessages = settings?.maxMessagesPerSession;
|
|
28161
|
-
const userMessageCount = messages.filter(m => m.message.
|
|
28705
|
+
const userMessageCount = messages.filter(m => m.message.role === "user").length;
|
|
28162
28706
|
const isLimitReached = maxMessages ? userMessageCount >= maxMessages : false;
|
|
28163
28707
|
const handleQuestionClick = (question) => {
|
|
28164
28708
|
onSendMessage(question);
|
|
@@ -28568,34 +29112,7 @@ function createThemeObserver(element, callback) {
|
|
|
28568
29112
|
return observer;
|
|
28569
29113
|
}
|
|
28570
29114
|
|
|
28571
|
-
function styleInject(css, ref) {
|
|
28572
|
-
if ( ref === void 0 ) ref = {};
|
|
28573
|
-
var insertAt = ref.insertAt;
|
|
28574
|
-
|
|
28575
|
-
if (typeof document === 'undefined') { return; }
|
|
28576
|
-
|
|
28577
|
-
var head = document.head || document.getElementsByTagName('head')[0];
|
|
28578
|
-
var style = document.createElement('style');
|
|
28579
|
-
style.type = 'text/css';
|
|
28580
|
-
|
|
28581
|
-
if (insertAt === 'top') {
|
|
28582
|
-
if (head.firstChild) {
|
|
28583
|
-
head.insertBefore(style, head.firstChild);
|
|
28584
|
-
} else {
|
|
28585
|
-
head.appendChild(style);
|
|
28586
|
-
}
|
|
28587
|
-
} else {
|
|
28588
|
-
head.appendChild(style);
|
|
28589
|
-
}
|
|
28590
|
-
|
|
28591
|
-
if (style.styleSheet) {
|
|
28592
|
-
style.styleSheet.cssText = css;
|
|
28593
|
-
} else {
|
|
28594
|
-
style.appendChild(document.createTextNode(css));
|
|
28595
|
-
}
|
|
28596
|
-
}
|
|
28597
|
-
|
|
28598
|
-
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}";
|
|
29115
|
+
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}";
|
|
28599
29116
|
styleInject(css_248z);
|
|
28600
29117
|
|
|
28601
29118
|
// Icon components mapping
|
|
@@ -28699,16 +29216,6 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
28699
29216
|
mediaQuery.removeEventListener('change', handleMediaChange);
|
|
28700
29217
|
};
|
|
28701
29218
|
}, [config]);
|
|
28702
|
-
// Debug logging
|
|
28703
|
-
react.useEffect(() => {
|
|
28704
|
-
console.log('[ChatWidget] Config loaded:', config ? 'YES' : 'NO');
|
|
28705
|
-
if (config) {
|
|
28706
|
-
console.log('[ChatWidget] Config details:', {
|
|
28707
|
-
accentColor: config.appearance?.primaryColor,
|
|
28708
|
-
autoDetectedTheme,
|
|
28709
|
-
});
|
|
28710
|
-
}
|
|
28711
|
-
}, [config, autoDetectedTheme]);
|
|
28712
29219
|
// Handle auto-open
|
|
28713
29220
|
react.useEffect(() => {
|
|
28714
29221
|
if (config?.settings.autoOpen) {
|
|
@@ -28763,17 +29270,8 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
28763
29270
|
...customStyles,
|
|
28764
29271
|
...(zIndex !== undefined ? { '--widget-z-index': String(zIndex) } : {}),
|
|
28765
29272
|
};
|
|
28766
|
-
// Debug logging for theme and styles
|
|
28767
|
-
react.useEffect(() => {
|
|
28768
|
-
console.log('[ChatWidget] Theme info:', {
|
|
28769
|
-
effectiveTheme,
|
|
28770
|
-
autoDetectedTheme,
|
|
28771
|
-
accentColor,
|
|
28772
|
-
});
|
|
28773
|
-
}, [effectiveTheme, autoDetectedTheme, accentColor]);
|
|
28774
29273
|
const handleToggle = () => {
|
|
28775
29274
|
const newState = !isOpen;
|
|
28776
|
-
console.log('[ChatWidget] handleToggle called, setting isOpen to:', newState);
|
|
28777
29275
|
setIsOpen(newState);
|
|
28778
29276
|
if (newState) {
|
|
28779
29277
|
onOpen?.();
|
|
@@ -28788,10 +29286,8 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
28788
29286
|
// Don't render until config is loaded to avoid flash of unstyled content
|
|
28789
29287
|
// In preview mode, config is always available
|
|
28790
29288
|
if (!config && !previewMode) {
|
|
28791
|
-
console.log('[ChatWidget] Not rendering - config not loaded yet');
|
|
28792
29289
|
return null;
|
|
28793
29290
|
}
|
|
28794
|
-
console.log('[ChatWidget] Rendering widget', { isOpen, hasConfig: !!config });
|
|
28795
29291
|
// Get button icon based on state
|
|
28796
29292
|
const IconComponent = isOpen ? iconComponents.FiChevronDown : iconComponents.FiMessageCircle;
|
|
28797
29293
|
return (jsxRuntime.jsx("div", { ref: containerRef, className: `ai-chat-widget ${effectiveTheme}`, style: mergedStyles, children: jsxRuntime.jsxs("div", { ref: widgetRef, className: `ai-chat-widget-container ${effectivePosition}`, children: [isOpen && (jsxRuntime.jsx(ChatWindow, { messages: messages, isLoading: isLoading, isTyping: isTyping, error: error, config: config, onSendMessage: sendMessage, onClose: handleToggle, onFeedback: handleFeedback,
|