@chatwidgetai/chat-widget 0.3.0 → 0.3.6
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/ai-chat-widget.umd.js +1903 -437
- package/dist/ai-chat-widget.umd.js.map +1 -1
- package/dist/api/client.d.ts +11 -2
- package/dist/api/client.d.ts.map +1 -1
- package/dist/components/ChatWidget.d.ts +2 -1
- package/dist/components/ChatWidget.d.ts.map +1 -1
- package/dist/components/ChatWindow.d.ts +1 -1
- package/dist/components/ChatWindow.d.ts.map +1 -1
- package/dist/components/DataPolicyView.d.ts +14 -0
- package/dist/components/DataPolicyView.d.ts.map +1 -0
- package/dist/hooks/useChat/action-handler.d.ts +6 -0
- package/dist/hooks/useChat/action-handler.d.ts.map +1 -0
- package/dist/hooks/useChat/action-lifecycle.d.ts +19 -0
- package/dist/hooks/useChat/action-lifecycle.d.ts.map +1 -0
- package/dist/hooks/useChat/error-utils.d.ts +7 -0
- package/dist/hooks/useChat/error-utils.d.ts.map +1 -0
- package/dist/hooks/{useChat.d.ts → useChat/index.d.ts} +4 -2
- package/dist/hooks/useChat/index.d.ts.map +1 -0
- package/dist/hooks/useChat/message-hydration.d.ts +4 -0
- package/dist/hooks/useChat/message-hydration.d.ts.map +1 -0
- package/dist/hooks/useChat/stream-handlers.d.ts +27 -0
- package/dist/hooks/useChat/stream-handlers.d.ts.map +1 -0
- package/dist/hooks/useChat/stream-state.d.ts +8 -0
- package/dist/hooks/useChat/stream-state.d.ts.map +1 -0
- package/dist/hooks/useChat/types.d.ts +26 -0
- package/dist/hooks/useChat/types.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +1904 -438
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1904 -437
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +22 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/sse-parser.d.ts.map +1 -1
- package/package.json +7 -3
- package/dist/hooks/useChat.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -10,11 +10,14 @@ async function* parseSSEStream(response, validator) {
|
|
|
10
10
|
const reader = response.body.getReader();
|
|
11
11
|
const decoder = new TextDecoder();
|
|
12
12
|
let buffer = "";
|
|
13
|
+
let eventCount = 0;
|
|
13
14
|
try {
|
|
14
15
|
while (true) {
|
|
15
16
|
const { done, value } = await reader.read();
|
|
16
|
-
if (done)
|
|
17
|
+
if (done) {
|
|
18
|
+
console.log(`[SSE Parser] Stream ended normally after ${eventCount} events`);
|
|
17
19
|
break;
|
|
20
|
+
}
|
|
18
21
|
buffer += decoder.decode(value, { stream: true });
|
|
19
22
|
const chunks = buffer.split("\n\n");
|
|
20
23
|
buffer = chunks.pop() || "";
|
|
@@ -26,6 +29,7 @@ async function* parseSSEStream(response, validator) {
|
|
|
26
29
|
const data = JSON.parse(line.slice(6));
|
|
27
30
|
if (validator) {
|
|
28
31
|
if (validator(data)) {
|
|
32
|
+
eventCount++;
|
|
29
33
|
yield data;
|
|
30
34
|
}
|
|
31
35
|
else {
|
|
@@ -33,6 +37,7 @@ async function* parseSSEStream(response, validator) {
|
|
|
33
37
|
}
|
|
34
38
|
}
|
|
35
39
|
else {
|
|
40
|
+
eventCount++;
|
|
36
41
|
yield data;
|
|
37
42
|
}
|
|
38
43
|
}
|
|
@@ -45,6 +50,10 @@ async function* parseSSEStream(response, validator) {
|
|
|
45
50
|
}
|
|
46
51
|
}
|
|
47
52
|
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
console.error(`[SSE Parser] Stream error after ${eventCount} events:`, error);
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
48
57
|
finally {
|
|
49
58
|
reader.releaseLock();
|
|
50
59
|
}
|
|
@@ -178,7 +187,7 @@ class WidgetApiClient {
|
|
|
178
187
|
const result = await response.json();
|
|
179
188
|
return result.file;
|
|
180
189
|
}
|
|
181
|
-
async *sendAgentMessageStream(conversationId, message, fileIds) {
|
|
190
|
+
async *sendAgentMessageStream(conversationId, message, fileIds, signal) {
|
|
182
191
|
const headers = {
|
|
183
192
|
'Content-Type': 'application/json',
|
|
184
193
|
};
|
|
@@ -194,6 +203,7 @@ class WidgetApiClient {
|
|
|
194
203
|
fileIds,
|
|
195
204
|
timeZone: this.getTimeZone(),
|
|
196
205
|
}),
|
|
206
|
+
signal,
|
|
197
207
|
});
|
|
198
208
|
if (!response.ok) {
|
|
199
209
|
throw await buildApiError(response, 'Failed to send agent message');
|
|
@@ -202,7 +212,7 @@ class WidgetApiClient {
|
|
|
202
212
|
return typeof data === 'object' && data !== null && 'type' in data;
|
|
203
213
|
});
|
|
204
214
|
}
|
|
205
|
-
async *continueAgentMessageStream(conversationId, toolCallId, state) {
|
|
215
|
+
async *continueAgentMessageStream(conversationId, toolCallId, state, signal) {
|
|
206
216
|
const headers = {
|
|
207
217
|
'Content-Type': 'application/json',
|
|
208
218
|
};
|
|
@@ -218,6 +228,7 @@ class WidgetApiClient {
|
|
|
218
228
|
state,
|
|
219
229
|
timeZone: this.getTimeZone(),
|
|
220
230
|
}),
|
|
231
|
+
signal,
|
|
221
232
|
});
|
|
222
233
|
if (!response.ok) {
|
|
223
234
|
throw await buildApiError(response, 'Failed to continue agent');
|
|
@@ -226,6 +237,33 @@ class WidgetApiClient {
|
|
|
226
237
|
return typeof data === 'object' && data !== null && 'type' in data;
|
|
227
238
|
});
|
|
228
239
|
}
|
|
240
|
+
async *dismissAgentMessageStream(conversationId, toolCallId, signal) {
|
|
241
|
+
const headers = {
|
|
242
|
+
'Content-Type': 'application/json',
|
|
243
|
+
};
|
|
244
|
+
if (this.config.currentRoute) {
|
|
245
|
+
headers['X-Current-Route'] = this.config.currentRoute;
|
|
246
|
+
}
|
|
247
|
+
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/agent/dismiss`, {
|
|
248
|
+
method: 'POST',
|
|
249
|
+
headers,
|
|
250
|
+
body: JSON.stringify({
|
|
251
|
+
conversationId: conversationId,
|
|
252
|
+
toolCallId,
|
|
253
|
+
reason: "user",
|
|
254
|
+
}),
|
|
255
|
+
signal,
|
|
256
|
+
});
|
|
257
|
+
if (response.status === 204) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (!response.ok) {
|
|
261
|
+
throw await buildApiError(response, 'Failed to dismiss action');
|
|
262
|
+
}
|
|
263
|
+
yield* parseSSEStream(response, (data) => {
|
|
264
|
+
return typeof data === 'object' && data !== null && 'type' in data;
|
|
265
|
+
});
|
|
266
|
+
}
|
|
229
267
|
/**
|
|
230
268
|
* Submit feedback for a message
|
|
231
269
|
*/
|
|
@@ -288,6 +326,27 @@ class WidgetApiClient {
|
|
|
288
326
|
return [];
|
|
289
327
|
}
|
|
290
328
|
}
|
|
329
|
+
/**
|
|
330
|
+
* Create a demo conversation with preset messages
|
|
331
|
+
* Used when demo animation completes to persist the demo conversation to the database
|
|
332
|
+
*/
|
|
333
|
+
async createDemoConversation(userMessage, assistantMessage) {
|
|
334
|
+
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/demo-conversation`, {
|
|
335
|
+
method: 'POST',
|
|
336
|
+
headers: {
|
|
337
|
+
'Content-Type': 'application/json',
|
|
338
|
+
},
|
|
339
|
+
body: JSON.stringify({
|
|
340
|
+
userMessage,
|
|
341
|
+
assistantMessage,
|
|
342
|
+
timeZone: this.getTimeZone(),
|
|
343
|
+
}),
|
|
344
|
+
});
|
|
345
|
+
if (!response.ok) {
|
|
346
|
+
throw await buildApiError(response, 'Failed to create demo conversation');
|
|
347
|
+
}
|
|
348
|
+
return response.json();
|
|
349
|
+
}
|
|
291
350
|
/**
|
|
292
351
|
* Validate widget access
|
|
293
352
|
*/
|
|
@@ -27118,22 +27177,19 @@ function ScrollButton({ onClick, visible, className = '' }) {
|
|
|
27118
27177
|
const formatToolName = (name) => {
|
|
27119
27178
|
return name
|
|
27120
27179
|
.replace(/^(action_|tool_)/, '')
|
|
27180
|
+
.replace(/-/g, '_')
|
|
27121
27181
|
.split('_')
|
|
27122
27182
|
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
27123
27183
|
.join(' ');
|
|
27124
27184
|
};
|
|
27125
|
-
const GearIcon = ({ spinning = false }) => (jsxRuntime.jsxs("svg", { className: `ai-chat-tool-gear ${spinning ? 'spinning' : ''}`, width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("path", { d: "M12 20a8 8 0 1 0 0-16 8 8 0 0 0 0 16Z" }), jsxRuntime.jsx("path", { d: "M12 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z" }), jsxRuntime.jsx("path", { d: "M12 2v2" }), jsxRuntime.jsx("path", { d: "M12 22v-2" }), jsxRuntime.jsx("path", { d: "m17 20.66-1-1.73" }), jsxRuntime.jsx("path", { d: "M11 10.27 7 3.34" }), jsxRuntime.jsx("path", { d: "m20.66 17-1.73-1" }), jsxRuntime.jsx("path", { d: "m3.34 7 1.73 1" }), jsxRuntime.jsx("path", { d: "M14 12h8" }), jsxRuntime.jsx("path", { d: "M2 12h2" }), jsxRuntime.jsx("path", { d: "m20.66 7-1.73 1" }), jsxRuntime.jsx("path", { d: "m3.34 17 1.73-1" }), jsxRuntime.jsx("path", { d: "m17 3.34-1 1.73" }), jsxRuntime.jsx("path", { d: "m11 13.73-4 6.93" })] }));
|
|
27126
|
-
const CheckIcon$2 = () => (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" }) }));
|
|
27127
|
-
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" }) }));
|
|
27128
27185
|
function ToolIndicator({ badges, className = '' }) {
|
|
27129
|
-
|
|
27130
|
-
return (jsxRuntime.jsxs("div", { className: `ai-chat-tool-row ${className}`, children: [jsxRuntime.jsx(GearIcon, { spinning: isAnyLoading }), jsxRuntime.jsx("div", { className: "ai-chat-tool-badges", children: badges.map((badge) => (jsxRuntime.jsxs("div", { className: `ai-chat-tool-badge ${badge.status}`, children: [badge.status !== 'loading' && (badge.status === 'error' ? jsxRuntime.jsx(ErrorIcon, {}) : jsxRuntime.jsx(CheckIcon$2, {})), jsxRuntime.jsx("span", { className: "tool-name", children: formatToolName(badge.name) })] }, badge.id))) })] }));
|
|
27186
|
+
return (jsxRuntime.jsx("div", { className: `ai-chat-tool-row ${className}`, children: jsxRuntime.jsx("div", { className: "ai-chat-tool-badges", children: badges.map((badge) => (jsxRuntime.jsx("div", { className: `ai-chat-tool-badge ${badge.status}`, children: jsxRuntime.jsx("span", { className: "tool-name", children: formatToolName(badge.name) }) }, badge.id))) }) }));
|
|
27131
27187
|
}
|
|
27132
27188
|
|
|
27133
27189
|
// SVG Icon components
|
|
27134
27190
|
const ThumbsUpIcon = () => (jsxRuntime.jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsxRuntime.jsx("path", { d: "M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3" }) }));
|
|
27135
27191
|
const ThumbsDownIcon = () => (jsxRuntime.jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsxRuntime.jsx("path", { d: "M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17" }) }));
|
|
27136
|
-
const CheckIcon$
|
|
27192
|
+
const CheckIcon$2 = () => (jsxRuntime.jsx("svg", { 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" }) }));
|
|
27137
27193
|
const FeedbackButtons = ({ messageId, currentFeedback, onFeedback, }) => {
|
|
27138
27194
|
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
|
27139
27195
|
const [submitted, setSubmitted] = React.useState(false);
|
|
@@ -27154,10 +27210,10 @@ const FeedbackButtons = ({ messageId, currentFeedback, onFeedback, }) => {
|
|
|
27154
27210
|
setIsSubmitting(false);
|
|
27155
27211
|
}
|
|
27156
27212
|
};
|
|
27157
|
-
return (jsxRuntime.jsxs("div", { className: `ai-chat-feedback ${submitted ? 'submitted' : ''}`, children: [jsxRuntime.jsxs("div", { className: "ai-chat-feedback-buttons", children: [jsxRuntime.jsx("button", { className: `ai-chat-feedback-button ${currentFeedback === 'positive' ? 'active' : ''}`, onClick: () => handleFeedback('positive'), disabled: isDisabled, "aria-label": "Helpful", title: "This was helpful", children: jsxRuntime.jsx(ThumbsUpIcon, {}) }), jsxRuntime.jsx("button", { className: `ai-chat-feedback-button ${currentFeedback === 'negative' ? 'active' : ''}`, onClick: () => handleFeedback('negative'), disabled: isDisabled, "aria-label": "Not helpful", title: "This was not helpful", children: jsxRuntime.jsx(ThumbsDownIcon, {}) })] }), submitted && (jsxRuntime.jsxs("div", { className: "ai-chat-feedback-message", "aria-live": "polite", children: [jsxRuntime.jsx("span", { className: "ai-chat-feedback-checkmark", children: jsxRuntime.jsx(CheckIcon$
|
|
27213
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-feedback ${submitted ? 'submitted' : ''}`, children: [jsxRuntime.jsxs("div", { className: "ai-chat-feedback-buttons", children: [jsxRuntime.jsx("button", { className: `ai-chat-feedback-button ${currentFeedback === 'positive' ? 'active' : ''}`, onClick: () => handleFeedback('positive'), disabled: isDisabled, "aria-label": "Helpful", title: "This was helpful", children: jsxRuntime.jsx(ThumbsUpIcon, {}) }), jsxRuntime.jsx("button", { className: `ai-chat-feedback-button ${currentFeedback === 'negative' ? 'active' : ''}`, onClick: () => handleFeedback('negative'), disabled: isDisabled, "aria-label": "Not helpful", title: "This was not helpful", children: jsxRuntime.jsx(ThumbsDownIcon, {}) })] }), submitted && (jsxRuntime.jsxs("div", { className: "ai-chat-feedback-message", "aria-live": "polite", children: [jsxRuntime.jsx("span", { className: "ai-chat-feedback-checkmark", children: jsxRuntime.jsx(CheckIcon$2, {}) }), jsxRuntime.jsx("span", { className: "ai-chat-feedback-text", children: "Thanks for feedback" })] }))] }));
|
|
27158
27214
|
};
|
|
27159
27215
|
|
|
27160
|
-
const Message = ({ message, showTimestamp = true, enableFeedback = true, onFeedback,
|
|
27216
|
+
const Message = ({ message, showTimestamp = true, enableFeedback = true, onFeedback, }) => {
|
|
27161
27217
|
const formatTime = (timestamp) => {
|
|
27162
27218
|
const date = typeof timestamp === 'string' ? new Date(timestamp) : timestamp;
|
|
27163
27219
|
return date.toLocaleTimeString('en-US', {
|
|
@@ -27177,15 +27233,15 @@ const Message = ({ message, showTimestamp = true, enableFeedback = true, onFeedb
|
|
|
27177
27233
|
return null;
|
|
27178
27234
|
}
|
|
27179
27235
|
// AI message rendering
|
|
27236
|
+
// Note: Actions are rendered by ToolMessageGroup for tool messages, not here
|
|
27180
27237
|
if (isAssistant) {
|
|
27181
27238
|
const aiContent = message.message.content || '';
|
|
27182
27239
|
const hasContent = aiContent.trim().length > 0;
|
|
27183
27240
|
if (!hasContent)
|
|
27184
27241
|
return null;
|
|
27185
|
-
|
|
27186
|
-
|
|
27187
|
-
|
|
27188
|
-
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, accentColor), 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 }))] }))] }));
|
|
27242
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-message assistant ${isError ? 'error' : ''}`, children: [jsxRuntime.jsx("div", { className: "ai-chat-message-content", children: jsxRuntime.jsx(Markdown, { remarkPlugins: [remarkGfm], components: {
|
|
27243
|
+
table: ({ children, ...props }) => (jsxRuntime.jsx("div", { className: "table-wrapper", children: jsxRuntime.jsx("div", { className: "table-scroll", children: jsxRuntime.jsx("table", { ...props, children: children }) }) })),
|
|
27244
|
+
}, children: aiContent }) }), 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 }))] }))] }));
|
|
27189
27245
|
}
|
|
27190
27246
|
// System message rendering
|
|
27191
27247
|
if (isSystem) {
|
|
@@ -27199,35 +27255,86 @@ const Message = ({ message, showTimestamp = true, enableFeedback = true, onFeedb
|
|
|
27199
27255
|
return null;
|
|
27200
27256
|
};
|
|
27201
27257
|
|
|
27202
|
-
|
|
27203
|
-
|
|
27258
|
+
// Centralized action state logic
|
|
27259
|
+
function isActionComplete(state) {
|
|
27260
|
+
if (!state)
|
|
27261
|
+
return false;
|
|
27262
|
+
const TERMINAL_STATUSES = [
|
|
27263
|
+
'completed', 'booked', 'scheduled', 'cancelled', 'failed', 'error',
|
|
27264
|
+
'displaying', 'clicked', 'contacted', 'submitted', 'sent'
|
|
27265
|
+
];
|
|
27266
|
+
const status = state.status;
|
|
27267
|
+
if (typeof status === 'string' && TERMINAL_STATUSES.includes(status)) {
|
|
27268
|
+
return true;
|
|
27269
|
+
}
|
|
27270
|
+
// For non-halting actions (query_contact_directory, etc.) with results/success
|
|
27271
|
+
if (!status && (state.success !== undefined || state.results !== undefined)) {
|
|
27272
|
+
return true;
|
|
27273
|
+
}
|
|
27274
|
+
return false;
|
|
27275
|
+
}
|
|
27276
|
+
function isActionLoading(message) {
|
|
27277
|
+
if (!message.action)
|
|
27278
|
+
return false;
|
|
27279
|
+
if (message.action.done)
|
|
27280
|
+
return false;
|
|
27281
|
+
if (message.isStreaming)
|
|
27282
|
+
return true;
|
|
27283
|
+
const state = message.action.state;
|
|
27284
|
+
return !isActionComplete(state);
|
|
27285
|
+
}
|
|
27286
|
+
const ToolMessageGroup = ({ messages, getActionRenderer, showToolIndicator = true, accentColor, variant, onActionDismiss, }) => {
|
|
27287
|
+
const visibleMessages = messages.filter(message => !message.action?.hidden);
|
|
27288
|
+
const actionMessages = visibleMessages.filter(message => message.action);
|
|
27289
|
+
// Debug logging
|
|
27290
|
+
console.log('[DEBUG ToolMessageGroup] ========================================');
|
|
27291
|
+
console.log('[DEBUG ToolMessageGroup] Total messages:', messages.length);
|
|
27292
|
+
console.log('[DEBUG ToolMessageGroup] Messages with action:', actionMessages.length);
|
|
27293
|
+
console.log('[DEBUG ToolMessageGroup] hasGetActionRenderer:', !!getActionRenderer);
|
|
27294
|
+
messages.forEach((msg, i) => {
|
|
27295
|
+
console.log(`[DEBUG ToolMessageGroup] Message ${i}:`, {
|
|
27296
|
+
id: msg.id,
|
|
27297
|
+
role: msg.message.role,
|
|
27298
|
+
hasAction: !!msg.action,
|
|
27299
|
+
actionImpl: msg.action?.implementation,
|
|
27300
|
+
toolExecuting: msg.toolExecuting,
|
|
27301
|
+
});
|
|
27302
|
+
});
|
|
27303
|
+
actionMessages.forEach((msg, i) => {
|
|
27304
|
+
const impl = msg.action?.implementation || '';
|
|
27305
|
+
const renderer = getActionRenderer?.(impl);
|
|
27306
|
+
console.log(`[DEBUG ToolMessageGroup] Action ${i}:`, {
|
|
27307
|
+
implementation: impl,
|
|
27308
|
+
hasRenderer: !!renderer,
|
|
27309
|
+
rendererType: renderer ? typeof renderer : 'undefined',
|
|
27310
|
+
state: msg.action?.state,
|
|
27311
|
+
});
|
|
27312
|
+
});
|
|
27204
27313
|
// If tool indicator is hidden AND there are no action cards to render, don't render anything
|
|
27205
|
-
// This prevents empty wrapper divs that cause layout jumping
|
|
27206
27314
|
if (!showToolIndicator && actionMessages.length === 0) {
|
|
27207
27315
|
return null;
|
|
27208
27316
|
}
|
|
27209
|
-
const badges =
|
|
27317
|
+
const badges = visibleMessages.map((message) => {
|
|
27210
27318
|
const toolName = message.toolExecuting || message.message.name || 'Tool';
|
|
27211
27319
|
const hasError = message.isError || false;
|
|
27212
|
-
const
|
|
27213
|
-
const actionStatus = actionState?.status;
|
|
27214
|
-
const terminalStatuses = ['completed', 'booked', 'scheduled', 'failed', 'cancelled', 'displaying', 'clicked'];
|
|
27215
|
-
const isTerminalStatus = actionStatus && terminalStatuses.includes(actionStatus);
|
|
27216
|
-
const isDone = message.action ? (message.action.done ?? isTerminalStatus ?? false) : !message.isStreaming;
|
|
27217
|
-
const isLoading = !isDone;
|
|
27320
|
+
const loading = isActionLoading(message);
|
|
27218
27321
|
return {
|
|
27219
27322
|
id: message.id,
|
|
27220
27323
|
name: toolName,
|
|
27221
|
-
status:
|
|
27324
|
+
status: loading ? 'loading' : hasError ? 'error' : 'completed',
|
|
27222
27325
|
};
|
|
27223
27326
|
});
|
|
27224
27327
|
return (jsxRuntime.jsxs("div", { className: "ai-chat-message tool", children: [showToolIndicator && jsxRuntime.jsx(ToolIndicator, { badges: badges }), actionMessages.map((message) => {
|
|
27225
|
-
if (!message.action || !getActionRenderer)
|
|
27328
|
+
if (!message.action || !getActionRenderer) {
|
|
27329
|
+
console.log('[ToolMessageGroup] Skipping - no action or renderer:', { hasAction: !!message.action, hasGetRenderer: !!getActionRenderer });
|
|
27226
27330
|
return null;
|
|
27331
|
+
}
|
|
27227
27332
|
const renderer = getActionRenderer(message.action.implementation);
|
|
27228
|
-
if (!renderer)
|
|
27333
|
+
if (!renderer) {
|
|
27334
|
+
console.log('[ToolMessageGroup] No renderer for:', message.action.implementation);
|
|
27229
27335
|
return null;
|
|
27230
|
-
|
|
27336
|
+
}
|
|
27337
|
+
return (jsxRuntime.jsx("div", { className: "ai-chat-tool-action", children: renderer(message, accentColor, variant, onActionDismiss) }, `action-${message.id}`));
|
|
27231
27338
|
})] }));
|
|
27232
27339
|
};
|
|
27233
27340
|
|
|
@@ -27289,12 +27396,17 @@ const FollowUpSuggestions = ({ suggestions, onQuestionClick, onActionClick, acce
|
|
|
27289
27396
|
};
|
|
27290
27397
|
|
|
27291
27398
|
const MessageList = (props) => {
|
|
27292
|
-
const { messages, isTyping = false, showTypingIndicator = true, showTimestamps = true, showToolCalls = false, enableFeedback = true, welcomeTitle, welcomeMessage, suggestedQuestions, accentColor, onSuggestedQuestionClick, onActionClick, onFeedback, onScrollStateChange, getActionRenderer, } = props;
|
|
27399
|
+
const { messages, isTyping = false, showTypingIndicator = true, showTimestamps = true, showToolCalls = false, enableFeedback = true, welcomeTitle, welcomeMessage, suggestedQuestions, accentColor, onSuggestedQuestionClick, onActionClick, onActionDismiss, onFeedback, onScrollStateChange, getActionRenderer, variant, } = props;
|
|
27293
27400
|
const containerRef = React.useRef(null);
|
|
27294
27401
|
const messagesEndRef = React.useRef(null);
|
|
27295
27402
|
const [showScrollButton, setShowScrollButton] = React.useState(false);
|
|
27296
27403
|
const prevMessageCountRef = React.useRef(0);
|
|
27297
|
-
const
|
|
27404
|
+
const visibleMessages = React.useMemo(() => messages.filter((message) => !message.action?.hidden), [messages]);
|
|
27405
|
+
const hasActiveAction = React.useMemo(() => {
|
|
27406
|
+
// Find the last tool message and check if its action is still active (not done)
|
|
27407
|
+
const lastToolMsg = [...visibleMessages].reverse().find(msg => msg.message.role === 'tool');
|
|
27408
|
+
return lastToolMsg?.action && lastToolMsg.action.done !== true;
|
|
27409
|
+
}, [visibleMessages]);
|
|
27298
27410
|
const checkScrollPosition = React.useCallback(() => {
|
|
27299
27411
|
const c = containerRef.current;
|
|
27300
27412
|
if (!c)
|
|
@@ -27317,7 +27429,7 @@ const MessageList = (props) => {
|
|
|
27317
27429
|
const c = containerRef.current;
|
|
27318
27430
|
if (!c)
|
|
27319
27431
|
return;
|
|
27320
|
-
const count =
|
|
27432
|
+
const count = visibleMessages.length;
|
|
27321
27433
|
const isNew = count > prevMessageCountRef.current;
|
|
27322
27434
|
prevMessageCountRef.current = count;
|
|
27323
27435
|
if (count === 0) {
|
|
@@ -27328,17 +27440,33 @@ const MessageList = (props) => {
|
|
|
27328
27440
|
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
27329
27441
|
}
|
|
27330
27442
|
checkScrollPosition();
|
|
27331
|
-
}, [
|
|
27443
|
+
}, [visibleMessages, isTyping, checkScrollPosition]);
|
|
27332
27444
|
const groupedMessages = React.useMemo(() => {
|
|
27445
|
+
console.log('[DEBUG MessageList] ========================================');
|
|
27446
|
+
console.log('[DEBUG MessageList] Processing messages:', visibleMessages.length);
|
|
27447
|
+
visibleMessages.forEach((m, i) => {
|
|
27448
|
+
console.log(`[DEBUG MessageList] Message ${i}:`, {
|
|
27449
|
+
id: m.id,
|
|
27450
|
+
role: m.message.role,
|
|
27451
|
+
hasAction: !!m.action,
|
|
27452
|
+
actionImpl: m.action?.implementation,
|
|
27453
|
+
content: (m.message.content || '').substring(0, 50),
|
|
27454
|
+
});
|
|
27455
|
+
});
|
|
27333
27456
|
const result = [];
|
|
27334
27457
|
let toolGroup = [];
|
|
27335
|
-
const flush = () => {
|
|
27336
|
-
|
|
27337
|
-
|
|
27338
|
-
|
|
27339
|
-
|
|
27340
|
-
|
|
27458
|
+
const flush = () => {
|
|
27459
|
+
if (toolGroup.length) {
|
|
27460
|
+
console.log('[DEBUG MessageList] Flushing tool group with', toolGroup.length, 'messages');
|
|
27461
|
+
result.push({ type: 'tool-group', messages: [...toolGroup] });
|
|
27462
|
+
toolGroup = [];
|
|
27463
|
+
}
|
|
27464
|
+
};
|
|
27465
|
+
for (const m of visibleMessages) {
|
|
27466
|
+
if (m.message.role === 'tool') {
|
|
27467
|
+
console.log('[DEBUG MessageList] Adding to tool group:', m.id);
|
|
27341
27468
|
toolGroup.push(m);
|
|
27469
|
+
}
|
|
27342
27470
|
else if (m.message.role === 'user') {
|
|
27343
27471
|
flush();
|
|
27344
27472
|
result.push({ type: 'message', message: m });
|
|
@@ -27350,6 +27478,7 @@ const MessageList = (props) => {
|
|
|
27350
27478
|
flush();
|
|
27351
27479
|
result.push({ type: 'message', message: m });
|
|
27352
27480
|
}
|
|
27481
|
+
// Don't flush on empty assistant messages - let tools continue grouping
|
|
27353
27482
|
}
|
|
27354
27483
|
else {
|
|
27355
27484
|
flush();
|
|
@@ -27357,18 +27486,27 @@ const MessageList = (props) => {
|
|
|
27357
27486
|
}
|
|
27358
27487
|
}
|
|
27359
27488
|
flush();
|
|
27489
|
+
console.log('[DEBUG MessageList] Final grouped result:', result.length, 'items');
|
|
27490
|
+
result.forEach((item, i) => {
|
|
27491
|
+
if (item.type === 'tool-group') {
|
|
27492
|
+
console.log(`[DEBUG MessageList] Group ${i}: tool-group with ${item.messages.length} messages`);
|
|
27493
|
+
}
|
|
27494
|
+
else {
|
|
27495
|
+
console.log(`[DEBUG MessageList] Group ${i}: message (${item.message.message.role})`);
|
|
27496
|
+
}
|
|
27497
|
+
});
|
|
27360
27498
|
return result;
|
|
27361
|
-
}, [
|
|
27362
|
-
const hasSuggestions =
|
|
27499
|
+
}, [visibleMessages]);
|
|
27500
|
+
const hasSuggestions = visibleMessages.length === 0 && onSuggestedQuestionClick && suggestedQuestions?.length;
|
|
27363
27501
|
const showWelcome = welcomeTitle || welcomeMessage || hasSuggestions;
|
|
27364
27502
|
return (jsxRuntime.jsxs("div", { ref: containerRef, className: "ai-chat-messages", role: "log", "aria-live": "polite", children: [showWelcome && (jsxRuntime.jsxs("div", { className: "ai-chat-welcome", children: [welcomeTitle && jsxRuntime.jsx("div", { className: "ai-chat-welcome-title", children: welcomeTitle }), welcomeMessage && jsxRuntime.jsx("div", { className: "ai-chat-welcome-text", children: welcomeMessage }), hasSuggestions && jsxRuntime.jsx(SuggestedQuestions, { questions: suggestedQuestions, onQuestionClick: onSuggestedQuestionClick })] })), groupedMessages.map((item, i) => {
|
|
27365
27503
|
if (item.type === 'tool-group') {
|
|
27366
|
-
return jsxRuntime.jsx(ToolMessageGroup, { messages: item.messages, getActionRenderer: getActionRenderer, showToolIndicator: showToolCalls, accentColor: accentColor }, `tg-${i}`);
|
|
27504
|
+
return (jsxRuntime.jsx(ToolMessageGroup, { messages: item.messages, getActionRenderer: getActionRenderer, showToolIndicator: showToolCalls, accentColor: accentColor, variant: variant, onActionDismiss: onActionDismiss }, `tg-${i}`));
|
|
27367
27505
|
}
|
|
27368
27506
|
const isLast = i === groupedMessages.length - 1;
|
|
27369
27507
|
const hasFollowUp = item.message.message.role === 'assistant' && item.message.suggestions?.length && isLast && !isTyping;
|
|
27370
27508
|
return (jsxRuntime.jsxs(React.Fragment, { children: [jsxRuntime.jsx(Message, { message: item.message, showTimestamp: showTimestamps, onFeedback: onFeedback, getActionRenderer: getActionRenderer, accentColor: accentColor }), hasFollowUp && onSuggestedQuestionClick && jsxRuntime.jsx(FollowUpSuggestions, { suggestions: item.message.suggestions, onQuestionClick: onSuggestedQuestionClick, onActionClick: onActionClick, accentColor: accentColor })] }, item.message.id));
|
|
27371
|
-
}), isTyping && showTypingIndicator && !hasActiveAction &&
|
|
27509
|
+
}), isTyping && showTypingIndicator && !hasActiveAction && visibleMessages.length > 0 && jsxRuntime.jsx(TypingIndicator, {}), jsxRuntime.jsx("div", { ref: messagesEndRef })] }));
|
|
27372
27510
|
};
|
|
27373
27511
|
|
|
27374
27512
|
const ALLOWED_EXTENSIONS = ['.pdf', '.doc', '.docx', '.txt', '.md', '.csv'];
|
|
@@ -27382,7 +27520,7 @@ const formatFileSize = (bytes) => {
|
|
|
27382
27520
|
return (bytes / 1024).toFixed(1) + ' KB';
|
|
27383
27521
|
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
|
|
27384
27522
|
};
|
|
27385
|
-
const MessageInput = ({ onSend, placeholder = 'Type your message...', disabled = false, enableFileUpload = false, separateFromChat = true, }) => {
|
|
27523
|
+
const MessageInput = ({ onSend, placeholder = 'Type your message...', disabled = false, enableFileUpload = false, separateFromChat = true, showDataPolicy = true, onDataPolicyClick, }) => {
|
|
27386
27524
|
const [value, setValue] = React.useState('');
|
|
27387
27525
|
const [selectedFiles, setSelectedFiles] = React.useState([]);
|
|
27388
27526
|
const textareaRef = React.useRef(null);
|
|
@@ -27417,10 +27555,14 @@ const MessageInput = ({ onSend, placeholder = 'Type your message...', disabled =
|
|
|
27417
27555
|
}
|
|
27418
27556
|
};
|
|
27419
27557
|
const canSend = value.trim() || selectedFiles.length > 0;
|
|
27420
|
-
return (jsxRuntime.jsxs("div", { className: `ai-chat-input-container ${separateFromChat ? 'separate' : 'integrated'}`, children: [selectedFiles.length > 0 && (jsxRuntime.jsx("div", { className: "ai-chat-file-list", children: selectedFiles.map((file, index) => (jsxRuntime.jsxs("div", { className: "ai-chat-file-item", children: [jsxRuntime.jsx("span", { className: "ai-chat-file-extension", children: getFileExtension(file.name) }), jsxRuntime.jsxs("div", { className: "ai-chat-file-info", children: [jsxRuntime.jsx("span", { className: "ai-chat-file-name", children: file.name }), jsxRuntime.jsx("span", { className: "ai-chat-file-size", children: formatFileSize(file.size) })] }), jsxRuntime.jsx("button", { className: "ai-chat-file-remove", onClick: () => handleRemoveFile(index), "aria-label": "Remove file", children: jsxRuntime.jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: jsxRuntime.jsx("path", { d: "M18 6L6 18M6 6l12 12" }) }) })] }, index))) })), jsxRuntime.jsxs("div", { className: "ai-chat-input-wrapper", children: [enableFileUpload && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("input", { ref: fileInputRef, type: "file", onChange: handleFileSelect, style: { display: 'none' }, multiple: true, accept: ALLOWED_EXTENSIONS.join(',') }), jsxRuntime.jsx("button", { className: "ai-chat-file-button", onClick: () => fileInputRef.current?.click(), disabled: disabled, "aria-label": "Attach file", children: jsxRuntime.jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: jsxRuntime.jsx("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48" }) }) })] })), jsxRuntime.jsx("textarea", { ref: textareaRef, className: "ai-chat-input", value: value, onChange: (e) => setValue(e.target.value), onKeyDown: handleKeyDown, placeholder: placeholder, disabled: disabled, rows: 2, wrap: "soft", "aria-label": "Message input" }), jsxRuntime.jsx("button", { className: `ai-chat-send-button ${canSend ? 'active' : ''}`, onClick: handleSend, disabled: disabled || !canSend, "aria-label": "Send message", children: jsxRuntime.jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: [jsxRuntime.jsx("path", { d: "M12 19V5" }), jsxRuntime.jsx("path", { d: "M5 12l7-7 7 7" })] }) })] })] }));
|
|
27558
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-input-container ${separateFromChat ? 'separate' : 'integrated'}`, children: [selectedFiles.length > 0 && (jsxRuntime.jsx("div", { className: "ai-chat-file-list", children: selectedFiles.map((file, index) => (jsxRuntime.jsxs("div", { className: "ai-chat-file-item", children: [jsxRuntime.jsx("span", { className: "ai-chat-file-extension", children: getFileExtension(file.name) }), jsxRuntime.jsxs("div", { className: "ai-chat-file-info", children: [jsxRuntime.jsx("span", { className: "ai-chat-file-name", children: file.name }), jsxRuntime.jsx("span", { className: "ai-chat-file-size", children: formatFileSize(file.size) })] }), jsxRuntime.jsx("button", { className: "ai-chat-file-remove", onClick: () => handleRemoveFile(index), "aria-label": "Remove file", children: jsxRuntime.jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: jsxRuntime.jsx("path", { d: "M18 6L6 18M6 6l12 12" }) }) })] }, index))) })), jsxRuntime.jsxs("div", { className: "ai-chat-input-wrapper", children: [enableFileUpload && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("input", { ref: fileInputRef, type: "file", onChange: handleFileSelect, style: { display: 'none' }, multiple: true, accept: ALLOWED_EXTENSIONS.join(',') }), jsxRuntime.jsx("button", { className: "ai-chat-file-button", onClick: () => fileInputRef.current?.click(), disabled: disabled, "aria-label": "Attach file", children: jsxRuntime.jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: jsxRuntime.jsx("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48" }) }) })] })), jsxRuntime.jsx("textarea", { ref: textareaRef, className: "ai-chat-input", value: value, onChange: (e) => setValue(e.target.value), onKeyDown: handleKeyDown, placeholder: placeholder, disabled: disabled, rows: 2, wrap: "soft", "aria-label": "Message input" }), jsxRuntime.jsx("button", { className: `ai-chat-send-button ${canSend ? 'active' : ''}`, onClick: handleSend, disabled: disabled || !canSend, "aria-label": "Send message", children: jsxRuntime.jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: [jsxRuntime.jsx("path", { d: "M12 19V5" }), jsxRuntime.jsx("path", { d: "M5 12l7-7 7 7" })] }) })] }), showDataPolicy && (jsxRuntime.jsxs("div", { className: "ai-chat-data-policy", children: [jsxRuntime.jsx("span", { children: "AI-generated responses may be inaccurate." }), onDataPolicyClick && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [' ', jsxRuntime.jsx("button", { type: "button", className: "ai-chat-data-policy-link", onClick: onDataPolicyClick, children: "Privacy Notice" })] }))] }))] }));
|
|
27421
27559
|
};
|
|
27422
27560
|
|
|
27423
|
-
|
|
27561
|
+
const CloseButton = ({ onClick, className = "", ariaLabel = "Close", }) => {
|
|
27562
|
+
return (jsxRuntime.jsx("button", { type: "button", className: `ai-chat-action-close-btn ${className}`, onClick: onClick, "aria-label": ariaLabel, children: jsxRuntime.jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", stroke: "currentColor", strokeWidth: "2", children: jsxRuntime.jsx("path", { d: "M1 1L13 13M1 13L13 1" }) }) }));
|
|
27563
|
+
};
|
|
27564
|
+
|
|
27565
|
+
function groupSlotsByDate$1(slots) {
|
|
27424
27566
|
const grouped = new Map();
|
|
27425
27567
|
for (const slot of slots) {
|
|
27426
27568
|
if (!slot || typeof slot !== 'object' || !slot.startTime || typeof slot.startTime !== 'string') {
|
|
@@ -27434,7 +27576,7 @@ function groupSlotsByDate(slots) {
|
|
|
27434
27576
|
}
|
|
27435
27577
|
return grouped;
|
|
27436
27578
|
}
|
|
27437
|
-
function formatDate(dateStr) {
|
|
27579
|
+
function formatDate$1(dateStr) {
|
|
27438
27580
|
try {
|
|
27439
27581
|
const date = new Date(dateStr);
|
|
27440
27582
|
return new Intl.DateTimeFormat("en-US", {
|
|
@@ -27447,19 +27589,19 @@ function formatDate(dateStr) {
|
|
|
27447
27589
|
return dateStr;
|
|
27448
27590
|
}
|
|
27449
27591
|
}
|
|
27450
|
-
function CalendarIcon() {
|
|
27592
|
+
function CalendarIcon$1() {
|
|
27451
27593
|
return (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" }) }));
|
|
27452
27594
|
}
|
|
27453
|
-
function CheckIcon() {
|
|
27595
|
+
function CheckIcon$1() {
|
|
27454
27596
|
return (jsxRuntime.jsx("svg", { className: "ai-chat-action-icon-success", viewBox: "0 0 20 20", fill: "currentColor", children: jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }));
|
|
27455
27597
|
}
|
|
27456
|
-
function ExternalLinkIcon$
|
|
27598
|
+
function ExternalLinkIcon$2() {
|
|
27457
27599
|
return (jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }), jsxRuntime.jsx("polyline", { points: "15 3 21 3 21 9" }), jsxRuntime.jsx("line", { x1: "10", y1: "14", x2: "21", y2: "3" })] }));
|
|
27458
27600
|
}
|
|
27459
|
-
function Skeleton({ width, height, borderRadius = '4px' }) {
|
|
27601
|
+
function Skeleton$2({ width, height, borderRadius = '4px' }) {
|
|
27460
27602
|
return (jsxRuntime.jsx("div", { className: "ai-chat-action-skeleton-item", style: { width, height, borderRadius } }));
|
|
27461
27603
|
}
|
|
27462
|
-
function GoogleCalendarCard({ action, onComplete, accentColor, className = '' }) {
|
|
27604
|
+
function GoogleCalendarCard({ action, onComplete, onDismiss, accentColor, className = '' }) {
|
|
27463
27605
|
const state = action.state;
|
|
27464
27606
|
const rawSlots = state.availableSlots;
|
|
27465
27607
|
const availableSlots = Array.isArray(rawSlots)
|
|
@@ -27473,13 +27615,12 @@ function GoogleCalendarCard({ action, onComplete, accentColor, className = '' })
|
|
|
27473
27615
|
: [];
|
|
27474
27616
|
const allowTopic = state.allowTopic !== false;
|
|
27475
27617
|
const isBooked = state.status === "booked";
|
|
27476
|
-
const slotsByDate = groupSlotsByDate(availableSlots);
|
|
27618
|
+
const slotsByDate = groupSlotsByDate$1(availableSlots);
|
|
27477
27619
|
const dates = Array.from(slotsByDate.keys()).sort();
|
|
27478
27620
|
const [selectedDate, setSelectedDate] = React.useState(dates[0] ?? "");
|
|
27479
27621
|
const [selectedSlot, setSelectedSlot] = React.useState(null);
|
|
27480
27622
|
const [topic, setTopic] = React.useState("");
|
|
27481
27623
|
const [error, setError] = React.useState(null);
|
|
27482
|
-
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
|
27483
27624
|
const slotsForSelectedDate = selectedDate ? slotsByDate.get(selectedDate) ?? [] : [];
|
|
27484
27625
|
const accentStyle = accentColor ? { '--action-accent': accentColor } : {};
|
|
27485
27626
|
const onConfirm = () => {
|
|
@@ -27492,8 +27633,252 @@ function GoogleCalendarCard({ action, onComplete, accentColor, className = '' })
|
|
|
27492
27633
|
return;
|
|
27493
27634
|
}
|
|
27494
27635
|
setError(null);
|
|
27636
|
+
onComplete?.(action.toolCallId, {
|
|
27637
|
+
...action.state,
|
|
27638
|
+
selectedSlot: {
|
|
27639
|
+
startTime: selectedSlot.startTime,
|
|
27640
|
+
endTime: selectedSlot.endTime,
|
|
27641
|
+
},
|
|
27642
|
+
topic: allowTopic ? topic.trim() : null,
|
|
27643
|
+
});
|
|
27644
|
+
};
|
|
27645
|
+
const handleDismiss = () => {
|
|
27646
|
+
onDismiss?.(action.toolCallId);
|
|
27647
|
+
};
|
|
27648
|
+
// Booked state
|
|
27649
|
+
if (isBooked) {
|
|
27650
|
+
const bookedSlot = state.selectedSlot;
|
|
27651
|
+
const bookedTopic = state.topic;
|
|
27652
|
+
const eventLink = state.bookedEventLink;
|
|
27653
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-action-card ai-chat-action-booked ${className}`, style: accentStyle, children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-header", children: [jsxRuntime.jsx("div", { className: "ai-chat-action-success-icon-wrapper", children: jsxRuntime.jsx(CheckIcon$1, {}) }), "Appointment Confirmed"] }), jsxRuntime.jsxs("div", { className: "ai-chat-action-body", children: [bookedTopic && (jsxRuntime.jsxs("div", { className: "ai-chat-action-detail-box", children: [jsxRuntime.jsx("span", { className: "ai-chat-action-label-small", children: "TOPIC" }), jsxRuntime.jsx("span", { className: "ai-chat-action-value-large", children: bookedTopic })] })), bookedSlot && bookedSlot.startTime && (jsxRuntime.jsxs("div", { className: "ai-chat-action-detail-box", children: [jsxRuntime.jsx("span", { className: "ai-chat-action-label-small", children: "TIME" }), jsxRuntime.jsx("span", { className: "ai-chat-action-value-large", children: bookedSlot.displayTime || new Date(bookedSlot.startTime).toLocaleString() })] })), eventLink && (jsxRuntime.jsxs("a", { href: eventLink, target: "_blank", rel: "noopener noreferrer", className: "ai-chat-action-link-button", children: ["View in Google Calendar", jsxRuntime.jsx(ExternalLinkIcon$2, {})] }))] })] }));
|
|
27654
|
+
}
|
|
27655
|
+
// Skeleton loading state - show when waiting for backend after user confirms
|
|
27656
|
+
const isWaitingForBackend = !action.done && state.selectedSlot && !isBooked;
|
|
27657
|
+
if (isWaitingForBackend) {
|
|
27658
|
+
return (jsxRuntime.jsx("div", { className: `ai-chat-action-card ai-chat-action-skeleton ${className}`, style: accentStyle, children: jsxRuntime.jsxs("div", { className: "ai-chat-action-skeleton-content", children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-skeleton-header", children: [jsxRuntime.jsx(Skeleton$2, { width: "28px", height: "28px", borderRadius: "50%" }), jsxRuntime.jsx(Skeleton$2, { width: "180px", height: "20px", borderRadius: "4px" })] }), jsxRuntime.jsxs("div", { className: "ai-chat-action-skeleton-box", children: [jsxRuntime.jsx(Skeleton$2, { width: "60px", height: "12px", borderRadius: "4px" }), jsxRuntime.jsx(Skeleton$2, { width: "120px", height: "18px", borderRadius: "4px" })] }), jsxRuntime.jsxs("div", { className: "ai-chat-action-skeleton-box", children: [jsxRuntime.jsx(Skeleton$2, { width: "50px", height: "12px", borderRadius: "4px" }), jsxRuntime.jsx(Skeleton$2, { width: "200px", height: "18px", borderRadius: "4px" })] }), jsxRuntime.jsx(Skeleton$2, { width: "100%", height: "44px", borderRadius: "999px" })] }) }));
|
|
27659
|
+
}
|
|
27660
|
+
// Booking form
|
|
27661
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-action-card ai-chat-action-card--closable ai-chat-google-calendar ${className}`, style: accentStyle, children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-header", children: [jsxRuntime.jsx(CalendarIcon$1, {}), "Schedule an Appointment", onDismiss && (jsxRuntime.jsx(CloseButton, { onClick: handleDismiss, ariaLabel: "Cancel appointment booking" }))] }), 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: () => {
|
|
27662
|
+
setSelectedDate(date);
|
|
27663
|
+
setSelectedSlot(null);
|
|
27664
|
+
}, children: formatDate$1(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." }))] })] }));
|
|
27665
|
+
}
|
|
27666
|
+
|
|
27667
|
+
function groupSlotsByDate(slots) {
|
|
27668
|
+
const grouped = new Map();
|
|
27669
|
+
for (const slot of slots) {
|
|
27670
|
+
if (!slot || typeof slot !== 'object' || !slot.startTime || typeof slot.startTime !== 'string') {
|
|
27671
|
+
continue;
|
|
27672
|
+
}
|
|
27673
|
+
const date = slot.startTime.slice(0, 10);
|
|
27674
|
+
if (!grouped.has(date)) {
|
|
27675
|
+
grouped.set(date, []);
|
|
27676
|
+
}
|
|
27677
|
+
grouped.get(date).push(slot);
|
|
27678
|
+
}
|
|
27679
|
+
return grouped;
|
|
27680
|
+
}
|
|
27681
|
+
function formatDate(dateStr) {
|
|
27682
|
+
try {
|
|
27683
|
+
const date = new Date(dateStr);
|
|
27684
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
27685
|
+
weekday: "short",
|
|
27686
|
+
month: "short",
|
|
27687
|
+
day: "numeric",
|
|
27688
|
+
}).format(date);
|
|
27689
|
+
}
|
|
27690
|
+
catch {
|
|
27691
|
+
return dateStr;
|
|
27692
|
+
}
|
|
27693
|
+
}
|
|
27694
|
+
function CalendarIcon() {
|
|
27695
|
+
return (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" }) }));
|
|
27696
|
+
}
|
|
27697
|
+
function MailIcon() {
|
|
27698
|
+
return (jsxRuntime.jsxs("svg", { className: "ai-chat-action-icon", viewBox: "0 0 20 20", fill: "currentColor", children: [jsxRuntime.jsx("path", { d: "M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z" }), jsxRuntime.jsx("path", { d: "M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z" })] }));
|
|
27699
|
+
}
|
|
27700
|
+
function CheckIcon() {
|
|
27701
|
+
return (jsxRuntime.jsx("svg", { className: "ai-chat-action-icon-success", viewBox: "0 0 20 20", fill: "currentColor", children: jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }));
|
|
27702
|
+
}
|
|
27703
|
+
function ErrorIcon() {
|
|
27704
|
+
return (jsxRuntime.jsx("svg", { className: "ai-chat-action-icon-error", viewBox: "0 0 20 20", fill: "currentColor", children: jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z", clipRule: "evenodd" }) }));
|
|
27705
|
+
}
|
|
27706
|
+
function ExternalLinkIcon$1() {
|
|
27707
|
+
return (jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }), jsxRuntime.jsx("polyline", { points: "15 3 21 3 21 9" }), jsxRuntime.jsx("line", { x1: "10", y1: "14", x2: "21", y2: "3" })] }));
|
|
27708
|
+
}
|
|
27709
|
+
function Skeleton$1({ width, height, borderRadius = '4px' }) {
|
|
27710
|
+
return (jsxRuntime.jsx("div", { className: "ai-chat-action-skeleton-item", style: { width, height, borderRadius } }));
|
|
27711
|
+
}
|
|
27712
|
+
function PinInputGroup$1({ values, onChange, disabled }) {
|
|
27713
|
+
const inputRefs = React.useRef([]);
|
|
27714
|
+
const handleChange = (index, value) => {
|
|
27715
|
+
// Only allow digits
|
|
27716
|
+
const digit = value.replace(/[^0-9]/g, '');
|
|
27717
|
+
const newValues = [...values];
|
|
27718
|
+
newValues[index] = digit.slice(-1);
|
|
27719
|
+
onChange(newValues);
|
|
27720
|
+
// Auto-focus next input
|
|
27721
|
+
if (digit && index < 5) {
|
|
27722
|
+
inputRefs.current[index + 1]?.focus();
|
|
27723
|
+
}
|
|
27724
|
+
};
|
|
27725
|
+
const handleKeyDown = (index, e) => {
|
|
27726
|
+
if (e.key === 'Backspace' && !values[index] && index > 0) {
|
|
27727
|
+
inputRefs.current[index - 1]?.focus();
|
|
27728
|
+
}
|
|
27729
|
+
};
|
|
27730
|
+
const handlePaste = (e) => {
|
|
27731
|
+
e.preventDefault();
|
|
27732
|
+
const pastedData = e.clipboardData.getData('text').replace(/[^0-9]/g, '').slice(0, 6);
|
|
27733
|
+
const newValues = pastedData.split('').concat(Array(6 - pastedData.length).fill(''));
|
|
27734
|
+
onChange(newValues);
|
|
27735
|
+
// Focus the next empty input or the last one
|
|
27736
|
+
const nextIndex = Math.min(pastedData.length, 5);
|
|
27737
|
+
inputRefs.current[nextIndex]?.focus();
|
|
27738
|
+
};
|
|
27739
|
+
return (jsxRuntime.jsx("div", { className: "ai-chat-pin-input-group", children: values.map((value, index) => (jsxRuntime.jsx("input", { ref: (el) => {
|
|
27740
|
+
inputRefs.current[index] = el;
|
|
27741
|
+
}, type: "text", inputMode: "numeric", maxLength: 1, className: "ai-chat-pin-input", value: value, onChange: (e) => handleChange(index, e.target.value), onKeyDown: (e) => handleKeyDown(index, e), onPaste: handlePaste, disabled: disabled, autoFocus: index === 0 }, index))) }));
|
|
27742
|
+
}
|
|
27743
|
+
function MicrosoftCalendarCard({ action, onComplete, onDismiss, accentColor, className = '' }) {
|
|
27744
|
+
const state = action.state;
|
|
27745
|
+
const phase = state.phase || "awaiting_email";
|
|
27746
|
+
const allowTopic = state.allowTopic !== false;
|
|
27747
|
+
const accentStyle = accentColor ? { '--action-accent': accentColor } : {};
|
|
27748
|
+
const handleDismiss = () => {
|
|
27749
|
+
onDismiss?.(action.toolCallId);
|
|
27750
|
+
};
|
|
27751
|
+
const showCloseButton = Boolean(onDismiss);
|
|
27752
|
+
// Debug: Log state changes
|
|
27753
|
+
const prevStateRef = React.useRef(null);
|
|
27754
|
+
React.useEffect(() => {
|
|
27755
|
+
if (JSON.stringify(prevStateRef.current) !== JSON.stringify(state)) {
|
|
27756
|
+
console.log('[MicrosoftCalendarCard] State updated:', {
|
|
27757
|
+
phase: state.phase,
|
|
27758
|
+
hasError: !!state.errorMessage,
|
|
27759
|
+
error: state.errorMessage,
|
|
27760
|
+
slotsCount: state.availableSlots?.length || 0
|
|
27761
|
+
});
|
|
27762
|
+
prevStateRef.current = state;
|
|
27763
|
+
}
|
|
27764
|
+
}, [state]);
|
|
27765
|
+
// Email phase state
|
|
27766
|
+
const [email, setEmail] = React.useState("");
|
|
27767
|
+
const [emailError, setEmailError] = React.useState(null);
|
|
27768
|
+
// OTP phase state
|
|
27769
|
+
const [otpValues, setOtpValues] = React.useState(Array(6).fill(''));
|
|
27770
|
+
const [otpError, setOtpError] = React.useState(null);
|
|
27771
|
+
// Loading states
|
|
27772
|
+
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
|
27773
|
+
// Reset loading state when phase changes (backend has responded)
|
|
27774
|
+
// Use useEffect for reliable re-rendering
|
|
27775
|
+
React.useEffect(() => {
|
|
27776
|
+
console.log('[MicrosoftCalendarCard] Phase changed to:', phase);
|
|
27777
|
+
setIsSubmitting(false);
|
|
27778
|
+
// Clear errors when transitioning to new phase
|
|
27779
|
+
if (phase === "awaiting_email") {
|
|
27780
|
+
setEmailError(null);
|
|
27781
|
+
setOtpError(null);
|
|
27782
|
+
setSelectionError(null);
|
|
27783
|
+
}
|
|
27784
|
+
else if (phase === "awaiting_otp") {
|
|
27785
|
+
setOtpError(state.errorMessage || null);
|
|
27786
|
+
setEmailError(null);
|
|
27787
|
+
setSelectionError(null);
|
|
27788
|
+
// Clear OTP input for fresh attempt
|
|
27789
|
+
setOtpValues(Array(6).fill(''));
|
|
27790
|
+
}
|
|
27791
|
+
else if (phase === "awaiting_options") {
|
|
27792
|
+
setEmailError(null);
|
|
27793
|
+
setOtpError(null);
|
|
27794
|
+
setSelectionError(null);
|
|
27795
|
+
}
|
|
27796
|
+
else if (phase === "awaiting_booking") {
|
|
27797
|
+
setSelectionError(state.errorMessage || null);
|
|
27798
|
+
setEmailError(null);
|
|
27799
|
+
setOtpError(null);
|
|
27800
|
+
}
|
|
27801
|
+
else if (phase === "booked" || phase === "cancelled" || phase === "error") {
|
|
27802
|
+
setEmailError(null);
|
|
27803
|
+
setOtpError(null);
|
|
27804
|
+
setSelectionError(null);
|
|
27805
|
+
setSelectedId(null); // Reset selection
|
|
27806
|
+
}
|
|
27807
|
+
}, [phase, state.errorMessage]);
|
|
27808
|
+
// Selection phase state
|
|
27809
|
+
const rawSlots = state.availableSlots;
|
|
27810
|
+
const availableSlots = Array.isArray(rawSlots)
|
|
27811
|
+
? rawSlots.filter((slot) => slot !== null &&
|
|
27812
|
+
slot !== undefined &&
|
|
27813
|
+
typeof slot === "object" &&
|
|
27814
|
+
"startTime" in slot &&
|
|
27815
|
+
"endTime" in slot &&
|
|
27816
|
+
typeof slot.startTime === "string" &&
|
|
27817
|
+
typeof slot.endTime === "string")
|
|
27818
|
+
: [];
|
|
27819
|
+
const slotsByDate = groupSlotsByDate(availableSlots);
|
|
27820
|
+
const dates = Array.from(slotsByDate.keys()).sort();
|
|
27821
|
+
const [selectedDate, setSelectedDate] = React.useState(dates[0] ?? "");
|
|
27822
|
+
const [selectedSlot, setSelectedSlot] = React.useState(null);
|
|
27823
|
+
const [topic, setTopic] = React.useState("");
|
|
27824
|
+
const [selectionError, setSelectionError] = React.useState(null);
|
|
27825
|
+
// Cancellation phase state
|
|
27826
|
+
const [selectedId, setSelectedId] = React.useState(null);
|
|
27827
|
+
const slotsForSelectedDate = selectedDate ? slotsByDate.get(selectedDate) ?? [] : [];
|
|
27828
|
+
// Phase 1: Email Input
|
|
27829
|
+
const handleEmailSubmit = () => {
|
|
27830
|
+
const trimmedEmail = email.trim();
|
|
27831
|
+
if (!trimmedEmail) {
|
|
27832
|
+
setEmailError("Please enter your email address");
|
|
27833
|
+
return;
|
|
27834
|
+
}
|
|
27835
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmedEmail)) {
|
|
27836
|
+
setEmailError("Please enter a valid email address");
|
|
27837
|
+
return;
|
|
27838
|
+
}
|
|
27839
|
+
setEmailError(null);
|
|
27840
|
+
setIsSubmitting(true);
|
|
27841
|
+
console.log('[MicrosoftCalendarCard] Submitting email:', trimmedEmail);
|
|
27842
|
+
setTimeout(() => {
|
|
27843
|
+
onComplete?.(action.toolCallId, {
|
|
27844
|
+
...action.state,
|
|
27845
|
+
email: trimmedEmail,
|
|
27846
|
+
});
|
|
27847
|
+
}, 50);
|
|
27848
|
+
};
|
|
27849
|
+
// Phase 2: OTP Verification
|
|
27850
|
+
const handleOtpSubmit = () => {
|
|
27851
|
+
const otpCode = otpValues.join('');
|
|
27852
|
+
if (otpCode.length !== 6) {
|
|
27853
|
+
setOtpError("Please enter the 6-digit code");
|
|
27854
|
+
return;
|
|
27855
|
+
}
|
|
27856
|
+
setOtpError(null);
|
|
27857
|
+
setIsSubmitting(true);
|
|
27858
|
+
console.log('[MicrosoftCalendarCard] Submitting OTP code');
|
|
27859
|
+
setTimeout(() => {
|
|
27860
|
+
onComplete?.(action.toolCallId, {
|
|
27861
|
+
...action.state,
|
|
27862
|
+
otpCode,
|
|
27863
|
+
});
|
|
27864
|
+
}, 50);
|
|
27865
|
+
};
|
|
27866
|
+
// Phase 3: Appointment Selection
|
|
27867
|
+
const handleAppointmentConfirm = () => {
|
|
27868
|
+
if (!selectedSlot) {
|
|
27869
|
+
setSelectionError("Please select a time slot");
|
|
27870
|
+
return;
|
|
27871
|
+
}
|
|
27872
|
+
if (allowTopic && !topic.trim()) {
|
|
27873
|
+
setSelectionError("Please enter a meeting topic");
|
|
27874
|
+
return;
|
|
27875
|
+
}
|
|
27876
|
+
setSelectionError(null);
|
|
27495
27877
|
setIsSubmitting(true);
|
|
27496
|
-
|
|
27878
|
+
console.log('[MicrosoftCalendarCard] Confirming appointment:', {
|
|
27879
|
+
slot: selectedSlot,
|
|
27880
|
+
topic: topic.trim()
|
|
27881
|
+
});
|
|
27497
27882
|
setTimeout(() => {
|
|
27498
27883
|
onComplete?.(action.toolCallId, {
|
|
27499
27884
|
...action.state,
|
|
@@ -27505,22 +27890,149 @@ function GoogleCalendarCard({ action, onComplete, accentColor, className = '' })
|
|
|
27505
27890
|
});
|
|
27506
27891
|
}, 50);
|
|
27507
27892
|
};
|
|
27508
|
-
//
|
|
27509
|
-
|
|
27893
|
+
// Handle "Use different email" button
|
|
27894
|
+
const handleUseDifferentEmail = () => {
|
|
27895
|
+
setEmail("");
|
|
27896
|
+
setEmailError(null);
|
|
27897
|
+
setOtpValues(Array(6).fill(''));
|
|
27898
|
+
setOtpError(null);
|
|
27899
|
+
onComplete?.(action.toolCallId, {
|
|
27900
|
+
phase: "awaiting_email",
|
|
27901
|
+
email: null,
|
|
27902
|
+
otpVerified: false,
|
|
27903
|
+
otpAttempts: 0,
|
|
27904
|
+
availableSlots: [],
|
|
27905
|
+
selectedSlot: null,
|
|
27906
|
+
topic: null,
|
|
27907
|
+
bookedEventId: null,
|
|
27908
|
+
bookedEventLink: null,
|
|
27909
|
+
allowTopic,
|
|
27910
|
+
errorMessage: null,
|
|
27911
|
+
});
|
|
27912
|
+
};
|
|
27913
|
+
// Phase 5: Booked Confirmation
|
|
27914
|
+
if (phase === "booked") {
|
|
27510
27915
|
const bookedSlot = state.selectedSlot;
|
|
27511
27916
|
const bookedTopic = state.topic;
|
|
27512
27917
|
const eventLink = state.bookedEventLink;
|
|
27513
|
-
|
|
27514
|
-
|
|
27515
|
-
|
|
27918
|
+
const bookedEmail = state.email;
|
|
27919
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-action-card ai-chat-action-booked ${className}`, style: accentStyle, children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-header", children: [jsxRuntime.jsx("div", { className: "ai-chat-action-success-icon-wrapper", children: jsxRuntime.jsx(CheckIcon, {}) }), "Appointment Confirmed"] }), jsxRuntime.jsxs("div", { className: "ai-chat-action-body", children: [bookedTopic && (jsxRuntime.jsxs("div", { className: "ai-chat-action-detail-box", children: [jsxRuntime.jsx("span", { className: "ai-chat-action-label-small", children: "TOPIC" }), jsxRuntime.jsx("span", { className: "ai-chat-action-value-large", children: bookedTopic })] })), bookedSlot && bookedSlot.startTime && (jsxRuntime.jsxs("div", { className: "ai-chat-action-detail-box", children: [jsxRuntime.jsx("span", { className: "ai-chat-action-label-small", children: "TIME" }), jsxRuntime.jsx("span", { className: "ai-chat-action-value-large", children: bookedSlot.displayTime || new Date(bookedSlot.startTime).toLocaleString() })] })), bookedEmail && (jsxRuntime.jsxs("div", { className: "ai-chat-action-hint", children: ["A calendar invitation has been sent to ", bookedEmail] })), eventLink && (jsxRuntime.jsxs("a", { href: eventLink, target: "_blank", rel: "noopener noreferrer", className: "ai-chat-action-link-button", children: ["View in Outlook Calendar", jsxRuntime.jsx(ExternalLinkIcon$1, {})] }))] })] }));
|
|
27920
|
+
}
|
|
27921
|
+
// Phase 6: Cancelled Confirmation
|
|
27922
|
+
if (phase === "cancelled") {
|
|
27923
|
+
const cancelledSubject = state.cancelledEventSubject;
|
|
27924
|
+
const userEmail = state.email;
|
|
27925
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-action-card ai-chat-action-booked ${className}`, style: accentStyle, children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-header", children: [jsxRuntime.jsx("div", { className: "ai-chat-action-success-icon-wrapper", children: jsxRuntime.jsx(CheckIcon, {}) }), "Appointment Cancelled"] }), jsxRuntime.jsxs("div", { className: "ai-chat-action-body", children: [cancelledSubject && (jsxRuntime.jsxs("div", { className: "ai-chat-action-detail-box", children: [jsxRuntime.jsx("span", { className: "ai-chat-action-label-small", children: "CANCELLED" }), jsxRuntime.jsx("span", { className: "ai-chat-action-value-large", children: cancelledSubject })] })), userEmail && (jsxRuntime.jsxs("div", { className: "ai-chat-action-hint", children: ["A cancellation notice has been sent to ", userEmail] }))] })] }));
|
|
27926
|
+
}
|
|
27927
|
+
// Error State
|
|
27928
|
+
if (phase === "error") {
|
|
27929
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-action-card ai-chat-action-error ${className}`, style: accentStyle, children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-header", children: [jsxRuntime.jsx(ErrorIcon, {}), "Error"] }), jsxRuntime.jsxs("div", { className: "ai-chat-action-body", children: [jsxRuntime.jsx("div", { className: "ai-chat-action-error-message", children: state.errorMessage || "An error occurred. Please try again." }), jsxRuntime.jsx("button", { className: "ai-chat-action-button-secondary", type: "button", onClick: () => {
|
|
27930
|
+
setEmail("");
|
|
27931
|
+
setEmailError(null);
|
|
27932
|
+
setOtpValues(Array(6).fill(''));
|
|
27933
|
+
setOtpError(null);
|
|
27934
|
+
onComplete?.(action.toolCallId, {
|
|
27935
|
+
phase: "awaiting_email",
|
|
27936
|
+
email: null,
|
|
27937
|
+
otpVerified: false,
|
|
27938
|
+
otpAttempts: 0,
|
|
27939
|
+
availableSlots: [],
|
|
27940
|
+
selectedSlot: null,
|
|
27941
|
+
topic: null,
|
|
27942
|
+
bookedEventId: null,
|
|
27943
|
+
bookedEventLink: null,
|
|
27944
|
+
allowTopic,
|
|
27945
|
+
errorMessage: null,
|
|
27946
|
+
});
|
|
27947
|
+
}, children: "Start Over" })] })] }));
|
|
27948
|
+
}
|
|
27949
|
+
// Loading State
|
|
27516
27950
|
if (isSubmitting) {
|
|
27517
|
-
return (jsxRuntime.jsx("div", { className: `ai-chat-action-card ai-chat-action-skeleton ${className}`, style: accentStyle, children: jsxRuntime.jsxs("div", { className: "ai-chat-action-skeleton-content", children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-skeleton-header", children: [jsxRuntime.jsx(Skeleton, { width: "28px", height: "28px", borderRadius: "50%" }), jsxRuntime.jsx(Skeleton, { width: "180px", height: "20px", borderRadius: "4px" })] }), jsxRuntime.jsxs("div", { className: "ai-chat-action-skeleton-box", children: [jsxRuntime.jsx(Skeleton, { width: "60px", height: "12px", borderRadius: "4px" }), jsxRuntime.jsx(Skeleton, { width: "120px", height: "18px", borderRadius: "4px" })] }), jsxRuntime.
|
|
27518
|
-
}
|
|
27519
|
-
//
|
|
27520
|
-
|
|
27521
|
-
|
|
27522
|
-
|
|
27523
|
-
|
|
27951
|
+
return (jsxRuntime.jsx("div", { className: `ai-chat-action-card ai-chat-action-skeleton ${className}`, style: accentStyle, children: jsxRuntime.jsxs("div", { className: "ai-chat-action-skeleton-content", children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-skeleton-header", children: [jsxRuntime.jsx(Skeleton$1, { width: "28px", height: "28px", borderRadius: "50%" }), jsxRuntime.jsx(Skeleton$1, { width: "180px", height: "20px", borderRadius: "4px" })] }), jsxRuntime.jsxs("div", { className: "ai-chat-action-skeleton-box", children: [jsxRuntime.jsx(Skeleton$1, { width: "60px", height: "12px", borderRadius: "4px" }), jsxRuntime.jsx(Skeleton$1, { width: "120px", height: "18px", borderRadius: "4px" })] }), jsxRuntime.jsx(Skeleton$1, { width: "100%", height: "44px", borderRadius: "999px" })] }) }));
|
|
27952
|
+
}
|
|
27953
|
+
// Phase 1: Email Input
|
|
27954
|
+
if (phase === "awaiting_email") {
|
|
27955
|
+
const displayError = state.errorMessage || emailError;
|
|
27956
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-action-card ai-chat-action-card--closable ${className}`, style: accentStyle, children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-header", children: [jsxRuntime.jsx(CalendarIcon, {}), "Schedule an Appointment", showCloseButton && (jsxRuntime.jsx(CloseButton, { onClick: handleDismiss, ariaLabel: "Cancel appointment booking" }))] }), jsxRuntime.jsxs("div", { className: "ai-chat-action-body", children: [jsxRuntime.jsx("div", { className: "ai-chat-action-hint", children: "We'll send a verification code to your email" }), jsxRuntime.jsxs("div", { className: "ai-chat-action-field", children: [jsxRuntime.jsx("label", { htmlFor: `email-${action.toolCallId}`, className: "ai-chat-action-label", children: "Email Address" }), jsxRuntime.jsx("input", { id: `email-${action.toolCallId}`, type: "email", className: "ai-chat-action-input", placeholder: "your@email.com", value: email, onChange: (e) => {
|
|
27957
|
+
setEmail(e.target.value);
|
|
27958
|
+
setEmailError(null);
|
|
27959
|
+
}, onKeyPress: (e) => {
|
|
27960
|
+
if (e.key === 'Enter') {
|
|
27961
|
+
handleEmailSubmit();
|
|
27962
|
+
}
|
|
27963
|
+
}, autoFocus: true })] }), displayError && (jsxRuntime.jsx("div", { className: "ai-chat-action-error", children: displayError })), jsxRuntime.jsx("button", { className: "ai-chat-action-button", type: "button", onClick: handleEmailSubmit, children: "Continue" })] })] }));
|
|
27964
|
+
}
|
|
27965
|
+
// Phase 2: OTP Input
|
|
27966
|
+
if (phase === "awaiting_otp") {
|
|
27967
|
+
const displayError = state.errorMessage || otpError;
|
|
27968
|
+
const userEmail = state.email;
|
|
27969
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-action-card ai-chat-action-card--closable ${className}`, style: accentStyle, children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-header", children: [jsxRuntime.jsx(MailIcon, {}), "Verify Your Email", showCloseButton && (jsxRuntime.jsx(CloseButton, { onClick: handleDismiss, ariaLabel: "Cancel appointment booking" }))] }), jsxRuntime.jsxs("div", { className: "ai-chat-action-body", children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-hint", children: ["We sent a 6-digit code to ", jsxRuntime.jsx("strong", { children: userEmail })] }), jsxRuntime.jsx(PinInputGroup$1, { values: otpValues, onChange: (newValues) => {
|
|
27970
|
+
setOtpValues(newValues);
|
|
27971
|
+
setOtpError(null);
|
|
27972
|
+
// Auto-submit when all 6 digits are entered
|
|
27973
|
+
if (newValues.every(v => v.length === 1)) {
|
|
27974
|
+
console.log('[MicrosoftCalendarCard] Auto-submitting OTP (all 6 digits entered)');
|
|
27975
|
+
setIsSubmitting(true);
|
|
27976
|
+
const code = newValues.join('');
|
|
27977
|
+
setTimeout(() => {
|
|
27978
|
+
onComplete?.(action.toolCallId, {
|
|
27979
|
+
...action.state,
|
|
27980
|
+
otpCode: code,
|
|
27981
|
+
});
|
|
27982
|
+
}, 100);
|
|
27983
|
+
}
|
|
27984
|
+
}, disabled: isSubmitting }), displayError && (jsxRuntime.jsx("div", { className: "ai-chat-action-error", children: displayError })), jsxRuntime.jsx("button", { className: "ai-chat-action-button", type: "button", onClick: handleOtpSubmit, disabled: otpValues.join('').length !== 6, children: "Verify Code" }), jsxRuntime.jsx("button", { className: "ai-chat-action-button-secondary", type: "button", onClick: handleUseDifferentEmail, children: "Use different email" })] })] }));
|
|
27985
|
+
}
|
|
27986
|
+
// Phase 3: Options Menu (with inline cancel buttons and confirmation)
|
|
27987
|
+
if (phase === "awaiting_options") {
|
|
27988
|
+
const userAppointments = state.userAppointments || [];
|
|
27989
|
+
const maxAppointments = state.maxAppointmentsPerUser || 3;
|
|
27990
|
+
const appointmentCount = userAppointments.length;
|
|
27991
|
+
const canBook = appointmentCount < maxAppointments;
|
|
27992
|
+
const hasAppointments = appointmentCount > 0;
|
|
27993
|
+
const displayError = state.errorMessage || selectionError;
|
|
27994
|
+
// If confirming cancellation, show confirmation dialog
|
|
27995
|
+
if (selectedId) {
|
|
27996
|
+
const appointmentToCancel = userAppointments.find(appt => appt.id === selectedId);
|
|
27997
|
+
if (!appointmentToCancel) {
|
|
27998
|
+
setSelectedId(null); // Reset if appointment not found
|
|
27999
|
+
}
|
|
28000
|
+
else {
|
|
28001
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-action-card ai-chat-action-card--closable ${className}`, style: accentStyle, children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-header", children: [jsxRuntime.jsx(CalendarIcon, {}), "Confirm Cancellation", showCloseButton && (jsxRuntime.jsx(CloseButton, { onClick: handleDismiss, ariaLabel: "Cancel appointment booking" }))] }), jsxRuntime.jsxs("div", { className: "ai-chat-action-body", children: [jsxRuntime.jsx("div", { className: "ai-chat-action-hint", children: "Are you sure you want to cancel this appointment?" }), jsxRuntime.jsx("div", { className: "ai-chat-action-appointment-item", style: { marginBottom: '16px' }, children: jsxRuntime.jsxs("div", { className: "ai-chat-action-appointment-info", children: [jsxRuntime.jsx("div", { className: "ai-chat-action-appointment-subject", children: appointmentToCancel.subject }), jsxRuntime.jsx("div", { className: "ai-chat-action-appointment-time", children: appointmentToCancel.displayTime })] }) }), displayError && (jsxRuntime.jsx("div", { className: "ai-chat-action-error", children: displayError })), jsxRuntime.jsxs("div", { className: "ai-chat-action-button-group", children: [jsxRuntime.jsx("button", { className: "ai-chat-action-button", type: "button", onClick: () => {
|
|
28002
|
+
setIsSubmitting(true);
|
|
28003
|
+
onComplete?.(action.toolCallId, {
|
|
28004
|
+
...action.state,
|
|
28005
|
+
selectedOption: "cancel",
|
|
28006
|
+
selectedAppointmentId: selectedId,
|
|
28007
|
+
});
|
|
28008
|
+
}, disabled: isSubmitting, children: isSubmitting ? 'Cancelling...' : 'Confirm Cancellation' }), jsxRuntime.jsx("button", { className: "ai-chat-action-button-secondary", type: "button", onClick: () => {
|
|
28009
|
+
setSelectedId(null);
|
|
28010
|
+
setSelectionError(null);
|
|
28011
|
+
}, disabled: isSubmitting, children: "Go Back" })] })] })] }));
|
|
28012
|
+
}
|
|
28013
|
+
}
|
|
28014
|
+
// Normal view with inline cancel buttons
|
|
28015
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-action-card ai-chat-action-card--closable ${className}`, style: accentStyle, children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-header", children: [jsxRuntime.jsx(CalendarIcon, {}), "Manage Your Appointments", showCloseButton && (jsxRuntime.jsx(CloseButton, { onClick: handleDismiss, ariaLabel: "Cancel appointment booking" }))] }), jsxRuntime.jsxs("div", { className: "ai-chat-action-body", children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-hint", children: ["You have ", appointmentCount, " active appointment", appointmentCount !== 1 ? 's' : '', canBook && ` (${maxAppointments - appointmentCount} slot${maxAppointments - appointmentCount !== 1 ? 's' : ''} remaining)`] }), hasAppointments && (jsxRuntime.jsxs("div", { className: "ai-chat-action-appointment-list", children: [jsxRuntime.jsx("div", { className: "ai-chat-action-label", children: "Your Upcoming Appointments" }), userAppointments.map((appt) => (jsxRuntime.jsxs("div", { className: "ai-chat-action-appointment-item", children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-appointment-info", children: [jsxRuntime.jsx("div", { className: "ai-chat-action-appointment-subject", children: appt.subject }), jsxRuntime.jsx("div", { className: "ai-chat-action-appointment-time", children: appt.displayTime })] }), jsxRuntime.jsx("button", { className: "ai-chat-action-button-secondary", type: "button", onClick: () => {
|
|
28016
|
+
setSelectedId(appt.id);
|
|
28017
|
+
setSelectionError(null);
|
|
28018
|
+
}, children: "Cancel" })] }, appt.id)))] })), displayError && (jsxRuntime.jsx("div", { className: "ai-chat-action-error", children: displayError })), canBook && (jsxRuntime.jsx("button", { className: "ai-chat-action-button", type: "button", onClick: () => {
|
|
28019
|
+
setIsSubmitting(true);
|
|
28020
|
+
onComplete?.(action.toolCallId, {
|
|
28021
|
+
...action.state,
|
|
28022
|
+
selectedOption: "book",
|
|
28023
|
+
});
|
|
28024
|
+
}, disabled: isSubmitting, children: isSubmitting ? 'Loading...' : 'Book New Appointment' })), !canBook && !hasAppointments && (jsxRuntime.jsx("div", { className: "ai-chat-action-hint", children: "No appointments found." }))] })] }));
|
|
28025
|
+
}
|
|
28026
|
+
// Phase 4: Appointment Selection (Booking)
|
|
28027
|
+
if (phase === "awaiting_booking") {
|
|
28028
|
+
const displayError = state.errorMessage || selectionError;
|
|
28029
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-action-card ai-chat-action-card--closable ${className}`, style: accentStyle, children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-header", children: [jsxRuntime.jsx(CalendarIcon, {}), "Select Appointment Time", showCloseButton && (jsxRuntime.jsx(CloseButton, { onClick: handleDismiss, ariaLabel: "Cancel appointment booking" }))] }), 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: () => {
|
|
28030
|
+
setSelectedDate(date);
|
|
28031
|
+
setSelectedSlot(null);
|
|
28032
|
+
}, 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))) })] })), displayError && (jsxRuntime.jsx("div", { className: "ai-chat-action-error", children: displayError })), jsxRuntime.jsx("button", { className: "ai-chat-action-button", type: "button", onClick: handleAppointmentConfirm, disabled: !selectedSlot, children: "Confirm Appointment" }), availableSlots.length === 0 && (jsxRuntime.jsx("div", { className: "ai-chat-action-hint", children: "No available time slots found." }))] })] }));
|
|
28033
|
+
}
|
|
28034
|
+
// Fallback
|
|
28035
|
+
return null;
|
|
27524
28036
|
}
|
|
27525
28037
|
|
|
27526
28038
|
function truncate(text, maxLength) {
|
|
@@ -27531,17 +28043,30 @@ function truncate(text, maxLength) {
|
|
|
27531
28043
|
function ExternalLinkIcon() {
|
|
27532
28044
|
return (jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }), jsxRuntime.jsx("polyline", { points: "15 3 21 3 21 9" }), jsxRuntime.jsx("line", { x1: "10", y1: "14", x2: "21", y2: "3" })] }));
|
|
27533
28045
|
}
|
|
28046
|
+
function SingleLinkPreview({ link, onLinkClick, accentColor }) {
|
|
28047
|
+
const domain = (() => {
|
|
28048
|
+
if (!link.url)
|
|
28049
|
+
return '';
|
|
28050
|
+
try {
|
|
28051
|
+
return new URL(link.url).hostname.replace('www.', '');
|
|
28052
|
+
}
|
|
28053
|
+
catch {
|
|
28054
|
+
return link.url;
|
|
28055
|
+
}
|
|
28056
|
+
})();
|
|
28057
|
+
const style = accentColor ? { '--action-accent': accentColor } : undefined;
|
|
28058
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-action-card ai-chat-link-preview", onClick: onLinkClick, style: style, role: "link", tabIndex: 0, onKeyDown: (e) => e.key === 'Enter' && onLinkClick(), children: [link.image && (jsxRuntime.jsx("div", { className: "ai-chat-link-preview__image", children: jsxRuntime.jsx("img", { src: link.image, alt: link.title, onError: (e) => {
|
|
28059
|
+
e.currentTarget.parentElement.style.display = 'none';
|
|
28060
|
+
} }) })), jsxRuntime.jsxs("div", { className: "ai-chat-link-preview__content", children: [jsxRuntime.jsxs("div", { className: "ai-chat-link-preview__site", children: [link.favicon && (jsxRuntime.jsx("img", { src: link.favicon, alt: "", className: "ai-chat-link-preview__favicon", onError: (e) => {
|
|
28061
|
+
e.currentTarget.style.display = 'none';
|
|
28062
|
+
} })), jsxRuntime.jsx("span", { className: "ai-chat-link-preview__domain", children: link.siteName || domain })] }), jsxRuntime.jsx("h4", { className: "ai-chat-link-preview__title", children: link.title }), link.description && (jsxRuntime.jsx("p", { className: "ai-chat-link-preview__description", children: truncate(link.description, 120) }))] }), jsxRuntime.jsx("div", { className: "ai-chat-link-preview__arrow", children: jsxRuntime.jsx(ExternalLinkIcon, {}) })] }));
|
|
28063
|
+
}
|
|
27534
28064
|
function LinkPreviewCard({ action, onComplete, accentColor }) {
|
|
27535
28065
|
const rawState = action.state;
|
|
27536
28066
|
const hasCompletedRef = React.useRef(false);
|
|
27537
28067
|
// Provide safe defaults if state is missing
|
|
27538
28068
|
const state = {
|
|
27539
|
-
|
|
27540
|
-
title: rawState?.title || 'Link',
|
|
27541
|
-
description: rawState?.description,
|
|
27542
|
-
image: rawState?.image,
|
|
27543
|
-
siteName: rawState?.siteName,
|
|
27544
|
-
favicon: rawState?.favicon,
|
|
28069
|
+
links: rawState?.links || [],
|
|
27545
28070
|
context: rawState?.context,
|
|
27546
28071
|
status: rawState?.status || 'displaying',
|
|
27547
28072
|
error: rawState?.error,
|
|
@@ -27549,34 +28074,29 @@ function LinkPreviewCard({ action, onComplete, accentColor }) {
|
|
|
27549
28074
|
const isError = state.status === 'error';
|
|
27550
28075
|
// Auto-complete on mount so AI can continue generating text response
|
|
27551
28076
|
React.useEffect(() => {
|
|
27552
|
-
if (!action.done && !hasCompletedRef.current && onComplete && state.
|
|
28077
|
+
if (!action.done && !hasCompletedRef.current && onComplete && state.links.length > 0) {
|
|
27553
28078
|
hasCompletedRef.current = true;
|
|
27554
28079
|
// Signal completion immediately - the card is displayed, AI can continue
|
|
27555
28080
|
onComplete(action.toolCallId, { ...state, status: 'displaying' });
|
|
27556
28081
|
}
|
|
27557
28082
|
}, [action.done, action.toolCallId, onComplete, state]);
|
|
27558
|
-
const
|
|
27559
|
-
if (
|
|
27560
|
-
window.open(
|
|
28083
|
+
const handleLinkClick = (url) => {
|
|
28084
|
+
if (url) {
|
|
28085
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
27561
28086
|
}
|
|
27562
28087
|
onComplete?.(action.toolCallId, { ...state, status: 'clicked' });
|
|
27563
28088
|
};
|
|
27564
|
-
|
|
27565
|
-
|
|
27566
|
-
|
|
27567
|
-
|
|
27568
|
-
|
|
27569
|
-
|
|
27570
|
-
|
|
27571
|
-
|
|
27572
|
-
|
|
27573
|
-
|
|
27574
|
-
|
|
27575
|
-
return (jsxRuntime.jsxs("div", { className: `ai-chat-link-preview ${isError ? 'ai-chat-link-preview--error' : ''}`, onClick: handleClick, style: style, role: "link", tabIndex: 0, onKeyDown: (e) => e.key === 'Enter' && handleClick(), children: [state.image && !isError && (jsxRuntime.jsx("div", { className: "ai-chat-link-preview__image", children: jsxRuntime.jsx("img", { src: state.image, alt: state.title, onError: (e) => {
|
|
27576
|
-
e.currentTarget.parentElement.style.display = 'none';
|
|
27577
|
-
} }) })), jsxRuntime.jsxs("div", { className: "ai-chat-link-preview__content", children: [jsxRuntime.jsxs("div", { className: "ai-chat-link-preview__site", children: [state.favicon && (jsxRuntime.jsx("img", { src: state.favicon, alt: "", className: "ai-chat-link-preview__favicon", onError: (e) => {
|
|
27578
|
-
e.currentTarget.style.display = 'none';
|
|
27579
|
-
} })), jsxRuntime.jsx("span", { className: "ai-chat-link-preview__domain", children: state.siteName || domain })] }), jsxRuntime.jsx("h4", { className: "ai-chat-link-preview__title", children: state.title }), state.description && (jsxRuntime.jsx("p", { className: "ai-chat-link-preview__description", children: truncate(state.description, 120) })), state.context && (jsxRuntime.jsx("p", { className: "ai-chat-link-preview__context", children: state.context })), isError && state.error && (jsxRuntime.jsx("p", { className: "ai-chat-link-preview__error-text", children: state.error }))] }), jsxRuntime.jsx("div", { className: "ai-chat-link-preview__arrow", children: jsxRuntime.jsx(ExternalLinkIcon, {}) })] }));
|
|
28089
|
+
if (isError) {
|
|
28090
|
+
return (jsxRuntime.jsx("div", { className: "ai-chat-action-card ai-chat-link-preview ai-chat-link-preview--error", children: jsxRuntime.jsx("div", { className: "ai-chat-link-preview__content", children: jsxRuntime.jsx("p", { className: "ai-chat-link-preview__error-text", children: state.error || 'Failed to load preview' }) }) }));
|
|
28091
|
+
}
|
|
28092
|
+
if (state.links.length === 0) {
|
|
28093
|
+
return null;
|
|
28094
|
+
}
|
|
28095
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-link-preview-container", children: [state.context && (jsxRuntime.jsx("p", { className: "ai-chat-link-preview__context", style: { marginBottom: '8px', fontSize: '0.9em', color: 'var(--ai-chat-fg-muted)' }, children: state.context })), jsxRuntime.jsx("div", { className: "ai-chat-link-preview-grid", style: {
|
|
28096
|
+
display: 'grid',
|
|
28097
|
+
gridTemplateColumns: state.links.length === 1 ? '1fr' : state.links.length === 2 ? 'repeat(2, 1fr)' : 'repeat(3, 1fr)',
|
|
28098
|
+
gap: '12px',
|
|
28099
|
+
}, children: state.links.map((link, index) => (jsxRuntime.jsx(SingleLinkPreview, { link: link, onLinkClick: () => handleLinkClick(link.url), accentColor: accentColor }, index))) })] }));
|
|
27580
28100
|
}
|
|
27581
28101
|
|
|
27582
28102
|
function PlayIcon() {
|
|
@@ -27642,7 +28162,7 @@ function VideoPlayerCard({ action, onComplete, accentColor }) {
|
|
|
27642
28162
|
return src;
|
|
27643
28163
|
}
|
|
27644
28164
|
};
|
|
27645
|
-
return (jsxRuntime.jsxs("div", { className: "ai-chat-
|
|
28165
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-action-card ai-chat-video-player", style: style, children: [jsxRuntime.jsx("div", { className: "ai-chat-video-player__container", children: isError ? (jsxRuntime.jsx("div", { className: "ai-chat-video-player__error", children: jsxRuntime.jsx("span", { children: state.error || 'Could not load video' }) })) : !isPlaying && state.thumbnailUrl ? (jsxRuntime.jsxs("div", { className: "ai-chat-video-player__thumbnail", onClick: handlePlay, children: [jsxRuntime.jsx("img", { src: state.thumbnailUrl, alt: state.title || 'Video thumbnail' }), jsxRuntime.jsx("button", { className: "ai-chat-video-player__play-btn", "aria-label": "Play video", children: jsxRuntime.jsx(PlayIcon, {}) }), jsxRuntime.jsx("div", { className: "ai-chat-video-player__provider-badge", children: getProviderLabel(state.provider) })] })) : !isPlaying ? (jsxRuntime.jsxs("div", { className: "ai-chat-video-player__placeholder", onClick: handlePlay, children: [jsxRuntime.jsx("button", { className: "ai-chat-video-player__play-btn", "aria-label": "Play video", children: jsxRuntime.jsx(PlayIcon, {}) }), jsxRuntime.jsx("span", { className: "ai-chat-video-player__click-text", children: "Click to play" }), jsxRuntime.jsx("div", { className: "ai-chat-video-player__provider-badge", children: getProviderLabel(state.provider) })] })) : state.provider === 'direct' ? (jsxRuntime.jsx("video", { src: state.embedUrl, controls: true, autoPlay: true, className: "ai-chat-video-player__video" })) : (jsxRuntime.jsx("iframe", { src: getEmbedSrc(), className: "ai-chat-video-player__iframe", allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share", allowFullScreen: true, title: state.title || 'Video player' })) }), state.context && (jsxRuntime.jsx("div", { className: "ai-chat-video-player__context", children: state.context }))] }));
|
|
27646
28166
|
}
|
|
27647
28167
|
|
|
27648
28168
|
function MapPinIcon() {
|
|
@@ -27703,7 +28223,9 @@ function LocationItem({ location, settings, accentColor, onDirections, showMap =
|
|
|
27703
28223
|
}
|
|
27704
28224
|
};
|
|
27705
28225
|
const style = accentColor ? { '--action-accent': accentColor } : undefined;
|
|
27706
|
-
|
|
28226
|
+
// Use smaller map height in compact mode
|
|
28227
|
+
const effectiveMapHeight = compact ? Math.min(mapHeight, 140) : mapHeight;
|
|
28228
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-action-card ai-chat-location-card ${compact ? 'ai-chat-location-card--compact' : ''}`, style: style, children: [showMap && settings.showMap !== false && (jsxRuntime.jsx("div", { className: "ai-chat-location-card__map", style: { height: effectiveMapHeight }, children: jsxRuntime.jsx("iframe", { src: getMapEmbedUrl(), allowFullScreen: true, loading: "lazy", referrerPolicy: "no-referrer-when-downgrade", title: `Map of ${location.name}` }) })), jsxRuntime.jsxs("div", { className: "ai-chat-location-card__content", children: [jsxRuntime.jsxs("div", { className: "ai-chat-location-card__header", children: [jsxRuntime.jsx("h4", { className: "ai-chat-location-card__name", children: location.name }), location.type && (jsxRuntime.jsx("span", { className: "ai-chat-location-card__type", children: location.type })), openStatus !== null && (jsxRuntime.jsx("span", { className: `ai-chat-location-card__status ai-chat-location-card__status--${openStatus ? 'open' : 'closed'}`, children: openStatus ? 'Open' : 'Closed' }))] }), jsxRuntime.jsxs("p", { className: "ai-chat-location-card__address", children: [jsxRuntime.jsx(MapPinIcon, {}), location.address] }), location.description && (jsxRuntime.jsx("p", { className: "ai-chat-location-card__description", children: location.description })), settings.showHours !== false && location.hours && location.hours.length > 0 && (jsxRuntime.jsx(HoursDisplay, { hours: location.hours, compact: compact })), settings.showPhone !== false && location.phone && (jsxRuntime.jsxs("button", { type: "button", className: "ai-chat-location-card__phone", onClick: handleCall, children: [jsxRuntime.jsx(PhoneIcon, {}), location.phone] })), jsxRuntime.jsxs("div", { className: "ai-chat-location-card__actions", children: [settings.showDirectionsButton !== false && (jsxRuntime.jsxs("button", { type: "button", className: "ai-chat-location-card__button", style: accentColor ? { backgroundColor: accentColor } : undefined, onClick: onDirections, children: [jsxRuntime.jsx(NavigationIcon, {}), compact ? 'Directions' : 'Get Directions'] })), !compact && location.website && (jsxRuntime.jsxs("a", { href: location.website, target: "_blank", rel: "noopener noreferrer", className: "ai-chat-location-card__link", children: [jsxRuntime.jsx(GlobeIcon, {}), "Website"] }))] })] })] }));
|
|
27707
28229
|
}
|
|
27708
28230
|
function LocationCard({ action, onComplete, accentColor, maxColumns = 3 }) {
|
|
27709
28231
|
const rawState = action.state;
|
|
@@ -27737,40 +28259,443 @@ function LocationCard({ action, onComplete, accentColor, maxColumns = 3 }) {
|
|
|
27737
28259
|
if (isSingleLocation) {
|
|
27738
28260
|
return (jsxRuntime.jsx(LocationItem, { location: locations[0], settings: settings, accentColor: accentColor, onDirections: () => handleDirections(locations[0]), showMap: true }));
|
|
27739
28261
|
}
|
|
27740
|
-
return (jsxRuntime.jsxs("div", { className: `ai-chat-location-card-list ai-chat-location-card-list--${layout}`, children: [jsxRuntime.jsxs("div", { className: "ai-chat-location-card-list__header", children: [jsxRuntime.jsx(MapPinIcon, {}), jsxRuntime.jsxs("span", { children: [locations.length, " Locations"] })] }), layout === 'stack' && (jsxRuntime.jsx("div", { className:
|
|
27741
|
-
gridTemplateColumns: `repeat(${stackColumns}, minmax(0, 1fr))`,
|
|
27742
|
-
}, children: locations.map((location) => (jsxRuntime.jsx(LocationItem, { location: location, settings: settings, accentColor: accentColor, onDirections: () => handleDirections(location), showMap: settings.showMap !== false, compact: locations.length > 2 }, location.id))) })), layout === 'grid' && (jsxRuntime.jsx("div", { className: "ai-chat-location-card-list__grid", children: locations.map((location) => (jsxRuntime.jsx(LocationItem, { location: location, settings: settings, accentColor: accentColor, onDirections: () => handleDirections(location), showMap: false, compact: true }, location.id))) })), layout === 'carousel' && (jsxRuntime.jsx("div", { className: "ai-chat-location-card-list__carousel", children: locations.map((location) => (jsxRuntime.jsx(LocationItem, { location: location, settings: settings, accentColor: accentColor, onDirections: () => handleDirections(location), showMap: true, compact: false }, location.id))) }))] }));
|
|
28262
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-location-card-list ai-chat-location-card-list--${layout}`, children: [jsxRuntime.jsxs("div", { className: "ai-chat-location-card-list__header", children: [jsxRuntime.jsx(MapPinIcon, {}), jsxRuntime.jsxs("span", { children: [locations.length, " Locations"] })] }), layout === 'stack' && (jsxRuntime.jsx("div", { className: `ai-chat-location-card-list__stack ai-chat-location-card-list__stack--cols-${stackColumns}`, children: locations.map((location) => (jsxRuntime.jsx(LocationItem, { location: location, settings: settings, accentColor: accentColor, onDirections: () => handleDirections(location), showMap: settings.showMap !== false, compact: locations.length > 2 }, location.id))) })), layout === 'grid' && (jsxRuntime.jsx("div", { className: "ai-chat-location-card-list__grid", children: locations.map((location) => (jsxRuntime.jsx(LocationItem, { location: location, settings: settings, accentColor: accentColor, onDirections: () => handleDirections(location), showMap: false, compact: true }, location.id))) })), layout === 'carousel' && (jsxRuntime.jsx("div", { className: "ai-chat-location-card-list__carousel", children: locations.map((location) => (jsxRuntime.jsx(LocationItem, { location: location, settings: settings, accentColor: accentColor, onDirections: () => handleDirections(location), showMap: true, compact: false }, location.id))) }))] }));
|
|
27743
28263
|
}
|
|
27744
28264
|
|
|
27745
|
-
|
|
27746
|
-
|
|
27747
|
-
const frontendActionHandlers = {};
|
|
27748
|
-
const actionRenderers = {};
|
|
27749
|
-
function getFrontendActionHandler(implementation) {
|
|
27750
|
-
return frontendActionHandlers[implementation];
|
|
28265
|
+
function UsersIcon() {
|
|
28266
|
+
return (jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("path", { d: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" }), jsxRuntime.jsx("circle", { cx: "9", cy: "7", r: "4" }), jsxRuntime.jsx("path", { d: "M22 21v-2a4 4 0 0 0-3-3.87" }), jsxRuntime.jsx("path", { d: "M16 3.13a4 4 0 0 1 0 7.75" })] }));
|
|
27751
28267
|
}
|
|
27752
|
-
|
|
27753
|
-
|
|
27754
|
-
}
|
|
27755
|
-
|
|
27756
|
-
|
|
27757
|
-
|
|
28268
|
+
const AVATAR_COLORS = [
|
|
28269
|
+
{ bg: '#E8F5E9', text: '#2E7D32' }, // Green
|
|
28270
|
+
{ bg: '#E3F2FD', text: '#1565C0' }, // Blue
|
|
28271
|
+
{ bg: '#FFF3E0', text: '#E65100' }, // Orange
|
|
28272
|
+
{ bg: '#F3E5F5', text: '#7B1FA2' }, // Purple
|
|
28273
|
+
{ bg: '#FFEBEE', text: '#C62828' }, // Red
|
|
28274
|
+
{ bg: '#E0F7FA', text: '#00838F' }, // Cyan
|
|
28275
|
+
{ bg: '#FFF8E1', text: '#F9A825' }, // Amber
|
|
28276
|
+
{ bg: '#FCE4EC', text: '#AD1457' }, // Pink
|
|
28277
|
+
];
|
|
28278
|
+
function getInitials(name) {
|
|
28279
|
+
const parts = name.trim().split(/\s+/);
|
|
28280
|
+
if (parts.length === 1) {
|
|
28281
|
+
return parts[0].substring(0, 2).toUpperCase();
|
|
27758
28282
|
}
|
|
27759
|
-
return
|
|
27760
|
-
}
|
|
27761
|
-
function waitForActionState(toolCallId) {
|
|
27762
|
-
return new Promise((resolve) => {
|
|
27763
|
-
pendingResolvers.set(toolCallId, resolve);
|
|
27764
|
-
});
|
|
28283
|
+
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
|
|
27765
28284
|
}
|
|
27766
|
-
function
|
|
27767
|
-
|
|
27768
|
-
|
|
27769
|
-
|
|
27770
|
-
resolver(state);
|
|
27771
|
-
return;
|
|
28285
|
+
function getColorFromName(name) {
|
|
28286
|
+
let hash = 0;
|
|
28287
|
+
for (let i = 0; i < name.length; i++) {
|
|
28288
|
+
hash = name.charCodeAt(i) + ((hash << 5) - hash);
|
|
27772
28289
|
}
|
|
27773
|
-
|
|
28290
|
+
return AVATAR_COLORS[Math.abs(hash) % AVATAR_COLORS.length];
|
|
28291
|
+
}
|
|
28292
|
+
function ContactItem({ contact, settings, accentColor, onEmail, onPhone, compact = false, layout = 'vertical', }) {
|
|
28293
|
+
const style = accentColor ? { '--action-accent': accentColor } : undefined;
|
|
28294
|
+
const layoutClass = layout === 'horizontal'
|
|
28295
|
+
? 'ai-chat-contact-card--horizontal'
|
|
28296
|
+
: 'ai-chat-contact-card--vertical';
|
|
28297
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-action-card ai-chat-contact-card ${layoutClass} ${compact ? 'ai-chat-contact-card--compact' : ''}`, style: style, children: [jsxRuntime.jsxs("div", { className: "ai-chat-contact-card__image-section", children: [contact.profilePictureUrl ? (jsxRuntime.jsx("img", { src: contact.profilePictureUrl, alt: contact.name, className: "ai-chat-contact-card__image", onError: (e) => {
|
|
28298
|
+
e.currentTarget.style.display = 'none';
|
|
28299
|
+
const placeholder = e.currentTarget.parentElement?.querySelector('.ai-chat-contact-card__initials');
|
|
28300
|
+
if (placeholder) {
|
|
28301
|
+
placeholder.style.display = 'flex';
|
|
28302
|
+
}
|
|
28303
|
+
} })) : null, (() => {
|
|
28304
|
+
const colors = getColorFromName(contact.name);
|
|
28305
|
+
return (jsxRuntime.jsx("div", { className: "ai-chat-contact-card__initials", style: {
|
|
28306
|
+
display: contact.profilePictureUrl ? 'none' : 'flex',
|
|
28307
|
+
backgroundColor: colors.bg,
|
|
28308
|
+
color: colors.text,
|
|
28309
|
+
}, children: getInitials(contact.name) }));
|
|
28310
|
+
})()] }), jsxRuntime.jsxs("div", { className: "ai-chat-contact-card__info", children: [jsxRuntime.jsx("h4", { className: "ai-chat-contact-card__name", children: contact.name }), settings.showRole !== false && contact.role && (jsxRuntime.jsx("p", { className: "ai-chat-contact-card__role", children: contact.role })), jsxRuntime.jsxs("div", { className: "ai-chat-contact-card__details", children: [settings.showEmail !== false && contact.email && (jsxRuntime.jsx("a", { href: `mailto:${contact.email}`, className: "ai-chat-contact-card__detail", onClick: onEmail, children: contact.email })), settings.showPhone !== false && contact.phone && (jsxRuntime.jsx("a", { href: `tel:${contact.phone}`, className: "ai-chat-contact-card__detail", onClick: onPhone, children: contact.phone }))] }), settings.showResponsibilities !== false && contact.responsibilities && contact.responsibilities.length > 0 && !compact && (jsxRuntime.jsxs("div", { className: "ai-chat-contact-card__responsibilities", children: [contact.responsibilities.slice(0, 3).map((resp, idx) => (jsxRuntime.jsx("span", { className: "ai-chat-contact-card__responsibility-tag", children: resp }, idx))), contact.responsibilities.length > 3 && (jsxRuntime.jsxs("span", { className: "ai-chat-contact-card__responsibility-more", children: ["+", contact.responsibilities.length - 3, " more"] }))] }))] })] }));
|
|
28311
|
+
}
|
|
28312
|
+
function ContactCard({ action, onComplete, accentColor, maxColumns = 3 }) {
|
|
28313
|
+
const rawState = action.state;
|
|
28314
|
+
const hasCompletedRef = React.useRef(false);
|
|
28315
|
+
const state = {
|
|
28316
|
+
contacts: rawState?.contacts || [],
|
|
28317
|
+
settings: rawState?.settings || {},
|
|
28318
|
+
query: rawState?.query,
|
|
28319
|
+
status: rawState?.status || 'displaying',
|
|
28320
|
+
};
|
|
28321
|
+
const { contacts, settings } = state;
|
|
28322
|
+
const isSingleContact = contacts.length === 1;
|
|
28323
|
+
const stackColumns = Math.min(Math.max(contacts.length, 1), maxColumns);
|
|
28324
|
+
React.useEffect(() => {
|
|
28325
|
+
if (!action.done && !hasCompletedRef.current && onComplete) {
|
|
28326
|
+
hasCompletedRef.current = true;
|
|
28327
|
+
onComplete(action.toolCallId, { ...state, status: 'displaying' });
|
|
28328
|
+
}
|
|
28329
|
+
}, [action.done, action.toolCallId, onComplete, state]);
|
|
28330
|
+
const handleContact = () => {
|
|
28331
|
+
onComplete?.(action.toolCallId, { ...state, status: 'contacted' });
|
|
28332
|
+
};
|
|
28333
|
+
if (contacts.length === 0) {
|
|
28334
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-contact-card ai-chat-contact-card--empty", children: [jsxRuntime.jsx("div", { className: "ai-chat-contact-card__empty-icon", children: jsxRuntime.jsx(UsersIcon, {}) }), jsxRuntime.jsxs("p", { className: "ai-chat-contact-card__empty-text", children: ["No contacts found", state.query ? ` for "${state.query}"` : ''] })] }));
|
|
28335
|
+
}
|
|
28336
|
+
if (isSingleContact) {
|
|
28337
|
+
return (jsxRuntime.jsx(ContactItem, { contact: contacts[0], settings: settings, accentColor: accentColor, onEmail: handleContact, onPhone: handleContact, layout: settings.layout || 'horizontal' }));
|
|
28338
|
+
}
|
|
28339
|
+
const isWidget = maxColumns === 1;
|
|
28340
|
+
const stackClassName = isWidget
|
|
28341
|
+
? 'ai-chat-contact-card-list__stack ai-chat-contact-card-list__stack--widget'
|
|
28342
|
+
: 'ai-chat-contact-card-list__stack';
|
|
28343
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-contact-card-list", style: { containerType: 'inline-size' }, children: [jsxRuntime.jsxs("div", { className: "ai-chat-contact-card-list__header", children: [jsxRuntime.jsx(UsersIcon, {}), jsxRuntime.jsxs("span", { children: [contacts.length, " Contacts"] })] }), jsxRuntime.jsx("div", { className: stackClassName, style: isWidget ? undefined : {
|
|
28344
|
+
gridTemplateColumns: `repeat(${stackColumns}, minmax(0, 1fr))`,
|
|
28345
|
+
}, children: contacts.map((contact) => (jsxRuntime.jsx(ContactItem, { contact: contact, settings: settings, accentColor: accentColor, onEmail: handleContact, onPhone: handleContact, compact: true, layout: settings.layout || 'vertical' }, contact.id))) })] }));
|
|
28346
|
+
}
|
|
28347
|
+
|
|
28348
|
+
function FormCard({ action, onComplete, onDismiss, accentColor }) {
|
|
28349
|
+
const state = action.state;
|
|
28350
|
+
const [currentStep, setCurrentStep] = React.useState(0);
|
|
28351
|
+
const [answers, setAnswers] = React.useState({});
|
|
28352
|
+
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
|
28353
|
+
const questions = state.questions || [];
|
|
28354
|
+
const currentQuestion = questions[currentStep];
|
|
28355
|
+
const totalQuestions = questions.length;
|
|
28356
|
+
const handleAnswerChange = (questionId, value) => {
|
|
28357
|
+
setAnswers((prev) => ({ ...prev, [questionId]: value }));
|
|
28358
|
+
};
|
|
28359
|
+
const handleNext = () => {
|
|
28360
|
+
if (currentStep < totalQuestions - 1) {
|
|
28361
|
+
setCurrentStep((prev) => prev + 1);
|
|
28362
|
+
}
|
|
28363
|
+
};
|
|
28364
|
+
const handlePrev = () => {
|
|
28365
|
+
if (currentStep > 0) {
|
|
28366
|
+
setCurrentStep((prev) => prev - 1);
|
|
28367
|
+
}
|
|
28368
|
+
};
|
|
28369
|
+
const handleSubmit = () => {
|
|
28370
|
+
if (!onComplete)
|
|
28371
|
+
return;
|
|
28372
|
+
setIsSubmitting(true);
|
|
28373
|
+
const formattedAnswers = Object.entries(answers).map(([questionId, value]) => ({
|
|
28374
|
+
questionId,
|
|
28375
|
+
value,
|
|
28376
|
+
}));
|
|
28377
|
+
onComplete(action.toolCallId, {
|
|
28378
|
+
...state,
|
|
28379
|
+
status: 'submitted',
|
|
28380
|
+
answers: formattedAnswers,
|
|
28381
|
+
});
|
|
28382
|
+
};
|
|
28383
|
+
const handleSkip = () => {
|
|
28384
|
+
if (!onComplete || !state.settings.allowSkip)
|
|
28385
|
+
return;
|
|
28386
|
+
onComplete(action.toolCallId, {
|
|
28387
|
+
...state,
|
|
28388
|
+
status: 'skipped',
|
|
28389
|
+
});
|
|
28390
|
+
};
|
|
28391
|
+
const isCurrentAnswered = () => {
|
|
28392
|
+
if (!currentQuestion)
|
|
28393
|
+
return false;
|
|
28394
|
+
const answer = answers[currentQuestion.id];
|
|
28395
|
+
if (!answer)
|
|
28396
|
+
return !currentQuestion.required;
|
|
28397
|
+
if (Array.isArray(answer))
|
|
28398
|
+
return answer.length > 0 || !currentQuestion.required;
|
|
28399
|
+
return answer.trim() !== '' || !currentQuestion.required;
|
|
28400
|
+
};
|
|
28401
|
+
const canSubmit = () => {
|
|
28402
|
+
return questions.every((q) => {
|
|
28403
|
+
const answer = answers[q.id];
|
|
28404
|
+
if (!q.required)
|
|
28405
|
+
return true;
|
|
28406
|
+
if (!answer)
|
|
28407
|
+
return false;
|
|
28408
|
+
if (Array.isArray(answer))
|
|
28409
|
+
return answer.length > 0;
|
|
28410
|
+
return answer.trim() !== '';
|
|
28411
|
+
});
|
|
28412
|
+
};
|
|
28413
|
+
const handleDismiss = () => {
|
|
28414
|
+
onDismiss?.(action.toolCallId);
|
|
28415
|
+
};
|
|
28416
|
+
// Error state
|
|
28417
|
+
if (state.status === 'error') {
|
|
28418
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-form-card ai-chat-form-card--error", children: [jsxRuntime.jsxs("div", { className: "ai-chat-form-card__header", children: [jsxRuntime.jsx("span", { className: "ai-chat-form-card__icon", children: "\u26A0\uFE0F" }), jsxRuntime.jsx("span", { className: "ai-chat-form-card__title", children: "Form Error" })] }), jsxRuntime.jsx("p", { className: "ai-chat-form-card__error", children: state.error || 'Could not load form' })] }));
|
|
28419
|
+
}
|
|
28420
|
+
// Submitted state
|
|
28421
|
+
if (state.status === 'submitted' || action.done) {
|
|
28422
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-form-card ai-chat-form-card--submitted", children: [jsxRuntime.jsxs("div", { className: "ai-chat-form-card__header", children: [jsxRuntime.jsx("span", { className: "ai-chat-form-card__icon", children: "\u2713" }), jsxRuntime.jsx("span", { className: "ai-chat-form-card__title", children: state.title })] }), jsxRuntime.jsx("p", { className: "ai-chat-form-card__success", children: state.settings.successMessage || 'Thank you for your response!' })] }));
|
|
28423
|
+
}
|
|
28424
|
+
// Skipped state
|
|
28425
|
+
if (state.status === 'skipped') {
|
|
28426
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-form-card ai-chat-form-card--skipped", children: [jsxRuntime.jsxs("div", { className: "ai-chat-form-card__header", children: [jsxRuntime.jsx("span", { className: "ai-chat-form-card__icon", children: "\u21B7" }), jsxRuntime.jsx("span", { className: "ai-chat-form-card__title", children: state.title })] }), jsxRuntime.jsx("p", { className: "ai-chat-form-card__skipped-text", children: "Form skipped" })] }));
|
|
28427
|
+
}
|
|
28428
|
+
// No questions
|
|
28429
|
+
if (totalQuestions === 0) {
|
|
28430
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-form-card ai-chat-form-card--empty", children: [jsxRuntime.jsxs("div", { className: "ai-chat-form-card__header", children: [jsxRuntime.jsx("span", { className: "ai-chat-form-card__icon", children: "\uD83D\uDCCB" }), jsxRuntime.jsx("span", { className: "ai-chat-form-card__title", children: state.title })] }), jsxRuntime.jsx("p", { className: "ai-chat-form-card__empty-text", children: "This form has no questions." })] }));
|
|
28431
|
+
}
|
|
28432
|
+
const showCloseButton = state.status === "displaying" && !action.done && Boolean(onDismiss);
|
|
28433
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-form-card${showCloseButton ? " ai-chat-form-card--closable" : ""}`, children: [jsxRuntime.jsxs("div", { className: "ai-chat-form-card__header", children: [jsxRuntime.jsx("span", { className: "ai-chat-form-card__icon", children: "\uD83D\uDCCB" }), jsxRuntime.jsx("span", { className: "ai-chat-form-card__title", children: state.title }), showCloseButton && (jsxRuntime.jsx(CloseButton, { onClick: handleDismiss, ariaLabel: "Close form" }))] }), state.description && (jsxRuntime.jsx("p", { className: "ai-chat-form-card__description", children: state.description })), state.context && (jsxRuntime.jsx("p", { className: "ai-chat-form-card__context", children: state.context })), state.settings.showProgress && (jsxRuntime.jsxs("div", { className: "ai-chat-form-card__progress", children: [jsxRuntime.jsx("div", { className: "ai-chat-form-card__progress-bar", style: {
|
|
28434
|
+
width: `${((currentStep + 1) / totalQuestions) * 100}%`,
|
|
28435
|
+
backgroundColor: accentColor || 'var(--ai-chat-accent-color, #3b82f6)',
|
|
28436
|
+
} }), jsxRuntime.jsxs("span", { className: "ai-chat-form-card__progress-text", children: [currentStep + 1, " of ", totalQuestions] })] })), jsxRuntime.jsxs("div", { className: "ai-chat-form-card__question", children: [jsxRuntime.jsxs("p", { className: "ai-chat-form-card__question-text", children: [currentQuestion.text, currentQuestion.required && jsxRuntime.jsx("span", { className: "ai-chat-form-card__required", children: "*" })] }), jsxRuntime.jsxs("div", { className: "ai-chat-form-card__answer", children: [currentQuestion.type === 'text' && (jsxRuntime.jsx("textarea", { className: "ai-chat-form-card__textarea", placeholder: currentQuestion.placeholder || 'Type your answer...', value: answers[currentQuestion.id] || '', onChange: (e) => handleAnswerChange(currentQuestion.id, e.target.value), rows: 3 })), currentQuestion.type === 'single_choice' && currentQuestion.options && (jsxRuntime.jsx("div", { className: "ai-chat-form-card__options", children: currentQuestion.options.map((option) => (jsxRuntime.jsxs("label", { className: "ai-chat-form-card__option", children: [jsxRuntime.jsx("input", { type: "radio", name: currentQuestion.id, value: option.value, checked: answers[currentQuestion.id] === option.value, onChange: () => handleAnswerChange(currentQuestion.id, option.value) }), jsxRuntime.jsx("span", { className: "ai-chat-form-card__option-text", children: option.text })] }, option.id))) })), currentQuestion.type === 'multiple_choice' && currentQuestion.options && (jsxRuntime.jsx("div", { className: "ai-chat-form-card__options", children: currentQuestion.options.map((option) => {
|
|
28437
|
+
const currentAnswers = answers[currentQuestion.id] || [];
|
|
28438
|
+
const isChecked = currentAnswers.includes(option.value);
|
|
28439
|
+
return (jsxRuntime.jsxs("label", { className: "ai-chat-form-card__option", children: [jsxRuntime.jsx("input", { type: "checkbox", value: option.value, checked: isChecked, onChange: () => {
|
|
28440
|
+
const newAnswers = isChecked
|
|
28441
|
+
? currentAnswers.filter((v) => v !== option.value)
|
|
28442
|
+
: [...currentAnswers, option.value];
|
|
28443
|
+
handleAnswerChange(currentQuestion.id, newAnswers);
|
|
28444
|
+
} }), jsxRuntime.jsx("span", { className: "ai-chat-form-card__option-text", children: option.text })] }, option.id));
|
|
28445
|
+
}) })), currentQuestion.type === 'rating' && (jsxRuntime.jsx("div", { className: "ai-chat-form-card__rating", children: Array.from({ length: (currentQuestion.maxRating || 5) - (currentQuestion.minRating || 1) + 1 }, (_, i) => (currentQuestion.minRating || 1) + i).map((rating) => (jsxRuntime.jsx("button", { type: "button", className: `ai-chat-form-card__rating-btn ${answers[currentQuestion.id] === String(rating) ? 'ai-chat-form-card__rating-btn--selected' : ''}`, onClick: () => handleAnswerChange(currentQuestion.id, String(rating)), style: {
|
|
28446
|
+
borderColor: answers[currentQuestion.id] === String(rating)
|
|
28447
|
+
? (accentColor || 'var(--ai-chat-accent-color, #3b82f6)')
|
|
28448
|
+
: undefined,
|
|
28449
|
+
backgroundColor: answers[currentQuestion.id] === String(rating)
|
|
28450
|
+
? (accentColor || 'var(--ai-chat-accent-color, #3b82f6)')
|
|
28451
|
+
: undefined,
|
|
28452
|
+
}, children: rating }, rating))) }))] })] }), jsxRuntime.jsxs("div", { className: "ai-chat-form-card__actions", children: [currentStep > 0 && (jsxRuntime.jsx("button", { type: "button", className: "ai-chat-form-card__btn ai-chat-form-card__btn--secondary", onClick: handlePrev, children: "Back" })), state.settings.allowSkip && currentStep === 0 && (jsxRuntime.jsx("button", { type: "button", className: "ai-chat-form-card__btn ai-chat-form-card__btn--ghost", onClick: handleSkip, children: "Skip" })), jsxRuntime.jsx("div", { className: "ai-chat-form-card__actions-spacer" }), currentStep < totalQuestions - 1 ? (jsxRuntime.jsx("button", { type: "button", className: "ai-chat-form-card__btn ai-chat-form-card__btn--primary", onClick: handleNext, disabled: !isCurrentAnswered(), style: {
|
|
28453
|
+
backgroundColor: accentColor || 'var(--ai-chat-accent-color, #3b82f6)',
|
|
28454
|
+
}, children: "Next" })) : (jsxRuntime.jsx("button", { type: "button", className: "ai-chat-form-card__btn ai-chat-form-card__btn--primary", onClick: handleSubmit, disabled: !canSubmit() || isSubmitting, style: {
|
|
28455
|
+
backgroundColor: accentColor || 'var(--ai-chat-accent-color, #3b82f6)',
|
|
28456
|
+
}, children: isSubmitting ? 'Submitting...' : (state.settings.submitButtonText || 'Submit') }))] })] }));
|
|
28457
|
+
}
|
|
28458
|
+
|
|
28459
|
+
function Skeleton({ width, height, borderRadius = '4px' }) {
|
|
28460
|
+
return (jsxRuntime.jsx("div", { className: "ai-chat-action-skeleton-item", style: { width, height, borderRadius } }));
|
|
28461
|
+
}
|
|
28462
|
+
function PinInputGroup({ values, onChange, disabled }) {
|
|
28463
|
+
const inputRefs = React.useRef([]);
|
|
28464
|
+
const handleChange = (index, value) => {
|
|
28465
|
+
// Only allow digits
|
|
28466
|
+
const digit = value.replace(/[^0-9]/g, '');
|
|
28467
|
+
const newValues = [...values];
|
|
28468
|
+
newValues[index] = digit.slice(-1);
|
|
28469
|
+
onChange(newValues);
|
|
28470
|
+
// Auto-focus next input
|
|
28471
|
+
if (digit && index < 5) {
|
|
28472
|
+
inputRefs.current[index + 1]?.focus();
|
|
28473
|
+
}
|
|
28474
|
+
};
|
|
28475
|
+
const handleKeyDown = (index, e) => {
|
|
28476
|
+
if (e.key === 'Backspace' && !values[index] && index > 0) {
|
|
28477
|
+
inputRefs.current[index - 1]?.focus();
|
|
28478
|
+
}
|
|
28479
|
+
};
|
|
28480
|
+
const handlePaste = (e) => {
|
|
28481
|
+
e.preventDefault();
|
|
28482
|
+
const pastedData = e.clipboardData.getData('text').replace(/[^0-9]/g, '').slice(0, 6);
|
|
28483
|
+
const newValues = pastedData.split('').concat(Array(6 - pastedData.length).fill(''));
|
|
28484
|
+
onChange(newValues);
|
|
28485
|
+
// Focus the next empty input or the last one
|
|
28486
|
+
const nextIndex = Math.min(pastedData.length, 5);
|
|
28487
|
+
inputRefs.current[nextIndex]?.focus();
|
|
28488
|
+
};
|
|
28489
|
+
return (jsxRuntime.jsx("div", { className: "ai-chat-pin-input-group", children: values.map((value, index) => (jsxRuntime.jsx("input", { ref: (el) => {
|
|
28490
|
+
inputRefs.current[index] = el;
|
|
28491
|
+
}, type: "text", inputMode: "numeric", maxLength: 1, className: "ai-chat-pin-input", value: value, onChange: (e) => handleChange(index, e.target.value), onKeyDown: (e) => handleKeyDown(index, e), onPaste: handlePaste, disabled: disabled, autoFocus: index === 0 }, index))) }));
|
|
28492
|
+
}
|
|
28493
|
+
function BookContactAppointmentCard({ action, onComplete, accentColor }) {
|
|
28494
|
+
const state = action.state;
|
|
28495
|
+
const [emailInput, setEmailInput] = React.useState('');
|
|
28496
|
+
const [otpValues, setOtpValues] = React.useState(Array(6).fill(''));
|
|
28497
|
+
const [subjectInput, setSubjectInput] = React.useState(state.subject || '');
|
|
28498
|
+
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
|
28499
|
+
const [emailError, setEmailError] = React.useState(null);
|
|
28500
|
+
const [otpError, setOtpError] = React.useState(null);
|
|
28501
|
+
const phase = state.phase || 'awaiting_email';
|
|
28502
|
+
const handleSubmit = (newState, delay = 50) => {
|
|
28503
|
+
if (!onComplete)
|
|
28504
|
+
return;
|
|
28505
|
+
setIsSubmitting(true);
|
|
28506
|
+
setTimeout(() => {
|
|
28507
|
+
onComplete(action.toolCallId, { ...state, ...newState, errorMessage: null });
|
|
28508
|
+
}, delay);
|
|
28509
|
+
};
|
|
28510
|
+
React.useEffect(() => {
|
|
28511
|
+
setIsSubmitting(false);
|
|
28512
|
+
if (phase === 'awaiting_email') {
|
|
28513
|
+
setEmailError(null);
|
|
28514
|
+
setOtpError(null);
|
|
28515
|
+
}
|
|
28516
|
+
else if (phase === 'awaiting_otp') {
|
|
28517
|
+
setOtpError(state.errorMessage || null);
|
|
28518
|
+
setEmailError(null);
|
|
28519
|
+
setOtpValues(Array(6).fill(''));
|
|
28520
|
+
if (state.email) {
|
|
28521
|
+
setEmailInput(state.email);
|
|
28522
|
+
}
|
|
28523
|
+
}
|
|
28524
|
+
else {
|
|
28525
|
+
setEmailError(null);
|
|
28526
|
+
setOtpError(null);
|
|
28527
|
+
}
|
|
28528
|
+
}, [phase, state.errorMessage]);
|
|
28529
|
+
const isWaitingForBackend = !action.done && Boolean(state.confirmed) && phase === 'awaiting_confirmation';
|
|
28530
|
+
const renderSkeleton = () => (jsxRuntime.jsx("div", { className: "ai-chat-booking-card", children: jsxRuntime.jsxs("div", { className: "ai-chat-action-skeleton-content", children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-skeleton-header", children: [jsxRuntime.jsx(Skeleton, { width: "28px", height: "28px", borderRadius: "50%" }), jsxRuntime.jsx(Skeleton, { width: "180px", height: "20px", borderRadius: "4px" })] }), jsxRuntime.jsxs("div", { className: "ai-chat-action-skeleton-box", children: [jsxRuntime.jsx(Skeleton, { width: "60px", height: "12px", borderRadius: "4px" }), jsxRuntime.jsx(Skeleton, { width: "120px", height: "18px", borderRadius: "4px" })] }), jsxRuntime.jsx(Skeleton, { width: "100%", height: "44px", borderRadius: "999px" })] }) }));
|
|
28531
|
+
const handleEmailSubmit = () => {
|
|
28532
|
+
const trimmedEmail = emailInput.trim();
|
|
28533
|
+
if (!trimmedEmail) {
|
|
28534
|
+
setEmailError('Please enter your email address');
|
|
28535
|
+
return;
|
|
28536
|
+
}
|
|
28537
|
+
setEmailError(null);
|
|
28538
|
+
setEmailInput(trimmedEmail);
|
|
28539
|
+
handleSubmit({ email: trimmedEmail });
|
|
28540
|
+
};
|
|
28541
|
+
const handleOtpSubmit = () => {
|
|
28542
|
+
const code = otpValues.join('');
|
|
28543
|
+
if (code.length !== 6) {
|
|
28544
|
+
setOtpError('Please enter the 6-digit code');
|
|
28545
|
+
return;
|
|
28546
|
+
}
|
|
28547
|
+
setOtpError(null);
|
|
28548
|
+
const resolvedEmail = state.email || emailInput.trim() || null;
|
|
28549
|
+
handleSubmit({ otpCode: code, email: resolvedEmail });
|
|
28550
|
+
};
|
|
28551
|
+
// ========================================
|
|
28552
|
+
// Terminal States
|
|
28553
|
+
// ========================================
|
|
28554
|
+
if (phase === 'booked') {
|
|
28555
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-booking-card ai-chat-booking-card--success", children: [jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__header", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__icon", children: "\u2713" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__title", children: "Appointment Booked" })] }), state.bookedTeamsLink && (jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__content", children: [jsxRuntime.jsx("p", { className: "ai-chat-booking-card__success-text", children: "Your appointment has been confirmed. You can join via Microsoft Teams." }), jsxRuntime.jsx("a", { href: state.bookedTeamsLink, target: "_blank", rel: "noopener noreferrer", className: "ai-chat-booking-card__link", style: { color: accentColor }, children: "Join Teams Meeting \u2192" })] }))] }));
|
|
28556
|
+
}
|
|
28557
|
+
if (phase === 'pending_approval') {
|
|
28558
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-booking-card ai-chat-booking-card--pending", children: [jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__header", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__icon", children: "\u23F3" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__title", children: "Awaiting Approval" })] }), jsxRuntime.jsx("div", { className: "ai-chat-booking-card__content", children: jsxRuntime.jsx("p", { className: "ai-chat-booking-card__pending-text", children: "Your appointment request has been sent and is awaiting approval from the contact." }) })] }));
|
|
28559
|
+
}
|
|
28560
|
+
if (phase === 'cancelled') {
|
|
28561
|
+
return (jsxRuntime.jsx("div", { className: "ai-chat-booking-card ai-chat-booking-card--cancelled", children: jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__header", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__icon", children: "\u2715" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__title", children: "Appointment Cancelled" })] }) }));
|
|
28562
|
+
}
|
|
28563
|
+
if (phase === 'error') {
|
|
28564
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-booking-card ai-chat-booking-card--error", children: [jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__header", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__icon", children: "\u26A0\uFE0F" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__title", children: "Booking Error" })] }), state.errorMessage && (jsxRuntime.jsx("p", { className: "ai-chat-booking-card__error", children: state.errorMessage }))] }));
|
|
28565
|
+
}
|
|
28566
|
+
if (isSubmitting || isWaitingForBackend) {
|
|
28567
|
+
return renderSkeleton();
|
|
28568
|
+
}
|
|
28569
|
+
// ========================================
|
|
28570
|
+
// Phase: Email Collection
|
|
28571
|
+
// ========================================
|
|
28572
|
+
if (phase === 'awaiting_email') {
|
|
28573
|
+
const displayError = emailError || state.errorMessage;
|
|
28574
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-booking-card", children: [jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__header", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__icon", children: "\uD83D\uDCE7" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__title", children: "Enter Your Email" })] }), jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__content", children: [jsxRuntime.jsx("p", { className: "ai-chat-booking-card__description", children: "Please enter your email address to start the booking process." }), displayError && (jsxRuntime.jsx("p", { className: "ai-chat-booking-card__error", children: displayError })), jsxRuntime.jsx("input", { type: "email", className: "ai-chat-booking-card__input", placeholder: "your@email.com", value: emailInput, onChange: (e) => {
|
|
28575
|
+
setEmailInput(e.target.value);
|
|
28576
|
+
setEmailError(null);
|
|
28577
|
+
}, onKeyDown: (e) => {
|
|
28578
|
+
if (e.key === 'Enter') {
|
|
28579
|
+
handleEmailSubmit();
|
|
28580
|
+
}
|
|
28581
|
+
} }), jsxRuntime.jsx("button", { className: "ai-chat-booking-card__btn ai-chat-booking-card__btn--primary", onClick: handleEmailSubmit, disabled: !emailInput.trim(), style: {
|
|
28582
|
+
backgroundColor: accentColor || undefined,
|
|
28583
|
+
borderColor: accentColor || undefined,
|
|
28584
|
+
}, children: "Continue" })] })] }));
|
|
28585
|
+
}
|
|
28586
|
+
// ========================================
|
|
28587
|
+
// Phase: OTP Verification
|
|
28588
|
+
// ========================================
|
|
28589
|
+
if (phase === 'awaiting_otp') {
|
|
28590
|
+
const displayError = otpError || state.errorMessage;
|
|
28591
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-booking-card", children: [jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__header", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__icon", children: "\uD83D\uDD10" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__title", children: "Verify Your Email" })] }), jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__content", children: [jsxRuntime.jsxs("p", { className: "ai-chat-booking-card__description", children: ["Enter the verification code sent to ", jsxRuntime.jsx("strong", { children: state.email })] }), jsxRuntime.jsx(PinInputGroup, { values: otpValues, onChange: (newValues) => {
|
|
28592
|
+
setOtpValues(newValues);
|
|
28593
|
+
setOtpError(null);
|
|
28594
|
+
if (newValues.every((value) => value.length === 1) && !isSubmitting) {
|
|
28595
|
+
const resolvedEmail = state.email || emailInput.trim() || null;
|
|
28596
|
+
handleSubmit({ otpCode: newValues.join(''), email: resolvedEmail }, 100);
|
|
28597
|
+
}
|
|
28598
|
+
}, disabled: isSubmitting }), displayError && (jsxRuntime.jsx("p", { className: "ai-chat-booking-card__error", children: displayError })), jsxRuntime.jsx("button", { className: "ai-chat-booking-card__btn ai-chat-booking-card__btn--primary", onClick: handleOtpSubmit, disabled: otpValues.join('').length !== 6, style: {
|
|
28599
|
+
backgroundColor: accentColor || undefined,
|
|
28600
|
+
borderColor: accentColor || undefined,
|
|
28601
|
+
}, children: "Verify" })] })] }));
|
|
28602
|
+
}
|
|
28603
|
+
// ========================================
|
|
28604
|
+
// Phase: Contact Selection
|
|
28605
|
+
// ========================================
|
|
28606
|
+
if (phase === 'awaiting_contact_selection') {
|
|
28607
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-booking-card", children: [jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__header", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__icon", children: "\uD83D\uDC65" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__title", children: "Select a Contact" })] }), jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__content", children: [state.errorMessage && (jsxRuntime.jsx("p", { className: "ai-chat-booking-card__error", children: state.errorMessage })), state.bookableContacts.length === 0 ? (jsxRuntime.jsx("p", { className: "ai-chat-booking-card__empty", children: "No contacts available for booking." })) : (jsxRuntime.jsx("div", { className: "ai-chat-booking-card__grid", children: state.bookableContacts.map((contact) => (jsxRuntime.jsxs("button", { className: `ai-chat-booking-card__contact ${state.selectedContactId === contact.id ? 'ai-chat-booking-card__contact--selected' : ''}`, onClick: () => handleSubmit({ selectedContactId: contact.id }), style: {
|
|
28608
|
+
borderColor: state.selectedContactId === contact.id
|
|
28609
|
+
? accentColor || undefined
|
|
28610
|
+
: undefined,
|
|
28611
|
+
backgroundColor: state.selectedContactId === contact.id
|
|
28612
|
+
? `${accentColor || '#3b82f6'}15`
|
|
28613
|
+
: undefined,
|
|
28614
|
+
}, children: [jsxRuntime.jsx("div", { className: "ai-chat-booking-card__contact-name", children: contact.name }), contact.role && (jsxRuntime.jsx("div", { className: "ai-chat-booking-card__contact-role", children: contact.role }))] }, contact.id))) }))] })] }));
|
|
28615
|
+
}
|
|
28616
|
+
// ========================================
|
|
28617
|
+
// Phase: Options Selection
|
|
28618
|
+
// ========================================
|
|
28619
|
+
if (phase === 'awaiting_options') {
|
|
28620
|
+
const selectedContact = state.bookableContacts.find((c) => c.id === state.selectedContactId);
|
|
28621
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-booking-card", children: [jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__header", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__icon", children: "\uD83D\uDCC5" }), jsxRuntime.jsxs("span", { className: "ai-chat-booking-card__title", children: ["Book with ", selectedContact?.name] })] }), jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__content", children: [state.errorMessage && (jsxRuntime.jsx("p", { className: "ai-chat-booking-card__error", children: state.errorMessage })), jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__options", children: [jsxRuntime.jsxs("button", { className: "ai-chat-booking-card__option-btn", onClick: () => handleSubmit({ selectedOption: 'book_new' }), children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__option-icon", children: "\u2795" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__option-text", children: "Book New Appointment" })] }), state.userAppointments.length > 0 && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("button", { className: "ai-chat-booking-card__option-btn", onClick: () => handleSubmit({ selectedOption: 'view_existing' }), children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__option-icon", children: "\uD83D\uDCCB" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__option-text", children: "View Appointments" })] }), jsxRuntime.jsxs("button", { className: "ai-chat-booking-card__option-btn", onClick: () => handleSubmit({ selectedOption: 'cancel_existing' }), children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__option-icon", children: "\u2715" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__option-text", children: "Cancel Appointment" })] })] }))] })] })] }));
|
|
28622
|
+
}
|
|
28623
|
+
// ========================================
|
|
28624
|
+
// Phase: View Existing Appointments
|
|
28625
|
+
// ========================================
|
|
28626
|
+
if (state.phase === 'awaiting_options' && state.selectedOption === 'view_existing') {
|
|
28627
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-booking-card", children: [jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__header", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__icon", children: "\uD83D\uDCCB" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__title", children: "Your Appointments" })] }), jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__content", children: [state.userAppointments.length === 0 ? (jsxRuntime.jsx("p", { className: "ai-chat-booking-card__empty", children: "You have no appointments yet." })) : (jsxRuntime.jsx("div", { className: "ai-chat-booking-card__appointments", children: state.userAppointments.map((apt) => (jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__appointment", children: [jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__appointment-header", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__appointment-subject", children: apt.subject }), jsxRuntime.jsx("span", { className: `ai-chat-booking-card__appointment-status ai-chat-booking-card__appointment-status--${apt.status}`, children: apt.status })] }), jsxRuntime.jsx("div", { className: "ai-chat-booking-card__appointment-time", children: apt.displayTime }), jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__appointment-contact", children: ["with ", apt.contactName] }), apt.teamsLink && (jsxRuntime.jsx("a", { href: apt.teamsLink, target: "_blank", rel: "noopener noreferrer", className: "ai-chat-booking-card__link", style: { color: accentColor }, children: "Join Teams Meeting \u2192" }))] }, apt.id))) })), jsxRuntime.jsx("button", { className: "ai-chat-booking-card__btn ai-chat-booking-card__btn--secondary", onClick: () => handleSubmit({ selectedOption: undefined }), children: "Back" })] })] }));
|
|
28628
|
+
}
|
|
28629
|
+
// ========================================
|
|
28630
|
+
// Phase: Cancel Appointment
|
|
28631
|
+
// ========================================
|
|
28632
|
+
if (state.phase === 'awaiting_options' && state.selectedOption === 'cancel_existing') {
|
|
28633
|
+
const activeAppointments = state.userAppointments.filter((a) => a.status !== 'cancelled' && a.status !== 'declined');
|
|
28634
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-booking-card", children: [jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__header", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__icon", children: "\u2715" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__title", children: "Cancel Appointment" })] }), jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__content", children: [state.errorMessage && (jsxRuntime.jsx("p", { className: "ai-chat-booking-card__error", children: state.errorMessage })), activeAppointments.length === 0 ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("p", { className: "ai-chat-booking-card__empty", children: "No active appointments to cancel." }), jsxRuntime.jsx("button", { className: "ai-chat-booking-card__btn ai-chat-booking-card__btn--secondary", onClick: () => handleSubmit({ selectedOption: undefined }), children: "Back" })] })) : (jsxRuntime.jsx("div", { className: "ai-chat-booking-card__appointments", children: activeAppointments.map((apt) => (jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__appointment", children: [jsxRuntime.jsx("div", { className: "ai-chat-booking-card__appointment-header", children: jsxRuntime.jsx("span", { className: "ai-chat-booking-card__appointment-subject", children: apt.subject }) }), jsxRuntime.jsx("div", { className: "ai-chat-booking-card__appointment-time", children: apt.displayTime }), jsxRuntime.jsx("button", { className: "ai-chat-booking-card__btn ai-chat-booking-card__btn--danger", onClick: () => handleSubmit({ selectedAppointmentId: apt.id, confirmCancel: true }), children: "Cancel This Appointment" })] }, apt.id))) }))] })] }));
|
|
28635
|
+
}
|
|
28636
|
+
// ========================================
|
|
28637
|
+
// Phase: Slot Selection
|
|
28638
|
+
// ========================================
|
|
28639
|
+
if (phase === 'awaiting_slot_selection') {
|
|
28640
|
+
const groupedSlots = state.availableSlots.reduce((acc, slot) => {
|
|
28641
|
+
if (!acc[slot.displayDate]) {
|
|
28642
|
+
acc[slot.displayDate] = [];
|
|
28643
|
+
}
|
|
28644
|
+
acc[slot.displayDate].push(slot);
|
|
28645
|
+
return acc;
|
|
28646
|
+
}, {});
|
|
28647
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-booking-card", children: [jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__header", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__icon", children: "\uD83D\uDD50" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__title", children: "Select a Time Slot" })] }), jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__content", children: [jsxRuntime.jsxs("p", { className: "ai-chat-booking-card__description", children: ["Available times in ", state.timeZone] }), state.errorMessage && (jsxRuntime.jsx("p", { className: "ai-chat-booking-card__error", children: state.errorMessage })), Object.entries(groupedSlots).map(([date, slots]) => (jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__date-group", children: [jsxRuntime.jsx("div", { className: "ai-chat-booking-card__date-header", children: date }), jsxRuntime.jsx("div", { className: "ai-chat-booking-card__slots", children: slots.map((slot, idx) => {
|
|
28648
|
+
const isSelected = state.selectedSlot?.startTime === slot.startTime &&
|
|
28649
|
+
state.selectedSlot?.endTime === slot.endTime;
|
|
28650
|
+
return (jsxRuntime.jsx("button", { className: `ai-chat-booking-card__slot ${isSelected ? 'ai-chat-booking-card__slot--selected' : ''}`, onClick: () => handleSubmit({
|
|
28651
|
+
selectedSlot: { startTime: slot.startTime, endTime: slot.endTime },
|
|
28652
|
+
}), style: {
|
|
28653
|
+
borderColor: isSelected ? accentColor || undefined : undefined,
|
|
28654
|
+
backgroundColor: isSelected ? `${accentColor || '#3b82f6'}15` : undefined,
|
|
28655
|
+
}, children: slot.displayTime }, `${slot.startTime}-${idx}`));
|
|
28656
|
+
}) })] }, date))), state.availableSlots.length === 0 && (jsxRuntime.jsx("p", { className: "ai-chat-booking-card__empty", children: "No available time slots." }))] })] }));
|
|
28657
|
+
}
|
|
28658
|
+
// ========================================
|
|
28659
|
+
// Phase: Confirmation
|
|
28660
|
+
// ========================================
|
|
28661
|
+
if (phase === 'awaiting_confirmation') {
|
|
28662
|
+
const selectedContact = state.bookableContacts.find((c) => c.id === state.selectedContactId);
|
|
28663
|
+
const selectedSlot = state.availableSlots.find((s) => s.startTime === state.selectedSlot?.startTime && s.endTime === state.selectedSlot?.endTime);
|
|
28664
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-booking-card", children: [jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__header", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__icon", children: "\u2713" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__title", children: "Confirm Booking" })] }), jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__content", children: [state.errorMessage && (jsxRuntime.jsx("p", { className: "ai-chat-booking-card__error", children: state.errorMessage })), jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__summary", children: [jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__summary-row", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__summary-label", children: "Contact:" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__summary-value", children: selectedContact?.name })] }), jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__summary-row", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__summary-label", children: "Date:" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__summary-value", children: selectedSlot?.displayDate })] }), jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__summary-row", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__summary-label", children: "Time:" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__summary-value", children: selectedSlot?.displayTime })] })] }), state.allowCustomSubject && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("label", { className: "ai-chat-booking-card__label", children: "Subject (optional):" }), jsxRuntime.jsx("input", { type: "text", className: "ai-chat-booking-card__input", placeholder: "Meeting subject", value: subjectInput, onChange: (e) => setSubjectInput(e.target.value) })] })), jsxRuntime.jsx("button", { className: "ai-chat-booking-card__btn ai-chat-booking-card__btn--primary", onClick: () => handleSubmit({
|
|
28665
|
+
subject: state.allowCustomSubject ? subjectInput || undefined : undefined,
|
|
28666
|
+
confirmed: true,
|
|
28667
|
+
}), style: {
|
|
28668
|
+
backgroundColor: accentColor || undefined,
|
|
28669
|
+
borderColor: accentColor || undefined,
|
|
28670
|
+
}, children: "Confirm Booking" })] })] }));
|
|
28671
|
+
}
|
|
28672
|
+
// Fallback for unknown states
|
|
28673
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-booking-card", children: [jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__header", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__icon", children: "\uD83D\uDCC5" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__title", children: "Booking" })] }), jsxRuntime.jsx("div", { className: "ai-chat-booking-card__content", children: jsxRuntime.jsx("p", { className: "ai-chat-booking-card__description", children: "Loading booking options..." }) })] }));
|
|
28674
|
+
}
|
|
28675
|
+
|
|
28676
|
+
const pendingResolvers = new Map();
|
|
28677
|
+
const resumeCallbacks = new Map();
|
|
28678
|
+
const frontendActionHandlers = {};
|
|
28679
|
+
const actionRenderers = {};
|
|
28680
|
+
function getFrontendActionHandler(implementation) {
|
|
28681
|
+
return frontendActionHandlers[implementation];
|
|
28682
|
+
}
|
|
28683
|
+
function getActionRenderer(implementation) {
|
|
28684
|
+
return actionRenderers[implementation];
|
|
28685
|
+
}
|
|
28686
|
+
function waitForActionState(toolCallId) {
|
|
28687
|
+
return new Promise((resolve) => {
|
|
28688
|
+
pendingResolvers.set(toolCallId, resolve);
|
|
28689
|
+
});
|
|
28690
|
+
}
|
|
28691
|
+
function resolveActionState(toolCallId, state) {
|
|
28692
|
+
const resolver = pendingResolvers.get(toolCallId);
|
|
28693
|
+
if (resolver) {
|
|
28694
|
+
pendingResolvers.delete(toolCallId);
|
|
28695
|
+
resolver(state);
|
|
28696
|
+
return;
|
|
28697
|
+
}
|
|
28698
|
+
const resumeCallback = resumeCallbacks.get(toolCallId);
|
|
27774
28699
|
if (resumeCallback) {
|
|
27775
28700
|
resumeCallback(state).catch((error) => {
|
|
27776
28701
|
console.error("[Action] Failed to resume action:", error);
|
|
@@ -27798,13 +28723,19 @@ function registerGoogleCalendarAction() {
|
|
|
27798
28723
|
// Register the handler
|
|
27799
28724
|
registerGoogleCalendarHandler();
|
|
27800
28725
|
// Register the renderer
|
|
27801
|
-
actionRenderers["google-calendar-appointment"] = (message) => {
|
|
28726
|
+
actionRenderers["google-calendar-appointment"] = (message, accentColor, _variant, onActionDismiss) => {
|
|
27802
28727
|
const action = message.action;
|
|
27803
28728
|
if (!action)
|
|
27804
28729
|
return null;
|
|
27805
28730
|
const handleComplete = (toolCallId, newState) => {
|
|
27806
28731
|
resolveActionState(toolCallId, newState);
|
|
27807
28732
|
};
|
|
28733
|
+
const handleDismiss = onActionDismiss
|
|
28734
|
+
? (toolCallId) => {
|
|
28735
|
+
resolveActionState(toolCallId, { __dismissed: true });
|
|
28736
|
+
onActionDismiss(toolCallId);
|
|
28737
|
+
}
|
|
28738
|
+
: undefined;
|
|
27808
28739
|
return (jsxRuntime.jsx(GoogleCalendarCard, { action: {
|
|
27809
28740
|
implementation: action.implementation,
|
|
27810
28741
|
toolCallId: action.toolCallId,
|
|
@@ -27812,7 +28743,45 @@ function registerGoogleCalendarAction() {
|
|
|
27812
28743
|
input: action.input,
|
|
27813
28744
|
state: action.state,
|
|
27814
28745
|
done: action.done ?? false,
|
|
27815
|
-
}, onComplete: handleComplete }));
|
|
28746
|
+
}, onComplete: handleComplete, onDismiss: handleDismiss, accentColor: accentColor }));
|
|
28747
|
+
};
|
|
28748
|
+
}
|
|
28749
|
+
|
|
28750
|
+
function registerMicrosoftCalendarHandler() {
|
|
28751
|
+
frontendActionHandlers["microsoft-calendar-appointment"] = async (_input, _state, context) => {
|
|
28752
|
+
return waitForActionState(context.toolCallId);
|
|
28753
|
+
};
|
|
28754
|
+
}
|
|
28755
|
+
|
|
28756
|
+
/**
|
|
28757
|
+
* Register microsoft-calendar-appointment action handler and renderer.
|
|
28758
|
+
* Called by initializeActionHandlers to prevent tree-shaking.
|
|
28759
|
+
*/
|
|
28760
|
+
function registerMicrosoftCalendarAction() {
|
|
28761
|
+
// Register the handler
|
|
28762
|
+
registerMicrosoftCalendarHandler();
|
|
28763
|
+
// Register the renderer
|
|
28764
|
+
actionRenderers["microsoft-calendar-appointment"] = (message, accentColor, _variant, onActionDismiss) => {
|
|
28765
|
+
const action = message.action;
|
|
28766
|
+
if (!action)
|
|
28767
|
+
return null;
|
|
28768
|
+
const handleComplete = (toolCallId, newState) => {
|
|
28769
|
+
resolveActionState(toolCallId, newState);
|
|
28770
|
+
};
|
|
28771
|
+
const handleDismiss = onActionDismiss
|
|
28772
|
+
? (toolCallId) => {
|
|
28773
|
+
resolveActionState(toolCallId, { __dismissed: true });
|
|
28774
|
+
onActionDismiss(toolCallId);
|
|
28775
|
+
}
|
|
28776
|
+
: undefined;
|
|
28777
|
+
return (jsxRuntime.jsx(MicrosoftCalendarCard, { action: {
|
|
28778
|
+
implementation: action.implementation,
|
|
28779
|
+
toolCallId: action.toolCallId,
|
|
28780
|
+
actionId: action.actionId,
|
|
28781
|
+
input: action.input,
|
|
28782
|
+
state: action.state,
|
|
28783
|
+
done: action.done ?? false,
|
|
28784
|
+
}, onComplete: handleComplete, onDismiss: handleDismiss, accentColor: accentColor }));
|
|
27816
28785
|
};
|
|
27817
28786
|
}
|
|
27818
28787
|
|
|
@@ -27826,7 +28795,7 @@ function registerLinkPreviewAction() {
|
|
|
27826
28795
|
return { ...state, status: "displaying" };
|
|
27827
28796
|
};
|
|
27828
28797
|
// Renderer - displays the link preview card
|
|
27829
|
-
actionRenderers["link-preview"] = (message) => {
|
|
28798
|
+
actionRenderers["link-preview"] = (message, accentColor) => {
|
|
27830
28799
|
const action = message.action;
|
|
27831
28800
|
if (!action)
|
|
27832
28801
|
return null;
|
|
@@ -27844,7 +28813,7 @@ function registerLinkPreviewAction() {
|
|
|
27844
28813
|
input: action.input,
|
|
27845
28814
|
state: action.state,
|
|
27846
28815
|
done: isDone,
|
|
27847
|
-
}, onComplete: handleComplete }));
|
|
28816
|
+
}, onComplete: handleComplete, accentColor: accentColor }));
|
|
27848
28817
|
};
|
|
27849
28818
|
}
|
|
27850
28819
|
|
|
@@ -27858,7 +28827,7 @@ function registerVideoPlayerAction() {
|
|
|
27858
28827
|
return { ...state, status: "displaying" };
|
|
27859
28828
|
};
|
|
27860
28829
|
// Renderer - displays the embedded video player card
|
|
27861
|
-
actionRenderers["video-player"] = (message) => {
|
|
28830
|
+
actionRenderers["video-player"] = (message, accentColor) => {
|
|
27862
28831
|
const action = message.action;
|
|
27863
28832
|
if (!action)
|
|
27864
28833
|
return null;
|
|
@@ -27876,7 +28845,7 @@ function registerVideoPlayerAction() {
|
|
|
27876
28845
|
input: action.input,
|
|
27877
28846
|
state: action.state,
|
|
27878
28847
|
done: isDone,
|
|
27879
|
-
}, onComplete: handleComplete }));
|
|
28848
|
+
}, onComplete: handleComplete, accentColor: accentColor }));
|
|
27880
28849
|
};
|
|
27881
28850
|
}
|
|
27882
28851
|
|
|
@@ -27890,7 +28859,7 @@ function registerLocationCardAction() {
|
|
|
27890
28859
|
return { ...state, status: "displaying" };
|
|
27891
28860
|
};
|
|
27892
28861
|
// Renderer - displays the location card
|
|
27893
|
-
actionRenderers["location-card"] = (message, accentColor) => {
|
|
28862
|
+
actionRenderers["location-card"] = (message, accentColor, variant) => {
|
|
27894
28863
|
const action = message.action;
|
|
27895
28864
|
if (!action)
|
|
27896
28865
|
return null;
|
|
@@ -27908,7 +28877,134 @@ function registerLocationCardAction() {
|
|
|
27908
28877
|
input: action.input,
|
|
27909
28878
|
state: action.state,
|
|
27910
28879
|
done: isDone,
|
|
27911
|
-
}, onComplete: handleComplete, accentColor: accentColor, maxColumns: 1 }));
|
|
28880
|
+
}, onComplete: handleComplete, accentColor: accentColor, maxColumns: variant === 'fullpage' ? 3 : 1 }));
|
|
28881
|
+
};
|
|
28882
|
+
}
|
|
28883
|
+
|
|
28884
|
+
function registerContactCardAction() {
|
|
28885
|
+
// Handler - auto-completes immediately since no user input is needed
|
|
28886
|
+
frontendActionHandlers['contact-card'] = async (_input, state, _context) => {
|
|
28887
|
+
return { ...state, status: 'displaying' };
|
|
28888
|
+
};
|
|
28889
|
+
// Renderer - displays the contact card
|
|
28890
|
+
actionRenderers['contact-card'] = (message, accentColor, variant) => {
|
|
28891
|
+
const action = message.action;
|
|
28892
|
+
if (!action)
|
|
28893
|
+
return null;
|
|
28894
|
+
const handleComplete = (toolCallId, newState) => {
|
|
28895
|
+
resolveActionState(toolCallId, newState);
|
|
28896
|
+
};
|
|
28897
|
+
// Check if action state indicates it's already complete
|
|
28898
|
+
const state = action.state;
|
|
28899
|
+
const status = state?.status;
|
|
28900
|
+
const isDone = action.done || status === 'displaying' || status === 'contacted';
|
|
28901
|
+
return (jsxRuntime.jsx(ContactCard, { action: {
|
|
28902
|
+
implementation: action.implementation,
|
|
28903
|
+
toolCallId: action.toolCallId,
|
|
28904
|
+
actionId: action.actionId,
|
|
28905
|
+
input: action.input,
|
|
28906
|
+
state: action.state,
|
|
28907
|
+
done: isDone,
|
|
28908
|
+
}, onComplete: handleComplete, accentColor: accentColor, maxColumns: variant === 'fullpage' ? 3 : 1 }));
|
|
28909
|
+
};
|
|
28910
|
+
}
|
|
28911
|
+
|
|
28912
|
+
function registerQueryContactDirectoryAction() {
|
|
28913
|
+
// Handler - auto-completes immediately since no user input is needed
|
|
28914
|
+
frontendActionHandlers['query-contact-directory'] = async (_input, state, _context) => {
|
|
28915
|
+
return { ...state, status: 'displaying' };
|
|
28916
|
+
};
|
|
28917
|
+
// Renderer - displays the contact card with search results
|
|
28918
|
+
actionRenderers['query-contact-directory'] = (message, accentColor, variant) => {
|
|
28919
|
+
const action = message.action;
|
|
28920
|
+
if (!action)
|
|
28921
|
+
return null;
|
|
28922
|
+
// Handle completion - triggers agent to continue with text response
|
|
28923
|
+
const handleComplete = (toolCallId, newState) => {
|
|
28924
|
+
resolveActionState(toolCallId, newState);
|
|
28925
|
+
};
|
|
28926
|
+
// Check if action state indicates it's already complete
|
|
28927
|
+
const state = action.state;
|
|
28928
|
+
const status = state?.status;
|
|
28929
|
+
const isDone = action.done || status === 'displaying' || status === 'contacted';
|
|
28930
|
+
return (jsxRuntime.jsx(ContactCard, { action: {
|
|
28931
|
+
implementation: action.implementation,
|
|
28932
|
+
toolCallId: action.toolCallId,
|
|
28933
|
+
actionId: action.actionId,
|
|
28934
|
+
input: action.input,
|
|
28935
|
+
state: action.state,
|
|
28936
|
+
done: isDone,
|
|
28937
|
+
}, onComplete: handleComplete, accentColor: accentColor, maxColumns: variant === 'fullpage' ? 3 : 1 }));
|
|
28938
|
+
};
|
|
28939
|
+
}
|
|
28940
|
+
|
|
28941
|
+
function registerDisplayFormAction() {
|
|
28942
|
+
// Handler - handles form submission and state updates
|
|
28943
|
+
frontendActionHandlers['display-form'] = async (_input, _state, context) => {
|
|
28944
|
+
return waitForActionState(context.toolCallId);
|
|
28945
|
+
};
|
|
28946
|
+
// Renderer - displays the form card
|
|
28947
|
+
actionRenderers['display-form'] = (message, accentColor, variant, onActionDismiss) => {
|
|
28948
|
+
const action = message.action;
|
|
28949
|
+
if (!action)
|
|
28950
|
+
return null;
|
|
28951
|
+
const handleComplete = (toolCallId, newState) => {
|
|
28952
|
+
resolveActionState(toolCallId, newState);
|
|
28953
|
+
};
|
|
28954
|
+
const handleDismiss = onActionDismiss
|
|
28955
|
+
? (toolCallId) => {
|
|
28956
|
+
resolveActionState(toolCallId, { __dismissed: true });
|
|
28957
|
+
onActionDismiss(toolCallId);
|
|
28958
|
+
}
|
|
28959
|
+
: undefined;
|
|
28960
|
+
// Check if action state indicates it's already complete
|
|
28961
|
+
const state = action.state;
|
|
28962
|
+
const status = state?.status;
|
|
28963
|
+
const isDone = action.done || status === 'completed' || status === 'submitted';
|
|
28964
|
+
return (jsxRuntime.jsx(FormCard, { action: {
|
|
28965
|
+
implementation: action.implementation,
|
|
28966
|
+
toolCallId: action.toolCallId,
|
|
28967
|
+
actionId: action.actionId,
|
|
28968
|
+
input: action.input,
|
|
28969
|
+
state: action.state,
|
|
28970
|
+
done: isDone,
|
|
28971
|
+
}, onComplete: handleComplete, onDismiss: handleDismiss, accentColor: accentColor }));
|
|
28972
|
+
};
|
|
28973
|
+
}
|
|
28974
|
+
|
|
28975
|
+
/**
|
|
28976
|
+
* Book Contact Appointment Handler
|
|
28977
|
+
* Frontend action handler that waits for action completion
|
|
28978
|
+
*/
|
|
28979
|
+
function registerBookContactAppointmentHandler() {
|
|
28980
|
+
frontendActionHandlers["book-contact-appointment"] = async (_input, _state, context) => {
|
|
28981
|
+
return waitForActionState(context.toolCallId);
|
|
28982
|
+
};
|
|
28983
|
+
}
|
|
28984
|
+
|
|
28985
|
+
/**
|
|
28986
|
+
* Register book-contact-appointment action handler and renderer.
|
|
28987
|
+
* Called by initializeActionHandlers to prevent tree-shaking.
|
|
28988
|
+
*/
|
|
28989
|
+
function registerBookContactAppointmentAction() {
|
|
28990
|
+
// Register the handler
|
|
28991
|
+
registerBookContactAppointmentHandler();
|
|
28992
|
+
// Register the renderer
|
|
28993
|
+
actionRenderers['book-contact-appointment'] = (message, accentColor) => {
|
|
28994
|
+
const action = message.action;
|
|
28995
|
+
if (!action)
|
|
28996
|
+
return null;
|
|
28997
|
+
const handleComplete = (toolCallId, newState) => {
|
|
28998
|
+
resolveActionState(toolCallId, newState);
|
|
28999
|
+
};
|
|
29000
|
+
return (jsxRuntime.jsx(BookContactAppointmentCard, { action: {
|
|
29001
|
+
implementation: action.implementation,
|
|
29002
|
+
toolCallId: action.toolCallId,
|
|
29003
|
+
actionId: action.actionId,
|
|
29004
|
+
input: action.input,
|
|
29005
|
+
state: action.state,
|
|
29006
|
+
done: action.done ?? false,
|
|
29007
|
+
}, onComplete: handleComplete, accentColor: accentColor }));
|
|
27912
29008
|
};
|
|
27913
29009
|
}
|
|
27914
29010
|
|
|
@@ -27925,9 +29021,14 @@ function initializeActionHandlers() {
|
|
|
27925
29021
|
initialized = true;
|
|
27926
29022
|
// Explicitly call each registration function to prevent tree-shaking
|
|
27927
29023
|
registerGoogleCalendarAction();
|
|
29024
|
+
registerMicrosoftCalendarAction();
|
|
27928
29025
|
registerLinkPreviewAction();
|
|
27929
29026
|
registerVideoPlayerAction();
|
|
27930
29027
|
registerLocationCardAction();
|
|
29028
|
+
registerQueryContactDirectoryAction();
|
|
29029
|
+
registerContactCardAction();
|
|
29030
|
+
registerDisplayFormAction();
|
|
29031
|
+
registerBookContactAppointmentAction();
|
|
27931
29032
|
}
|
|
27932
29033
|
|
|
27933
29034
|
/**
|
|
@@ -28147,12 +29248,6 @@ function isStorageAvailable() {
|
|
|
28147
29248
|
}
|
|
28148
29249
|
}
|
|
28149
29250
|
|
|
28150
|
-
/**
|
|
28151
|
-
* useChat Hook
|
|
28152
|
-
* Main state management for chat functionality
|
|
28153
|
-
*/
|
|
28154
|
-
// Initialize action handlers immediately to prevent tree-shaking
|
|
28155
|
-
initializeActionHandlers();
|
|
28156
29251
|
function hydrateToolNames(messages) {
|
|
28157
29252
|
const toolCallNameById = new Map();
|
|
28158
29253
|
for (const entry of messages) {
|
|
@@ -28196,118 +29291,78 @@ function hydrateToolNames(messages) {
|
|
|
28196
29291
|
};
|
|
28197
29292
|
});
|
|
28198
29293
|
}
|
|
28199
|
-
function
|
|
28200
|
-
|
|
28201
|
-
|
|
28202
|
-
return entry;
|
|
28203
|
-
}
|
|
28204
|
-
const content = typeof entry.message.content === "string" ? entry.message.content : "";
|
|
28205
|
-
if (content.trim().length > 0) {
|
|
28206
|
-
return entry;
|
|
28207
|
-
}
|
|
28208
|
-
return {
|
|
28209
|
-
...entry,
|
|
28210
|
-
message: { ...entry.message, content: getActionPrompt(entry.action.implementation) },
|
|
28211
|
-
};
|
|
28212
|
-
});
|
|
29294
|
+
function hydrateMessages(messages) {
|
|
29295
|
+
const visibleMessages = messages.filter((message) => !message.action?.hidden);
|
|
29296
|
+
return hydrateToolNames(visibleMessages);
|
|
28213
29297
|
}
|
|
28214
|
-
|
|
28215
|
-
|
|
28216
|
-
|
|
28217
|
-
|
|
28218
|
-
|
|
28219
|
-
|
|
28220
|
-
|
|
28221
|
-
|
|
29298
|
+
|
|
29299
|
+
function deriveErrorInfo(error) {
|
|
29300
|
+
if (error instanceof ApiError) {
|
|
29301
|
+
const retryAfterSeconds = typeof error.retryAfterMs === 'number'
|
|
29302
|
+
? Math.max(1, Math.ceil(error.retryAfterMs / 1000))
|
|
29303
|
+
: undefined;
|
|
29304
|
+
const lowerMessage = (error.message || '').toLowerCase();
|
|
29305
|
+
let message;
|
|
29306
|
+
switch (error.status) {
|
|
29307
|
+
case 429: {
|
|
29308
|
+
const isPerUser = lowerMessage.includes('user');
|
|
29309
|
+
const base = isPerUser
|
|
29310
|
+
? 'You have reached the per-user rate limit.'
|
|
29311
|
+
: 'This widget has received too many requests.';
|
|
29312
|
+
if (retryAfterSeconds) {
|
|
29313
|
+
message = `${base} Please wait ${retryAfterSeconds} second${retryAfterSeconds === 1 ? '' : 's'} before trying again.`;
|
|
29314
|
+
}
|
|
29315
|
+
else {
|
|
29316
|
+
message = `${base} Please wait a moment and try again.`;
|
|
29317
|
+
}
|
|
29318
|
+
break;
|
|
29319
|
+
}
|
|
29320
|
+
case 401:
|
|
29321
|
+
message = 'Authentication failed. Please refresh the page or verify your API key.';
|
|
29322
|
+
break;
|
|
29323
|
+
case 403:
|
|
29324
|
+
message = 'Access to this widget is restricted. Please contact the site owner if you believe this is an error.';
|
|
29325
|
+
break;
|
|
29326
|
+
case 404:
|
|
29327
|
+
if (lowerMessage.includes('not active')) {
|
|
29328
|
+
message = 'This widget is currently inactive. Please contact the site owner.';
|
|
29329
|
+
}
|
|
29330
|
+
else {
|
|
29331
|
+
message = 'We could not find this widget. It may have been removed.';
|
|
29332
|
+
}
|
|
29333
|
+
break;
|
|
29334
|
+
default:
|
|
29335
|
+
if (error.status >= 500) {
|
|
29336
|
+
message = 'The server encountered an error. Please try again shortly.';
|
|
29337
|
+
}
|
|
29338
|
+
else if (error.status > 0) {
|
|
29339
|
+
message = error.message || 'Something went wrong. Please try again.';
|
|
29340
|
+
}
|
|
29341
|
+
else {
|
|
29342
|
+
message = error.message || 'Unable to connect to the server. Please check your internet connection.';
|
|
29343
|
+
}
|
|
28222
29344
|
}
|
|
28223
|
-
|
|
28224
|
-
|
|
28225
|
-
|
|
28226
|
-
|
|
29345
|
+
return { message, retryAfterSeconds, status: error.status };
|
|
29346
|
+
}
|
|
29347
|
+
if (error instanceof Error) {
|
|
29348
|
+
const lower = error.message.toLowerCase();
|
|
29349
|
+
if (lower.includes('network')) {
|
|
29350
|
+
return { message: 'Unable to connect to the server. Please check your internet connection.' };
|
|
28227
29351
|
}
|
|
28228
|
-
|
|
28229
|
-
|
|
28230
|
-
const status = state.status;
|
|
28231
|
-
if (status === "displaying" || status === "clicked") {
|
|
28232
|
-
return {
|
|
28233
|
-
...entry,
|
|
28234
|
-
action: { ...entry.action, done: true },
|
|
28235
|
-
};
|
|
28236
|
-
}
|
|
29352
|
+
if (lower.includes('timeout')) {
|
|
29353
|
+
return { message: 'The request timed out. Please try again.' };
|
|
28237
29354
|
}
|
|
28238
|
-
|
|
28239
|
-
|
|
28240
|
-
const status = state.status;
|
|
28241
|
-
if (status === "booked" || status === "cancelled" || status === "error") {
|
|
28242
|
-
return {
|
|
28243
|
-
...entry,
|
|
28244
|
-
action: { ...entry.action, done: true },
|
|
28245
|
-
};
|
|
28246
|
-
}
|
|
29355
|
+
if (lower.includes('unauthorized') || lower.includes('401')) {
|
|
29356
|
+
return { message: 'Authentication failed. Please refresh the page or verify your API key.' };
|
|
28247
29357
|
}
|
|
28248
|
-
|
|
28249
|
-
|
|
28250
|
-
}
|
|
28251
|
-
function hydrateMessages(messages) {
|
|
28252
|
-
return hydrateActionDoneStatus(hydrateActionContent(hydrateToolNames(messages)));
|
|
28253
|
-
}
|
|
28254
|
-
function setupActionResumeCallbacks(messages, client, conversationId, setState, onMessageUpdate) {
|
|
28255
|
-
// Find all incomplete actions and register resume callbacks
|
|
28256
|
-
for (const message of messages) {
|
|
28257
|
-
if (message.action && !message.action.done) {
|
|
28258
|
-
const toolCallId = message.action.toolCallId;
|
|
28259
|
-
const toolName = message.message.name || message.toolExecuting || "tool";
|
|
28260
|
-
registerActionResumeCallback(toolCallId, async (newState) => {
|
|
28261
|
-
// When user interacts with the action after reload, continue the stream
|
|
28262
|
-
try {
|
|
28263
|
-
// Update the action message with the new state
|
|
28264
|
-
setState(prev => ({
|
|
28265
|
-
...prev,
|
|
28266
|
-
messages: prev.messages.map(m => m.action?.toolCallId === toolCallId
|
|
28267
|
-
? {
|
|
28268
|
-
...m,
|
|
28269
|
-
action: m.action ? { ...m.action, state: newState } : undefined,
|
|
28270
|
-
}
|
|
28271
|
-
: m),
|
|
28272
|
-
isTyping: true,
|
|
28273
|
-
}));
|
|
28274
|
-
const streamState = createStreamState();
|
|
28275
|
-
// Continue the agent stream with the new state
|
|
28276
|
-
for await (const event of client.continueAgentMessageStream(conversationId, toolCallId, newState)) {
|
|
28277
|
-
if (event.type === "done") {
|
|
28278
|
-
finalizeToolMessage(streamState, setState, toolCallId, toolName);
|
|
28279
|
-
streamState.sources = event.sources;
|
|
28280
|
-
streamState.toolCallToActionId = event.tool_call_to_action_id;
|
|
28281
|
-
finalizeStreamMessages(setState, streamState.newMessageIds, event.sources, event.tool_call_to_action_id, event.suggestions);
|
|
28282
|
-
continue;
|
|
28283
|
-
}
|
|
28284
|
-
if (event.type === "error") {
|
|
28285
|
-
const errorMessage = {
|
|
28286
|
-
id: generateMessageId(),
|
|
28287
|
-
message: {
|
|
28288
|
-
role: "assistant",
|
|
28289
|
-
content: "Sorry, an error occurred. Please try again later.",
|
|
28290
|
-
},
|
|
28291
|
-
timestamp: new Date().toISOString(),
|
|
28292
|
-
sources: [],
|
|
28293
|
-
isError: true,
|
|
28294
|
-
};
|
|
28295
|
-
upsertMessage(setState, errorMessage, false);
|
|
28296
|
-
setState(prev => ({ ...prev, isTyping: false }));
|
|
28297
|
-
return;
|
|
28298
|
-
}
|
|
28299
|
-
handleStreamEvent(event, streamState, onMessageUpdate, setState);
|
|
28300
|
-
}
|
|
28301
|
-
setState(prev => ({ ...prev, isTyping: false }));
|
|
28302
|
-
}
|
|
28303
|
-
catch (error) {
|
|
28304
|
-
console.error("[Action Resume] Failed to continue stream:", error);
|
|
28305
|
-
setState(prev => ({ ...prev, isTyping: false }));
|
|
28306
|
-
}
|
|
28307
|
-
});
|
|
29358
|
+
if (lower.includes('internal server error') || lower.includes('500')) {
|
|
29359
|
+
return { message: 'The server encountered an error. Please try again shortly.' };
|
|
28308
29360
|
}
|
|
29361
|
+
return { message: error.message || 'Something went wrong. Please try again.' };
|
|
28309
29362
|
}
|
|
29363
|
+
return { message: 'Something went wrong. Please try again.' };
|
|
28310
29364
|
}
|
|
29365
|
+
|
|
28311
29366
|
function createStreamState() {
|
|
28312
29367
|
return {
|
|
28313
29368
|
currentContent: "",
|
|
@@ -28316,6 +29371,7 @@ function createStreamState() {
|
|
|
28316
29371
|
newMessageIds: new Set(),
|
|
28317
29372
|
sources: [],
|
|
28318
29373
|
toolCallToActionId: {},
|
|
29374
|
+
requestId: generateMessageId(),
|
|
28319
29375
|
};
|
|
28320
29376
|
}
|
|
28321
29377
|
function upsertMessage(setState, message, isTyping) {
|
|
@@ -28351,15 +29407,40 @@ function finalizeStreamMessages(setState, messageIds, sources, toolCallToActionI
|
|
|
28351
29407
|
return msg;
|
|
28352
29408
|
}
|
|
28353
29409
|
// Attach suggestions only to the last assistant message
|
|
28354
|
-
|
|
28355
|
-
|
|
28356
|
-
|
|
28357
|
-
return { ...msg, sources, toolCallToActionId };
|
|
29410
|
+
const withSuggestions = index === lastAssistantIndex && suggestions && suggestions.length > 0
|
|
29411
|
+
? { suggestions }
|
|
29412
|
+
: {};
|
|
29413
|
+
return { ...msg, sources, toolCallToActionId, ...withSuggestions };
|
|
28358
29414
|
}),
|
|
28359
29415
|
isTyping: false,
|
|
28360
29416
|
};
|
|
28361
29417
|
});
|
|
28362
29418
|
}
|
|
29419
|
+
function finalizeToolMessage(streamState, setState, toolCallId, toolName) {
|
|
29420
|
+
setState(prev => {
|
|
29421
|
+
const messages = prev.messages.map((entry) => {
|
|
29422
|
+
const matchesToolCall = entry.message.role === "tool" && entry.message.tool_call_id === toolCallId;
|
|
29423
|
+
if (!matchesToolCall) {
|
|
29424
|
+
return entry;
|
|
29425
|
+
}
|
|
29426
|
+
const existingName = entry.message.name || toolName;
|
|
29427
|
+
return {
|
|
29428
|
+
...entry,
|
|
29429
|
+
message: {
|
|
29430
|
+
role: "tool",
|
|
29431
|
+
content: typeof entry.message.content === "string" ? entry.message.content : "",
|
|
29432
|
+
tool_call_id: toolCallId,
|
|
29433
|
+
name: existingName,
|
|
29434
|
+
},
|
|
29435
|
+
isStreaming: false,
|
|
29436
|
+
toolExecuting: existingName,
|
|
29437
|
+
};
|
|
29438
|
+
});
|
|
29439
|
+
return { ...prev, messages, isTyping: false, isLoading: false };
|
|
29440
|
+
});
|
|
29441
|
+
streamState.activeToolCallCount = Math.max(0, streamState.activeToolCallCount - 1);
|
|
29442
|
+
}
|
|
29443
|
+
|
|
28363
29444
|
function handleContentEvent(event, streamState, onMessageUpdate, setState) {
|
|
28364
29445
|
streamState.currentContent += event.content;
|
|
28365
29446
|
const assistantMessage = {
|
|
@@ -28406,8 +29487,6 @@ function handleToolStartEvent(event, streamState, onMessageUpdate, setState) {
|
|
|
28406
29487
|
}
|
|
28407
29488
|
function handleToolEndEvent(event, streamState, _onMessageUpdate, setState) {
|
|
28408
29489
|
streamState.activeToolCallCount = Math.max(0, streamState.activeToolCallCount - 1);
|
|
28409
|
-
// Update state and mark action as done in a single setState call
|
|
28410
|
-
// Keep isTyping: true because the agent may continue generating content after tool completion
|
|
28411
29490
|
setState(prev => {
|
|
28412
29491
|
const messages = prev.messages.map((msg) => {
|
|
28413
29492
|
const matchesToolCall = msg.message.role === "tool" && msg.message.tool_call_id === event.tool_call_id;
|
|
@@ -28415,7 +29494,27 @@ function handleToolEndEvent(event, streamState, _onMessageUpdate, setState) {
|
|
|
28415
29494
|
return msg;
|
|
28416
29495
|
}
|
|
28417
29496
|
const existingName = msg.message.name || event.tool_name;
|
|
28418
|
-
|
|
29497
|
+
let action = msg.action;
|
|
29498
|
+
if (event.action_id && event.implementation) {
|
|
29499
|
+
action = {
|
|
29500
|
+
...action,
|
|
29501
|
+
implementation: event.implementation,
|
|
29502
|
+
toolCallId: event.tool_call_id,
|
|
29503
|
+
actionId: event.action_id,
|
|
29504
|
+
input: (event.input || {}),
|
|
29505
|
+
state: (event.state || {}),
|
|
29506
|
+
done: event.done,
|
|
29507
|
+
};
|
|
29508
|
+
}
|
|
29509
|
+
else if (action) {
|
|
29510
|
+
action = {
|
|
29511
|
+
...action,
|
|
29512
|
+
input: event.input ? event.input : action.input,
|
|
29513
|
+
state: event.state ? event.state : action.state,
|
|
29514
|
+
done: event.done,
|
|
29515
|
+
};
|
|
29516
|
+
}
|
|
29517
|
+
const updatedMsg = {
|
|
28419
29518
|
...msg,
|
|
28420
29519
|
message: {
|
|
28421
29520
|
role: "tool",
|
|
@@ -28425,14 +29524,10 @@ function handleToolEndEvent(event, streamState, _onMessageUpdate, setState) {
|
|
|
28425
29524
|
},
|
|
28426
29525
|
isStreaming: false,
|
|
28427
29526
|
toolExecuting: existingName,
|
|
28428
|
-
action
|
|
28429
|
-
...msg.action,
|
|
28430
|
-
state: event.state || msg.action.state,
|
|
28431
|
-
done: true, // Mark action as completed
|
|
28432
|
-
} : undefined,
|
|
29527
|
+
action,
|
|
28433
29528
|
};
|
|
29529
|
+
return updatedMsg;
|
|
28434
29530
|
});
|
|
28435
|
-
// Keep typing indicator visible - it will be hidden by done/finalizeStreamMessages
|
|
28436
29531
|
return { ...prev, messages, isTyping: true, isLoading: false };
|
|
28437
29532
|
});
|
|
28438
29533
|
}
|
|
@@ -28464,34 +29559,6 @@ function handleToolErrorEvent(event, streamState, _onMessageUpdate, setState) {
|
|
|
28464
29559
|
return { ...prev, messages, isTyping: true, isLoading: false };
|
|
28465
29560
|
});
|
|
28466
29561
|
}
|
|
28467
|
-
function finalizeToolMessage(streamState, setState, toolCallId, toolName) {
|
|
28468
|
-
setState(prev => {
|
|
28469
|
-
const messages = prev.messages.map((entry) => {
|
|
28470
|
-
const matchesToolCall = entry.message.role === "tool" && entry.message.tool_call_id === toolCallId;
|
|
28471
|
-
if (!matchesToolCall) {
|
|
28472
|
-
return entry;
|
|
28473
|
-
}
|
|
28474
|
-
const existingName = entry.message.name || toolName;
|
|
28475
|
-
return {
|
|
28476
|
-
...entry,
|
|
28477
|
-
message: {
|
|
28478
|
-
role: "tool",
|
|
28479
|
-
content: typeof entry.message.content === "string" ? entry.message.content : "",
|
|
28480
|
-
tool_call_id: toolCallId,
|
|
28481
|
-
name: existingName,
|
|
28482
|
-
},
|
|
28483
|
-
isStreaming: false,
|
|
28484
|
-
toolExecuting: existingName,
|
|
28485
|
-
action: entry.action ? {
|
|
28486
|
-
...entry.action,
|
|
28487
|
-
done: true, // Mark action as completed
|
|
28488
|
-
} : undefined,
|
|
28489
|
-
};
|
|
28490
|
-
});
|
|
28491
|
-
return { ...prev, messages, isTyping: false, isLoading: false };
|
|
28492
|
-
});
|
|
28493
|
-
streamState.activeToolCallCount = Math.max(0, streamState.activeToolCallCount - 1);
|
|
28494
|
-
}
|
|
28495
29562
|
function handleDoneEvent(event, streamState, _onMessageUpdate, setState) {
|
|
28496
29563
|
streamState.sources = event.sources;
|
|
28497
29564
|
streamState.toolCallToActionId = event.tool_call_to_action_id;
|
|
@@ -28550,6 +29617,10 @@ function handleStreamEvent(event, streamState, onMessageUpdate, setState) {
|
|
|
28550
29617
|
console.warn('[Chat] Unknown event type:', event.type);
|
|
28551
29618
|
}
|
|
28552
29619
|
}
|
|
29620
|
+
|
|
29621
|
+
function isDismissedState(state) {
|
|
29622
|
+
return Boolean(state.__dismissed);
|
|
29623
|
+
}
|
|
28553
29624
|
async function handleActionLoop(client, initialEvent, streamState, onMessageUpdate, setState, widgetId, conversationId, getMessages) {
|
|
28554
29625
|
let pendingEvent = initialEvent;
|
|
28555
29626
|
while (pendingEvent) {
|
|
@@ -28576,7 +29647,7 @@ async function handleActionLoop(client, initialEvent, streamState, onMessageUpda
|
|
|
28576
29647
|
actionId: pendingEvent.action_id,
|
|
28577
29648
|
input: pendingEvent.input,
|
|
28578
29649
|
state: pendingEvent.state,
|
|
28579
|
-
done: false,
|
|
29650
|
+
done: pendingEvent.done ?? false,
|
|
28580
29651
|
},
|
|
28581
29652
|
};
|
|
28582
29653
|
if (streamState.activeToolCallCount === 0) {
|
|
@@ -28590,7 +29661,7 @@ async function handleActionLoop(client, initialEvent, streamState, onMessageUpda
|
|
|
28590
29661
|
id: generateMessageId(),
|
|
28591
29662
|
message: {
|
|
28592
29663
|
role: "assistant",
|
|
28593
|
-
content: "Sorry, an error occurred.
|
|
29664
|
+
content: "Sorry, an error occurred.",
|
|
28594
29665
|
},
|
|
28595
29666
|
timestamp: new Date().toISOString(),
|
|
28596
29667
|
sources: [],
|
|
@@ -28614,7 +29685,7 @@ async function handleActionLoop(client, initialEvent, streamState, onMessageUpda
|
|
|
28614
29685
|
console.error("[Widget] Frontend action failed:", error);
|
|
28615
29686
|
const errorMessageEntry = {
|
|
28616
29687
|
id: generateMessageId(),
|
|
28617
|
-
message: { role: "assistant", content: "Sorry, an error occurred.
|
|
29688
|
+
message: { role: "assistant", content: "Sorry, an error occurred." },
|
|
28618
29689
|
timestamp: new Date().toISOString(),
|
|
28619
29690
|
sources: [],
|
|
28620
29691
|
isError: true,
|
|
@@ -28624,16 +29695,17 @@ async function handleActionLoop(client, initialEvent, streamState, onMessageUpda
|
|
|
28624
29695
|
return;
|
|
28625
29696
|
}
|
|
28626
29697
|
pendingEvent = null;
|
|
28627
|
-
const
|
|
28628
|
-
|
|
28629
|
-
|
|
28630
|
-
|
|
28631
|
-
|
|
28632
|
-
|
|
28633
|
-
|
|
28634
|
-
|
|
28635
|
-
|
|
28636
|
-
|
|
29698
|
+
const dismissed = isDismissedState(nextState);
|
|
29699
|
+
if (!dismissed) {
|
|
29700
|
+
const updatedToolMessage = {
|
|
29701
|
+
...toolMessage,
|
|
29702
|
+
action: toolMessage.action ? { ...toolMessage.action, state: nextState } : toolMessage.action,
|
|
29703
|
+
};
|
|
29704
|
+
upsertMessage(setState, updatedToolMessage, true);
|
|
29705
|
+
}
|
|
29706
|
+
if (dismissed) {
|
|
29707
|
+
return;
|
|
29708
|
+
}
|
|
28637
29709
|
let streamEnded = false;
|
|
28638
29710
|
for await (const event of client.continueAgentMessageStream(conversationId, resumeToolCallId, nextState)) {
|
|
28639
29711
|
if (event.type === "action_request") {
|
|
@@ -28641,22 +29713,20 @@ async function handleActionLoop(client, initialEvent, streamState, onMessageUpda
|
|
|
28641
29713
|
break;
|
|
28642
29714
|
}
|
|
28643
29715
|
if (event.type === "done") {
|
|
28644
|
-
//
|
|
28645
|
-
// updated by tool_end event or by the user's frontend action.
|
|
29716
|
+
// Finalize tool message and stream messages
|
|
28646
29717
|
finalizeToolMessage(streamState, setState, resumeToolCallId, toolName);
|
|
28647
|
-
// Handle the done event but skip the tool finalization part since we already did it
|
|
28648
29718
|
streamState.sources = event.sources;
|
|
28649
29719
|
streamState.toolCallToActionId = event.tool_call_to_action_id;
|
|
28650
29720
|
finalizeStreamMessages(setState, streamState.newMessageIds, event.sources, event.tool_call_to_action_id, event.suggestions);
|
|
28651
29721
|
streamEnded = true;
|
|
28652
|
-
continue;
|
|
29722
|
+
continue;
|
|
28653
29723
|
}
|
|
28654
29724
|
if (event.type === "error") {
|
|
28655
29725
|
const errorMessage = {
|
|
28656
29726
|
id: generateMessageId(),
|
|
28657
29727
|
message: {
|
|
28658
29728
|
role: "assistant",
|
|
28659
|
-
content: "Sorry, an error occurred.
|
|
29729
|
+
content: "Sorry, an error occurred.",
|
|
28660
29730
|
},
|
|
28661
29731
|
timestamp: new Date().toISOString(),
|
|
28662
29732
|
sources: [],
|
|
@@ -28667,73 +29737,83 @@ async function handleActionLoop(client, initialEvent, streamState, onMessageUpda
|
|
|
28667
29737
|
}
|
|
28668
29738
|
handleStreamEvent(event, streamState, onMessageUpdate, setState);
|
|
28669
29739
|
}
|
|
28670
|
-
// If stream ended without a done event
|
|
29740
|
+
// If stream ended without a done event, finalize the tool message
|
|
28671
29741
|
if (!streamEnded && !pendingEvent) {
|
|
28672
29742
|
finalizeToolMessage(streamState, setState, resumeToolCallId, toolName);
|
|
28673
29743
|
}
|
|
28674
29744
|
}
|
|
28675
29745
|
}
|
|
28676
|
-
function
|
|
28677
|
-
|
|
28678
|
-
|
|
28679
|
-
|
|
28680
|
-
|
|
28681
|
-
|
|
28682
|
-
|
|
28683
|
-
|
|
28684
|
-
|
|
28685
|
-
|
|
28686
|
-
|
|
28687
|
-
|
|
28688
|
-
|
|
28689
|
-
|
|
28690
|
-
|
|
28691
|
-
|
|
28692
|
-
|
|
28693
|
-
|
|
28694
|
-
|
|
28695
|
-
|
|
28696
|
-
|
|
28697
|
-
|
|
28698
|
-
|
|
28699
|
-
|
|
28700
|
-
|
|
28701
|
-
|
|
28702
|
-
|
|
28703
|
-
|
|
28704
|
-
|
|
28705
|
-
|
|
28706
|
-
|
|
28707
|
-
|
|
28708
|
-
|
|
28709
|
-
|
|
28710
|
-
|
|
28711
|
-
|
|
29746
|
+
function setupActionResumeCallbacks(messages, client, conversationId, setState, onMessageUpdate, createStreamState, registerCallback) {
|
|
29747
|
+
// Find all incomplete actions and register resume callbacks
|
|
29748
|
+
for (const message of messages) {
|
|
29749
|
+
if (message.action && !message.action.done && !message.action.hidden) {
|
|
29750
|
+
const toolCallId = message.action.toolCallId;
|
|
29751
|
+
const toolName = message.message.name || message.toolExecuting || "tool";
|
|
29752
|
+
registerCallback(toolCallId, async (newState) => {
|
|
29753
|
+
// When user interacts with the action after reload, continue the stream
|
|
29754
|
+
try {
|
|
29755
|
+
// Update the action message with the new state and check completion
|
|
29756
|
+
if (isDismissedState(newState)) {
|
|
29757
|
+
setState(prev => ({ ...prev, isTyping: false }));
|
|
29758
|
+
return;
|
|
29759
|
+
}
|
|
29760
|
+
setState(prev => ({
|
|
29761
|
+
...prev,
|
|
29762
|
+
messages: prev.messages.map(m => {
|
|
29763
|
+
if (m.action?.toolCallId !== toolCallId) {
|
|
29764
|
+
return m;
|
|
29765
|
+
}
|
|
29766
|
+
if (!m.action) {
|
|
29767
|
+
return m;
|
|
29768
|
+
}
|
|
29769
|
+
return { ...m, action: { ...m.action, state: newState } };
|
|
29770
|
+
}),
|
|
29771
|
+
isTyping: true,
|
|
29772
|
+
}));
|
|
29773
|
+
const streamState = createStreamState();
|
|
29774
|
+
// Continue the agent stream with the new state
|
|
29775
|
+
for await (const event of client.continueAgentMessageStream(conversationId, toolCallId, newState)) {
|
|
29776
|
+
if (event.type === "done") {
|
|
29777
|
+
finalizeToolMessage(streamState, setState, toolCallId, toolName);
|
|
29778
|
+
streamState.sources = event.sources;
|
|
29779
|
+
streamState.toolCallToActionId = event.tool_call_to_action_id;
|
|
29780
|
+
finalizeStreamMessages(setState, streamState.newMessageIds, event.sources, event.tool_call_to_action_id, event.suggestions);
|
|
29781
|
+
continue;
|
|
29782
|
+
}
|
|
29783
|
+
if (event.type === "error") {
|
|
29784
|
+
const errorMessage = {
|
|
29785
|
+
id: generateMessageId(),
|
|
29786
|
+
message: {
|
|
29787
|
+
role: "assistant",
|
|
29788
|
+
content: "Sorry, an error occurred. Please try again later.",
|
|
29789
|
+
},
|
|
29790
|
+
timestamp: new Date().toISOString(),
|
|
29791
|
+
sources: [],
|
|
29792
|
+
isError: true,
|
|
29793
|
+
};
|
|
29794
|
+
upsertMessage(setState, errorMessage, false);
|
|
29795
|
+
setState(prev => ({ ...prev, isTyping: false }));
|
|
29796
|
+
return;
|
|
29797
|
+
}
|
|
29798
|
+
handleStreamEvent(event, streamState, onMessageUpdate, setState);
|
|
29799
|
+
}
|
|
29800
|
+
setState(prev => ({ ...prev, isTyping: false }));
|
|
28712
29801
|
}
|
|
28713
|
-
|
|
28714
|
-
|
|
29802
|
+
catch (error) {
|
|
29803
|
+
console.error("[Action Resume] Failed to continue stream:", error);
|
|
29804
|
+
setState(prev => ({ ...prev, isTyping: false }));
|
|
28715
29805
|
}
|
|
29806
|
+
});
|
|
28716
29807
|
}
|
|
28717
|
-
return { message, retryAfterSeconds, status: error.status };
|
|
28718
|
-
}
|
|
28719
|
-
if (error instanceof Error) {
|
|
28720
|
-
const lower = error.message.toLowerCase();
|
|
28721
|
-
if (lower.includes('network')) {
|
|
28722
|
-
return { message: 'Unable to connect to the server. Please check your internet connection.' };
|
|
28723
|
-
}
|
|
28724
|
-
if (lower.includes('timeout')) {
|
|
28725
|
-
return { message: 'The request timed out. Please try again.' };
|
|
28726
|
-
}
|
|
28727
|
-
if (lower.includes('unauthorized') || lower.includes('401')) {
|
|
28728
|
-
return { message: 'Authentication failed. Please refresh the page or verify your API key.' };
|
|
28729
|
-
}
|
|
28730
|
-
if (lower.includes('internal server error') || lower.includes('500')) {
|
|
28731
|
-
return { message: 'The server encountered an error. Please try again shortly.' };
|
|
28732
|
-
}
|
|
28733
|
-
return { message: error.message || 'Something went wrong. Please try again.' };
|
|
28734
29808
|
}
|
|
28735
|
-
return { message: 'Something went wrong. Please try again.' };
|
|
28736
29809
|
}
|
|
29810
|
+
|
|
29811
|
+
/**
|
|
29812
|
+
* useChat Hook
|
|
29813
|
+
* Main state management for chat functionality
|
|
29814
|
+
*/
|
|
29815
|
+
// Initialize action handlers immediately to prevent tree-shaking
|
|
29816
|
+
initializeActionHandlers();
|
|
28737
29817
|
function useChat(options) {
|
|
28738
29818
|
const { widgetId, apiUrl, currentRoute, onMessage, onError, skipInitialization = false, } = options;
|
|
28739
29819
|
const [state, setState] = React.useState({
|
|
@@ -28742,27 +29822,24 @@ function useChat(options) {
|
|
|
28742
29822
|
isLoading: false,
|
|
28743
29823
|
isTyping: false,
|
|
28744
29824
|
error: null,
|
|
28745
|
-
conversationId: '',
|
|
29825
|
+
conversationId: '',
|
|
28746
29826
|
config: null,
|
|
28747
29827
|
});
|
|
28748
29828
|
const stateRef = React.useRef(state);
|
|
28749
29829
|
React.useEffect(() => {
|
|
28750
29830
|
stateRef.current = state;
|
|
28751
29831
|
}, [state]);
|
|
28752
|
-
// Chat history state
|
|
28753
29832
|
const [conversations, setConversations] = React.useState([]);
|
|
28754
|
-
// Stream cancellation and rate limiting
|
|
28755
29833
|
const abortControllerRef = React.useRef(null);
|
|
29834
|
+
const currentRequestIdRef = React.useRef(null);
|
|
28756
29835
|
const lastNewChatTimeRef = React.useRef(0);
|
|
28757
|
-
const NEW_CHAT_COOLDOWN_MS = 5000;
|
|
29836
|
+
const NEW_CHAT_COOLDOWN_MS = 5000;
|
|
28758
29837
|
const apiClient = React.useRef(new WidgetApiClient({ widgetId, apiUrl, currentRoute }));
|
|
28759
|
-
// Update API client when currentRoute changes
|
|
28760
29838
|
React.useEffect(() => {
|
|
28761
29839
|
apiClient.current = new WidgetApiClient({ widgetId, apiUrl, currentRoute });
|
|
28762
29840
|
}, [widgetId, apiUrl, currentRoute]);
|
|
28763
29841
|
// Load configuration on mount and hydrate with existing conversation if available
|
|
28764
29842
|
React.useEffect(() => {
|
|
28765
|
-
// Skip initialization in preview mode
|
|
28766
29843
|
if (skipInitialization) {
|
|
28767
29844
|
return;
|
|
28768
29845
|
}
|
|
@@ -28799,7 +29876,7 @@ function useChat(options) {
|
|
|
28799
29876
|
}));
|
|
28800
29877
|
// Setup resume callbacks for incomplete actions
|
|
28801
29878
|
if (conversationId) {
|
|
28802
|
-
setupActionResumeCallbacks(hydratedMessages, apiClient.current, conversationId, setState, onMessage ?? (() => { }));
|
|
29879
|
+
setupActionResumeCallbacks(hydratedMessages, apiClient.current, conversationId, setState, onMessage ?? (() => { }), createStreamState, registerActionResumeCallback);
|
|
28803
29880
|
}
|
|
28804
29881
|
}
|
|
28805
29882
|
catch (error) {
|
|
@@ -28816,14 +29893,13 @@ function useChat(options) {
|
|
|
28816
29893
|
initialize();
|
|
28817
29894
|
return () => {
|
|
28818
29895
|
isMounted = false;
|
|
28819
|
-
// Cleanup resume callbacks
|
|
28820
29896
|
state.messages.forEach(message => {
|
|
28821
29897
|
if (message.action?.toolCallId) {
|
|
28822
29898
|
unregisterActionResumeCallback(message.action.toolCallId);
|
|
28823
29899
|
}
|
|
28824
29900
|
});
|
|
28825
29901
|
};
|
|
28826
|
-
}, [widgetId, apiUrl, onError]);
|
|
29902
|
+
}, [widgetId, apiUrl, onError, skipInitialization]);
|
|
28827
29903
|
// Save conversation when messages change
|
|
28828
29904
|
React.useEffect(() => {
|
|
28829
29905
|
const persistConversation = state.config?.settings.persistConversation ?? true;
|
|
@@ -28839,19 +29915,15 @@ function useChat(options) {
|
|
|
28839
29915
|
const hasFiles = !!files && files.length > 0;
|
|
28840
29916
|
if (!trimmedContent && !hasFiles)
|
|
28841
29917
|
return;
|
|
28842
|
-
// Block parallel streams - don't allow sending while already streaming
|
|
28843
29918
|
if (stateRef.current.isTyping) {
|
|
28844
29919
|
console.warn('[Widget] Cannot send message while streaming is in progress');
|
|
28845
29920
|
return;
|
|
28846
29921
|
}
|
|
28847
|
-
// Cancel any existing stream before starting new one
|
|
28848
29922
|
if (abortControllerRef.current) {
|
|
28849
29923
|
abortControllerRef.current.abort();
|
|
28850
29924
|
abortControllerRef.current = null;
|
|
28851
29925
|
}
|
|
28852
|
-
// Create new abort controller for this stream
|
|
28853
29926
|
abortControllerRef.current = new AbortController();
|
|
28854
|
-
// Strip [EXECUTE_ACTION:...] prefix from displayed message (but keep for API)
|
|
28855
29927
|
const displayContent = trimmedContent.replace(/^\[EXECUTE_ACTION:[^\]]+\]\s*/, '');
|
|
28856
29928
|
const userMessage = {
|
|
28857
29929
|
id: generateMessageId(),
|
|
@@ -28862,12 +29934,11 @@ function useChat(options) {
|
|
|
28862
29934
|
timestamp: new Date().toISOString(),
|
|
28863
29935
|
sources: [],
|
|
28864
29936
|
};
|
|
28865
|
-
// Add user message immediately
|
|
28866
29937
|
setState(prev => ({
|
|
28867
29938
|
...prev,
|
|
28868
29939
|
messages: [...prev.messages, userMessage],
|
|
28869
|
-
isLoading: false,
|
|
28870
|
-
isTyping: true,
|
|
29940
|
+
isLoading: false,
|
|
29941
|
+
isTyping: true,
|
|
28871
29942
|
error: null,
|
|
28872
29943
|
}));
|
|
28873
29944
|
onMessage?.(userMessage);
|
|
@@ -28907,26 +29978,27 @@ function useChat(options) {
|
|
|
28907
29978
|
}
|
|
28908
29979
|
catch (uploadError) {
|
|
28909
29980
|
console.error('Failed to upload file:', uploadError);
|
|
28910
|
-
// Continue with other files
|
|
28911
29981
|
}
|
|
28912
29982
|
}
|
|
28913
29983
|
}
|
|
28914
|
-
// Stream the response
|
|
28915
29984
|
let lastStreamedMessage = null;
|
|
28916
29985
|
const streamState = createStreamState();
|
|
28917
|
-
|
|
29986
|
+
currentRequestIdRef.current = streamState.requestId;
|
|
28918
29987
|
const currentAbortController = abortControllerRef.current;
|
|
28919
29988
|
const streamConversationId = conversationId;
|
|
28920
|
-
const
|
|
29989
|
+
const streamRequestId = streamState.requestId;
|
|
29990
|
+
const stream = apiClient.current.sendAgentMessageStream(conversationId, trimmedContent, fileIds, currentAbortController?.signal);
|
|
28921
29991
|
for await (const event of stream) {
|
|
28922
|
-
|
|
28923
|
-
|
|
28924
|
-
|
|
29992
|
+
if (currentAbortController?.signal.aborted ||
|
|
29993
|
+
stateRef.current.conversationId !== streamConversationId ||
|
|
29994
|
+
currentRequestIdRef.current !== streamRequestId) {
|
|
29995
|
+
console.log('[Widget] Stream aborted, conversation changed, or superseded by new request');
|
|
28925
29996
|
break;
|
|
28926
29997
|
}
|
|
28927
29998
|
if (event.type === "action_request") {
|
|
28928
|
-
|
|
28929
|
-
|
|
29999
|
+
if (currentAbortController?.signal.aborted ||
|
|
30000
|
+
stateRef.current.conversationId !== streamConversationId ||
|
|
30001
|
+
currentRequestIdRef.current !== streamRequestId) {
|
|
28930
30002
|
break;
|
|
28931
30003
|
}
|
|
28932
30004
|
await handleActionLoop(apiClient.current, event, streamState, (message) => {
|
|
@@ -28938,30 +30010,26 @@ function useChat(options) {
|
|
|
28938
30010
|
lastStreamedMessage = message;
|
|
28939
30011
|
}, setState);
|
|
28940
30012
|
}
|
|
28941
|
-
|
|
28942
|
-
|
|
28943
|
-
|
|
30013
|
+
if (currentAbortController?.signal.aborted ||
|
|
30014
|
+
stateRef.current.conversationId !== streamConversationId ||
|
|
30015
|
+
currentRequestIdRef.current !== streamRequestId) {
|
|
30016
|
+
console.log('[Widget] Stream was aborted or superseded, skipping finalization');
|
|
28944
30017
|
return;
|
|
28945
30018
|
}
|
|
28946
|
-
// Stream completed - finalize state
|
|
28947
30019
|
setState(prev => ({
|
|
28948
30020
|
...prev,
|
|
28949
30021
|
isLoading: false,
|
|
28950
30022
|
isTyping: false,
|
|
28951
30023
|
}));
|
|
28952
|
-
// Notify about final message
|
|
28953
30024
|
if (lastStreamedMessage) {
|
|
28954
30025
|
onMessage?.(lastStreamedMessage);
|
|
28955
30026
|
}
|
|
28956
|
-
// Generate follow-up suggestions asynchronously
|
|
28957
30027
|
const enableFollowUps = state.config?.settings.enableFollowUpSuggestions !== false;
|
|
28958
30028
|
const actionIds = state.config?.actions || [];
|
|
28959
30029
|
if (enableFollowUps) {
|
|
28960
|
-
// Don't await - let it run in background
|
|
28961
30030
|
apiClient.current.generateFollowUps(stateRef.current.messages, actionIds)
|
|
28962
30031
|
.then(suggestions => {
|
|
28963
30032
|
if (suggestions.length > 0) {
|
|
28964
|
-
// Attach suggestions to the last assistant message
|
|
28965
30033
|
setState(prev => {
|
|
28966
30034
|
const messages = [...prev.messages];
|
|
28967
30035
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
@@ -28990,7 +30058,7 @@ function useChat(options) {
|
|
|
28990
30058
|
},
|
|
28991
30059
|
timestamp: new Date().toISOString(),
|
|
28992
30060
|
sources: [],
|
|
28993
|
-
isError: !fallbackMessage,
|
|
30061
|
+
isError: !fallbackMessage,
|
|
28994
30062
|
};
|
|
28995
30063
|
setState(prev => ({
|
|
28996
30064
|
...prev,
|
|
@@ -29002,9 +30070,6 @@ function useChat(options) {
|
|
|
29002
30070
|
onError?.(err);
|
|
29003
30071
|
}
|
|
29004
30072
|
}, [state.conversationId, state.config, state.messages, onMessage, onError]);
|
|
29005
|
-
/**
|
|
29006
|
-
* Clear all messages
|
|
29007
|
-
*/
|
|
29008
30073
|
const clearMessages = React.useCallback(() => {
|
|
29009
30074
|
setState(prev => ({
|
|
29010
30075
|
...prev,
|
|
@@ -29017,9 +30082,6 @@ function useChat(options) {
|
|
|
29017
30082
|
clearConversation(widgetId);
|
|
29018
30083
|
}
|
|
29019
30084
|
}, [widgetId, state.config?.settings.persistConversation]);
|
|
29020
|
-
/**
|
|
29021
|
-
* Submit feedback for a message
|
|
29022
|
-
*/
|
|
29023
30085
|
const submitFeedback = React.useCallback(async (messageId, feedback) => {
|
|
29024
30086
|
try {
|
|
29025
30087
|
const message = state.messages.find(msg => msg.id === messageId);
|
|
@@ -29029,7 +30091,6 @@ function useChat(options) {
|
|
|
29029
30091
|
: undefined;
|
|
29030
30092
|
console.log('Submitting feedback:', { conversationId: state.conversationId, messageId, feedback });
|
|
29031
30093
|
await apiClient.current.submitFeedback(state.conversationId, messageId, feedback, meta);
|
|
29032
|
-
// Update message with feedback
|
|
29033
30094
|
setState(prev => ({
|
|
29034
30095
|
...prev,
|
|
29035
30096
|
messages: prev.messages.map(msg => msg.id === messageId
|
|
@@ -29044,9 +30105,51 @@ function useChat(options) {
|
|
|
29044
30105
|
onError?.(err);
|
|
29045
30106
|
}
|
|
29046
30107
|
}, [state.conversationId, onError]);
|
|
29047
|
-
|
|
29048
|
-
|
|
29049
|
-
|
|
30108
|
+
const dismissAction = React.useCallback(async (toolCallId) => {
|
|
30109
|
+
if (!toolCallId)
|
|
30110
|
+
return;
|
|
30111
|
+
const dismissedAt = new Date().toISOString();
|
|
30112
|
+
setState(prev => ({
|
|
30113
|
+
...prev,
|
|
30114
|
+
messages: prev.messages.map((message) => {
|
|
30115
|
+
if (message.action?.toolCallId !== toolCallId) {
|
|
30116
|
+
return message;
|
|
30117
|
+
}
|
|
30118
|
+
if (!message.action) {
|
|
30119
|
+
return message;
|
|
30120
|
+
}
|
|
30121
|
+
return {
|
|
30122
|
+
...message,
|
|
30123
|
+
action: {
|
|
30124
|
+
...message.action,
|
|
30125
|
+
hidden: true,
|
|
30126
|
+
dismissedAt,
|
|
30127
|
+
dismissedBy: "user",
|
|
30128
|
+
done: true,
|
|
30129
|
+
},
|
|
30130
|
+
};
|
|
30131
|
+
}),
|
|
30132
|
+
isTyping: true,
|
|
30133
|
+
isLoading: false,
|
|
30134
|
+
}));
|
|
30135
|
+
unregisterActionResumeCallback(toolCallId);
|
|
30136
|
+
const conversationId = stateRef.current.conversationId;
|
|
30137
|
+
if (!conversationId) {
|
|
30138
|
+
setState(prev => ({ ...prev, isTyping: false }));
|
|
30139
|
+
return;
|
|
30140
|
+
}
|
|
30141
|
+
try {
|
|
30142
|
+
const streamState = createStreamState();
|
|
30143
|
+
for await (const event of apiClient.current.dismissAgentMessageStream(conversationId, toolCallId)) {
|
|
30144
|
+
handleStreamEvent(event, streamState, onMessage ?? (() => { }), setState);
|
|
30145
|
+
}
|
|
30146
|
+
setState(prev => ({ ...prev, isTyping: false, isLoading: false }));
|
|
30147
|
+
}
|
|
30148
|
+
catch (error) {
|
|
30149
|
+
console.error("[Widget] dismissAction error:", error);
|
|
30150
|
+
setState(prev => ({ ...prev, isTyping: false, isLoading: false }));
|
|
30151
|
+
}
|
|
30152
|
+
}, [onMessage]);
|
|
29050
30153
|
const loadConversations = React.useCallback(() => {
|
|
29051
30154
|
const persistConversation = state.config?.settings.persistConversation ?? true;
|
|
29052
30155
|
if (!persistConversation || !isStorageAvailable()) {
|
|
@@ -29063,13 +30166,11 @@ function useChat(options) {
|
|
|
29063
30166
|
})));
|
|
29064
30167
|
}, [widgetId, state.config?.settings.persistConversation]);
|
|
29065
30168
|
const switchConversation = React.useCallback(async (conversationId) => {
|
|
29066
|
-
// Cancel any active stream before switching conversations
|
|
29067
30169
|
if (abortControllerRef.current) {
|
|
29068
30170
|
abortControllerRef.current.abort();
|
|
29069
30171
|
abortControllerRef.current = null;
|
|
29070
30172
|
}
|
|
29071
30173
|
const persistConversation = state.config?.settings.persistConversation ?? true;
|
|
29072
|
-
// First try to load from localStorage
|
|
29073
30174
|
if (persistConversation && isStorageAvailable()) {
|
|
29074
30175
|
const stored = loadConversationById(widgetId, conversationId);
|
|
29075
30176
|
if (stored) {
|
|
@@ -29088,7 +30189,6 @@ function useChat(options) {
|
|
|
29088
30189
|
try {
|
|
29089
30190
|
const conversation = await apiClient.current.getOrCreateConversation(conversationId);
|
|
29090
30191
|
const hydratedMessages = hydrateMessages(conversation.messages);
|
|
29091
|
-
// Clear old resume callbacks
|
|
29092
30192
|
state.messages.forEach(message => {
|
|
29093
30193
|
if (message.action?.toolCallId) {
|
|
29094
30194
|
unregisterActionResumeCallback(message.action.toolCallId);
|
|
@@ -29100,9 +30200,7 @@ function useChat(options) {
|
|
|
29100
30200
|
messages: hydratedMessages,
|
|
29101
30201
|
isLoading: false,
|
|
29102
30202
|
}));
|
|
29103
|
-
|
|
29104
|
-
setupActionResumeCallbacks(hydratedMessages, apiClient.current, conversation.id, setState, onMessage ?? (() => { }));
|
|
29105
|
-
// Save to local storage
|
|
30203
|
+
setupActionResumeCallbacks(hydratedMessages, apiClient.current, conversation.id, setState, onMessage ?? (() => { }), createStreamState, registerActionResumeCallback);
|
|
29106
30204
|
if (persistConversation && isStorageAvailable()) {
|
|
29107
30205
|
saveConversation(widgetId, conversation.id, hydratedMessages);
|
|
29108
30206
|
}
|
|
@@ -29113,19 +30211,16 @@ function useChat(options) {
|
|
|
29113
30211
|
}
|
|
29114
30212
|
}, [widgetId, state.config?.settings.persistConversation]);
|
|
29115
30213
|
const startNewConversation = React.useCallback(() => {
|
|
29116
|
-
// Rate limiting - prevent spamming new chats
|
|
29117
30214
|
const now = Date.now();
|
|
29118
30215
|
if (now - lastNewChatTimeRef.current < NEW_CHAT_COOLDOWN_MS) {
|
|
29119
30216
|
console.warn('[Widget] New chat rate limited - please wait');
|
|
29120
30217
|
return;
|
|
29121
30218
|
}
|
|
29122
30219
|
lastNewChatTimeRef.current = now;
|
|
29123
|
-
// Cancel any active stream before starting new conversation
|
|
29124
30220
|
if (abortControllerRef.current) {
|
|
29125
30221
|
abortControllerRef.current.abort();
|
|
29126
30222
|
abortControllerRef.current = null;
|
|
29127
30223
|
}
|
|
29128
|
-
// Reset typing state if stream was active
|
|
29129
30224
|
setState(prev => ({
|
|
29130
30225
|
...prev,
|
|
29131
30226
|
messages: [],
|
|
@@ -29144,11 +30239,8 @@ function useChat(options) {
|
|
|
29144
30239
|
if (!persistConversation || !isStorageAvailable()) {
|
|
29145
30240
|
return;
|
|
29146
30241
|
}
|
|
29147
|
-
// Delete from storage
|
|
29148
30242
|
deleteConversation(widgetId, conversationId);
|
|
29149
|
-
// Update local state
|
|
29150
30243
|
setConversations(prev => prev.filter(c => c.id !== conversationId));
|
|
29151
|
-
// If we deleted the current conversation, clear it
|
|
29152
30244
|
if (state.conversationId === conversationId) {
|
|
29153
30245
|
setState(prev => ({
|
|
29154
30246
|
...prev,
|
|
@@ -29158,6 +30250,43 @@ function useChat(options) {
|
|
|
29158
30250
|
}));
|
|
29159
30251
|
}
|
|
29160
30252
|
}, [widgetId, state.config?.settings.persistConversation, state.conversationId]);
|
|
30253
|
+
const createDemoConversation = React.useCallback(async (userMessage, assistantMessage) => {
|
|
30254
|
+
try {
|
|
30255
|
+
const result = await apiClient.current.createDemoConversation(userMessage, assistantMessage);
|
|
30256
|
+
if (result.success && result.id) {
|
|
30257
|
+
// Update state with the new conversation ID
|
|
30258
|
+
setState(prev => ({
|
|
30259
|
+
...prev,
|
|
30260
|
+
conversationId: result.id,
|
|
30261
|
+
}));
|
|
30262
|
+
// Save to local storage if persistence is enabled
|
|
30263
|
+
const persistConversation = state.config?.settings.persistConversation ?? true;
|
|
30264
|
+
if (persistConversation && isStorageAvailable()) {
|
|
30265
|
+
const demoMessages = [
|
|
30266
|
+
{
|
|
30267
|
+
id: generateMessageId(),
|
|
30268
|
+
message: { role: 'user', content: userMessage },
|
|
30269
|
+
timestamp: new Date().toISOString(),
|
|
30270
|
+
sources: [],
|
|
30271
|
+
},
|
|
30272
|
+
{
|
|
30273
|
+
id: generateMessageId(),
|
|
30274
|
+
message: { role: 'assistant', content: assistantMessage },
|
|
30275
|
+
timestamp: new Date(Date.now() + 1000).toISOString(),
|
|
30276
|
+
sources: [],
|
|
30277
|
+
},
|
|
30278
|
+
];
|
|
30279
|
+
saveConversation(widgetId, result.id, demoMessages);
|
|
30280
|
+
}
|
|
30281
|
+
return result.id;
|
|
30282
|
+
}
|
|
30283
|
+
return null;
|
|
30284
|
+
}
|
|
30285
|
+
catch (error) {
|
|
30286
|
+
console.error('[useChat] Failed to create demo conversation:', error);
|
|
30287
|
+
return null;
|
|
30288
|
+
}
|
|
30289
|
+
}, [widgetId, state.config?.settings.persistConversation]);
|
|
29161
30290
|
return {
|
|
29162
30291
|
messages: state.messages,
|
|
29163
30292
|
isLoading: state.isLoading,
|
|
@@ -29168,20 +30297,29 @@ function useChat(options) {
|
|
|
29168
30297
|
sendMessage,
|
|
29169
30298
|
clearMessages,
|
|
29170
30299
|
submitFeedback,
|
|
29171
|
-
|
|
30300
|
+
dismissAction,
|
|
29172
30301
|
conversations,
|
|
29173
30302
|
loadConversations,
|
|
29174
30303
|
switchConversation,
|
|
29175
30304
|
startNewConversation,
|
|
29176
30305
|
deleteConversation: deleteConversation$1,
|
|
30306
|
+
createDemoConversation,
|
|
29177
30307
|
};
|
|
29178
30308
|
}
|
|
29179
30309
|
|
|
30310
|
+
const DataPolicyView = ({ config, widgetName, }) => {
|
|
30311
|
+
const headerTitle = widgetName || config?.appearance?.headerTitle || 'AI Assistant';
|
|
30312
|
+
const hasFileUpload = config?.settings?.enableFileUpload ?? false;
|
|
30313
|
+
const persistsConversation = config?.settings?.persistConversation ?? true;
|
|
30314
|
+
return (jsxRuntime.jsx("div", { className: "ai-chat-data-policy-view", children: jsxRuntime.jsxs("div", { className: "ai-chat-data-policy-content", children: [jsxRuntime.jsx("div", { className: "ai-chat-data-policy-intro", children: jsxRuntime.jsxs("p", { children: ["This privacy notice informs you pursuant to Art. 13 GDPR about how ", jsxRuntime.jsx("strong", { children: headerTitle }), " processes data when you use this chat. Please note that the specific scope of processing depends on the operator of this website/application (the controller) and on the respective activated functions."] }) }), jsxRuntime.jsxs("div", { className: "ai-chat-data-policy-section", children: [jsxRuntime.jsx("h3", { children: "Controller / Contact" }), jsxRuntime.jsx("p", { children: "The controller within the meaning of Art. 4 No. 7 GDPR is the operator of this website/application. The contact details (and, if applicable, the contact details of a data protection officer) can be found in the privacy policy or in the legal notice of the website into which this chat widget is embedded." })] }), jsxRuntime.jsxs("div", { className: "ai-chat-data-policy-section", children: [jsxRuntime.jsx("h3", { children: "Processed Data" }), jsxRuntime.jsxs("p", { children: ["The chat processes the following categories of data. ", jsxRuntime.jsx("strong", { children: "Chat Content" }), " comprises messages (text) and, if applicable, context information that you provide in the chat. This content is processed to generate responses and provide the conversation."] }), hasFileUpload && (jsxRuntime.jsxs("p", { children: [jsxRuntime.jsx("strong", { children: "Uploaded Files" }), " that you transmit to the chat are processed to handle your request. Processing may include extracting text or information."] })), jsxRuntime.jsxs("p", { children: [jsxRuntime.jsx("strong", { children: "Technical Usage Data" }), " includes timestamps, session or request information, as well as technical metadata required for operation, security (abuse prevention), and error analysis."] }), jsxRuntime.jsx("p", { children: "Please do not enter special categories of personal data (e.g., health data), passwords, credit card or bank data, or confidential business secrets in the chat. AI-generated responses may be inaccurate and should be checked independently before use." })] }), jsxRuntime.jsxs("div", { className: "ai-chat-data-policy-section", children: [jsxRuntime.jsx("h3", { children: "Purposes and Legal Bases" }), jsxRuntime.jsxs("p", { children: [jsxRuntime.jsx("strong", { children: "Provision of the chat and answering of inquiries" }), " is based on Art. 6 Para. 1 lit. b GDPR, insofar as contractual or pre-contractual measures apply; otherwise on Art. 6 Para. 1 lit. f GDPR (legitimate interest in efficient communication and support)."] }), jsxRuntime.jsxs("p", { children: [jsxRuntime.jsx("strong", { children: "Quality assurance, operation and security" }), " are based on Art. 6 Para. 1 lit. f GDPR, for example for stability, abuse detection, and troubleshooting."] }), jsxRuntime.jsxs("p", { children: [jsxRuntime.jsx("strong", { children: "Consent-based processing" }), " may occur if the operator provides for this (Art. 6 Para. 1 lit. a GDPR)."] })] }), jsxRuntime.jsxs("div", { className: "ai-chat-data-policy-section", children: [jsxRuntime.jsx("h3", { children: "Recipients and Processors" }), jsxRuntime.jsxs("p", { children: [jsxRuntime.jsx("strong", { children: "Hosting/IT Service Providers" }), " may be used by the operator for hosting, logging, monitoring, and infrastructure."] }), jsxRuntime.jsxs("p", { children: [jsxRuntime.jsx("strong", { children: "AI Service Providers" }), " may receive chat content to generate responses. Where required, this is done on the basis of a data processing agreement (Art. 28 GDPR)."] }), jsxRuntime.jsxs("p", { children: [jsxRuntime.jsx("strong", { children: "Third-Country Transfer" }), " may occur if recipients are located outside the EU/EEA. In this case, appropriate safeguards (e.g., EU Standard Contractual Clauses) are used where required."] })] }), jsxRuntime.jsxs("div", { className: "ai-chat-data-policy-section", children: [jsxRuntime.jsx("h3", { children: "Retention Period" }), persistsConversation ? (jsxRuntime.jsxs("p", { children: [jsxRuntime.jsx("strong", { children: "Chat History" }), " may be stored to continue the conversation across multiple sessions."] })) : (jsxRuntime.jsxs("p", { children: [jsxRuntime.jsx("strong", { children: "Chat History" }), " is not permanently stored and ends when the chat is closed."] })), hasFileUpload && (jsxRuntime.jsxs("p", { children: [jsxRuntime.jsx("strong", { children: "Files" }), " are processed only as long as necessary to handle the request and are then deleted, unless longer retention is legally required."] })), jsxRuntime.jsxs("p", { children: [jsxRuntime.jsx("strong", { children: "Technical Logs" }), " may be stored for a limited period to ensure secure operation."] })] }), jsxRuntime.jsxs("div", { className: "ai-chat-data-policy-section", children: [jsxRuntime.jsx("h3", { children: "Your Rights (Data Subject Rights)" }), jsxRuntime.jsx("p", { children: "You have the following rights under the GDPR: Right to Information (Art. 15), Right to Rectification (Art. 16), Right to Erasure (Art. 17) and Restriction of Processing (Art. 18), Right to Data Portability (Art. 20), Right to Objection to processing based on legitimate interests (Art. 21), and Right to Complain to a supervisory authority (Art. 77)." }), jsxRuntime.jsx("p", { children: "Note: Without clear identification features, the operator may not be able to assign individual chat histories to a person. For inquiries, please contact the operator of this website/application." })] })] }) }));
|
|
30315
|
+
};
|
|
30316
|
+
|
|
29180
30317
|
const MenuIcon = () => (jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("line", { x1: "4", y1: "10", x2: "20", y2: "10" }), jsxRuntime.jsx("line", { x1: "10", y1: "14", x2: "20", y2: "14" })] }));
|
|
29181
30318
|
const PlusIcon = () => (jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("line", { x1: "12", y1: "5", x2: "12", y2: "19" }), jsxRuntime.jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" })] }));
|
|
29182
30319
|
const TrashIcon = () => (jsxRuntime.jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("path", { d: "M3 6h18" }), jsxRuntime.jsx("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }), jsxRuntime.jsx("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" })] }));
|
|
29183
30320
|
const CloseIcon = () => (jsxRuntime.jsxs("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }));
|
|
29184
|
-
const
|
|
30321
|
+
const BackIcon = () => (jsxRuntime.jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("path", { d: "M19 12H5" }), jsxRuntime.jsx("path", { d: "M12 19l-7-7 7-7" })] }));
|
|
30322
|
+
const ChatWindow = ({ messages, isLoading, isTyping, config, onSendMessage, onClose: _onClose, onFeedback, onActionClick, onActionDismiss,
|
|
29185
30323
|
// Chat history props (only active when persistConversation is true)
|
|
29186
30324
|
conversations = [], onLoadConversations, onSwitchConversation, onStartNewConversation, onDeleteConversation, currentConversationId,
|
|
29187
30325
|
// Override props for live preview
|
|
@@ -29198,6 +30336,8 @@ headerTitleOverride, welcomeTitleOverride, welcomeMessageOverride, placeholderOv
|
|
|
29198
30336
|
const inputPlaceholder = placeholderOverride ?? appearance?.placeholder ?? 'Ask me anything...';
|
|
29199
30337
|
// Track if history panel is open
|
|
29200
30338
|
const [showHistory, setShowHistory] = React.useState(false);
|
|
30339
|
+
// Track if data policy view is open
|
|
30340
|
+
const [showDataPolicy, setShowDataPolicy] = React.useState(false);
|
|
29201
30341
|
// Scroll button state (managed by MessageList)
|
|
29202
30342
|
const [showScrollButton, setShowScrollButton] = React.useState(false);
|
|
29203
30343
|
const [scrollToBottom, setScrollToBottom] = React.useState(null);
|
|
@@ -29207,6 +30347,13 @@ headerTitleOverride, welcomeTitleOverride, welcomeMessageOverride, placeholderOv
|
|
|
29207
30347
|
}, []);
|
|
29208
30348
|
// History exit animation when starting a new chat from overview
|
|
29209
30349
|
const [isHistoryExiting, setIsHistoryExiting] = React.useState(false);
|
|
30350
|
+
// Handle data policy click
|
|
30351
|
+
const handleDataPolicyClick = React.useCallback(() => {
|
|
30352
|
+
setShowDataPolicy(true);
|
|
30353
|
+
}, []);
|
|
30354
|
+
const handleDataPolicyBack = React.useCallback(() => {
|
|
30355
|
+
setShowDataPolicy(false);
|
|
30356
|
+
}, []);
|
|
29210
30357
|
// Load conversations when history panel opens
|
|
29211
30358
|
const handleOpenHistory = () => {
|
|
29212
30359
|
setShowHistory(true);
|
|
@@ -29254,10 +30401,22 @@ headerTitleOverride, welcomeTitleOverride, welcomeMessageOverride, placeholderOv
|
|
|
29254
30401
|
// The backend will detect and trigger the action based on the message
|
|
29255
30402
|
onSendMessage(question);
|
|
29256
30403
|
};
|
|
29257
|
-
return (jsxRuntime.jsxs("div", { className: `ai-chat-window size-${size}`, role: "dialog", "aria-label": "Chat window", children: [jsxRuntime.jsx("div", { className: `ai-chat-header ${showHistory ? 'is-history' : ''}`, children: showHistory ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "ai-chat-title", children: headerTitle }), jsxRuntime.jsx("button", { className: "ai-chat-header-button", onClick: handleNewConversation, "aria-label": "New chat", children: jsxRuntime.jsx(PlusIcon, {}) })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "ai-chat-header-content", children: [appearance?.logo && (jsxRuntime.jsx("img", { src: appearance.logo, alt: "Logo", className: "ai-chat-logo" })), jsxRuntime.jsx("div", { className: "ai-chat-title", children: headerTitle })] }), jsxRuntime.jsxs("div", { className: "ai-chat-header-actions", children: [canShowHistory && (jsxRuntime.jsx("button", { className: "ai-chat-header-button", onClick: handleOpenHistory, "aria-label": "Chat overview", children: jsxRuntime.jsx(MenuIcon, {}) })), jsxRuntime.jsx("button", { className: "ai-chat-close-button header-close-button", onClick: _onClose, "aria-label": "Close chat", children: jsxRuntime.jsx(CloseIcon, {}) })] })] })) }),
|
|
30404
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-window size-${size}`, role: "dialog", "aria-label": "Chat window", children: [jsxRuntime.jsx("div", { className: `ai-chat-header ${showHistory ? 'is-history' : ''} ${showDataPolicy ? 'is-data-policy' : ''}`, children: showDataPolicy ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("button", { className: "ai-chat-header-button", onClick: handleDataPolicyBack, "aria-label": "Back to chat", children: jsxRuntime.jsx(BackIcon, {}) }), jsxRuntime.jsx("div", { className: "ai-chat-title", children: "Privacy Notice" })] })) : showHistory ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: "ai-chat-title", children: headerTitle }), jsxRuntime.jsx("button", { className: "ai-chat-header-button", onClick: handleNewConversation, "aria-label": "New chat", children: jsxRuntime.jsx(PlusIcon, {}) })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { className: "ai-chat-header-content", children: [appearance?.logo && (jsxRuntime.jsx("img", { src: appearance.logo, alt: "Logo", className: "ai-chat-logo" })), jsxRuntime.jsx("div", { className: "ai-chat-title", children: headerTitle })] }), jsxRuntime.jsxs("div", { className: "ai-chat-header-actions", children: [canShowHistory && (jsxRuntime.jsx("button", { className: "ai-chat-header-button", onClick: handleOpenHistory, "aria-label": "Chat overview", children: jsxRuntime.jsx(MenuIcon, {}) })), jsxRuntime.jsx("button", { className: "ai-chat-close-button header-close-button", onClick: _onClose, "aria-label": "Close chat", children: jsxRuntime.jsx(CloseIcon, {}) })] })] })) }), showDataPolicy ? (jsxRuntime.jsx(DataPolicyView, { config: config, onBack: handleDataPolicyBack, widgetName: headerTitle })) : showHistory ? (
|
|
30405
|
+
/* History Panel */
|
|
30406
|
+
jsxRuntime.jsxs("div", { className: "ai-chat-history-panel", children: [conversations.length === 0 ? (jsxRuntime.jsx("div", { className: "ai-chat-history-empty", children: "No previous conversations" })) : (jsxRuntime.jsx("div", { className: `ai-chat-history-list ${isHistoryExiting ? 'exiting' : ''}`, children: conversations.map((conv) => (jsxRuntime.jsx("div", { className: `ai-chat-history-item ${conv.id === currentConversationId ? 'active' : ''}`, onClick: () => handleSelectConversation(conv.id), children: jsxRuntime.jsxs("div", { className: "ai-chat-history-item-content", children: [jsxRuntime.jsx("div", { className: "ai-chat-history-item-preview", children: conv.preview }), onDeleteConversation && (jsxRuntime.jsx("button", { className: "ai-chat-history-item-delete", onClick: (e) => {
|
|
29258
30407
|
e.stopPropagation();
|
|
29259
30408
|
onDeleteConversation(conv.id);
|
|
29260
|
-
}, "aria-label": "Delete conversation", children: jsxRuntime.jsx(TrashIcon, {}) }))] }) }, conv.id))) })), jsxRuntime.jsx(MessageInput, { onSend: (text) => handleSendFromOverview(text), placeholder: inputPlaceholder, disabled: isLoading, enableFileUpload: settings?.enableFileUpload })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
30409
|
+
}, "aria-label": "Delete conversation", children: jsxRuntime.jsx(TrashIcon, {}) }))] }) }, conv.id))) })), jsxRuntime.jsx(MessageInput, { onSend: (text) => handleSendFromOverview(text), placeholder: inputPlaceholder, disabled: isLoading, enableFileUpload: settings?.enableFileUpload, showDataPolicy: settings?.showDataPolicy ?? true, onDataPolicyClick: handleDataPolicyClick })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [maxMessages && userMessageCount >= maxMessages - 2 && !isLimitReached && (jsxRuntime.jsxs("div", { className: "ai-chat-warning", role: "alert", children: [maxMessages - userMessageCount, " message", maxMessages - userMessageCount !== 1 ? 's' : '', " remaining"] })), isLimitReached && (jsxRuntime.jsx("div", { className: "ai-chat-error", role: "alert", children: "Message limit reached. Please start a new conversation." })), (() => {
|
|
30410
|
+
console.log('[DEBUG ChatWindow] Rendering MessageList with', messages.length, 'messages');
|
|
30411
|
+
messages.forEach((m, i) => {
|
|
30412
|
+
console.log(`[DEBUG ChatWindow] msg ${i}:`, { role: m.message.role, hasAction: !!m.action, impl: m.action?.implementation });
|
|
30413
|
+
});
|
|
30414
|
+
console.log('[DEBUG ChatWindow] getActionRenderer available:', !!getActionRenderer);
|
|
30415
|
+
if (getActionRenderer) {
|
|
30416
|
+
console.log('[DEBUG ChatWindow] Testing renderer for query-contact-directory:', !!getActionRenderer('query-contact-directory'));
|
|
30417
|
+
}
|
|
30418
|
+
return null;
|
|
30419
|
+
})(), jsxRuntime.jsx(MessageList, { messages: messages, isTyping: isTyping, showTypingIndicator: settings?.showTypingIndicator, showTimestamps: settings?.showTimestamps, showToolCalls: settings?.showToolCalls, enableFeedback: settings?.enableFeedback, welcomeTitle: welcomeTitle || 'Welcome Message', welcomeMessage: welcomeMessage, suggestedQuestions: suggestedQuestionsOverride ?? settings?.suggestedQuestions, accentColor: appearance?.primaryColor, onSuggestedQuestionClick: handleQuestionClick, onActionClick: onActionClick, onActionDismiss: onActionDismiss, onFeedback: onFeedback, onScrollStateChange: handleScrollStateChange, getActionRenderer: getActionRenderer }), settings?.showDataPolicy && (jsxRuntime.jsxs("div", { className: "ai-chat-page-disclaimer", children: [jsxRuntime.jsx("span", { children: "AI-generated responses may be inaccurate." }), jsxRuntime.jsx("button", { type: "button", className: "ai-chat-page-disclaimer-link", onClick: handleDataPolicyClick, children: "Privacy Notice" })] })), jsxRuntime.jsx(ScrollButton, { onClick: () => scrollToBottom?.(), visible: showScrollButton }), jsxRuntime.jsx(MessageInput, { onSend: onSendMessage, placeholder: isLimitReached ? 'Message limit reached' : (isTyping ? 'Waiting for response...' : inputPlaceholder), disabled: isLoading || isTyping || isLimitReached, enableFileUpload: settings?.enableFileUpload, showDataPolicy: settings?.showDataPolicy ?? true, onDataPolicyClick: handleDataPolicyClick })] }))] }));
|
|
29261
30420
|
};
|
|
29262
30421
|
|
|
29263
30422
|
/**
|
|
@@ -29663,7 +30822,7 @@ function styleInject(css, ref) {
|
|
|
29663
30822
|
if ( ref === void 0 ) ref = {};
|
|
29664
30823
|
var insertAt = ref.insertAt;
|
|
29665
30824
|
|
|
29666
|
-
if (typeof document === 'undefined') { return; }
|
|
30825
|
+
if (!css || typeof document === 'undefined') { return; }
|
|
29667
30826
|
|
|
29668
30827
|
var head = document.head || document.getElementsByTagName('head')[0];
|
|
29669
30828
|
var style = document.createElement('style');
|
|
@@ -29686,7 +30845,10 @@ function styleInject(css, ref) {
|
|
|
29686
30845
|
}
|
|
29687
30846
|
}
|
|
29688
30847
|
|
|
29689
|
-
var css_248z = ".ai-chat-message{animation:ai-chat-message-appear .2s var(--chat-ease-bounce);max-width:85%}.ai-chat-message-content{border-radius:var(--chat-radius-bubble,14px);font-size:var(--chat-text-md,15px);line-height:var(--chat-line-relaxed,1.6);padding:var(--chat-space-sm,8px) var(--chat-space-md,16px)}.ai-chat-message.user .ai-chat-message-content{background:var(--chat-user-bg,#f4f3f0);border-bottom-right-radius:var(--chat-radius-sm,4px);color:var(--chat-user-text,#000)}.ai-chat-message.assistant .ai-chat-message-content{background:var(--chat-assistant-bg,transparent);color:var(--chat-assistant-text,#000)}.ai-chat-message-timestamp{color:var(--chat-text-muted,#71717a);font-size:var(--chat-text-xs,12px);margin-top:var(--chat-space-xs,4px);padding:0 var(--chat-space-xs,4px)}.ai-chat-message.streaming .ai-chat-message-content:after{animation:ai-chat-cursor-blink .8s infinite;content:\"▋\";margin-left:2px;opacity:.7}@keyframes ai-chat-message-appear{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes ai-chat-cursor-blink{0%,50%{opacity:1}51%,to{opacity:0}}.ai-chat-message.fullpage .ai-chat-message-content{font-size:var(--chat-text-lg,18px);padding:var(--chat-space-md,16px) var(--chat-space-lg,24px)}.ai-chat-typing{gap:var(--chat-space-xs,4px);padding:var(--chat-space-sm,8px) var(--chat-space-md,16px)}.ai-chat-typing-dot{background:var(--chat-text-muted,#71717a)}.ai-chat-tool-gear{color:var(--text-primary,#3e3e3e)}.ai-chat-tool-gear.spinning{animation:ai-chat-spin 1.5s linear infinite}.ai-chat-tool-badge{border-radius:8px}.ai-chat-tool-badge.loading{background:#e5e7eb;border:1px solid #d1d5db;color:#1f2937}.ai-chat-tool-badge.error{background:rgba(239,68,68,.1);color:#ef4444}.ai-chat-tool-check{color:#22c55e;flex-shrink:0}.ai-chat-tool-error{color:#ef4444;flex-shrink:0}.ai-chat-tool-badge .tool-name{max-width:150px;overflow:hidden;text-overflow:ellipsis}.ai-chat-widget.dark .ai-chat-tool-gear,.ai-chat-widget[data-theme=dark] .ai-chat-tool-gear,.dark .ai-chat-tool-gear,[data-theme=dark] .ai-chat-tool-gear{color:#fff}.ai-chat-widget.dark .ai-chat-tool-badge,.ai-chat-widget[data-theme=dark] .ai-chat-tool-badge,.dark .ai-chat-tool-badge,[data-theme=dark] .ai-chat-tool-badge{background:hsla(0,0%,100%,.12);border:1px solid hsla(0,0%,100%,.2);color:hsla(0,0%,100%,.9)}.ai-chat-widget.dark .ai-chat-tool-badge.error,.ai-chat-widget[data-theme=dark] .ai-chat-tool-badge.error,.dark .ai-chat-tool-badge.error,[data-theme=dark] .ai-chat-tool-badge.error{background:rgba(239,68,68,.2);color:#f87171}.chakra-ui-dark .ai-chat-tool-gear,html.dark .ai-chat-tool-gear{color:#fff}.chakra-ui-dark .ai-chat-tool-badge,html.dark .ai-chat-tool-badge{background:hsla(0,0%,100%,.12);border:1px solid hsla(0,0%,100%,.2);color:hsla(0,0%,100%,.9)}.chakra-ui-dark .ai-chat-tool-badge.error,html.dark .ai-chat-tool-badge.error{background:rgba(239,68,68,.2);color:#f87171}@keyframes ai-chat-skeleton-pulse{0%,to{opacity:.4}50%{opacity:.7}}.ai-chat-action-skeleton-item{animation:ai-chat-skeleton-pulse 1.5s ease-in-out infinite;background:var(--bg-secondary,#e5e7eb)}.ai-chat-widget.dark .ai-chat-action-skeleton-item,.chakra-ui-dark .ai-chat-action-skeleton-item,.dark .ai-chat-action-skeleton-item,[data-theme=dark] .ai-chat-action-skeleton-item{background:hsla(0,0%,100%,.1)}.ai-chat-action-skeleton-content{display:flex;flex-direction:column;gap:16px}.ai-chat-action-skeleton-header{align-items:center;display:flex;gap:10px}.ai-chat-action-skeleton-box{background:rgba(0,0,0,.08);border-radius:10px;display:flex;flex-direction:column;gap:8px;padding:14px}.ai-chat-widget.dark .ai-chat-action-skeleton-box,.chakra-ui-dark .ai-chat-action-skeleton-box,.dark .ai-chat-action-skeleton-box,[data-theme=dark] .ai-chat-action-skeleton-box{background:rgba(0,0,0,.25)}.ai-chat-action-card{--action-accent:var(--primary-color,#3b82f6);background:var(--bg-secondary,#f4f4f4);border:none;border-radius:var(--radius-lg,12px);box-sizing:border-box;margin-top:var(--space-sm,8px);max-width:100%;padding:var(--space-md,16px);transition:all var(--duration-normal,.25s) ease;width:100%}.ai-chat-widget.dark .ai-chat-action-card,.chakra-ui-dark .ai-chat-action-card,.dark .ai-chat-action-card,[data-theme=dark] .ai-chat-action-card{background:var(--bg-secondary,#3a3a3a)}.ai-chat-action-booked{background:var(--bg-secondary,#f4f4f4);border:none}.ai-chat-widget.dark .ai-chat-action-booked,.chakra-ui-dark .ai-chat-action-booked,.dark .ai-chat-action-booked,[data-theme=dark] .ai-chat-action-booked{background:var(--bg-secondary,#3a3a3a)}.ai-chat-action-header{align-items:center;color:var(--text-primary,#3e3e3e);display:flex;font-size:var(--text-md,15px);font-weight:var(--font-weight-semibold,600);gap:var(--space-sm,8px);margin-bottom:var(--space-md,16px)}.ai-chat-widget.dark .ai-chat-action-header,.chakra-ui-dark .ai-chat-action-header,.dark .ai-chat-action-header,[data-theme=dark] .ai-chat-action-header{color:var(--text-primary,#fff)}.ai-chat-action-icon{color:var(--action-accent,var(--primary-color,#3b82f6));flex-shrink:0;height:20px;width:20px}.ai-chat-action-success-icon-wrapper{align-items:center;background:var(--action-accent,var(--primary-color,#22c55e));border-radius:50%;color:#fff;display:flex;flex-shrink:0;height:24px;justify-content:center;width:24px}.ai-chat-action-icon-success{color:currentColor;height:14px;width:14px}.ai-chat-action-detail-box{background:var(--bg-primary,#fff);border:1px solid var(--border-subtle,rgba(0,0,0,.08));border-radius:var(--radius-md,8px);display:flex;flex-direction:column;gap:4px;padding:12px 16px}.ai-chat-widget.dark .ai-chat-action-detail-box,.chakra-ui-dark .ai-chat-action-detail-box,.dark .ai-chat-action-detail-box,[data-theme=dark] .ai-chat-action-detail-box{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.05)}.ai-chat-action-label-small{color:var(--text-muted,#71717a);font-size:11px;font-weight:600;letter-spacing:.5px;text-transform:uppercase}.ai-chat-action-value-large{color:var(--text-primary,#3e3e3e);font-size:15px;font-weight:500}.ai-chat-widget.dark .ai-chat-action-value-large,.chakra-ui-dark .ai-chat-action-value-large,.dark .ai-chat-action-value-large,[data-theme=dark] .ai-chat-action-value-large{color:#fff}.ai-chat-action-link-button{align-items:center;background:var(--action-accent,var(--primary-color,#3b82f6));border:none;border-radius:9999px;box-sizing:border-box;color:#fff;display:flex;font-size:14px;font-weight:600;gap:6px;justify-content:center;margin-top:8px;padding:12px;text-decoration:none;transition:all .2s ease;width:100%}.ai-chat-action-link-button:hover{opacity:.9;transform:translateY(-1px)}.ai-chat-action-body{display:flex;flex-direction:column;gap:var(--space-md,16px)}.ai-chat-action-field{display:flex;flex-direction:column;gap:var(--space-xs,6px)}.ai-chat-action-label{color:var(--text-secondary,#6b7280);font-size:var(--text-sm,13px);font-weight:var(--font-weight-medium,500)}.ai-chat-widget.dark .ai-chat-action-label,.chakra-ui-dark .ai-chat-action-label,.dark .ai-chat-action-label,[data-theme=dark] .ai-chat-action-label{color:var(--text-secondary,#a1a1aa)}.ai-chat-action-input{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:var(--radius-md,8px);color:var(--text-primary,#3e3e3e);font-size:var(--text-sm,13px);outline:none;padding:10px 12px;transition:border-color .2s ease,box-shadow .2s ease}.ai-chat-action-input:focus{border-color:var(--action-accent,var(--primary-color,#3b82f6));box-shadow:0 0 0 3px rgba(59,130,246,.1)}.ai-chat-action-input::placeholder{color:var(--text-muted,#9ca3af)}.ai-chat-widget.dark .ai-chat-action-input,.chakra-ui-dark .ai-chat-action-input,.dark .ai-chat-action-input,[data-theme=dark] .ai-chat-action-input{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-action-input:focus,.chakra-ui-dark .ai-chat-action-input:focus,.dark .ai-chat-action-input:focus,[data-theme=dark] .ai-chat-action-input:focus{border-color:var(--action-accent,var(--primary-color,#3b82f6));box-shadow:0 0 0 3px rgba(59,130,246,.2)}.ai-chat-action-date-grid{display:grid;gap:var(--space-xs,6px);grid-template-columns:repeat(auto-fill,minmax(90px,1fr))}.ai-chat-action-date-btn{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:var(--radius-md,8px);color:var(--text-primary,#3e3e3e);cursor:pointer;font-size:var(--text-xs,12px);font-weight:var(--font-weight-medium,500);padding:8px 10px;text-align:center;transition:all .15s ease}.ai-chat-action-date-btn:hover{background:rgba(59,130,246,.05);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-action-date-btn.active{background:var(--action-accent,var(--primary-color,#3b82f6));border-color:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-widget.dark .ai-chat-action-date-btn,.chakra-ui-dark .ai-chat-action-date-btn,.dark .ai-chat-action-date-btn,[data-theme=dark] .ai-chat-action-date-btn{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-action-date-btn:hover,.chakra-ui-dark .ai-chat-action-date-btn:hover,.dark .ai-chat-action-date-btn:hover,[data-theme=dark] .ai-chat-action-date-btn:hover{background:rgba(59,130,246,.15);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-widget.dark .ai-chat-action-date-btn.active,.chakra-ui-dark .ai-chat-action-date-btn.active,.dark .ai-chat-action-date-btn.active,[data-theme=dark] .ai-chat-action-date-btn.active{background:var(--action-accent,var(--primary-color,#3b82f6));border-color:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-action-time-grid{display:grid;gap:var(--space-xs,6px);grid-template-columns:repeat(auto-fill,minmax(100px,1fr))}.ai-chat-action-time-btn{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:var(--radius-md,8px);color:var(--text-primary,#3e3e3e);cursor:pointer;font-size:var(--text-xs,12px);font-weight:var(--font-weight-medium,500);padding:8px 10px;text-align:center;transition:all .15s ease}.ai-chat-action-time-btn:hover{background:rgba(59,130,246,.05);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-action-time-btn.active{background:var(--action-accent,var(--primary-color,#3b82f6));border-color:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-widget.dark .ai-chat-action-time-btn,.chakra-ui-dark .ai-chat-action-time-btn,.dark .ai-chat-action-time-btn,[data-theme=dark] .ai-chat-action-time-btn{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-action-time-btn:hover,.chakra-ui-dark .ai-chat-action-time-btn:hover,.dark .ai-chat-action-time-btn:hover,[data-theme=dark] .ai-chat-action-time-btn:hover{background:rgba(59,130,246,.15);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-widget.dark .ai-chat-action-time-btn.active,.chakra-ui-dark .ai-chat-action-time-btn.active,.dark .ai-chat-action-time-btn.active,[data-theme=dark] .ai-chat-action-time-btn.active{background:var(--action-accent,var(--primary-color,#3b82f6));border-color:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-action-button{background:var(--action-accent,var(--primary-color,#3b82f6));border:none;border-radius:9999px;color:#fff;cursor:pointer;font-size:var(--text-sm,13px);font-weight:var(--font-weight-semibold,600);padding:12px 16px;transition:all .2s ease;width:100%}.ai-chat-action-button:hover:not(:disabled){opacity:.9;transform:translateY(-1px)}.ai-chat-action-button:disabled{cursor:not-allowed;opacity:.5}.ai-chat-action-error{background:rgba(239,68,68,.1);border-radius:var(--radius-md,8px);color:#dc2626;font-size:var(--text-sm,13px);padding:10px 12px}.ai-chat-widget.dark .ai-chat-action-error,.chakra-ui-dark .ai-chat-action-error,.dark .ai-chat-action-error,[data-theme=dark] .ai-chat-action-error{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-action-hint{color:var(--text-muted,#9ca3af);font-size:var(--text-sm,13px);padding:var(--space-sm,8px);text-align:center}.ai-chat-link-preview{--action-accent:var(--primary-color,#3b82f6);background:var(--bg-secondary,#f4f4f4);border:1px solid var(--border-subtle,rgba(0,0,0,.08));border-radius:var(--radius-lg,12px);cursor:pointer;display:flex;flex-direction:column;margin-top:var(--space-sm,8px);overflow:hidden;position:relative;transition:border-color .2s,box-shadow .2s,transform .2s}.ai-chat-link-preview:hover{border-color:var(--action-accent);box-shadow:0 2px 8px rgba(0,0,0,.1);transform:translateY(-1px)}.ai-chat-link-preview:focus{border-color:var(--action-accent);box-shadow:0 0 0 2px rgba(59,130,246,.2);outline:none}.ai-chat-widget.dark .ai-chat-link-preview,.chakra-ui-dark .ai-chat-link-preview,.dark .ai-chat-link-preview,[data-theme=dark] .ai-chat-link-preview{background:var(--bg-secondary,#3a3a3a);border-color:hsla(0,0%,100%,.08)}.ai-chat-widget.dark .ai-chat-link-preview:hover,.chakra-ui-dark .ai-chat-link-preview:hover,.dark .ai-chat-link-preview:hover,[data-theme=dark] .ai-chat-link-preview:hover{border-color:var(--action-accent);box-shadow:0 2px 12px rgba(0,0,0,.3)}.ai-chat-link-preview__image{aspect-ratio:1.91/1;background:var(--bg-muted,#e5e7eb);overflow:hidden;width:100%}.ai-chat-widget.dark .ai-chat-link-preview__image,.chakra-ui-dark .ai-chat-link-preview__image,.dark .ai-chat-link-preview__image,[data-theme=dark] .ai-chat-link-preview__image{background:hsla(0,0%,100%,.05)}.ai-chat-link-preview__image img{height:100%;object-fit:cover;width:100%}.ai-chat-link-preview__content{flex:1;padding:12px}.ai-chat-link-preview__site{align-items:center;display:flex;gap:6px;margin-bottom:6px}.ai-chat-link-preview__favicon{border-radius:2px;flex-shrink:0;height:16px;width:16px}.ai-chat-link-preview__domain{color:var(--text-muted,#71717a);font-size:12px;letter-spacing:.5px;overflow:hidden;text-overflow:ellipsis;text-transform:uppercase;white-space:nowrap}.ai-chat-link-preview__title{-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;color:var(--text-primary,#3e3e3e);display:-webkit-box;font-size:15px;font-weight:600;line-height:1.3;margin:0 0 4px;overflow:hidden}.ai-chat-widget.dark .ai-chat-link-preview__title,.chakra-ui-dark .ai-chat-link-preview__title,.dark .ai-chat-link-preview__title,[data-theme=dark] .ai-chat-link-preview__title{color:#fff}.ai-chat-link-preview__description{-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;color:var(--text-muted,#71717a);display:-webkit-box;font-size:13px;line-height:1.4;margin:0;overflow:hidden}.ai-chat-link-preview__context{border-top:1px solid var(--border-subtle,rgba(0,0,0,.08));color:var(--text-muted,#71717a);font-size:12px;font-style:italic;margin:8px 0 0;padding-top:8px}.ai-chat-widget.dark .ai-chat-link-preview__context,.chakra-ui-dark .ai-chat-link-preview__context,.dark .ai-chat-link-preview__context,[data-theme=dark] .ai-chat-link-preview__context{border-color:hsla(0,0%,100%,.08)}.ai-chat-link-preview__arrow{align-items:center;background:var(--bg-primary,#fff);border-radius:50%;box-shadow:0 1px 3px rgba(0,0,0,.1);color:var(--text-muted,#71717a);display:flex;height:28px;justify-content:center;opacity:0;position:absolute;right:12px;top:12px;transition:opacity .2s,background .2s;width:28px}.ai-chat-link-preview:hover .ai-chat-link-preview__arrow{opacity:1}.ai-chat-widget.dark .ai-chat-link-preview__arrow,.chakra-ui-dark .ai-chat-link-preview__arrow,.dark .ai-chat-link-preview__arrow,[data-theme=dark] .ai-chat-link-preview__arrow{background:hsla(0,0%,100%,.1);color:#fff}.ai-chat-link-preview--error{border-color:rgba(239,68,68,.3)}.ai-chat-link-preview--error:hover{border-color:rgba(239,68,68,.5)}.ai-chat-link-preview__error-text{color:#dc2626;font-size:12px;margin:4px 0 0}.ai-chat-widget.dark .ai-chat-link-preview__error-text,.chakra-ui-dark .ai-chat-link-preview__error-text,.dark .ai-chat-link-preview__error-text,[data-theme=dark] .ai-chat-link-preview__error-text{color:#fca5a5}.ai-chat-video-player{--action-accent:var(--primary-color,#3b82f6);background:var(--bg-secondary,#f4f4f4);border-radius:var(--radius-lg,12px);display:flex;flex-direction:column;margin-top:var(--space-sm,8px);overflow:hidden}.ai-chat-widget.dark .ai-chat-video-player,.chakra-ui-dark .ai-chat-video-player,.dark .ai-chat-video-player,[data-theme=dark] .ai-chat-video-player{background:var(--bg-secondary,#3a3a3a)}.ai-chat-video-player__header{align-items:center;color:var(--action-accent,var(--primary-color,#3b82f6));display:flex;gap:8px;padding:12px 12px 8px}.ai-chat-video-player__title{color:var(--text-primary,#3e3e3e);font-size:14px;font-weight:600}.ai-chat-widget.dark .ai-chat-video-player__title,.chakra-ui-dark .ai-chat-video-player__title,.dark .ai-chat-video-player__title,[data-theme=dark] .ai-chat-video-player__title{color:#fff}.ai-chat-video-player__container{aspect-ratio:16/9;background:#000;position:relative;width:100%}.ai-chat-video-player__thumbnail{cursor:pointer;height:100%;position:relative;width:100%}.ai-chat-video-player__thumbnail img{height:100%;object-fit:cover;width:100%}.ai-chat-video-player__placeholder{align-items:center;background:linear-gradient(135deg,#1a1a2e,#16213e);cursor:pointer;display:flex;flex-direction:column;gap:8px;height:100%;justify-content:center;position:relative;width:100%}.ai-chat-video-player__click-text{color:hsla(0,0%,100%,.7);font-size:13px}.ai-chat-video-player__play-btn{align-items:center;background:rgba(0,0,0,.7);border:none;border-radius:50%;color:#fff;cursor:pointer;display:flex;height:64px;justify-content:center;left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);transition:background .2s,transform .2s;width:64px}.ai-chat-video-player__placeholder .ai-chat-video-player__play-btn{left:auto;position:relative;top:auto;transform:none}.ai-chat-video-player__play-btn:hover{background:rgba(0,0,0,.9);transform:translate(-50%,-50%) scale(1.05)}.ai-chat-video-player__placeholder .ai-chat-video-player__play-btn:hover{transform:scale(1.05)}.ai-chat-video-player__provider-badge{background:rgba(0,0,0,.8);border-radius:4px;bottom:8px;color:#fff;font-size:11px;font-weight:600;letter-spacing:.5px;padding:4px 8px;position:absolute;right:8px;text-transform:uppercase}.ai-chat-video-player__iframe,.ai-chat-video-player__video{border:none;height:100%;left:0;position:absolute;top:0;width:100%}.ai-chat-video-player__error{align-items:center;background:rgba(239,68,68,.1);color:#dc2626;display:flex;font-size:13px;height:100%;justify-content:center;padding:16px;text-align:center;width:100%}.ai-chat-widget.dark .ai-chat-video-player__error,.chakra-ui-dark .ai-chat-video-player__error,.dark .ai-chat-video-player__error,[data-theme=dark] .ai-chat-video-player__error{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-video-player__context{border-top:1px solid var(--border-subtle,rgba(0,0,0,.08));color:var(--text-muted,#71717a);font-size:12px;font-style:italic;padding:10px 12px}.ai-chat-widget.dark .ai-chat-video-player__context,.chakra-ui-dark .ai-chat-video-player__context,.dark .ai-chat-video-player__context,[data-theme=dark] .ai-chat-video-player__context{border-color:hsla(0,0%,100%,.08)}.ai-chat-location-card{background:var(--bg-secondary,#fff);border:1px solid var(--border-subtle,rgba(0,0,0,.08));border-radius:12px;overflow:hidden}.ai-chat-widget.dark .ai-chat-location-card,.chakra-ui-dark .ai-chat-location-card,.dark .ai-chat-location-card,[data-theme=dark] .ai-chat-location-card{background:hsla(0,0%,100%,.05);border-color:hsla(0,0%,100%,.1)}.ai-chat-location-card--compact{border-radius:10px}.ai-chat-location-card--error{color:var(--text-muted,#71717a);padding:16px;text-align:center}.ai-chat-location-card__map{background:var(--bg-muted,#f4f4f5);position:relative;width:100%}.ai-chat-widget.dark .ai-chat-location-card__map,.chakra-ui-dark .ai-chat-location-card__map,.dark .ai-chat-location-card__map,[data-theme=dark] .ai-chat-location-card__map{background:rgba(0,0,0,.2)}.ai-chat-location-card__map iframe{border:none;display:block;height:100%;width:100%}.ai-chat-location-card__content{padding:14px}.ai-chat-location-card--compact .ai-chat-location-card__content{padding:12px}.ai-chat-location-card__header{align-items:center;display:flex;flex-wrap:wrap;gap:8px;margin-bottom:8px}.ai-chat-location-card__name{color:var(--text-primary,#18181b);flex:1;font-size:16px;font-weight:600;margin:0;min-width:0}.ai-chat-widget.dark .ai-chat-location-card__name,.chakra-ui-dark .ai-chat-location-card__name,.dark .ai-chat-location-card__name,[data-theme=dark] .ai-chat-location-card__name{color:#fff}.ai-chat-location-card--compact .ai-chat-location-card__name{font-size:14px}.ai-chat-location-card__type{background:var(--bg-muted,#f4f4f5);border-radius:10px;color:var(--text-muted,#71717a);font-size:11px;font-weight:500;letter-spacing:.5px;padding:2px 8px;text-transform:uppercase}.ai-chat-widget.dark .ai-chat-location-card__type,.chakra-ui-dark .ai-chat-location-card__type,.dark .ai-chat-location-card__type,[data-theme=dark] .ai-chat-location-card__type{background:hsla(0,0%,100%,.1);color:hsla(0,0%,100%,.7)}.ai-chat-location-card__status{border-radius:12px;font-size:12px;font-weight:500;padding:2px 8px}.ai-chat-location-card__status--open{background:#dcfce7;color:#16a34a}.ai-chat-location-card__status--closed{background:#fef2f2;color:#dc2626}.ai-chat-widget.dark .ai-chat-location-card__status--open,.chakra-ui-dark .ai-chat-location-card__status--open,.dark .ai-chat-location-card__status--open,[data-theme=dark] .ai-chat-location-card__status--open{background:rgba(34,197,94,.2);color:#4ade80}.ai-chat-widget.dark .ai-chat-location-card__status--closed,.chakra-ui-dark .ai-chat-location-card__status--closed,.dark .ai-chat-location-card__status--closed,[data-theme=dark] .ai-chat-location-card__status--closed{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-location-card__address{align-items:flex-start;color:var(--text-muted,#71717a);display:flex;font-size:13px;gap:6px;line-height:1.4;margin:0 0 8px}.ai-chat-location-card__address svg{flex-shrink:0;margin-top:2px}.ai-chat-location-card__description{color:var(--text-muted,#71717a);font-size:13px;line-height:1.4;margin:0 0 8px}.ai-chat-location-card__hours{align-items:flex-start;color:var(--text-muted,#71717a);display:flex;font-size:13px;gap:6px;margin-bottom:8px}.ai-chat-location-card__hours svg{flex-shrink:0;margin-top:2px}.ai-chat-location-card__hours-list{flex:1}.ai-chat-location-card__hours-toggle{align-items:center;background:none;border:none;color:inherit;cursor:pointer;display:flex;font:inherit;gap:4px;padding:0}.ai-chat-location-card__hours-toggle:hover{text-decoration:underline}.ai-chat-location-card__hours-full{list-style:none;margin:8px 0 0;padding:0}.ai-chat-location-card__hours-full li{display:flex;font-size:12px;justify-content:space-between;padding:4px 0}.ai-chat-location-card__hours-today{color:var(--text-primary,#18181b);font-weight:600}.ai-chat-widget.dark .ai-chat-location-card__hours-today,.chakra-ui-dark .ai-chat-location-card__hours-today,.dark .ai-chat-location-card__hours-today,[data-theme=dark] .ai-chat-location-card__hours-today{color:#fff}.ai-chat-location-card__phone{align-items:center;background:none;border:none;color:var(--action-accent,#3b82f6);cursor:pointer;display:flex;font-size:13px;gap:6px;margin-bottom:12px;padding:0}.ai-chat-location-card__phone:hover{text-decoration:underline}.ai-chat-location-card__actions{display:flex;gap:8px;justify-content:flex-start;width:100%}.ai-chat-location-card__button{align-items:center;background:var(--action-accent,#3b82f6);border:none;border-radius:20px;color:#fff;cursor:pointer;display:flex;flex:1;font-size:13px;font-weight:500;gap:6px;justify-content:center;padding:10px 16px;transition:opacity .2s}.ai-chat-location-card__button:hover{opacity:.9}.ai-chat-location-card--compact .ai-chat-location-card__button{font-size:12px;padding:8px 12px}.ai-chat-location-card__link{align-items:center;background:var(--bg-secondary,#fff);border:1px solid var(--border-subtle,rgba(0,0,0,.08));border-radius:20px;color:var(--text-primary,#18181b);display:flex;font-size:13px;gap:6px;padding:10px 16px;text-decoration:none;transition:border-color .2s}.ai-chat-widget.dark .ai-chat-location-card__link,.chakra-ui-dark .ai-chat-location-card__link,.dark .ai-chat-location-card__link,[data-theme=dark] .ai-chat-location-card__link{background:hsla(0,0%,100%,.05);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-location-card__link:hover{border-color:var(--action-accent,#3b82f6)}.ai-chat-location-card-list{display:flex;flex-direction:column;gap:8px}.ai-chat-location-card-list__header{align-items:center;color:var(--text-muted,#71717a);display:flex;font-size:13px;font-weight:500;gap:6px;margin-bottom:4px;padding:0 4px}.ai-chat-location-card-list__stack{display:grid;gap:12px;grid-template-columns:repeat(3,minmax(0,1fr))}@media (max-width:1200px){.ai-chat-location-card-list__stack{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (max-width:768px){.ai-chat-location-card-list__stack{grid-template-columns:1fr}}@media (max-width:520px){.ai-chat-location-card-list__stack{grid-template-columns:1fr!important}}@media (min-width:521px) and (max-width:900px){.ai-chat-location-card-list__stack{grid-template-columns:repeat(2,minmax(0,1fr))!important}}.ai-chat-location-card-list__grid{display:grid;gap:8px;grid-template-columns:repeat(2,1fr)}@media (max-width:400px){.ai-chat-location-card-list__grid{grid-template-columns:1fr}}.ai-chat-location-card-list__carousel{-webkit-overflow-scrolling:touch;-ms-overflow-style:none;display:flex;gap:8px;overflow-x:auto;padding-bottom:4px;scroll-snap-type:x mandatory;scrollbar-width:none}.ai-chat-location-card-list__carousel::-webkit-scrollbar{display:none}.ai-chat-location-card-list__carousel>.ai-chat-location-card{flex:0 0 280px;scroll-snap-align:start}.ai-chat-widget,.chat-ui{--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,.chat-ui.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,.chat-ui{-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: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 ai-chat-window-open{0%{opacity:0;transform:scale(.9) translateY(20px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes ai-chat-window-close{0%{opacity:1;transform:scale(1) translateY(0)}to{opacity:0;transform:scale(.9) translateY(20px)}}@keyframes ai-chat-message-slide-in{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}@keyframes ai-chat-welcome-fade-in{0%{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}@keyframes ai-chat-typing-pulse{0%,60%,to{opacity:.4;transform:translateY(0) scale(1)}30%{opacity:1;transform:translateY(-4px) scale(1.1)}}@keyframes ai-chat-gear-spin{to{transform:rotate(1turn)}}@keyframes ai-chat-tool-active{0%,to{background:var(--bg-secondary);opacity:1}50%{background:var(--bg-tertiary);opacity:1}}@keyframes ai-chat-feedback-morph{0%{opacity:.5;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@keyframes ai-chat-checkmark-pop{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)}}@keyframes ai-chat-tool-gradient{0%{background-position:200% 0}to{background-position:-200% 0}}@media (max-width:480px){.ai-chat-widget-container.is-open{height:100vh!important;inset:0!important;width:100vw!important}.ai-chat-widget-container.is-open .ai-chat-window{animation:none!important;border-radius:0!important;height:100%!important;inset:0!important;max-height:100%!important;max-width:100%!important;position:absolute!important;width:100%!important}.ai-chat-widget-container.is-open .ai-chat-button{display:none!important;visibility:hidden!important}.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-window{animation:ai-chat-window-open var(--duration-slow,.35s) var(--spring-bounce);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:ai-chat-window-close var(--duration-normal) var(--spring-smooth) forwards}.ai-chat-window.size-small{height:500px;width:380px}.ai-chat-window.size-medium,.ai-chat-window.size-small{max-height:calc(100vh - 100px);max-width:calc(100vw - 40px)}.ai-chat-window.size-medium{height:600px;width:420px}.ai-chat-window.size-large{height:700px;max-height:calc(100vh - 100px);max-width:calc(100vw - 40px);width:480px}.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,.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:color var(--duration-fast) ease;width:32px}.ai-chat-close-button:hover,.ai-chat-header-button:hover{color:var(--text-primary)}.ai-chat-close-button:active,.ai-chat-header-button:active{transform:scale(.95)}.ai-chat-close-button svg,.ai-chat-header-button svg{height:22px;width:22px}.ai-chat-button{align-items:center;background:var(--button-color,var(--btn-primary-bg));border:1px solid var(--border-default,#d3d3d3);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:opacity var(--duration-fast) ease;width:var(--button-size,56px);z-index:1}.ai-chat-button:hover{opacity:.9}.ai-chat-button:active{opacity:.8}.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-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-end;background:var(--input-bg,#f4f4f4);border:1px solid var(--input-border,#d3d3d3);border-radius:var(--radius-input,62px);box-sizing:border-box;display:flex;gap:0;height:52px;overflow:hidden;padding:6px 6px 6px 16px;position:relative;transition:all var(--duration-fast,.15s) ease;z-index:5}.ai-chat-input-wrapper.multiline{border-radius:14px!important;min-height:64px;padding:10px 10px 10px 14px}.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!important;background:transparent!important;border:none!important;border-radius:0!important;box-shadow:none!important;box-sizing:border-box!important;color:var(--input-text,#000)!important;flex:1!important;font-family:inherit!important;font-size:var(--text-md,15px)!important;height:40px!important;line-height:20px!important;margin:0!important;max-height:40px!important;min-height:40px!important;min-width:0!important;outline:none!important;overflow-wrap:anywhere!important;overflow-x:hidden!important;overflow-y:auto!important;padding:10px var(--space-sm,8px)!important;resize:none!important;white-space:pre-wrap!important;width:0!important;word-break:break-word!important}.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;min-height:40px;min-width:40px;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-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:ai-chat-message-slide-in .2s var(--spring-bounce);display:flex;flex-direction:column;max-width:90%}.ai-chat-message.user{align-items:flex-end;align-self:flex-end}.ai-chat-message.assistant{align-items:flex-start;align-self:flex-start;max-width:100%}.ai-chat-message.tool{align-self:flex-start;margin:0 -16px;max-width:100%;padding:0;width:calc(100% + 32px)}.ai-chat-message-content{word-wrap:break-word;border-radius:18px;font-size:var(--text-md,15px);line-height:var(--line-height-relaxed,1.6);overflow-wrap:break-word;padding:8px 14px}.ai-chat-message.user .ai-chat-message-content{background:var(--user-bg,#f4f3f0);border-radius:18px;color:var(--user-text,#000)}.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.assistant .ai-chat-message-content{background:var(--agent-bg,transparent);color:var(--agent-text,#000);padding:0}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content{color:var(--agent-text,#fff)}.ai-chat-message-timestamp{color:var(--text-muted,#71717a);font-size:var(--text-xs,12px);margin-top:var(--space-xs,4px);padding:0 var(--space-xs,4px)}.ai-chat-welcome{animation:ai-chat-welcome-fade-in .3s var(--spring-smooth);display:flex;flex-direction:column;gap:var(--space-md,16px);padding:var(--space-lg,24px) 0}.ai-chat-welcome-title{color:var(--text-primary,#3e3e3e);font-size:var(--text-2xl,28px);font-weight:var(--font-weight-bold,700);line-height:var(--line-height-tight,1.3)}.ai-chat-widget.dark .ai-chat-welcome-title{color:var(--text-primary,#fff)}.ai-chat-welcome-text{color:var(--text-secondary,#000);font-size:var(--text-md,15px);line-height:var(--line-height-relaxed,1.6);max-width:100%}.ai-chat-widget.dark .ai-chat-welcome-text{color:var(--text-secondary,#fff)}.ai-chat-typing{align-items:center;display:flex;gap:var(--space-xs,4px);padding:var(--space-sm,8px) var(--space-md,16px)}.ai-chat-typing-dot{animation:ai-chat-typing-bounce 1.4s ease-in-out infinite both;background:var(--text-muted,#71717a);border-radius:50%;height:8px;width:8px}.ai-chat-typing-dot:first-child{animation-delay:-.32s}.ai-chat-typing-dot:nth-child(2){animation-delay:-.16s}.ai-chat-typing-dot:nth-child(3){animation-delay:0s}@keyframes ai-chat-typing-bounce{0%,80%,to{opacity:.4;transform:scale(.6)}40%{opacity:1;transform:scale(1)}}.ai-chat-scroll-button{align-items:center;background:var(--bg-secondary,#f4f4f5);border:1px solid var(--border-subtle,rgba(0,0,0,.1));border-radius:50%;bottom:80px;box-shadow:0 2px 8px rgba(0,0,0,.1);color:var(--text-secondary,#71717a);cursor:pointer;display:flex;height:36px;justify-content:center;left:50%;opacity:0;pointer-events:none;position:absolute;transform:translateX(-50%);transition:background .15s ease,box-shadow .15s ease,opacity .15s ease,visibility .15s ease;visibility:hidden;width:36px;z-index:15}.ai-chat-scroll-button.visible{opacity:1;pointer-events:auto;visibility:visible}.ai-chat-scroll-button:hover{background:var(--bg-tertiary,#e4e4e7);box-shadow:0 4px 12px rgba(0,0,0,.15)}.ai-chat-scroll-button:active{background:var(--bg-tertiary,#d4d4d8)}.ai-chat-widget.dark .ai-chat-scroll-button{background:var(--bg-secondary,#3f3f46);border-color:var(--border-subtle,hsla(0,0%,100%,.1));box-shadow:0 2px 8px rgba(0,0,0,.3);color:var(--text-secondary,#a1a1aa)}.ai-chat-widget.dark .ai-chat-scroll-button:hover{background:var(--bg-tertiary,#52525b);box-shadow:0 4px 12px rgba(0,0,0,.4)}.ai-chat-error{background:var(--bg-secondary);border-radius:var(--radius-chat-bubble);color:var(--text-primary);font-size:var(--text-md);margin:0 auto;padding:10px var(--space-md)}.ai-chat-message.assistant .ai-chat-message-content p{margin:0 0 var(--space-sm) 0}.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:var(--space-sm) 0;padding-left:var(--space-lg)}.ai-chat-message.assistant .ai-chat-message-content li{margin-bottom:var(--space-xs)}.ai-chat-message.assistant .ai-chat-message-content code{background:rgba(0,0,0,.05);border-radius:var(--radius-sm);font-family:SF Mono,Monaco,Cascadia Code,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,.05);border-radius:var(--radius-md);margin:var(--space-sm) 0;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%,.05)}.ai-chat-message.assistant .ai-chat-message-content pre code{background:transparent;padding:0}.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 strong{font-weight:var(--font-weight-semibold)}.ai-chat-message.assistant .ai-chat-message-content blockquote{border-left:3px solid var(--border-default);color:var(--text-muted);margin:var(--space-sm) 0;padding-left:var(--space-md)}.ai-chat-message.assistant .ai-chat-message-content hr{border:none;border-top:1px solid var(--border-subtle,rgba(0,0,0,.1));margin:var(--space-lg,24px) 0}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content hr{border-top-color:var(--border-subtle,hsla(0,0%,100%,.1))}.ai-chat-message.assistant .ai-chat-message-content table{border-collapse:collapse;font-size:var(--text-sm);margin:var(--space-sm) 0;width:100%}.ai-chat-message.assistant .ai-chat-message-content td,.ai-chat-message.assistant .ai-chat-message-content th{border:1px solid var(--border-subtle);padding:var(--space-sm);text-align:left}.ai-chat-message.assistant .ai-chat-message-content th{font-weight:var(--font-weight-semibold);white-space:nowrap}.ai-chat-message.assistant .ai-chat-message-content tbody tr:nth-child(2n){background:rgba(0,0,0,.02)}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content tbody tr:nth-child(2n){background:hsla(0,0%,100%,.03)}.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{align-items:center;background:transparent;border:1px solid var(--border-default,#d4d4d8);border-radius:var(--radius-preset-badge,18px);color:var(--text-primary,#18181b);cursor:pointer;display:inline-flex;font-size:14px;font-weight:400;gap:6px;justify-content:center;line-height:1.3;max-width:100%;overflow:hidden;padding:8px 14px;text-overflow:ellipsis;transition:background .15s ease,border-color .15s ease,transform .1s ease;white-space:nowrap}.ai-chat-widget.dark .ai-chat-suggested-question{background:transparent;border-color:var(--border-subtle,#52525b);color:var(--text-primary,#fafafa)}.ai-chat-suggested-question-text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ai-chat-suggested-question:hover{background:var(--bg-hover,#f4f4f5);border-color:var(--border-default,#d4d4d8)}.ai-chat-widget.dark .ai-chat-suggested-question:hover{background:var(--bg-hover,#3f3f46);border-color:var(--border-subtle,#52525b)}.ai-chat-suggested-question:active{transform:scale(.98)}.ai-chat-suggested-question.action-type{border:none}.ai-chat-suggested-question.action-type,.ai-chat-widget.dark .ai-chat-suggested-question.action-type{background:var(--primary-color,var(--button-color,#ef4444));color:var(--button-icon-color,#fff)}.ai-chat-suggested-question.action-type:hover{background:var(--primary-color,var(--button-color,#ef4444));opacity:.9}.ai-chat-suggested-question-icon{align-items:center;display:flex;flex-shrink:0;justify-content:center}.ai-chat-suggested-question:not(.action-type) .ai-chat-suggested-question-icon{display:none}.ai-chat-follow-up-suggestions{box-sizing:border-box;margin:0;padding:8px 16px 0;width:100%}.ai-chat-follow-up-list{align-items:flex-end;display:flex;flex-direction:column;gap:6px}.ai-chat-follow-up-item{align-items:center;border:none;border-radius:var(--radius-preset-badge,18px);cursor:pointer;display:inline-flex;font-size:14px;font-weight:400;gap:6px;justify-content:center;line-height:1.3;max-width:100%;overflow:hidden;padding:8px 14px;text-overflow:ellipsis;transition:opacity .15s ease,transform .1s ease;white-space:nowrap}.ai-chat-follow-up-item,.ai-chat-widget.dark .ai-chat-follow-up-item{background:var(--primary-color,var(--button-color,#07f));color:var(--button-icon-color,#fff)}.ai-chat-follow-up-item:hover{opacity:.9}.ai-chat-follow-up-item:active{transform:scale(.98)}.ai-chat-follow-up-item.question-type{background:transparent;border:1px solid var(--border-default,#d4d4d8);color:var(--text-primary,#18181b)}.ai-chat-widget.dark .ai-chat-follow-up-item.question-type,.dark .ai-chat-follow-up-item.question-type,[data-theme=dark] .ai-chat-follow-up-item.question-type{background:transparent;border:1px solid var(--border-subtle,#52525b);color:var(--text-primary,#fafafa)}@media (prefers-color-scheme:dark){.ai-chat-follow-up-item.question-type{background:transparent;border:1px solid var(--border-subtle,#52525b);color:var(--text-primary,#fafafa)}}.ai-chat-follow-up-item.question-type:hover{background:var(--bg-hover,#f4f4f5);opacity:1}.ai-chat-widget.dark .ai-chat-follow-up-item.question-type:hover,.dark .ai-chat-follow-up-item.question-type:hover,[data-theme=dark] .ai-chat-follow-up-item.question-type:hover{background:var(--bg-hover,#3f3f46);opacity:1}@media (prefers-color-scheme:dark){.ai-chat-follow-up-item.question-type:hover{background:var(--bg-hover,#3f3f46);opacity:1}}.ai-chat-follow-up-item.action-type{background:var(--primary-color,var(--button-color,#07f));border:none;color:var(--button-icon-color,#fff)}.ai-chat-follow-up-icon{align-items:center;display:flex;flex-shrink:0;justify-content:center}.ai-chat-follow-up-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.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:ai-chat-feedback-morph .3s var(--spring-bounce);gap:var(--space-xs)}.ai-chat-feedback-message{align-items:center;display:flex;gap:4px;margin-left:var(--space-xxs)}.ai-chat-feedback-checkmark{animation:ai-chat-checkmark-pop .3s var(--spring-bounce);color:#10b981;font-size:var(--text-md);font-weight:700}.ai-chat-feedback-text{color:#10b981;font-size:var(--text-xs);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-xs) 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,36px);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 var(--space-xs,4px) 0 var(--space-md,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;border-radius:var(--radius-sm,6px);color:var(--text-muted,#71717a);cursor:pointer;display:flex;flex-shrink:0;height:24px;justify-content:center;margin-left:auto;margin-right:var(--space-xs,4px);opacity:0;transition:opacity var(--duration-fast,.15s) ease,background var(--duration-fast,.15s) ease,color var(--duration-fast,.15s) ease;width:24px}.ai-chat-history-item-delete svg{height:14px;width:14px}.ai-chat-history-item:hover .ai-chat-history-item-delete{opacity:1}.ai-chat-history-item-delete:hover{background:rgba(239,68,68,.1);color:#ef4444}.ai-chat-widget.dark .ai-chat-history-item-delete:hover{background:rgba(239,68,68,.2);color:#f87171}.ai-chat-tool-row{align-items:center;display:flex;gap:10px;margin:2px 0;padding:0 16px}.ai-chat-tool-gear{color:#1f2937;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;background:#e5e7eb;border:1px solid #d1d5db;border-radius:var(--radius-action-badge,8px);color:#1f2937;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:#e5e7eb;border:1px solid #d1d5db;color:#1f2937}.ai-chat-widget.dark .ai-chat-tool-badge,.ai-chat-widget[data-theme=dark] .ai-chat-tool-badge,.chakra-ui-dark .ai-chat-tool-badge,.dark .ai-chat-tool-badge,[data-theme=dark] .ai-chat-tool-badge,html.dark .ai-chat-tool-badge{background:hsla(0,0%,100%,.12);border:1px solid hsla(0,0%,100%,.2);color:hsla(0,0%,100%,.9)}.ai-chat-widget.dark .ai-chat-tool-gear,.ai-chat-widget[data-theme=dark] .ai-chat-tool-gear,.chakra-ui-dark .ai-chat-tool-gear,.dark .ai-chat-tool-gear,[data-theme=dark] .ai-chat-tool-gear,html.dark .ai-chat-tool-gear{color:#fff}.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:#fff;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}.ai-chat-tool-action{box-sizing:border-box;padding:0 16px;width:100%}";
|
|
30848
|
+
var css_248z$1 = ".ai-chat-message{animation:ai-chat-message-appear .2s var(--chat-ease-bounce);max-width:85%}.ai-chat-message-content{border-radius:var(--chat-radius-bubble,14px);font-size:var(--chat-text-md,15px);line-height:var(--chat-line-relaxed,1.6);padding:var(--chat-space-sm,8px) var(--chat-space-md,16px)}.ai-chat-message.user .ai-chat-message-content{background:var(--chat-user-bg,#f4f3f0);border-bottom-right-radius:var(--chat-radius-sm,4px);color:var(--chat-user-text,#000)}.ai-chat-message.assistant .ai-chat-message-content{background:var(--chat-assistant-bg,transparent);color:var(--chat-assistant-text,#000)}.ai-chat-message-timestamp{color:var(--chat-text-muted,#71717a);font-size:var(--chat-text-xs,12px);margin-top:var(--chat-space-xs,4px);padding:0 var(--chat-space-xs,4px)}.ai-chat-message.streaming .ai-chat-message-content:after{animation:ai-chat-cursor-blink .8s infinite;content:\"▋\";margin-left:2px;opacity:.7}@keyframes ai-chat-message-appear{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes ai-chat-cursor-blink{0%,50%{opacity:1}51%,to{opacity:0}}.ai-chat-message.fullpage .ai-chat-message-content{font-size:var(--chat-text-lg,18px);padding:var(--chat-space-md,16px) var(--chat-space-lg,24px)}.ai-chat-typing{gap:var(--chat-space-xs,4px);padding:var(--chat-space-sm,8px) var(--chat-space-md,16px)}.ai-chat-typing-dot{background:var(--chat-text-muted,#71717a)}.ai-chat-tool-row{padding:0 16px}.ai-chat-tool-gear{color:var(--text-primary,#3e3e3e)}.ai-chat-tool-badge{border-radius:8px}.ai-chat-tool-badge.loading{background:#e5e7eb;border:1px solid #d1d5db;color:#1f2937}.ai-chat-tool-badge.error{background:rgba(239,68,68,.1);color:#ef4444}.ai-chat-tool-check{color:#22c55e;flex-shrink:0}.ai-chat-tool-error{color:#ef4444;flex-shrink:0}.ai-chat-tool-badge .tool-name{max-width:150px;overflow:hidden;text-overflow:ellipsis}.ai-chat-widget.dark .ai-chat-tool-gear,.ai-chat-widget[data-theme=dark] .ai-chat-tool-gear,.dark .ai-chat-tool-gear,[data-theme=dark] .ai-chat-tool-gear{color:#fff}.ai-chat-widget.dark .ai-chat-tool-badge,.ai-chat-widget[data-theme=dark] .ai-chat-tool-badge,.dark .ai-chat-tool-badge,[data-theme=dark] .ai-chat-tool-badge{background:hsla(0,0%,100%,.12);border:1px solid hsla(0,0%,100%,.2);color:hsla(0,0%,100%,.9)}.ai-chat-widget.dark .ai-chat-tool-badge.error,.ai-chat-widget[data-theme=dark] .ai-chat-tool-badge.error,.dark .ai-chat-tool-badge.error,[data-theme=dark] .ai-chat-tool-badge.error{background:rgba(239,68,68,.2);color:#f87171}.chakra-ui-dark .ai-chat-tool-gear,html.dark .ai-chat-tool-gear{color:#fff}.chakra-ui-dark .ai-chat-tool-badge,html.dark .ai-chat-tool-badge{background:hsla(0,0%,100%,.12);border:1px solid hsla(0,0%,100%,.2);color:hsla(0,0%,100%,.9)}.chakra-ui-dark .ai-chat-tool-badge.error,html.dark .ai-chat-tool-badge.error{background:rgba(239,68,68,.2);color:#f87171}.ai-chat-pin-input-group{align-items:center;display:flex;flex-wrap:nowrap;gap:8px;justify-content:center;margin:4px 0 8px}.ai-chat-pin-input{align-items:center;appearance:none;background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:var(--radius-md,8px);box-sizing:border-box;color:var(--text-primary,#3e3e3e);display:inline-flex;flex:0 0 42px;font-family:inherit;font-size:18px;font-weight:600;height:46px;justify-content:center;line-height:1;max-width:42px;min-width:42px;outline:none;padding:0;text-align:center;transition:border-color .2s ease,box-shadow .2s ease;width:42px}.ai-chat-pin-input:focus{border-color:var(--action-accent,var(--primary-color,#3b82f6));box-shadow:0 0 0 3px rgba(59,130,246,.1)}.ai-chat-pin-input:disabled{cursor:not-allowed;opacity:.6}.ai-chat-widget.dark .ai-chat-pin-input,.chakra-ui-dark .ai-chat-pin-input,.dark .ai-chat-pin-input,[data-theme=dark] .ai-chat-pin-input{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-pin-input:focus,.chakra-ui-dark .ai-chat-pin-input:focus,.dark .ai-chat-pin-input:focus,[data-theme=dark] .ai-chat-pin-input:focus{border-color:var(--action-accent,var(--primary-color,#3b82f6));box-shadow:0 0 0 3px rgba(59,130,246,.2)}.ai-chat-action-button-secondary{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:9999px;color:var(--text-secondary,#6b7280);cursor:pointer;font-size:var(--text-sm,13px);font-weight:var(--font-weight-medium,500);padding:10px 16px;transition:all .2s ease;width:100%}.ai-chat-action-button-secondary:hover:not(:disabled){background:rgba(59,130,246,.05);border-color:var(--action-accent,var(--primary-color,#3b82f6));color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-action-button-secondary:disabled{cursor:not-allowed;opacity:.5}.ai-chat-widget.dark .ai-chat-action-button-secondary,.chakra-ui-dark .ai-chat-action-button-secondary,.dark .ai-chat-action-button-secondary,[data-theme=dark] .ai-chat-action-button-secondary{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#e5e7eb}.ai-chat-widget.dark .ai-chat-action-button-secondary:hover:not(:disabled),.chakra-ui-dark .ai-chat-action-button-secondary:hover:not(:disabled),.dark .ai-chat-action-button-secondary:hover:not(:disabled),[data-theme=dark] .ai-chat-action-button-secondary:hover:not(:disabled){background:rgba(59,130,246,.2);border-color:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-action-appointment-list,.ai-chat-action-button-group{display:flex;flex-direction:column;gap:8px}.ai-chat-action-appointment-item{align-items:center;background:var(--bg-primary,#fff);border:1px solid var(--border-subtle,rgba(0,0,0,.08));border-radius:var(--radius-md,8px);display:flex;gap:12px;justify-content:space-between;padding:10px 12px}.ai-chat-widget.dark .ai-chat-action-appointment-item,.chakra-ui-dark .ai-chat-action-appointment-item,.dark .ai-chat-action-appointment-item,[data-theme=dark] .ai-chat-action-appointment-item{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.08)}.ai-chat-action-appointment-info{display:flex;flex-direction:column;gap:2px;min-width:0}.ai-chat-action-appointment-subject{color:var(--text-primary,#3e3e3e);font-size:14px;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ai-chat-widget.dark .ai-chat-action-appointment-subject,.chakra-ui-dark .ai-chat-action-appointment-subject,.dark .ai-chat-action-appointment-subject,[data-theme=dark] .ai-chat-action-appointment-subject{color:#fff}.ai-chat-action-appointment-time{color:var(--text-muted,#71717a);font-size:12px}.ai-chat-action-appointment-item .ai-chat-action-button-secondary{font-size:12px;padding:6px 12px;width:auto}.ai-chat-action-error-message{background:rgba(239,68,68,.1);border-radius:var(--radius-md,8px);color:#dc2626;font-size:var(--text-sm,13px);padding:12px}.ai-chat-widget.dark .ai-chat-action-error-message,.chakra-ui-dark .ai-chat-action-error-message,.dark .ai-chat-action-error-message,[data-theme=dark] .ai-chat-action-error-message{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-widget,.chat-ui{--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,.chat-ui.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,.chat-ui{-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,2147483647)}.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}.ai-chat-widget-container.container-mode{position:absolute}@keyframes ai-chat-window-open{0%{opacity:0;transform:scale(.9) translateY(20px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes ai-chat-window-close{0%{opacity:1;transform:scale(1) translateY(0)}to{opacity:0;transform:scale(.9) translateY(20px)}}@keyframes ai-chat-message-slide-in{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}@keyframes ai-chat-welcome-fade-in{0%{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}@keyframes ai-chat-typing-pulse{0%,60%,to{opacity:.4;transform:translateY(0) scale(1)}30%{opacity:1;transform:translateY(-4px) scale(1.1)}}@keyframes ai-chat-gear-spin{to{transform:rotate(1turn)}}@keyframes ai-chat-tool-active{0%,to{background:var(--bg-secondary);opacity:1}50%{background:var(--bg-tertiary);opacity:1}}@keyframes ai-chat-feedback-morph{0%{opacity:.5;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@keyframes ai-chat-checkmark-pop{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)}}@keyframes ai-chat-tool-gradient{0%{background-position:200% 0}to{background-position:-200% 0}}.ai-chat-window{animation:ai-chat-window-open var(--duration-slow,.35s) var(--spring-bounce);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:ai-chat-window-close var(--duration-normal) var(--spring-smooth) forwards}.ai-chat-window.size-small{height:500px;width:380px}.ai-chat-window.size-medium,.ai-chat-window.size-small{max-height:calc(100vh - 100px);max-width:calc(100vw - 40px)}.ai-chat-window.size-medium{height:600px;width:420px}.ai-chat-window.size-large{height:700px;max-height:calc(100vh - 100px);max-width:calc(100vw - 40px);width:480px}.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:12px 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,.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:color var(--duration-fast) ease;width:32px}.ai-chat-close-button:hover,.ai-chat-header-button:hover{color:var(--text-primary)}.ai-chat-close-button:active,.ai-chat-header-button:active{transform:scale(.95)}.ai-chat-close-button svg,.ai-chat-header-button svg{height:22px;width:22px}.ai-chat-button{align-items:center;background:var(--button-color,var(--btn-primary-bg));border:1px solid var(--border-default,#d3d3d3);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:transform var(--duration-fast) ease;width:var(--button-size,56px);z-index:1}.ai-chat-button:hover{transform:scale(1.05)}.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-welcome-bubble{animation:ai-chat-bubble-fade-in .3s ease-out;background:var(--button-color,var(--btn-primary-bg,#07f));border:none;border-radius:16px;box-shadow:none;box-sizing:border-box;color:var(--button-icon-color,var(--btn-primary-text,#fff));cursor:pointer;font-size:13px;font-weight:500;line-height:1.5;max-width:min(420px,90vw);padding:12px 32px 12px 16px;position:absolute;text-align:left;white-space:normal;width:auto;z-index:0}.ai-chat-welcome-bubble-close{align-items:center;background:transparent;border:none;border-radius:50%;color:inherit;cursor:pointer;display:flex;height:20px;justify-content:center;opacity:.8;padding:0;position:absolute;right:8px;top:8px;transition:opacity .15s ease,background .15s ease;width:20px}.ai-chat-welcome-bubble-close:hover{background:hsla(0,0%,100%,.2);opacity:1}.ai-chat-welcome-bubble-close svg{height:12px;width:12px}.ai-chat-welcome-bubble-arrow{border-bottom:8px solid transparent;border-top:8px solid transparent;height:0;position:absolute;width:0}.ai-chat-widget-container.bottom-right .ai-chat-welcome-bubble{bottom:50%;right:calc(100% + 12px);text-align:left;transform:translateY(50%)}.ai-chat-widget-container.bottom-left .ai-chat-welcome-bubble{bottom:50%;left:calc(100% + 12px);text-align:left;transform:translateY(50%)}.ai-chat-widget-container.top-right .ai-chat-welcome-bubble{right:calc(100% + 12px);text-align:left;top:50%;transform:translateY(-50%)}.ai-chat-widget-container.top-left .ai-chat-welcome-bubble{left:calc(100% + 12px);text-align:left;top:50%;transform:translateY(-50%)}.ai-chat-widget-container.bottom-right .ai-chat-welcome-bubble-arrow{border-left:8px solid var(--button-color,var(--btn-primary-bg,#07f));right:-8px;top:50%;transform:translateY(-50%)}.ai-chat-widget-container.bottom-left .ai-chat-welcome-bubble-arrow{border-right:8px solid var(--button-color,var(--btn-primary-bg,#07f));left:-8px;top:50%;transform:translateY(-50%)}.ai-chat-widget-container.top-right .ai-chat-welcome-bubble-arrow{border-left:8px solid var(--button-color,var(--btn-primary-bg,#07f));right:-8px;top:50%;transform:translateY(-50%)}.ai-chat-widget-container.top-left .ai-chat-welcome-bubble-arrow{border-right:8px solid var(--button-color,var(--btn-primary-bg,#07f));left:-8px;top:50%;transform:translateY(-50%)}.ai-chat-welcome-bubble:hover{filter:brightness(1.1)}.ai-chat-widget-container.bottom-right .ai-chat-welcome-bubble{animation-name:ai-chat-bubble-fade-in-bottom-right}@keyframes ai-chat-bubble-fade-in-bottom-right{0%{opacity:0;transform:translateY(50%) translateX(8px)}to{opacity:1;transform:translateY(50%) translateX(0)}}.ai-chat-widget-container.bottom-left .ai-chat-welcome-bubble{animation-name:ai-chat-bubble-fade-in-bottom-left}@keyframes ai-chat-bubble-fade-in-bottom-left{0%{opacity:0;transform:translateY(50%) translateX(-8px)}to{opacity:1;transform:translateY(50%) translateX(0)}}.ai-chat-widget-container.top-right .ai-chat-welcome-bubble{animation-name:ai-chat-bubble-fade-in-top-right}@keyframes ai-chat-bubble-fade-in-top-right{0%{opacity:0;transform:translateY(-50%) translateX(8px)}to{opacity:1;transform:translateY(-50%) translateX(0)}}.ai-chat-widget-container.top-left .ai-chat-welcome-bubble{animation-name:ai-chat-bubble-fade-in-top-left}@keyframes ai-chat-bubble-fade-in-top-left{0%{opacity:0;transform:translateY(-50%) translateX(-8px)}to{opacity:1;transform:translateY(-50%) translateX(0)}}.ai-chat-trigger-pill{align-items:center;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);background:hsla(0,0%,100%,.08);border:1px solid hsla(0,0%,100%,.15);border-radius:9999px;color:var(--text-primary,#fff);cursor:pointer;display:flex;font-size:13px;font-weight:500;gap:8px;height:40px;justify-content:center;padding:0 20px;position:relative;transition:all .3s ease;white-space:nowrap;z-index:1}.ai-chat-trigger-pill.is-open{background:var(--button-color,var(--btn-primary-bg));border-color:var(--border-default,#d3d3d3);box-shadow:var(--shadow-button,0 0 15px 9px rgba(0,0,0,.03));height:56px;padding:0;width:56px}.ai-chat-trigger-pill.is-open .ai-chat-trigger-pill-icon{height:24px;width:24px}.ai-chat-trigger-pill:hover:not(.is-open){background:hsla(0,0%,100%,.12);border-color:hsla(0,0%,100%,.25)}.ai-chat-trigger-pill.is-open:hover{transform:scale(1.05)}.ai-chat-trigger-pill:active{transform:scale(.98)}.ai-chat-trigger-pill-icon{flex-shrink:0;height:16px;transition:all .3s ease;width:16px}.ai-chat-widget.light .ai-chat-trigger-pill{background:rgba(0,0,0,.04);border-color:rgba(0,0,0,.12);color:var(--text-primary,#1a1a1a)}.ai-chat-widget.light .ai-chat-trigger-pill:hover:not(.is-open){background:rgba(0,0,0,.08);border-color:rgba(0,0,0,.2)}.ai-chat-trigger-input-container{align-items:flex-end;display:flex;flex-direction:column;gap:8px}.ai-chat-widget-container.bottom-left .ai-chat-trigger-input-container,.ai-chat-widget-container.top-left .ai-chat-trigger-input-container{align-items:flex-start}.ai-chat-trigger-input-expand{align-items:center;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);background:hsla(0,0%,100%,.1);border:1px solid hsla(0,0%,100%,.15);border-radius:50%;color:hsla(0,0%,100%,.7);cursor:pointer;display:flex;height:32px;justify-content:center;padding:0;transition:all .2s ease;width:32px}.ai-chat-trigger-input-expand:hover{background:hsla(0,0%,100%,.15);color:hsla(0,0%,100%,.9);transform:translateY(-2px)}.ai-chat-trigger-input-expand:active{transform:translateY(0)}.ai-chat-trigger-input-expand svg{height:16px;width:16px}.ai-chat-widget.light .ai-chat-trigger-input-expand{background:rgba(0,0,0,.05);border:1px solid rgba(0,0,0,.1);color:rgba(0,0,0,.5)}.ai-chat-widget.light .ai-chat-trigger-input-expand:hover{background:rgba(0,0,0,.1);color:rgba(0,0,0,.7)}.ai-chat-trigger-input-wrapper{max-width:calc(100vw - 40px);position:relative;z-index:1}.ai-chat-widget-container.trigger-input-bar .ai-chat-trigger-input-wrapper{width:348px}.ai-chat-widget-container.trigger-input-bar.size-medium .ai-chat-trigger-input-wrapper{width:388px}.ai-chat-widget-container.trigger-input-bar.size-large .ai-chat-trigger-input-wrapper{width:448px}.ai-chat-trigger-input{backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);background:hsla(0,0%,100%,.08);border:1px solid hsla(0,0%,100%,.12);border-radius:var(--radius-input,62px);box-shadow:0 4px 24px rgba(0,0,0,.15);box-sizing:border-box;color:var(--input-text,#fff);font-size:var(--text-md,15px);height:52px;outline:none;padding:6px 52px 6px 16px;transition:all .2s ease;width:100%}.ai-chat-trigger-input::placeholder{color:var(--text-placeholder,hsla(0,0%,100%,.5))}.ai-chat-trigger-input:focus{background:hsla(0,0%,100%,.12);border-color:hsla(0,0%,100%,.25)}.ai-chat-trigger-input-btn{align-items:center;background:var(--primary-color,var(--button-color,#07f));border:none;border-radius:50%;box-shadow:0 2px 8px rgba(0,119,255,.3);color:#fff;cursor:pointer;display:flex;height:40px;justify-content:center;position:absolute;right:6px;top:50%;transform:translateY(-50%);transition:all .2s ease;width:40px}.ai-chat-trigger-input-btn:hover:not(:disabled){box-shadow:0 4px 12px rgba(0,119,255,.4);transform:translateY(-50%) scale(1.05)}.ai-chat-trigger-input-btn:active:not(:disabled){transform:translateY(-50%) scale(.95)}.ai-chat-trigger-input-btn:disabled{box-shadow:none;cursor:not-allowed;opacity:.4}.ai-chat-trigger-input-btn svg{height:18px;width:18px}.ai-chat-widget.light .ai-chat-trigger-input{backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);background:hsla(0,0%,100%,.7);border:1px solid rgba(0,0,0,.1);box-shadow:0 4px 24px rgba(0,0,0,.08);color:var(--input-text,#1a1a1a)}.ai-chat-widget.light .ai-chat-trigger-input::placeholder{color:var(--text-placeholder,rgba(0,0,0,.4))}.ai-chat-widget.light .ai-chat-trigger-input:focus{background:hsla(0,0%,100%,.85);border-color:rgba(0,0,0,.2)}.ai-chat-widget.light .ai-chat-trigger-input-btn{background:var(--primary-color,var(--button-color,#07f));box-shadow:0 2px 8px rgba(0,119,255,.25);color:#fff}.ai-chat-widget.light .ai-chat-trigger-input-btn:hover:not(:disabled){box-shadow:0 4px 12px rgba(0,119,255,.35)}.ai-chat-widget-container.trigger-input-bar{align-items:flex-end;display:flex;flex-direction:column;gap:12px}.ai-chat-widget-container.trigger-input-bar.bottom-left,.ai-chat-widget-container.trigger-input-bar.top-left{align-items:flex-start}.ai-chat-widget-container.trigger-input-bar .ai-chat-window{bottom:auto;left:auto;order:-1;position:relative;right:auto;top:auto;width:100%}.ai-chat-widget-container.trigger-input-bar .ai-chat-window.size-small{max-width:calc(100vw - 40px);width:380px}.ai-chat-widget-container.trigger-input-bar .ai-chat-window.size-medium{max-width:calc(100vw - 40px);width:420px}.ai-chat-widget-container.trigger-input-bar .ai-chat-window.size-large{max-width:calc(100vw - 40px);width:480px}.ai-chat-widget-container.trigger-input-bar .ai-chat-button,.ai-chat-widget-container.trigger-pill-text .ai-chat-button{display:none}.ai-chat-widget-container.trigger-pill-text.is-open{gap:8px}.ai-chat-input-container{background:var(--bg-primary,#fff);bottom:0;left:0;padding:8px 0 16px;position:absolute;right:0;z-index:10}.ai-chat-widget.dark .ai-chat-input-container{background:var(--bg-primary,#282625)}.ai-chat-input-container.separate{padding:0 var(--radius-window-gutter,16px) var(--radius-window-gutter,16px)}.ai-chat-input-wrapper{align-items:flex-end;background:var(--input-bg,#f4f4f4);border:1px solid var(--input-border,#d3d3d3);border-radius:var(--radius-input,62px);box-sizing:border-box;display:flex;gap:0;height:52px;overflow:hidden;padding:6px 6px 6px 16px;position:relative;transition:all var(--duration-fast,.15s) ease;z-index:5}.ai-chat-input-wrapper.multiline{border-radius:14px!important;min-height:64px;padding:10px 10px 10px 14px}.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!important;background:transparent!important;border:none!important;border-radius:0!important;box-shadow:none!important;box-sizing:border-box!important;color:var(--input-text,#000)!important;flex:1!important;font-family:inherit!important;font-size:var(--text-md,15px)!important;height:40px!important;line-height:20px!important;margin:0!important;max-height:40px!important;min-height:40px!important;min-width:0!important;outline:none!important;overflow-wrap:anywhere!important;overflow-x:hidden!important;overflow-y:auto!important;padding:10px var(--space-sm,8px)!important;resize:none!important;white-space:pre-wrap!important;width:0!important;word-break:break-word!important}.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;min-height:40px;min-width:40px;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-data-policy{bottom:2px;color:var(--text-muted,#71717a);font-size:9px;left:0;line-height:1.4;opacity:.5;pointer-events:auto;position:absolute;right:0;text-align:center}.ai-chat-widget.dark .ai-chat-data-policy{color:var(--text-muted,#a1a1aa)}.ai-chat-data-policy-link{background:none;border:none;color:var(--text-muted,#71717a);cursor:pointer;font-family:inherit;font-size:inherit;margin:0;padding:0;text-decoration:underline;text-underline-offset:2px;transition:color .15s ease}.ai-chat-data-policy-link:hover{color:var(--text-secondary,#52525b)}.ai-chat-widget.dark .ai-chat-data-policy-link{color:var(--text-muted,#a1a1aa)}.ai-chat-widget.dark .ai-chat-data-policy-link:hover{color:var(--text-secondary,#d4d4d8)}.ai-chat-page-disclaimer{align-items:center;color:var(--text-muted,#71717a);display:flex;font-size:10px;gap:4px;justify-content:center;line-height:1.4;opacity:.7;padding:8px 16px;text-align:center}.ai-chat-widget.dark .ai-chat-page-disclaimer{color:var(--text-muted,#a1a1aa)}.ai-chat-page-disclaimer-link{background:none;border:none;color:var(--text-muted,#71717a);cursor:pointer;font-family:inherit;font-size:inherit;margin:0;padding:0;text-decoration:underline;text-underline-offset:2px;transition:color .15s ease}.ai-chat-page-disclaimer-link:hover{color:var(--text-secondary,#52525b)}.ai-chat-widget.dark .ai-chat-page-disclaimer-link{color:var(--text-muted,#a1a1aa)}.ai-chat-widget.dark .ai-chat-page-disclaimer-link:hover{color:var(--text-secondary,#d4d4d8)}.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:0 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:ai-chat-message-slide-in .2s var(--spring-bounce);display:flex;flex-direction:column;max-width:90%}.ai-chat-message.user{align-items:flex-end;align-self:flex-end}.ai-chat-message.assistant{align-items:flex-start;align-self:flex-start;max-width:100%;width:100%}.ai-chat-message.tool{align-self:stretch;max-width:none;padding:0}.ai-chat-message-content{word-wrap:break-word;border-radius:18px;font-size:var(--text-md,15px);line-height:var(--line-height-relaxed,1.6);overflow-wrap:break-word;padding:8px 14px}.ai-chat-message.user .ai-chat-message-content{background:var(--user-bg,#f4f3f0);border-radius:18px;color:var(--user-text,#000)}.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.assistant .ai-chat-message-content{background:var(--agent-bg,transparent);box-sizing:border-box;color:var(--agent-text,#000);padding:0;width:100%}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content{color:var(--agent-text,#fff)}.ai-chat-message-timestamp{color:var(--text-muted,#71717a);font-size:var(--text-xs,12px);margin-top:var(--space-xs,4px);padding:0 var(--space-xs,4px)}.ai-chat-welcome{animation:ai-chat-welcome-fade-in .3s var(--spring-smooth);display:flex;flex-direction:column;gap:var(--space-md,16px);padding:var(--space-lg,24px) 0}.ai-chat-welcome-title{color:var(--text-primary,#3e3e3e);font-size:var(--text-2xl,28px);font-weight:var(--font-weight-bold,700);line-height:var(--line-height-tight,1.3)}.ai-chat-widget.dark .ai-chat-welcome-title{color:var(--text-primary,#fff)}.ai-chat-welcome-text{color:var(--text-secondary,#000);font-size:var(--text-md,15px);line-height:var(--line-height-relaxed,1.6);max-width:100%}.ai-chat-widget.dark .ai-chat-welcome-text{color:var(--text-secondary,#fff)}.ai-chat-typing{align-items:center;display:flex;gap:var(--space-xs,4px);padding:var(--space-sm,8px) var(--space-md,16px)}.ai-chat-typing-dot{animation:ai-chat-typing-bounce 1.4s ease-in-out infinite both;background:var(--text-muted,#71717a);border-radius:50%;height:8px;width:8px}.ai-chat-typing-dot:first-child{animation-delay:-.32s}.ai-chat-typing-dot:nth-child(2){animation-delay:-.16s}.ai-chat-typing-dot:nth-child(3){animation-delay:0s}@keyframes ai-chat-typing-bounce{0%,80%,to{opacity:.4;transform:scale(.6)}40%{opacity:1;transform:scale(1)}}.ai-chat-scroll-button{align-items:center;background:var(--bg-secondary,#f4f4f5);border:1px solid var(--border-subtle,rgba(0,0,0,.1));border-radius:50%;bottom:80px;box-shadow:0 2px 8px rgba(0,0,0,.1);color:var(--text-secondary,#71717a);cursor:pointer;display:flex;height:36px;justify-content:center;left:50%;opacity:0;pointer-events:none;position:absolute;transform:translateX(-50%);transition:background .15s ease,box-shadow .15s ease,opacity .15s ease,visibility .15s ease;visibility:hidden;width:36px;z-index:15}.ai-chat-scroll-button.visible{opacity:1;pointer-events:auto;visibility:visible}.ai-chat-scroll-button:hover{background:var(--bg-tertiary,#e4e4e7);box-shadow:0 4px 12px rgba(0,0,0,.15)}.ai-chat-scroll-button:active{background:var(--bg-tertiary,#d4d4d8)}.ai-chat-widget.dark .ai-chat-scroll-button{background:var(--bg-secondary,#3f3f46);border-color:var(--border-subtle,hsla(0,0%,100%,.1));box-shadow:0 2px 8px rgba(0,0,0,.3);color:var(--text-secondary,#a1a1aa)}.ai-chat-widget.dark .ai-chat-scroll-button:hover{background:var(--bg-tertiary,#52525b);box-shadow:0 4px 12px rgba(0,0,0,.4)}.ai-chat-error{background:var(--bg-secondary);border-radius:var(--radius-chat-bubble);color:var(--text-primary);font-size:var(--text-md);margin:0 auto;padding:10px var(--space-md)}.ai-chat-message.assistant .ai-chat-message-content p{margin:0 0 var(--space-sm) 0}.ai-chat-message.assistant .ai-chat-message-content p:last-child{margin-bottom:0}.ai-chat-message.assistant .ai-chat-message-content h1{font-size:1.5em}.ai-chat-message.assistant .ai-chat-message-content h1,.ai-chat-message.assistant .ai-chat-message-content h2{color:var(--text-primary,#3e3e3e);font-weight:var(--font-weight-bold,700);line-height:var(--line-height-tight,1.3);margin:var(--space-md,16px) 0 var(--space-sm,8px) 0}.ai-chat-message.assistant .ai-chat-message-content h2{font-size:1.3em}.ai-chat-message.assistant .ai-chat-message-content h3{color:var(--text-primary,#3e3e3e);font-size:1.15em;font-weight:var(--font-weight-semibold,600);line-height:var(--line-height-tight,1.3);margin:var(--space-sm,8px) 0 var(--space-xs,4px) 0}.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{color:var(--text-primary,#3e3e3e);font-size:1em;font-weight:var(--font-weight-semibold,600);line-height:var(--line-height-tight,1.3);margin:var(--space-sm,8px) 0 var(--space-xs,4px) 0}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content h1,.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content h2,.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content h3,.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content h4,.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content h5,.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content h6{color:var(--text-primary,#fff)}.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,.ai-chat-message.assistant .ai-chat-message-content>h4:first-child,.ai-chat-message.assistant .ai-chat-message-content>h5:first-child,.ai-chat-message.assistant .ai-chat-message-content>h6:first-child{margin-top:0}.ai-chat-message.assistant .ai-chat-message-content ul{list-style-type:disc;margin:var(--space-sm,8px) 0;padding-left:var(--space-lg,24px)}.ai-chat-message.assistant .ai-chat-message-content ul ul{list-style-type:circle;margin:var(--space-xs,4px) 0}.ai-chat-message.assistant .ai-chat-message-content ul ul ul{list-style-type:square}.ai-chat-message.assistant .ai-chat-message-content ol{list-style-type:decimal;margin:var(--space-sm,8px) 0;padding-left:var(--space-lg,24px)}.ai-chat-message.assistant .ai-chat-message-content ol ol{list-style-type:lower-alpha;margin:var(--space-xs,4px) 0}.ai-chat-message.assistant .ai-chat-message-content ol ol ol{list-style-type:lower-roman}.ai-chat-message.assistant .ai-chat-message-content li{margin-bottom:var(--space-xs,4px);padding-left:var(--space-xs,4px)}.ai-chat-message.assistant .ai-chat-message-content li:last-child{margin-bottom:0}.ai-chat-message.assistant .ai-chat-message-content li>ol,.ai-chat-message.assistant .ai-chat-message-content li>ul{margin-top:var(--space-xs,4px)}.ai-chat-message.assistant .ai-chat-message-content code{background:rgba(0,0,0,.05);border-radius:var(--radius-sm);font-family:SF Mono,Monaco,Cascadia Code,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,.05);border-radius:var(--radius-md);margin:var(--space-sm) 0;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%,.05)}.ai-chat-message.assistant .ai-chat-message-content pre code{background:transparent;padding:0}.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 strong{font-weight:var(--font-weight-semibold)}.ai-chat-message.assistant .ai-chat-message-content blockquote{border-left:3px solid var(--border-default);color:var(--text-muted);margin:var(--space-sm) 0;padding-left:var(--space-md)}.ai-chat-message.assistant .ai-chat-message-content hr{border:none;border-top:1px solid var(--border-subtle,rgba(0,0,0,.1));margin:var(--space-lg,24px) 0}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content hr{border-top-color:var(--border-subtle,hsla(0,0%,100%,.1))}.ai-chat-message.assistant .ai-chat-message-content .table-wrapper{border:1px solid var(--border-subtle,rgba(0,0,0,.1));border-radius:var(--radius-md,8px);box-sizing:border-box;display:block;margin:var(--space-sm) var(--space-sm);max-width:100%;overflow:hidden;width:auto}.ai-chat-message.assistant .ai-chat-message-content .table-scroll{max-width:100%;overflow-x:auto;overflow-y:hidden;width:100%}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content .table-wrapper{border-color:var(--border-subtle,hsla(0,0%,100%,.1))}.ai-chat-message.assistant .ai-chat-message-content table{border-collapse:collapse;font-size:var(--text-sm);min-width:100%;width:max-content}.ai-chat-message.assistant .ai-chat-message-content td,.ai-chat-message.assistant .ai-chat-message-content th{border-bottom:1px solid var(--border-subtle,rgba(0,0,0,.1));border-right:1px solid var(--border-subtle,rgba(0,0,0,.1));padding:var(--space-sm);text-align:left}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content td,.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content th{border-color:var(--border-subtle,hsla(0,0%,100%,.1))}.ai-chat-message.assistant .ai-chat-message-content td:last-child,.ai-chat-message.assistant .ai-chat-message-content th:last-child{border-right:none}.ai-chat-message.assistant .ai-chat-message-content tr:last-child td{border-bottom:none}.ai-chat-message.assistant .ai-chat-message-content th{background:rgba(0,0,0,.03);font-weight:var(--font-weight-semibold);white-space:nowrap}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content th{background:hsla(0,0%,100%,.05)}.ai-chat-message.assistant .ai-chat-message-content tbody tr:nth-child(2n){background:rgba(0,0,0,.02)}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content tbody tr:nth-child(2n){background:hsla(0,0%,100%,.03)}.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{align-items:center;background:transparent;border:1px solid var(--border-default,#d4d4d8);border-radius:var(--radius-preset-badge,18px);color:var(--text-primary,#18181b);cursor:pointer;display:inline-flex;font-size:14px;font-weight:400;gap:6px;justify-content:center;line-height:1.3;max-width:100%;overflow:hidden;padding:8px 14px;text-overflow:ellipsis;transition:background .15s ease,border-color .15s ease,transform .1s ease;white-space:nowrap}.ai-chat-widget.dark .ai-chat-suggested-question{background:transparent;border-color:var(--border-subtle,#52525b);color:var(--text-primary,#fafafa)}.ai-chat-suggested-question-text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ai-chat-suggested-question:hover{background:var(--bg-hover,#f4f4f5);border-color:var(--border-default,#d4d4d8)}.ai-chat-widget.dark .ai-chat-suggested-question:hover{background:var(--bg-hover,#3f3f46);border-color:var(--border-subtle,#52525b)}.ai-chat-suggested-question:active{transform:scale(.98)}.ai-chat-suggested-question.action-type{border:none}.ai-chat-suggested-question.action-type,.ai-chat-widget.dark .ai-chat-suggested-question.action-type{background:var(--primary-color,var(--button-color,#ef4444));color:var(--button-icon-color,#fff)}.ai-chat-suggested-question.action-type:hover{background:var(--primary-color,var(--button-color,#ef4444));opacity:.9}.ai-chat-suggested-question-icon{align-items:center;display:flex;flex-shrink:0;justify-content:center}.ai-chat-suggested-question:not(.action-type) .ai-chat-suggested-question-icon{display:none}.ai-chat-follow-up-suggestions{box-sizing:border-box;margin:0;padding:8px 16px 0;width:100%}.ai-chat-follow-up-list{align-items:flex-end;display:flex;flex-direction:column;gap:6px}.ai-chat-follow-up-item{align-items:center;border:none;border-radius:var(--radius-preset-badge,18px);cursor:pointer;display:inline-flex;font-size:14px;font-weight:400;gap:6px;justify-content:center;line-height:1.3;max-width:100%;overflow:hidden;padding:8px 14px;text-overflow:ellipsis;transition:opacity .15s ease,transform .1s ease;white-space:nowrap}.ai-chat-follow-up-item,.ai-chat-widget.dark .ai-chat-follow-up-item{background:var(--primary-color,var(--button-color,#07f));color:var(--button-icon-color,#fff)}.ai-chat-follow-up-item:hover{opacity:.9}.ai-chat-follow-up-item:active{transform:scale(.98)}.ai-chat-follow-up-item.question-type{background:transparent;border:1px solid var(--border-default,#d4d4d8);color:var(--text-primary,#18181b)}.ai-chat-widget.dark .ai-chat-follow-up-item.question-type,.dark .ai-chat-follow-up-item.question-type,[data-color-mode=dark] .ai-chat-follow-up-item.question-type,[data-theme=dark] .ai-chat-follow-up-item.question-type{background:transparent;border:1px solid var(--border-subtle,#52525b);color:var(--text-primary,#fafafa)}@media (prefers-color-scheme:dark){.ai-chat-follow-up-item.question-type{background:transparent;border:1px solid var(--border-subtle,#52525b);color:var(--text-primary,#fafafa)}}.ai-chat-follow-up-item.question-type:hover{background:var(--bg-hover,#f4f4f5);opacity:1}.ai-chat-widget.dark .ai-chat-follow-up-item.question-type:hover,.dark .ai-chat-follow-up-item.question-type:hover,[data-color-mode=dark] .ai-chat-follow-up-item.question-type:hover,[data-theme=dark] .ai-chat-follow-up-item.question-type:hover{background:var(--bg-hover,#3f3f46);opacity:1}@media (prefers-color-scheme:dark){.ai-chat-follow-up-item.question-type:hover{background:var(--bg-hover,#3f3f46);opacity:1}}.ai-chat-follow-up-item.action-type{background:var(--primary-color,var(--button-color,#07f));border:none;color:var(--button-icon-color,#fff)}.ai-chat-follow-up-icon{align-items:center;display:flex;flex-shrink:0;justify-content:center}.ai-chat-follow-up-text{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.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:ai-chat-feedback-morph .3s var(--spring-bounce);gap:var(--space-xs)}.ai-chat-feedback-message{align-items:center;display:flex;gap:4px;margin-left:var(--space-xxs)}.ai-chat-feedback-checkmark{animation:ai-chat-checkmark-pop .3s var(--spring-bounce);color:#10b981;font-size:var(--text-md);font-weight:700}.ai-chat-feedback-text{color:#10b981;font-size:var(--text-xs);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-xs) 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,36px);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 var(--space-xs,4px) 0 var(--space-md,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;border-radius:var(--radius-sm,6px);color:var(--text-muted,#71717a);cursor:pointer;display:flex;flex-shrink:0;height:24px;justify-content:center;margin-left:auto;margin-right:var(--space-xs,4px);opacity:0;transition:opacity var(--duration-fast,.15s) ease,background var(--duration-fast,.15s) ease,color var(--duration-fast,.15s) ease;width:24px}.ai-chat-history-item-delete svg{height:14px;width:14px}.ai-chat-history-item:hover .ai-chat-history-item-delete{opacity:1}.ai-chat-history-item-delete:hover{background:rgba(239,68,68,.1);color:#ef4444}.ai-chat-widget.dark .ai-chat-history-item-delete:hover{background:rgba(239,68,68,.2);color:#f87171}.ai-chat-tool-row{align-items:center;display:flex;gap:10px;margin:2px 0;padding:0}.ai-chat-tool-gear{color:#1f2937;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;background:#e5e7eb;border:1px solid #d1d5db;border-radius:var(--radius-action-badge,8px);color:#1f2937;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:#e5e7eb;border:1px solid #d1d5db;color:#1f2937}.ai-chat-widget.dark .ai-chat-tool-badge,.ai-chat-widget[data-theme=dark] .ai-chat-tool-badge,.chakra-ui-dark .ai-chat-tool-badge,.dark .ai-chat-tool-badge,[data-theme=dark] .ai-chat-tool-badge,html.dark .ai-chat-tool-badge{background:hsla(0,0%,100%,.12);border:1px solid hsla(0,0%,100%,.2);color:hsla(0,0%,100%,.9)}.ai-chat-widget.dark .ai-chat-tool-gear,.ai-chat-widget[data-theme=dark] .ai-chat-tool-gear,.chakra-ui-dark .ai-chat-tool-gear,.dark .ai-chat-tool-gear,[data-theme=dark] .ai-chat-tool-gear,html.dark .ai-chat-tool-gear{color:#fff}.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:#fff;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}.ai-chat-tool-action{box-sizing:border-box;padding:0;width:100%}@keyframes ai-chat-skeleton-pulse{0%,to{opacity:.4}50%{opacity:.7}}.ai-chat-action-skeleton-item{animation:ai-chat-skeleton-pulse 1.5s ease-in-out infinite;background:var(--bg-secondary,#e5e7eb)}.ai-chat-widget.dark .ai-chat-action-skeleton-item,.chakra-ui-dark .ai-chat-action-skeleton-item,.dark .ai-chat-action-skeleton-item,[data-theme=dark] .ai-chat-action-skeleton-item{background:hsla(0,0%,100%,.1)}.ai-chat-action-skeleton-content{display:flex;flex-direction:column;gap:16px}.ai-chat-action-skeleton-header{align-items:center;display:flex;gap:10px}.ai-chat-action-skeleton-box{background:rgba(0,0,0,.08);border-radius:10px;display:flex;flex-direction:column;gap:8px;padding:14px}.ai-chat-widget.dark .ai-chat-action-skeleton-box,.chakra-ui-dark .ai-chat-action-skeleton-box,.dark .ai-chat-action-skeleton-box,[data-theme=dark] .ai-chat-action-skeleton-box{background:rgba(0,0,0,.25)}.ai-chat-action-card{--action-accent:var(--primary-color,#3b82f6);background:var(--bg-secondary,#f9fafb);border:1px solid var(--border-subtle,rgba(0,0,0,.06));border-radius:12px;box-sizing:border-box;margin-top:4px;padding:16px;transition:all .2s ease;width:100%}.ai-chat-widget.dark .ai-chat-action-card,.chakra-ui-dark .ai-chat-action-card,.dark .ai-chat-action-card,[data-theme=dark] .ai-chat-action-card{background:hsla(0,0%,100%,.05);border-color:hsla(0,0%,100%,.1)}.ai-chat-action-booked{background:var(--bg-secondary,#f9fafb);border:1px solid var(--border-subtle,rgba(0,0,0,.06))}.ai-chat-widget.dark .ai-chat-action-booked,.chakra-ui-dark .ai-chat-action-booked,.dark .ai-chat-action-booked,[data-theme=dark] .ai-chat-action-booked{background:var(--bg-secondary,#3a3a3a)}.ai-chat-action-header{align-items:center;color:var(--text-primary,#3e3e3e);display:flex;font-size:var(--text-md,15px);font-weight:var(--font-weight-semibold,600);gap:var(--space-sm,8px);margin-bottom:var(--space-md,16px)}.ai-chat-widget.dark .ai-chat-action-header,.chakra-ui-dark .ai-chat-action-header,.dark .ai-chat-action-header,[data-theme=dark] .ai-chat-action-header{color:var(--text-primary,#fff)}.ai-chat-action-icon{color:var(--action-accent,var(--primary-color,#3b82f6));flex-shrink:0;height:20px;width:20px}.ai-chat-action-success-icon-wrapper{align-items:center;background:var(--action-accent,var(--primary-color,#22c55e));border-radius:50%;color:#fff;display:flex;flex-shrink:0;height:24px;justify-content:center;width:24px}.ai-chat-action-icon-success{color:currentColor;height:14px;width:14px}.ai-chat-action-detail-box{background:var(--bg-primary,#fff);border:1px solid var(--border-subtle,rgba(0,0,0,.08));border-radius:var(--radius-md,8px);display:flex;flex-direction:column;gap:4px;padding:12px 16px}.ai-chat-widget.dark .ai-chat-action-detail-box,.chakra-ui-dark .ai-chat-action-detail-box,.dark .ai-chat-action-detail-box,[data-theme=dark] .ai-chat-action-detail-box{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.05)}.ai-chat-action-label-small{color:var(--text-muted,#71717a);font-size:11px;font-weight:600;letter-spacing:.5px;text-transform:uppercase}.ai-chat-action-value-large{color:var(--text-primary,#3e3e3e);font-size:15px;font-weight:500}.ai-chat-widget.dark .ai-chat-action-value-large,.chakra-ui-dark .ai-chat-action-value-large,.dark .ai-chat-action-value-large,[data-theme=dark] .ai-chat-action-value-large{color:#fff}.ai-chat-action-body{display:flex;flex-direction:column;gap:var(--space-md,16px)}.ai-chat-action-close-btn{align-items:center;background:transparent;border:none;border-radius:6px;color:var(--ai-chat-fg-muted,#6b7280);cursor:pointer;display:flex;height:28px;justify-content:center;padding:0;position:absolute;right:12px;top:12px;transition:all .15s ease;width:28px;z-index:10}.ai-chat-action-close-btn:hover{background:var(--ai-chat-bg-muted,#f3f4f6);color:var(--ai-chat-fg,#1f2937)}.ai-chat-action-close-btn:active{transform:scale(.95)}.ai-chat-action-close-btn:focus-visible{outline:2px solid var(--ai-chat-accent,#2563eb);outline-offset:2px}.ai-chat-action-card--closable,.ai-chat-form-card--closable{position:relative}.ai-chat-action-card--closable .ai-chat-action-header,.ai-chat-form-card--closable .ai-chat-form-card__header{padding-right:40px}.ai-chat-widget.dark .ai-chat-action-close-btn:hover,.chakra-ui-dark .ai-chat-action-close-btn:hover,.dark .ai-chat-action-close-btn:hover,[data-theme=dark] .ai-chat-action-close-btn:hover{background:hsla(0,0%,100%,.1);color:var(--ai-chat-fg,#f3f4f6)}.ai-chat-action-field{display:flex;flex-direction:column;gap:var(--space-xs,6px)}.ai-chat-action-label{color:var(--text-secondary,#6b7280);font-size:var(--text-sm,13px);font-weight:var(--font-weight-medium,500)}.ai-chat-widget.dark .ai-chat-action-label,.chakra-ui-dark .ai-chat-action-label,.dark .ai-chat-action-label,[data-theme=dark] .ai-chat-action-label{color:var(--text-secondary,#a1a1aa)}.ai-chat-action-input{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:var(--radius-md,8px);color:var(--text-primary,#3e3e3e);font-size:var(--text-sm,13px);outline:none;padding:10px 12px;transition:border-color .2s ease,box-shadow .2s ease}.ai-chat-action-input:focus{border-color:var(--action-accent,var(--primary-color,#3b82f6));box-shadow:0 0 0 3px rgba(59,130,246,.1)}.ai-chat-action-input::placeholder{color:var(--text-muted,#9ca3af)}.ai-chat-widget.dark .ai-chat-action-input,.chakra-ui-dark .ai-chat-action-input,.dark .ai-chat-action-input,[data-theme=dark] .ai-chat-action-input{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-action-input:focus,.chakra-ui-dark .ai-chat-action-input:focus,.dark .ai-chat-action-input:focus,[data-theme=dark] .ai-chat-action-input:focus{border-color:var(--action-accent,var(--primary-color,#3b82f6));box-shadow:0 0 0 3px rgba(59,130,246,.2)}.ai-chat-action-button{background:var(--action-accent,var(--primary-color,#3b82f6));border:none;border-radius:9999px;color:#fff;cursor:pointer;font-size:var(--text-sm,13px);font-weight:var(--font-weight-semibold,600);padding:12px 16px;transition:all .2s ease;width:100%}.ai-chat-action-button:hover:not(:disabled){opacity:.9;transform:translateY(-1px)}.ai-chat-action-button:disabled{cursor:not-allowed;opacity:.5}.ai-chat-action-link-button{align-items:center;background:var(--action-accent,var(--primary-color,#3b82f6));border:none;border-radius:9999px;box-sizing:border-box;color:#fff;display:flex;font-size:14px;font-weight:600;gap:6px;justify-content:center;margin-top:8px;padding:12px;text-decoration:none;transition:all .2s ease;width:100%}.ai-chat-action-link-button:hover{opacity:.9;transform:translateY(-1px)}.ai-chat-action-error{background:rgba(239,68,68,.1);border-radius:var(--radius-md,8px);color:#dc2626;font-size:var(--text-sm,13px);padding:10px 12px}.ai-chat-widget.dark .ai-chat-action-error,.chakra-ui-dark .ai-chat-action-error,.dark .ai-chat-action-error,[data-theme=dark] .ai-chat-action-error{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-action-hint{color:var(--text-muted,#9ca3af);font-size:var(--text-sm,13px);padding:var(--space-sm,8px);text-align:center}.ai-chat-action-date-grid{display:grid;gap:var(--space-xs,6px);grid-template-columns:repeat(auto-fill,minmax(90px,1fr))}.ai-chat-action-date-btn{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:var(--radius-md,8px);color:var(--text-primary,#3e3e3e);cursor:pointer;font-size:var(--text-xs,12px);font-weight:var(--font-weight-medium,500);padding:8px 10px;text-align:center;transition:all .15s ease}.ai-chat-action-date-btn:hover{background:rgba(59,130,246,.05);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-action-date-btn.active{background:var(--action-accent,var(--primary-color,#3b82f6));border-color:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-widget.dark .ai-chat-action-date-btn,.chakra-ui-dark .ai-chat-action-date-btn,.dark .ai-chat-action-date-btn,[data-theme=dark] .ai-chat-action-date-btn{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-action-date-btn:hover,.chakra-ui-dark .ai-chat-action-date-btn:hover,.dark .ai-chat-action-date-btn:hover,[data-theme=dark] .ai-chat-action-date-btn:hover{background:rgba(59,130,246,.15);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-widget.dark .ai-chat-action-date-btn.active,.chakra-ui-dark .ai-chat-action-date-btn.active,.dark .ai-chat-action-date-btn.active,[data-theme=dark] .ai-chat-action-date-btn.active{background:var(--action-accent,var(--primary-color,#3b82f6));border-color:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-action-time-grid{display:grid;gap:var(--space-xs,6px);grid-template-columns:repeat(auto-fill,minmax(100px,1fr))}.ai-chat-action-time-btn{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:var(--radius-md,8px);color:var(--text-primary,#3e3e3e);cursor:pointer;font-size:var(--text-xs,12px);font-weight:var(--font-weight-medium,500);padding:8px 10px;text-align:center;transition:all .15s ease}.ai-chat-action-time-btn:hover{background:rgba(59,130,246,.05);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-action-time-btn.active{background:var(--action-accent,var(--primary-color,#3b82f6));border-color:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-widget.dark .ai-chat-action-time-btn,.chakra-ui-dark .ai-chat-action-time-btn,.dark .ai-chat-action-time-btn,[data-theme=dark] .ai-chat-action-time-btn{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-action-time-btn:hover,.chakra-ui-dark .ai-chat-action-time-btn:hover,.dark .ai-chat-action-time-btn:hover,[data-theme=dark] .ai-chat-action-time-btn:hover{background:rgba(59,130,246,.15);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-widget.dark .ai-chat-action-time-btn.active,.chakra-ui-dark .ai-chat-action-time-btn.active,.dark .ai-chat-action-time-btn.active,[data-theme=dark] .ai-chat-action-time-btn.active{background:var(--action-accent,var(--primary-color,#3b82f6));border-color:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-link-preview{--action-accent:var(--primary-color,#3b82f6);background:var(--bg-secondary,#f4f4f4);border:1px solid var(--border-subtle,rgba(0,0,0,.08));border-radius:var(--radius-lg,12px);cursor:pointer;display:flex;flex-direction:column;margin-top:4px;overflow:hidden;padding:0!important;position:relative;transition:border-color .2s,box-shadow .2s,transform .2s}.ai-chat-link-preview:hover{border-color:var(--action-accent);box-shadow:0 2px 8px rgba(0,0,0,.1);transform:translateY(-1px)}.ai-chat-link-preview:focus{border-color:var(--action-accent);box-shadow:0 0 0 2px rgba(59,130,246,.2);outline:none}.ai-chat-widget.dark .ai-chat-link-preview,.chakra-ui-dark .ai-chat-link-preview,.dark .ai-chat-link-preview,[data-theme=dark] .ai-chat-link-preview{background:var(--bg-secondary,#3a3a3a);border-color:hsla(0,0%,100%,.08)}.ai-chat-widget.dark .ai-chat-link-preview:hover,.chakra-ui-dark .ai-chat-link-preview:hover,.dark .ai-chat-link-preview:hover,[data-theme=dark] .ai-chat-link-preview:hover{border-color:var(--action-accent);box-shadow:0 2px 12px rgba(0,0,0,.3)}.ai-chat-link-preview__image{aspect-ratio:1.91/1;background:var(--bg-muted,#e5e7eb);overflow:hidden;width:100%}.ai-chat-widget.dark .ai-chat-link-preview__image,.chakra-ui-dark .ai-chat-link-preview__image,.dark .ai-chat-link-preview__image,[data-theme=dark] .ai-chat-link-preview__image{background:hsla(0,0%,100%,.05)}.ai-chat-link-preview__image img{height:100%;object-fit:cover;width:100%}.ai-chat-link-preview__content{flex:1;padding:8px 10px}.ai-chat-link-preview__site{align-items:center;display:flex;gap:6px;margin-bottom:6px}.ai-chat-link-preview__favicon{border-radius:2px;flex-shrink:0;height:16px;width:16px}.ai-chat-link-preview__domain{color:var(--text-muted,#71717a);font-size:12px;letter-spacing:.5px;overflow:hidden;text-overflow:ellipsis;text-transform:uppercase;white-space:nowrap}.ai-chat-link-preview__title{-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;color:var(--text-primary,#3e3e3e);display:-webkit-box;font-size:15px;font-weight:600;line-height:1.3;margin:0 0 4px;overflow:hidden}.ai-chat-widget.dark .ai-chat-link-preview__title,.chakra-ui-dark .ai-chat-link-preview__title,.dark .ai-chat-link-preview__title,[data-theme=dark] .ai-chat-link-preview__title{color:#fff}.ai-chat-link-preview__description{-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;color:var(--text-muted,#71717a);display:-webkit-box;font-size:13px;line-height:1.4;margin:0;overflow:hidden}.ai-chat-link-preview__context{border-top:1px solid var(--border-subtle,rgba(0,0,0,.08));color:var(--text-muted,#71717a);font-size:12px;font-style:italic;margin:8px 0 0;padding-top:8px}.ai-chat-widget.dark .ai-chat-link-preview__context,.chakra-ui-dark .ai-chat-link-preview__context,.dark .ai-chat-link-preview__context,[data-theme=dark] .ai-chat-link-preview__context{border-color:hsla(0,0%,100%,.08)}.ai-chat-link-preview__arrow{align-items:center;background:var(--bg-primary,#fff);border-radius:50%;box-shadow:0 1px 3px rgba(0,0,0,.1);color:var(--text-muted,#71717a);display:flex;height:28px;justify-content:center;opacity:0;position:absolute;right:12px;top:12px;transition:opacity .2s,background .2s;width:28px}.ai-chat-link-preview:hover .ai-chat-link-preview__arrow{opacity:1}.ai-chat-widget.dark .ai-chat-link-preview__arrow,.chakra-ui-dark .ai-chat-link-preview__arrow,.dark .ai-chat-link-preview__arrow,[data-theme=dark] .ai-chat-link-preview__arrow{background:hsla(0,0%,100%,.1);color:#fff}.ai-chat-link-preview--error{border-color:rgba(239,68,68,.3)}.ai-chat-link-preview--error:hover{border-color:rgba(239,68,68,.5)}.ai-chat-link-preview__error-text{color:#dc2626;font-size:12px;margin:4px 0 0}.ai-chat-widget.dark .ai-chat-link-preview__error-text,.chakra-ui-dark .ai-chat-link-preview__error-text,.dark .ai-chat-link-preview__error-text,[data-theme=dark] .ai-chat-link-preview__error-text{color:#fca5a5}.ai-chat-video-player{--action-accent:var(--primary-color,#3b82f6);background:var(--bg-secondary,#f4f4f4);border-radius:var(--radius-lg,12px);display:flex;flex-direction:column;gap:0;margin-top:4px;overflow:hidden;padding:0!important}.ai-chat-widget.dark .ai-chat-video-player,.chakra-ui-dark .ai-chat-video-player,.dark .ai-chat-video-player,[data-theme=dark] .ai-chat-video-player{background:var(--bg-secondary,#3a3a3a)}.ai-chat-video-player__header{align-items:center;color:var(--action-accent,var(--primary-color,#3b82f6));display:flex;gap:8px}.ai-chat-video-player__title{color:var(--text-primary,#3e3e3e);font-size:14px;font-weight:600}.ai-chat-widget.dark .ai-chat-video-player__title,.chakra-ui-dark .ai-chat-video-player__title,.dark .ai-chat-video-player__title,[data-theme=dark] .ai-chat-video-player__title{color:#fff}.ai-chat-video-player__container{aspect-ratio:16/9;background:#000;border-radius:8px;overflow:hidden;position:relative;width:100%}.ai-chat-video-player__thumbnail{cursor:pointer;height:100%;position:relative;width:100%}.ai-chat-video-player__thumbnail img{height:100%;object-fit:cover;width:100%}.ai-chat-video-player__placeholder{align-items:center;background:linear-gradient(135deg,#1a1a2e,#16213e);cursor:pointer;display:flex;flex-direction:column;gap:8px;height:100%;justify-content:center;position:relative;width:100%}.ai-chat-video-player__click-text{color:hsla(0,0%,100%,.7);font-size:13px}.ai-chat-video-player__play-btn{align-items:center;background:rgba(0,0,0,.7);border:none;border-radius:50%;color:#fff;cursor:pointer;display:flex;height:64px;justify-content:center;left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);transition:background .2s,transform .2s;width:64px}.ai-chat-video-player__placeholder .ai-chat-video-player__play-btn{left:auto;position:relative;top:auto;transform:none}.ai-chat-video-player__play-btn:hover{background:rgba(0,0,0,.9);transform:translate(-50%,-50%) scale(1.05)}.ai-chat-video-player__placeholder .ai-chat-video-player__play-btn:hover{transform:scale(1.05)}.ai-chat-video-player__provider-badge{background:rgba(0,0,0,.8);border-radius:4px;bottom:8px;color:#fff;font-size:11px;font-weight:600;letter-spacing:.5px;padding:4px 8px;position:absolute;right:8px;text-transform:uppercase}.ai-chat-video-player__iframe,.ai-chat-video-player__video{border:none;height:100%;left:0;position:absolute;top:0;width:100%}.ai-chat-video-player__error{align-items:center;background:rgba(239,68,68,.1);color:#dc2626;display:flex;font-size:13px;height:100%;justify-content:center;padding:16px;text-align:center;width:100%}.ai-chat-widget.dark .ai-chat-video-player__error,.chakra-ui-dark .ai-chat-video-player__error,.dark .ai-chat-video-player__error,[data-theme=dark] .ai-chat-video-player__error{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-video-player__context{color:var(--text-muted,#71717a);font-size:12px;font-style:italic;margin-top:4px;padding:8px 12px 12px}.ai-chat-location-card{background:var(--bg-secondary,#fff);border:1px solid var(--border-subtle,rgba(0,0,0,.08));border-radius:12px;overflow:hidden;padding:0}.ai-chat-widget.dark .ai-chat-location-card,.chakra-ui-dark .ai-chat-location-card,.dark .ai-chat-location-card,[data-theme=dark] .ai-chat-location-card{background:hsla(0,0%,100%,.05);border-color:hsla(0,0%,100%,.1)}.ai-chat-location-card--compact{border-radius:10px}.ai-chat-location-card--error{color:var(--text-muted,#71717a);padding:16px;text-align:center}.ai-chat-location-card__map{background:var(--bg-muted,#f4f4f5);position:relative;width:100%}.ai-chat-widget.dark .ai-chat-location-card__map,.chakra-ui-dark .ai-chat-location-card__map,.dark .ai-chat-location-card__map,[data-theme=dark] .ai-chat-location-card__map{background:rgba(0,0,0,.2)}.ai-chat-location-card__map iframe{border:none;display:block;height:100%;width:100%}.ai-chat-location-card__content{padding:12px}.ai-chat-location-card--compact .ai-chat-location-card__content{padding:10px}.ai-chat-location-card__header{align-items:center;display:flex;flex-wrap:wrap;gap:8px;margin-bottom:8px}.ai-chat-location-card__name{color:var(--text-primary,#18181b);flex:1;font-size:16px;font-weight:600;margin:0;min-width:0}.ai-chat-widget.dark .ai-chat-location-card__name,.chakra-ui-dark .ai-chat-location-card__name,.dark .ai-chat-location-card__name,[data-theme=dark] .ai-chat-location-card__name{color:#fff}.ai-chat-location-card--compact .ai-chat-location-card__name{font-size:14px}.ai-chat-location-card__type{background:var(--bg-muted,#f4f4f5);border-radius:10px;color:var(--text-muted,#71717a);font-size:11px;font-weight:500;letter-spacing:.5px;padding:2px 8px;text-transform:uppercase}.ai-chat-widget.dark .ai-chat-location-card__type,.chakra-ui-dark .ai-chat-location-card__type,.dark .ai-chat-location-card__type,[data-theme=dark] .ai-chat-location-card__type{background:hsla(0,0%,100%,.1);color:hsla(0,0%,100%,.7)}.ai-chat-location-card__status{border-radius:12px;font-size:12px;font-weight:500;padding:2px 8px}.ai-chat-location-card__status--open{background:#dcfce7;color:#16a34a}.ai-chat-location-card__status--closed{background:#fef2f2;color:#dc2626}.ai-chat-widget.dark .ai-chat-location-card__status--open,.chakra-ui-dark .ai-chat-location-card__status--open,.dark .ai-chat-location-card__status--open,[data-theme=dark] .ai-chat-location-card__status--open{background:rgba(34,197,94,.2);color:#4ade80}.ai-chat-widget.dark .ai-chat-location-card__status--closed,.chakra-ui-dark .ai-chat-location-card__status--closed,.dark .ai-chat-location-card__status--closed,[data-theme=dark] .ai-chat-location-card__status--closed{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-location-card__address{align-items:flex-start;color:var(--text-muted,#71717a);display:flex;font-size:13px;gap:6px;line-height:1.4;margin:0 0 8px}.ai-chat-location-card__address svg{flex-shrink:0;margin-top:2px}.ai-chat-location-card__description{color:var(--text-muted,#71717a);font-size:13px;line-height:1.4;margin:0 0 8px}.ai-chat-location-card__hours{align-items:flex-start;color:var(--text-muted,#71717a);display:flex;font-size:13px;gap:6px;margin-bottom:8px}.ai-chat-location-card__hours svg{flex-shrink:0;margin-top:2px}.ai-chat-location-card__hours-list{flex:1}.ai-chat-location-card__hours-toggle{align-items:center;background:none;border:none;color:inherit;cursor:pointer;display:flex;font:inherit;gap:4px;padding:0}.ai-chat-location-card__hours-toggle:hover{text-decoration:underline}.ai-chat-location-card__hours-full{list-style:none;margin:8px 0 0;padding:0}.ai-chat-location-card__hours-full li{display:flex;font-size:12px;justify-content:space-between;padding:4px 0}.ai-chat-location-card__hours-today{color:var(--text-primary,#18181b);font-weight:600}.ai-chat-widget.dark .ai-chat-location-card__hours-today,.chakra-ui-dark .ai-chat-location-card__hours-today,.dark .ai-chat-location-card__hours-today,[data-theme=dark] .ai-chat-location-card__hours-today{color:#fff}.ai-chat-location-card__phone{align-items:center;background:none;border:none;color:var(--action-accent,#3b82f6);cursor:pointer;display:flex;font-size:13px;gap:6px;margin-bottom:12px;padding:0}.ai-chat-location-card__phone:hover{text-decoration:underline}.ai-chat-location-card__actions{display:flex;gap:8px;justify-content:flex-start;width:100%}.ai-chat-location-card__button{align-items:center;background:var(--action-accent,#3b82f6);border:none;border-radius:20px;color:#fff;cursor:pointer;display:flex;flex:1;font-size:13px;font-weight:500;gap:6px;justify-content:center;padding:10px 16px;transition:opacity .2s}.ai-chat-location-card__button:hover{opacity:.9}.ai-chat-location-card--compact .ai-chat-location-card__button{font-size:12px;padding:8px 12px}.ai-chat-location-card__link{align-items:center;background:var(--bg-secondary,#fff);border:1px solid var(--border-subtle,rgba(0,0,0,.08));border-radius:20px;color:var(--text-primary,#18181b);display:flex;font-size:13px;gap:6px;padding:10px 16px;text-decoration:none;transition:border-color .2s}.ai-chat-widget.dark .ai-chat-location-card__link,.chakra-ui-dark .ai-chat-location-card__link,.dark .ai-chat-location-card__link,[data-theme=dark] .ai-chat-location-card__link{background:hsla(0,0%,100%,.05);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-location-card__link:hover{border-color:var(--action-accent,#3b82f6)}.ai-chat-location-card-list{display:flex;flex-direction:column;gap:8px}.ai-chat-location-card-list__header{align-items:center;color:var(--text-muted,#71717a);display:flex;font-size:13px;font-weight:500;gap:6px;margin-bottom:4px;padding:0 4px}.ai-chat-location-card-list__stack{display:grid;gap:12px;grid-template-columns:1fr}.ai-chat-location-card-list__stack--cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.ai-chat-location-card-list__stack--cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}@media (max-width:1000px){.ai-chat-location-card-list__stack--cols-3{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (max-width:640px){.ai-chat-location-card-list__stack--cols-2,.ai-chat-location-card-list__stack--cols-3{grid-template-columns:1fr}}.ai-chat-location-card-list__grid{display:grid;gap:8px;grid-template-columns:repeat(2,1fr)}@media (max-width:400px){.ai-chat-location-card-list__grid{grid-template-columns:1fr}}.ai-chat-location-card-list__carousel{-webkit-overflow-scrolling:touch;-ms-overflow-style:none;display:flex;gap:8px;overflow-x:auto;padding-bottom:4px;scroll-snap-type:x mandatory;scrollbar-width:none}.ai-chat-location-card-list__carousel::-webkit-scrollbar{display:none}.ai-chat-location-card-list__carousel>.ai-chat-location-card{flex:0 0 280px;scroll-snap-align:start}.ai-chat-contact-card{background:#fff;border:1px solid rgba(0,0,0,.08);border-radius:16px;overflow:hidden;padding:0;position:relative}.ai-chat-widget.dark .ai-chat-contact-card,.ai-chat-widget[data-theme=dark] .ai-chat-contact-card,.chakra-ui-dark .ai-chat-contact-card,.dark .ai-chat-contact-card,[data-theme=dark] .ai-chat-contact-card,html.dark .ai-chat-contact-card{background:#4a4a4a;border-color:hsla(0,0%,100%,.08)}.ai-chat-contact-card-list{gap:12px;width:100%}.ai-chat-contact-card--compact{border-radius:12px}.ai-chat-contact-card--empty{align-items:center;background:var(--bg-secondary,#f4f4f5);display:flex;flex-direction:column;gap:8px;justify-content:center;padding:24px 16px;text-align:center}.ai-chat-widget.dark .ai-chat-contact-card--empty,.chakra-ui-dark .ai-chat-contact-card--empty,.dark .ai-chat-contact-card--empty,[data-theme=dark] .ai-chat-contact-card--empty{background:#3a3a3a}.ai-chat-contact-card__empty-icon{color:var(--text-muted,#71717a);opacity:.6}.ai-chat-contact-card__empty-text{color:var(--text-muted,#71717a);font-size:14px;margin:0}.ai-chat-contact-card--vertical{display:flex;flex-direction:column}.ai-chat-contact-card--vertical .ai-chat-contact-card__image-section{aspect-ratio:3/2;overflow:hidden;width:100%}.ai-chat-contact-card--vertical .ai-chat-contact-card__image{height:100%;object-fit:cover;width:100%}.ai-chat-contact-card--vertical .ai-chat-contact-card__image-placeholder{align-items:center;background:linear-gradient(135deg,#5a5a5a,#3a3a3a);color:hsla(0,0%,100%,.5);display:flex;height:100%;justify-content:center;width:100%}.ai-chat-contact-card--vertical .ai-chat-contact-card__image-placeholder svg{height:48px;width:48px}.ai-chat-contact-card--vertical .ai-chat-contact-card__info{padding:16px;text-align:center}.ai-chat-contact-card--horizontal{display:flex;flex-direction:row}.ai-chat-contact-card--horizontal .ai-chat-contact-card__image-section{height:160px;min-width:140px;overflow:hidden;width:140px}.ai-chat-contact-card--horizontal.ai-chat-contact-card--compact .ai-chat-contact-card__image-section{height:120px;min-width:100px;width:100px}.ai-chat-contact-card--horizontal .ai-chat-contact-card__image{height:100%;object-fit:cover;width:100%}.ai-chat-contact-card--horizontal .ai-chat-contact-card__image-placeholder{align-items:center;background:linear-gradient(135deg,#5a5a5a,#3a3a3a);color:hsla(0,0%,100%,.5);display:flex;height:100%;justify-content:center;width:100%}.ai-chat-contact-card--horizontal .ai-chat-contact-card__image-placeholder svg{height:36px;width:36px}.ai-chat-contact-card--horizontal .ai-chat-contact-card__info{display:flex;flex:1;flex-direction:column;justify-content:center;padding:16px}.ai-chat-contact-card__name{color:var(--action-accent,#ef4444);font-size:18px;font-weight:600;line-height:1.3;margin:0}.ai-chat-contact-card--compact .ai-chat-contact-card__name{font-size:15px}.ai-chat-contact-card__role{color:rgba(0,0,0,.7);font-size:14px;font-weight:400;margin:2px 0 0}.ai-chat-widget.dark .ai-chat-contact-card__role,.ai-chat-widget[data-theme=dark] .ai-chat-contact-card__role,.chakra-ui-dark .ai-chat-contact-card__role,.dark .ai-chat-contact-card__role,[data-theme=dark] .ai-chat-contact-card__role,html.dark .ai-chat-contact-card__role{color:hsla(0,0%,100%,.9)}.ai-chat-contact-card--compact .ai-chat-contact-card__role{font-size:13px}.ai-chat-contact-card__details{display:flex;flex-direction:column;gap:2px;margin-top:12px}.ai-chat-contact-card__detail{color:rgba(0,0,0,.6);display:block;font-size:14px;line-height:1.5;margin:0;text-decoration:none}.ai-chat-contact-card__detail:hover{color:#000;text-decoration:underline}.ai-chat-widget.dark .ai-chat-contact-card__detail,.ai-chat-widget[data-theme=dark] .ai-chat-contact-card__detail,.chakra-ui-dark .ai-chat-contact-card__detail,.dark .ai-chat-contact-card__detail,[data-theme=dark] .ai-chat-contact-card__detail,html.dark .ai-chat-contact-card__detail{color:hsla(0,0%,100%,.7)}.ai-chat-widget.dark .ai-chat-contact-card__detail:hover,.ai-chat-widget[data-theme=dark] .ai-chat-contact-card__detail:hover,.chakra-ui-dark .ai-chat-contact-card__detail:hover,.dark .ai-chat-contact-card__detail:hover,[data-theme=dark] .ai-chat-contact-card__detail:hover,html.dark .ai-chat-contact-card__detail:hover{color:#fff}.ai-chat-contact-card--compact .ai-chat-contact-card__detail{font-size:13px}.ai-chat-contact-card__responsibilities{display:flex;flex-wrap:wrap;gap:4px;margin-top:8px}.ai-chat-contact-card__responsibility-tag{background:rgba(0,0,0,.08);border-radius:10px;color:rgba(0,0,0,.8);font-size:11px;font-weight:500;padding:3px 10px}.ai-chat-widget.dark .ai-chat-contact-card__responsibility-tag,.ai-chat-widget[data-theme=dark] .ai-chat-contact-card__responsibility-tag,.chakra-ui-dark .ai-chat-contact-card__responsibility-tag,.dark .ai-chat-contact-card__responsibility-tag,[data-theme=dark] .ai-chat-contact-card__responsibility-tag,html.dark .ai-chat-contact-card__responsibility-tag{background:hsla(0,0%,100%,.15);color:hsla(0,0%,100%,.9)}.ai-chat-contact-card__responsibility-more{color:rgba(0,0,0,.5);font-size:11px;padding:3px 4px}.ai-chat-widget.dark .ai-chat-contact-card__responsibility-more,.ai-chat-widget[data-theme=dark] .ai-chat-contact-card__responsibility-more,.chakra-ui-dark .ai-chat-contact-card__responsibility-more,.dark .ai-chat-contact-card__responsibility-more,[data-theme=dark] .ai-chat-contact-card__responsibility-more,html.dark .ai-chat-contact-card__responsibility-more{color:hsla(0,0%,100%,.5)}.ai-chat-contact-card__actions{display:flex;gap:8px;padding:0 12px 12px}.ai-chat-contact-card--compact .ai-chat-contact-card__actions{gap:6px;padding:0 10px 10px}.ai-chat-contact-card__button{align-items:center;border:none;border-radius:9999px;cursor:pointer;display:flex;font-size:14px;font-weight:600;gap:8px;justify-content:center;padding:12px 20px;transition:all .15s ease;white-space:nowrap}.ai-chat-contact-card--compact .ai-chat-contact-card__button{font-size:13px;padding:10px 16px}.ai-chat-contact-card__button:hover{box-shadow:0 4px 12px rgba(0,0,0,.15);transform:translateY(-1px)}.ai-chat-contact-card__button:active{transform:translateY(0)}.ai-chat-contact-card__button--primary{background:var(--action-accent,#3b82f6);color:#fff;flex:1}.ai-chat-contact-card__button--primary:hover{background:color-mix(in srgb,var(--action-accent,#3b82f6) 90%,#000)}.ai-chat-contact-card__button--secondary{background:var(--bg-muted,#f4f4f5);border:1px solid var(--border-subtle,rgba(0,0,0,.08));color:var(--text-primary,#18181b);flex:1}.ai-chat-contact-card__button--secondary:hover{background:var(--bg-hover,#e4e4e7)}.ai-chat-widget.dark .ai-chat-contact-card__button--secondary,.chakra-ui-dark .ai-chat-contact-card__button--secondary,.dark .ai-chat-contact-card__button--secondary,[data-theme=dark] .ai-chat-contact-card__button--secondary{background:hsla(0,0%,100%,.1);border-color:hsla(0,0%,100%,.15);color:#fff}.ai-chat-widget.dark .ai-chat-contact-card__button--secondary:hover,.chakra-ui-dark .ai-chat-contact-card__button--secondary:hover,.dark .ai-chat-contact-card__button--secondary:hover,[data-theme=dark] .ai-chat-contact-card__button--secondary:hover{background:hsla(0,0%,100%,.15)}.ai-chat-contact-card-list{display:flex;flex-direction:column;gap:8px}.ai-chat-contact-card-list__header{align-items:center;color:var(--text-muted,#71717a);display:flex;font-size:13px;font-weight:500;gap:6px;margin-bottom:2px;margin-top:8px;padding:0 4px}.ai-chat-contact-card-list__stack{display:grid;gap:12px;grid-template-columns:repeat(3,minmax(0,1fr))}@media (max-width:900px){.ai-chat-contact-card-list__stack{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (max-width:600px){.ai-chat-contact-card-list__stack{grid-template-columns:1fr}}.ai-chat-contact-card-list__stack--widget{grid-template-columns:1fr}@container (min-width: 380px){.ai-chat-contact-card-list__stack--widget{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (max-width:520px){.ai-chat-contact-card-list__stack{grid-template-columns:1fr!important}.ai-chat-contact-card--horizontal{flex-direction:column}.ai-chat-contact-card--horizontal .ai-chat-contact-card__image-section{aspect-ratio:3/2;height:auto;min-width:100%;width:100%}}.ai-chat-contact-card__initials{align-items:center;display:flex;font-size:48px;font-weight:600;height:100%;justify-content:center;letter-spacing:.05em;text-transform:uppercase;width:100%}.ai-chat-contact-card--compact .ai-chat-contact-card__initials{font-size:32px}.ai-chat-contact-card--horizontal .ai-chat-contact-card__initials{font-size:28px}.ai-chat-contact-card--horizontal.ai-chat-contact-card--compact .ai-chat-contact-card__initials{font-size:22px}.ai-chat-form-card{--action-accent:var(--primary-color,#3b82f6);background:var(--bg-secondary,#f9fafb);border:1px solid var(--border-subtle,rgba(0,0,0,.06));border-radius:12px;box-sizing:border-box;margin:6px 0;overflow:hidden;padding:16px;width:100%}.ai-chat-widget.dark .ai-chat-form-card,.chakra-ui-dark .ai-chat-form-card,.dark .ai-chat-form-card,[data-theme=dark] .ai-chat-form-card{background:hsla(0,0%,100%,.05);border-color:hsla(0,0%,100%,.1)}.ai-chat-form-card--empty,.ai-chat-form-card--error,.ai-chat-form-card--skipped,.ai-chat-form-card--submitted{padding:12px 16px}.ai-chat-form-card__header{align-items:center;display:flex;gap:8px;margin-bottom:12px}.ai-chat-form-card__icon{font-size:18px}.ai-chat-form-card__title{color:var(--text-primary,#3e3e3e);font-size:15px;font-weight:600}.ai-chat-widget.dark .ai-chat-form-card__title,.chakra-ui-dark .ai-chat-form-card__title,.dark .ai-chat-form-card__title,[data-theme=dark] .ai-chat-form-card__title{color:#fff}.ai-chat-form-card__description{color:var(--text-muted,#71717a);font-size:13px;line-height:1.4;margin:0 0 12px}.ai-chat-form-card__context{color:var(--text-muted,#71717a);font-size:12px;font-style:italic;margin:0 0 12px}.ai-chat-form-card__error{color:#dc2626;font-size:13px;margin:0}.ai-chat-widget.dark .ai-chat-form-card__error,.chakra-ui-dark .ai-chat-form-card__error,.dark .ai-chat-form-card__error,[data-theme=dark] .ai-chat-form-card__error{color:#fca5a5}.ai-chat-form-card__success{color:#16a34a;font-size:13px;margin:0}.ai-chat-widget.dark .ai-chat-form-card__success,.chakra-ui-dark .ai-chat-form-card__success,.dark .ai-chat-form-card__success,[data-theme=dark] .ai-chat-form-card__success{color:#4ade80}.ai-chat-form-card__empty-text,.ai-chat-form-card__skipped-text{color:var(--text-muted,#71717a);font-size:13px;margin:0}.ai-chat-form-card__progress{align-items:center;display:flex;gap:12px;margin-bottom:16px}.ai-chat-form-card__progress-bar{background:var(--action-accent,var(--primary-color,#3b82f6));border-radius:2px;flex:1;height:4px;transition:width .3s ease}.ai-chat-form-card__progress-text{color:var(--text-muted,#71717a);font-size:12px;white-space:nowrap}.ai-chat-form-card__question{margin-bottom:16px}.ai-chat-form-card__question-text{color:var(--text-primary,#3e3e3e);font-size:14px;font-weight:500;line-height:1.4;margin:0 0 12px}.ai-chat-widget.dark .ai-chat-form-card__question-text,.chakra-ui-dark .ai-chat-form-card__question-text,.dark .ai-chat-form-card__question-text,[data-theme=dark] .ai-chat-form-card__question-text{color:#fff}.ai-chat-form-card__required{color:#dc2626;margin-left:2px}.ai-chat-form-card__answer{margin-top:8px}.ai-chat-form-card__textarea{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:var(--radius-md,8px);box-sizing:border-box;color:var(--text-primary,#3e3e3e);font-family:inherit;font-size:14px;min-height:80px;outline:none;padding:10px 12px;resize:vertical;transition:border-color .2s ease,box-shadow .2s ease;width:100%}.ai-chat-form-card__textarea:focus{border-color:var(--action-accent,var(--primary-color,#3b82f6));box-shadow:0 0 0 3px rgba(59,130,246,.1)}.ai-chat-form-card__textarea::placeholder{color:var(--text-muted,#9ca3af)}.ai-chat-widget.dark .ai-chat-form-card__textarea,.chakra-ui-dark .ai-chat-form-card__textarea,.dark .ai-chat-form-card__textarea,[data-theme=dark] .ai-chat-form-card__textarea{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-form-card__textarea:focus,.chakra-ui-dark .ai-chat-form-card__textarea:focus,.dark .ai-chat-form-card__textarea:focus,[data-theme=dark] .ai-chat-form-card__textarea:focus{border-color:var(--action-accent,var(--primary-color,#3b82f6));box-shadow:0 0 0 3px rgba(59,130,246,.2)}.ai-chat-form-card__options{display:flex;flex-direction:column;gap:8px}.ai-chat-form-card__option{align-items:center;background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:var(--radius-md,8px);cursor:pointer;display:flex;gap:10px;padding:10px 12px;transition:border-color .15s,background .15s}.ai-chat-form-card__option:hover{background:rgba(59,130,246,.05);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-widget.dark .ai-chat-form-card__option,.chakra-ui-dark .ai-chat-form-card__option,.dark .ai-chat-form-card__option,[data-theme=dark] .ai-chat-form-card__option{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1)}.ai-chat-widget.dark .ai-chat-form-card__option:hover,.chakra-ui-dark .ai-chat-form-card__option:hover,.dark .ai-chat-form-card__option:hover,[data-theme=dark] .ai-chat-form-card__option:hover{background:rgba(59,130,246,.15);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-form-card__option input{accent-color:var(--action-accent,var(--primary-color,#3b82f6));margin:0}.ai-chat-form-card__option-text{color:var(--text-primary,#3e3e3e);font-size:14px}.ai-chat-widget.dark .ai-chat-form-card__option-text,.chakra-ui-dark .ai-chat-form-card__option-text,.dark .ai-chat-form-card__option-text,[data-theme=dark] .ai-chat-form-card__option-text{color:#fff}.ai-chat-form-card__rating{display:flex;flex-wrap:wrap;gap:8px}.ai-chat-form-card__rating-btn{align-items:center;background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:var(--radius-md,8px);color:var(--text-primary,#3e3e3e);cursor:pointer;display:flex;font-size:14px;font-weight:500;height:40px;justify-content:center;transition:all .15s ease;width:40px}.ai-chat-form-card__rating-btn--selected,.ai-chat-form-card__rating-btn:hover{border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-form-card__rating-btn--selected{background:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-widget.dark .ai-chat-form-card__rating-btn,.chakra-ui-dark .ai-chat-form-card__rating-btn,.dark .ai-chat-form-card__rating-btn,[data-theme=dark] .ai-chat-form-card__rating-btn{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-form-card__rating-btn:hover,.chakra-ui-dark .ai-chat-form-card__rating-btn:hover,.dark .ai-chat-form-card__rating-btn:hover,[data-theme=dark] .ai-chat-form-card__rating-btn:hover{border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-widget.dark .ai-chat-form-card__rating-btn--selected,.chakra-ui-dark .ai-chat-form-card__rating-btn--selected,.dark .ai-chat-form-card__rating-btn--selected,[data-theme=dark] .ai-chat-form-card__rating-btn--selected{background:var(--action-accent,var(--primary-color,#3b82f6));border-color:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-form-card__actions{align-items:center;border-top:1px solid var(--border-subtle,rgba(0,0,0,.08));display:flex;gap:8px;margin-top:16px;padding-top:16px}.ai-chat-widget.dark .ai-chat-form-card__actions,.chakra-ui-dark .ai-chat-form-card__actions,.dark .ai-chat-form-card__actions,[data-theme=dark] .ai-chat-form-card__actions{border-color:hsla(0,0%,100%,.08)}.ai-chat-form-card__actions-spacer{flex:1}.ai-chat-form-card__btn{border:none;border-radius:9999px;cursor:pointer;font-family:inherit;font-size:13px;font-weight:500;padding:8px 16px;transition:all .2s ease}.ai-chat-form-card__btn:disabled{cursor:not-allowed;opacity:.5}.ai-chat-form-card__btn--primary{background:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-form-card__btn--primary:hover:not(:disabled){opacity:.9;transform:translateY(-1px)}.ai-chat-form-card__btn--secondary{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);color:var(--text-primary,#3e3e3e)}.ai-chat-form-card__btn--secondary:hover:not(:disabled){border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-widget.dark .ai-chat-form-card__btn--secondary,.chakra-ui-dark .ai-chat-form-card__btn--secondary,.dark .ai-chat-form-card__btn--secondary,[data-theme=dark] .ai-chat-form-card__btn--secondary{background:hsla(0,0%,100%,.05);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-form-card__btn--ghost{background:transparent;color:var(--text-muted,#71717a)}.ai-chat-form-card__btn--ghost:hover:not(:disabled){background:rgba(0,0,0,.05);color:var(--text-primary,#3e3e3e)}.ai-chat-widget.dark .ai-chat-form-card__btn--ghost:hover:not(:disabled),.chakra-ui-dark .ai-chat-form-card__btn--ghost:hover:not(:disabled),.dark .ai-chat-form-card__btn--ghost:hover:not(:disabled),[data-theme=dark] .ai-chat-form-card__btn--ghost:hover:not(:disabled){background:hsla(0,0%,100%,.05);color:#fff}.ai-chat-booking-card{--action-accent:var(--primary-color,#3b82f6);background:var(--bg-secondary,#f9fafb);border:1px solid var(--border-subtle,rgba(0,0,0,.06));border-radius:12px;box-sizing:border-box;margin:6px 0;padding:16px;width:100%}.ai-chat-widget.dark .ai-chat-booking-card,.chakra-ui-dark .ai-chat-booking-card,.dark .ai-chat-booking-card,[data-theme=dark] .ai-chat-booking-card{background:hsla(0,0%,100%,.05);border-color:hsla(0,0%,100%,.1)}.ai-chat-booking-card__header{align-items:center;display:flex;gap:8px;margin-bottom:12px}.ai-chat-booking-card__icon{flex-shrink:0;font-size:18px}.ai-chat-booking-card__title{color:var(--text-primary,#3e3e3e);font-size:15px;font-weight:600}.ai-chat-widget.dark .ai-chat-booking-card__title,.chakra-ui-dark .ai-chat-booking-card__title,.dark .ai-chat-booking-card__title,[data-theme=dark] .ai-chat-booking-card__title{color:#fff}.ai-chat-booking-card__content{display:flex;flex-direction:column;gap:12px}.ai-chat-booking-card__description{color:var(--text-muted,#71717a);font-size:13px;line-height:1.4;margin:0}.ai-chat-booking-card__empty{color:var(--text-muted,#71717a);font-size:13px;padding:16px;text-align:center}.ai-chat-booking-card__input{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:8px;box-sizing:border-box;color:var(--text-primary,#3e3e3e);font-family:inherit;font-size:14px;outline:none;padding:10px 12px;transition:border-color .2s ease,box-shadow .2s ease;width:100%}.ai-chat-booking-card__input:focus{border-color:var(--action-accent,var(--primary-color,#3b82f6));box-shadow:0 0 0 3px rgba(59,130,246,.1)}.ai-chat-booking-card__input::placeholder{color:var(--text-muted,#9ca3af)}.ai-chat-widget.dark .ai-chat-booking-card__input,.chakra-ui-dark .ai-chat-booking-card__input,.dark .ai-chat-booking-card__input,[data-theme=dark] .ai-chat-booking-card__input{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-booking-card__input:focus,.chakra-ui-dark .ai-chat-booking-card__input:focus,.dark .ai-chat-booking-card__input:focus,[data-theme=dark] .ai-chat-booking-card__input:focus{border-color:var(--action-accent,var(--primary-color,#3b82f6));box-shadow:0 0 0 3px rgba(59,130,246,.2)}.ai-chat-booking-card__label{color:var(--text-secondary,#6b7280);display:block;font-size:13px;font-weight:500;margin-bottom:4px}.ai-chat-widget.dark .ai-chat-booking-card__label,.chakra-ui-dark .ai-chat-booking-card__label,.dark .ai-chat-booking-card__label,[data-theme=dark] .ai-chat-booking-card__label{color:var(--text-secondary,#a1a1aa)}.ai-chat-booking-card__btn{border:none;border-radius:9999px;box-sizing:border-box;cursor:pointer;font-family:inherit;font-size:13px;font-weight:500;padding:10px 16px;transition:all .2s ease;width:100%}.ai-chat-booking-card__btn:disabled{cursor:not-allowed;opacity:.5}.ai-chat-booking-card__btn--primary{background:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-booking-card__btn--primary:hover:not(:disabled){opacity:.9;transform:translateY(-1px)}.ai-chat-booking-card__btn--secondary{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);color:var(--text-primary,#3e3e3e)}.ai-chat-booking-card__btn--secondary:hover:not(:disabled){border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-widget.dark .ai-chat-booking-card__btn--secondary,.chakra-ui-dark .ai-chat-booking-card__btn--secondary,.dark .ai-chat-booking-card__btn--secondary,[data-theme=dark] .ai-chat-booking-card__btn--secondary{background:hsla(0,0%,100%,.05);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-booking-card__btn--danger{background:rgba(220,38,38,.1);border:1px solid rgba(220,38,38,.2);color:#dc2626}.ai-chat-booking-card__btn--danger:hover:not(:disabled){background:rgba(220,38,38,.15)}.ai-chat-widget.dark .ai-chat-booking-card__btn--danger,.chakra-ui-dark .ai-chat-booking-card__btn--danger,.dark .ai-chat-booking-card__btn--danger,[data-theme=dark] .ai-chat-booking-card__btn--danger{background:rgba(239,68,68,.2);border-color:rgba(239,68,68,.3);color:#fca5a5}.ai-chat-booking-card__grid{display:grid;gap:10px;grid-template-columns:repeat(auto-fill,minmax(200px,1fr))}.ai-chat-booking-card__contact{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:8px;cursor:pointer;display:flex;flex-direction:column;font-family:inherit;padding:12px;text-align:left;transition:all .2s ease}.ai-chat-booking-card__contact:hover{background:rgba(59,130,246,.05);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-booking-card__contact--selected{border-width:2px}.ai-chat-widget.dark .ai-chat-booking-card__contact,.chakra-ui-dark .ai-chat-booking-card__contact,.dark .ai-chat-booking-card__contact,[data-theme=dark] .ai-chat-booking-card__contact{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1)}.ai-chat-widget.dark .ai-chat-booking-card__contact--selected,.chakra-ui-dark .ai-chat-booking-card__contact--selected,.dark .ai-chat-booking-card__contact--selected,[data-theme=dark] .ai-chat-booking-card__contact--selected{background:rgba(59,130,246,.15)}.ai-chat-booking-card__contact-name{color:var(--text-primary,#3e3e3e);font-size:14px;font-weight:500}.ai-chat-widget.dark .ai-chat-booking-card__contact-name,.chakra-ui-dark .ai-chat-booking-card__contact-name,.dark .ai-chat-booking-card__contact-name,[data-theme=dark] .ai-chat-booking-card__contact-name{color:#fff}.ai-chat-booking-card__contact-role{color:var(--text-muted,#71717a);font-size:12px;margin-top:2px}.ai-chat-booking-card__options{display:flex;flex-direction:column;gap:8px}.ai-chat-booking-card__option-btn{align-items:center;background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:8px;box-sizing:border-box;cursor:pointer;display:flex;font-family:inherit;gap:12px;padding:12px;text-align:left;transition:all .2s ease;width:100%}.ai-chat-booking-card__option-btn:hover{background:rgba(59,130,246,.05);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-widget.dark .ai-chat-booking-card__option-btn,.chakra-ui-dark .ai-chat-booking-card__option-btn,.dark .ai-chat-booking-card__option-btn,[data-theme=dark] .ai-chat-booking-card__option-btn{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1)}.ai-chat-booking-card__option-icon{flex-shrink:0;font-size:18px}.ai-chat-booking-card__option-text{color:var(--text-primary,#3e3e3e);font-size:14px;font-weight:500}.ai-chat-widget.dark .ai-chat-booking-card__option-text,.chakra-ui-dark .ai-chat-booking-card__option-text,.dark .ai-chat-booking-card__option-text,[data-theme=dark] .ai-chat-booking-card__option-text{color:#fff}.ai-chat-booking-card__date-group{margin-bottom:12px}.ai-chat-booking-card__date-header{color:var(--text-muted,#71717a);font-size:12px;font-weight:600;letter-spacing:.5px;margin-bottom:8px;text-transform:uppercase}.ai-chat-booking-card__slots{display:grid;gap:8px;grid-template-columns:repeat(auto-fill,minmax(120px,1fr))}.ai-chat-booking-card__slot{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:8px;color:var(--text-primary,#3e3e3e);cursor:pointer;font-family:inherit;font-size:13px;font-weight:500;padding:10px 12px;transition:all .2s ease}.ai-chat-booking-card__slot:hover{background:rgba(59,130,246,.05);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-booking-card__slot--selected{border-width:2px}.ai-chat-widget.dark .ai-chat-booking-card__slot,.chakra-ui-dark .ai-chat-booking-card__slot,.dark .ai-chat-booking-card__slot,[data-theme=dark] .ai-chat-booking-card__slot{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-booking-card__slot--selected,.chakra-ui-dark .ai-chat-booking-card__slot--selected,.dark .ai-chat-booking-card__slot--selected,[data-theme=dark] .ai-chat-booking-card__slot--selected{background:rgba(59,130,246,.15)}.ai-chat-booking-card__appointments{display:flex;flex-direction:column;gap:10px}.ai-chat-booking-card__appointment{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:8px;padding:12px}.ai-chat-widget.dark .ai-chat-booking-card__appointment,.chakra-ui-dark .ai-chat-booking-card__appointment,.dark .ai-chat-booking-card__appointment,[data-theme=dark] .ai-chat-booking-card__appointment{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1)}.ai-chat-booking-card__appointment-header{align-items:center;display:flex;gap:8px;justify-content:space-between;margin-bottom:6px}.ai-chat-booking-card__appointment-subject{color:var(--text-primary,#3e3e3e);font-size:14px;font-weight:500}.ai-chat-widget.dark .ai-chat-booking-card__appointment-subject,.chakra-ui-dark .ai-chat-booking-card__appointment-subject,.dark .ai-chat-booking-card__appointment-subject,[data-theme=dark] .ai-chat-booking-card__appointment-subject{color:#fff}.ai-chat-booking-card__appointment-status{border-radius:4px;font-size:11px;font-weight:600;padding:2px 6px;text-transform:uppercase}.ai-chat-booking-card__appointment-status--confirmed,.ai-chat-booking-card__appointment-status--pending{background:rgba(34,197,94,.1);color:#16a34a}.ai-chat-widget.dark .ai-chat-booking-card__appointment-status--confirmed,.ai-chat-widget.dark .ai-chat-booking-card__appointment-status--pending,.chakra-ui-dark .ai-chat-booking-card__appointment-status--confirmed,.chakra-ui-dark .ai-chat-booking-card__appointment-status--pending,.dark .ai-chat-booking-card__appointment-status--confirmed,.dark .ai-chat-booking-card__appointment-status--pending,[data-theme=dark] .ai-chat-booking-card__appointment-status--confirmed,[data-theme=dark] .ai-chat-booking-card__appointment-status--pending{background:rgba(34,197,94,.2);color:#4ade80}.ai-chat-booking-card__appointment-status--cancelled{background:hsla(220,9%,46%,.1);color:#6b7280}.ai-chat-booking-card__appointment-time{color:var(--text-secondary,#6b7280);font-size:13px;margin-bottom:4px}.ai-chat-widget.dark .ai-chat-booking-card__appointment-time,.chakra-ui-dark .ai-chat-booking-card__appointment-time,.dark .ai-chat-booking-card__appointment-time,[data-theme=dark] .ai-chat-booking-card__appointment-time{color:var(--text-secondary,#a1a1aa)}.ai-chat-booking-card__appointment-contact{color:var(--text-muted,#71717a);font-size:12px}.ai-chat-booking-card__summary{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:8px;margin-bottom:12px;padding:12px}.ai-chat-widget.dark .ai-chat-booking-card__summary,.chakra-ui-dark .ai-chat-booking-card__summary,.dark .ai-chat-booking-card__summary,[data-theme=dark] .ai-chat-booking-card__summary{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1)}.ai-chat-booking-card__summary-row{align-items:center;display:flex;justify-content:space-between;padding:6px 0}.ai-chat-booking-card__summary-row:not(:last-child){border-bottom:1px solid var(--border-subtle,rgba(0,0,0,.06))}.ai-chat-widget.dark .ai-chat-booking-card__summary-row:not(:last-child),.chakra-ui-dark .ai-chat-booking-card__summary-row:not(:last-child),.dark .ai-chat-booking-card__summary-row:not(:last-child),[data-theme=dark] .ai-chat-booking-card__summary-row:not(:last-child){border-bottom-color:hsla(0,0%,100%,.08)}.ai-chat-booking-card__summary-label{color:var(--text-muted,#71717a);font-size:12px;font-weight:600;letter-spacing:.5px;text-transform:uppercase}.ai-chat-booking-card__summary-value{color:var(--text-primary,#3e3e3e);font-size:14px;font-weight:500;text-align:right}.ai-chat-widget.dark .ai-chat-booking-card__summary-value,.chakra-ui-dark .ai-chat-booking-card__summary-value,.dark .ai-chat-booking-card__summary-value,[data-theme=dark] .ai-chat-booking-card__summary-value{color:#fff}.ai-chat-booking-card__link{display:inline-block;font-size:13px;font-weight:500;margin-top:8px;text-decoration:none;transition:opacity .2s ease}.ai-chat-booking-card__link:hover{opacity:.8;text-decoration:underline}.ai-chat-booking-card--success{background:rgba(34,197,94,.05);border-color:rgba(34,197,94,.2)}.ai-chat-widget.dark .ai-chat-booking-card--success,.chakra-ui-dark .ai-chat-booking-card--success,.dark .ai-chat-booking-card--success,[data-theme=dark] .ai-chat-booking-card--success{background:rgba(34,197,94,.1);border-color:rgba(34,197,94,.3)}.ai-chat-booking-card--pending{background:rgba(234,179,8,.05);border-color:rgba(234,179,8,.2)}.ai-chat-widget.dark .ai-chat-booking-card--pending,.chakra-ui-dark .ai-chat-booking-card--pending,.dark .ai-chat-booking-card--pending,[data-theme=dark] .ai-chat-booking-card--pending{background:rgba(234,179,8,.1);border-color:rgba(234,179,8,.3)}.ai-chat-booking-card--cancelled{background:hsla(220,9%,46%,.05);border-color:hsla(220,9%,46%,.2)}.ai-chat-widget.dark .ai-chat-booking-card--cancelled,.chakra-ui-dark .ai-chat-booking-card--cancelled,.dark .ai-chat-booking-card--cancelled,[data-theme=dark] .ai-chat-booking-card--cancelled{background:hsla(220,9%,46%,.1);border-color:hsla(220,9%,46%,.3)}.ai-chat-booking-card--error{background:rgba(239,68,68,.05);border-color:rgba(239,68,68,.2)}.ai-chat-widget.dark .ai-chat-booking-card--error,.chakra-ui-dark .ai-chat-booking-card--error,.dark .ai-chat-booking-card--error,[data-theme=dark] .ai-chat-booking-card--error{background:rgba(239,68,68,.1);border-color:rgba(239,68,68,.3)}.ai-chat-booking-card__pending-text,.ai-chat-booking-card__success-text{color:var(--text-secondary,#6b7280);font-size:13px;margin:0}.ai-chat-widget.dark .ai-chat-booking-card__pending-text,.ai-chat-widget.dark .ai-chat-booking-card__success-text,.chakra-ui-dark .ai-chat-booking-card__pending-text,.chakra-ui-dark .ai-chat-booking-card__success-text,.dark .ai-chat-booking-card__pending-text,.dark .ai-chat-booking-card__success-text,[data-theme=dark] .ai-chat-booking-card__pending-text,[data-theme=dark] .ai-chat-booking-card__success-text{color:var(--text-secondary,#a1a1aa)}.ai-chat-booking-card__error{color:#dc2626;font-size:13px;margin:0}.ai-chat-widget.dark .ai-chat-booking-card__error,.chakra-ui-dark .ai-chat-booking-card__error,.dark .ai-chat-booking-card__error,[data-theme=dark] .ai-chat-booking-card__error{color:#fca5a5}.chat-fullpage{--fp-max-width:800px;--fp-padding-x:16px;--fp-padding-top-mobile:64px;--fp-padding-top-desktop:16px;--fp-padding-bottom:200px;--fp-input-bottom:0}.chat-fullpage .ai-chat-messages{background:transparent;height:100%;margin:0 auto;max-width:var(--fp-max-width);padding:var(--fp-padding-top-desktop) var(--fp-padding-x) var(--fp-padding-bottom)}@media (max-width:768px){.chat-fullpage .ai-chat-messages{padding-top:var(--fp-padding-top-mobile)}}.chat-fullpage .ai-chat-message{animation:none}.chat-fullpage .ai-chat-message.user{max-width:85%}.chat-fullpage .ai-chat-message.user .ai-chat-message-content{background:var(--bg-muted,#f4f4f5);border-radius:24px;color:#000;padding:8px 16px}.chat-fullpage.dark .ai-chat-message.user .ai-chat-message-content{background:var(--bg-muted,#27272a);color:#fff}.chat-fullpage .ai-chat-message.assistant{width:100%}.chat-fullpage .ai-chat-message.assistant .ai-chat-message-content{color:#000;padding:8px 16px}.chat-fullpage.dark .ai-chat-message.assistant .ai-chat-message-content{color:#fff}.chat-fullpage .ai-chat-message.tool{margin:0;padding:0;width:100%}.chat-fullpage .ai-chat-welcome{align-items:center;display:flex;flex-direction:column;gap:24px;justify-content:center;min-height:60vh;padding:24px;text-align:center}.chat-fullpage .ai-chat-welcome-title{font-size:32px;font-weight:600}.chat-fullpage .ai-chat-welcome-text{color:var(--text-muted,#71717a);font-size:18px;max-width:400px}.chat-fullpage .ai-chat-input-container{background:transparent;bottom:0;left:0;padding:16px 16px calc(16px + env(safe-area-inset-bottom));position:fixed;right:0;z-index:20}.chat-fullpage .ai-chat-input-container:after{background:var(--bg-primary,#fff);bottom:0;content:\"\";height:calc(40% + 16px);left:0;pointer-events:none;position:absolute;right:0;z-index:-1}.chat-fullpage.dark .ai-chat-input-container:after{background:var(--bg-primary,#18181b)}@media (min-width:769px){.chat-fullpage .ai-chat-input-container{background:transparent;bottom:var(--fp-input-bottom);left:50%;max-width:var(--fp-max-width);padding:0;position:absolute;right:auto;transform:translateX(-50%);width:100%}}.chat-fullpage .ai-chat-input-wrapper{background:var(--bg-muted,#f4f4f5);border:1px solid var(--border-muted,#e4e4e7);border-radius:24px;box-shadow:0 1px 8px rgba(0,0,0,.06);margin:0 auto;max-width:var(--fp-max-width)}.chat-fullpage.dark .ai-chat-input-wrapper{background:var(--bg-muted,#27272a);border-color:var(--border-muted,#3f3f46);box-shadow:0 1px 12px rgba(0,0,0,.25)}.chat-fullpage .ai-chat-scroll-button{bottom:100px}@media (min-width:769px){.chat-fullpage .ai-chat-scroll-button{bottom:90px}}.chat-fullpage .ai-chat-suggested-questions{display:flex;flex-wrap:wrap;gap:8px;justify-content:center;max-width:600px}.chat-fullpage .ai-chat-suggested-question{border-radius:9999px;font-size:14px;padding:8px 16px}.chat-fullpage .ai-chat-follow-up-suggestions{margin-top:12px}.chat-fullpage .ai-chat-follow-up-item.question-type{background:transparent;border:1px solid var(--border-default,#d4d4d8);color:#000}.chat-fullpage .ai-chat-follow-up-item.question-type:hover{background:var(--bg-hover,#f4f4f5)}.chat-fullpage.dark .ai-chat-follow-up-item.question-type{background:transparent;border:1px solid var(--border-subtle,#52525b);color:#fff}.chat-fullpage.dark .ai-chat-follow-up-item.question-type:hover{background:var(--bg-hover,#3f3f46)}.chat-fullpage .ai-chat-typing{padding:8px 16px}@media (max-width:480px){body.ai-chat-widget-open{height:100%!important;overflow:hidden!important;position:fixed!important;touch-action:none!important;width:100%!important}.ai-chat-widget-container.is-open{height:100vh!important;height:100dvh!important;width:100vw!important;z-index:var(--widget-z-index,2147483647)!important}.ai-chat-widget-container.is-open,.ai-chat-widget-container.is-open .ai-chat-window{bottom:0!important;left:0!important;position:fixed!important;right:0!important;top:0!important}.ai-chat-widget-container.is-open .ai-chat-window{animation:none!important;border:none!important;border-radius:0!important;box-shadow:none!important;height:100%!important;max-height:100%!important;max-width:100%!important;outline:none!important;transform:none!important;width:100%!important}.ai-chat-widget-container.is-open .ai-chat-button{display:none!important;pointer-events:none!important;visibility:hidden!important}.ai-chat-widget-container.is-open .ai-chat-header{border-radius:0!important;flex-shrink:0;padding-left:max(16px,env(safe-area-inset-left));padding-right:max(16px,env(safe-area-inset-right));padding-top:max(12px,env(safe-area-inset-top));position:relative;z-index:100}.ai-chat-widget-container.is-open .ai-chat-messages{-webkit-overflow-scrolling:touch;flex:1;overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain;padding-bottom:120px;padding-left:max(16px,env(safe-area-inset-left));padding-right:max(16px,env(safe-area-inset-right));touch-action:pan-y}.ai-chat-widget-container.is-open .ai-chat-input-container{background:var(--bg-primary,#fff);bottom:0!important;left:0!important;padding:8px max(12px,env(safe-area-inset-right)) max(16px,calc(env(safe-area-inset-bottom) + 8px)) max(12px,env(safe-area-inset-left));position:fixed!important;right:0!important;z-index:100}.ai-chat-widget.dark .ai-chat-widget-container.is-open .ai-chat-input-container{background:var(--bg-primary,#282625)}.ai-chat-widget-container.is-open .ai-chat-input-container:after{display:none}.ai-chat-widget-container.is-open .ai-chat-input-wrapper{margin:0;max-width:100%}.ai-chat-widget-container.is-open .ai-chat-scroll-button{bottom:calc(80px + env(safe-area-inset-bottom))}.ai-chat-widget-container.is-open .ai-chat-welcome{padding:16px 0}.ai-chat-widget-container.is-open .ai-chat-welcome-title{font-size:24px}.ai-chat-widget-container.is-open .ai-chat-suggested-questions{align-items:stretch;flex-direction:column}.ai-chat-widget-container.is-open .ai-chat-suggested-question{text-align:center;width:100%}}@media (min-width:481px) and (max-width:768px){.ai-chat-widget-container.is-open .ai-chat-window{border-radius:22px 22px 44px 44px;max-height:calc(100vh - 100px);max-width:calc(100vw - 32px)}}";
|
|
30849
|
+
styleInject(css_248z$1);
|
|
30850
|
+
|
|
30851
|
+
var css_248z = ".ai-chat-data-policy-view{display:flex;flex:1;flex-direction:column;min-height:0;overflow:hidden}.ai-chat-data-policy-content{-webkit-overflow-scrolling:touch;flex:1;overflow-y:auto;padding:20px 16px 40px}.ai-chat-data-policy-intro{margin-bottom:24px}.ai-chat-data-policy-intro p{color:var(--text-primary,#18181b);font-size:13px;line-height:1.6;margin:0;text-align:left}.ai-chat-widget.dark .ai-chat-data-policy-intro p{color:var(--text-primary,#fafafa)}.ai-chat-data-policy-intro strong{color:var(--text-primary,#18181b)}.ai-chat-widget.dark .ai-chat-data-policy-intro strong{color:var(--text-primary,#fafafa)}.ai-chat-data-policy-section{margin-bottom:20px}.ai-chat-data-policy-section h3{color:var(--text-primary,#18181b);font-size:14px;font-weight:600;margin:0 0 12px}.ai-chat-widget.dark .ai-chat-data-policy-section h3{color:var(--text-primary,#fafafa)}.ai-chat-data-policy-section p{color:var(--text-secondary,#52525b);font-size:12px;line-height:1.6;margin:0 0 12px;text-align:justify;text-justify:inter-word}.ai-chat-widget.dark .ai-chat-data-policy-section p{color:var(--text-secondary,#a1a1aa)}.ai-chat-data-policy-section p strong{color:var(--text-primary,#18181b)}.ai-chat-widget.dark .ai-chat-data-policy-section p strong{color:var(--text-primary,#fafafa)}.ai-chat-data-policy-section p:last-child{margin-bottom:0}";
|
|
29690
30852
|
styleInject(css_248z);
|
|
29691
30853
|
|
|
29692
30854
|
// Icon components mapping
|
|
@@ -29694,11 +30856,23 @@ const iconComponents = {
|
|
|
29694
30856
|
FiMessageCircle: () => (jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsxRuntime.jsx("path", { d: "M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" }) })),
|
|
29695
30857
|
FiChevronDown: () => (jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: jsxRuntime.jsx("polyline", { points: "6 9 12 15 18 9" }) })),
|
|
29696
30858
|
};
|
|
29697
|
-
const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = false, previewConfig, position = 'bottom-right', primaryColor, size, headerTitle, welcomeTitle, welcomeMessage, placeholder, theme, suggestedQuestions, customStyles, currentRoute, defaultOpen = false, zIndex, onOpen, onClose, onMessage, onError, mode = 'bubble', }) => {
|
|
30859
|
+
const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = false, previewConfig, demoMode = false, demoInput, demoOutput, onDemoComplete, position = 'bottom-right', primaryColor, size, headerTitle, welcomeTitle, welcomeMessage, placeholder, welcomeBubbleText, triggerType, triggerText, theme, suggestedQuestions, customStyles, currentRoute, defaultOpen = false, zIndex, containerMode = false, onOpen, onClose, onMessage, onError, mode = 'bubble', }) => {
|
|
29698
30860
|
const [isOpen, setIsOpen] = React.useState(defaultOpen);
|
|
30861
|
+
const [inputBarValue, setInputBarValue] = React.useState('');
|
|
29699
30862
|
const [autoDetectedTheme, setAutoDetectedTheme] = React.useState('light');
|
|
30863
|
+
const [showWelcomeBubble, setShowWelcomeBubble] = React.useState(false);
|
|
29700
30864
|
const widgetRef = React.useRef(null);
|
|
29701
30865
|
const containerRef = React.useRef(null);
|
|
30866
|
+
// Demo mode state - track whether demo has been activated and completed
|
|
30867
|
+
const [demoMessages, setDemoMessages] = React.useState([]);
|
|
30868
|
+
const [isDemoComplete, setIsDemoComplete] = React.useState(false);
|
|
30869
|
+
const [isDemoTyping, setIsDemoTyping] = React.useState(false);
|
|
30870
|
+
const [demoInputBarText, setDemoInputBarText] = React.useState(''); // For animating text in input bar
|
|
30871
|
+
const [isDemoAnimatingInput, setIsDemoAnimatingInput] = React.useState(false); // True while typing into input bar
|
|
30872
|
+
const [isDemoActive, setIsDemoActive] = React.useState(false); // True when demo animation is running
|
|
30873
|
+
const demoStartedRef = React.useRef(false);
|
|
30874
|
+
// Track if user has sent a real message (transitions from demo to real chat)
|
|
30875
|
+
const [userSentRealMessage, setUserSentRealMessage] = React.useState(false);
|
|
29702
30876
|
// Determine mode
|
|
29703
30877
|
const isEmbedded = mode === 'embedded';
|
|
29704
30878
|
// Default config for preview mode
|
|
@@ -29718,6 +30892,7 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
29718
30892
|
showChatHistory: true,
|
|
29719
30893
|
showTimestamps: true,
|
|
29720
30894
|
showTypingIndicator: true,
|
|
30895
|
+
showToolCalls: false,
|
|
29721
30896
|
enableFileUpload: false,
|
|
29722
30897
|
enableFeedback: true,
|
|
29723
30898
|
},
|
|
@@ -29743,29 +30918,54 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
29743
30918
|
...previewConfig?.behavior,
|
|
29744
30919
|
},
|
|
29745
30920
|
};
|
|
29746
|
-
// Always call useChat hook (React rules), but skip initialization in preview mode
|
|
30921
|
+
// Always call useChat hook (React rules), but skip initialization in preview mode or during demo
|
|
30922
|
+
// Skip initialization during demo to prevent loading old conversations
|
|
30923
|
+
// Note: We still pass the real widgetId so createDemoConversation works correctly
|
|
30924
|
+
const shouldSkipInit = previewMode || (demoMode && !userSentRealMessage);
|
|
29747
30925
|
const chatHook = useChat({
|
|
29748
30926
|
widgetId: previewMode ? '__preview__' : (widgetId || '__preview__'),
|
|
29749
30927
|
apiUrl,
|
|
29750
30928
|
currentRoute,
|
|
29751
|
-
onMessage:
|
|
29752
|
-
onError:
|
|
29753
|
-
skipInitialization:
|
|
30929
|
+
onMessage: shouldSkipInit ? undefined : onMessage,
|
|
30930
|
+
onError: shouldSkipInit ? undefined : onError,
|
|
30931
|
+
skipInitialization: shouldSkipInit, // Don't make API calls in preview mode or during demo
|
|
29754
30932
|
});
|
|
29755
30933
|
// Extract values from hook or use preview defaults
|
|
29756
|
-
const
|
|
29757
|
-
const
|
|
29758
|
-
const
|
|
29759
|
-
const error = previewMode ? null : chatHook.error;
|
|
30934
|
+
const hookMessages = previewMode ? [] : chatHook.messages;
|
|
30935
|
+
const hookIsLoading = previewMode ? false : chatHook.isLoading;
|
|
30936
|
+
const hookIsTyping = previewMode ? false : chatHook.isTyping;
|
|
29760
30937
|
const config = previewMode ? mergedPreviewConfig : chatHook.config;
|
|
29761
|
-
const
|
|
30938
|
+
const hookSendMessage = previewMode ? (() => Promise.resolve()) : chatHook.sendMessage;
|
|
29762
30939
|
const submitFeedback = previewMode ? (() => Promise.resolve()) : chatHook.submitFeedback;
|
|
30940
|
+
const dismissAction = previewMode ? (() => Promise.resolve()) : chatHook.dismissAction;
|
|
29763
30941
|
const conversations = previewMode ? [] : chatHook.conversations;
|
|
29764
30942
|
const loadConversations = previewMode ? (() => { }) : chatHook.loadConversations;
|
|
29765
30943
|
const switchConversation = previewMode ? (() => Promise.resolve()) : chatHook.switchConversation;
|
|
29766
30944
|
const startNewConversation = previewMode ? (() => { }) : chatHook.startNewConversation;
|
|
29767
30945
|
const deleteConversation = previewMode ? (() => { }) : chatHook.deleteConversation;
|
|
30946
|
+
const createDemoConversation = previewMode ? (() => Promise.resolve(null)) : chatHook.createDemoConversation;
|
|
29768
30947
|
const conversationId = previewMode ? '' : chatHook.conversationId;
|
|
30948
|
+
// Message display logic:
|
|
30949
|
+
// - During demo (before user sends real message): show demoMessages
|
|
30950
|
+
// - After user sends real message: show hookMessages
|
|
30951
|
+
const showDemoMessages = demoMode && demoMessages.length > 0 && !userSentRealMessage;
|
|
30952
|
+
const messages = showDemoMessages ? demoMessages : hookMessages;
|
|
30953
|
+
const isLoading = showDemoMessages ? false : hookIsLoading;
|
|
30954
|
+
const isTyping = showDemoMessages ? isDemoTyping : hookIsTyping;
|
|
30955
|
+
// Send message handler - transitions from demo to real chat when user sends first message
|
|
30956
|
+
const sendMessage = React.useCallback((content) => {
|
|
30957
|
+
if (demoMode && isDemoActive && !isDemoComplete) {
|
|
30958
|
+
// Demo animation is still running - ignore user input
|
|
30959
|
+
return Promise.resolve();
|
|
30960
|
+
}
|
|
30961
|
+
// User is sending a real message - transition to real chat
|
|
30962
|
+
if (demoMode && !userSentRealMessage) {
|
|
30963
|
+
setUserSentRealMessage(true);
|
|
30964
|
+
// Clear demo messages so hook messages take over
|
|
30965
|
+
setDemoMessages([]);
|
|
30966
|
+
}
|
|
30967
|
+
return hookSendMessage(content);
|
|
30968
|
+
}, [demoMode, isDemoActive, isDemoComplete, userSentRealMessage, hookSendMessage]);
|
|
29769
30969
|
// Auto-detect theme from background
|
|
29770
30970
|
React.useEffect(() => {
|
|
29771
30971
|
if (!containerRef.current)
|
|
@@ -29791,8 +30991,16 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
29791
30991
|
mediaQuery.removeEventListener('change', handleMediaChange);
|
|
29792
30992
|
};
|
|
29793
30993
|
}, [config]);
|
|
29794
|
-
//
|
|
30994
|
+
// Check if device is mobile
|
|
30995
|
+
const isMobile = typeof window !== 'undefined' && window.innerWidth <= 480;
|
|
30996
|
+
// Handle auto-open (only for bubble mode, disabled on mobile)
|
|
29795
30997
|
React.useEffect(() => {
|
|
30998
|
+
// Never auto-open on mobile devices
|
|
30999
|
+
if (isMobile)
|
|
31000
|
+
return undefined;
|
|
31001
|
+
// Don't auto-open if demo mode is active - let demo animation control opening
|
|
31002
|
+
if (demoMode && !isDemoComplete)
|
|
31003
|
+
return undefined;
|
|
29796
31004
|
if (!isEmbedded && config?.settings.autoOpen) {
|
|
29797
31005
|
const delay = config.settings.autoOpenDelay || 0;
|
|
29798
31006
|
const timer = setTimeout(() => {
|
|
@@ -29802,7 +31010,7 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
29802
31010
|
return () => clearTimeout(timer);
|
|
29803
31011
|
}
|
|
29804
31012
|
return undefined;
|
|
29805
|
-
}, [config, onOpen, isEmbedded]);
|
|
31013
|
+
}, [config, onOpen, isEmbedded, isMobile, demoMode, isDemoComplete]);
|
|
29806
31014
|
// Handle close on Escape key (only for bubble mode)
|
|
29807
31015
|
React.useEffect(() => {
|
|
29808
31016
|
if (!isOpen || isEmbedded)
|
|
@@ -29816,6 +31024,203 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
29816
31024
|
document.addEventListener('keydown', handleEscapeKey);
|
|
29817
31025
|
return () => document.removeEventListener('keydown', handleEscapeKey);
|
|
29818
31026
|
}, [isOpen, onClose, isEmbedded]);
|
|
31027
|
+
// Handle body scroll lock on mobile when widget is open
|
|
31028
|
+
React.useEffect(() => {
|
|
31029
|
+
if (!isOpen || isEmbedded)
|
|
31030
|
+
return;
|
|
31031
|
+
// Only apply scroll lock on mobile
|
|
31032
|
+
const checkMobile = window.innerWidth <= 480;
|
|
31033
|
+
if (!checkMobile)
|
|
31034
|
+
return;
|
|
31035
|
+
// Add class to body to lock scrolling
|
|
31036
|
+
document.body.classList.add('ai-chat-widget-open');
|
|
31037
|
+
return () => {
|
|
31038
|
+
document.body.classList.remove('ai-chat-widget-open');
|
|
31039
|
+
};
|
|
31040
|
+
}, [isOpen, isEmbedded]);
|
|
31041
|
+
// Handle welcome bubble visibility based on frequency setting
|
|
31042
|
+
// Frequency options: 'always' (every page visit), 'session', 'weekly', 'monthly'
|
|
31043
|
+
React.useEffect(() => {
|
|
31044
|
+
if (isEmbedded || previewMode)
|
|
31045
|
+
return;
|
|
31046
|
+
const bubbleText = welcomeBubbleText ?? config?.appearance?.welcomeBubbleText;
|
|
31047
|
+
if (!bubbleText) {
|
|
31048
|
+
setShowWelcomeBubble(false);
|
|
31049
|
+
return;
|
|
31050
|
+
}
|
|
31051
|
+
const frequency = config?.appearance?.welcomeBubbleFrequency ?? 'session';
|
|
31052
|
+
const storageKey = `ai-chat-bubble-dismissed-${widgetId || 'default'}`;
|
|
31053
|
+
// Check if bubble should be shown based on frequency
|
|
31054
|
+
const shouldShowBubble = () => {
|
|
31055
|
+
if (frequency === 'always') {
|
|
31056
|
+
// Always show on every page visit (no storage check)
|
|
31057
|
+
return true;
|
|
31058
|
+
}
|
|
31059
|
+
if (frequency === 'session') {
|
|
31060
|
+
// Show once per session
|
|
31061
|
+
return sessionStorage.getItem(storageKey) !== 'true';
|
|
31062
|
+
}
|
|
31063
|
+
// For weekly/monthly, use localStorage with timestamp
|
|
31064
|
+
try {
|
|
31065
|
+
const stored = localStorage.getItem(storageKey);
|
|
31066
|
+
if (!stored)
|
|
31067
|
+
return true;
|
|
31068
|
+
const dismissedAt = parseInt(stored, 10);
|
|
31069
|
+
if (isNaN(dismissedAt))
|
|
31070
|
+
return true;
|
|
31071
|
+
const now = Date.now();
|
|
31072
|
+
const weekMs = 7 * 24 * 60 * 60 * 1000;
|
|
31073
|
+
const monthMs = 30 * 24 * 60 * 60 * 1000;
|
|
31074
|
+
if (frequency === 'weekly') {
|
|
31075
|
+
return now - dismissedAt > weekMs;
|
|
31076
|
+
}
|
|
31077
|
+
if (frequency === 'monthly') {
|
|
31078
|
+
return now - dismissedAt > monthMs;
|
|
31079
|
+
}
|
|
31080
|
+
}
|
|
31081
|
+
catch {
|
|
31082
|
+
return true;
|
|
31083
|
+
}
|
|
31084
|
+
return true;
|
|
31085
|
+
};
|
|
31086
|
+
if (shouldShowBubble() && !isOpen) {
|
|
31087
|
+
setShowWelcomeBubble(true);
|
|
31088
|
+
}
|
|
31089
|
+
}, [widgetId, welcomeBubbleText, config, isOpen, isEmbedded, previewMode]);
|
|
31090
|
+
// Demo mode: animate typing into input bar, then open widget with conversation
|
|
31091
|
+
// This shows the widget in action without making expensive AI calls
|
|
31092
|
+
React.useEffect(() => {
|
|
31093
|
+
// Determine trigger type early (from prop or config)
|
|
31094
|
+
// IMPORTANT: For demo mode, we need config to be loaded to know the trigger type
|
|
31095
|
+
// If config is not loaded yet, wait for it (unless triggerType prop is provided)
|
|
31096
|
+
const effectiveTriggerTypeForDemo = triggerType ?? config?.appearance?.triggerType ?? 'button';
|
|
31097
|
+
const isInputBarTrigger = effectiveTriggerTypeForDemo === 'input-bar';
|
|
31098
|
+
// Debug logging
|
|
31099
|
+
console.log('[Widget] Demo effect check:', {
|
|
31100
|
+
demoMode,
|
|
31101
|
+
isDemoComplete,
|
|
31102
|
+
demoStartedRef: demoStartedRef.current,
|
|
31103
|
+
hasDemoInput: !!demoInput,
|
|
31104
|
+
hasDemoOutput: !!demoOutput,
|
|
31105
|
+
triggerType: effectiveTriggerTypeForDemo,
|
|
31106
|
+
isInputBar: isInputBarTrigger,
|
|
31107
|
+
isOpen,
|
|
31108
|
+
});
|
|
31109
|
+
// Start demo when demoMode is enabled and we have input/output
|
|
31110
|
+
const shouldStartDemo = demoMode && !isDemoComplete && !demoStartedRef.current && demoInput && demoOutput;
|
|
31111
|
+
if (!shouldStartDemo) {
|
|
31112
|
+
console.log('[Widget] Demo not starting:', { shouldStartDemo, reason: !demoMode ? 'demoMode false' : !demoInput ? 'no demoInput' : !demoOutput ? 'no demoOutput' : isDemoComplete ? 'already complete' : demoStartedRef.current ? 'already started' : 'unknown' });
|
|
31113
|
+
return;
|
|
31114
|
+
}
|
|
31115
|
+
// Wait for config to load before starting demo (unless triggerType prop is explicitly provided)
|
|
31116
|
+
// This prevents the demo from starting with wrong trigger type
|
|
31117
|
+
if (!triggerType && !config) {
|
|
31118
|
+
console.log('[Widget] Demo waiting for config or triggerType prop');
|
|
31119
|
+
return;
|
|
31120
|
+
}
|
|
31121
|
+
// For input-bar: start immediately (widget closed, type into bar)
|
|
31122
|
+
// For others: wait until widget is open
|
|
31123
|
+
if (!isInputBarTrigger && !isOpen) {
|
|
31124
|
+
console.log('[Widget] Demo waiting for widget to open (non-input-bar trigger)');
|
|
31125
|
+
return;
|
|
31126
|
+
}
|
|
31127
|
+
console.log('[Widget] Starting demo animation...', {
|
|
31128
|
+
triggerType: effectiveTriggerTypeForDemo,
|
|
31129
|
+
isInputBar: isInputBarTrigger,
|
|
31130
|
+
demoInput,
|
|
31131
|
+
demoOutput: demoOutput?.substring(0, 50) + '...',
|
|
31132
|
+
});
|
|
31133
|
+
demoStartedRef.current = true;
|
|
31134
|
+
setIsDemoActive(true);
|
|
31135
|
+
// Use stable IDs to prevent React from remounting components
|
|
31136
|
+
const userMessageId = 'demo-user-message';
|
|
31137
|
+
const assistantMessageId = 'demo-assistant-message';
|
|
31138
|
+
// Helper to create a message with stable ID
|
|
31139
|
+
const createMessage = (id, role, content, isStreaming = false) => ({
|
|
31140
|
+
id,
|
|
31141
|
+
message: { role, content },
|
|
31142
|
+
timestamp: new Date().toISOString(),
|
|
31143
|
+
sources: [],
|
|
31144
|
+
isStreaming,
|
|
31145
|
+
});
|
|
31146
|
+
// Animation sequence - runs to completion even if widget is closed
|
|
31147
|
+
// This ensures conversation is always created and saved consistently
|
|
31148
|
+
const runDemoAnimation = async () => {
|
|
31149
|
+
// STEP 1: Create conversation IMMEDIATELY at the start
|
|
31150
|
+
// This ensures we have a conversation ID before any animation
|
|
31151
|
+
console.log('[Widget] Creating demo conversation at start...');
|
|
31152
|
+
let conversationId = null;
|
|
31153
|
+
try {
|
|
31154
|
+
conversationId = await createDemoConversation(demoInput, demoOutput);
|
|
31155
|
+
if (conversationId) {
|
|
31156
|
+
console.log('[Widget] Demo conversation created:', conversationId);
|
|
31157
|
+
}
|
|
31158
|
+
}
|
|
31159
|
+
catch (err) {
|
|
31160
|
+
console.warn('[Widget] Failed to create demo conversation:', err);
|
|
31161
|
+
}
|
|
31162
|
+
// STEP 2: For input-bar trigger, animate typing into input bar first
|
|
31163
|
+
if (isInputBarTrigger && !isOpen) {
|
|
31164
|
+
setIsDemoAnimatingInput(true);
|
|
31165
|
+
// Wait before starting to type - ensure input bar is fully visible
|
|
31166
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
31167
|
+
// Type into input bar character by character
|
|
31168
|
+
const inputChars = demoInput.split('');
|
|
31169
|
+
for (let i = 0; i < inputChars.length; i++) {
|
|
31170
|
+
setDemoInputBarText(demoInput.slice(0, i + 1));
|
|
31171
|
+
// Slower typing speed for better visibility
|
|
31172
|
+
const delay = inputChars[i] === ' ' ? 100 : 60 + Math.random() * 40;
|
|
31173
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
31174
|
+
}
|
|
31175
|
+
// Longer pause after typing so user can read the question
|
|
31176
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
31177
|
+
setIsDemoAnimatingInput(false);
|
|
31178
|
+
setDemoInputBarText('');
|
|
31179
|
+
setIsOpen(true);
|
|
31180
|
+
onOpen?.();
|
|
31181
|
+
// Let widget open animation complete
|
|
31182
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
31183
|
+
}
|
|
31184
|
+
else {
|
|
31185
|
+
// For non-input-bar triggers, just wait for widget to settle
|
|
31186
|
+
await new Promise(resolve => setTimeout(resolve, 800));
|
|
31187
|
+
}
|
|
31188
|
+
// STEP 3: Add user message
|
|
31189
|
+
setDemoMessages([createMessage(userMessageId, 'user', demoInput)]);
|
|
31190
|
+
// Show typing indicator after a pause
|
|
31191
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
31192
|
+
setIsDemoTyping(true);
|
|
31193
|
+
// "Thinking" pause
|
|
31194
|
+
await new Promise(resolve => setTimeout(resolve, 1200));
|
|
31195
|
+
setIsDemoTyping(false);
|
|
31196
|
+
// STEP 4: Stream the response character by character
|
|
31197
|
+
const chars = demoOutput.split('');
|
|
31198
|
+
let currentText = '';
|
|
31199
|
+
// Initial message with empty content
|
|
31200
|
+
setDemoMessages([
|
|
31201
|
+
createMessage(userMessageId, 'user', demoInput),
|
|
31202
|
+
createMessage(assistantMessageId, 'assistant', '', true),
|
|
31203
|
+
]);
|
|
31204
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
31205
|
+
// Stream characters - continues even if widget is closed
|
|
31206
|
+
for (let i = 0; i < chars.length; i++) {
|
|
31207
|
+
currentText += chars[i];
|
|
31208
|
+
setDemoMessages([
|
|
31209
|
+
createMessage(userMessageId, 'user', demoInput),
|
|
31210
|
+
createMessage(assistantMessageId, 'assistant', currentText, i < chars.length - 1),
|
|
31211
|
+
]);
|
|
31212
|
+
const delay = chars[i] === ' ' ? 12 : 20;
|
|
31213
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
31214
|
+
}
|
|
31215
|
+
// STEP 5: Mark demo complete
|
|
31216
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
31217
|
+
setIsDemoComplete(true);
|
|
31218
|
+
setIsDemoActive(false);
|
|
31219
|
+
console.log('[Widget] Demo animation complete');
|
|
31220
|
+
onDemoComplete?.();
|
|
31221
|
+
};
|
|
31222
|
+
runDemoAnimation();
|
|
31223
|
+
}, [demoMode, isOpen, isDemoComplete, demoInput, demoOutput, onDemoComplete, triggerType, config?.appearance?.triggerType, onOpen, createDemoConversation]);
|
|
29819
31224
|
// Determine theme - use prop override if provided, otherwise auto-detect
|
|
29820
31225
|
const appearanceConfig = config?.appearance;
|
|
29821
31226
|
const effectiveTheme = theme ?? autoDetectedTheme;
|
|
@@ -29824,11 +31229,14 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
29824
31229
|
// Get accent color from prop or config (empty string means no accent color / vanilla mode)
|
|
29825
31230
|
const accentColor = primaryColor ?? appearanceConfig?.primaryColor ?? '';
|
|
29826
31231
|
// Apply prop overrides for live preview (props take priority over config)
|
|
29827
|
-
size || appearanceConfig?.size || 'small';
|
|
31232
|
+
const effectiveSize = size || appearanceConfig?.size || 'small';
|
|
29828
31233
|
const effectiveHeaderTitle = headerTitle ?? appearanceConfig?.headerTitle ?? '';
|
|
29829
31234
|
const effectiveWelcomeTitle = welcomeTitle ?? appearanceConfig?.welcomeTitle ?? '';
|
|
29830
31235
|
const effectiveWelcomeMessage = welcomeMessage ?? appearanceConfig?.welcomeMessage ?? '';
|
|
29831
31236
|
const effectivePlaceholder = placeholder ?? appearanceConfig?.placeholder ?? '';
|
|
31237
|
+
const effectiveWelcomeBubbleText = welcomeBubbleText ?? appearanceConfig?.welcomeBubbleText ?? '';
|
|
31238
|
+
const effectiveTriggerType = triggerType ?? appearanceConfig?.triggerType ?? 'button';
|
|
31239
|
+
const effectiveTriggerText = triggerText ?? appearanceConfig?.triggerText ?? 'Chat';
|
|
29832
31240
|
// Generate styles using simplified theme system
|
|
29833
31241
|
const simpleAppearance = {
|
|
29834
31242
|
accentColor};
|
|
@@ -29845,18 +31253,65 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
29845
31253
|
...customStyles,
|
|
29846
31254
|
...(zIndex !== undefined ? { '--widget-z-index': String(zIndex) } : {}),
|
|
29847
31255
|
};
|
|
31256
|
+
// Dismiss bubble and store based on frequency setting
|
|
31257
|
+
const dismissBubble = () => {
|
|
31258
|
+
setShowWelcomeBubble(false);
|
|
31259
|
+
const frequency = config?.appearance?.welcomeBubbleFrequency ?? 'session';
|
|
31260
|
+
const storageKey = `ai-chat-bubble-dismissed-${widgetId || 'default'}`;
|
|
31261
|
+
try {
|
|
31262
|
+
if (frequency === 'always') {
|
|
31263
|
+
// For 'always', use sessionStorage so it only hides until page refresh
|
|
31264
|
+
sessionStorage.setItem(storageKey, 'true');
|
|
31265
|
+
}
|
|
31266
|
+
else if (frequency === 'session') {
|
|
31267
|
+
sessionStorage.setItem(storageKey, 'true');
|
|
31268
|
+
}
|
|
31269
|
+
else {
|
|
31270
|
+
// For weekly/monthly, store timestamp in localStorage
|
|
31271
|
+
localStorage.setItem(storageKey, String(Date.now()));
|
|
31272
|
+
}
|
|
31273
|
+
}
|
|
31274
|
+
catch {
|
|
31275
|
+
// Ignore storage errors
|
|
31276
|
+
}
|
|
31277
|
+
};
|
|
29848
31278
|
const handleToggle = () => {
|
|
29849
31279
|
if (isEmbedded)
|
|
29850
31280
|
return;
|
|
29851
31281
|
const newState = !isOpen;
|
|
29852
31282
|
setIsOpen(newState);
|
|
31283
|
+
// Dismiss welcome bubble when chat is opened
|
|
31284
|
+
if (newState && showWelcomeBubble) {
|
|
31285
|
+
dismissBubble();
|
|
31286
|
+
}
|
|
29853
31287
|
if (newState) {
|
|
29854
31288
|
onOpen?.();
|
|
29855
31289
|
}
|
|
29856
31290
|
else {
|
|
31291
|
+
// Demo animation continues in background when widget is closed
|
|
31292
|
+
// The conversation was already created at the start, so closing is safe
|
|
31293
|
+
// When user reopens, they'll see the current state of the demo animation
|
|
29857
31294
|
onClose?.();
|
|
29858
31295
|
}
|
|
29859
31296
|
};
|
|
31297
|
+
const handleDismissBubble = (e) => {
|
|
31298
|
+
e.stopPropagation();
|
|
31299
|
+
dismissBubble();
|
|
31300
|
+
};
|
|
31301
|
+
// Handle input bar submit - opens widget and sends message
|
|
31302
|
+
const handleInputBarSubmit = React.useCallback((e) => {
|
|
31303
|
+
e.preventDefault();
|
|
31304
|
+
if (!inputBarValue.trim() || previewMode)
|
|
31305
|
+
return;
|
|
31306
|
+
// Open the widget
|
|
31307
|
+
setIsOpen(true);
|
|
31308
|
+
onOpen?.();
|
|
31309
|
+
// Send the message after a brief delay to allow widget to open
|
|
31310
|
+
setTimeout(() => {
|
|
31311
|
+
sendMessage(inputBarValue.trim());
|
|
31312
|
+
setInputBarValue('');
|
|
31313
|
+
}, 100);
|
|
31314
|
+
}, [inputBarValue, previewMode, onOpen, sendMessage]);
|
|
29860
31315
|
const handleFeedback = async (messageId, feedback) => {
|
|
29861
31316
|
await submitFeedback(messageId, feedback);
|
|
29862
31317
|
};
|
|
@@ -29872,24 +31327,36 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
29872
31327
|
sendMessage(actionInstruction);
|
|
29873
31328
|
};
|
|
29874
31329
|
// Don't render until config is loaded to avoid flash of unstyled content
|
|
31330
|
+
// Exception: If we have essential props (triggerType), allow rendering the trigger immediately
|
|
31331
|
+
// This improves perceived loading speed - users see the trigger while config loads
|
|
29875
31332
|
// In preview mode, config is always available
|
|
29876
|
-
|
|
31333
|
+
const canRenderWithoutConfig = !!triggerType;
|
|
31334
|
+
if (!config && !previewMode && !canRenderWithoutConfig) {
|
|
29877
31335
|
return null;
|
|
29878
31336
|
}
|
|
29879
31337
|
// Get button icon based on state
|
|
29880
31338
|
const IconComponent = isOpen ? iconComponents.FiChevronDown : iconComponents.FiMessageCircle;
|
|
29881
31339
|
// Embedded mode renders directly without wrapper positioning
|
|
29882
31340
|
if (isEmbedded) {
|
|
29883
|
-
return (jsxRuntime.jsx("div", { ref: containerRef, className: `ai-chat-widget ai-chat-widget-embedded ${effectiveTheme}`, style: { ...mergedStyles, width: '100%', height: '100%' }, children: jsxRuntime.jsx(ChatWindow, { messages: messages, isLoading: isLoading, isTyping: isTyping,
|
|
29884
|
-
}
|
|
29885
|
-
|
|
31341
|
+
return (jsxRuntime.jsx("div", { ref: containerRef, className: `ai-chat-widget ai-chat-widget-embedded ${effectiveTheme}`, style: { ...mergedStyles, width: '100%', height: '100%' }, children: jsxRuntime.jsx(ChatWindow, { messages: messages, isLoading: isLoading, isTyping: isTyping, config: config, onSendMessage: sendMessage, onClose: () => { }, onFeedback: handleFeedback, onActionClick: handleActionClick, onActionDismiss: dismissAction, conversations: conversations, onLoadConversations: loadConversations, onSwitchConversation: switchConversation, onStartNewConversation: startNewConversation, onDeleteConversation: deleteConversation, currentConversationId: conversationId, headerTitleOverride: effectiveHeaderTitle, welcomeTitleOverride: effectiveWelcomeTitle, welcomeMessageOverride: effectiveWelcomeMessage, placeholderOverride: effectivePlaceholder, suggestedQuestionsOverride: suggestedQuestions }) }));
|
|
31342
|
+
}
|
|
31343
|
+
// Determine trigger class for container
|
|
31344
|
+
const triggerClass = effectiveTriggerType === 'pill-text'
|
|
31345
|
+
? 'trigger-pill-text'
|
|
31346
|
+
: effectiveTriggerType === 'input-bar'
|
|
31347
|
+
? 'trigger-input-bar'
|
|
31348
|
+
: '';
|
|
31349
|
+
// Size class for CSS targeting (used by input-bar trigger for width matching)
|
|
31350
|
+
const sizeClass = `size-${effectiveSize}`;
|
|
31351
|
+
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} ${isOpen ? 'is-open' : ''} ${containerMode ? 'container-mode' : ''} ${triggerClass} ${sizeClass}`, children: [isOpen && (jsxRuntime.jsx(ChatWindow, { messages: demoMode && !isDemoComplete ? demoMessages : messages, isLoading: demoMode && !isDemoComplete ? false : isLoading, isTyping: demoMode && !isDemoComplete ? isDemoTyping : isTyping, config: config, onSendMessage: sendMessage, onClose: handleToggle, onFeedback: handleFeedback, onActionClick: handleActionClick, onActionDismiss: dismissAction,
|
|
29886
31352
|
// Chat history props (only active when persistConversation is true)
|
|
29887
31353
|
conversations: conversations, onLoadConversations: loadConversations, onSwitchConversation: switchConversation, onStartNewConversation: startNewConversation, onDeleteConversation: deleteConversation, currentConversationId: conversationId,
|
|
29888
31354
|
// Override props for live preview
|
|
29889
|
-
headerTitleOverride: effectiveHeaderTitle, welcomeTitleOverride: effectiveWelcomeTitle, welcomeMessageOverride: effectiveWelcomeMessage, placeholderOverride: effectivePlaceholder, suggestedQuestionsOverride: suggestedQuestions })), jsxRuntime.jsx("button", { className: `ai-chat-button ${isOpen ? 'is-open' : ''}`, onClick: handleToggle, "aria-label": isOpen ? "Minimize chat" : "Open chat", children: jsxRuntime.jsx("div", { className: "ai-chat-button-svg", children: jsxRuntime.jsx(IconComponent, {}) }) })] }) }));
|
|
31355
|
+
headerTitleOverride: effectiveHeaderTitle, welcomeTitleOverride: effectiveWelcomeTitle, welcomeMessageOverride: effectiveWelcomeMessage, placeholderOverride: effectivePlaceholder, suggestedQuestionsOverride: suggestedQuestions })), effectiveTriggerType === 'button' && !isOpen && effectiveWelcomeBubbleText && (previewMode || showWelcomeBubble) && (jsxRuntime.jsxs("div", { className: "ai-chat-welcome-bubble", onClick: handleToggle, children: [jsxRuntime.jsx("span", { children: effectiveWelcomeBubbleText }), jsxRuntime.jsx("button", { className: "ai-chat-welcome-bubble-close", onClick: handleDismissBubble, "aria-label": "Dismiss", children: jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }) }), jsxRuntime.jsx("div", { className: "ai-chat-welcome-bubble-arrow" })] })), effectiveTriggerType === 'button' && (jsxRuntime.jsx("button", { className: `ai-chat-button ${isOpen ? 'is-open' : ''}`, onClick: handleToggle, "aria-label": isOpen ? "Minimize chat" : "Open chat", children: jsxRuntime.jsx("div", { className: "ai-chat-button-svg", children: jsxRuntime.jsx(IconComponent, {}) }) })), effectiveTriggerType === 'pill-text' && (jsxRuntime.jsxs("button", { className: `ai-chat-trigger-pill ${isOpen ? 'is-open' : ''}`, onClick: handleToggle, "aria-label": isOpen ? "Close chat" : "Open chat", children: [jsxRuntime.jsx("div", { className: "ai-chat-trigger-pill-icon", children: jsxRuntime.jsx(IconComponent, {}) }), !isOpen && jsxRuntime.jsx("span", { children: effectiveTriggerText })] })), effectiveTriggerType === 'input-bar' && !isOpen && (jsxRuntime.jsxs("div", { className: "ai-chat-trigger-input-container", children: [jsxRuntime.jsx("button", { type: "button", className: "ai-chat-trigger-input-expand", onClick: handleToggle, "aria-label": "Open chat", children: jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsxRuntime.jsx("polyline", { points: "18 15 12 9 6 15" }) }) }), jsxRuntime.jsxs("form", { className: "ai-chat-trigger-input-wrapper", onSubmit: handleInputBarSubmit, children: [jsxRuntime.jsx("input", { type: "text", className: "ai-chat-trigger-input", placeholder: effectivePlaceholder || "Ask me anything...", value: isDemoAnimatingInput ? demoInputBarText : inputBarValue, onChange: (e) => !isDemoAnimatingInput && setInputBarValue(e.target.value), readOnly: isDemoAnimatingInput, "aria-label": "Chat input" }), jsxRuntime.jsx("button", { type: "submit", className: "ai-chat-trigger-input-btn", disabled: isDemoAnimatingInput ? !demoInputBarText.trim() : !inputBarValue.trim(), "aria-label": "Send message", children: jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("line", { x1: "22", y1: "2", x2: "11", y2: "13" }), jsxRuntime.jsx("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })] }) })] })] }))] }) }));
|
|
29890
31356
|
};
|
|
29891
31357
|
|
|
29892
31358
|
exports.ApiError = ApiError;
|
|
29893
31359
|
exports.ChatWidget = ChatWidget;
|
|
31360
|
+
exports.DataPolicyView = DataPolicyView;
|
|
29894
31361
|
exports.useChat = useChat;
|
|
29895
31362
|
//# sourceMappingURL=index.js.map
|