@chatwidgetai/chat-widget 0.3.1 → 0.3.8
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 +1890 -1018
- package/dist/ai-chat-widget.umd.js.map +1 -1
- package/dist/api/client.d.ts +21 -2
- package/dist/api/client.d.ts.map +1 -1
- package/dist/components/ChatWidget/hooks/useWidgetAppearance.d.ts +37 -0
- package/dist/components/ChatWidget/hooks/useWidgetAppearance.d.ts.map +1 -0
- package/dist/components/ChatWidget/icons.d.ts +2 -0
- package/dist/components/ChatWidget/icons.d.ts.map +1 -0
- package/dist/components/ChatWidget/parts/WidgetTriggers.d.ts +25 -0
- package/dist/components/ChatWidget/parts/WidgetTriggers.d.ts.map +1 -0
- package/dist/components/ChatWidget.d.ts.map +1 -1
- package/dist/components/ChatWindow.d.ts +5 -1
- package/dist/components/ChatWindow.d.ts.map +1 -1
- package/dist/components/DataPolicyView.d.ts.map +1 -1
- package/dist/components/WelcomeBubble.d.ts +12 -0
- package/dist/components/WelcomeBubble.d.ts.map +1 -0
- package/dist/components/icons.d.ts +21 -0
- package/dist/components/icons.d.ts.map +1 -0
- package/dist/components/triggers/ButtonTrigger.d.ts +11 -0
- package/dist/components/triggers/ButtonTrigger.d.ts.map +1 -0
- package/dist/components/triggers/InputBarTrigger.d.ts +18 -0
- package/dist/components/triggers/InputBarTrigger.d.ts.map +1 -0
- package/dist/components/triggers/PillTrigger.d.ts +12 -0
- package/dist/components/triggers/PillTrigger.d.ts.map +1 -0
- package/dist/components/triggers/index.d.ts +8 -0
- package/dist/components/triggers/index.d.ts.map +1 -0
- package/dist/hooks/useChat/action-handler.d.ts +17 -1
- package/dist/hooks/useChat/action-handler.d.ts.map +1 -1
- package/dist/hooks/useChat/action-lifecycle.d.ts.map +1 -1
- package/dist/hooks/useChat/index.d.ts +7 -0
- package/dist/hooks/useChat/index.d.ts.map +1 -1
- package/dist/hooks/useChat/message-hydration.d.ts.map +1 -1
- package/dist/hooks/useChat/stream-buffer.d.ts +18 -0
- package/dist/hooks/useChat/stream-buffer.d.ts.map +1 -0
- package/dist/hooks/useChat/stream-handlers.d.ts +3 -3
- package/dist/hooks/useChat/stream-handlers.d.ts.map +1 -1
- package/dist/hooks/useChat/stream-state.d.ts.map +1 -1
- package/dist/hooks/useChat/types.d.ts +2 -0
- package/dist/hooks/useChat/types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +1891 -1019
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1891 -1018
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +12 -85
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -212,31 +212,83 @@ class WidgetApiClient {
|
|
|
212
212
|
return typeof data === 'object' && data !== null && 'type' in data;
|
|
213
213
|
});
|
|
214
214
|
}
|
|
215
|
-
async *
|
|
215
|
+
async *dismissAgentMessageStream(conversationId, toolCallId, signal) {
|
|
216
216
|
const headers = {
|
|
217
217
|
'Content-Type': 'application/json',
|
|
218
218
|
};
|
|
219
219
|
if (this.config.currentRoute) {
|
|
220
220
|
headers['X-Current-Route'] = this.config.currentRoute;
|
|
221
221
|
}
|
|
222
|
-
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/agent/
|
|
222
|
+
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/agent/dismiss`, {
|
|
223
223
|
method: 'POST',
|
|
224
224
|
headers,
|
|
225
225
|
body: JSON.stringify({
|
|
226
226
|
conversationId: conversationId,
|
|
227
227
|
toolCallId,
|
|
228
|
-
|
|
228
|
+
reason: "user",
|
|
229
|
+
}),
|
|
230
|
+
signal,
|
|
231
|
+
});
|
|
232
|
+
if (response.status === 204) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
if (!response.ok) {
|
|
236
|
+
throw await buildApiError(response, 'Failed to dismiss action');
|
|
237
|
+
}
|
|
238
|
+
yield* parseSSEStream(response, (data) => {
|
|
239
|
+
return typeof data === 'object' && data !== null && 'type' in data;
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Continue agent after halting action completes
|
|
244
|
+
* Call this when frontend completes an action and wants to pass result back to agent
|
|
245
|
+
*/
|
|
246
|
+
async *continueAgentAction(conversationId, toolCallId, body, signal) {
|
|
247
|
+
const headers = {
|
|
248
|
+
'Content-Type': 'application/json',
|
|
249
|
+
};
|
|
250
|
+
if (this.config.currentRoute) {
|
|
251
|
+
headers['X-Current-Route'] = this.config.currentRoute;
|
|
252
|
+
}
|
|
253
|
+
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/agent/continue`, {
|
|
254
|
+
method: 'POST',
|
|
255
|
+
headers,
|
|
256
|
+
body: JSON.stringify({
|
|
257
|
+
conversationId,
|
|
258
|
+
toolCallId,
|
|
259
|
+
body,
|
|
229
260
|
timeZone: this.getTimeZone(),
|
|
230
261
|
}),
|
|
231
262
|
signal,
|
|
232
263
|
});
|
|
233
264
|
if (!response.ok) {
|
|
234
|
-
throw await buildApiError(response, 'Failed to continue agent');
|
|
265
|
+
throw await buildApiError(response, 'Failed to continue agent action');
|
|
235
266
|
}
|
|
236
267
|
yield* parseSSEStream(response, (data) => {
|
|
237
268
|
return typeof data === 'object' && data !== null && 'type' in data;
|
|
238
269
|
});
|
|
239
270
|
}
|
|
271
|
+
/**
|
|
272
|
+
* NEW: Call action endpoint (frontend-owned flow)
|
|
273
|
+
* Used for multi-step actions like booking appointments
|
|
274
|
+
*/
|
|
275
|
+
async callActionEndpoint(actionId, endpoint, input, token) {
|
|
276
|
+
const headers = {
|
|
277
|
+
'Content-Type': 'application/json',
|
|
278
|
+
};
|
|
279
|
+
if (token) {
|
|
280
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
281
|
+
}
|
|
282
|
+
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/actions/${actionId}/${endpoint}`, {
|
|
283
|
+
method: 'POST',
|
|
284
|
+
headers,
|
|
285
|
+
body: JSON.stringify(input),
|
|
286
|
+
});
|
|
287
|
+
if (!response.ok) {
|
|
288
|
+
throw await buildApiError(response, `Action endpoint '${endpoint}' failed`);
|
|
289
|
+
}
|
|
290
|
+
return response.json();
|
|
291
|
+
}
|
|
240
292
|
/**
|
|
241
293
|
* Submit feedback for a message
|
|
242
294
|
*/
|
|
@@ -267,7 +319,8 @@ class WidgetApiClient {
|
|
|
267
319
|
}
|
|
268
320
|
/**
|
|
269
321
|
* Generate follow-up suggestions based on conversation context and available actions.
|
|
270
|
-
*
|
|
322
|
+
* @deprecated Follow-ups are now generated server-side in parallel and included in the done event.
|
|
323
|
+
* This method is kept for backwards compatibility but is no longer called by the widget.
|
|
271
324
|
*/
|
|
272
325
|
async generateFollowUps(messages, actionIds) {
|
|
273
326
|
try {
|
|
@@ -299,6 +352,27 @@ class WidgetApiClient {
|
|
|
299
352
|
return [];
|
|
300
353
|
}
|
|
301
354
|
}
|
|
355
|
+
/**
|
|
356
|
+
* Create a demo conversation with preset messages
|
|
357
|
+
* Used when demo animation completes to persist the demo conversation to the database
|
|
358
|
+
*/
|
|
359
|
+
async createDemoConversation(userMessage, assistantMessage) {
|
|
360
|
+
const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/demo-conversation`, {
|
|
361
|
+
method: 'POST',
|
|
362
|
+
headers: {
|
|
363
|
+
'Content-Type': 'application/json',
|
|
364
|
+
},
|
|
365
|
+
body: JSON.stringify({
|
|
366
|
+
userMessage,
|
|
367
|
+
assistantMessage,
|
|
368
|
+
timeZone: this.getTimeZone(),
|
|
369
|
+
}),
|
|
370
|
+
});
|
|
371
|
+
if (!response.ok) {
|
|
372
|
+
throw await buildApiError(response, 'Failed to create demo conversation');
|
|
373
|
+
}
|
|
374
|
+
return response.json();
|
|
375
|
+
}
|
|
302
376
|
/**
|
|
303
377
|
* Validate widget access
|
|
304
378
|
*/
|
|
@@ -27114,7 +27188,7 @@ function TypingIndicator({ className = '' }) {
|
|
|
27114
27188
|
}
|
|
27115
27189
|
|
|
27116
27190
|
// Styles are provided by global messages.css - no component-specific CSS needed
|
|
27117
|
-
function ChevronDownIcon() {
|
|
27191
|
+
function ChevronDownIcon$1() {
|
|
27118
27192
|
return (jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsxRuntime.jsx("polyline", { points: "6 9 12 15 18 9" }) }));
|
|
27119
27193
|
}
|
|
27120
27194
|
function ScrollButton({ onClick, visible, className = '' }) {
|
|
@@ -27123,24 +27197,35 @@ function ScrollButton({ onClick, visible, className = '' }) {
|
|
|
27123
27197
|
visible && 'visible',
|
|
27124
27198
|
className,
|
|
27125
27199
|
].filter(Boolean).join(' ');
|
|
27126
|
-
return (jsxRuntime.jsx("button", { type: "button", className: classes, onClick: onClick, "aria-label": "Scroll to bottom", children: jsxRuntime.jsx(ChevronDownIcon, {}) }));
|
|
27200
|
+
return (jsxRuntime.jsx("button", { type: "button", className: classes, onClick: onClick, "aria-label": "Scroll to bottom", children: jsxRuntime.jsx(ChevronDownIcon$1, {}) }));
|
|
27127
27201
|
}
|
|
27128
27202
|
|
|
27129
27203
|
const formatToolName = (name) => {
|
|
27130
27204
|
return name
|
|
27131
27205
|
.replace(/^(action_|tool_)/, '')
|
|
27206
|
+
.replace(/-/g, '_')
|
|
27132
27207
|
.split('_')
|
|
27133
27208
|
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
27134
27209
|
.join(' ');
|
|
27135
27210
|
};
|
|
27136
27211
|
function ToolIndicator({ badges, className = '' }) {
|
|
27137
|
-
|
|
27212
|
+
// Consolidate multiple badges into a single status indicator
|
|
27213
|
+
const hasLoading = badges.some(b => b.status === 'loading');
|
|
27214
|
+
const hasError = badges.some(b => b.status === 'error');
|
|
27215
|
+
const status = hasLoading ? 'loading' : hasError ? 'error' : 'completed';
|
|
27216
|
+
// Show only the most relevant badge name, or count if multiple
|
|
27217
|
+
const displayName = badges.length === 1
|
|
27218
|
+
? formatToolName(badges[0].name)
|
|
27219
|
+
: badges.length > 1
|
|
27220
|
+
? `${badges.length} actions`
|
|
27221
|
+
: 'Processing';
|
|
27222
|
+
return (jsxRuntime.jsx("div", { className: `ai-chat-tool-row ${className}`, children: jsxRuntime.jsx("div", { className: "ai-chat-tool-badges", children: jsxRuntime.jsx("div", { className: `ai-chat-tool-badge ${status}`, children: jsxRuntime.jsx("span", { className: "tool-name", children: displayName }) }) }) }));
|
|
27138
27223
|
}
|
|
27139
27224
|
|
|
27140
27225
|
// SVG Icon components
|
|
27141
27226
|
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" }) }));
|
|
27142
27227
|
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" }) }));
|
|
27143
|
-
const CheckIcon$
|
|
27228
|
+
const CheckIcon$1 = () => (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" }) }));
|
|
27144
27229
|
const FeedbackButtons = ({ messageId, currentFeedback, onFeedback, }) => {
|
|
27145
27230
|
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
|
27146
27231
|
const [submitted, setSubmitted] = React.useState(false);
|
|
@@ -27161,7 +27246,7 @@ const FeedbackButtons = ({ messageId, currentFeedback, onFeedback, }) => {
|
|
|
27161
27246
|
setIsSubmitting(false);
|
|
27162
27247
|
}
|
|
27163
27248
|
};
|
|
27164
|
-
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$
|
|
27249
|
+
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$1, {}) }), jsxRuntime.jsx("span", { className: "ai-chat-feedback-text", children: "Thanks for feedback" })] }))] }));
|
|
27165
27250
|
};
|
|
27166
27251
|
|
|
27167
27252
|
const Message = ({ message, showTimestamp = true, enableFeedback = true, onFeedback, }) => {
|
|
@@ -27190,9 +27275,11 @@ const Message = ({ message, showTimestamp = true, enableFeedback = true, onFeedb
|
|
|
27190
27275
|
const hasContent = aiContent.trim().length > 0;
|
|
27191
27276
|
if (!hasContent)
|
|
27192
27277
|
return null;
|
|
27193
|
-
|
|
27278
|
+
// Only show timestamp and feedback when message is complete (not streaming)
|
|
27279
|
+
const isComplete = !message.isStreaming;
|
|
27280
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-message assistant ${isError ? 'error' : ''} ${message.isStreaming ? 'streaming' : ''}`, children: [jsxRuntime.jsx("div", { className: "ai-chat-message-content", children: jsxRuntime.jsx(Markdown, { remarkPlugins: [remarkGfm], components: {
|
|
27194
27281
|
table: ({ children, ...props }) => (jsxRuntime.jsx("div", { className: "table-wrapper", children: jsxRuntime.jsx("div", { className: "table-scroll", children: jsxRuntime.jsx("table", { ...props, children: children }) }) })),
|
|
27195
|
-
}, 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 }))] }))] }));
|
|
27282
|
+
}, children: aiContent }) }), showTimestamp && isComplete && (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 }))] }))] }));
|
|
27196
27283
|
}
|
|
27197
27284
|
// System message rendering
|
|
27198
27285
|
if (isSystem) {
|
|
@@ -27212,7 +27299,7 @@ function isActionComplete(state) {
|
|
|
27212
27299
|
return false;
|
|
27213
27300
|
const TERMINAL_STATUSES = [
|
|
27214
27301
|
'completed', 'booked', 'scheduled', 'cancelled', 'failed', 'error',
|
|
27215
|
-
'displaying', 'clicked', 'contacted', 'submitted', 'sent'
|
|
27302
|
+
'displaying', 'displayed', 'clicked', 'contacted', 'submitted', 'sent'
|
|
27216
27303
|
];
|
|
27217
27304
|
const status = state.status;
|
|
27218
27305
|
if (typeof status === 'string' && TERMINAL_STATUSES.includes(status)) {
|
|
@@ -27231,11 +27318,12 @@ function isActionLoading(message) {
|
|
|
27231
27318
|
return false;
|
|
27232
27319
|
if (message.isStreaming)
|
|
27233
27320
|
return true;
|
|
27234
|
-
const
|
|
27235
|
-
return !isActionComplete(
|
|
27321
|
+
const input = message.action.input;
|
|
27322
|
+
return !isActionComplete(input);
|
|
27236
27323
|
}
|
|
27237
|
-
const ToolMessageGroup = ({ messages, getActionRenderer, showToolIndicator = true, accentColor, variant }) => {
|
|
27238
|
-
const
|
|
27324
|
+
const ToolMessageGroup = ({ messages, getActionRenderer, showToolIndicator = true, accentColor, variant, onActionDismiss, onCallEndpoint, }) => {
|
|
27325
|
+
const visibleMessages = messages.filter(message => !message.action?.hidden);
|
|
27326
|
+
const actionMessages = visibleMessages.filter(message => message.action);
|
|
27239
27327
|
// Debug logging
|
|
27240
27328
|
console.log('[DEBUG ToolMessageGroup] ========================================');
|
|
27241
27329
|
console.log('[DEBUG ToolMessageGroup] Total messages:', messages.length);
|
|
@@ -27257,14 +27345,14 @@ const ToolMessageGroup = ({ messages, getActionRenderer, showToolIndicator = tru
|
|
|
27257
27345
|
implementation: impl,
|
|
27258
27346
|
hasRenderer: !!renderer,
|
|
27259
27347
|
rendererType: renderer ? typeof renderer : 'undefined',
|
|
27260
|
-
|
|
27348
|
+
input: msg.action?.input,
|
|
27261
27349
|
});
|
|
27262
27350
|
});
|
|
27263
27351
|
// If tool indicator is hidden AND there are no action cards to render, don't render anything
|
|
27264
27352
|
if (!showToolIndicator && actionMessages.length === 0) {
|
|
27265
27353
|
return null;
|
|
27266
27354
|
}
|
|
27267
|
-
const badges =
|
|
27355
|
+
const badges = visibleMessages.map((message) => {
|
|
27268
27356
|
const toolName = message.toolExecuting || message.message.name || 'Tool';
|
|
27269
27357
|
const hasError = message.isError || false;
|
|
27270
27358
|
const loading = isActionLoading(message);
|
|
@@ -27284,7 +27372,7 @@ const ToolMessageGroup = ({ messages, getActionRenderer, showToolIndicator = tru
|
|
|
27284
27372
|
console.log('[ToolMessageGroup] No renderer for:', message.action.implementation);
|
|
27285
27373
|
return null;
|
|
27286
27374
|
}
|
|
27287
|
-
return (jsxRuntime.jsx("div", { className: "ai-chat-tool-action", children: renderer(message, accentColor, variant) }, `action-${message.id}`));
|
|
27375
|
+
return (jsxRuntime.jsx("div", { className: "ai-chat-tool-action", children: renderer(message, accentColor, variant, onActionDismiss, onCallEndpoint) }, `action-${message.id}`));
|
|
27288
27376
|
})] }));
|
|
27289
27377
|
};
|
|
27290
27378
|
|
|
@@ -27318,12 +27406,29 @@ const SuggestedQuestions = ({ questions, onQuestionClick, }) => {
|
|
|
27318
27406
|
};
|
|
27319
27407
|
|
|
27320
27408
|
const MAX_TEXT_LENGTH = 40;
|
|
27409
|
+
const STAGGER_DELAY_MS = 200; // Delay between each suggestion appearing
|
|
27321
27410
|
const FollowUpSuggestions = ({ suggestions, onQuestionClick, onActionClick, accentColor, }) => {
|
|
27322
|
-
|
|
27323
|
-
return null;
|
|
27324
|
-
}
|
|
27411
|
+
const [visibleIndices, setVisibleIndices] = React.useState(new Set());
|
|
27325
27412
|
// Filter out empty suggestions
|
|
27326
|
-
const validSuggestions = suggestions
|
|
27413
|
+
const validSuggestions = suggestions?.filter(s => s && s.text && s.text.trim()) || [];
|
|
27414
|
+
// Create a stable key for the current suggestions set
|
|
27415
|
+
const suggestionsKey = validSuggestions.map(s => s.id).join(',');
|
|
27416
|
+
// Stagger reveal each suggestion one at a time
|
|
27417
|
+
React.useEffect(() => {
|
|
27418
|
+
// Reset when suggestions change
|
|
27419
|
+
setVisibleIndices(new Set());
|
|
27420
|
+
if (validSuggestions.length === 0)
|
|
27421
|
+
return;
|
|
27422
|
+
const timers = [];
|
|
27423
|
+
// Reveal each suggestion one by one
|
|
27424
|
+
validSuggestions.slice(0, 5).forEach((_, index) => {
|
|
27425
|
+
const timer = setTimeout(() => {
|
|
27426
|
+
setVisibleIndices(prev => new Set([...prev, index]));
|
|
27427
|
+
}, (index + 1) * STAGGER_DELAY_MS);
|
|
27428
|
+
timers.push(timer);
|
|
27429
|
+
});
|
|
27430
|
+
return () => timers.forEach(t => clearTimeout(t));
|
|
27431
|
+
}, [suggestionsKey]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
27327
27432
|
if (validSuggestions.length === 0) {
|
|
27328
27433
|
return null;
|
|
27329
27434
|
}
|
|
@@ -27331,8 +27436,12 @@ const FollowUpSuggestions = ({ suggestions, onQuestionClick, onActionClick, acce
|
|
|
27331
27436
|
? { "--primary-color": accentColor }
|
|
27332
27437
|
: undefined;
|
|
27333
27438
|
const trimText = (text) => text.length > MAX_TEXT_LENGTH ? `${text.slice(0, MAX_TEXT_LENGTH)}...` : text;
|
|
27334
|
-
return (jsxRuntime.jsx("div", { className: "ai-chat-follow-up-suggestions", style: containerStyle, children: jsxRuntime.jsx("div", { className: "ai-chat-follow-up-list", children: validSuggestions.slice(0, 5).map((suggestion) => {
|
|
27439
|
+
return (jsxRuntime.jsx("div", { className: "ai-chat-follow-up-suggestions", style: containerStyle, children: jsxRuntime.jsx("div", { className: "ai-chat-follow-up-list", children: validSuggestions.slice(0, 5).map((suggestion, index) => {
|
|
27335
27440
|
const isActionSuggestion = suggestion.type === 'action';
|
|
27441
|
+
const isVisible = visibleIndices.has(index);
|
|
27442
|
+
// Only render if visible - this adds items one by one instead of showing all at once
|
|
27443
|
+
if (!isVisible)
|
|
27444
|
+
return null;
|
|
27336
27445
|
const className = `ai-chat-follow-up-item ${isActionSuggestion ? 'action-type' : 'question-type'}`;
|
|
27337
27446
|
const handleClick = () => {
|
|
27338
27447
|
if (isActionSuggestion && onActionClick) {
|
|
@@ -27346,39 +27455,47 @@ const FollowUpSuggestions = ({ suggestions, onQuestionClick, onActionClick, acce
|
|
|
27346
27455
|
};
|
|
27347
27456
|
|
|
27348
27457
|
const MessageList = (props) => {
|
|
27349
|
-
const { messages, isTyping = false, showTypingIndicator = true, showTimestamps = true, showToolCalls = false, enableFeedback = true, welcomeTitle, welcomeMessage, suggestedQuestions, accentColor, onSuggestedQuestionClick, onActionClick, onFeedback, onScrollStateChange, getActionRenderer, variant, } = props;
|
|
27458
|
+
const { messages, isTyping = false, showTypingIndicator = true, showTimestamps = true, showToolCalls = false, enableFeedback = true, welcomeTitle, welcomeMessage, suggestedQuestions, accentColor, onSuggestedQuestionClick, onActionClick, onActionDismiss, onFeedback, onScrollStateChange, getActionRenderer, onCallEndpoint, variant, } = props;
|
|
27350
27459
|
const containerRef = React.useRef(null);
|
|
27351
27460
|
const messagesEndRef = React.useRef(null);
|
|
27352
27461
|
const [showScrollButton, setShowScrollButton] = React.useState(false);
|
|
27353
27462
|
const prevMessageCountRef = React.useRef(0);
|
|
27463
|
+
const visibleMessages = React.useMemo(() => messages.filter((message) => !message.action?.hidden), [messages]);
|
|
27354
27464
|
const hasActiveAction = React.useMemo(() => {
|
|
27355
27465
|
// Find the last tool message and check if its action is still active (not done)
|
|
27356
|
-
const lastToolMsg = [...
|
|
27466
|
+
const lastToolMsg = [...visibleMessages].reverse().find(msg => msg.message.role === 'tool');
|
|
27357
27467
|
return lastToolMsg?.action && lastToolMsg.action.done !== true;
|
|
27358
|
-
}, [
|
|
27359
|
-
const checkScrollPosition = React.useCallback(() => {
|
|
27360
|
-
const c = containerRef.current;
|
|
27361
|
-
if (!c)
|
|
27362
|
-
return;
|
|
27363
|
-
const pb = parseInt(getComputedStyle(c).paddingBottom || '0', 10);
|
|
27364
|
-
setShowScrollButton(c.scrollHeight - c.scrollTop - c.clientHeight - pb > 24);
|
|
27365
|
-
}, []);
|
|
27468
|
+
}, [visibleMessages]);
|
|
27366
27469
|
const scrollToBottom = React.useCallback(() => {
|
|
27367
27470
|
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
27368
27471
|
}, []);
|
|
27369
|
-
|
|
27472
|
+
// Use IntersectionObserver to detect when the end marker is visible
|
|
27473
|
+
// This is more reliable than scroll position calculations
|
|
27370
27474
|
React.useEffect(() => {
|
|
27371
|
-
const
|
|
27372
|
-
|
|
27475
|
+
const endMarker = messagesEndRef.current;
|
|
27476
|
+
const container = containerRef.current;
|
|
27477
|
+
if (!endMarker || !container)
|
|
27373
27478
|
return;
|
|
27374
|
-
|
|
27375
|
-
|
|
27376
|
-
|
|
27479
|
+
const observer = new IntersectionObserver((entries) => {
|
|
27480
|
+
// If the end marker is intersecting (visible), hide the button
|
|
27481
|
+
// If it's not visible, show the button
|
|
27482
|
+
const isAtBottom = entries[0]?.isIntersecting ?? false;
|
|
27483
|
+
setShowScrollButton(!isAtBottom);
|
|
27484
|
+
}, {
|
|
27485
|
+
root: container,
|
|
27486
|
+
// rootMargin adds extra space - if end marker is within 100px of viewport, consider it "visible"
|
|
27487
|
+
rootMargin: '0px 0px 100px 0px',
|
|
27488
|
+
threshold: 0,
|
|
27489
|
+
});
|
|
27490
|
+
observer.observe(endMarker);
|
|
27491
|
+
return () => observer.disconnect();
|
|
27492
|
+
}, []);
|
|
27493
|
+
React.useEffect(() => { onScrollStateChange?.(showScrollButton, scrollToBottom); }, [showScrollButton, scrollToBottom, onScrollStateChange]);
|
|
27377
27494
|
React.useEffect(() => {
|
|
27378
27495
|
const c = containerRef.current;
|
|
27379
27496
|
if (!c)
|
|
27380
27497
|
return;
|
|
27381
|
-
const count =
|
|
27498
|
+
const count = visibleMessages.length;
|
|
27382
27499
|
const isNew = count > prevMessageCountRef.current;
|
|
27383
27500
|
prevMessageCountRef.current = count;
|
|
27384
27501
|
if (count === 0) {
|
|
@@ -27388,32 +27505,18 @@ const MessageList = (props) => {
|
|
|
27388
27505
|
if ((isNew || isTyping) && c.scrollHeight - c.scrollTop - c.clientHeight < 150) {
|
|
27389
27506
|
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
27390
27507
|
}
|
|
27391
|
-
|
|
27392
|
-
}, [messages, isTyping, checkScrollPosition]);
|
|
27508
|
+
}, [visibleMessages, isTyping]);
|
|
27393
27509
|
const groupedMessages = React.useMemo(() => {
|
|
27394
|
-
console.log('[DEBUG MessageList] ========================================');
|
|
27395
|
-
console.log('[DEBUG MessageList] Processing messages:', messages.length);
|
|
27396
|
-
messages.forEach((m, i) => {
|
|
27397
|
-
console.log(`[DEBUG MessageList] Message ${i}:`, {
|
|
27398
|
-
id: m.id,
|
|
27399
|
-
role: m.message.role,
|
|
27400
|
-
hasAction: !!m.action,
|
|
27401
|
-
actionImpl: m.action?.implementation,
|
|
27402
|
-
content: (m.message.content || '').substring(0, 50),
|
|
27403
|
-
});
|
|
27404
|
-
});
|
|
27405
27510
|
const result = [];
|
|
27406
27511
|
let toolGroup = [];
|
|
27407
27512
|
const flush = () => {
|
|
27408
27513
|
if (toolGroup.length) {
|
|
27409
|
-
console.log('[DEBUG MessageList] Flushing tool group with', toolGroup.length, 'messages');
|
|
27410
27514
|
result.push({ type: 'tool-group', messages: [...toolGroup] });
|
|
27411
27515
|
toolGroup = [];
|
|
27412
27516
|
}
|
|
27413
27517
|
};
|
|
27414
|
-
for (const m of
|
|
27518
|
+
for (const m of visibleMessages) {
|
|
27415
27519
|
if (m.message.role === 'tool') {
|
|
27416
|
-
console.log('[DEBUG MessageList] Adding to tool group:', m.id);
|
|
27417
27520
|
toolGroup.push(m);
|
|
27418
27521
|
}
|
|
27419
27522
|
else if (m.message.role === 'user') {
|
|
@@ -27435,27 +27538,26 @@ const MessageList = (props) => {
|
|
|
27435
27538
|
}
|
|
27436
27539
|
}
|
|
27437
27540
|
flush();
|
|
27438
|
-
console.log('[DEBUG MessageList] Final grouped result:', result.length, 'items');
|
|
27439
|
-
result.forEach((item, i) => {
|
|
27440
|
-
if (item.type === 'tool-group') {
|
|
27441
|
-
console.log(`[DEBUG MessageList] Group ${i}: tool-group with ${item.messages.length} messages`);
|
|
27442
|
-
}
|
|
27443
|
-
else {
|
|
27444
|
-
console.log(`[DEBUG MessageList] Group ${i}: message (${item.message.message.role})`);
|
|
27445
|
-
}
|
|
27446
|
-
});
|
|
27447
27541
|
return result;
|
|
27448
|
-
}, [
|
|
27449
|
-
|
|
27542
|
+
}, [visibleMessages]);
|
|
27543
|
+
// Get the last assistant message's suggestions for rendering at the end
|
|
27544
|
+
const lastAssistantSuggestions = React.useMemo(() => {
|
|
27545
|
+
for (let i = groupedMessages.length - 1; i >= 0; i--) {
|
|
27546
|
+
const item = groupedMessages[i];
|
|
27547
|
+
if (item.type === 'message' && item.message.message.role === 'assistant') {
|
|
27548
|
+
return item.message.suggestions;
|
|
27549
|
+
}
|
|
27550
|
+
}
|
|
27551
|
+
return undefined;
|
|
27552
|
+
}, [groupedMessages]);
|
|
27553
|
+
const hasSuggestions = visibleMessages.length === 0 && onSuggestedQuestionClick && suggestedQuestions?.length;
|
|
27450
27554
|
const showWelcome = welcomeTitle || welcomeMessage || hasSuggestions;
|
|
27451
27555
|
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) => {
|
|
27452
27556
|
if (item.type === 'tool-group') {
|
|
27453
|
-
return jsxRuntime.jsx(ToolMessageGroup, { messages: item.messages, getActionRenderer: getActionRenderer, showToolIndicator: showToolCalls, accentColor: accentColor, variant: variant }, `tg-${i}`);
|
|
27557
|
+
return (jsxRuntime.jsx(ToolMessageGroup, { messages: item.messages, getActionRenderer: getActionRenderer, showToolIndicator: showToolCalls, accentColor: accentColor, variant: variant, onActionDismiss: onActionDismiss, onCallEndpoint: onCallEndpoint }, `tg-${i}`));
|
|
27454
27558
|
}
|
|
27455
|
-
|
|
27456
|
-
|
|
27457
|
-
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));
|
|
27458
|
-
}), isTyping && showTypingIndicator && !hasActiveAction && messages.length > 0 && jsxRuntime.jsx(TypingIndicator, {}), jsxRuntime.jsx("div", { ref: messagesEndRef })] }));
|
|
27559
|
+
return (jsxRuntime.jsx(Message, { message: item.message, showTimestamp: showTimestamps, enableFeedback: enableFeedback, onFeedback: onFeedback, getActionRenderer: getActionRenderer, accentColor: accentColor }, item.message.id));
|
|
27560
|
+
}), !isTyping && lastAssistantSuggestions?.length && onSuggestedQuestionClick && (jsxRuntime.jsx(FollowUpSuggestions, { suggestions: lastAssistantSuggestions, onQuestionClick: onSuggestedQuestionClick, onActionClick: onActionClick, accentColor: accentColor })), isTyping && showTypingIndicator && !hasActiveAction && visibleMessages.length > 0 && jsxRuntime.jsx(TypingIndicator, {}), jsxRuntime.jsx("div", { ref: messagesEndRef })] }));
|
|
27459
27561
|
};
|
|
27460
27562
|
|
|
27461
27563
|
const ALLOWED_EXTENSIONS = ['.pdf', '.doc', '.docx', '.txt', '.md', '.csv'];
|
|
@@ -27469,7 +27571,7 @@ const formatFileSize = (bytes) => {
|
|
|
27469
27571
|
return (bytes / 1024).toFixed(1) + ' KB';
|
|
27470
27572
|
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
|
|
27471
27573
|
};
|
|
27472
|
-
const MessageInput = ({ onSend, placeholder = 'Type your message...', disabled = false, enableFileUpload = false, separateFromChat = true, showDataPolicy = true, onDataPolicyClick, }) => {
|
|
27574
|
+
const MessageInput = ({ onSend, placeholder = 'Type your message...', disabled = false, enableFileUpload = false, separateFromChat = true, showDataPolicy = true, onDataPolicyClick, onInputFocus, }) => {
|
|
27473
27575
|
const [value, setValue] = React.useState('');
|
|
27474
27576
|
const [selectedFiles, setSelectedFiles] = React.useState([]);
|
|
27475
27577
|
const textareaRef = React.useRef(null);
|
|
@@ -27504,481 +27606,15 @@ const MessageInput = ({ onSend, placeholder = 'Type your message...', disabled =
|
|
|
27504
27606
|
}
|
|
27505
27607
|
};
|
|
27506
27608
|
const canSend = value.trim() || selectedFiles.length > 0;
|
|
27507
|
-
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: "
|
|
27609
|
+
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, onFocus: onInputFocus, 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" })] }))] }))] }));
|
|
27508
27610
|
};
|
|
27509
27611
|
|
|
27510
|
-
function
|
|
27511
|
-
const grouped = new Map();
|
|
27512
|
-
for (const slot of slots) {
|
|
27513
|
-
if (!slot || typeof slot !== 'object' || !slot.startTime || typeof slot.startTime !== 'string') {
|
|
27514
|
-
continue;
|
|
27515
|
-
}
|
|
27516
|
-
const date = slot.startTime.slice(0, 10);
|
|
27517
|
-
if (!grouped.has(date)) {
|
|
27518
|
-
grouped.set(date, []);
|
|
27519
|
-
}
|
|
27520
|
-
grouped.get(date).push(slot);
|
|
27521
|
-
}
|
|
27522
|
-
return grouped;
|
|
27523
|
-
}
|
|
27524
|
-
function formatDate$1(dateStr) {
|
|
27525
|
-
try {
|
|
27526
|
-
const date = new Date(dateStr);
|
|
27527
|
-
return new Intl.DateTimeFormat("en-US", {
|
|
27528
|
-
weekday: "short",
|
|
27529
|
-
month: "short",
|
|
27530
|
-
day: "numeric",
|
|
27531
|
-
}).format(date);
|
|
27532
|
-
}
|
|
27533
|
-
catch {
|
|
27534
|
-
return dateStr;
|
|
27535
|
-
}
|
|
27536
|
-
}
|
|
27537
|
-
function CalendarIcon$1() {
|
|
27538
|
-
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" }) }));
|
|
27539
|
-
}
|
|
27540
|
-
function CheckIcon$1() {
|
|
27541
|
-
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" }) }));
|
|
27542
|
-
}
|
|
27543
|
-
function ExternalLinkIcon$2() {
|
|
27544
|
-
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" })] }));
|
|
27545
|
-
}
|
|
27546
|
-
function Skeleton$1({ width, height, borderRadius = '4px' }) {
|
|
27547
|
-
return (jsxRuntime.jsx("div", { className: "ai-chat-action-skeleton-item", style: { width, height, borderRadius } }));
|
|
27548
|
-
}
|
|
27549
|
-
function GoogleCalendarCard({ action, onComplete, accentColor, className = '' }) {
|
|
27550
|
-
const state = action.state;
|
|
27551
|
-
const rawSlots = state.availableSlots;
|
|
27552
|
-
const availableSlots = Array.isArray(rawSlots)
|
|
27553
|
-
? rawSlots.filter((slot) => slot !== null &&
|
|
27554
|
-
slot !== undefined &&
|
|
27555
|
-
typeof slot === "object" &&
|
|
27556
|
-
"startTime" in slot &&
|
|
27557
|
-
"endTime" in slot &&
|
|
27558
|
-
typeof slot.startTime === "string" &&
|
|
27559
|
-
typeof slot.endTime === "string")
|
|
27560
|
-
: [];
|
|
27561
|
-
const allowTopic = state.allowTopic !== false;
|
|
27562
|
-
const isBooked = state.status === "booked";
|
|
27563
|
-
const slotsByDate = groupSlotsByDate$1(availableSlots);
|
|
27564
|
-
const dates = Array.from(slotsByDate.keys()).sort();
|
|
27565
|
-
const [selectedDate, setSelectedDate] = React.useState(dates[0] ?? "");
|
|
27566
|
-
const [selectedSlot, setSelectedSlot] = React.useState(null);
|
|
27567
|
-
const [topic, setTopic] = React.useState("");
|
|
27568
|
-
const [error, setError] = React.useState(null);
|
|
27569
|
-
const slotsForSelectedDate = selectedDate ? slotsByDate.get(selectedDate) ?? [] : [];
|
|
27570
|
-
const accentStyle = accentColor ? { '--action-accent': accentColor } : {};
|
|
27571
|
-
const onConfirm = () => {
|
|
27572
|
-
if (!selectedSlot) {
|
|
27573
|
-
setError("Please select a time slot.");
|
|
27574
|
-
return;
|
|
27575
|
-
}
|
|
27576
|
-
if (allowTopic && !topic.trim()) {
|
|
27577
|
-
setError("Please enter a topic for the meeting.");
|
|
27578
|
-
return;
|
|
27579
|
-
}
|
|
27580
|
-
setError(null);
|
|
27581
|
-
onComplete?.(action.toolCallId, {
|
|
27582
|
-
...action.state,
|
|
27583
|
-
selectedSlot: {
|
|
27584
|
-
startTime: selectedSlot.startTime,
|
|
27585
|
-
endTime: selectedSlot.endTime,
|
|
27586
|
-
},
|
|
27587
|
-
topic: allowTopic ? topic.trim() : null,
|
|
27588
|
-
});
|
|
27589
|
-
};
|
|
27590
|
-
// Booked state
|
|
27591
|
-
if (isBooked) {
|
|
27592
|
-
const bookedSlot = state.selectedSlot;
|
|
27593
|
-
const bookedTopic = state.topic;
|
|
27594
|
-
const eventLink = state.bookedEventLink;
|
|
27595
|
-
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, {})] }))] })] }));
|
|
27596
|
-
}
|
|
27597
|
-
// Skeleton loading state - show when waiting for backend after user confirms
|
|
27598
|
-
const isWaitingForBackend = !action.done && state.selectedSlot && !isBooked;
|
|
27599
|
-
if (isWaitingForBackend) {
|
|
27600
|
-
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.jsxs("div", { className: "ai-chat-action-skeleton-box", children: [jsxRuntime.jsx(Skeleton$1, { width: "50px", height: "12px", borderRadius: "4px" }), jsxRuntime.jsx(Skeleton$1, { width: "200px", height: "18px", borderRadius: "4px" })] }), jsxRuntime.jsx(Skeleton$1, { width: "100%", height: "44px", borderRadius: "999px" })] }) }));
|
|
27601
|
-
}
|
|
27602
|
-
// Booking form
|
|
27603
|
-
return (jsxRuntime.jsxs("div", { className: `ai-chat-action-card ai-chat-google-calendar ${className}`, style: accentStyle, children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-header", children: [jsxRuntime.jsx(CalendarIcon$1, {}), "Schedule an Appointment"] }), jsxRuntime.jsxs("div", { className: "ai-chat-action-body", children: [allowTopic && (jsxRuntime.jsxs("div", { className: "ai-chat-action-field", children: [jsxRuntime.jsx("label", { htmlFor: `topic-${action.toolCallId}`, className: "ai-chat-action-label", children: "Meeting Topic" }), jsxRuntime.jsx("input", { id: `topic-${action.toolCallId}`, type: "text", className: "ai-chat-action-input", placeholder: "e.g., Product Demo", value: topic, onChange: (e) => setTopic(e.target.value) })] })), jsxRuntime.jsxs("div", { className: "ai-chat-action-field", children: [jsxRuntime.jsx("label", { className: "ai-chat-action-label", children: "Select Date" }), jsxRuntime.jsx("div", { className: "ai-chat-action-date-grid", children: dates.slice(0, 7).map((date) => (jsxRuntime.jsx("button", { type: "button", className: `ai-chat-action-date-btn ${selectedDate === date ? "active" : ""}`, onClick: () => {
|
|
27604
|
-
setSelectedDate(date);
|
|
27605
|
-
setSelectedSlot(null);
|
|
27606
|
-
}, 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." }))] })] }));
|
|
27607
|
-
}
|
|
27608
|
-
|
|
27609
|
-
function groupSlotsByDate(slots) {
|
|
27610
|
-
const grouped = new Map();
|
|
27611
|
-
for (const slot of slots) {
|
|
27612
|
-
if (!slot || typeof slot !== 'object' || !slot.startTime || typeof slot.startTime !== 'string') {
|
|
27613
|
-
continue;
|
|
27614
|
-
}
|
|
27615
|
-
const date = slot.startTime.slice(0, 10);
|
|
27616
|
-
if (!grouped.has(date)) {
|
|
27617
|
-
grouped.set(date, []);
|
|
27618
|
-
}
|
|
27619
|
-
grouped.get(date).push(slot);
|
|
27620
|
-
}
|
|
27621
|
-
return grouped;
|
|
27622
|
-
}
|
|
27623
|
-
function formatDate(dateStr) {
|
|
27624
|
-
try {
|
|
27625
|
-
const date = new Date(dateStr);
|
|
27626
|
-
return new Intl.DateTimeFormat("en-US", {
|
|
27627
|
-
weekday: "short",
|
|
27628
|
-
month: "short",
|
|
27629
|
-
day: "numeric",
|
|
27630
|
-
}).format(date);
|
|
27631
|
-
}
|
|
27632
|
-
catch {
|
|
27633
|
-
return dateStr;
|
|
27634
|
-
}
|
|
27635
|
-
}
|
|
27636
|
-
function CalendarIcon() {
|
|
27637
|
-
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" }) }));
|
|
27638
|
-
}
|
|
27639
|
-
function MailIcon() {
|
|
27640
|
-
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" })] }));
|
|
27641
|
-
}
|
|
27642
|
-
function CheckIcon() {
|
|
27643
|
-
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" }) }));
|
|
27644
|
-
}
|
|
27645
|
-
function ErrorIcon() {
|
|
27646
|
-
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" }) }));
|
|
27647
|
-
}
|
|
27648
|
-
function ExternalLinkIcon$1() {
|
|
27649
|
-
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" })] }));
|
|
27650
|
-
}
|
|
27651
|
-
function Skeleton({ width, height, borderRadius = '4px' }) {
|
|
27652
|
-
return (jsxRuntime.jsx("div", { className: "ai-chat-action-skeleton-item", style: { width, height, borderRadius } }));
|
|
27653
|
-
}
|
|
27654
|
-
function PinInputGroup({ values, onChange, disabled }) {
|
|
27655
|
-
const inputRefs = React.useRef([]);
|
|
27656
|
-
const handleChange = (index, value) => {
|
|
27657
|
-
// Only allow digits
|
|
27658
|
-
const digit = value.replace(/[^0-9]/g, '');
|
|
27659
|
-
const newValues = [...values];
|
|
27660
|
-
newValues[index] = digit.slice(-1);
|
|
27661
|
-
onChange(newValues);
|
|
27662
|
-
// Auto-focus next input
|
|
27663
|
-
if (digit && index < 5) {
|
|
27664
|
-
inputRefs.current[index + 1]?.focus();
|
|
27665
|
-
}
|
|
27666
|
-
};
|
|
27667
|
-
const handleKeyDown = (index, e) => {
|
|
27668
|
-
if (e.key === 'Backspace' && !values[index] && index > 0) {
|
|
27669
|
-
inputRefs.current[index - 1]?.focus();
|
|
27670
|
-
}
|
|
27671
|
-
};
|
|
27672
|
-
const handlePaste = (e) => {
|
|
27673
|
-
e.preventDefault();
|
|
27674
|
-
const pastedData = e.clipboardData.getData('text').replace(/[^0-9]/g, '').slice(0, 6);
|
|
27675
|
-
const newValues = pastedData.split('').concat(Array(6 - pastedData.length).fill(''));
|
|
27676
|
-
onChange(newValues);
|
|
27677
|
-
// Focus the next empty input or the last one
|
|
27678
|
-
const nextIndex = Math.min(pastedData.length, 5);
|
|
27679
|
-
inputRefs.current[nextIndex]?.focus();
|
|
27680
|
-
};
|
|
27681
|
-
return (jsxRuntime.jsx("div", { className: "ai-chat-pin-input-group", children: values.map((value, index) => (jsxRuntime.jsx("input", { ref: (el) => {
|
|
27682
|
-
inputRefs.current[index] = el;
|
|
27683
|
-
}, 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))) }));
|
|
27684
|
-
}
|
|
27685
|
-
function MicrosoftCalendarCard({ action, onComplete, accentColor, className = '' }) {
|
|
27686
|
-
const state = action.state;
|
|
27687
|
-
const phase = state.phase || "awaiting_email";
|
|
27688
|
-
const allowTopic = state.allowTopic !== false;
|
|
27689
|
-
const accentStyle = accentColor ? { '--action-accent': accentColor } : {};
|
|
27690
|
-
// Debug: Log state changes
|
|
27691
|
-
const prevStateRef = React.useRef(null);
|
|
27692
|
-
React.useEffect(() => {
|
|
27693
|
-
if (JSON.stringify(prevStateRef.current) !== JSON.stringify(state)) {
|
|
27694
|
-
console.log('[MicrosoftCalendarCard] State updated:', {
|
|
27695
|
-
phase: state.phase,
|
|
27696
|
-
hasError: !!state.errorMessage,
|
|
27697
|
-
error: state.errorMessage,
|
|
27698
|
-
slotsCount: state.availableSlots?.length || 0
|
|
27699
|
-
});
|
|
27700
|
-
prevStateRef.current = state;
|
|
27701
|
-
}
|
|
27702
|
-
}, [state]);
|
|
27703
|
-
// Email phase state
|
|
27704
|
-
const [email, setEmail] = React.useState("");
|
|
27705
|
-
const [emailError, setEmailError] = React.useState(null);
|
|
27706
|
-
// OTP phase state
|
|
27707
|
-
const [otpValues, setOtpValues] = React.useState(Array(6).fill(''));
|
|
27708
|
-
const [otpError, setOtpError] = React.useState(null);
|
|
27709
|
-
// Loading states
|
|
27710
|
-
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
|
27711
|
-
// Reset loading state when phase changes (backend has responded)
|
|
27712
|
-
// Use useEffect for reliable re-rendering
|
|
27713
|
-
React.useEffect(() => {
|
|
27714
|
-
console.log('[MicrosoftCalendarCard] Phase changed to:', phase);
|
|
27715
|
-
setIsSubmitting(false);
|
|
27716
|
-
// Clear errors when transitioning to new phase
|
|
27717
|
-
if (phase === "awaiting_email") {
|
|
27718
|
-
setEmailError(null);
|
|
27719
|
-
setOtpError(null);
|
|
27720
|
-
setSelectionError(null);
|
|
27721
|
-
}
|
|
27722
|
-
else if (phase === "awaiting_otp") {
|
|
27723
|
-
setOtpError(state.errorMessage || null);
|
|
27724
|
-
setEmailError(null);
|
|
27725
|
-
setSelectionError(null);
|
|
27726
|
-
// Clear OTP input for fresh attempt
|
|
27727
|
-
setOtpValues(Array(6).fill(''));
|
|
27728
|
-
}
|
|
27729
|
-
else if (phase === "awaiting_options") {
|
|
27730
|
-
setEmailError(null);
|
|
27731
|
-
setOtpError(null);
|
|
27732
|
-
setSelectionError(null);
|
|
27733
|
-
}
|
|
27734
|
-
else if (phase === "awaiting_booking") {
|
|
27735
|
-
setSelectionError(state.errorMessage || null);
|
|
27736
|
-
setEmailError(null);
|
|
27737
|
-
setOtpError(null);
|
|
27738
|
-
}
|
|
27739
|
-
else if (phase === "booked" || phase === "cancelled" || phase === "error") {
|
|
27740
|
-
setEmailError(null);
|
|
27741
|
-
setOtpError(null);
|
|
27742
|
-
setSelectionError(null);
|
|
27743
|
-
setSelectedId(null); // Reset selection
|
|
27744
|
-
}
|
|
27745
|
-
}, [phase, state.errorMessage]);
|
|
27746
|
-
// Selection phase state
|
|
27747
|
-
const rawSlots = state.availableSlots;
|
|
27748
|
-
const availableSlots = Array.isArray(rawSlots)
|
|
27749
|
-
? rawSlots.filter((slot) => slot !== null &&
|
|
27750
|
-
slot !== undefined &&
|
|
27751
|
-
typeof slot === "object" &&
|
|
27752
|
-
"startTime" in slot &&
|
|
27753
|
-
"endTime" in slot &&
|
|
27754
|
-
typeof slot.startTime === "string" &&
|
|
27755
|
-
typeof slot.endTime === "string")
|
|
27756
|
-
: [];
|
|
27757
|
-
const slotsByDate = groupSlotsByDate(availableSlots);
|
|
27758
|
-
const dates = Array.from(slotsByDate.keys()).sort();
|
|
27759
|
-
const [selectedDate, setSelectedDate] = React.useState(dates[0] ?? "");
|
|
27760
|
-
const [selectedSlot, setSelectedSlot] = React.useState(null);
|
|
27761
|
-
const [topic, setTopic] = React.useState("");
|
|
27762
|
-
const [selectionError, setSelectionError] = React.useState(null);
|
|
27763
|
-
// Cancellation phase state
|
|
27764
|
-
const [selectedId, setSelectedId] = React.useState(null);
|
|
27765
|
-
const slotsForSelectedDate = selectedDate ? slotsByDate.get(selectedDate) ?? [] : [];
|
|
27766
|
-
// Phase 1: Email Input
|
|
27767
|
-
const handleEmailSubmit = () => {
|
|
27768
|
-
const trimmedEmail = email.trim();
|
|
27769
|
-
if (!trimmedEmail) {
|
|
27770
|
-
setEmailError("Please enter your email address");
|
|
27771
|
-
return;
|
|
27772
|
-
}
|
|
27773
|
-
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmedEmail)) {
|
|
27774
|
-
setEmailError("Please enter a valid email address");
|
|
27775
|
-
return;
|
|
27776
|
-
}
|
|
27777
|
-
setEmailError(null);
|
|
27778
|
-
setIsSubmitting(true);
|
|
27779
|
-
console.log('[MicrosoftCalendarCard] Submitting email:', trimmedEmail);
|
|
27780
|
-
setTimeout(() => {
|
|
27781
|
-
onComplete?.(action.toolCallId, {
|
|
27782
|
-
...action.state,
|
|
27783
|
-
email: trimmedEmail,
|
|
27784
|
-
});
|
|
27785
|
-
}, 50);
|
|
27786
|
-
};
|
|
27787
|
-
// Phase 2: OTP Verification
|
|
27788
|
-
const handleOtpSubmit = () => {
|
|
27789
|
-
const otpCode = otpValues.join('');
|
|
27790
|
-
if (otpCode.length !== 6) {
|
|
27791
|
-
setOtpError("Please enter the 6-digit code");
|
|
27792
|
-
return;
|
|
27793
|
-
}
|
|
27794
|
-
setOtpError(null);
|
|
27795
|
-
setIsSubmitting(true);
|
|
27796
|
-
console.log('[MicrosoftCalendarCard] Submitting OTP code');
|
|
27797
|
-
setTimeout(() => {
|
|
27798
|
-
onComplete?.(action.toolCallId, {
|
|
27799
|
-
...action.state,
|
|
27800
|
-
otpCode,
|
|
27801
|
-
});
|
|
27802
|
-
}, 50);
|
|
27803
|
-
};
|
|
27804
|
-
// Phase 3: Appointment Selection
|
|
27805
|
-
const handleAppointmentConfirm = () => {
|
|
27806
|
-
if (!selectedSlot) {
|
|
27807
|
-
setSelectionError("Please select a time slot");
|
|
27808
|
-
return;
|
|
27809
|
-
}
|
|
27810
|
-
if (allowTopic && !topic.trim()) {
|
|
27811
|
-
setSelectionError("Please enter a meeting topic");
|
|
27812
|
-
return;
|
|
27813
|
-
}
|
|
27814
|
-
setSelectionError(null);
|
|
27815
|
-
setIsSubmitting(true);
|
|
27816
|
-
console.log('[MicrosoftCalendarCard] Confirming appointment:', {
|
|
27817
|
-
slot: selectedSlot,
|
|
27818
|
-
topic: topic.trim()
|
|
27819
|
-
});
|
|
27820
|
-
setTimeout(() => {
|
|
27821
|
-
onComplete?.(action.toolCallId, {
|
|
27822
|
-
...action.state,
|
|
27823
|
-
selectedSlot: {
|
|
27824
|
-
startTime: selectedSlot.startTime,
|
|
27825
|
-
endTime: selectedSlot.endTime,
|
|
27826
|
-
},
|
|
27827
|
-
topic: allowTopic ? topic.trim() : null,
|
|
27828
|
-
});
|
|
27829
|
-
}, 50);
|
|
27830
|
-
};
|
|
27831
|
-
// Handle "Use different email" button
|
|
27832
|
-
const handleUseDifferentEmail = () => {
|
|
27833
|
-
setEmail("");
|
|
27834
|
-
setEmailError(null);
|
|
27835
|
-
setOtpValues(Array(6).fill(''));
|
|
27836
|
-
setOtpError(null);
|
|
27837
|
-
onComplete?.(action.toolCallId, {
|
|
27838
|
-
phase: "awaiting_email",
|
|
27839
|
-
email: null,
|
|
27840
|
-
otpVerified: false,
|
|
27841
|
-
otpAttempts: 0,
|
|
27842
|
-
availableSlots: [],
|
|
27843
|
-
selectedSlot: null,
|
|
27844
|
-
topic: null,
|
|
27845
|
-
bookedEventId: null,
|
|
27846
|
-
bookedEventLink: null,
|
|
27847
|
-
allowTopic,
|
|
27848
|
-
errorMessage: null,
|
|
27849
|
-
});
|
|
27850
|
-
};
|
|
27851
|
-
// Phase 5: Booked Confirmation
|
|
27852
|
-
if (phase === "booked") {
|
|
27853
|
-
const bookedSlot = state.selectedSlot;
|
|
27854
|
-
const bookedTopic = state.topic;
|
|
27855
|
-
const eventLink = state.bookedEventLink;
|
|
27856
|
-
const bookedEmail = state.email;
|
|
27857
|
-
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, {})] }))] })] }));
|
|
27858
|
-
}
|
|
27859
|
-
// Phase 6: Cancelled Confirmation
|
|
27860
|
-
if (phase === "cancelled") {
|
|
27861
|
-
const cancelledSubject = state.cancelledEventSubject;
|
|
27862
|
-
const userEmail = state.email;
|
|
27863
|
-
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] }))] })] }));
|
|
27864
|
-
}
|
|
27865
|
-
// Error State
|
|
27866
|
-
if (phase === "error") {
|
|
27867
|
-
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: () => {
|
|
27868
|
-
setEmail("");
|
|
27869
|
-
setEmailError(null);
|
|
27870
|
-
setOtpValues(Array(6).fill(''));
|
|
27871
|
-
setOtpError(null);
|
|
27872
|
-
onComplete?.(action.toolCallId, {
|
|
27873
|
-
phase: "awaiting_email",
|
|
27874
|
-
email: null,
|
|
27875
|
-
otpVerified: false,
|
|
27876
|
-
otpAttempts: 0,
|
|
27877
|
-
availableSlots: [],
|
|
27878
|
-
selectedSlot: null,
|
|
27879
|
-
topic: null,
|
|
27880
|
-
bookedEventId: null,
|
|
27881
|
-
bookedEventLink: null,
|
|
27882
|
-
allowTopic,
|
|
27883
|
-
errorMessage: null,
|
|
27884
|
-
});
|
|
27885
|
-
}, children: "Start Over" })] })] }));
|
|
27886
|
-
}
|
|
27887
|
-
// Loading State
|
|
27888
|
-
if (isSubmitting) {
|
|
27889
|
-
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.jsx(Skeleton, { width: "100%", height: "44px", borderRadius: "999px" })] }) }));
|
|
27890
|
-
}
|
|
27891
|
-
// Phase 1: Email Input
|
|
27892
|
-
if (phase === "awaiting_email") {
|
|
27893
|
-
const displayError = state.errorMessage || emailError;
|
|
27894
|
-
return (jsxRuntime.jsxs("div", { className: `ai-chat-action-card ${className}`, style: accentStyle, children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-header", children: [jsxRuntime.jsx(CalendarIcon, {}), "Schedule an Appointment"] }), 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) => {
|
|
27895
|
-
setEmail(e.target.value);
|
|
27896
|
-
setEmailError(null);
|
|
27897
|
-
}, onKeyPress: (e) => {
|
|
27898
|
-
if (e.key === 'Enter') {
|
|
27899
|
-
handleEmailSubmit();
|
|
27900
|
-
}
|
|
27901
|
-
}, 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" })] })] }));
|
|
27902
|
-
}
|
|
27903
|
-
// Phase 2: OTP Input
|
|
27904
|
-
if (phase === "awaiting_otp") {
|
|
27905
|
-
const displayError = state.errorMessage || otpError;
|
|
27906
|
-
const userEmail = state.email;
|
|
27907
|
-
return (jsxRuntime.jsxs("div", { className: `ai-chat-action-card ${className}`, style: accentStyle, children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-header", children: [jsxRuntime.jsx(MailIcon, {}), "Verify Your Email"] }), 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, { values: otpValues, onChange: (newValues) => {
|
|
27908
|
-
setOtpValues(newValues);
|
|
27909
|
-
setOtpError(null);
|
|
27910
|
-
// Auto-submit when all 6 digits are entered
|
|
27911
|
-
if (newValues.every(v => v.length === 1)) {
|
|
27912
|
-
console.log('[MicrosoftCalendarCard] Auto-submitting OTP (all 6 digits entered)');
|
|
27913
|
-
setIsSubmitting(true);
|
|
27914
|
-
const code = newValues.join('');
|
|
27915
|
-
setTimeout(() => {
|
|
27916
|
-
onComplete?.(action.toolCallId, {
|
|
27917
|
-
...action.state,
|
|
27918
|
-
otpCode: code,
|
|
27919
|
-
});
|
|
27920
|
-
}, 100);
|
|
27921
|
-
}
|
|
27922
|
-
}, 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" })] })] }));
|
|
27923
|
-
}
|
|
27924
|
-
// Phase 3: Options Menu (with inline cancel buttons and confirmation)
|
|
27925
|
-
if (phase === "awaiting_options") {
|
|
27926
|
-
const userAppointments = state.userAppointments || [];
|
|
27927
|
-
const maxAppointments = state.maxAppointmentsPerUser || 3;
|
|
27928
|
-
const appointmentCount = userAppointments.length;
|
|
27929
|
-
const canBook = appointmentCount < maxAppointments;
|
|
27930
|
-
const hasAppointments = appointmentCount > 0;
|
|
27931
|
-
const displayError = state.errorMessage || selectionError;
|
|
27932
|
-
// If confirming cancellation, show confirmation dialog
|
|
27933
|
-
if (selectedId) {
|
|
27934
|
-
const appointmentToCancel = userAppointments.find(appt => appt.id === selectedId);
|
|
27935
|
-
if (!appointmentToCancel) {
|
|
27936
|
-
setSelectedId(null); // Reset if appointment not found
|
|
27937
|
-
}
|
|
27938
|
-
else {
|
|
27939
|
-
return (jsxRuntime.jsxs("div", { className: `ai-chat-action-card ${className}`, style: accentStyle, children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-header", children: [jsxRuntime.jsx(CalendarIcon, {}), "Confirm Cancellation"] }), 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: () => {
|
|
27940
|
-
setIsSubmitting(true);
|
|
27941
|
-
onComplete?.(action.toolCallId, {
|
|
27942
|
-
...action.state,
|
|
27943
|
-
selectedOption: "cancel",
|
|
27944
|
-
selectedAppointmentId: selectedId,
|
|
27945
|
-
});
|
|
27946
|
-
}, disabled: isSubmitting, children: isSubmitting ? 'Cancelling...' : 'Confirm Cancellation' }), jsxRuntime.jsx("button", { className: "ai-chat-action-button-secondary", type: "button", onClick: () => {
|
|
27947
|
-
setSelectedId(null);
|
|
27948
|
-
setSelectionError(null);
|
|
27949
|
-
}, disabled: isSubmitting, children: "Go Back" })] })] })] }));
|
|
27950
|
-
}
|
|
27951
|
-
}
|
|
27952
|
-
// Normal view with inline cancel buttons
|
|
27953
|
-
return (jsxRuntime.jsxs("div", { className: `ai-chat-action-card ${className}`, style: accentStyle, children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-header", children: [jsxRuntime.jsx(CalendarIcon, {}), "Manage Your Appointments"] }), 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: () => {
|
|
27954
|
-
setSelectedId(appt.id);
|
|
27955
|
-
setSelectionError(null);
|
|
27956
|
-
}, 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: () => {
|
|
27957
|
-
setIsSubmitting(true);
|
|
27958
|
-
onComplete?.(action.toolCallId, {
|
|
27959
|
-
...action.state,
|
|
27960
|
-
selectedOption: "book",
|
|
27961
|
-
});
|
|
27962
|
-
}, disabled: isSubmitting, children: isSubmitting ? 'Loading...' : 'Book New Appointment' })), !canBook && !hasAppointments && (jsxRuntime.jsx("div", { className: "ai-chat-action-hint", children: "No appointments found." }))] })] }));
|
|
27963
|
-
}
|
|
27964
|
-
// Phase 4: Appointment Selection (Booking)
|
|
27965
|
-
if (phase === "awaiting_booking") {
|
|
27966
|
-
const displayError = state.errorMessage || selectionError;
|
|
27967
|
-
return (jsxRuntime.jsxs("div", { className: `ai-chat-action-card ${className}`, style: accentStyle, children: [jsxRuntime.jsxs("div", { className: "ai-chat-action-header", children: [jsxRuntime.jsx(CalendarIcon, {}), "Select Appointment Time"] }), 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: () => {
|
|
27968
|
-
setSelectedDate(date);
|
|
27969
|
-
setSelectedSlot(null);
|
|
27970
|
-
}, 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." }))] })] }));
|
|
27971
|
-
}
|
|
27972
|
-
// Fallback
|
|
27973
|
-
return null;
|
|
27974
|
-
}
|
|
27975
|
-
|
|
27976
|
-
function truncate(text, maxLength) {
|
|
27612
|
+
function truncate$1(text, maxLength) {
|
|
27977
27613
|
if (text.length <= maxLength)
|
|
27978
27614
|
return text;
|
|
27979
27615
|
return text.slice(0, maxLength).trim() + '...';
|
|
27980
27616
|
}
|
|
27981
|
-
function ExternalLinkIcon() {
|
|
27617
|
+
function ExternalLinkIcon$2() {
|
|
27982
27618
|
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" })] }));
|
|
27983
27619
|
}
|
|
27984
27620
|
function SingleLinkPreview({ link, onLinkClick, accentColor }) {
|
|
@@ -27997,10 +27633,10 @@ function SingleLinkPreview({ link, onLinkClick, accentColor }) {
|
|
|
27997
27633
|
e.currentTarget.parentElement.style.display = 'none';
|
|
27998
27634
|
} }) })), 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) => {
|
|
27999
27635
|
e.currentTarget.style.display = 'none';
|
|
28000
|
-
} })), 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, {}) })] }));
|
|
27636
|
+
} })), 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$1(link.description, 120) }))] }), jsxRuntime.jsx("div", { className: "ai-chat-link-preview__arrow", children: jsxRuntime.jsx(ExternalLinkIcon$2, {}) })] }));
|
|
28001
27637
|
}
|
|
28002
27638
|
function LinkPreviewCard({ action, onComplete, accentColor }) {
|
|
28003
|
-
const rawState = action.
|
|
27639
|
+
const rawState = action.input;
|
|
28004
27640
|
const hasCompletedRef = React.useRef(false);
|
|
28005
27641
|
// Provide safe defaults if state is missing
|
|
28006
27642
|
const state = {
|
|
@@ -28030,11 +27666,12 @@ function LinkPreviewCard({ action, onComplete, accentColor }) {
|
|
|
28030
27666
|
if (state.links.length === 0) {
|
|
28031
27667
|
return null;
|
|
28032
27668
|
}
|
|
28033
|
-
|
|
28034
|
-
|
|
28035
|
-
|
|
28036
|
-
|
|
28037
|
-
|
|
27669
|
+
const gridClass = state.links.length === 1
|
|
27670
|
+
? 'ai-chat-link-preview-grid ai-chat-link-preview-grid--single'
|
|
27671
|
+
: state.links.length === 2
|
|
27672
|
+
? 'ai-chat-link-preview-grid ai-chat-link-preview-grid--double'
|
|
27673
|
+
: 'ai-chat-link-preview-grid ai-chat-link-preview-grid--triple';
|
|
27674
|
+
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: gridClass, children: state.links.map((link, index) => (jsxRuntime.jsx(SingleLinkPreview, { link: link, onLinkClick: () => handleLinkClick(link.url), accentColor: accentColor }, index))) })] }));
|
|
28038
27675
|
}
|
|
28039
27676
|
|
|
28040
27677
|
function PlayIcon() {
|
|
@@ -28055,7 +27692,7 @@ function getProviderLabel(provider) {
|
|
|
28055
27692
|
}
|
|
28056
27693
|
}
|
|
28057
27694
|
function VideoPlayerCard({ action, onComplete, accentColor }) {
|
|
28058
|
-
const rawState = action.
|
|
27695
|
+
const rawState = action.input;
|
|
28059
27696
|
const hasCompletedRef = React.useRef(false);
|
|
28060
27697
|
const [isPlaying, setIsPlaying] = React.useState(false);
|
|
28061
27698
|
// Provide safe defaults if state is missing
|
|
@@ -28166,7 +27803,7 @@ function LocationItem({ location, settings, accentColor, onDirections, showMap =
|
|
|
28166
27803
|
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"] }))] })] })] }));
|
|
28167
27804
|
}
|
|
28168
27805
|
function LocationCard({ action, onComplete, accentColor, maxColumns = 3 }) {
|
|
28169
|
-
const rawState = action.
|
|
27806
|
+
const rawState = action.input;
|
|
28170
27807
|
const hasCompletedRef = React.useRef(false);
|
|
28171
27808
|
const state = {
|
|
28172
27809
|
locations: rawState?.locations || [],
|
|
@@ -28248,7 +27885,7 @@ function ContactItem({ contact, settings, accentColor, onEmail, onPhone, compact
|
|
|
28248
27885
|
})()] }), 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"] }))] }))] })] }));
|
|
28249
27886
|
}
|
|
28250
27887
|
function ContactCard({ action, onComplete, accentColor, maxColumns = 3 }) {
|
|
28251
|
-
const rawState = action.
|
|
27888
|
+
const rawState = action.input;
|
|
28252
27889
|
const hasCompletedRef = React.useRef(false);
|
|
28253
27890
|
const state = {
|
|
28254
27891
|
contacts: rawState?.contacts || [],
|
|
@@ -28283,11 +27920,37 @@ function ContactCard({ action, onComplete, accentColor, maxColumns = 3 }) {
|
|
|
28283
27920
|
}, 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))) })] }));
|
|
28284
27921
|
}
|
|
28285
27922
|
|
|
28286
|
-
|
|
28287
|
-
|
|
27923
|
+
const CloseButton = ({ onClick, className = "", ariaLabel = "Close", }) => {
|
|
27924
|
+
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" }) }) }));
|
|
27925
|
+
};
|
|
27926
|
+
|
|
27927
|
+
function FormIcon(props) {
|
|
27928
|
+
return (jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [jsxRuntime.jsx("path", { d: "M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z" }), jsxRuntime.jsx("polyline", { points: "14,2 14,8 20,8" }), jsxRuntime.jsx("line", { x1: "16", y1: "13", x2: "8", y2: "13" }), jsxRuntime.jsx("line", { x1: "16", y1: "17", x2: "8", y2: "17" }), jsxRuntime.jsx("line", { x1: "10", y1: "9", x2: "8", y2: "9" })] }));
|
|
27929
|
+
}
|
|
27930
|
+
function CheckIcon(props) {
|
|
27931
|
+
return (jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: jsxRuntime.jsx("polyline", { points: "20,6 9,17 4,12" }) }));
|
|
27932
|
+
}
|
|
27933
|
+
function WarningIcon(props) {
|
|
27934
|
+
return (jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [jsxRuntime.jsx("path", { d: "M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" }), jsxRuntime.jsx("line", { x1: "12", y1: "9", x2: "12", y2: "13" }), jsxRuntime.jsx("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })] }));
|
|
27935
|
+
}
|
|
27936
|
+
function SkipIcon(props) {
|
|
27937
|
+
return (jsxRuntime.jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: [jsxRuntime.jsx("polygon", { points: "5,4 15,12 5,20 5,4" }), jsxRuntime.jsx("line", { x1: "19", y1: "5", x2: "19", y2: "19" })] }));
|
|
27938
|
+
}
|
|
27939
|
+
function BackArrowIcon(props) {
|
|
27940
|
+
return (jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", ...props, children: jsxRuntime.jsx("polyline", { points: "15,18 9,12 15,6" }) }));
|
|
27941
|
+
}
|
|
27942
|
+
|
|
27943
|
+
function FormCard({ action, onComplete, onDismiss, accentColor }) {
|
|
27944
|
+
const state = action.input;
|
|
28288
27945
|
const [currentStep, setCurrentStep] = React.useState(0);
|
|
28289
27946
|
const [answers, setAnswers] = React.useState({});
|
|
28290
27947
|
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
|
27948
|
+
const [localStatus, setLocalStatus] = React.useState(null);
|
|
27949
|
+
// If action is already done, show simple completion indicator
|
|
27950
|
+
// The agent's response will convey what happened
|
|
27951
|
+
if (action.done) {
|
|
27952
|
+
return (jsxRuntime.jsx("div", { className: "ai-chat-form-card ai-chat-form-card--completed", children: jsxRuntime.jsxs("div", { className: "ai-chat-form-card__header", children: [jsxRuntime.jsx("span", { className: "ai-chat-form-card__icon", children: jsxRuntime.jsx(CheckIcon, {}) }), jsxRuntime.jsx("span", { className: "ai-chat-form-card__title", children: "Form completed" })] }) }));
|
|
27953
|
+
}
|
|
28291
27954
|
const questions = state.questions || [];
|
|
28292
27955
|
const currentQuestion = questions[currentStep];
|
|
28293
27956
|
const totalQuestions = questions.length;
|
|
@@ -28308,6 +27971,7 @@ function FormCard({ action, onComplete, accentColor }) {
|
|
|
28308
27971
|
if (!onComplete)
|
|
28309
27972
|
return;
|
|
28310
27973
|
setIsSubmitting(true);
|
|
27974
|
+
setLocalStatus('submitted');
|
|
28311
27975
|
const formattedAnswers = Object.entries(answers).map(([questionId, value]) => ({
|
|
28312
27976
|
questionId,
|
|
28313
27977
|
value,
|
|
@@ -28321,6 +27985,7 @@ function FormCard({ action, onComplete, accentColor }) {
|
|
|
28321
27985
|
const handleSkip = () => {
|
|
28322
27986
|
if (!onComplete || !state.settings.allowSkip)
|
|
28323
27987
|
return;
|
|
27988
|
+
setLocalStatus('skipped');
|
|
28324
27989
|
onComplete(action.toolCallId, {
|
|
28325
27990
|
...state,
|
|
28326
27991
|
status: 'skipped',
|
|
@@ -28348,23 +28013,29 @@ function FormCard({ action, onComplete, accentColor }) {
|
|
|
28348
28013
|
return answer.trim() !== '';
|
|
28349
28014
|
});
|
|
28350
28015
|
};
|
|
28016
|
+
const handleDismiss = () => {
|
|
28017
|
+
onDismiss?.(action.toolCallId);
|
|
28018
|
+
};
|
|
28019
|
+
// Use local status if available (for immediate UI feedback), otherwise use state.status
|
|
28020
|
+
const effectiveStatus = localStatus || state.status;
|
|
28351
28021
|
// Error state
|
|
28352
|
-
if (
|
|
28353
|
-
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:
|
|
28022
|
+
if (effectiveStatus === 'error') {
|
|
28023
|
+
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: jsxRuntime.jsx(WarningIcon, {}) }), 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' })] }));
|
|
28354
28024
|
}
|
|
28355
28025
|
// Submitted state
|
|
28356
|
-
if (
|
|
28357
|
-
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:
|
|
28026
|
+
if (effectiveStatus === 'submitted') {
|
|
28027
|
+
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: jsxRuntime.jsx(CheckIcon, {}) }), 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!' })] }));
|
|
28358
28028
|
}
|
|
28359
28029
|
// Skipped state
|
|
28360
|
-
if (
|
|
28361
|
-
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:
|
|
28030
|
+
if (effectiveStatus === 'skipped') {
|
|
28031
|
+
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: jsxRuntime.jsx(SkipIcon, {}) }), 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" })] }));
|
|
28362
28032
|
}
|
|
28363
28033
|
// No questions
|
|
28364
28034
|
if (totalQuestions === 0) {
|
|
28365
|
-
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:
|
|
28035
|
+
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: jsxRuntime.jsx(FormIcon, {}) }), 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." })] }));
|
|
28366
28036
|
}
|
|
28367
|
-
|
|
28037
|
+
const showCloseButton = effectiveStatus === "displaying" && !action.done && Boolean(onDismiss);
|
|
28038
|
+
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: jsxRuntime.jsx(FormIcon, {}) }), 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: {
|
|
28368
28039
|
width: `${((currentStep + 1) / totalQuestions) * 100}%`,
|
|
28369
28040
|
backgroundColor: accentColor || 'var(--ai-chat-accent-color, #3b82f6)',
|
|
28370
28041
|
} }), 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) => {
|
|
@@ -28390,104 +28061,915 @@ function FormCard({ action, onComplete, accentColor }) {
|
|
|
28390
28061
|
}, children: isSubmitting ? 'Submitting...' : (state.settings.submitButtonText || 'Submit') }))] })] }));
|
|
28391
28062
|
}
|
|
28392
28063
|
|
|
28393
|
-
|
|
28394
|
-
const
|
|
28395
|
-
const
|
|
28396
|
-
const
|
|
28397
|
-
|
|
28398
|
-
|
|
28399
|
-
|
|
28400
|
-
|
|
28401
|
-
|
|
28402
|
-
|
|
28403
|
-
|
|
28404
|
-
|
|
28405
|
-
|
|
28406
|
-
|
|
28407
|
-
|
|
28408
|
-
|
|
28409
|
-
const
|
|
28410
|
-
|
|
28411
|
-
|
|
28412
|
-
|
|
28413
|
-
|
|
28414
|
-
|
|
28415
|
-
|
|
28416
|
-
|
|
28417
|
-
|
|
28418
|
-
console.error("[Action] Failed to resume action:", error);
|
|
28419
|
-
});
|
|
28420
|
-
}
|
|
28421
|
-
}
|
|
28422
|
-
function registerActionResumeCallback(toolCallId, callback) {
|
|
28423
|
-
resumeCallbacks.set(toolCallId, callback);
|
|
28424
|
-
}
|
|
28425
|
-
function unregisterActionResumeCallback(toolCallId) {
|
|
28426
|
-
resumeCallbacks.delete(toolCallId);
|
|
28064
|
+
function EmailPhase({ accentColor, onDismiss, showCloseButton, onSubmit, isLoading, error, }) {
|
|
28065
|
+
const [emailInput, setEmailInput] = React.useState('');
|
|
28066
|
+
const [localError, setLocalError] = React.useState(null);
|
|
28067
|
+
const handleSubmit = () => {
|
|
28068
|
+
const trimmedEmail = emailInput.trim();
|
|
28069
|
+
if (!trimmedEmail) {
|
|
28070
|
+
setLocalError('Please enter your email address');
|
|
28071
|
+
return;
|
|
28072
|
+
}
|
|
28073
|
+
if (!trimmedEmail.includes('@')) {
|
|
28074
|
+
setLocalError('Please enter a valid email address');
|
|
28075
|
+
return;
|
|
28076
|
+
}
|
|
28077
|
+
setLocalError(null);
|
|
28078
|
+
onSubmit(trimmedEmail);
|
|
28079
|
+
};
|
|
28080
|
+
const displayError = localError || error;
|
|
28081
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-booking-card${showCloseButton ? ' ai-chat-booking-card--closable' : ''}`, children: [jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__header", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__title", children: "Enter Your Email" }), showCloseButton && onDismiss && jsxRuntime.jsx(CloseButton, { onClick: onDismiss, ariaLabel: "Cancel booking" })] }), 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) => {
|
|
28082
|
+
setEmailInput(e.target.value);
|
|
28083
|
+
setLocalError(null);
|
|
28084
|
+
}, onKeyDown: (e) => {
|
|
28085
|
+
if (e.key === 'Enter' && !isLoading) {
|
|
28086
|
+
handleSubmit();
|
|
28087
|
+
}
|
|
28088
|
+
}, disabled: isLoading }), jsxRuntime.jsx("button", { className: "ai-chat-booking-card__btn ai-chat-booking-card__btn--primary", onClick: handleSubmit, disabled: !emailInput.trim() || isLoading, children: isLoading ? 'Sending...' : 'Continue' })] })] }));
|
|
28427
28089
|
}
|
|
28428
28090
|
|
|
28429
|
-
function
|
|
28430
|
-
|
|
28431
|
-
|
|
28091
|
+
function Skeleton({ width, height, borderRadius = '4px' }) {
|
|
28092
|
+
return (jsxRuntime.jsx("div", { className: "ai-chat-action-skeleton-item", style: { width, height, borderRadius } }));
|
|
28093
|
+
}
|
|
28094
|
+
|
|
28095
|
+
function PinInputGroup({ values, onChange, disabled }) {
|
|
28096
|
+
const inputRefs = React.useRef([]);
|
|
28097
|
+
const handleChange = (index, value) => {
|
|
28098
|
+
// Only allow digits
|
|
28099
|
+
const digit = value.replace(/[^0-9]/g, '');
|
|
28100
|
+
const newValues = [...values];
|
|
28101
|
+
newValues[index] = digit.slice(-1);
|
|
28102
|
+
onChange(newValues);
|
|
28103
|
+
// Auto-focus next input
|
|
28104
|
+
if (digit && index < 5) {
|
|
28105
|
+
inputRefs.current[index + 1]?.focus();
|
|
28106
|
+
}
|
|
28107
|
+
};
|
|
28108
|
+
const handleKeyDown = (index, e) => {
|
|
28109
|
+
if (e.key === 'Backspace' && !values[index] && index > 0) {
|
|
28110
|
+
inputRefs.current[index - 1]?.focus();
|
|
28111
|
+
}
|
|
28112
|
+
};
|
|
28113
|
+
const handlePaste = (e) => {
|
|
28114
|
+
e.preventDefault();
|
|
28115
|
+
const pastedData = e.clipboardData.getData('text').replace(/[^0-9]/g, '').slice(0, 6);
|
|
28116
|
+
const newValues = pastedData.split('').concat(Array(6 - pastedData.length).fill(''));
|
|
28117
|
+
onChange(newValues);
|
|
28118
|
+
// Focus the next empty input or the last one
|
|
28119
|
+
const nextIndex = Math.min(pastedData.length, 5);
|
|
28120
|
+
inputRefs.current[nextIndex]?.focus();
|
|
28121
|
+
};
|
|
28122
|
+
return (jsxRuntime.jsx("div", { className: "ai-chat-pin-input-group", children: values.map((value, index) => (jsxRuntime.jsx("input", { ref: (el) => {
|
|
28123
|
+
inputRefs.current[index] = el;
|
|
28124
|
+
}, 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))) }));
|
|
28125
|
+
}
|
|
28126
|
+
|
|
28127
|
+
function OtpPhase({ accentColor, onDismiss, showCloseButton, email, onSubmit, onResend, isLoading, error, }) {
|
|
28128
|
+
const [otpValues, setOtpValues] = React.useState(Array(6).fill(''));
|
|
28129
|
+
const [localError, setLocalError] = React.useState(null);
|
|
28130
|
+
const [isResending, setIsResending] = React.useState(false);
|
|
28131
|
+
const handleSubmit = () => {
|
|
28132
|
+
const code = otpValues.join('');
|
|
28133
|
+
if (code.length !== 6) {
|
|
28134
|
+
setLocalError('Please enter the 6-digit code');
|
|
28135
|
+
return;
|
|
28136
|
+
}
|
|
28137
|
+
setLocalError(null);
|
|
28138
|
+
onSubmit(code);
|
|
28139
|
+
};
|
|
28140
|
+
const handleResend = async () => {
|
|
28141
|
+
setIsResending(true);
|
|
28142
|
+
setLocalError(null);
|
|
28143
|
+
try {
|
|
28144
|
+
await onResend();
|
|
28145
|
+
}
|
|
28146
|
+
finally {
|
|
28147
|
+
setIsResending(false);
|
|
28148
|
+
}
|
|
28432
28149
|
};
|
|
28150
|
+
const displayError = localError || error;
|
|
28151
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-booking-card${showCloseButton ? ' ai-chat-booking-card--closable' : ''}`, children: [jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__header", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__title", children: "Verify Your Email" }), showCloseButton && onDismiss && jsxRuntime.jsx(CloseButton, { onClick: onDismiss, ariaLabel: "Cancel booking" })] }), 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: email })] }), jsxRuntime.jsx(PinInputGroup, { values: otpValues, onChange: (newValues) => {
|
|
28152
|
+
setOtpValues(newValues);
|
|
28153
|
+
setLocalError(null);
|
|
28154
|
+
// Auto-submit when all 6 digits are entered
|
|
28155
|
+
if (newValues.every((value) => value.length === 1) && !isLoading) {
|
|
28156
|
+
onSubmit(newValues.join(''));
|
|
28157
|
+
}
|
|
28158
|
+
}, disabled: isLoading }), 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: handleSubmit, disabled: otpValues.join('').length !== 6 || isLoading, children: isLoading ? 'Verifying...' : 'Verify' }), jsxRuntime.jsx("button", { className: "ai-chat-booking-card__btn ai-chat-booking-card__btn--secondary", onClick: handleResend, disabled: isLoading || isResending, children: isResending ? 'Sending...' : 'Resend Code' })] })] }));
|
|
28159
|
+
}
|
|
28160
|
+
|
|
28161
|
+
function ContactSelectionPhase({ accentColor, onDismiss, showCloseButton, contacts, onSelect, }) {
|
|
28162
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-booking-card${showCloseButton ? ' ai-chat-booking-card--closable' : ''}`, children: [jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__header", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__title", children: "Select Contact" }), showCloseButton && onDismiss && jsxRuntime.jsx(CloseButton, { onClick: onDismiss, ariaLabel: "Cancel booking" })] }), jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__content", children: [jsxRuntime.jsx("p", { className: "ai-chat-booking-card__description", children: "Choose who you would like to book an appointment with:" }), jsxRuntime.jsx("div", { className: "ai-chat-booking-card__list", children: contacts.map((contact) => (jsxRuntime.jsxs("button", { className: "ai-chat-booking-card__list-item", onClick: () => onSelect(contact.id), children: [jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__list-item-content", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__list-item-name", children: contact.name }), contact.role && (jsxRuntime.jsx("span", { className: "ai-chat-booking-card__list-item-role", children: contact.role }))] }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__list-item-arrow", "aria-hidden": "true" })] }, contact.id))) })] })] }));
|
|
28163
|
+
}
|
|
28164
|
+
|
|
28165
|
+
// Calendar icon SVG
|
|
28166
|
+
const CalendarIcon = () => (jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2", ry: "2" }), jsxRuntime.jsx("line", { x1: "16", y1: "2", x2: "16", y2: "6" }), jsxRuntime.jsx("line", { x1: "8", y1: "2", x2: "8", y2: "6" }), jsxRuntime.jsx("line", { x1: "3", y1: "10", x2: "21", y2: "10" })] }));
|
|
28167
|
+
// List icon SVG
|
|
28168
|
+
const ListIcon = () => (jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("line", { x1: "8", y1: "6", x2: "21", y2: "6" }), jsxRuntime.jsx("line", { x1: "8", y1: "12", x2: "21", y2: "12" }), jsxRuntime.jsx("line", { x1: "8", y1: "18", x2: "21", y2: "18" }), jsxRuntime.jsx("line", { x1: "3", y1: "6", x2: "3.01", y2: "6" }), jsxRuntime.jsx("line", { x1: "3", y1: "12", x2: "3.01", y2: "12" }), jsxRuntime.jsx("line", { x1: "3", y1: "18", x2: "3.01", y2: "18" })] }));
|
|
28169
|
+
function OptionsPhase({ onDismiss, showCloseButton, appointments, maxAppointments, onBookNew, onViewAppointments, isCancelling, }) {
|
|
28170
|
+
const activeAppointments = appointments.filter(a => a.status !== 'cancelled' && a.status !== 'declined');
|
|
28171
|
+
const canBookNew = activeAppointments.length < maxAppointments;
|
|
28172
|
+
const hasAppointments = appointments.length > 0;
|
|
28173
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-booking-card${showCloseButton ? ' ai-chat-booking-card--closable' : ''}`, children: [jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__header", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__title", children: "Booking Options" }), showCloseButton && onDismiss && jsxRuntime.jsx(CloseButton, { onClick: onDismiss, ariaLabel: "Cancel booking" })] }), jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__content", children: [!canBookNew && (jsxRuntime.jsxs("p", { className: "ai-chat-booking-card__warning", children: ["You have reached the maximum number of appointments (", maxAppointments, "). Please cancel an existing appointment to book a new one."] })), jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__options", children: [jsxRuntime.jsxs("button", { className: "ai-chat-booking-card__option-btn", onClick: onBookNew, disabled: !canBookNew || isCancelling, children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__option-icon", children: jsxRuntime.jsx(CalendarIcon, {}) }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__option-text", children: "Book New Appointment" })] }), hasAppointments && (jsxRuntime.jsxs("button", { className: "ai-chat-booking-card__option-btn", onClick: onViewAppointments, disabled: isCancelling, children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__option-icon", children: jsxRuntime.jsx(ListIcon, {}) }), jsxRuntime.jsxs("span", { className: "ai-chat-booking-card__option-text", children: ["View My Appointments (", activeAppointments.length, ")"] })] }))] })] })] }));
|
|
28174
|
+
}
|
|
28175
|
+
|
|
28176
|
+
function ViewAppointmentsPhase({ accentColor, onDismiss, showCloseButton, appointments, onBack, onCancelAppointment, isCancelling, }) {
|
|
28177
|
+
const [cancellingId, setCancellingId] = React.useState(null);
|
|
28178
|
+
const handleCancel = async (appointmentId) => {
|
|
28179
|
+
setCancellingId(appointmentId);
|
|
28180
|
+
await onCancelAppointment(appointmentId);
|
|
28181
|
+
setCancellingId(null);
|
|
28182
|
+
};
|
|
28183
|
+
const activeAppointments = appointments.filter(a => a.status !== 'cancelled');
|
|
28184
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-booking-card${showCloseButton ? ' ai-chat-booking-card--closable' : ''}`, children: [jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__header", children: [jsxRuntime.jsx("button", { className: "ai-chat-booking-card__back-btn", onClick: onBack, children: "Back" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__title", children: "My Appointments" }), showCloseButton && onDismiss && jsxRuntime.jsx(CloseButton, { onClick: onDismiss, ariaLabel: "Cancel booking" })] }), jsxRuntime.jsx("div", { className: "ai-chat-booking-card__content", children: activeAppointments.length === 0 ? (jsxRuntime.jsx("p", { className: "ai-chat-booking-card__empty", children: "No appointments found." })) : (jsxRuntime.jsx("div", { className: "ai-chat-booking-card__appointments", children: activeAppointments.map((apt) => (jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__appointment", children: [jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__appointment-info", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__appointment-subject", children: apt.subject }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__appointment-contact", children: apt.contactName }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__appointment-time", children: apt.displayTime }), jsxRuntime.jsx("span", { className: `ai-chat-booking-card__appointment-status ai-chat-booking-card__appointment-status--${apt.status}`, children: apt.status === 'pending' ? 'Pending Approval' : apt.status })] }), jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__appointment-actions", children: [apt.teamsLink && (jsxRuntime.jsx("a", { href: apt.teamsLink, target: "_blank", rel: "noopener noreferrer", className: "ai-chat-booking-card__appointment-link", children: "Join Meeting" })), apt.googleMeetLink && (jsxRuntime.jsx("a", { href: apt.googleMeetLink, target: "_blank", rel: "noopener noreferrer", className: "ai-chat-booking-card__appointment-link", children: "Join Meeting" })), jsxRuntime.jsx("button", { className: "ai-chat-booking-card__appointment-cancel", onClick: () => handleCancel(apt.id), disabled: isCancelling || cancellingId === apt.id, children: cancellingId === apt.id ? 'Cancelling...' : 'Cancel' })] })] }, apt.id))) })) })] }));
|
|
28185
|
+
}
|
|
28186
|
+
|
|
28187
|
+
function SlotSelectionPhase({ onDismiss, showCloseButton, slots, contactName, onSelect, onBack, isLoading, error, }) {
|
|
28188
|
+
// Group slots by date
|
|
28189
|
+
const slotsByDate = React.useMemo(() => {
|
|
28190
|
+
const grouped = {};
|
|
28191
|
+
for (const slot of slots) {
|
|
28192
|
+
const date = slot.displayDate;
|
|
28193
|
+
if (!grouped[date]) {
|
|
28194
|
+
grouped[date] = [];
|
|
28195
|
+
}
|
|
28196
|
+
grouped[date].push(slot);
|
|
28197
|
+
}
|
|
28198
|
+
return grouped;
|
|
28199
|
+
}, [slots]);
|
|
28200
|
+
const dates = Object.keys(slotsByDate);
|
|
28201
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-booking-card${showCloseButton ? ' ai-chat-booking-card--closable' : ''}`, children: [jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__header", children: [jsxRuntime.jsx("button", { className: "ai-chat-booking-card__back-btn", onClick: onBack, children: "Back" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__title", children: "Select Time Slot" }), showCloseButton && onDismiss && jsxRuntime.jsx(CloseButton, { onClick: onDismiss, ariaLabel: "Cancel booking" })] }), jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__content", children: [contactName && (jsxRuntime.jsxs("p", { className: "ai-chat-booking-card__description", children: ["Available times for ", jsxRuntime.jsx("strong", { children: contactName }), ":"] })), error && (jsxRuntime.jsx("p", { className: "ai-chat-booking-card__error", children: error })), slots.length === 0 ? (jsxRuntime.jsx("p", { className: "ai-chat-booking-card__empty", children: "No available time slots found. Please try again later." })) : (jsxRuntime.jsx("div", { className: "ai-chat-booking-card__slots-container", children: dates.map((date) => (jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__date-group", children: [jsxRuntime.jsx("h4", { className: "ai-chat-booking-card__date-header", children: date }), jsxRuntime.jsx("div", { className: "ai-chat-booking-card__slots", children: slotsByDate[date].map((slot) => (jsxRuntime.jsx("button", { className: "ai-chat-booking-card__slot", onClick: () => onSelect({ startTime: slot.startTime, endTime: slot.endTime }), disabled: isLoading, children: slot.displayTime }, `${slot.startTime}-${slot.endTime}`))) })] }, date))) }))] })] }));
|
|
28202
|
+
}
|
|
28203
|
+
|
|
28204
|
+
function ConfirmationPhase({ onDismiss, showCloseButton, contactName, slotDisplay, subjectMode, predefinedSubjects, onConfirm, onBack, isLoading, error, }) {
|
|
28205
|
+
const [subject, setSubject] = React.useState('');
|
|
28206
|
+
const [selectedPredefined, setSelectedPredefined] = React.useState('');
|
|
28207
|
+
const [localError, setLocalError] = React.useState(null);
|
|
28208
|
+
const handleConfirm = () => {
|
|
28209
|
+
const finalSubject = subjectMode === 'predefined' ? selectedPredefined : subject.trim();
|
|
28210
|
+
if (!finalSubject) {
|
|
28211
|
+
setLocalError('Please enter a subject for your appointment');
|
|
28212
|
+
return;
|
|
28213
|
+
}
|
|
28214
|
+
setLocalError(null);
|
|
28215
|
+
onConfirm(finalSubject);
|
|
28216
|
+
};
|
|
28217
|
+
const displayError = localError || error;
|
|
28218
|
+
const isValid = subjectMode === 'predefined' ? !!selectedPredefined : !!subject.trim();
|
|
28219
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-booking-card${showCloseButton ? ' ai-chat-booking-card--closable' : ''}`, children: [jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__header", children: [jsxRuntime.jsx("button", { className: "ai-chat-booking-card__back-btn", onClick: onBack, "aria-label": "Go back", children: jsxRuntime.jsx(BackArrowIcon, {}) }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__title", children: "Confirm Booking" }), showCloseButton && onDismiss && jsxRuntime.jsx(CloseButton, { onClick: onDismiss, ariaLabel: "Cancel booking" })] }), jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__content", children: [jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__summary", children: [contactName && (jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__summary-row", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__summary-label", children: "With:" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__summary-value", children: contactName })] })), jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__summary-row", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__summary-label", children: "When:" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__summary-value", children: slotDisplay })] })] }), displayError && (jsxRuntime.jsx("p", { className: "ai-chat-booking-card__error", children: displayError })), subjectMode === 'predefined' ? (jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__field", children: [jsxRuntime.jsx("label", { className: "ai-chat-booking-card__label", children: "Select a topic:" }), jsxRuntime.jsx("div", { className: "ai-chat-booking-card__subject-options", children: predefinedSubjects.map((subj) => (jsxRuntime.jsx("button", { className: `ai-chat-booking-card__subject-option ${selectedPredefined === subj ? 'ai-chat-booking-card__subject-option--selected' : ''}`, onClick: () => {
|
|
28220
|
+
setSelectedPredefined(subj);
|
|
28221
|
+
setLocalError(null);
|
|
28222
|
+
}, disabled: isLoading, children: subj }, subj))) })] })) : (jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__field", children: [jsxRuntime.jsx("label", { className: "ai-chat-booking-card__label", children: "What would you like to discuss?" }), jsxRuntime.jsx("input", { type: "text", className: "ai-chat-booking-card__input", placeholder: "Enter appointment subject...", value: subject, onChange: (e) => {
|
|
28223
|
+
setSubject(e.target.value);
|
|
28224
|
+
setLocalError(null);
|
|
28225
|
+
}, onKeyDown: (e) => {
|
|
28226
|
+
if (e.key === 'Enter' && isValid && !isLoading) {
|
|
28227
|
+
handleConfirm();
|
|
28228
|
+
}
|
|
28229
|
+
}, disabled: isLoading })] })), jsxRuntime.jsx("button", { className: "ai-chat-booking-card__btn ai-chat-booking-card__btn--primary", onClick: handleConfirm, disabled: !isValid || isLoading, children: isLoading ? 'Booking...' : 'Confirm Booking' })] })] }));
|
|
28230
|
+
}
|
|
28231
|
+
|
|
28232
|
+
function BookedPhase({ subject, contactName, meetingLink, meetingProvider, }) {
|
|
28233
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-booking-card ai-chat-booking-card--success", children: [jsxRuntime.jsx("div", { className: "ai-chat-booking-card__header", children: jsxRuntime.jsx("span", { className: "ai-chat-booking-card__title", children: "Appointment Booked" }) }), jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__content", children: [jsxRuntime.jsx("div", { className: "ai-chat-booking-card__success-icon", "aria-hidden": "true" }), jsxRuntime.jsx("p", { className: "ai-chat-booking-card__success-message", children: "Your appointment has been successfully scheduled." }), 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: "Subject:" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__summary-value", children: subject })] }), contactName && (jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__summary-row", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__summary-label", children: "With:" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__summary-value", children: contactName })] }))] }), meetingLink && (jsxRuntime.jsx("a", { href: meetingLink, target: "_blank", rel: "noopener noreferrer", className: "ai-chat-booking-card__btn ai-chat-booking-card__btn--primary", style: {
|
|
28234
|
+
textDecoration: 'none',
|
|
28235
|
+
display: 'inline-block',
|
|
28236
|
+
textAlign: 'center',
|
|
28237
|
+
}, children: meetingProvider === 'google' ? 'Join Google Meet' : 'Join Teams Meeting' }))] })] }));
|
|
28238
|
+
}
|
|
28239
|
+
|
|
28240
|
+
function PendingApprovalPhase({ subject, contactName }) {
|
|
28241
|
+
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__status-icon ai-chat-booking-card__status-icon--pending" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__title", children: "Awaiting Approval" })] }), jsxRuntime.jsxs("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." }), 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: "Subject:" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__summary-value", children: subject })] }), contactName && (jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__summary-row", children: [jsxRuntime.jsx("span", { className: "ai-chat-booking-card__summary-label", children: "With:" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__summary-value", children: contactName })] }))] })] })] }));
|
|
28433
28242
|
}
|
|
28434
28243
|
|
|
28435
28244
|
/**
|
|
28436
|
-
*
|
|
28437
|
-
* Called by initializeActionHandlers to prevent tree-shaking.
|
|
28245
|
+
* Cancelled Phase Component (terminal state)
|
|
28438
28246
|
*/
|
|
28439
|
-
function
|
|
28440
|
-
|
|
28441
|
-
|
|
28442
|
-
|
|
28443
|
-
|
|
28444
|
-
|
|
28445
|
-
|
|
28446
|
-
|
|
28447
|
-
|
|
28448
|
-
|
|
28449
|
-
|
|
28450
|
-
|
|
28451
|
-
|
|
28452
|
-
|
|
28453
|
-
|
|
28454
|
-
|
|
28455
|
-
|
|
28456
|
-
|
|
28457
|
-
|
|
28247
|
+
function CancelledPhase() {
|
|
28248
|
+
return (jsxRuntime.jsxs("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__status-icon ai-chat-booking-card__status-icon--cancelled" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__title", children: "Appointment Cancelled" })] }), jsxRuntime.jsx("div", { className: "ai-chat-booking-card__content", children: jsxRuntime.jsx("p", { className: "ai-chat-booking-card__description", children: "This appointment has been cancelled." }) })] }));
|
|
28249
|
+
}
|
|
28250
|
+
|
|
28251
|
+
function ErrorPhase({ error, onRetry }) {
|
|
28252
|
+
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__status-icon ai-chat-booking-card__status-icon--error" }), jsxRuntime.jsx("span", { className: "ai-chat-booking-card__title", children: "Booking Error" })] }), jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__content", children: [jsxRuntime.jsx("p", { className: "ai-chat-booking-card__error", children: error }), onRetry && (jsxRuntime.jsx("button", { className: "ai-chat-booking-card__btn ai-chat-booking-card__btn--secondary", onClick: onRetry, children: "Try Again" }))] })] }));
|
|
28253
|
+
}
|
|
28254
|
+
|
|
28255
|
+
function CompletedPhase() {
|
|
28256
|
+
return (jsxRuntime.jsx("div", { className: "ai-chat-booking-card ai-chat-booking-card--completed", children: jsxRuntime.jsxs("div", { className: "ai-chat-booking-card__completed-indicator", children: [jsxRuntime.jsx(CheckIcon, {}), jsxRuntime.jsx("span", { children: "Booking action completed" })] }) }));
|
|
28257
|
+
}
|
|
28258
|
+
|
|
28259
|
+
function LoadingPhase() {
|
|
28260
|
+
return (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" })] }) }));
|
|
28261
|
+
}
|
|
28262
|
+
|
|
28263
|
+
/**
|
|
28264
|
+
* Core action lifecycle handlers for booking flow
|
|
28265
|
+
*/
|
|
28266
|
+
function useBookingAction({ onComplete, onDismiss, toolCallId, }) {
|
|
28267
|
+
const handleDismiss = React.useCallback(() => {
|
|
28268
|
+
onDismiss?.(toolCallId);
|
|
28269
|
+
}, [onDismiss, toolCallId]);
|
|
28270
|
+
const finishAction = React.useCallback((body) => {
|
|
28271
|
+
if (!onComplete)
|
|
28272
|
+
return;
|
|
28273
|
+
onComplete(toolCallId, body);
|
|
28274
|
+
}, [onComplete, toolCallId]);
|
|
28275
|
+
return {
|
|
28276
|
+
handleDismiss,
|
|
28277
|
+
finishAction,
|
|
28458
28278
|
};
|
|
28459
28279
|
}
|
|
28460
28280
|
|
|
28461
|
-
|
|
28462
|
-
|
|
28463
|
-
|
|
28281
|
+
/**
|
|
28282
|
+
* Helper functions for accessing booking data
|
|
28283
|
+
*/
|
|
28284
|
+
function useBookingHelpers({ contacts, slots, bookingMode, }) {
|
|
28285
|
+
const getContactName = React.useCallback((contactId) => {
|
|
28286
|
+
if (!contactId)
|
|
28287
|
+
return bookingMode === 'general' ? 'Shared Calendar' : null;
|
|
28288
|
+
const contact = contacts.find(c => c.id === contactId);
|
|
28289
|
+
return contact?.name || null;
|
|
28290
|
+
}, [contacts, bookingMode]);
|
|
28291
|
+
const getSlotDisplay = React.useCallback((slot) => {
|
|
28292
|
+
if (!slot)
|
|
28293
|
+
return '';
|
|
28294
|
+
const slotData = slots.find(s => s.startTime === slot.startTime && s.endTime === slot.endTime);
|
|
28295
|
+
return slotData ? `${slotData.displayDate} ${slotData.displayTime}` : '';
|
|
28296
|
+
}, [slots]);
|
|
28297
|
+
return {
|
|
28298
|
+
getContactName,
|
|
28299
|
+
getSlotDisplay,
|
|
28464
28300
|
};
|
|
28465
28301
|
}
|
|
28466
28302
|
|
|
28467
28303
|
/**
|
|
28468
|
-
*
|
|
28469
|
-
* Called by initializeActionHandlers to prevent tree-shaking.
|
|
28304
|
+
* OTP-related operations for email verification
|
|
28470
28305
|
*/
|
|
28471
|
-
function
|
|
28472
|
-
|
|
28473
|
-
|
|
28474
|
-
|
|
28475
|
-
|
|
28476
|
-
|
|
28477
|
-
|
|
28478
|
-
|
|
28479
|
-
|
|
28480
|
-
|
|
28306
|
+
function useBookingOtp({ onCallEndpoint, state, setState, config, }) {
|
|
28307
|
+
const handleSendOtp = React.useCallback(async (email) => {
|
|
28308
|
+
if (!onCallEndpoint) {
|
|
28309
|
+
setState(prev => ({ ...prev, error: 'Endpoint not available' }));
|
|
28310
|
+
return;
|
|
28311
|
+
}
|
|
28312
|
+
setState(prev => ({ ...prev, isLoading: true, error: null, email }));
|
|
28313
|
+
try {
|
|
28314
|
+
await onCallEndpoint('send-otp', { email });
|
|
28315
|
+
setState(prev => ({ ...prev, isLoading: false, step: 'otp' }));
|
|
28316
|
+
}
|
|
28317
|
+
catch (err) {
|
|
28318
|
+
const message = err instanceof Error ? err.message : 'Failed to send verification code';
|
|
28319
|
+
setState(prev => ({ ...prev, isLoading: false, error: message }));
|
|
28320
|
+
}
|
|
28321
|
+
}, [onCallEndpoint, setState]);
|
|
28322
|
+
const handleVerifyOtp = React.useCallback(async (code) => {
|
|
28323
|
+
if (!onCallEndpoint) {
|
|
28324
|
+
setState(prev => ({ ...prev, error: 'Endpoint not available' }));
|
|
28325
|
+
return;
|
|
28326
|
+
}
|
|
28327
|
+
setState(prev => ({ ...prev, isLoading: true, error: null }));
|
|
28328
|
+
try {
|
|
28329
|
+
const result = await onCallEndpoint('verify-otp', {
|
|
28330
|
+
email: state.email,
|
|
28331
|
+
code,
|
|
28332
|
+
bookingMode: config.bookingMode,
|
|
28333
|
+
timeZone: config.timeZone,
|
|
28334
|
+
});
|
|
28335
|
+
const nextStep = config.bookingMode === 'general'
|
|
28336
|
+
? 'options'
|
|
28337
|
+
: result.contacts.length === 1
|
|
28338
|
+
? 'options'
|
|
28339
|
+
: 'contact_selection';
|
|
28340
|
+
setState(prev => ({
|
|
28341
|
+
...prev,
|
|
28342
|
+
isLoading: false,
|
|
28343
|
+
token: result.token,
|
|
28344
|
+
contacts: result.contacts,
|
|
28345
|
+
appointments: result.appointments,
|
|
28346
|
+
selectedContactId: result.contacts.length === 1 ? result.contacts[0].id : null,
|
|
28347
|
+
step: nextStep,
|
|
28348
|
+
}));
|
|
28349
|
+
}
|
|
28350
|
+
catch (err) {
|
|
28351
|
+
const message = err instanceof Error ? err.message : 'Invalid verification code';
|
|
28352
|
+
setState(prev => ({ ...prev, isLoading: false, error: message }));
|
|
28353
|
+
}
|
|
28354
|
+
}, [onCallEndpoint, state.email, config.bookingMode, config.timeZone, setState]);
|
|
28355
|
+
const handleResendOtp = React.useCallback(async () => {
|
|
28356
|
+
await handleSendOtp(state.email);
|
|
28357
|
+
}, [handleSendOtp, state.email]);
|
|
28358
|
+
return {
|
|
28359
|
+
handleSendOtp,
|
|
28360
|
+
handleVerifyOtp,
|
|
28361
|
+
handleResendOtp,
|
|
28362
|
+
};
|
|
28363
|
+
}
|
|
28364
|
+
|
|
28365
|
+
/**
|
|
28366
|
+
* Navigation and flow control handlers for booking steps
|
|
28367
|
+
*/
|
|
28368
|
+
function useBookingNavigation({ onCallEndpoint, state, setState, config, }) {
|
|
28369
|
+
const handleSelectContact = React.useCallback((contactId) => {
|
|
28370
|
+
setState(prev => ({
|
|
28371
|
+
...prev,
|
|
28372
|
+
selectedContactId: contactId,
|
|
28373
|
+
step: 'options',
|
|
28374
|
+
}));
|
|
28375
|
+
}, [setState]);
|
|
28376
|
+
const handleBookNew = React.useCallback(async () => {
|
|
28377
|
+
if (!onCallEndpoint || !state.token) {
|
|
28378
|
+
setState(prev => ({ ...prev, error: 'Not authenticated' }));
|
|
28379
|
+
return;
|
|
28380
|
+
}
|
|
28381
|
+
setState(prev => ({ ...prev, isLoading: true, error: null }));
|
|
28382
|
+
try {
|
|
28383
|
+
const result = await onCallEndpoint('get-slots', {
|
|
28384
|
+
contactId: state.selectedContactId,
|
|
28385
|
+
daysAhead: config.daysAhead,
|
|
28386
|
+
timeZone: config.timeZone,
|
|
28387
|
+
}, { token: state.token });
|
|
28388
|
+
setState(prev => ({
|
|
28389
|
+
...prev,
|
|
28390
|
+
isLoading: false,
|
|
28391
|
+
slots: result.slots,
|
|
28392
|
+
step: 'slot_selection',
|
|
28393
|
+
}));
|
|
28394
|
+
}
|
|
28395
|
+
catch (err) {
|
|
28396
|
+
const message = err instanceof Error ? err.message : 'Failed to load available slots';
|
|
28397
|
+
setState(prev => ({ ...prev, isLoading: false, error: message }));
|
|
28398
|
+
}
|
|
28399
|
+
}, [onCallEndpoint, state.token, state.selectedContactId, config.daysAhead, config.timeZone, setState]);
|
|
28400
|
+
const handleViewAppointments = React.useCallback(() => {
|
|
28401
|
+
setState(prev => ({ ...prev, step: 'view_appointments' }));
|
|
28402
|
+
}, [setState]);
|
|
28403
|
+
const handleBackToOptions = React.useCallback(() => {
|
|
28404
|
+
setState(prev => ({ ...prev, step: 'options', error: null }));
|
|
28405
|
+
}, [setState]);
|
|
28406
|
+
const handleSelectSlot = React.useCallback((slot) => {
|
|
28407
|
+
setState(prev => ({
|
|
28408
|
+
...prev,
|
|
28409
|
+
selectedSlot: slot,
|
|
28410
|
+
step: 'confirmation',
|
|
28411
|
+
}));
|
|
28412
|
+
}, [setState]);
|
|
28413
|
+
const handleBackToSlots = React.useCallback(() => {
|
|
28414
|
+
setState(prev => ({ ...prev, step: 'slot_selection', error: null }));
|
|
28415
|
+
}, [setState]);
|
|
28416
|
+
return {
|
|
28417
|
+
handleSelectContact,
|
|
28418
|
+
handleBookNew,
|
|
28419
|
+
handleViewAppointments,
|
|
28420
|
+
handleBackToOptions,
|
|
28421
|
+
handleSelectSlot,
|
|
28422
|
+
handleBackToSlots,
|
|
28423
|
+
};
|
|
28424
|
+
}
|
|
28425
|
+
|
|
28426
|
+
/**
|
|
28427
|
+
* Core booking operations (confirm and cancel)
|
|
28428
|
+
*/
|
|
28429
|
+
function useBookingOperations({ onCallEndpoint, state, setState, config, getContactName, finishAction, }) {
|
|
28430
|
+
const [isCancelling, setIsCancelling] = React.useState(false);
|
|
28431
|
+
const handleConfirmBooking = React.useCallback(async (subject) => {
|
|
28432
|
+
if (!onCallEndpoint || !state.token || !state.selectedSlot) {
|
|
28433
|
+
setState(prev => ({ ...prev, error: 'Missing required data' }));
|
|
28434
|
+
return;
|
|
28435
|
+
}
|
|
28436
|
+
setState(prev => ({ ...prev, isLoading: true, error: null, subject }));
|
|
28437
|
+
try {
|
|
28438
|
+
const result = await onCallEndpoint('book', {
|
|
28439
|
+
contactId: state.selectedContactId,
|
|
28440
|
+
slot: state.selectedSlot,
|
|
28441
|
+
subject,
|
|
28442
|
+
timeZone: config.timeZone,
|
|
28443
|
+
}, { token: state.token });
|
|
28444
|
+
const contactName = getContactName(state.selectedContactId);
|
|
28445
|
+
if (result.status === 'pending_approval') {
|
|
28446
|
+
setState(prev => ({
|
|
28447
|
+
...prev,
|
|
28448
|
+
isLoading: false,
|
|
28449
|
+
step: 'pending_approval',
|
|
28450
|
+
}));
|
|
28451
|
+
// Finish with pending status
|
|
28452
|
+
finishAction({
|
|
28453
|
+
status: 'pending_approval',
|
|
28454
|
+
subject,
|
|
28455
|
+
contactName,
|
|
28456
|
+
message: `Appointment request "${subject}" submitted and awaiting approval.`,
|
|
28457
|
+
});
|
|
28458
|
+
}
|
|
28459
|
+
else {
|
|
28460
|
+
setState(prev => ({
|
|
28461
|
+
...prev,
|
|
28462
|
+
isLoading: false,
|
|
28463
|
+
bookedMeetingLink: result.meetingLink || null,
|
|
28464
|
+
bookedMeetingProvider: result.meetingProvider || null,
|
|
28465
|
+
step: 'booked',
|
|
28466
|
+
}));
|
|
28467
|
+
// Finish with booked status
|
|
28468
|
+
finishAction({
|
|
28469
|
+
status: 'booked',
|
|
28470
|
+
subject,
|
|
28471
|
+
contactName,
|
|
28472
|
+
meetingLink: result.meetingLink,
|
|
28473
|
+
message: `Successfully booked appointment "${subject}"${contactName ? ` with ${contactName}` : ''}.`,
|
|
28474
|
+
});
|
|
28475
|
+
}
|
|
28476
|
+
}
|
|
28477
|
+
catch (err) {
|
|
28478
|
+
const message = err instanceof Error ? err.message : 'Failed to book appointment';
|
|
28479
|
+
setState(prev => ({ ...prev, isLoading: false, error: message }));
|
|
28480
|
+
}
|
|
28481
|
+
}, [onCallEndpoint, state.token, state.selectedSlot, state.selectedContactId, config.timeZone, getContactName, finishAction, setState]);
|
|
28482
|
+
const handleCancelAppointment = React.useCallback(async (appointmentId) => {
|
|
28483
|
+
if (!onCallEndpoint || !state.token) {
|
|
28484
|
+
setState(prev => ({ ...prev, error: 'Not authenticated' }));
|
|
28485
|
+
return;
|
|
28486
|
+
}
|
|
28487
|
+
setIsCancelling(true);
|
|
28488
|
+
try {
|
|
28489
|
+
await onCallEndpoint('cancel', { appointmentId }, { token: state.token });
|
|
28490
|
+
// Update local appointments list
|
|
28491
|
+
setState(prev => ({
|
|
28492
|
+
...prev,
|
|
28493
|
+
appointments: prev.appointments.map(apt => apt.id === appointmentId ? { ...apt, status: 'cancelled' } : apt),
|
|
28494
|
+
}));
|
|
28495
|
+
}
|
|
28496
|
+
catch (err) {
|
|
28497
|
+
const message = err instanceof Error ? err.message : 'Failed to cancel appointment';
|
|
28498
|
+
setState(prev => ({ ...prev, error: message }));
|
|
28499
|
+
}
|
|
28500
|
+
finally {
|
|
28501
|
+
setIsCancelling(false);
|
|
28502
|
+
}
|
|
28503
|
+
}, [onCallEndpoint, state.token, setState]);
|
|
28504
|
+
return {
|
|
28505
|
+
handleConfirmBooking,
|
|
28506
|
+
handleCancelAppointment,
|
|
28507
|
+
isCancelling,
|
|
28508
|
+
};
|
|
28509
|
+
}
|
|
28510
|
+
|
|
28511
|
+
// Default config values
|
|
28512
|
+
const DEFAULT_CONFIG = {
|
|
28513
|
+
bookingMode: 'per-contact',
|
|
28514
|
+
timeZone: 'UTC',
|
|
28515
|
+
maxAppointmentsPerUser: 3,
|
|
28516
|
+
daysAhead: 14,
|
|
28517
|
+
subjectMode: 'user_defined',
|
|
28518
|
+
predefinedSubjects: [],
|
|
28519
|
+
};
|
|
28520
|
+
// Initial UI state
|
|
28521
|
+
const createInitialState = () => ({
|
|
28522
|
+
step: 'email',
|
|
28523
|
+
email: '',
|
|
28524
|
+
selectedContactId: null,
|
|
28525
|
+
selectedSlot: null,
|
|
28526
|
+
subject: '',
|
|
28527
|
+
token: null,
|
|
28528
|
+
contacts: [],
|
|
28529
|
+
appointments: [],
|
|
28530
|
+
slots: [],
|
|
28531
|
+
bookedMeetingLink: null,
|
|
28532
|
+
bookedMeetingProvider: null,
|
|
28533
|
+
isLoading: false,
|
|
28534
|
+
error: null,
|
|
28535
|
+
});
|
|
28536
|
+
function BookContactAppointmentCard({ action, onComplete, onDismiss, onCallEndpoint, accentColor, }) {
|
|
28537
|
+
// Parse config from input (from getInitialClientState)
|
|
28538
|
+
const initialState = action.input;
|
|
28539
|
+
const config = {
|
|
28540
|
+
bookingMode: initialState.bookingMode || DEFAULT_CONFIG.bookingMode,
|
|
28541
|
+
timeZone: initialState.timeZone || DEFAULT_CONFIG.timeZone,
|
|
28542
|
+
maxAppointmentsPerUser: initialState.maxAppointmentsPerUser || DEFAULT_CONFIG.maxAppointmentsPerUser,
|
|
28543
|
+
daysAhead: initialState.daysAhead || DEFAULT_CONFIG.daysAhead,
|
|
28544
|
+
subjectMode: initialState.subjectMode || DEFAULT_CONFIG.subjectMode,
|
|
28545
|
+
predefinedSubjects: initialState.predefinedSubjects || DEFAULT_CONFIG.predefinedSubjects,
|
|
28546
|
+
};
|
|
28547
|
+
// Local UI state
|
|
28548
|
+
const [state, setState] = React.useState(createInitialState);
|
|
28549
|
+
// Check if action is already done (from server state)
|
|
28550
|
+
const isDone = action.done;
|
|
28551
|
+
// Determine if dismiss should be available
|
|
28552
|
+
const isInteractiveStep = ['email', 'otp', 'contact_selection', 'options', 'view_appointments', 'slot_selection', 'confirmation'].includes(state.step);
|
|
28553
|
+
const showCloseButton = !isDone && isInteractiveStep && Boolean(onDismiss);
|
|
28554
|
+
// =========================================================================
|
|
28555
|
+
// Custom Hooks
|
|
28556
|
+
// =========================================================================
|
|
28557
|
+
// Core action lifecycle
|
|
28558
|
+
const { handleDismiss, finishAction } = useBookingAction({
|
|
28559
|
+
onComplete,
|
|
28560
|
+
onDismiss,
|
|
28561
|
+
toolCallId: action.toolCallId,
|
|
28562
|
+
});
|
|
28563
|
+
// Helper functions
|
|
28564
|
+
const { getContactName, getSlotDisplay } = useBookingHelpers({
|
|
28565
|
+
contacts: state.contacts,
|
|
28566
|
+
slots: state.slots,
|
|
28567
|
+
bookingMode: config.bookingMode,
|
|
28568
|
+
});
|
|
28569
|
+
// OTP operations
|
|
28570
|
+
const { handleSendOtp, handleVerifyOtp, handleResendOtp, } = useBookingOtp({
|
|
28571
|
+
onCallEndpoint,
|
|
28572
|
+
state: { email: state.email },
|
|
28573
|
+
setState,
|
|
28574
|
+
config: {
|
|
28575
|
+
bookingMode: config.bookingMode,
|
|
28576
|
+
timeZone: config.timeZone,
|
|
28577
|
+
},
|
|
28578
|
+
});
|
|
28579
|
+
// Navigation handlers
|
|
28580
|
+
const { handleSelectContact, handleBookNew, handleViewAppointments, handleBackToOptions, handleSelectSlot, handleBackToSlots, } = useBookingNavigation({
|
|
28581
|
+
onCallEndpoint,
|
|
28582
|
+
state,
|
|
28583
|
+
setState,
|
|
28584
|
+
config: {
|
|
28585
|
+
daysAhead: config.daysAhead,
|
|
28586
|
+
timeZone: config.timeZone,
|
|
28587
|
+
},
|
|
28588
|
+
});
|
|
28589
|
+
// Booking operations
|
|
28590
|
+
const { handleConfirmBooking, handleCancelAppointment, isCancelling, } = useBookingOperations({
|
|
28591
|
+
onCallEndpoint,
|
|
28592
|
+
state,
|
|
28593
|
+
setState,
|
|
28594
|
+
config: {
|
|
28595
|
+
timeZone: config.timeZone,
|
|
28596
|
+
},
|
|
28597
|
+
getContactName,
|
|
28598
|
+
finishAction,
|
|
28599
|
+
});
|
|
28600
|
+
// =========================================================================
|
|
28601
|
+
// Render
|
|
28602
|
+
// =========================================================================
|
|
28603
|
+
// If action is already done, show a simple completion indicator
|
|
28604
|
+
// The agent's response will convey what happened - no need to reconstruct terminal state
|
|
28605
|
+
if (isDone) {
|
|
28606
|
+
return jsxRuntime.jsx(CompletedPhase, {});
|
|
28607
|
+
}
|
|
28608
|
+
// Show loading when making API calls
|
|
28609
|
+
if (state.isLoading) {
|
|
28610
|
+
return jsxRuntime.jsx(LoadingPhase, {});
|
|
28611
|
+
}
|
|
28612
|
+
// Render based on current step
|
|
28613
|
+
switch (state.step) {
|
|
28614
|
+
case 'email':
|
|
28615
|
+
return (jsxRuntime.jsx(EmailPhase, { accentColor: accentColor, onDismiss: handleDismiss, showCloseButton: showCloseButton, onSubmit: handleSendOtp, isLoading: state.isLoading, error: state.error }));
|
|
28616
|
+
case 'otp':
|
|
28617
|
+
return (jsxRuntime.jsx(OtpPhase, { accentColor: accentColor, onDismiss: handleDismiss, showCloseButton: showCloseButton, email: state.email, onSubmit: handleVerifyOtp, onResend: handleResendOtp, isLoading: state.isLoading, error: state.error }));
|
|
28618
|
+
case 'contact_selection':
|
|
28619
|
+
return (jsxRuntime.jsx(ContactSelectionPhase, { accentColor: accentColor, onDismiss: handleDismiss, showCloseButton: showCloseButton, contacts: state.contacts, onSelect: handleSelectContact }));
|
|
28620
|
+
case 'options':
|
|
28621
|
+
return (jsxRuntime.jsx(OptionsPhase, { accentColor: accentColor, onDismiss: handleDismiss, showCloseButton: showCloseButton, appointments: state.appointments.filter(a => a.status !== 'cancelled'), maxAppointments: config.maxAppointmentsPerUser, onBookNew: handleBookNew, onViewAppointments: handleViewAppointments, onCancelAppointment: handleCancelAppointment, isCancelling: isCancelling }));
|
|
28622
|
+
case 'view_appointments':
|
|
28623
|
+
return (jsxRuntime.jsx(ViewAppointmentsPhase, { accentColor: accentColor, onDismiss: handleDismiss, showCloseButton: showCloseButton, appointments: state.appointments, onBack: handleBackToOptions, onCancelAppointment: handleCancelAppointment, isCancelling: isCancelling }));
|
|
28624
|
+
case 'slot_selection':
|
|
28625
|
+
return (jsxRuntime.jsx(SlotSelectionPhase, { accentColor: accentColor, onDismiss: handleDismiss, showCloseButton: showCloseButton, slots: state.slots, contactName: getContactName(state.selectedContactId), onSelect: handleSelectSlot, onBack: handleBackToOptions, isLoading: state.isLoading, error: state.error }));
|
|
28626
|
+
case 'confirmation':
|
|
28627
|
+
return (jsxRuntime.jsx(ConfirmationPhase, { accentColor: accentColor, onDismiss: handleDismiss, showCloseButton: showCloseButton, contactName: getContactName(state.selectedContactId), slot: state.selectedSlot, slotDisplay: getSlotDisplay(state.selectedSlot), subjectMode: config.subjectMode, predefinedSubjects: config.predefinedSubjects, onConfirm: handleConfirmBooking, onBack: handleBackToSlots, isLoading: state.isLoading, error: state.error }));
|
|
28628
|
+
case 'booked':
|
|
28629
|
+
return (jsxRuntime.jsx(BookedPhase, { subject: state.subject, contactName: getContactName(state.selectedContactId), meetingLink: state.bookedMeetingLink, meetingProvider: state.bookedMeetingProvider }));
|
|
28630
|
+
case 'pending_approval':
|
|
28631
|
+
return (jsxRuntime.jsx(PendingApprovalPhase, { subject: state.subject, contactName: getContactName(state.selectedContactId) }));
|
|
28632
|
+
case 'cancelled':
|
|
28633
|
+
return jsxRuntime.jsx(CancelledPhase, {});
|
|
28634
|
+
case 'error':
|
|
28635
|
+
return jsxRuntime.jsx(ErrorPhase, { error: state.error || 'An error occurred' });
|
|
28636
|
+
default:
|
|
28637
|
+
return jsxRuntime.jsx(ErrorPhase, { error: "Unknown state" });
|
|
28638
|
+
}
|
|
28639
|
+
}
|
|
28640
|
+
|
|
28641
|
+
function ExternalLinkIcon$1() {
|
|
28642
|
+
return (jsxRuntime.jsxs("svg", { width: "14", height: "14", 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" })] }));
|
|
28643
|
+
}
|
|
28644
|
+
function ImageCard({ item, accentColor, showOverlay = true, onClick, onLinkClick }) {
|
|
28645
|
+
const [imageError, setImageError] = React.useState(false);
|
|
28646
|
+
const style = accentColor ? { '--action-accent': accentColor } : undefined;
|
|
28647
|
+
const hasMetadata = item.title || item.description || item.link;
|
|
28648
|
+
const handleLinkClick = (e) => {
|
|
28649
|
+
e.stopPropagation();
|
|
28650
|
+
if (item.link) {
|
|
28651
|
+
onLinkClick?.(item.link);
|
|
28652
|
+
window.open(item.link, '_blank', 'noopener,noreferrer');
|
|
28653
|
+
}
|
|
28654
|
+
};
|
|
28655
|
+
const handleImageClick = () => {
|
|
28656
|
+
if (onClick) {
|
|
28657
|
+
onClick();
|
|
28658
|
+
}
|
|
28659
|
+
};
|
|
28660
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-image-card", style: style, onClick: handleImageClick, children: [jsxRuntime.jsx("div", { className: "ai-chat-image-card__media", children: !imageError ? (jsxRuntime.jsx("img", { src: item.url, alt: item.alt || item.title || 'Image', className: "ai-chat-image-card__image", onError: () => setImageError(true) })) : (jsxRuntime.jsx("div", { className: "ai-chat-image-card__image-error", children: jsxRuntime.jsx("span", { children: "Failed to load image" }) })) }), hasMetadata && showOverlay && (jsxRuntime.jsxs("div", { className: "ai-chat-image-card__content", children: [item.title && (jsxRuntime.jsx("h4", { className: "ai-chat-image-card__title", children: item.title })), item.description && (jsxRuntime.jsx("p", { className: "ai-chat-image-card__description", children: item.description })), item.link && (jsxRuntime.jsxs("button", { className: "ai-chat-image-card__link-btn", onClick: handleLinkClick, children: [item.linkText || 'View More', jsxRuntime.jsx(ExternalLinkIcon$1, {})] }))] }))] }));
|
|
28661
|
+
}
|
|
28662
|
+
|
|
28663
|
+
function ImageGallery({ items, columns = 2, accentColor }) {
|
|
28664
|
+
const [imageErrors, setImageErrors] = React.useState(new Set());
|
|
28665
|
+
const imageItems = items;
|
|
28666
|
+
if (imageItems.length === 0)
|
|
28667
|
+
return null;
|
|
28668
|
+
// Determine if first item should span full width (for odd counts > 1)
|
|
28669
|
+
const itemCount = imageItems.length;
|
|
28670
|
+
const isOddCount = itemCount > 1 && itemCount % 2 === 1;
|
|
28671
|
+
const style = {
|
|
28672
|
+
...(accentColor ? { '--action-accent': accentColor } : {}),
|
|
28673
|
+
'--gallery-item-count': itemCount,
|
|
28674
|
+
};
|
|
28675
|
+
const handleImageError = (index) => {
|
|
28676
|
+
setImageErrors(prev => new Set(prev).add(index));
|
|
28677
|
+
};
|
|
28678
|
+
return (jsxRuntime.jsx("div", { className: "ai-chat-image-gallery", style: style, "data-item-count": itemCount, children: jsxRuntime.jsx("div", { className: `ai-chat-image-gallery__grid ${isOddCount ? 'odd-count' : ''}`, "data-count": itemCount, children: imageItems.map((item, index) => (jsxRuntime.jsx("div", { className: `ai-chat-image-gallery__item ${isOddCount && index === 0 ? 'span-full' : ''}`, children: jsxRuntime.jsxs("div", { className: "ai-chat-image-gallery__item-media", children: [!imageErrors.has(index) ? (jsxRuntime.jsx("img", { src: item.url, alt: item.alt || item.title || `Image ${index + 1}`, onError: () => handleImageError(index) })) : (jsxRuntime.jsx("div", { className: "ai-chat-image-gallery__error", children: jsxRuntime.jsx("span", { children: "Failed to load" }) })), item.title && (jsxRuntime.jsx("div", { className: "ai-chat-image-gallery__item-overlay", children: jsxRuntime.jsx("span", { className: "ai-chat-image-gallery__item-title", children: item.title }) }))] }) }, index))) }) }));
|
|
28679
|
+
}
|
|
28680
|
+
|
|
28681
|
+
function ExternalLinkIcon() {
|
|
28682
|
+
return (jsxRuntime.jsxs("svg", { width: "14", height: "14", 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" })] }));
|
|
28683
|
+
}
|
|
28684
|
+
function truncate(text, maxLength) {
|
|
28685
|
+
if (text.length <= maxLength)
|
|
28686
|
+
return text;
|
|
28687
|
+
return text.slice(0, maxLength).trim() + '...';
|
|
28688
|
+
}
|
|
28689
|
+
function ProjectCard({ item, accentColor, onLinkClick, variant }) {
|
|
28690
|
+
const [imageError, setImageError] = React.useState(false);
|
|
28691
|
+
const style = accentColor ? { '--action-accent': accentColor } : undefined;
|
|
28692
|
+
const imageUrl = item.url;
|
|
28693
|
+
// Determine variant based on content if not specified
|
|
28694
|
+
const hasTextContent = item.title || item.description;
|
|
28695
|
+
const effectiveVariant = variant || (hasTextContent ? 'full' : 'image-only');
|
|
28696
|
+
const handleCardClick = () => {
|
|
28697
|
+
if (item.link) {
|
|
28698
|
+
onLinkClick?.(item.link);
|
|
28699
|
+
window.open(item.link, '_blank', 'noopener,noreferrer');
|
|
28700
|
+
}
|
|
28701
|
+
};
|
|
28702
|
+
// Image-only variant - just the image with optional hover effect
|
|
28703
|
+
if (effectiveVariant === 'image-only') {
|
|
28704
|
+
return (jsxRuntime.jsx("div", { className: `ai-chat-project-card ai-chat-project-card--image-only ${item.link ? 'clickable' : ''}`, style: style, onClick: item.link ? handleCardClick : undefined, role: item.link ? 'link' : undefined, tabIndex: item.link ? 0 : undefined, onKeyDown: (e) => e.key === 'Enter' && item.link && handleCardClick(), children: jsxRuntime.jsx("div", { className: "ai-chat-project-card__media", children: imageUrl && !imageError ? (jsxRuntime.jsx("img", { src: imageUrl, alt: item.alt || 'Image', onError: () => setImageError(true) })) : (jsxRuntime.jsx("div", { className: "ai-chat-project-card__placeholder", children: imageError ? 'Failed to load' : 'No image' })) }) }));
|
|
28705
|
+
}
|
|
28706
|
+
// Overlay variant - title overlaid on image
|
|
28707
|
+
if (effectiveVariant === 'overlay') {
|
|
28708
|
+
return (jsxRuntime.jsx("div", { className: `ai-chat-project-card ai-chat-project-card--overlay ${item.link ? 'clickable' : ''}`, style: style, onClick: item.link ? handleCardClick : undefined, role: item.link ? 'link' : undefined, tabIndex: item.link ? 0 : undefined, onKeyDown: (e) => e.key === 'Enter' && item.link && handleCardClick(), children: jsxRuntime.jsxs("div", { className: "ai-chat-project-card__media", children: [imageUrl && !imageError ? (jsxRuntime.jsx("img", { src: imageUrl, alt: item.alt || item.title || 'Image', onError: () => setImageError(true) })) : (jsxRuntime.jsx("div", { className: "ai-chat-project-card__placeholder", children: imageError ? 'Failed to load' : 'No image' })), item.title && (jsxRuntime.jsx("div", { className: "ai-chat-project-card__overlay", children: jsxRuntime.jsx("h4", { className: "ai-chat-project-card__overlay-title", children: truncate(item.title, 50) }) }))] }) }));
|
|
28709
|
+
}
|
|
28710
|
+
// Full variant - image with separate content section below
|
|
28711
|
+
return (jsxRuntime.jsxs("div", { className: `ai-chat-project-card ai-chat-project-card--full ${item.link ? 'clickable' : ''}`, style: style, onClick: item.link ? handleCardClick : undefined, role: item.link ? 'link' : undefined, tabIndex: item.link ? 0 : undefined, onKeyDown: (e) => e.key === 'Enter' && item.link && handleCardClick(), children: [jsxRuntime.jsx("div", { className: "ai-chat-project-card__media", children: imageUrl && !imageError ? (jsxRuntime.jsx("img", { src: imageUrl, alt: item.alt || item.title || 'Project image', onError: () => setImageError(true) })) : (jsxRuntime.jsx("div", { className: "ai-chat-project-card__placeholder", children: imageError ? 'Failed to load' : 'No image' })) }), hasTextContent && (jsxRuntime.jsxs("div", { className: "ai-chat-project-card__content", children: [item.title && (jsxRuntime.jsx("h4", { className: "ai-chat-project-card__title", children: truncate(item.title, 60) })), item.description && (jsxRuntime.jsx("p", { className: "ai-chat-project-card__description", children: truncate(item.description, 120) })), item.link && (jsxRuntime.jsxs("div", { className: "ai-chat-project-card__link", children: [jsxRuntime.jsx("span", { children: item.linkText || 'View' }), jsxRuntime.jsx(ExternalLinkIcon, {})] }))] }))] }));
|
|
28712
|
+
}
|
|
28713
|
+
|
|
28714
|
+
/**
|
|
28715
|
+
* URL validation utilities for images, cards, and links
|
|
28716
|
+
* Prevents 404 errors by validating URLs before display
|
|
28717
|
+
*/
|
|
28718
|
+
/**
|
|
28719
|
+
* Validate a single URL by attempting to load it
|
|
28720
|
+
*/
|
|
28721
|
+
/**
|
|
28722
|
+
* Validate an image URL by attempting to load it as an Image
|
|
28723
|
+
*/
|
|
28724
|
+
async function validateImageUrl(url, timeout = 5000) {
|
|
28725
|
+
if (!url || typeof url !== 'string') {
|
|
28726
|
+
return { isValid: false, error: 'Invalid URL format' };
|
|
28727
|
+
}
|
|
28728
|
+
try {
|
|
28729
|
+
new URL(url);
|
|
28730
|
+
}
|
|
28731
|
+
catch (e) {
|
|
28732
|
+
return { isValid: false, error: 'Invalid URL structure' };
|
|
28733
|
+
}
|
|
28734
|
+
return new Promise((resolve) => {
|
|
28735
|
+
const img = new Image();
|
|
28736
|
+
const timeoutId = setTimeout(() => {
|
|
28737
|
+
img.src = '';
|
|
28738
|
+
resolve({ isValid: false, error: 'Image load timeout' });
|
|
28739
|
+
}, timeout);
|
|
28740
|
+
img.onload = () => {
|
|
28741
|
+
clearTimeout(timeoutId);
|
|
28742
|
+
resolve({ isValid: true });
|
|
28481
28743
|
};
|
|
28482
|
-
|
|
28483
|
-
|
|
28484
|
-
|
|
28485
|
-
|
|
28486
|
-
|
|
28487
|
-
|
|
28488
|
-
|
|
28489
|
-
|
|
28744
|
+
img.onerror = () => {
|
|
28745
|
+
clearTimeout(timeoutId);
|
|
28746
|
+
resolve({ isValid: false, error: 'Failed to load image' });
|
|
28747
|
+
};
|
|
28748
|
+
img.src = url;
|
|
28749
|
+
});
|
|
28750
|
+
}
|
|
28751
|
+
/**
|
|
28752
|
+
* Validate multiple image URLs concurrently
|
|
28753
|
+
*/
|
|
28754
|
+
async function validateImageUrls(urls, timeout = 5000, maxConcurrent = 10) {
|
|
28755
|
+
const results = new Map();
|
|
28756
|
+
// Process in batches to limit concurrent requests
|
|
28757
|
+
for (let i = 0; i < urls.length; i += maxConcurrent) {
|
|
28758
|
+
const batch = urls.slice(i, i + maxConcurrent);
|
|
28759
|
+
const batchResults = await Promise.all(batch.map(async (url) => {
|
|
28760
|
+
const result = await validateImageUrl(url, timeout);
|
|
28761
|
+
return { url, result };
|
|
28762
|
+
}));
|
|
28763
|
+
batchResults.forEach(({ url, result }) => {
|
|
28764
|
+
results.set(url, result);
|
|
28765
|
+
});
|
|
28766
|
+
}
|
|
28767
|
+
return results;
|
|
28768
|
+
}
|
|
28769
|
+
/**
|
|
28770
|
+
* Validate image items and separate valid from invalid
|
|
28771
|
+
*/
|
|
28772
|
+
async function validateImageItems(items, timeout = 5000) {
|
|
28773
|
+
const urls = items.map(item => item.url).filter(Boolean);
|
|
28774
|
+
if (urls.length === 0) {
|
|
28775
|
+
return { validItems: [], invalidItems: items };
|
|
28776
|
+
}
|
|
28777
|
+
const validationResults = await validateImageUrls(urls, timeout);
|
|
28778
|
+
const validItems = [];
|
|
28779
|
+
const invalidItems = [];
|
|
28780
|
+
items.forEach(item => {
|
|
28781
|
+
if (!item.url) {
|
|
28782
|
+
invalidItems.push({
|
|
28783
|
+
...item,
|
|
28784
|
+
isValid: false,
|
|
28785
|
+
validationError: 'Missing URL',
|
|
28786
|
+
});
|
|
28787
|
+
return;
|
|
28788
|
+
}
|
|
28789
|
+
const result = validationResults.get(item.url);
|
|
28790
|
+
if (result?.isValid) {
|
|
28791
|
+
validItems.push({
|
|
28792
|
+
...item,
|
|
28793
|
+
isValid: true,
|
|
28794
|
+
});
|
|
28795
|
+
}
|
|
28796
|
+
else {
|
|
28797
|
+
invalidItems.push({
|
|
28798
|
+
...item,
|
|
28799
|
+
isValid: false,
|
|
28800
|
+
validationError: result?.error || 'Unknown error',
|
|
28801
|
+
});
|
|
28802
|
+
}
|
|
28803
|
+
});
|
|
28804
|
+
return { validItems, invalidItems };
|
|
28805
|
+
}
|
|
28806
|
+
/**
|
|
28807
|
+
* Preload and validate images before rendering
|
|
28808
|
+
* Returns items with validation status
|
|
28809
|
+
*/
|
|
28810
|
+
async function preloadAndValidateImages(items, timeout = 5000) {
|
|
28811
|
+
const { validItems, invalidItems } = await validateImageItems(items, timeout);
|
|
28812
|
+
if (invalidItems.length > 0) {
|
|
28813
|
+
console.warn('[URLValidator] Invalid image URLs detected:', invalidItems.map(i => ({
|
|
28814
|
+
url: i.url,
|
|
28815
|
+
error: i.validationError,
|
|
28816
|
+
})));
|
|
28817
|
+
}
|
|
28818
|
+
return [...validItems, ...invalidItems];
|
|
28819
|
+
}
|
|
28820
|
+
|
|
28821
|
+
function determineLayout(items, requestedLayout) {
|
|
28822
|
+
// Single item always uses single layout
|
|
28823
|
+
if (items.length === 1) {
|
|
28824
|
+
return 'single';
|
|
28825
|
+
}
|
|
28826
|
+
// For multiple items, use gallery (which now supports overlay titles)
|
|
28827
|
+
// Cards and gallery are now visually the same - 2-column grid with overlay titles
|
|
28828
|
+
return 'gallery';
|
|
28829
|
+
}
|
|
28830
|
+
function determineColumns(itemCount, maxColumns = 2) {
|
|
28831
|
+
if (itemCount === 1)
|
|
28832
|
+
return 1;
|
|
28833
|
+
return Math.min(2, maxColumns);
|
|
28834
|
+
}
|
|
28835
|
+
function StructuredImageDisplay({ action, onComplete, accentColor, maxColumns = 3 }) {
|
|
28836
|
+
const rawState = action.input;
|
|
28837
|
+
const hasCompletedRef = React.useRef(false);
|
|
28838
|
+
const [validatedItems, setValidatedItems] = React.useState([]);
|
|
28839
|
+
const [isValidating, setIsValidating] = React.useState(true);
|
|
28840
|
+
const [validationErrors, setValidationErrors] = React.useState([]);
|
|
28841
|
+
// Provide safe defaults if state is missing
|
|
28842
|
+
const state = {
|
|
28843
|
+
items: rawState?.items || [],
|
|
28844
|
+
layout: rawState?.layout || 'single',
|
|
28845
|
+
context: rawState?.context,
|
|
28846
|
+
columns: rawState?.columns,
|
|
28847
|
+
status: rawState?.status || 'displaying',
|
|
28848
|
+
error: rawState?.error,
|
|
28490
28849
|
};
|
|
28850
|
+
const isError = state.status === 'error';
|
|
28851
|
+
// Validate URLs before displaying
|
|
28852
|
+
React.useEffect(() => {
|
|
28853
|
+
if (state.items.length === 0) {
|
|
28854
|
+
setIsValidating(false);
|
|
28855
|
+
return;
|
|
28856
|
+
}
|
|
28857
|
+
let isMounted = true;
|
|
28858
|
+
const validateItems = async () => {
|
|
28859
|
+
try {
|
|
28860
|
+
const validated = await preloadAndValidateImages(state.items, 3000);
|
|
28861
|
+
if (!isMounted)
|
|
28862
|
+
return;
|
|
28863
|
+
const valid = validated.filter(item => item.isValid !== false);
|
|
28864
|
+
const invalid = validated.filter(item => item.isValid === false);
|
|
28865
|
+
setValidatedItems(valid);
|
|
28866
|
+
if (invalid.length > 0) {
|
|
28867
|
+
const errors = invalid.map(item => `${item.url}: ${item.validationError || 'Failed to load'}`);
|
|
28868
|
+
setValidationErrors(errors);
|
|
28869
|
+
console.warn('[StructuredImageDisplay] Filtered invalid URLs:', errors);
|
|
28870
|
+
}
|
|
28871
|
+
}
|
|
28872
|
+
catch (error) {
|
|
28873
|
+
console.error('[StructuredImageDisplay] Validation error:', error);
|
|
28874
|
+
// Fallback to unvalidated items on error
|
|
28875
|
+
if (isMounted) {
|
|
28876
|
+
setValidatedItems(state.items);
|
|
28877
|
+
}
|
|
28878
|
+
}
|
|
28879
|
+
finally {
|
|
28880
|
+
if (isMounted) {
|
|
28881
|
+
setIsValidating(false);
|
|
28882
|
+
}
|
|
28883
|
+
}
|
|
28884
|
+
};
|
|
28885
|
+
validateItems();
|
|
28886
|
+
return () => {
|
|
28887
|
+
isMounted = false;
|
|
28888
|
+
};
|
|
28889
|
+
}, [state.items]);
|
|
28890
|
+
// Auto-complete on mount so AI can continue generating text response
|
|
28891
|
+
React.useEffect(() => {
|
|
28892
|
+
if (!action.done && !hasCompletedRef.current && onComplete && state.items.length > 0) {
|
|
28893
|
+
hasCompletedRef.current = true;
|
|
28894
|
+
onComplete(action.toolCallId, { ...state, status: 'displaying' });
|
|
28895
|
+
}
|
|
28896
|
+
}, [action.done, action.toolCallId, onComplete, state]);
|
|
28897
|
+
const handleInteraction = () => {
|
|
28898
|
+
onComplete?.(action.toolCallId, { ...state, status: 'interacted' });
|
|
28899
|
+
};
|
|
28900
|
+
const handleLinkClick = (url) => {
|
|
28901
|
+
handleInteraction();
|
|
28902
|
+
};
|
|
28903
|
+
const style = accentColor ? { '--action-accent': accentColor } : undefined;
|
|
28904
|
+
// Error state
|
|
28905
|
+
if (isError) {
|
|
28906
|
+
return (jsxRuntime.jsx("div", { className: "ai-chat-structured-image ai-chat-structured-image--error", style: style, children: jsxRuntime.jsx("div", { className: "ai-chat-structured-image__error", children: state.error || 'Failed to load media' }) }));
|
|
28907
|
+
}
|
|
28908
|
+
// Loading state
|
|
28909
|
+
if (isValidating) {
|
|
28910
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-structured-image", style: style, children: [state.context && (jsxRuntime.jsx("p", { className: "ai-chat-structured-image__context", children: state.context })), jsxRuntime.jsx("div", { className: "ai-chat-structured-image__loading", children: "Validating images..." })] }));
|
|
28911
|
+
}
|
|
28912
|
+
// No valid items after validation
|
|
28913
|
+
if (validatedItems.length === 0) {
|
|
28914
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-structured-image", style: style, children: [state.context && (jsxRuntime.jsx("p", { className: "ai-chat-structured-image__context", children: state.context })), validationErrors.length > 0 && (jsxRuntime.jsx("div", { className: "ai-chat-structured-image__error", children: "All images failed to load. Please check the URLs." }))] }));
|
|
28915
|
+
}
|
|
28916
|
+
const layout = determineLayout(validatedItems, state.layout);
|
|
28917
|
+
const columns = state.columns || determineColumns(validatedItems.length, maxColumns);
|
|
28918
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-structured-image", style: style, children: [state.context && (jsxRuntime.jsx("p", { className: "ai-chat-structured-image__context", children: state.context })), validationErrors.length > 0 && (jsxRuntime.jsxs("div", { className: "ai-chat-structured-image__warning", children: [validationErrors.length, " image(s) could not be loaded and were filtered."] })), layout === 'single' && validatedItems[0] && (jsxRuntime.jsx(ImageCard, { item: validatedItems[0], accentColor: accentColor, onLinkClick: handleLinkClick })), layout === 'gallery' && (jsxRuntime.jsx(ImageGallery, { items: validatedItems, columns: columns, accentColor: accentColor, maxColumns: maxColumns })), layout === 'cards' && (jsxRuntime.jsx("div", { className: "ai-chat-structured-image__cards", style: { '--card-columns': validatedItems.length === 1 ? 1 : 2 }, children: validatedItems.map((item, index) => (jsxRuntime.jsx(ProjectCard, { item: item, accentColor: accentColor, onLinkClick: handleLinkClick, variant: "overlay" }, index))) })), layout === 'carousel' && (jsxRuntime.jsx(ImageCarousel, { items: validatedItems, accentColor: accentColor, onLinkClick: handleLinkClick }))] }));
|
|
28919
|
+
}
|
|
28920
|
+
function ChevronLeftIcon() {
|
|
28921
|
+
return (jsxRuntime.jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsxRuntime.jsx("polyline", { points: "15 18 9 12 15 6" }) }));
|
|
28922
|
+
}
|
|
28923
|
+
function ChevronRightIcon() {
|
|
28924
|
+
return (jsxRuntime.jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsxRuntime.jsx("polyline", { points: "9 18 15 12 9 6" }) }));
|
|
28925
|
+
}
|
|
28926
|
+
function ImageCarousel({ items, accentColor, onLinkClick }) {
|
|
28927
|
+
const [currentIndex, setCurrentIndex] = React.useState(0);
|
|
28928
|
+
const containerRef = React.useRef(null);
|
|
28929
|
+
const goToPrevious = () => {
|
|
28930
|
+
setCurrentIndex(prev => prev === 0 ? items.length - 1 : prev - 1);
|
|
28931
|
+
};
|
|
28932
|
+
const goToNext = () => {
|
|
28933
|
+
setCurrentIndex(prev => prev === items.length - 1 ? 0 : prev + 1);
|
|
28934
|
+
};
|
|
28935
|
+
const style = accentColor ? { '--action-accent': accentColor } : undefined;
|
|
28936
|
+
return (jsxRuntime.jsxs("div", { className: "ai-chat-image-carousel", style: style, ref: containerRef, children: [jsxRuntime.jsx("div", { className: "ai-chat-image-carousel__track", style: { transform: `translateX(-${currentIndex * 100}%)` }, children: items.map((item, index) => (jsxRuntime.jsx("div", { className: "ai-chat-image-carousel__slide", children: jsxRuntime.jsx(ProjectCard, { item: item, accentColor: accentColor, onLinkClick: onLinkClick }) }, index))) }), items.length > 1 && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("button", { className: "ai-chat-image-carousel__nav prev", onClick: goToPrevious, "aria-label": "Previous", children: jsxRuntime.jsx(ChevronLeftIcon, {}) }), jsxRuntime.jsx("button", { className: "ai-chat-image-carousel__nav next", onClick: goToNext, "aria-label": "Next", children: jsxRuntime.jsx(ChevronRightIcon, {}) }), jsxRuntime.jsx("div", { className: "ai-chat-image-carousel__dots", children: items.map((_, index) => (jsxRuntime.jsx("button", { className: `ai-chat-image-carousel__dot ${index === currentIndex ? 'active' : ''}`, onClick: () => setCurrentIndex(index), "aria-label": `Go to slide ${index + 1}` }, index))) })] }))] }));
|
|
28937
|
+
}
|
|
28938
|
+
|
|
28939
|
+
const pendingResolvers = new Map();
|
|
28940
|
+
const resumeCallbacks = new Map();
|
|
28941
|
+
const frontendActionHandlers = {};
|
|
28942
|
+
const actionRenderers = {};
|
|
28943
|
+
function getFrontendActionHandler(implementation) {
|
|
28944
|
+
return frontendActionHandlers[implementation];
|
|
28945
|
+
}
|
|
28946
|
+
function getActionRenderer(implementation) {
|
|
28947
|
+
return actionRenderers[implementation];
|
|
28948
|
+
}
|
|
28949
|
+
function waitForActionState(toolCallId) {
|
|
28950
|
+
return new Promise((resolve) => {
|
|
28951
|
+
pendingResolvers.set(toolCallId, resolve);
|
|
28952
|
+
});
|
|
28953
|
+
}
|
|
28954
|
+
function resolveActionState(toolCallId, state) {
|
|
28955
|
+
const resolver = pendingResolvers.get(toolCallId);
|
|
28956
|
+
if (resolver) {
|
|
28957
|
+
pendingResolvers.delete(toolCallId);
|
|
28958
|
+
resolver(state);
|
|
28959
|
+
return;
|
|
28960
|
+
}
|
|
28961
|
+
const resumeCallback = resumeCallbacks.get(toolCallId);
|
|
28962
|
+
if (resumeCallback) {
|
|
28963
|
+
resumeCallback(state).catch((error) => {
|
|
28964
|
+
console.error("[Action] Failed to resume action:", error);
|
|
28965
|
+
});
|
|
28966
|
+
}
|
|
28967
|
+
}
|
|
28968
|
+
function registerActionResumeCallback(toolCallId, callback) {
|
|
28969
|
+
resumeCallbacks.set(toolCallId, callback);
|
|
28970
|
+
}
|
|
28971
|
+
function unregisterActionResumeCallback(toolCallId) {
|
|
28972
|
+
resumeCallbacks.delete(toolCallId);
|
|
28491
28973
|
}
|
|
28492
28974
|
|
|
28493
28975
|
/**
|
|
@@ -28495,9 +28977,15 @@ function registerMicrosoftCalendarAction() {
|
|
|
28495
28977
|
* Called by initializeActionHandlers to prevent tree-shaking.
|
|
28496
28978
|
*/
|
|
28497
28979
|
function registerLinkPreviewAction() {
|
|
28498
|
-
// Handler - auto-completes immediately
|
|
28980
|
+
// Handler - auto-completes immediately, returns body for agent
|
|
28499
28981
|
frontendActionHandlers["link-preview"] = async (_input, state, _context) => {
|
|
28500
|
-
|
|
28982
|
+
const links = state?.links || [];
|
|
28983
|
+
// Return body directly - sent to backend via continueAgentAction
|
|
28984
|
+
return {
|
|
28985
|
+
status: "displayed",
|
|
28986
|
+
linkCount: links.length,
|
|
28987
|
+
message: `Displayed ${links.length} link preview${links.length > 1 ? 's' : ''}.`,
|
|
28988
|
+
};
|
|
28501
28989
|
};
|
|
28502
28990
|
// Renderer - displays the link preview card
|
|
28503
28991
|
actionRenderers["link-preview"] = (message, accentColor) => {
|
|
@@ -28507,16 +28995,15 @@ function registerLinkPreviewAction() {
|
|
|
28507
28995
|
const handleComplete = (toolCallId, newState) => {
|
|
28508
28996
|
resolveActionState(toolCallId, newState);
|
|
28509
28997
|
};
|
|
28510
|
-
// Check if action
|
|
28511
|
-
const
|
|
28512
|
-
const status =
|
|
28513
|
-
const isDone = action.done || status === "displaying" || status === "clicked";
|
|
28998
|
+
// Check if action input indicates it's already complete (displaying or clicked)
|
|
28999
|
+
const input = action.input;
|
|
29000
|
+
const status = input?.status;
|
|
29001
|
+
const isDone = action.done || status === "displaying" || status === "displayed" || status === "clicked";
|
|
28514
29002
|
return (jsxRuntime.jsx(LinkPreviewCard, { action: {
|
|
28515
29003
|
implementation: action.implementation,
|
|
28516
29004
|
toolCallId: action.toolCallId,
|
|
28517
29005
|
actionId: action.actionId,
|
|
28518
29006
|
input: action.input,
|
|
28519
|
-
state: action.state,
|
|
28520
29007
|
done: isDone,
|
|
28521
29008
|
}, onComplete: handleComplete, accentColor: accentColor }));
|
|
28522
29009
|
};
|
|
@@ -28527,9 +29014,15 @@ function registerLinkPreviewAction() {
|
|
|
28527
29014
|
* Called by initializeActionHandlers to prevent tree-shaking.
|
|
28528
29015
|
*/
|
|
28529
29016
|
function registerVideoPlayerAction() {
|
|
28530
|
-
// Handler - auto-completes immediately
|
|
29017
|
+
// Handler - auto-completes immediately, returns body for agent
|
|
28531
29018
|
frontendActionHandlers["video-player"] = async (_input, state, _context) => {
|
|
28532
|
-
|
|
29019
|
+
const videos = state?.videos || [];
|
|
29020
|
+
// Return body directly - sent to backend via continueAgentAction
|
|
29021
|
+
return {
|
|
29022
|
+
status: "displayed",
|
|
29023
|
+
videoCount: videos.length,
|
|
29024
|
+
message: `Displayed ${videos.length} video${videos.length !== 1 ? 's' : ''}.`,
|
|
29025
|
+
};
|
|
28533
29026
|
};
|
|
28534
29027
|
// Renderer - displays the embedded video player card
|
|
28535
29028
|
actionRenderers["video-player"] = (message, accentColor) => {
|
|
@@ -28539,16 +29032,15 @@ function registerVideoPlayerAction() {
|
|
|
28539
29032
|
const handleComplete = (toolCallId, newState) => {
|
|
28540
29033
|
resolveActionState(toolCallId, newState);
|
|
28541
29034
|
};
|
|
28542
|
-
// Check if action
|
|
28543
|
-
const
|
|
28544
|
-
const status =
|
|
28545
|
-
const isDone = action.done || status === "displaying" || status === "played";
|
|
29035
|
+
// Check if action input indicates it's already complete (displaying or played)
|
|
29036
|
+
const input = action.input;
|
|
29037
|
+
const status = input?.status;
|
|
29038
|
+
const isDone = action.done || status === "displaying" || status === "displayed" || status === "played";
|
|
28546
29039
|
return (jsxRuntime.jsx(VideoPlayerCard, { action: {
|
|
28547
29040
|
implementation: action.implementation,
|
|
28548
29041
|
toolCallId: action.toolCallId,
|
|
28549
29042
|
actionId: action.actionId,
|
|
28550
29043
|
input: action.input,
|
|
28551
|
-
state: action.state,
|
|
28552
29044
|
done: isDone,
|
|
28553
29045
|
}, onComplete: handleComplete, accentColor: accentColor }));
|
|
28554
29046
|
};
|
|
@@ -28559,9 +29051,15 @@ function registerVideoPlayerAction() {
|
|
|
28559
29051
|
* Called by initializeActionHandlers to prevent tree-shaking.
|
|
28560
29052
|
*/
|
|
28561
29053
|
function registerLocationCardAction() {
|
|
28562
|
-
// Handler - auto-completes immediately
|
|
29054
|
+
// Handler - auto-completes immediately, returns body for agent
|
|
28563
29055
|
frontendActionHandlers["location-card"] = async (_input, state, _context) => {
|
|
28564
|
-
|
|
29056
|
+
const locations = state?.locations || [];
|
|
29057
|
+
// Return body directly - sent to backend via continueAgentAction
|
|
29058
|
+
return {
|
|
29059
|
+
status: "displayed",
|
|
29060
|
+
locationCount: locations.length,
|
|
29061
|
+
message: `Displayed ${locations.length} location${locations.length !== 1 ? 's' : ''}.`,
|
|
29062
|
+
};
|
|
28565
29063
|
};
|
|
28566
29064
|
// Renderer - displays the location card
|
|
28567
29065
|
actionRenderers["location-card"] = (message, accentColor, variant) => {
|
|
@@ -28571,25 +29069,30 @@ function registerLocationCardAction() {
|
|
|
28571
29069
|
const handleComplete = (toolCallId, newState) => {
|
|
28572
29070
|
resolveActionState(toolCallId, newState);
|
|
28573
29071
|
};
|
|
28574
|
-
// Check if action
|
|
28575
|
-
const
|
|
28576
|
-
const status =
|
|
28577
|
-
const isDone = action.done || status === "displaying" || status === "directions_opened";
|
|
29072
|
+
// Check if action input indicates it's already complete
|
|
29073
|
+
const input = action.input;
|
|
29074
|
+
const status = input?.status;
|
|
29075
|
+
const isDone = action.done || status === "displaying" || status === "displayed" || status === "directions_opened";
|
|
28578
29076
|
return (jsxRuntime.jsx(LocationCard, { action: {
|
|
28579
29077
|
implementation: action.implementation,
|
|
28580
29078
|
toolCallId: action.toolCallId,
|
|
28581
29079
|
actionId: action.actionId,
|
|
28582
29080
|
input: action.input,
|
|
28583
|
-
state: action.state,
|
|
28584
29081
|
done: isDone,
|
|
28585
29082
|
}, onComplete: handleComplete, accentColor: accentColor, maxColumns: variant === 'fullpage' ? 3 : 1 }));
|
|
28586
29083
|
};
|
|
28587
29084
|
}
|
|
28588
29085
|
|
|
28589
29086
|
function registerContactCardAction() {
|
|
28590
|
-
// Handler - auto-completes immediately
|
|
29087
|
+
// Handler - auto-completes immediately, returns body for agent
|
|
28591
29088
|
frontendActionHandlers['contact-card'] = async (_input, state, _context) => {
|
|
28592
|
-
|
|
29089
|
+
const contacts = state?.contacts || [];
|
|
29090
|
+
// Return body directly - sent to backend via continueAgentAction
|
|
29091
|
+
return {
|
|
29092
|
+
status: 'displayed',
|
|
29093
|
+
contactCount: contacts.length,
|
|
29094
|
+
message: `Displayed ${contacts.length} contact${contacts.length !== 1 ? 's' : ''}.`,
|
|
29095
|
+
};
|
|
28593
29096
|
};
|
|
28594
29097
|
// Renderer - displays the contact card
|
|
28595
29098
|
actionRenderers['contact-card'] = (message, accentColor, variant) => {
|
|
@@ -28599,16 +29102,15 @@ function registerContactCardAction() {
|
|
|
28599
29102
|
const handleComplete = (toolCallId, newState) => {
|
|
28600
29103
|
resolveActionState(toolCallId, newState);
|
|
28601
29104
|
};
|
|
28602
|
-
// Check if action
|
|
28603
|
-
const
|
|
28604
|
-
const status =
|
|
28605
|
-
const isDone = action.done || status === 'displaying' || status === 'contacted';
|
|
29105
|
+
// Check if action input indicates it's already complete
|
|
29106
|
+
const input = action.input;
|
|
29107
|
+
const status = input?.status;
|
|
29108
|
+
const isDone = action.done || status === 'displaying' || status === 'displayed' || status === 'contacted';
|
|
28606
29109
|
return (jsxRuntime.jsx(ContactCard, { action: {
|
|
28607
29110
|
implementation: action.implementation,
|
|
28608
29111
|
toolCallId: action.toolCallId,
|
|
28609
29112
|
actionId: action.actionId,
|
|
28610
29113
|
input: action.input,
|
|
28611
|
-
state: action.state,
|
|
28612
29114
|
done: isDone,
|
|
28613
29115
|
}, onComplete: handleComplete, accentColor: accentColor, maxColumns: variant === 'fullpage' ? 3 : 1 }));
|
|
28614
29116
|
};
|
|
@@ -28619,55 +29121,133 @@ function registerQueryContactDirectoryAction() {
|
|
|
28619
29121
|
frontendActionHandlers['query-contact-directory'] = async (_input, state, _context) => {
|
|
28620
29122
|
return { ...state, status: 'displaying' };
|
|
28621
29123
|
};
|
|
28622
|
-
// Renderer - displays the contact card with search results
|
|
28623
|
-
actionRenderers['query-contact-directory'] = (message, accentColor, variant) => {
|
|
29124
|
+
// Renderer - displays the contact card with search results
|
|
29125
|
+
actionRenderers['query-contact-directory'] = (message, accentColor, variant) => {
|
|
29126
|
+
const action = message.action;
|
|
29127
|
+
if (!action)
|
|
29128
|
+
return null;
|
|
29129
|
+
// Handle completion - triggers agent to continue with text response
|
|
29130
|
+
const handleComplete = (toolCallId, newState) => {
|
|
29131
|
+
resolveActionState(toolCallId, newState);
|
|
29132
|
+
};
|
|
29133
|
+
// Check if action input indicates it's already complete
|
|
29134
|
+
const input = action.input;
|
|
29135
|
+
const status = input?.status;
|
|
29136
|
+
const isDone = action.done || status === 'displaying' || status === 'contacted';
|
|
29137
|
+
return (jsxRuntime.jsx(ContactCard, { action: {
|
|
29138
|
+
implementation: action.implementation,
|
|
29139
|
+
toolCallId: action.toolCallId,
|
|
29140
|
+
actionId: action.actionId,
|
|
29141
|
+
input: action.input,
|
|
29142
|
+
done: isDone,
|
|
29143
|
+
}, onComplete: handleComplete, accentColor: accentColor, maxColumns: variant === 'fullpage' ? 3 : 1 }));
|
|
29144
|
+
};
|
|
29145
|
+
}
|
|
29146
|
+
|
|
29147
|
+
function registerDisplayFormAction() {
|
|
29148
|
+
// Handler - handles form submission and state updates
|
|
29149
|
+
frontendActionHandlers['display-form'] = async (_input, _state, context) => {
|
|
29150
|
+
return waitForActionState(context.toolCallId);
|
|
29151
|
+
};
|
|
29152
|
+
// Renderer - displays the form card
|
|
29153
|
+
// Rendering is based only on action.input and action.done
|
|
29154
|
+
// When done=true, FormCard shows a simple completion indicator
|
|
29155
|
+
actionRenderers['display-form'] = (message, accentColor, _variant, onActionDismiss) => {
|
|
29156
|
+
const action = message.action;
|
|
29157
|
+
if (!action)
|
|
29158
|
+
return null;
|
|
29159
|
+
const handleComplete = (toolCallId, newState) => {
|
|
29160
|
+
resolveActionState(toolCallId, newState);
|
|
29161
|
+
};
|
|
29162
|
+
const handleDismiss = onActionDismiss
|
|
29163
|
+
? (toolCallId) => onActionDismiss(toolCallId)
|
|
29164
|
+
: undefined;
|
|
29165
|
+
return (jsxRuntime.jsx(FormCard, { action: {
|
|
29166
|
+
implementation: action.implementation,
|
|
29167
|
+
toolCallId: action.toolCallId,
|
|
29168
|
+
actionId: action.actionId,
|
|
29169
|
+
input: action.input,
|
|
29170
|
+
done: action.done ?? false,
|
|
29171
|
+
}, onComplete: handleComplete, onDismiss: handleDismiss, accentColor: accentColor }));
|
|
29172
|
+
};
|
|
29173
|
+
}
|
|
29174
|
+
|
|
29175
|
+
/**
|
|
29176
|
+
* Book Contact Appointment Handler
|
|
29177
|
+
* Frontend action handler that waits for action completion
|
|
29178
|
+
*/
|
|
29179
|
+
function registerBookContactAppointmentHandler() {
|
|
29180
|
+
frontendActionHandlers["book-contact-appointment"] = async (_input, _state, context) => {
|
|
29181
|
+
return waitForActionState(context.toolCallId);
|
|
29182
|
+
};
|
|
29183
|
+
}
|
|
29184
|
+
|
|
29185
|
+
/**
|
|
29186
|
+
* Register book-contact-appointment action handler and renderer.
|
|
29187
|
+
* Called by initializeActionHandlers to prevent tree-shaking.
|
|
29188
|
+
*/
|
|
29189
|
+
function registerBookContactAppointmentAction() {
|
|
29190
|
+
// Register the handler
|
|
29191
|
+
registerBookContactAppointmentHandler();
|
|
29192
|
+
// Register the renderer
|
|
29193
|
+
actionRenderers['book-contact-appointment'] = (message, accentColor, _variant, _onActionDismiss, onCallEndpoint) => {
|
|
28624
29194
|
const action = message.action;
|
|
28625
29195
|
if (!action)
|
|
28626
29196
|
return null;
|
|
28627
|
-
// Handle completion - triggers agent to continue with text response
|
|
28628
29197
|
const handleComplete = (toolCallId, newState) => {
|
|
28629
29198
|
resolveActionState(toolCallId, newState);
|
|
28630
29199
|
};
|
|
28631
|
-
//
|
|
28632
|
-
|
|
28633
|
-
const
|
|
28634
|
-
|
|
28635
|
-
|
|
29200
|
+
// Create action-specific endpoint callback
|
|
29201
|
+
// Include toolCallId for done flag enforcement (server rejects calls for completed actions)
|
|
29202
|
+
const handleCallEndpoint = onCallEndpoint
|
|
29203
|
+
? async (endpoint, input, options) => {
|
|
29204
|
+
return onCallEndpoint(action.actionId, endpoint, input, { ...options, toolCallId: action.toolCallId });
|
|
29205
|
+
}
|
|
29206
|
+
: undefined;
|
|
29207
|
+
// Create dismiss handler
|
|
29208
|
+
const handleDismiss = _onActionDismiss
|
|
29209
|
+
? (toolCallId) => _onActionDismiss(toolCallId)
|
|
29210
|
+
: undefined;
|
|
29211
|
+
return (jsxRuntime.jsx(BookContactAppointmentCard, { action: {
|
|
28636
29212
|
implementation: action.implementation,
|
|
28637
29213
|
toolCallId: action.toolCallId,
|
|
28638
29214
|
actionId: action.actionId,
|
|
28639
29215
|
input: action.input,
|
|
28640
|
-
|
|
28641
|
-
|
|
28642
|
-
}, onComplete: handleComplete, accentColor: accentColor, maxColumns: variant === 'fullpage' ? 3 : 1 }));
|
|
29216
|
+
done: action.done ?? false,
|
|
29217
|
+
}, onComplete: handleComplete, onDismiss: handleDismiss, onCallEndpoint: handleCallEndpoint, accentColor: accentColor }));
|
|
28643
29218
|
};
|
|
28644
29219
|
}
|
|
28645
29220
|
|
|
28646
|
-
function
|
|
28647
|
-
// Handler -
|
|
28648
|
-
frontendActionHandlers['
|
|
28649
|
-
|
|
29221
|
+
function registerStructuredImageAction() {
|
|
29222
|
+
// Handler - auto-completes immediately, returns body for agent
|
|
29223
|
+
frontendActionHandlers['structured-image'] = async (_input, state, _context) => {
|
|
29224
|
+
const images = state?.images || [];
|
|
29225
|
+
// Return body directly - sent to backend via continueAgentAction
|
|
29226
|
+
return {
|
|
29227
|
+
status: 'displayed',
|
|
29228
|
+
imageCount: images.length,
|
|
29229
|
+
message: `Displayed ${images.length} image${images.length !== 1 ? 's' : ''}.`,
|
|
29230
|
+
};
|
|
28650
29231
|
};
|
|
28651
|
-
// Renderer - displays the
|
|
28652
|
-
actionRenderers['
|
|
29232
|
+
// Renderer - displays the image content
|
|
29233
|
+
actionRenderers['structured-image'] = (message, accentColor, variant, onActionDismiss) => {
|
|
28653
29234
|
const action = message.action;
|
|
28654
29235
|
if (!action)
|
|
28655
29236
|
return null;
|
|
28656
29237
|
const handleComplete = (toolCallId, newState) => {
|
|
28657
29238
|
resolveActionState(toolCallId, newState);
|
|
28658
29239
|
};
|
|
28659
|
-
// Check if action
|
|
28660
|
-
const
|
|
28661
|
-
const status =
|
|
28662
|
-
const isDone = action.done || status === '
|
|
28663
|
-
return (jsxRuntime.jsx(
|
|
29240
|
+
// Check if action input indicates it's already complete
|
|
29241
|
+
const input = action.input;
|
|
29242
|
+
const status = input?.status;
|
|
29243
|
+
const isDone = action.done || status === 'displaying' || status === 'displayed' || status === 'interacted';
|
|
29244
|
+
return (jsxRuntime.jsx(StructuredImageDisplay, { action: {
|
|
28664
29245
|
implementation: action.implementation,
|
|
28665
29246
|
toolCallId: action.toolCallId,
|
|
28666
29247
|
actionId: action.actionId,
|
|
28667
29248
|
input: action.input,
|
|
28668
|
-
state: action.state,
|
|
28669
29249
|
done: isDone,
|
|
28670
|
-
}, onComplete: handleComplete, accentColor: accentColor }));
|
|
29250
|
+
}, onComplete: handleComplete, onDismiss: onActionDismiss ? () => onActionDismiss(action.toolCallId) : undefined, accentColor: accentColor, maxColumns: variant === 'fullpage' ? 3 : 2 }));
|
|
28671
29251
|
};
|
|
28672
29252
|
}
|
|
28673
29253
|
|
|
@@ -28683,14 +29263,14 @@ function initializeActionHandlers() {
|
|
|
28683
29263
|
return;
|
|
28684
29264
|
initialized = true;
|
|
28685
29265
|
// Explicitly call each registration function to prevent tree-shaking
|
|
28686
|
-
registerGoogleCalendarAction();
|
|
28687
|
-
registerMicrosoftCalendarAction();
|
|
28688
29266
|
registerLinkPreviewAction();
|
|
28689
29267
|
registerVideoPlayerAction();
|
|
28690
29268
|
registerLocationCardAction();
|
|
28691
29269
|
registerQueryContactDirectoryAction();
|
|
28692
29270
|
registerContactCardAction();
|
|
28693
29271
|
registerDisplayFormAction();
|
|
29272
|
+
registerBookContactAppointmentAction();
|
|
29273
|
+
registerStructuredImageAction();
|
|
28694
29274
|
}
|
|
28695
29275
|
|
|
28696
29276
|
/**
|
|
@@ -28953,8 +29533,49 @@ function hydrateToolNames(messages) {
|
|
|
28953
29533
|
};
|
|
28954
29534
|
});
|
|
28955
29535
|
}
|
|
29536
|
+
/**
|
|
29537
|
+
* Clean up messages that were interrupted during streaming.
|
|
29538
|
+
* - Marks streaming messages as complete
|
|
29539
|
+
* - Removes empty assistant messages
|
|
29540
|
+
* - Marks incomplete tool calls as done
|
|
29541
|
+
*/
|
|
29542
|
+
function cleanupInterruptedMessages(messages) {
|
|
29543
|
+
return messages
|
|
29544
|
+
.map((msg) => {
|
|
29545
|
+
// Mark streaming messages as complete
|
|
29546
|
+
if (msg.isStreaming) {
|
|
29547
|
+
msg = { ...msg, isStreaming: false };
|
|
29548
|
+
}
|
|
29549
|
+
// Mark incomplete tool calls as done (they were interrupted)
|
|
29550
|
+
if (msg.message.role === 'tool' && msg.action && !msg.action.done) {
|
|
29551
|
+
msg = {
|
|
29552
|
+
...msg,
|
|
29553
|
+
action: {
|
|
29554
|
+
...msg.action,
|
|
29555
|
+
done: true,
|
|
29556
|
+
// Mark as interrupted so UI can show appropriate state
|
|
29557
|
+
input: {
|
|
29558
|
+
...msg.action.input,
|
|
29559
|
+
_interrupted: true,
|
|
29560
|
+
},
|
|
29561
|
+
},
|
|
29562
|
+
};
|
|
29563
|
+
}
|
|
29564
|
+
return msg;
|
|
29565
|
+
})
|
|
29566
|
+
// Remove empty assistant messages (streaming was interrupted before content)
|
|
29567
|
+
.filter((msg) => {
|
|
29568
|
+
if (msg.message.role === 'assistant') {
|
|
29569
|
+
const content = msg.message.content;
|
|
29570
|
+
return content && typeof content === 'string' && content.trim().length > 0;
|
|
29571
|
+
}
|
|
29572
|
+
return true;
|
|
29573
|
+
});
|
|
29574
|
+
}
|
|
28956
29575
|
function hydrateMessages(messages) {
|
|
28957
|
-
|
|
29576
|
+
const visibleMessages = messages.filter((message) => !message.action?.hidden);
|
|
29577
|
+
const cleanedMessages = cleanupInterruptedMessages(visibleMessages);
|
|
29578
|
+
return hydrateToolNames(cleanedMessages);
|
|
28958
29579
|
}
|
|
28959
29580
|
|
|
28960
29581
|
function deriveErrorInfo(error) {
|
|
@@ -29024,6 +29645,75 @@ function deriveErrorInfo(error) {
|
|
|
29024
29645
|
return { message: 'Something went wrong. Please try again.' };
|
|
29025
29646
|
}
|
|
29026
29647
|
|
|
29648
|
+
/**
|
|
29649
|
+
* Stream Buffer
|
|
29650
|
+
* Smooths out streaming text rendering by buffering words and draining them
|
|
29651
|
+
* with requestAnimationFrame for a fluid typing effect.
|
|
29652
|
+
*/
|
|
29653
|
+
function createStreamBuffer() {
|
|
29654
|
+
return {
|
|
29655
|
+
pendingWords: [],
|
|
29656
|
+
displayedContent: '',
|
|
29657
|
+
streamEnded: false,
|
|
29658
|
+
rafHandle: null,
|
|
29659
|
+
};
|
|
29660
|
+
}
|
|
29661
|
+
function appendToBuffer(buffer, content) {
|
|
29662
|
+
// Split by whitespace while preserving the whitespace characters
|
|
29663
|
+
const words = content.split(/(\s+)/);
|
|
29664
|
+
buffer.pendingWords.push(...words.filter(w => w.length > 0));
|
|
29665
|
+
}
|
|
29666
|
+
function startBufferDrain(buffer, onContentUpdate) {
|
|
29667
|
+
if (buffer.rafHandle !== null) {
|
|
29668
|
+
return; // Already draining
|
|
29669
|
+
}
|
|
29670
|
+
const drainLoop = () => {
|
|
29671
|
+
if (buffer.pendingWords.length === 0) {
|
|
29672
|
+
if (buffer.streamEnded) {
|
|
29673
|
+
buffer.rafHandle = null;
|
|
29674
|
+
return;
|
|
29675
|
+
}
|
|
29676
|
+
// No words to render, wait for more
|
|
29677
|
+
buffer.rafHandle = requestAnimationFrame(drainLoop);
|
|
29678
|
+
return;
|
|
29679
|
+
}
|
|
29680
|
+
// Adaptive: render more words if buffer is filling up
|
|
29681
|
+
const wordsToRender = buffer.pendingWords.length > 20 ? 3 : 1;
|
|
29682
|
+
const chunk = buffer.pendingWords.splice(0, wordsToRender).join('');
|
|
29683
|
+
buffer.displayedContent += chunk;
|
|
29684
|
+
onContentUpdate(buffer.displayedContent);
|
|
29685
|
+
buffer.rafHandle = requestAnimationFrame(drainLoop);
|
|
29686
|
+
};
|
|
29687
|
+
buffer.rafHandle = requestAnimationFrame(drainLoop);
|
|
29688
|
+
}
|
|
29689
|
+
function flushBuffer(buffer) {
|
|
29690
|
+
buffer.streamEnded = true;
|
|
29691
|
+
if (buffer.rafHandle !== null) {
|
|
29692
|
+
cancelAnimationFrame(buffer.rafHandle);
|
|
29693
|
+
buffer.rafHandle = null;
|
|
29694
|
+
}
|
|
29695
|
+
const remaining = buffer.pendingWords.join('');
|
|
29696
|
+
buffer.displayedContent += remaining;
|
|
29697
|
+
buffer.pendingWords = [];
|
|
29698
|
+
return buffer.displayedContent;
|
|
29699
|
+
}
|
|
29700
|
+
function cancelBuffer(buffer) {
|
|
29701
|
+
if (buffer.rafHandle !== null) {
|
|
29702
|
+
cancelAnimationFrame(buffer.rafHandle);
|
|
29703
|
+
buffer.rafHandle = null;
|
|
29704
|
+
}
|
|
29705
|
+
buffer.streamEnded = true;
|
|
29706
|
+
}
|
|
29707
|
+
function resetBuffer(buffer) {
|
|
29708
|
+
if (buffer.rafHandle !== null) {
|
|
29709
|
+
cancelAnimationFrame(buffer.rafHandle);
|
|
29710
|
+
buffer.rafHandle = null;
|
|
29711
|
+
}
|
|
29712
|
+
buffer.pendingWords = [];
|
|
29713
|
+
buffer.displayedContent = '';
|
|
29714
|
+
buffer.streamEnded = false;
|
|
29715
|
+
}
|
|
29716
|
+
|
|
29027
29717
|
function createStreamState() {
|
|
29028
29718
|
return {
|
|
29029
29719
|
currentContent: "",
|
|
@@ -29033,6 +29723,7 @@ function createStreamState() {
|
|
|
29033
29723
|
sources: [],
|
|
29034
29724
|
toolCallToActionId: {},
|
|
29035
29725
|
requestId: generateMessageId(),
|
|
29726
|
+
buffer: createStreamBuffer(),
|
|
29036
29727
|
};
|
|
29037
29728
|
}
|
|
29038
29729
|
function upsertMessage(setState, message, isTyping) {
|
|
@@ -29095,6 +29786,8 @@ function finalizeToolMessage(streamState, setState, toolCallId, toolName) {
|
|
|
29095
29786
|
},
|
|
29096
29787
|
isStreaming: false,
|
|
29097
29788
|
toolExecuting: existingName,
|
|
29789
|
+
// Mark action as done when tool message is finalized
|
|
29790
|
+
action: entry.action ? { ...entry.action, done: true } : undefined,
|
|
29098
29791
|
};
|
|
29099
29792
|
});
|
|
29100
29793
|
return { ...prev, messages, isTyping: false, isLoading: false };
|
|
@@ -29103,26 +29796,34 @@ function finalizeToolMessage(streamState, setState, toolCallId, toolName) {
|
|
|
29103
29796
|
}
|
|
29104
29797
|
|
|
29105
29798
|
function handleContentEvent(event, streamState, onMessageUpdate, setState) {
|
|
29799
|
+
// Track full content for finalization
|
|
29106
29800
|
streamState.currentContent += event.content;
|
|
29107
|
-
|
|
29108
|
-
|
|
29109
|
-
|
|
29110
|
-
|
|
29111
|
-
|
|
29112
|
-
|
|
29113
|
-
|
|
29114
|
-
|
|
29115
|
-
|
|
29116
|
-
|
|
29117
|
-
|
|
29118
|
-
|
|
29119
|
-
|
|
29801
|
+
// Add to buffer for smooth rendering
|
|
29802
|
+
appendToBuffer(streamState.buffer, event.content);
|
|
29803
|
+
// Start drain loop if not already running
|
|
29804
|
+
startBufferDrain(streamState.buffer, (displayedContent) => {
|
|
29805
|
+
const assistantMessage = {
|
|
29806
|
+
id: streamState.currentMessageId,
|
|
29807
|
+
message: { role: "assistant", content: displayedContent },
|
|
29808
|
+
timestamp: new Date().toISOString(),
|
|
29809
|
+
sources: streamState.sources,
|
|
29810
|
+
isStreaming: true,
|
|
29811
|
+
};
|
|
29812
|
+
streamState.newMessageIds.add(assistantMessage.id);
|
|
29813
|
+
onMessageUpdate(assistantMessage);
|
|
29814
|
+
const hasContent = displayedContent.trim().length > 0;
|
|
29815
|
+
const isToolExecuting = streamState.activeToolCallCount > 0;
|
|
29816
|
+
const isTyping = (!hasContent && assistantMessage.isStreaming) || isToolExecuting;
|
|
29817
|
+
upsertMessage(setState, assistantMessage, isTyping);
|
|
29818
|
+
});
|
|
29120
29819
|
}
|
|
29121
29820
|
function handleToolStartEvent(event, streamState, onMessageUpdate, setState) {
|
|
29122
|
-
|
|
29821
|
+
// Flush buffer before tool starts
|
|
29822
|
+
const flushedContent = flushBuffer(streamState.buffer);
|
|
29823
|
+
if (flushedContent.trim()) {
|
|
29123
29824
|
const finalAssistant = {
|
|
29124
29825
|
id: streamState.currentMessageId,
|
|
29125
|
-
message: { role: "assistant", content:
|
|
29826
|
+
message: { role: "assistant", content: flushedContent },
|
|
29126
29827
|
timestamp: new Date().toISOString(),
|
|
29127
29828
|
sources: streamState.sources,
|
|
29128
29829
|
isStreaming: false,
|
|
@@ -29133,6 +29834,8 @@ function handleToolStartEvent(event, streamState, onMessageUpdate, setState) {
|
|
|
29133
29834
|
streamState.currentContent = "";
|
|
29134
29835
|
streamState.currentMessageId = generateMessageId();
|
|
29135
29836
|
}
|
|
29837
|
+
// Reset buffer for post-tool content
|
|
29838
|
+
resetBuffer(streamState.buffer);
|
|
29136
29839
|
const toolMessageId = event.tool_call_id;
|
|
29137
29840
|
streamState.activeToolCallCount += 1;
|
|
29138
29841
|
streamState.newMessageIds.add(toolMessageId);
|
|
@@ -29150,43 +29853,22 @@ function handleToolEndEvent(event, streamState, _onMessageUpdate, setState) {
|
|
|
29150
29853
|
streamState.activeToolCallCount = Math.max(0, streamState.activeToolCallCount - 1);
|
|
29151
29854
|
setState(prev => {
|
|
29152
29855
|
const messages = prev.messages.map((msg) => {
|
|
29153
|
-
|
|
29154
|
-
if (!matchesToolCall) {
|
|
29856
|
+
if (msg.message.role !== "tool" || msg.message.tool_call_id !== event.tool_call_id) {
|
|
29155
29857
|
return msg;
|
|
29156
29858
|
}
|
|
29157
|
-
const
|
|
29158
|
-
|
|
29159
|
-
|
|
29160
|
-
|
|
29161
|
-
|
|
29162
|
-
|
|
29163
|
-
|
|
29164
|
-
input: (event.input || {}),
|
|
29165
|
-
state: (event.state || {}),
|
|
29166
|
-
done: event.done,
|
|
29167
|
-
};
|
|
29168
|
-
}
|
|
29169
|
-
else if (action) {
|
|
29170
|
-
action = {
|
|
29171
|
-
...action,
|
|
29172
|
-
input: event.input ? event.input : action.input,
|
|
29173
|
-
state: event.state ? event.state : action.state,
|
|
29174
|
-
done: event.done,
|
|
29175
|
-
};
|
|
29176
|
-
}
|
|
29177
|
-
const updatedMsg = {
|
|
29859
|
+
const toolName = msg.message.name || event.tool_name;
|
|
29860
|
+
// For actions: just mark as done, preserve existing input (display data is immutable)
|
|
29861
|
+
// For non-action tools: update normally
|
|
29862
|
+
const action = msg.action
|
|
29863
|
+
? { ...msg.action, done: event.done }
|
|
29864
|
+
: undefined;
|
|
29865
|
+
return {
|
|
29178
29866
|
...msg,
|
|
29179
|
-
message: {
|
|
29180
|
-
role: "tool",
|
|
29181
|
-
content: event.state ? JSON.stringify(event.state) : (typeof msg.message.content === "string" ? msg.message.content : ""),
|
|
29182
|
-
tool_call_id: event.tool_call_id,
|
|
29183
|
-
name: existingName,
|
|
29184
|
-
},
|
|
29867
|
+
message: { ...msg.message, name: toolName },
|
|
29185
29868
|
isStreaming: false,
|
|
29186
|
-
toolExecuting:
|
|
29869
|
+
toolExecuting: toolName,
|
|
29187
29870
|
action,
|
|
29188
29871
|
};
|
|
29189
|
-
return updatedMsg;
|
|
29190
29872
|
});
|
|
29191
29873
|
return { ...prev, messages, isTyping: true, isLoading: false };
|
|
29192
29874
|
});
|
|
@@ -29219,11 +29901,6 @@ function handleToolErrorEvent(event, streamState, _onMessageUpdate, setState) {
|
|
|
29219
29901
|
return { ...prev, messages, isTyping: true, isLoading: false };
|
|
29220
29902
|
});
|
|
29221
29903
|
}
|
|
29222
|
-
function handleDoneEvent(event, streamState, _onMessageUpdate, setState) {
|
|
29223
|
-
streamState.sources = event.sources;
|
|
29224
|
-
streamState.toolCallToActionId = event.tool_call_to_action_id;
|
|
29225
|
-
finalizeStreamMessages(setState, streamState.newMessageIds, event.sources, event.tool_call_to_action_id, event.suggestions);
|
|
29226
|
-
}
|
|
29227
29904
|
function handleHaltEvent(event, _streamState, onMessageUpdate, setState) {
|
|
29228
29905
|
const toolNames = event.tool_calls.map(call => call.name).join(", ");
|
|
29229
29906
|
const notice = toolNames
|
|
@@ -29238,7 +29915,8 @@ function handleHaltEvent(event, _streamState, onMessageUpdate, setState) {
|
|
|
29238
29915
|
};
|
|
29239
29916
|
onMessageUpdate(haltMessage);
|
|
29240
29917
|
upsertMessage(setState, haltMessage, false);
|
|
29241
|
-
|
|
29918
|
+
// Stop loading/typing state when halted
|
|
29919
|
+
setState(prev => ({ ...prev, isLoading: false, isTyping: false, error: "Awaiting external tool handling." }));
|
|
29242
29920
|
}
|
|
29243
29921
|
function handleErrorEvent(_event, _streamState, onMessageUpdate, setState) {
|
|
29244
29922
|
const errorMessage = {
|
|
@@ -29250,6 +29928,8 @@ function handleErrorEvent(_event, _streamState, onMessageUpdate, setState) {
|
|
|
29250
29928
|
};
|
|
29251
29929
|
onMessageUpdate(errorMessage);
|
|
29252
29930
|
upsertMessage(setState, errorMessage, false);
|
|
29931
|
+
// Stop loading/typing state on error
|
|
29932
|
+
setState(prev => ({ ...prev, isLoading: false, isTyping: false }));
|
|
29253
29933
|
}
|
|
29254
29934
|
const eventHandlers = {
|
|
29255
29935
|
content: handleContentEvent,
|
|
@@ -29277,7 +29957,37 @@ function handleStreamEvent(event, streamState, onMessageUpdate, setState) {
|
|
|
29277
29957
|
console.warn('[Chat] Unknown event type:', event.type);
|
|
29278
29958
|
}
|
|
29279
29959
|
}
|
|
29960
|
+
function handleDoneEvent(event, streamState, onMessageUpdate, setState) {
|
|
29961
|
+
// Flush any remaining buffered content
|
|
29962
|
+
const flushedContent = flushBuffer(streamState.buffer);
|
|
29963
|
+
// Update the final message with complete content if there was content
|
|
29964
|
+
if (flushedContent.trim()) {
|
|
29965
|
+
const finalMessage = {
|
|
29966
|
+
id: streamState.currentMessageId,
|
|
29967
|
+
message: { role: "assistant", content: flushedContent },
|
|
29968
|
+
timestamp: new Date().toISOString(),
|
|
29969
|
+
sources: event.sources,
|
|
29970
|
+
isStreaming: false,
|
|
29971
|
+
};
|
|
29972
|
+
streamState.newMessageIds.add(finalMessage.id);
|
|
29973
|
+
onMessageUpdate(finalMessage);
|
|
29974
|
+
upsertMessage(setState, finalMessage, false);
|
|
29975
|
+
}
|
|
29976
|
+
streamState.sources = event.sources;
|
|
29977
|
+
streamState.toolCallToActionId = event.tool_call_to_action_id;
|
|
29978
|
+
finalizeStreamMessages(setState, streamState.newMessageIds, event.sources, event.tool_call_to_action_id, event.suggestions);
|
|
29979
|
+
}
|
|
29280
29980
|
|
|
29981
|
+
/**
|
|
29982
|
+
* Handle the action loop for halting actions.
|
|
29983
|
+
*
|
|
29984
|
+
* Flow:
|
|
29985
|
+
* 1. Display the action in UI
|
|
29986
|
+
* 2. Call the frontend handler (which waits for user interaction or returns immediately for display actions)
|
|
29987
|
+
* 3. Handler returns body when complete
|
|
29988
|
+
* 4. Send body to backend via continueAgentAction
|
|
29989
|
+
* 5. Process any subsequent events (may include new action_request for chained actions)
|
|
29990
|
+
*/
|
|
29281
29991
|
async function handleActionLoop(client, initialEvent, streamState, onMessageUpdate, setState, widgetId, conversationId, getMessages) {
|
|
29282
29992
|
let pendingEvent = initialEvent;
|
|
29283
29993
|
while (pendingEvent) {
|
|
@@ -29303,7 +30013,6 @@ async function handleActionLoop(client, initialEvent, streamState, onMessageUpda
|
|
|
29303
30013
|
toolCallId: pendingEvent.tool_call_id,
|
|
29304
30014
|
actionId: pendingEvent.action_id,
|
|
29305
30015
|
input: pendingEvent.input,
|
|
29306
|
-
state: pendingEvent.state,
|
|
29307
30016
|
done: pendingEvent.done ?? false,
|
|
29308
30017
|
},
|
|
29309
30018
|
};
|
|
@@ -29328,9 +30037,13 @@ async function handleActionLoop(client, initialEvent, streamState, onMessageUpda
|
|
|
29328
30037
|
upsertMessage(setState, errorMessage, false);
|
|
29329
30038
|
return;
|
|
29330
30039
|
}
|
|
29331
|
-
|
|
30040
|
+
// Handler returns body when complete (for display actions this is immediate,
|
|
30041
|
+
// for interactive actions this waits for user completion)
|
|
30042
|
+
// Note: For halting actions, input contains the transformed data from getInitialClientState
|
|
30043
|
+
let body;
|
|
29332
30044
|
try {
|
|
29333
|
-
|
|
30045
|
+
body = await handler(pendingEvent.input, pendingEvent.input, // Input now contains the display data for halting actions
|
|
30046
|
+
{
|
|
29334
30047
|
widgetId,
|
|
29335
30048
|
conversationId,
|
|
29336
30049
|
toolCallId: pendingEvent.tool_call_id,
|
|
@@ -29352,19 +30065,16 @@ async function handleActionLoop(client, initialEvent, streamState, onMessageUpda
|
|
|
29352
30065
|
return;
|
|
29353
30066
|
}
|
|
29354
30067
|
pendingEvent = null;
|
|
29355
|
-
|
|
29356
|
-
|
|
29357
|
-
action: toolMessage.action ? { ...toolMessage.action, state: nextState } : toolMessage.action,
|
|
29358
|
-
};
|
|
29359
|
-
upsertMessage(setState, updatedToolMessage, true);
|
|
30068
|
+
// Send body to backend and continue agent
|
|
30069
|
+
// No need to update message - input is immutable and already contains display data
|
|
29360
30070
|
let streamEnded = false;
|
|
29361
|
-
for await (const event of client.
|
|
30071
|
+
for await (const event of client.continueAgentAction(conversationId, resumeToolCallId, body)) {
|
|
29362
30072
|
if (event.type === "action_request") {
|
|
30073
|
+
// Chained action - continue the loop
|
|
29363
30074
|
pendingEvent = event;
|
|
29364
30075
|
break;
|
|
29365
30076
|
}
|
|
29366
30077
|
if (event.type === "done") {
|
|
29367
|
-
// Finalize tool message and stream messages
|
|
29368
30078
|
finalizeToolMessage(streamState, setState, resumeToolCallId, toolName);
|
|
29369
30079
|
streamState.sources = event.sources;
|
|
29370
30080
|
streamState.toolCallToActionId = event.tool_call_to_action_id;
|
|
@@ -29394,57 +30104,118 @@ async function handleActionLoop(client, initialEvent, streamState, onMessageUpda
|
|
|
29394
30104
|
}
|
|
29395
30105
|
}
|
|
29396
30106
|
}
|
|
29397
|
-
|
|
30107
|
+
/**
|
|
30108
|
+
* Setup resume callbacks for incomplete actions after page reload.
|
|
30109
|
+
*
|
|
30110
|
+
* When a page reloads with an incomplete action, this sets up callbacks
|
|
30111
|
+
* so when the user completes the action, we can continue the agent flow.
|
|
30112
|
+
*/
|
|
30113
|
+
function setupActionResumeCallbacks(messages, client, conversationId, widgetId, setState, onMessageUpdate, createStreamState, registerCallback) {
|
|
29398
30114
|
// Find all incomplete actions and register resume callbacks
|
|
29399
30115
|
for (const message of messages) {
|
|
29400
|
-
if (message.action && !message.action.done) {
|
|
29401
|
-
const
|
|
29402
|
-
const
|
|
29403
|
-
registerCallback(
|
|
29404
|
-
// When user
|
|
30116
|
+
if (message.action && !message.action.done && !message.action.hidden) {
|
|
30117
|
+
const initialToolCallId = message.action.toolCallId;
|
|
30118
|
+
const initialToolName = message.message.name || message.toolExecuting || "tool";
|
|
30119
|
+
registerCallback(initialToolCallId, async (body) => {
|
|
30120
|
+
// When user completes the action after reload, continue the stream
|
|
29405
30121
|
try {
|
|
29406
|
-
// Update the action message with the new state and check completion
|
|
29407
30122
|
setState(prev => ({
|
|
29408
30123
|
...prev,
|
|
29409
|
-
messages: prev.messages.map(m => {
|
|
29410
|
-
if (m.action?.toolCallId !== toolCallId) {
|
|
29411
|
-
return m;
|
|
29412
|
-
}
|
|
29413
|
-
if (!m.action) {
|
|
29414
|
-
return m;
|
|
29415
|
-
}
|
|
29416
|
-
return { ...m, action: { ...m.action, state: newState } };
|
|
29417
|
-
}),
|
|
29418
30124
|
isTyping: true,
|
|
29419
30125
|
}));
|
|
29420
30126
|
const streamState = createStreamState();
|
|
29421
|
-
|
|
29422
|
-
|
|
29423
|
-
|
|
29424
|
-
|
|
29425
|
-
|
|
29426
|
-
|
|
29427
|
-
|
|
29428
|
-
|
|
30127
|
+
let currentToolCallId = initialToolCallId;
|
|
30128
|
+
let currentToolName = initialToolName;
|
|
30129
|
+
let currentBody = body;
|
|
30130
|
+
// Loop to handle chained action_requests
|
|
30131
|
+
while (true) {
|
|
30132
|
+
let nextActionRequest = null;
|
|
30133
|
+
// Continue the agent with the body
|
|
30134
|
+
for await (const event of client.continueAgentAction(conversationId, currentToolCallId, currentBody)) {
|
|
30135
|
+
if (event.type === "action_request") {
|
|
30136
|
+
// Chained action - need to handle the new action request
|
|
30137
|
+
nextActionRequest = event;
|
|
30138
|
+
break;
|
|
30139
|
+
}
|
|
30140
|
+
if (event.type === "done") {
|
|
30141
|
+
finalizeToolMessage(streamState, setState, currentToolCallId, currentToolName);
|
|
30142
|
+
streamState.sources = event.sources;
|
|
30143
|
+
streamState.toolCallToActionId = event.tool_call_to_action_id;
|
|
30144
|
+
finalizeStreamMessages(setState, streamState.newMessageIds, event.sources, event.tool_call_to_action_id, event.suggestions);
|
|
30145
|
+
setState(prev => ({ ...prev, isTyping: false }));
|
|
30146
|
+
return;
|
|
30147
|
+
}
|
|
30148
|
+
if (event.type === "error") {
|
|
30149
|
+
const errorMessage = {
|
|
30150
|
+
id: generateMessageId(),
|
|
30151
|
+
message: {
|
|
30152
|
+
role: "assistant",
|
|
30153
|
+
content: "Sorry, an error occurred. Please try again later.",
|
|
30154
|
+
},
|
|
30155
|
+
timestamp: new Date().toISOString(),
|
|
30156
|
+
sources: [],
|
|
30157
|
+
isError: true,
|
|
30158
|
+
};
|
|
30159
|
+
upsertMessage(setState, errorMessage, false);
|
|
30160
|
+
setState(prev => ({ ...prev, isTyping: false }));
|
|
30161
|
+
return;
|
|
30162
|
+
}
|
|
30163
|
+
handleStreamEvent(event, streamState, onMessageUpdate, setState);
|
|
30164
|
+
}
|
|
30165
|
+
// If no action_request, stream ended normally
|
|
30166
|
+
if (!nextActionRequest) {
|
|
30167
|
+
setState(prev => ({ ...prev, isTyping: false }));
|
|
30168
|
+
return;
|
|
29429
30169
|
}
|
|
29430
|
-
|
|
29431
|
-
|
|
29432
|
-
|
|
29433
|
-
|
|
29434
|
-
|
|
29435
|
-
|
|
29436
|
-
|
|
29437
|
-
|
|
29438
|
-
|
|
29439
|
-
|
|
29440
|
-
|
|
29441
|
-
|
|
30170
|
+
// Update UI with new action for the chained action
|
|
30171
|
+
const toolMessage = {
|
|
30172
|
+
id: nextActionRequest.tool_call_id,
|
|
30173
|
+
sources: [],
|
|
30174
|
+
message: {
|
|
30175
|
+
role: "tool",
|
|
30176
|
+
content: "",
|
|
30177
|
+
tool_call_id: nextActionRequest.tool_call_id,
|
|
30178
|
+
name: currentToolName,
|
|
30179
|
+
},
|
|
30180
|
+
timestamp: new Date().toISOString(),
|
|
30181
|
+
isStreaming: true,
|
|
30182
|
+
toolExecuting: currentToolName,
|
|
30183
|
+
action: {
|
|
30184
|
+
implementation: nextActionRequest.implementation,
|
|
30185
|
+
toolCallId: nextActionRequest.tool_call_id,
|
|
30186
|
+
actionId: nextActionRequest.action_id,
|
|
30187
|
+
input: nextActionRequest.input,
|
|
30188
|
+
done: nextActionRequest.done ?? false,
|
|
30189
|
+
},
|
|
30190
|
+
};
|
|
30191
|
+
upsertMessage(setState, toolMessage, true);
|
|
30192
|
+
// Wait for user to complete the chained action
|
|
30193
|
+
const handler = getFrontendActionHandler(nextActionRequest.implementation);
|
|
30194
|
+
if (!handler) {
|
|
30195
|
+
console.error("[Action Resume] No handler for implementation:", nextActionRequest.implementation);
|
|
30196
|
+
setState(prev => ({ ...prev, isTyping: false }));
|
|
30197
|
+
return;
|
|
30198
|
+
}
|
|
30199
|
+
let handlerResult;
|
|
30200
|
+
try {
|
|
30201
|
+
handlerResult = await handler(nextActionRequest.input, nextActionRequest.input, // Input contains display data for halting actions
|
|
30202
|
+
{
|
|
30203
|
+
widgetId,
|
|
30204
|
+
conversationId,
|
|
30205
|
+
toolCallId: nextActionRequest.tool_call_id,
|
|
30206
|
+
actionId: nextActionRequest.action_id,
|
|
30207
|
+
implementation: nextActionRequest.implementation,
|
|
30208
|
+
});
|
|
30209
|
+
}
|
|
30210
|
+
catch (handlerError) {
|
|
30211
|
+
console.error("[Action Resume] Handler failed:", handlerError);
|
|
29442
30212
|
setState(prev => ({ ...prev, isTyping: false }));
|
|
29443
30213
|
return;
|
|
29444
30214
|
}
|
|
29445
|
-
|
|
30215
|
+
// Continue loop with new body
|
|
30216
|
+
currentToolCallId = nextActionRequest.tool_call_id;
|
|
30217
|
+
currentBody = handlerResult;
|
|
29446
30218
|
}
|
|
29447
|
-
setState(prev => ({ ...prev, isTyping: false }));
|
|
29448
30219
|
}
|
|
29449
30220
|
catch (error) {
|
|
29450
30221
|
console.error("[Action Resume] Failed to continue stream:", error);
|
|
@@ -29523,7 +30294,7 @@ function useChat(options) {
|
|
|
29523
30294
|
}));
|
|
29524
30295
|
// Setup resume callbacks for incomplete actions
|
|
29525
30296
|
if (conversationId) {
|
|
29526
|
-
setupActionResumeCallbacks(hydratedMessages, apiClient.current, conversationId, setState, onMessage ?? (() => { }), createStreamState, registerActionResumeCallback);
|
|
30297
|
+
setupActionResumeCallbacks(hydratedMessages, apiClient.current, conversationId, widgetId, setState, onMessage ?? (() => { }), createStreamState, registerActionResumeCallback);
|
|
29527
30298
|
}
|
|
29528
30299
|
}
|
|
29529
30300
|
catch (error) {
|
|
@@ -29546,7 +30317,7 @@ function useChat(options) {
|
|
|
29546
30317
|
}
|
|
29547
30318
|
});
|
|
29548
30319
|
};
|
|
29549
|
-
}, [widgetId, apiUrl, onError]);
|
|
30320
|
+
}, [widgetId, apiUrl, onError, skipInitialization]);
|
|
29550
30321
|
// Save conversation when messages change
|
|
29551
30322
|
React.useEffect(() => {
|
|
29552
30323
|
const persistConversation = state.config?.settings.persistConversation ?? true;
|
|
@@ -29557,6 +30328,21 @@ function useChat(options) {
|
|
|
29557
30328
|
saveConversation(widgetId, state.conversationId, state.messages);
|
|
29558
30329
|
}
|
|
29559
30330
|
}, [widgetId, state.messages, state.conversationId, state.config?.settings.persistConversation]);
|
|
30331
|
+
// Save conversation on page unload to preserve streaming state
|
|
30332
|
+
React.useEffect(() => {
|
|
30333
|
+
const persistConversation = state.config?.settings.persistConversation ?? true;
|
|
30334
|
+
if (!persistConversation || !isStorageAvailable())
|
|
30335
|
+
return;
|
|
30336
|
+
const handleBeforeUnload = () => {
|
|
30337
|
+
const currentState = stateRef.current;
|
|
30338
|
+
if (currentState.messages.length > 0 && currentState.conversationId) {
|
|
30339
|
+
// Force save current state before page closes
|
|
30340
|
+
saveConversation(widgetId, currentState.conversationId, currentState.messages);
|
|
30341
|
+
}
|
|
30342
|
+
};
|
|
30343
|
+
window.addEventListener('beforeunload', handleBeforeUnload);
|
|
30344
|
+
return () => window.removeEventListener('beforeunload', handleBeforeUnload);
|
|
30345
|
+
}, [widgetId, state.config?.settings.persistConversation]);
|
|
29560
30346
|
const sendMessage = React.useCallback(async (content, files) => {
|
|
29561
30347
|
const trimmedContent = content.trim();
|
|
29562
30348
|
const hasFiles = !!files && files.length > 0;
|
|
@@ -29640,12 +30426,14 @@ function useChat(options) {
|
|
|
29640
30426
|
stateRef.current.conversationId !== streamConversationId ||
|
|
29641
30427
|
currentRequestIdRef.current !== streamRequestId) {
|
|
29642
30428
|
console.log('[Widget] Stream aborted, conversation changed, or superseded by new request');
|
|
30429
|
+
cancelBuffer(streamState.buffer);
|
|
29643
30430
|
break;
|
|
29644
30431
|
}
|
|
29645
30432
|
if (event.type === "action_request") {
|
|
29646
30433
|
if (currentAbortController?.signal.aborted ||
|
|
29647
30434
|
stateRef.current.conversationId !== streamConversationId ||
|
|
29648
30435
|
currentRequestIdRef.current !== streamRequestId) {
|
|
30436
|
+
cancelBuffer(streamState.buffer);
|
|
29649
30437
|
break;
|
|
29650
30438
|
}
|
|
29651
30439
|
await handleActionLoop(apiClient.current, event, streamState, (message) => {
|
|
@@ -29671,26 +30459,8 @@ function useChat(options) {
|
|
|
29671
30459
|
if (lastStreamedMessage) {
|
|
29672
30460
|
onMessage?.(lastStreamedMessage);
|
|
29673
30461
|
}
|
|
29674
|
-
|
|
29675
|
-
|
|
29676
|
-
if (enableFollowUps) {
|
|
29677
|
-
apiClient.current.generateFollowUps(stateRef.current.messages, actionIds)
|
|
29678
|
-
.then(suggestions => {
|
|
29679
|
-
if (suggestions.length > 0) {
|
|
29680
|
-
setState(prev => {
|
|
29681
|
-
const messages = [...prev.messages];
|
|
29682
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
29683
|
-
if (messages[i].message.role === 'assistant' && !messages[i].isError) {
|
|
29684
|
-
messages[i] = { ...messages[i], suggestions };
|
|
29685
|
-
break;
|
|
29686
|
-
}
|
|
29687
|
-
}
|
|
29688
|
-
return { ...prev, messages };
|
|
29689
|
-
});
|
|
29690
|
-
}
|
|
29691
|
-
})
|
|
29692
|
-
.catch(err => console.warn('[Widget] Follow-up generation failed:', err));
|
|
29693
|
-
}
|
|
30462
|
+
// Follow-up suggestions are now generated server-side in parallel and included in the done event
|
|
30463
|
+
// They are automatically attached to the last assistant message by finalizeStreamMessages()
|
|
29694
30464
|
}
|
|
29695
30465
|
catch (error) {
|
|
29696
30466
|
console.error("[Widget] sendMessage error:", error);
|
|
@@ -29752,6 +30522,51 @@ function useChat(options) {
|
|
|
29752
30522
|
onError?.(err);
|
|
29753
30523
|
}
|
|
29754
30524
|
}, [state.conversationId, onError]);
|
|
30525
|
+
const dismissAction = React.useCallback(async (toolCallId) => {
|
|
30526
|
+
if (!toolCallId)
|
|
30527
|
+
return;
|
|
30528
|
+
const dismissedAt = new Date().toISOString();
|
|
30529
|
+
setState(prev => ({
|
|
30530
|
+
...prev,
|
|
30531
|
+
messages: prev.messages.map((message) => {
|
|
30532
|
+
if (message.action?.toolCallId !== toolCallId) {
|
|
30533
|
+
return message;
|
|
30534
|
+
}
|
|
30535
|
+
if (!message.action) {
|
|
30536
|
+
return message;
|
|
30537
|
+
}
|
|
30538
|
+
return {
|
|
30539
|
+
...message,
|
|
30540
|
+
action: {
|
|
30541
|
+
...message.action,
|
|
30542
|
+
hidden: true,
|
|
30543
|
+
dismissedAt,
|
|
30544
|
+
dismissedBy: "user",
|
|
30545
|
+
done: true,
|
|
30546
|
+
},
|
|
30547
|
+
};
|
|
30548
|
+
}),
|
|
30549
|
+
isTyping: true,
|
|
30550
|
+
isLoading: false,
|
|
30551
|
+
}));
|
|
30552
|
+
unregisterActionResumeCallback(toolCallId);
|
|
30553
|
+
const conversationId = stateRef.current.conversationId;
|
|
30554
|
+
if (!conversationId) {
|
|
30555
|
+
setState(prev => ({ ...prev, isTyping: false }));
|
|
30556
|
+
return;
|
|
30557
|
+
}
|
|
30558
|
+
try {
|
|
30559
|
+
const streamState = createStreamState();
|
|
30560
|
+
for await (const event of apiClient.current.dismissAgentMessageStream(conversationId, toolCallId)) {
|
|
30561
|
+
handleStreamEvent(event, streamState, onMessage ?? (() => { }), setState);
|
|
30562
|
+
}
|
|
30563
|
+
setState(prev => ({ ...prev, isTyping: false, isLoading: false }));
|
|
30564
|
+
}
|
|
30565
|
+
catch (error) {
|
|
30566
|
+
console.error("[Widget] dismissAction error:", error);
|
|
30567
|
+
setState(prev => ({ ...prev, isTyping: false, isLoading: false }));
|
|
30568
|
+
}
|
|
30569
|
+
}, [onMessage]);
|
|
29755
30570
|
const loadConversations = React.useCallback(() => {
|
|
29756
30571
|
const persistConversation = state.config?.settings.persistConversation ?? true;
|
|
29757
30572
|
if (!persistConversation || !isStorageAvailable()) {
|
|
@@ -29802,7 +30617,7 @@ function useChat(options) {
|
|
|
29802
30617
|
messages: hydratedMessages,
|
|
29803
30618
|
isLoading: false,
|
|
29804
30619
|
}));
|
|
29805
|
-
setupActionResumeCallbacks(hydratedMessages, apiClient.current, conversation.id, setState, onMessage ?? (() => { }), createStreamState, registerActionResumeCallback);
|
|
30620
|
+
setupActionResumeCallbacks(hydratedMessages, apiClient.current, conversation.id, widgetId, setState, onMessage ?? (() => { }), createStreamState, registerActionResumeCallback);
|
|
29806
30621
|
if (persistConversation && isStorageAvailable()) {
|
|
29807
30622
|
saveConversation(widgetId, conversation.id, hydratedMessages);
|
|
29808
30623
|
}
|
|
@@ -29852,6 +30667,53 @@ function useChat(options) {
|
|
|
29852
30667
|
}));
|
|
29853
30668
|
}
|
|
29854
30669
|
}, [widgetId, state.config?.settings.persistConversation, state.conversationId]);
|
|
30670
|
+
const createDemoConversation = React.useCallback(async (userMessage, assistantMessage) => {
|
|
30671
|
+
try {
|
|
30672
|
+
const result = await apiClient.current.createDemoConversation(userMessage, assistantMessage);
|
|
30673
|
+
if (result.success && result.id) {
|
|
30674
|
+
// Update state with the new conversation ID
|
|
30675
|
+
setState(prev => ({
|
|
30676
|
+
...prev,
|
|
30677
|
+
conversationId: result.id,
|
|
30678
|
+
}));
|
|
30679
|
+
// Save to local storage if persistence is enabled
|
|
30680
|
+
const persistConversation = state.config?.settings.persistConversation ?? true;
|
|
30681
|
+
if (persistConversation && isStorageAvailable()) {
|
|
30682
|
+
const demoMessages = [
|
|
30683
|
+
{
|
|
30684
|
+
id: generateMessageId(),
|
|
30685
|
+
message: { role: 'user', content: userMessage },
|
|
30686
|
+
timestamp: new Date().toISOString(),
|
|
30687
|
+
sources: [],
|
|
30688
|
+
},
|
|
30689
|
+
{
|
|
30690
|
+
id: generateMessageId(),
|
|
30691
|
+
message: { role: 'assistant', content: assistantMessage },
|
|
30692
|
+
timestamp: new Date(Date.now() + 1000).toISOString(),
|
|
30693
|
+
sources: [],
|
|
30694
|
+
},
|
|
30695
|
+
];
|
|
30696
|
+
saveConversation(widgetId, result.id, demoMessages);
|
|
30697
|
+
}
|
|
30698
|
+
return result.id;
|
|
30699
|
+
}
|
|
30700
|
+
return null;
|
|
30701
|
+
}
|
|
30702
|
+
catch (error) {
|
|
30703
|
+
console.error('[useChat] Failed to create demo conversation:', error);
|
|
30704
|
+
return null;
|
|
30705
|
+
}
|
|
30706
|
+
}, [widgetId, state.config?.settings.persistConversation]);
|
|
30707
|
+
// Call an action endpoint directly (frontend-owned flow)
|
|
30708
|
+
// Auto-injects conversationId and toolCallId for done flag enforcement
|
|
30709
|
+
const callActionEndpoint = React.useCallback(async (actionId, endpoint, input, options) => {
|
|
30710
|
+
const enrichedInput = {
|
|
30711
|
+
...input,
|
|
30712
|
+
conversationId: state.conversationId,
|
|
30713
|
+
toolCallId: options?.toolCallId,
|
|
30714
|
+
};
|
|
30715
|
+
return apiClient.current.callActionEndpoint(actionId, endpoint, enrichedInput, options?.token);
|
|
30716
|
+
}, [state.conversationId]);
|
|
29855
30717
|
return {
|
|
29856
30718
|
messages: state.messages,
|
|
29857
30719
|
isLoading: state.isLoading,
|
|
@@ -29862,20 +30724,22 @@ function useChat(options) {
|
|
|
29862
30724
|
sendMessage,
|
|
29863
30725
|
clearMessages,
|
|
29864
30726
|
submitFeedback,
|
|
30727
|
+
dismissAction,
|
|
29865
30728
|
conversations,
|
|
29866
30729
|
loadConversations,
|
|
29867
30730
|
switchConversation,
|
|
29868
30731
|
startNewConversation,
|
|
29869
30732
|
deleteConversation: deleteConversation$1,
|
|
30733
|
+
createDemoConversation,
|
|
30734
|
+
callActionEndpoint,
|
|
29870
30735
|
};
|
|
29871
30736
|
}
|
|
29872
30737
|
|
|
29873
|
-
const ShieldIcon = () => (jsxRuntime.jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("path", { d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" }), jsxRuntime.jsx("path", { d: "M9 12l2 2 4-4" })] }));
|
|
29874
30738
|
const DataPolicyView = ({ config, widgetName, }) => {
|
|
29875
30739
|
const headerTitle = widgetName || config?.appearance?.headerTitle || 'AI Assistant';
|
|
29876
30740
|
const hasFileUpload = config?.settings?.enableFileUpload ?? false;
|
|
29877
30741
|
const persistsConversation = config?.settings?.persistConversation ?? true;
|
|
29878
|
-
return (jsxRuntime.jsx("div", { className: "ai-chat-data-policy-view", children: jsxRuntime.jsxs("div", { className: "ai-chat-data-policy-content", children: [jsxRuntime.
|
|
30742
|
+
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." })] })] }) }));
|
|
29879
30743
|
};
|
|
29880
30744
|
|
|
29881
30745
|
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" })] }));
|
|
@@ -29883,17 +30747,19 @@ const PlusIcon = () => (jsxRuntime.jsxs("svg", { width: "22", height: "22", view
|
|
|
29883
30747
|
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" })] }));
|
|
29884
30748
|
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" })] }));
|
|
29885
30749
|
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" })] }));
|
|
29886
|
-
const ChatWindow = ({ messages, isLoading, isTyping, config, onSendMessage, onClose: _onClose, onFeedback, onActionClick,
|
|
30750
|
+
const ChatWindow = ({ messages, isLoading, isTyping, config, onSendMessage, onClose: _onClose, onFeedback, onActionClick, onActionDismiss, onCallEndpoint,
|
|
29887
30751
|
// Chat history props (only active when persistConversation is true)
|
|
29888
30752
|
conversations = [], onLoadConversations, onSwitchConversation, onStartNewConversation, onDeleteConversation, currentConversationId,
|
|
29889
30753
|
// Override props for live preview
|
|
29890
|
-
headerTitleOverride, welcomeTitleOverride, welcomeMessageOverride, placeholderOverride, suggestedQuestionsOverride,
|
|
30754
|
+
sizeOverride, headerTitleOverride, welcomeTitleOverride, welcomeMessageOverride, placeholderOverride, suggestedQuestionsOverride,
|
|
30755
|
+
// Demo mode
|
|
30756
|
+
onInputFocus, }) => {
|
|
29891
30757
|
const appearance = config?.appearance;
|
|
29892
30758
|
const settings = config?.settings;
|
|
29893
30759
|
// Check if chat history should be shown (requires both persistConversation AND showChatHistory)
|
|
29894
30760
|
const canShowHistory = (settings?.persistConversation ?? true) && (settings?.showChatHistory ?? true);
|
|
29895
30761
|
// Apply overrides for live preview (overrides take priority over saved config)
|
|
29896
|
-
const size = appearance?.size
|
|
30762
|
+
const size = sizeOverride ?? appearance?.size ?? 'medium';
|
|
29897
30763
|
const headerTitle = headerTitleOverride ?? appearance?.headerTitle ?? 'AI Assistant';
|
|
29898
30764
|
const welcomeTitle = welcomeTitleOverride ?? appearance?.welcomeTitle ?? '';
|
|
29899
30765
|
const welcomeMessage = welcomeMessageOverride ?? appearance?.welcomeMessage ?? '';
|
|
@@ -29965,12 +30831,12 @@ headerTitleOverride, welcomeTitleOverride, welcomeMessageOverride, placeholderOv
|
|
|
29965
30831
|
// The backend will detect and trigger the action based on the message
|
|
29966
30832
|
onSendMessage(question);
|
|
29967
30833
|
};
|
|
29968
|
-
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: "
|
|
30834
|
+
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 ? (
|
|
29969
30835
|
/* History Panel */
|
|
29970
30836
|
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) => {
|
|
29971
30837
|
e.stopPropagation();
|
|
29972
30838
|
onDeleteConversation(conv.id);
|
|
29973
|
-
}, "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." })), (() => {
|
|
30839
|
+
}, "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, onInputFocus: onInputFocus })] })) : (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." })), (() => {
|
|
29974
30840
|
console.log('[DEBUG ChatWindow] Rendering MessageList with', messages.length, 'messages');
|
|
29975
30841
|
messages.forEach((m, i) => {
|
|
29976
30842
|
console.log(`[DEBUG ChatWindow] msg ${i}:`, { role: m.message.role, hasAction: !!m.action, impl: m.action?.implementation });
|
|
@@ -29980,7 +30846,15 @@ headerTitleOverride, welcomeTitleOverride, welcomeMessageOverride, placeholderOv
|
|
|
29980
30846
|
console.log('[DEBUG ChatWindow] Testing renderer for query-contact-directory:', !!getActionRenderer('query-contact-directory'));
|
|
29981
30847
|
}
|
|
29982
30848
|
return null;
|
|
29983
|
-
})(), 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, onFeedback: onFeedback, onScrollStateChange: handleScrollStateChange, getActionRenderer: getActionRenderer }), 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 })] }))] }));
|
|
30849
|
+
})(), 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, onCallEndpoint: onCallEndpoint }), 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, onInputFocus: onInputFocus })] }))] }));
|
|
30850
|
+
};
|
|
30851
|
+
|
|
30852
|
+
const MessageCircleIcon = () => (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" }) }));
|
|
30853
|
+
const ChevronDownIcon = () => (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" }) }));
|
|
30854
|
+
// Icon components mapping for dynamic lookup
|
|
30855
|
+
const iconComponents = {
|
|
30856
|
+
FiMessageCircle: MessageCircleIcon,
|
|
30857
|
+
FiChevronDown: ChevronDownIcon,
|
|
29984
30858
|
};
|
|
29985
30859
|
|
|
29986
30860
|
/**
|
|
@@ -30278,108 +31152,60 @@ function generateThemeStyles(appearance, theme) {
|
|
|
30278
31152
|
return styles;
|
|
30279
31153
|
}
|
|
30280
31154
|
|
|
30281
|
-
|
|
30282
|
-
|
|
30283
|
-
|
|
30284
|
-
|
|
30285
|
-
|
|
30286
|
-
|
|
30287
|
-
|
|
30288
|
-
* Uses multiple sampling points for accuracy
|
|
30289
|
-
*/
|
|
30290
|
-
function sampleBackgroundColor(element) {
|
|
30291
|
-
const rect = element.getBoundingClientRect();
|
|
30292
|
-
const centerX = rect.left + rect.width / 2;
|
|
30293
|
-
const centerY = rect.top + rect.height / 2;
|
|
30294
|
-
// Try to get the element behind our widget
|
|
30295
|
-
// Temporarily hide the element to sample what's behind
|
|
30296
|
-
const originalVisibility = element.style.visibility;
|
|
30297
|
-
element.style.visibility = 'hidden';
|
|
30298
|
-
// Sample the center point
|
|
30299
|
-
const elementBehind = document.elementFromPoint(centerX, centerY);
|
|
30300
|
-
// Restore visibility
|
|
30301
|
-
element.style.visibility = originalVisibility;
|
|
30302
|
-
if (!elementBehind) {
|
|
30303
|
-
return '#ffffff'; // Default to white
|
|
30304
|
-
}
|
|
30305
|
-
// Get computed background color
|
|
30306
|
-
const computedStyle = window.getComputedStyle(elementBehind);
|
|
30307
|
-
let bgColor = computedStyle.backgroundColor;
|
|
30308
|
-
// If transparent, walk up the DOM tree
|
|
30309
|
-
if (bgColor === 'rgba(0, 0, 0, 0)' || bgColor === 'transparent') {
|
|
30310
|
-
let parent = elementBehind.parentElement;
|
|
30311
|
-
while (parent) {
|
|
30312
|
-
const parentStyle = window.getComputedStyle(parent);
|
|
30313
|
-
bgColor = parentStyle.backgroundColor;
|
|
30314
|
-
if (bgColor !== 'rgba(0, 0, 0, 0)' && bgColor !== 'transparent') {
|
|
30315
|
-
break;
|
|
30316
|
-
}
|
|
30317
|
-
parent = parent.parentElement;
|
|
30318
|
-
}
|
|
30319
|
-
}
|
|
30320
|
-
// If still transparent, check body/html
|
|
30321
|
-
if (bgColor === 'rgba(0, 0, 0, 0)' || bgColor === 'transparent') {
|
|
30322
|
-
const bodyStyle = window.getComputedStyle(document.body);
|
|
30323
|
-
bgColor = bodyStyle.backgroundColor;
|
|
30324
|
-
if (bgColor === 'rgba(0, 0, 0, 0)' || bgColor === 'transparent') {
|
|
30325
|
-
return '#ffffff'; // Default to white
|
|
31155
|
+
function useWidgetAppearance({ containerRef, config, theme, primaryColor, position, size, headerTitle, welcomeTitle, welcomeMessage, placeholder, welcomeBubbleText, triggerType, triggerText, customStyles, zIndex, previewMode: _previewMode, }) {
|
|
31156
|
+
// Use theme prop directly - no dynamic detection
|
|
31157
|
+
const effectiveTheme = theme ?? "light";
|
|
31158
|
+
// Set data attribute for CSS styling
|
|
31159
|
+
React.useEffect(() => {
|
|
31160
|
+
if (containerRef.current) {
|
|
31161
|
+
containerRef.current.setAttribute('data-theme', effectiveTheme);
|
|
30326
31162
|
}
|
|
30327
|
-
}
|
|
30328
|
-
|
|
30329
|
-
|
|
30330
|
-
|
|
30331
|
-
|
|
30332
|
-
|
|
30333
|
-
|
|
30334
|
-
|
|
30335
|
-
const
|
|
30336
|
-
|
|
30337
|
-
|
|
30338
|
-
const
|
|
30339
|
-
const
|
|
30340
|
-
|
|
30341
|
-
|
|
30342
|
-
|
|
30343
|
-
|
|
30344
|
-
|
|
30345
|
-
|
|
30346
|
-
|
|
30347
|
-
|
|
30348
|
-
|
|
30349
|
-
|
|
30350
|
-
const
|
|
30351
|
-
return
|
|
30352
|
-
|
|
30353
|
-
|
|
30354
|
-
|
|
30355
|
-
|
|
30356
|
-
|
|
30357
|
-
|
|
30358
|
-
|
|
31163
|
+
}, [effectiveTheme, containerRef]);
|
|
31164
|
+
const appearanceConfig = config?.appearance;
|
|
31165
|
+
const effectivePosition = position || appearanceConfig?.position || "bottom-right";
|
|
31166
|
+
const accentColor = primaryColor !== undefined ? primaryColor : appearanceConfig?.primaryColor ?? "";
|
|
31167
|
+
const effectiveSize = size || appearanceConfig?.size || "small";
|
|
31168
|
+
const effectiveHeaderTitle = headerTitle ?? appearanceConfig?.headerTitle ?? "";
|
|
31169
|
+
const effectiveWelcomeTitle = welcomeTitle ?? appearanceConfig?.welcomeTitle ?? "";
|
|
31170
|
+
const effectiveWelcomeMessage = welcomeMessage ?? appearanceConfig?.welcomeMessage ?? "";
|
|
31171
|
+
const effectivePlaceholder = placeholder ?? appearanceConfig?.placeholder ?? "";
|
|
31172
|
+
const effectiveWelcomeBubbleText = welcomeBubbleText ?? appearanceConfig?.welcomeBubbleText ?? "";
|
|
31173
|
+
const effectiveTriggerType = triggerType ?? appearanceConfig?.triggerType ?? "button";
|
|
31174
|
+
const effectiveTriggerText = triggerText ?? appearanceConfig?.triggerText ?? "Chat";
|
|
31175
|
+
const simpleAppearance = {
|
|
31176
|
+
accentColor};
|
|
31177
|
+
const generatedStyles = generateThemeStyles(simpleAppearance, effectiveTheme);
|
|
31178
|
+
const legacyStyles = appearanceConfig ? applyAppearanceStyles(appearanceConfig) : {};
|
|
31179
|
+
const mergedStyles = {
|
|
31180
|
+
...legacyStyles,
|
|
31181
|
+
...generatedStyles,
|
|
31182
|
+
...customStyles,
|
|
31183
|
+
...(zIndex !== undefined ? { "--widget-z-index": String(zIndex) } : {}),
|
|
31184
|
+
};
|
|
31185
|
+
// Compute icon contrast color for inline button styling (prevents FOUC)
|
|
31186
|
+
const iconContrastColor = accentColor ? getContrastText(accentColor) : undefined;
|
|
31187
|
+
return {
|
|
31188
|
+
effectiveTheme,
|
|
31189
|
+
effectivePosition,
|
|
31190
|
+
accentColor,
|
|
31191
|
+
iconContrastColor,
|
|
31192
|
+
effectiveSize,
|
|
31193
|
+
effectiveHeaderTitle,
|
|
31194
|
+
effectiveWelcomeTitle,
|
|
31195
|
+
effectiveWelcomeMessage,
|
|
31196
|
+
effectivePlaceholder,
|
|
31197
|
+
effectiveWelcomeBubbleText,
|
|
31198
|
+
effectiveTriggerType,
|
|
31199
|
+
effectiveTriggerText,
|
|
31200
|
+
mergedStyles,
|
|
31201
|
+
};
|
|
30359
31202
|
}
|
|
30360
|
-
|
|
30361
|
-
|
|
30362
|
-
|
|
30363
|
-
|
|
30364
|
-
|
|
30365
|
-
|
|
30366
|
-
const theme = detectTheme(element);
|
|
30367
|
-
if (theme !== lastTheme) {
|
|
30368
|
-
lastTheme = theme;
|
|
30369
|
-
callback(theme);
|
|
30370
|
-
}
|
|
30371
|
-
});
|
|
30372
|
-
// Observe body for class/style changes (common for theme switching)
|
|
30373
|
-
observer.observe(document.body, {
|
|
30374
|
-
attributes: true,
|
|
30375
|
-
attributeFilter: ['class', 'style', 'data-theme', 'data-bs-theme'],
|
|
30376
|
-
});
|
|
30377
|
-
// Also observe html element
|
|
30378
|
-
observer.observe(document.documentElement, {
|
|
30379
|
-
attributes: true,
|
|
30380
|
-
attributeFilter: ['class', 'style', 'data-theme', 'data-bs-theme'],
|
|
30381
|
-
});
|
|
30382
|
-
return observer;
|
|
31203
|
+
|
|
31204
|
+
function WidgetTriggers({ triggerType, isOpen, onToggle, triggerText, placeholder, IconComponent, showWelcomeBubble, welcomeBubbleText, previewMode, onDismissBubble, isInputBarCollapsed, setIsInputBarCollapsed, inputBarValue, setInputBarValue, onSubmitInputBar, accentColor, iconColor, }) {
|
|
31205
|
+
// Inline button styles to prevent flash of unstyled content (FOUC)
|
|
31206
|
+
// Applied directly to button elements, bypassing CSS variable cascade timing
|
|
31207
|
+
const buttonStyle = accentColor ? { background: accentColor, color: iconColor } : undefined;
|
|
31208
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [triggerType === "button" && !isOpen && welcomeBubbleText && (previewMode || showWelcomeBubble) && (jsxRuntime.jsxs("div", { className: "ai-chat-welcome-bubble", onClick: onToggle, children: [jsxRuntime.jsx("span", { children: welcomeBubbleText }), jsxRuntime.jsx("button", { className: "ai-chat-welcome-bubble-close", onClick: onDismissBubble, "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" })] })), triggerType === "button" && (jsxRuntime.jsx("button", { className: `ai-chat-button ${isOpen ? "is-open" : ""}`, onClick: onToggle, "aria-label": isOpen ? "Minimize chat" : "Open chat", style: buttonStyle, children: jsxRuntime.jsx("div", { className: "ai-chat-button-svg", children: jsxRuntime.jsx(IconComponent, {}) }) })), triggerType === "pill-text" && (jsxRuntime.jsxs("button", { className: `ai-chat-trigger-pill ${isOpen ? "is-open" : ""}`, onClick: onToggle, "aria-label": isOpen ? "Close chat" : "Open chat", style: isOpen ? buttonStyle : undefined, children: [jsxRuntime.jsx("div", { className: "ai-chat-trigger-pill-icon", children: jsxRuntime.jsx(IconComponent, {}) }), !isOpen && jsxRuntime.jsx("span", { children: triggerText })] })), triggerType === "input-bar" && !isOpen && (jsxRuntime.jsx("div", { className: "ai-chat-trigger-input-container", children: isInputBarCollapsed ? (jsxRuntime.jsxs("div", { className: "ai-chat-trigger-input-row", children: [jsxRuntime.jsx("button", { type: "button", className: "ai-chat-trigger-collapse-toggle", onClick: () => setIsInputBarCollapsed(false), "aria-label": "Expand input bar", children: jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsxRuntime.jsx("polyline", { points: "15 18 9 12 15 6" }) }) }), jsxRuntime.jsx("button", { type: "button", className: "ai-chat-button", onClick: onToggle, "aria-label": "Open chat", style: buttonStyle, children: jsxRuntime.jsx("div", { className: "ai-chat-button-svg", children: jsxRuntime.jsx(IconComponent, {}) }) })] })) : (jsxRuntime.jsxs("div", { className: "ai-chat-trigger-input-row", children: [jsxRuntime.jsx("button", { type: "button", className: "ai-chat-trigger-collapse-toggle", onClick: () => setIsInputBarCollapsed(true), "aria-label": "Collapse input bar", children: jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsxRuntime.jsx("polyline", { points: "9 18 15 12 9 6" }) }) }), jsxRuntime.jsxs("form", { className: "ai-chat-trigger-input-wrapper", onSubmit: onSubmitInputBar, children: [jsxRuntime.jsx("input", { type: "text", className: "ai-chat-trigger-input", placeholder: placeholder || "Ask me anything...", value: inputBarValue, onChange: (event) => setInputBarValue(event.target.value), "aria-label": "Chat input" }), jsxRuntime.jsx("button", { type: "submit", className: "ai-chat-trigger-input-btn", disabled: !inputBarValue.trim(), "aria-label": "Send message", style: buttonStyle, children: jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("line", { x1: "12", y1: "19", x2: "12", y2: "5" }), jsxRuntime.jsx("polyline", { points: "5 12 12 5 19 12" })] }) })] }), jsxRuntime.jsx("button", { type: "button", className: "ai-chat-trigger-input-expand", onClick: onToggle, "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" }) }) })] })) }))] }));
|
|
30383
31209
|
}
|
|
30384
31210
|
|
|
30385
31211
|
function styleInject(css, ref) {
|
|
@@ -30409,21 +31235,17 @@ function styleInject(css, ref) {
|
|
|
30409
31235
|
}
|
|
30410
31236
|
}
|
|
30411
31237
|
|
|
30412
|
-
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: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-welcome-bubble{animation:ai-chat-bubble-fade-in .3s ease-out;background:var(--button-color,var(--btn-primary-bg,#07f));border:none;border-radius:12px;box-shadow:0 4px 12px rgba(0,0,0,.15);color:var(--button-icon-color,var(--btn-primary-text,#fff));cursor:pointer;font-size:14px;font-weight:500;line-height:1.4;padding:12px 16px;position:absolute;width:200px;z-index:0}.ai-chat-widget-container.bottom-right .ai-chat-welcome-bubble{bottom:68px;right:0;text-align:right}.ai-chat-widget-container.bottom-left .ai-chat-welcome-bubble{bottom:68px;left:0;right:auto;text-align:left}.ai-chat-widget-container.top-right .ai-chat-welcome-bubble{right:0;text-align:right;top:68px}.ai-chat-widget-container.top-left .ai-chat-welcome-bubble{left:0;right:auto;text-align:left;top:68px}.ai-chat-welcome-bubble:hover{filter:brightness(1.1)}@keyframes ai-chat-bubble-fade-in{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.ai-chat-widget-container.top-left .ai-chat-welcome-bubble,.ai-chat-widget-container.top-right .ai-chat-welcome-bubble{animation-name:ai-chat-bubble-fade-in-down}@keyframes ai-chat-bubble-fade-in-down{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}.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-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 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-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-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{border-top:1px solid var(--border-subtle,rgba(0,0,0,.08));color:var(--text-muted,#71717a);font-size:12px;font-style:italic;margin-top:4px;padding-top:8px}.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;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}.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)}}";
|
|
31238
|
+
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)}@keyframes ai-chat-message-appear{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(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-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;--breakpoint-mobile:480px;--breakpoint-tablet:768px;--breakpoint-desktop:769px;--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)!important}.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{height:100%;inset:0;pointer-events:none;position:absolute;width:100%}.ai-chat-widget-container.container-mode .ai-chat-button,.ai-chat-widget-container.container-mode .ai-chat-trigger-button,.ai-chat-widget-container.container-mode .ai-chat-trigger-input-container,.ai-chat-widget-container.container-mode .ai-chat-window{pointer-events:auto}.ai-chat-widget-container.container-mode:not(.trigger-input-bar) .ai-chat-button{position:absolute}.ai-chat-widget-container.container-mode:not(.trigger-input-bar).bottom-right .ai-chat-button{bottom:20px;left:auto;right:20px;top:auto}.ai-chat-widget-container.container-mode:not(.trigger-input-bar).bottom-left .ai-chat-button{bottom:20px;left:20px;right:auto;top:auto}.ai-chat-widget-container.container-mode:not(.trigger-input-bar).top-right .ai-chat-button{bottom:auto;left:auto;right:20px;top:20px}.ai-chat-widget-container.container-mode:not(.trigger-input-bar).top-left .ai-chat-button{bottom:auto;left:20px;right:auto;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-user-message-sent{0%{opacity:0;transform:translateX(20px) scale(.95)}60%{opacity:1;transform:translateX(-4px) scale(1.02)}to{opacity:1;transform:translateX(0) scale(1)}}@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{-webkit-font-smoothing:subpixel-antialiased;-moz-osx-font-smoothing:auto;animation:ai-chat-window-open var(--duration-slow,.35s) var(--spring-bounce);-webkit-backface-visibility:hidden;backface-visibility:hidden;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;-webkit-transform:translateZ(0);transform:translateZ(0);transform-origin:bottom right;will-change:transform,opacity;z-index:2147483647!important}.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;max-height:calc(100vh - 100px);max-width:calc(100vw - 40px);min-width:320px;width:380px}.ai-chat-window.size-medium{height:calc(100vh - 140px);max-height:680px;max-width:calc(100vw - 40px);min-width:380px;width:440px}.ai-chat-window.size-large{height:calc(100vh - 48px);max-height:calc(100vh - 48px);max-width:560px;min-width:440px;width:33vw}.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-widget-container.trigger-button.size-large.bottom-left .ai-chat-window,.ai-chat-widget-container.trigger-button.size-large.bottom-right .ai-chat-window{bottom:0}.ai-chat-widget-container.trigger-button.size-large.top-left .ai-chat-window,.ai-chat-widget-container.trigger-button.size-large.top-right .ai-chat-window{top:0}.ai-chat-widget-container.trigger-pill-text.size-large.bottom-left .ai-chat-window,.ai-chat-widget-container.trigger-pill-text.size-large.bottom-right .ai-chat-window{bottom:72px;height:calc(100vh - 120px);max-height:calc(100vh - 120px)}.ai-chat-widget-container.trigger-pill-text.size-large.top-left .ai-chat-window,.ai-chat-widget-container.trigger-pill-text.size-large.top-right .ai-chat-window{height:calc(100vh - 120px);max-height:calc(100vh - 120px);top:72px}.ai-chat-widget-container.trigger-input-bar.size-large.bottom-left .ai-chat-window,.ai-chat-widget-container.trigger-input-bar.size-large.bottom-right .ai-chat-window{bottom:72px;height:calc(100vh - 120px);max-height:calc(100vh - 120px)}.ai-chat-widget-container.trigger-input-bar.size-large.top-left .ai-chat-window,.ai-chat-widget-container.trigger-input-bar.size-large.top-right .ai-chat-window{height:calc(100vh - 120px);max-height:calc(100vh - 120px);top:72px}.ai-chat-widget-container.container-mode.bottom-right .ai-chat-window{bottom:20px;left:auto;right:20px;top:auto}.ai-chat-widget-container.container-mode.bottom-left .ai-chat-window{bottom:20px;left:20px;right:auto;top:auto}.ai-chat-widget-container.container-mode.top-right .ai-chat-window{bottom:auto;left:auto;right:20px;top:20px}.ai-chat-widget-container.container-mode.top-left .ai-chat-window{bottom:auto;left:20px;right:auto;top:20px}.ai-chat-widget-container.container-mode.size-small .ai-chat-window{height:min(500px,50%);max-height:calc(100% - 100px);max-width:calc(100% - 40px);min-height:300px;min-width:280px;width:min(380px,calc(100% - 40px))}.ai-chat-widget-container.container-mode.size-medium .ai-chat-window{height:min(680px,70%);max-height:calc(100% - 80px);max-width:calc(100% - 40px);min-height:400px;min-width:320px;width:min(440px,calc(100% - 40px))}.ai-chat-widget-container.container-mode.size-large .ai-chat-window{height:90%;max-height:calc(100% - 60px);max-width:calc(100% - 40px);min-height:500px;min-width:380px;width:min(560px,calc(100% - 40px))}.ai-chat-widget-container.trigger-button.container-mode.bottom-left .ai-chat-window,.ai-chat-widget-container.trigger-button.container-mode.bottom-right .ai-chat-window{bottom:88px}.ai-chat-widget-container.trigger-pill-text.container-mode.bottom-left .ai-chat-window,.ai-chat-widget-container.trigger-pill-text.container-mode.bottom-right .ai-chat-window{bottom:80px}.ai-chat-widget-container.trigger-button.container-mode.size-medium .ai-chat-window,.ai-chat-widget-container.trigger-button.container-mode.size-small .ai-chat-window,.ai-chat-widget-container.trigger-pill-text.container-mode.size-medium .ai-chat-window,.ai-chat-widget-container.trigger-pill-text.container-mode.size-small .ai-chat-window{max-height:calc(100% - 108px)}.ai-chat-widget-container.trigger-button.container-mode.size-large .ai-chat-window,.ai-chat-widget-container.trigger-pill-text.container-mode.size-large .ai-chat-window{height:calc(100% - 108px);max-height:calc(100% - 108px)}.ai-chat-widget-container.trigger-button.container-mode.top-left .ai-chat-window,.ai-chat-widget-container.trigger-button.container-mode.top-right .ai-chat-window{bottom:auto;top:88px}.ai-chat-widget-container.trigger-pill-text.container-mode.top-left .ai-chat-window,.ai-chat-widget-container.trigger-pill-text.container-mode.top-right .ai-chat-window{bottom:auto;top:80px}.ai-chat-widget-container.container-mode.mobile-mode.is-open{margin:0!important;padding:0!important}.ai-chat-widget-container.container-mode.mobile-mode.is-open,.ai-chat-widget-container.container-mode.mobile-mode.is-open .ai-chat-window{bottom:0!important;height:100%!important;left:0!important;position:absolute!important;right:0!important;top:0!important;width:100%!important}.ai-chat-widget-container.container-mode.mobile-mode.is-open .ai-chat-window{animation:none!important;border:none!important;border-radius:0!important;box-shadow:none!important;max-height:100%!important;max-width:100%!important;min-height:100%!important;min-width:100%!important}.ai-chat-widget-container.container-mode.mobile-mode.is-open .ai-chat-button,.ai-chat-widget-container.container-mode.mobile-mode.is-open .ai-chat-trigger-input-container,.ai-chat-widget-container.container-mode.mobile-mode.is-open .ai-chat-trigger-pill{display:none!important}.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{-webkit-font-smoothing:subpixel-antialiased;-moz-osx-font-smoothing:auto;align-items:center;-webkit-backface-visibility:hidden;backface-visibility:hidden;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;-webkit-transform:translateZ(0);transform:translateZ(0);transition:transform var(--duration-fast) ease;width:var(--button-size,56px);will-change:transform;z-index:2147483647!important}.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:2147483647!important}.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{-webkit-font-smoothing:subpixel-antialiased;-moz-osx-font-smoothing:auto;align-items:center;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);-webkit-backface-visibility:hidden;backface-visibility:hidden;background:var(--trigger-bg,rgba(0,0,0,.04));border:1px solid var(--trigger-border,rgba(0,0,0,.12));border-radius:9999px;box-shadow:0 4px 16px rgba(0,0,0,.12);color:var(--trigger-text,#1a1a1a);cursor:pointer;display:flex;font-size:14px;font-weight:500;gap:10px;height:48px;justify-content:center;padding:0 24px;position:relative;-webkit-transform:translateZ(0);transform:translateZ(0);transition:all .3s ease,color .4s ease,background .4s ease,border-color .4s ease;white-space:nowrap;will-change:transform;z-index:2147483647!important}.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));color:var(--button-icon-color,var(--btn-primary-text));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:var(--trigger-bg-hover,rgba(0,0,0,.08));border-color:var(--trigger-border-hover,rgba(0,0,0,.2))}.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:20px;transition:all .3s ease;width:20px}.ai-chat-widget-container.trigger-pill-text.container-mode .ai-chat-trigger-pill{position:absolute}.ai-chat-widget-container.trigger-pill-text.container-mode.bottom-right .ai-chat-trigger-pill{bottom:20px;left:auto;right:20px;top:auto}.ai-chat-widget-container.trigger-pill-text.container-mode.bottom-left .ai-chat-trigger-pill{bottom:20px;left:20px;right:auto;top:auto}.ai-chat-widget-container.trigger-pill-text.container-mode.top-right .ai-chat-trigger-pill{bottom:auto;left:auto;right:20px;top:20px}.ai-chat-widget-container.trigger-pill-text.container-mode.top-left .ai-chat-trigger-pill{bottom:auto;left:20px;right:auto;top:20px}.ai-chat-widget-container.trigger-pill-text.container-mode .ai-chat-trigger-pill{pointer-events:auto}.ai-chat-trigger-input-container{align-items:flex-end;display:flex;flex-direction:column;gap:8px;z-index:2147483647!important}.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-row{align-items:center;display:flex;gap:8px;transition:gap .4s cubic-bezier(.4,0,.2,1),width .4s cubic-bezier(.4,0,.2,1)}.ai-chat-widget-container.trigger-input-bar.is-collapsed .ai-chat-trigger-input-row{gap:4px}.ai-chat-trigger-input-expand{-webkit-font-smoothing:subpixel-antialiased;-moz-osx-font-smoothing:auto;align-items:center;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);-webkit-backface-visibility:hidden;backface-visibility:hidden;background:var(--trigger-bg,rgba(0,0,0,.04));border:1px solid var(--trigger-border,rgba(0,0,0,.12));border-radius:50%;box-shadow:0 2px 12px rgba(0,0,0,.12);color:var(--trigger-text-muted,rgba(0,0,0,.5));cursor:pointer;display:flex;flex-shrink:0;height:48px;justify-content:center;padding:0;-webkit-transform:translateZ(0);transform:translateZ(0);transition:all .2s ease,color .4s ease,background .4s ease,border-color .4s ease;width:48px;will-change:transform}.ai-chat-trigger-input-expand:hover{background:var(--trigger-bg-hover,rgba(0,0,0,.08));color:var(--trigger-text,#1a1a1a);transform:translateY(-2px)}.ai-chat-trigger-input-expand:active{transform:translateY(0)}.ai-chat-trigger-input-expand svg{stroke-width:2.5;height:20px;width:20px}.ai-chat-trigger-input-wrapper{flex:1;min-width:120px;position:relative;transform-origin:right center;transition:flex .4s cubic-bezier(.4,0,.2,1),width .4s cubic-bezier(.4,0,.2,1),opacity .3s cubic-bezier(.4,0,.2,1),transform .4s cubic-bezier(.4,0,.2,1);z-index:1}.ai-chat-widget-container.bottom-left .ai-chat-trigger-input-wrapper,.ai-chat-widget-container.top-left .ai-chat-trigger-input-wrapper{transform-origin:left center}.ai-chat-trigger-input{-webkit-font-smoothing:subpixel-antialiased;-moz-osx-font-smoothing:auto;backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);-webkit-backface-visibility:hidden;backface-visibility:hidden;background:var(--trigger-bg,rgba(0,0,0,.04));border:1px solid var(--trigger-border,rgba(0,0,0,.12));border-radius:var(--radius-input,62px);box-shadow:0 4px 24px rgba(0,0,0,.15);box-sizing:border-box;color:var(--trigger-text,#1a1a1a);font-size:var(--text-md,15px);height:48px;outline:none;padding:6px 52px 6px 16px;-webkit-transform:translateZ(0);transform:translateZ(0);transition:all .2s ease,color .4s ease,background .4s ease,border-color .4s ease;width:100%}.ai-chat-trigger-input::placeholder{color:var(--trigger-text-muted,rgba(0,0,0,.5))}.ai-chat-trigger-input:focus{background:var(--trigger-bg-hover,rgba(0,0,0,.08));border-color:var(--trigger-border-hover,rgba(0,0,0,.2))}.ai-chat-trigger-input-btn{-webkit-font-smoothing:subpixel-antialiased;-moz-osx-font-smoothing:auto;align-items:center;-webkit-backface-visibility:hidden;backface-visibility:hidden;background:var(--primary-color,var(--button-color,#07f));border:none;border-radius:50%;box-shadow:0 2px 8px rgba(0,119,255,.3);color:var(--send-button-icon-color,var(--button-icon-color,#fff));cursor:pointer;display:flex;height:38px;justify-content:center;position:absolute;right:4px;top:50%;transform:translateY(-50%);transition:all .2s ease;width:38px;will-change:transform}.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-container.trigger-input-bar .ai-chat-trigger-input-row{width:min(380px,calc(100vw - 40px))}.ai-chat-widget-container.trigger-input-bar.size-medium .ai-chat-trigger-input-row{width:min(440px,calc(100vw - 40px))}.ai-chat-widget-container.trigger-input-bar.size-large .ai-chat-trigger-input-row{width:min(560px,calc(100vw - 40px))}.ai-chat-widget-container.trigger-input-bar .ai-chat-trigger-input-wrapper{flex:1;min-width:160px}.ai-chat-widget-container.trigger-input-bar.container-mode .ai-chat-trigger-input-container{position:absolute;width:min(380px,calc(100% - 40px))}.ai-chat-widget-container.trigger-input-bar.container-mode.size-small .ai-chat-trigger-input-container{width:min(380px,calc(100% - 40px))}.ai-chat-widget-container.trigger-input-bar.container-mode.size-medium .ai-chat-trigger-input-container{width:min(440px,calc(100% - 40px))}.ai-chat-widget-container.trigger-input-bar.container-mode.size-large .ai-chat-trigger-input-container{width:min(560px,calc(100% - 40px))}.ai-chat-widget-container.trigger-input-bar.container-mode.bottom-right .ai-chat-trigger-input-container{bottom:20px;left:auto;right:20px;top:auto}.ai-chat-widget-container.trigger-input-bar.container-mode.bottom-left .ai-chat-trigger-input-container{bottom:20px;left:20px;right:auto;top:auto}.ai-chat-widget-container.trigger-input-bar.container-mode.top-right .ai-chat-trigger-input-container{bottom:auto;left:auto;right:20px;top:20px}.ai-chat-widget-container.trigger-input-bar.container-mode.top-left .ai-chat-trigger-input-container{bottom:auto;left:20px;right:auto;top:20px}.ai-chat-widget-container.trigger-input-bar.container-mode .ai-chat-trigger-input-row{width:100%}.ai-chat-widget-container.trigger-input-bar.container-mode .ai-chat-trigger-input-wrapper{flex:1;min-width:120px}.ai-chat-widget-container.trigger-input-bar.is-collapsed .ai-chat-trigger-input-wrapper{opacity:0;overflow:hidden;pointer-events:none;transform:scaleX(0);width:0}.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}.ai-chat-widget-container.trigger-input-bar .ai-chat-window.size-small{max-width:calc(100vw - 40px);min-width:320px;width:380px}.ai-chat-widget-container.trigger-input-bar .ai-chat-window.size-medium{max-width:calc(100vw - 40px);min-width:380px;width:440px}.ai-chat-widget-container.trigger-input-bar .ai-chat-window.size-large{max-width:calc(100vw - 40px);min-width:440px;width:560px}.ai-chat-widget-container.trigger-input-bar.container-mode .ai-chat-window{max-width:calc(100% - 40px);position:absolute}.ai-chat-widget-container.trigger-input-bar.container-mode.bottom-right .ai-chat-window{bottom:20px;left:auto;right:20px;top:auto}.ai-chat-widget-container.trigger-input-bar.container-mode.bottom-left .ai-chat-window{bottom:20px;left:20px;right:auto;top:auto}.ai-chat-widget-container.trigger-input-bar.container-mode.top-right .ai-chat-window{bottom:auto;left:auto;right:20px;top:20px}.ai-chat-widget-container.trigger-input-bar.container-mode.top-left .ai-chat-window{bottom:auto;left:20px;right:auto;top:20px}.ai-chat-widget-container.trigger-input-bar.container-mode.size-small .ai-chat-window{max-width:calc(100% - 40px);width:380px}.ai-chat-widget-container.trigger-input-bar.container-mode.size-medium .ai-chat-window{max-width:calc(100% - 40px);width:440px}.ai-chat-widget-container.trigger-input-bar.container-mode.size-large .ai-chat-window{max-width:calc(100% - 40px);width:560px}.ai-chat-widget-container.trigger-input-bar.mobile-mode .ai-chat-trigger-input-container{width:calc(100% - 40px)}.ai-chat-widget-container.trigger-input-bar.mobile-mode .ai-chat-trigger-input-row{width:100%}.ai-chat-widget-container.trigger-input-bar.mobile-mode .ai-chat-trigger-input-wrapper{flex:1;min-width:120px}.ai-chat-widget-container.trigger-input-bar.mobile-mode.is-collapsed .ai-chat-trigger-input-container{width:auto}.ai-chat-widget-container.trigger-input-bar.mobile-mode.is-collapsed .ai-chat-trigger-input-row{gap:8px;justify-content:flex-end}.ai-chat-trigger-collapse-toggle{align-items:center;background:transparent;border:none;border-radius:50%;color:var(--trigger-text-muted,rgba(0,0,0,.5));cursor:pointer;display:flex;flex-shrink:0;height:24px;justify-content:center;margin-right:-4px;order:-1;padding:0;transition:all .2s ease;width:24px}.ai-chat-trigger-collapse-toggle:hover{color:var(--trigger-text,#1a1a1a)}.ai-chat-trigger-collapse-toggle svg{stroke-width:2.5;height:20px;transition:transform .4s cubic-bezier(.4,0,.2,1);width:20px}.ai-chat-widget-container.trigger-input-bar.bottom-left .ai-chat-trigger-collapse-toggle svg,.ai-chat-widget-container.trigger-input-bar.top-left .ai-chat-trigger-collapse-toggle svg{transform:rotate(180deg)}.ai-chat-trigger-input-expand{margin-left:-4px;order:1;transition:transform .2s ease,opacity .3s ease}.ai-chat-widget-container.container-mode .ai-chat-trigger-input-expand{margin-left:0}.ai-chat-trigger-input-expand svg{transition:transform .4s cubic-bezier(.4,0,.2,1)}.ai-chat-widget-container.trigger-input-bar .ai-chat-trigger-input-expand svg{transform:rotate(0deg)}.ai-chat-widget-container.bottom-left .ai-chat-trigger-collapse-toggle,.ai-chat-widget-container.top-left .ai-chat-trigger-collapse-toggle{margin-left:-4px;margin-right:0;order:1}.ai-chat-widget-container.bottom-left .ai-chat-trigger-input-expand,.ai-chat-widget-container.top-left .ai-chat-trigger-input-expand{margin-left:0;margin-right:-4px;order:-1}.ai-chat-widget-container.trigger-input-bar .ai-chat-trigger-input-row .ai-chat-button{order:1}.ai-chat-widget-container.trigger-input-bar.bottom-left .ai-chat-trigger-input-row .ai-chat-button,.ai-chat-widget-container.trigger-input-bar.top-left .ai-chat-trigger-input-row .ai-chat-button{order:-1}.ai-chat-widget-container.trigger-input-bar.is-collapsed.bottom-right .ai-chat-button,.ai-chat-widget-container.trigger-input-bar.is-collapsed.top-right .ai-chat-button{margin-right:16px}.ai-chat-widget-container.trigger-input-bar.is-collapsed.bottom-left .ai-chat-button,.ai-chat-widget-container.trigger-input-bar.is-collapsed.top-left .ai-chat-button{margin-left:16px}.ai-chat-widget-container.trigger-input-bar.is-collapsed .ai-chat-trigger-input-row{flex-direction:row;justify-content:flex-end}.ai-chat-widget-container.trigger-input-bar.is-collapsed.bottom-left .ai-chat-trigger-input-row,.ai-chat-widget-container.trigger-input-bar.is-collapsed.top-left .ai-chat-trigger-input-row{justify-content:flex-start}.ai-chat-widget-container.trigger-input-bar .ai-chat-button{display:flex;opacity:1;transform:scale(1);transition:transform .4s cubic-bezier(.4,0,.2,1),opacity .3s cubic-bezier(.4,0,.2,1),width .4s cubic-bezier(.4,0,.2,1),height .4s cubic-bezier(.4,0,.2,1)}.ai-chat-widget-container.trigger-input-bar:not(.is-collapsed) .ai-chat-button{border:none;height:0;margin:0;min-width:0;opacity:0;overflow:hidden;padding:0;pointer-events:none;transform:scale(0);width:0}.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-widget.light .ai-chat-trigger-pill:not(.is-open){backdrop-filter:none;-webkit-backdrop-filter:none;background:#fff;border:1px solid rgba(0,0,0,.15);box-shadow:0 2px 8px rgba(0,0,0,.08);color:#18181b}.ai-chat-widget.light .ai-chat-trigger-pill:not(.is-open):hover{background:#fafafa;border-color:rgba(0,0,0,.2)}.ai-chat-widget.light .ai-chat-trigger-input-expand{backdrop-filter:none;-webkit-backdrop-filter:none;background:#fff;border:1px solid rgba(0,0,0,.15);box-shadow:0 2px 8px rgba(0,0,0,.08);color:#52525b}.ai-chat-widget.light .ai-chat-trigger-input-expand:hover{background:#fafafa;color:#18181b}.ai-chat-widget.light .ai-chat-trigger-input{backdrop-filter:none;-webkit-backdrop-filter:none;background:#fff;border:1px solid rgba(0,0,0,.15);box-shadow:0 2px 8px rgba(0,0,0,.08);color:#18181b}.ai-chat-widget.light .ai-chat-trigger-input::placeholder{color:#71717a}.ai-chat-widget.light .ai-chat-trigger-input:focus{background:#fff;border-color:rgba(0,0,0,.25)}.ai-chat-widget.light .ai-chat-trigger-collapse-toggle{color:#71717a}.ai-chat-widget.light .ai-chat-trigger-collapse-toggle:hover{color:#18181b}.ai-chat-widget.dark .ai-chat-trigger-pill:not(.is-open){backdrop-filter:none;-webkit-backdrop-filter:none;background:#27272a;border:1px solid hsla(0,0%,100%,.15);box-shadow:0 2px 8px rgba(0,0,0,.3);color:#fafafa}.ai-chat-widget.dark .ai-chat-trigger-pill:not(.is-open):hover{background:#3f3f46;border-color:hsla(0,0%,100%,.25)}.ai-chat-widget.dark .ai-chat-trigger-input-expand{backdrop-filter:none;-webkit-backdrop-filter:none;background:#27272a;border:1px solid hsla(0,0%,100%,.15);box-shadow:0 2px 8px rgba(0,0,0,.3);color:#a1a1aa}.ai-chat-widget.dark .ai-chat-trigger-input-expand:hover{background:#3f3f46;color:#fafafa}.ai-chat-widget.dark .ai-chat-trigger-input{backdrop-filter:none;-webkit-backdrop-filter:none;background:#27272a;border:1px solid hsla(0,0%,100%,.15);box-shadow:0 2px 8px rgba(0,0,0,.3);color:#fafafa}.ai-chat-widget.dark .ai-chat-trigger-input::placeholder{color:#71717a}.ai-chat-widget.dark .ai-chat-trigger-input:focus{background:#27272a;border-color:hsla(0,0%,100%,.25)}.ai-chat-widget.dark .ai-chat-trigger-collapse-toggle{color:#71717a}.ai-chat-widget.dark .ai-chat-trigger-collapse-toggle:hover{color:#fafafa}.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{-webkit-font-smoothing:subpixel-antialiased;-moz-osx-font-smoothing:auto;align-items:flex-end;-webkit-backface-visibility:hidden;backface-visibility:hidden;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;-webkit-transform:translateZ(0);transform:translateZ(0);transition:all var(--duration-fast,.15s) ease;will-change:transform;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:16px!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{-webkit-font-smoothing:subpixel-antialiased;-moz-osx-font-smoothing:auto;align-items:center;align-self:center;-webkit-backface-visibility:hidden;backface-visibility:hidden;background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#151515))));border:none;border-radius:50%;color:var(--send-button-icon-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;-webkit-transform:translateZ(0);transform:translateZ(0);transition:all var(--duration-fast,.15s) ease;width:40px;will-change:transform,opacity}.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(--send-button-icon-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(--send-button-icon-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(--send-button-icon-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;min-height:0;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;animation:ai-chat-user-message-sent .35s var(--spring-bounce)}.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);max-width:100%;overflow-wrap:break-word;padding:8px 14px;word-break:break-word}.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-meta{align-items:center;display:flex;gap:var(--space-sm,8px);margin-top:var(--space-xs,4px);padding:0 var(--space-xs,4px)}.ai-chat-message.user .ai-chat-message-meta{justify-content:flex-end}.ai-chat-message.assistant .ai-chat-message-meta{justify-content:flex-start}.ai-chat-message-timestamp{color:var(--text-muted,#71717a);font-size:var(--text-xs,12px)}.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-message.assistant.streaming .ai-chat-message-content>ol:last-child>li:last-child,.ai-chat-message.assistant.streaming .ai-chat-message-content>p:last-child,.ai-chat-message.assistant.streaming .ai-chat-message-content>ul:last-child>li:last-child{animation:ai-chat-text-fade-in .3s ease-out}@keyframes ai-chat-text-fade-in{0%{opacity:0}to{opacity:1}}.ai-chat-suggested-questions{align-self:flex-end;margin:0;padding:16px 0 0;width:100%}.ai-chat-suggested-questions-list{align-items:center;display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end}.ai-chat-suggested-question{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-widget-container.container-mode.trigger-input-bar .ai-chat-suggested-questions{box-sizing:border-box;margin:0;padding:16px 0 0;width:100%}.ai-chat-widget-container.container-mode.trigger-input-bar .ai-chat-suggested-questions-list{box-sizing:border-box;display:flex;flex-wrap:wrap;gap:6px}.ai-chat-widget-container.container-mode.trigger-input-bar.bottom-right .ai-chat-suggested-questions-list,.ai-chat-widget-container.container-mode.trigger-input-bar.top-right .ai-chat-suggested-questions-list{justify-content:flex-end;margin-left:auto;margin-right:0;padding-right:0}.ai-chat-widget-container.container-mode.trigger-input-bar.bottom-left .ai-chat-suggested-questions-list,.ai-chat-widget-container.container-mode.trigger-input-bar.top-left .ai-chat-suggested-questions-list{justify-content:flex-start;margin-left:0;margin-right:auto;padding-left:0}.ai-chat-widget-container.container-mode.trigger-input-bar.size-small .ai-chat-suggested-questions-list{max-width:calc(100% - 132px);width:348px}.ai-chat-widget-container.container-mode.trigger-input-bar.size-medium .ai-chat-suggested-questions-list{max-width:calc(100% - 132px);width:408px}.ai-chat-widget-container.container-mode.trigger-input-bar.size-large .ai-chat-suggested-questions-list{max-width:calc(100% - 132px);width:528px}.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;animation:followUpFadeIn .25s ease-out forwards;background:var(--primary-color,var(--button-color,#07f));border:none;border-radius:var(--radius-preset-badge,18px);color:var(--button-icon-color,#fff);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;white-space:nowrap}@keyframes followUpFadeIn{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.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}@media (max-width:480px){.ai-chat-action-card{padding:12px}.ai-chat-action-header{font-size:var(--text-sm,14px);margin-bottom:var(--space-sm,12px)}.ai-chat-action-button{font-size:13px;padding:10px 14px}.ai-chat-action-link-button{font-size:13px;padding:10px}}.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-link-preview-grid{display:grid;gap:12px}.ai-chat-link-preview-grid--single{grid-template-columns:1fr}.ai-chat-link-preview-grid--double{grid-template-columns:repeat(2,1fr)}.ai-chat-link-preview-grid--triple{grid-template-columns:repeat(3,1fr)}@media (max-width:768px){.ai-chat-link-preview-grid--triple{grid-template-columns:repeat(2,1fr)}}@media (max-width:480px){.ai-chat-link-preview-grid--double,.ai-chat-link-preview-grid--triple{grid-template-columns:1fr}}.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}@media (max-width:480px){.ai-chat-video-player__play-btn{height:56px;width:56px}.ai-chat-video-player__provider-badge{font-size:10px;padding:3px 6px}}.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;flex-wrap:wrap;gap:8px;justify-content:flex-start;width:100%}@media (max-width:380px){.ai-chat-location-card__actions{flex-direction:column}.ai-chat-location-card__button,.ai-chat-location-card__link{flex:none;justify-content:center;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}@media (max-width:480px){.ai-chat-location-card__content{padding:10px}.ai-chat-location-card__name{font-size:14px}.ai-chat-location-card__address,.ai-chat-location-card__description,.ai-chat-location-card__hours{font-size:12px}.ai-chat-location-card__button,.ai-chat-location-card__link{font-size:12px;padding:8px 12px}.ai-chat-location-card-list__carousel>.ai-chat-location-card{flex:0 0 240px}}.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;flex-wrap:wrap;gap:8px;padding:0 12px 12px}@media (max-width:380px){.ai-chat-contact-card__actions{flex-direction:column}.ai-chat-contact-card__button{flex:none;width:100%}}.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}@media (max-width:480px){.ai-chat-contact-card__name{font-size:16px}.ai-chat-contact-card__detail,.ai-chat-contact-card__role{font-size:13px}.ai-chat-contact-card__button{font-size:13px;padding:10px 16px}.ai-chat-contact-card--horizontal .ai-chat-contact-card__info,.ai-chat-contact-card--vertical .ai-chat-contact-card__info{padding:12px}}.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--completed,.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--completed .ai-chat-form-card__header{margin-bottom:0}.ai-chat-form-card--completed .ai-chat-form-card__icon svg{color:#10b981}.ai-chat-form-card__header{align-items:center;display:flex;gap:8px;margin-bottom:12px}.ai-chat-form-card__icon{align-items:center;display:inline-flex;justify-content:center}.ai-chat-form-card__icon svg{color:var(--text-primary,#3e3e3e);height:18px;width:18px}.ai-chat-widget.dark .ai-chat-form-card__icon svg,.chakra-ui-dark .ai-chat-form-card__icon svg,.dark .ai-chat-form-card__icon svg,[data-theme=dark] .ai-chat-form-card__icon svg{color:#fff}.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 .ai-chat-form-card__icon svg{color:#dc2626}.ai-chat-form-card--submitted .ai-chat-form-card__icon svg{color:#16a34a}.ai-chat-widget.dark .ai-chat-form-card--error .ai-chat-form-card__icon svg,.chakra-ui-dark .ai-chat-form-card--error .ai-chat-form-card__icon svg,.dark .ai-chat-form-card--error .ai-chat-form-card__icon svg,[data-theme=dark] .ai-chat-form-card--error .ai-chat-form-card__icon svg{color:#fca5a5}.ai-chat-widget.dark .ai-chat-form-card--submitted .ai-chat-form-card__icon svg,.chakra-ui-dark .ai-chat-form-card--submitted .ai-chat-form-card__icon svg,.dark .ai-chat-form-card--submitted .ai-chat-form-card__icon svg,[data-theme=dark] .ai-chat-form-card--submitted .ai-chat-form-card__icon svg{color:#4ade80}.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;justify-content:flex-start}@media (max-width:380px){.ai-chat-form-card__rating{gap:6px}.ai-chat-form-card__rating-btn{font-size:13px;height:36px;width:36px}}.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;flex-wrap:wrap;gap:8px;margin-top:16px;padding-top:16px}@media (max-width:380px){.ai-chat-form-card__actions{align-items:stretch;flex-direction:column}.ai-chat-form-card__actions-spacer{display:none}.ai-chat-form-card__btn{width:100%}}.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}@media (max-width:480px){.ai-chat-form-card{padding:12px}.ai-chat-form-card__title{font-size:14px}.ai-chat-form-card__context,.ai-chat-form-card__description{font-size:12px}.ai-chat-form-card__question-text{font-size:13px}.ai-chat-form-card__textarea{font-size:13px;min-height:70px}.ai-chat-form-card__option{padding:8px 10px}.ai-chat-form-card__option-text{font-size:13px}}.ai-chat-booking-card{--action-accent:var(--primary-color,#3b82f6);background:var(--bg-secondary,#f5f5f5);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%,.06)}.ai-chat-booking-card__header{align-items:center;display:flex;gap:8px;margin-bottom:12px}.ai-chat-booking-card__back-btn{align-items:center;background:rgba(0,0,0,.05);border:none;border-radius:6px;color:var(--text-secondary,#6b7280);cursor:pointer;display:flex;font-family:inherit;font-size:13px;font-weight:500;justify-content:center;padding:6px;transition:background .15s ease,color .15s ease}.ai-chat-booking-card__back-btn svg{height:16px;width:16px}.ai-chat-booking-card__back-btn:hover{background:rgba(0,0,0,.1);color:var(--text-primary,#3e3e3e)}.ai-chat-widget.dark .ai-chat-booking-card__back-btn,.chakra-ui-dark .ai-chat-booking-card__back-btn,.dark .ai-chat-booking-card__back-btn,[data-theme=dark] .ai-chat-booking-card__back-btn{background:hsla(0,0%,100%,.08);color:var(--text-secondary,#a1a1aa)}.ai-chat-widget.dark .ai-chat-booking-card__back-btn:hover,.chakra-ui-dark .ai-chat-booking-card__back-btn:hover,.dark .ai-chat-booking-card__back-btn:hover,[data-theme=dark] .ai-chat-booking-card__back-btn:hover{background:hsla(0,0%,100%,.15);color:#fff}.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__status-icon{align-items:center;border-radius:50%;display:flex;flex-shrink:0;height:20px;justify-content:center;position:relative;width:20px}.ai-chat-booking-card__status-icon--pending{background:#6b7280}.ai-chat-booking-card__status-icon--pending:before{background:#fff;border-radius:1px;content:\"\";height:6px;position:absolute;top:5px;width:2px}.ai-chat-booking-card__status-icon--pending:after{background:#fff;border-radius:1px;content:\"\";height:2px;left:9px;position:absolute;top:9px;width:4px}.ai-chat-booking-card__status-icon--cancelled{background:#6b7280}.ai-chat-booking-card__status-icon--cancelled:after,.ai-chat-booking-card__status-icon--cancelled:before{background:#fff;border-radius:1px;content:\"\";height:2px;position:absolute;width:10px}.ai-chat-booking-card__status-icon--cancelled:before{transform:rotate(45deg)}.ai-chat-booking-card__status-icon--cancelled:after{transform:rotate(-45deg)}.ai-chat-booking-card__status-icon--error{background:#6b7280}.ai-chat-booking-card__status-icon--error:after{color:#fff;content:\"!\";font-size:12px;font-weight:700}.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__warning{background:rgba(0,0,0,.04);border-radius:8px;color:var(--text-secondary,#6b7280);font-size:13px;margin:0;padding:10px 12px}.ai-chat-widget.dark .ai-chat-booking-card__warning,.chakra-ui-dark .ai-chat-booking-card__warning,.dark .ai-chat-booking-card__warning,[data-theme=dark] .ai-chat-booking-card__warning{background:hsla(0,0%,100%,.06);color:var(--text-secondary,#a1a1aa)}.ai-chat-booking-card__input{background:rgba(0,0,0,.05);border:none;border-radius:8px;box-sizing:border-box;color:var(--text-primary,#3e3e3e);font-family:inherit;font-size:14px;outline:none;padding:10px 12px;transition:background .15s ease,box-shadow .15s ease;width:100%}.ai-chat-booking-card__input:focus{background:rgba(0,0,0,.08);box-shadow:0 0 0 2px var(--action-accent,var(--primary-color,#3b82f6))}.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:hsla(0,0%,100%,.08);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{background:hsla(0,0%,100%,.12)}.ai-chat-booking-card__field{display:flex;flex-direction:column;gap:8px}.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 .15s 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){filter:brightness(1.1);transform:translateY(-1px)}.ai-chat-booking-card__btn--secondary{background:rgba(0,0,0,.06);color:var(--text-primary,#3e3e3e)}.ai-chat-booking-card__btn--secondary:hover:not(:disabled){background:rgba(0,0,0,.1)}.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%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-booking-card__btn--secondary:hover:not(:disabled),.chakra-ui-dark .ai-chat-booking-card__btn--secondary:hover:not(:disabled),.dark .ai-chat-booking-card__btn--secondary:hover:not(:disabled),[data-theme=dark] .ai-chat-booking-card__btn--secondary:hover:not(:disabled){background:hsla(0,0%,100%,.15)}.ai-chat-booking-card__btn--danger{background:rgba(220,38,38,.1);color:#dc2626}.ai-chat-booking-card__btn--danger:hover:not(:disabled){background:rgba(220,38,38,.18)}.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);color:#fca5a5}.ai-chat-booking-card__btn--inline{font-size:12px;margin-top:8px;padding:6px 12px;width:auto}.ai-chat-booking-card__options{display:flex;flex-direction:column;gap:8px}.ai-chat-booking-card__option-btn{align-items:center;background:rgba(0,0,0,.04);border:none;border-radius:10px;box-sizing:border-box;cursor:pointer;display:flex;font-family:inherit;gap:12px;padding:14px 16px;text-align:left;transition:background .15s ease;width:100%}.ai-chat-booking-card__option-btn:hover:not(:disabled){background:rgba(0,0,0,.08)}.ai-chat-booking-card__option-btn:disabled{cursor:not-allowed;opacity:.5}.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:hsla(0,0%,100%,.06)}.ai-chat-widget.dark .ai-chat-booking-card__option-btn:hover:not(:disabled),.chakra-ui-dark .ai-chat-booking-card__option-btn:hover:not(:disabled),.dark .ai-chat-booking-card__option-btn:hover:not(:disabled),[data-theme=dark] .ai-chat-booking-card__option-btn:hover:not(:disabled){background:hsla(0,0%,100%,.1)}.ai-chat-booking-card__option-icon{align-items:center;background:var(--action-accent,var(--primary-color,#3b82f6));border-radius:8px;color:#fff;display:flex;flex-shrink:0;height:32px;justify-content:center;width:32px}.ai-chat-booking-card__option-icon svg{height:16px;width:16px}.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__list{display:flex;flex-direction:column;gap:6px}.ai-chat-booking-card__list-item{align-items:center;background:rgba(0,0,0,.04);border:none;border-radius:10px;box-sizing:border-box;cursor:pointer;display:flex;font-family:inherit;justify-content:space-between;padding:12px 14px;text-align:left;transition:background .15s ease;width:100%}.ai-chat-booking-card__list-item:hover{background:rgba(0,0,0,.08)}.ai-chat-widget.dark .ai-chat-booking-card__list-item,.chakra-ui-dark .ai-chat-booking-card__list-item,.dark .ai-chat-booking-card__list-item,[data-theme=dark] .ai-chat-booking-card__list-item{background:hsla(0,0%,100%,.06)}.ai-chat-widget.dark .ai-chat-booking-card__list-item:hover,.chakra-ui-dark .ai-chat-booking-card__list-item:hover,.dark .ai-chat-booking-card__list-item:hover,[data-theme=dark] .ai-chat-booking-card__list-item:hover{background:hsla(0,0%,100%,.1)}.ai-chat-booking-card__list-item-content{display:flex;flex-direction:column;gap:2px}.ai-chat-booking-card__list-item-name{color:var(--text-primary,#3e3e3e);font-size:14px;font-weight:500}.ai-chat-widget.dark .ai-chat-booking-card__list-item-name,.chakra-ui-dark .ai-chat-booking-card__list-item-name,.dark .ai-chat-booking-card__list-item-name,[data-theme=dark] .ai-chat-booking-card__list-item-name{color:#fff}.ai-chat-booking-card__list-item-role{color:var(--text-muted,#71717a);font-size:12px}.ai-chat-booking-card__list-item-arrow{flex-shrink:0;height:16px;position:relative;width:16px}.ai-chat-booking-card__list-item-arrow:before{border-width:medium;border-bottom:2px solid var(--text-muted,#9ca3af);border-left:0 solid var(--text-muted,#9ca3af);border-right:2px solid var(--text-muted,#9ca3af);border-top:0 solid var(--text-muted,#9ca3af);content:\"\";height:6px;left:50%;position:absolute;top:50%;transform:translate(-70%,-50%) rotate(-45deg);width:6px}.ai-chat-booking-card__slots-container{display:flex;flex-direction:column;gap:12px;max-height:280px;overflow-y:auto}.ai-chat-booking-card__date-group{display:flex;flex-direction:column;gap:8px}.ai-chat-booking-card__date-header{color:var(--text-muted,#71717a);font-size:12px;font-weight:600;letter-spacing:.5px;margin:0;padding-left:2px;text-transform:uppercase}.ai-chat-booking-card__slots{display:flex;flex-wrap:wrap;gap:6px}.ai-chat-booking-card__slot{background:rgba(0,0,0,.05);border:none;border-radius:20px;color:var(--text-primary,#3e3e3e);cursor:pointer;flex:0 0 auto;font-family:inherit;font-size:13px;font-weight:500;padding:8px 14px;transition:background .15s ease,transform .1s ease;white-space:nowrap}.ai-chat-booking-card__slot:hover:not(:disabled){background:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-booking-card__slot:active:not(:disabled){transform:scale(.97)}.ai-chat-booking-card__slot:disabled{cursor:not-allowed;opacity:.5}.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:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-booking-card__slot:hover:not(:disabled),.chakra-ui-dark .ai-chat-booking-card__slot:hover:not(:disabled),.dark .ai-chat-booking-card__slot:hover:not(:disabled),[data-theme=dark] .ai-chat-booking-card__slot:hover:not(:disabled){background:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-booking-card__subject-options{display:flex;flex-direction:column;gap:6px}.ai-chat-booking-card__subject-option{background:rgba(0,0,0,.04);border:none;border-radius:10px;color:var(--text-primary,#3e3e3e);cursor:pointer;font-family:inherit;font-size:14px;font-weight:500;padding:12px 14px;text-align:left;transition:background .15s ease,filter .15s ease}.ai-chat-booking-card__subject-option:hover:not(.ai-chat-booking-card__subject-option--selected){background:rgba(0,0,0,.08)}.ai-chat-booking-card__subject-option--selected,.ai-chat-booking-card__subject-option--selected:hover{background:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-booking-card__subject-option--selected:hover{filter:brightness(1.1)}.ai-chat-widget.dark .ai-chat-booking-card__subject-option,.chakra-ui-dark .ai-chat-booking-card__subject-option,.dark .ai-chat-booking-card__subject-option,[data-theme=dark] .ai-chat-booking-card__subject-option{background:hsla(0,0%,100%,.06);color:#fff}.ai-chat-widget.dark .ai-chat-booking-card__subject-option:hover:not(.ai-chat-booking-card__subject-option--selected),.chakra-ui-dark .ai-chat-booking-card__subject-option:hover:not(.ai-chat-booking-card__subject-option--selected),.dark .ai-chat-booking-card__subject-option:hover:not(.ai-chat-booking-card__subject-option--selected),[data-theme=dark] .ai-chat-booking-card__subject-option:hover:not(.ai-chat-booking-card__subject-option--selected){background:hsla(0,0%,100%,.1)}.ai-chat-widget.dark .ai-chat-booking-card__subject-option--selected,.ai-chat-widget.dark .ai-chat-booking-card__subject-option--selected:hover,.chakra-ui-dark .ai-chat-booking-card__subject-option--selected,.chakra-ui-dark .ai-chat-booking-card__subject-option--selected:hover,.dark .ai-chat-booking-card__subject-option--selected,.dark .ai-chat-booking-card__subject-option--selected:hover,[data-theme=dark] .ai-chat-booking-card__subject-option--selected,[data-theme=dark] .ai-chat-booking-card__subject-option--selected:hover{background:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-booking-card__appointments{display:flex;flex-direction:column;gap:8px}.ai-chat-booking-card__appointment{background:rgba(0,0,0,.04);border-radius:10px;padding:12px 14px}.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:hsla(0,0%,100%,.06)}.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:10px;font-size:11px;font-weight:600;padding:3px 8px;text-transform:uppercase}.ai-chat-booking-card__appointment-status--confirmed{background:rgba(59,130,246,.15);color:#3b82f6}.ai-chat-booking-card__appointment-status--pending{background:hsla(220,9%,46%,.15);color:#6b7280}.ai-chat-widget.dark .ai-chat-booking-card__appointment-status--confirmed,.chakra-ui-dark .ai-chat-booking-card__appointment-status--confirmed,.dark .ai-chat-booking-card__appointment-status--confirmed,[data-theme=dark] .ai-chat-booking-card__appointment-status--confirmed{background:rgba(59,130,246,.25);color:#60a5fa}.ai-chat-widget.dark .ai-chat-booking-card__appointment-status--pending,.chakra-ui-dark .ai-chat-booking-card__appointment-status--pending,.dark .ai-chat-booking-card__appointment-status--pending,[data-theme=dark] .ai-chat-booking-card__appointment-status--pending{background:hsla(220,9%,46%,.25);color:#9ca3af}.ai-chat-booking-card__appointment-status--cancelled{background:hsla(220,9%,46%,.15);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__appointment-info{display:flex;flex-direction:column;gap:4px;margin-bottom:10px}.ai-chat-booking-card__appointment-actions{display:flex;flex-wrap:wrap;gap:8px}@media (max-width:380px){.ai-chat-booking-card__appointment-actions{flex-direction:column}.ai-chat-booking-card__appointment-cancel,.ai-chat-booking-card__appointment-link{justify-content:center;text-align:center;width:100%}}.ai-chat-booking-card__appointment-link{background:var(--action-accent,var(--primary-color,#3b82f6));border-radius:6px;color:#fff;display:inline-block;font-size:12px;font-weight:500;padding:6px 12px;text-decoration:none;transition:filter .15s ease}.ai-chat-booking-card__appointment-link:hover{filter:brightness(1.1);text-decoration:none}.ai-chat-booking-card__appointment-cancel{background:rgba(220,38,38,.1);border:none;border-radius:6px;color:#dc2626;cursor:pointer;font-family:inherit;font-size:12px;font-weight:500;padding:6px 12px;transition:background .15s ease}.ai-chat-booking-card__appointment-cancel:hover:not(:disabled){background:rgba(220,38,38,.18)}.ai-chat-booking-card__appointment-cancel:disabled{cursor:not-allowed;opacity:.5}.ai-chat-widget.dark .ai-chat-booking-card__appointment-cancel,.chakra-ui-dark .ai-chat-booking-card__appointment-cancel,.dark .ai-chat-booking-card__appointment-cancel,[data-theme=dark] .ai-chat-booking-card__appointment-cancel{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-booking-card__summary{background:rgba(0,0,0,.04);border-radius:10px;padding:12px 14px}.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:hsla(0,0%,100%,.06)}.ai-chat-booking-card__summary-row{align-items:center;display:flex;gap:8px;justify-content:space-between;padding:6px 0}@media (max-width:380px){.ai-chat-booking-card__summary-row{align-items:flex-start;flex-direction:column;gap:2px}.ai-chat-booking-card__summary-value{text-align:left}}.ai-chat-booking-card__summary-row:not(:last-child){border-bottom:1px solid 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__success-icon{align-items:center;background:var(--action-accent,var(--primary-color,#3b82f6));border-radius:50%;display:flex;height:40px;justify-content:center;margin:0 auto 12px;position:relative;width:40px}.ai-chat-booking-card__success-icon:after{border:solid #fff;border-width:0 3px 3px 0;content:\"\";height:16px;margin-bottom:4px;transform:rotate(45deg);width:10px}.ai-chat-booking-card__success-message{color:var(--text-secondary,#6b7280);font-size:14px;margin:0 0 12px;text-align:center}.ai-chat-widget.dark .ai-chat-booking-card__success-message,.chakra-ui-dark .ai-chat-booking-card__success-message,.dark .ai-chat-booking-card__success-message,[data-theme=dark] .ai-chat-booking-card__success-message{color:var(--text-secondary,#a1a1aa)}.ai-chat-booking-card__link{display:inline-block;font-size:13px;font-weight:500;margin-top:8px;text-decoration:none;transition:opacity .15s ease}.ai-chat-booking-card__link:hover{opacity:.8;text-decoration:underline}.ai-chat-booking-card--success{background:rgba(59,130,246,.08)}.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(59,130,246,.12)}.ai-chat-booking-card--pending{background:hsla(220,9%,46%,.08)}.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:hsla(220,9%,46%,.12)}.ai-chat-booking-card--cancelled{background:hsla(220,9%,46%,.08)}.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%,.12)}.ai-chat-booking-card--error{background:hsla(220,9%,46%,.08)}.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:hsla(220,9%,46%,.12)}.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{background:rgba(220,38,38,.08);border-radius:8px;color:#dc2626;font-size:13px;margin:0;padding:10px 12px}.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,.15);color:#fca5a5}.ai-chat-booking-card--closable{position:relative}.ai-chat-booking-card--closable .ai-chat-booking-card__header{padding-right:40px}.ai-chat-pin-input-group{display:flex;flex-wrap:wrap;gap:8px;justify-content:center}@media (max-width:320px){.ai-chat-pin-input-group{gap:6px}}.ai-chat-pin-input{background:rgba(0,0,0,.05);border:none;border-radius:8px;box-sizing:border-box;color:var(--text-primary,#3e3e3e);font-family:inherit;font-size:20px;font-weight:600;height:48px;outline:none;padding:0;text-align:center;transition:background .15s ease,box-shadow .15s ease;width:42px}@media (max-width:320px){.ai-chat-pin-input{font-size:18px;height:42px;width:36px}}.ai-chat-pin-input:focus{background:rgba(0,0,0,.08);box-shadow:0 0 0 2px var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-pin-input::placeholder{color:var(--text-muted,#9ca3af)}.ai-chat-pin-input:disabled{cursor:not-allowed;opacity:.5}.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:hsla(0,0%,100%,.08);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{background:hsla(0,0%,100%,.12)}@media (max-width:480px){.ai-chat-booking-card{padding:12px}.ai-chat-booking-card__title{font-size:14px}.ai-chat-booking-card__option-btn{padding:12px 14px}.ai-chat-booking-card__option-icon{height:28px;width:28px}.ai-chat-booking-card__option-icon svg{height:14px;width:14px}.ai-chat-booking-card__option-text{font-size:13px}.ai-chat-booking-card__slots-container{max-height:240px}.ai-chat-booking-card__slot{font-size:12px;padding:6px 12px}}.ai-chat-structured-image{--action-accent:var(--primary-color,#3b82f6);display:flex;flex-direction:column;gap:8px;width:100%}.ai-chat-structured-image__context{color:var(--text-primary,#3e3e3e);font-size:13px;line-height:1.4;margin:0 0 4px}.ai-chat-widget.dark .ai-chat-structured-image__context,.chakra-ui-dark .ai-chat-structured-image__context,.dark .ai-chat-structured-image__context,[data-theme=dark] .ai-chat-structured-image__context{color:var(--text-primary,#fff)}.ai-chat-structured-image__error{background:rgba(239,68,68,.1);border-radius:var(--radius-md,8px);color:#dc2626;font-size:13px;padding:12px 16px;text-align:center}.ai-chat-widget.dark .ai-chat-structured-image__error,.chakra-ui-dark .ai-chat-structured-image__error,.dark .ai-chat-structured-image__error,[data-theme=dark] .ai-chat-structured-image__error{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-structured-image__cards{display:grid;gap:8px;grid-template-columns:repeat(2,1fr)}.ai-chat-structured-image__cards:has(.ai-chat-project-card:only-child){grid-template-columns:1fr}@supports not selector(:has(*)){.ai-chat-structured-image__cards{grid-template-columns:repeat(var(--card-columns,2),1fr)}}@media (max-width:480px){.ai-chat-structured-image__cards{grid-template-columns:1fr}}.ai-chat-media-display{--action-accent:var(--primary-color,#3b82f6);display:flex;flex-direction:column;gap:8px;margin-top:4px;padding:0!important}.ai-chat-media-display__context{color:var(--text-muted,#71717a);font-size:13px;margin:0 0 4px;padding:0 4px}.ai-chat-media-display__error{background:rgba(239,68,68,.1);border-radius:var(--radius-lg,12px);color:#dc2626;font-size:13px;padding:16px;text-align:center}.ai-chat-widget.dark .ai-chat-media-display__error,.chakra-ui-dark .ai-chat-media-display__error,.dark .ai-chat-media-display__error,[data-theme=dark] .ai-chat-media-display__error{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-media-display__cards{display:grid;gap:12px;grid-template-columns:repeat(var(--card-columns,1),1fr)}.ai-chat-image-card{--action-accent:var(--primary-color,#3b82f6);background:transparent;display:flex;flex-direction:column}.ai-chat-image-card,.ai-chat-image-card__media{border-radius:var(--radius-lg,12px);overflow:hidden}.ai-chat-image-card__media{position:relative;width:100%}.ai-chat-image-card__image{border-radius:var(--radius-lg,12px);display:block;height:auto;max-height:300px;object-fit:cover;width:100%}.ai-chat-image-card__image-error{align-items:center;background:var(--bg-muted,#e5e7eb);border-radius:var(--radius-lg,12px);color:var(--text-muted,#71717a);display:flex;font-size:13px;height:150px;justify-content:center;width:100%}.ai-chat-image-card__content{padding:10px 0 0}.ai-chat-image-card__title{color:var(--text-primary,#1a1a1a);font-size:14px;font-weight:600;line-height:1.3;margin:0 0 4px}.ai-chat-widget.dark .ai-chat-image-card__title,.dark .ai-chat-image-card__title,[data-theme=dark] .ai-chat-image-card__title{color:#fff}.ai-chat-image-card__description{color:var(--text-muted,#6b7280);font-size:13px;line-height:1.4;margin:0 0 8px}.ai-chat-image-card__link-btn{align-items:center;background:transparent;border:none;color:var(--action-accent);cursor:pointer;display:inline-flex;font-size:13px;font-weight:500;gap:4px;padding:0;transition:opacity .2s}.ai-chat-image-card__link-btn:hover{opacity:.8}.ai-chat-media-card{--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);display:flex;flex-direction:column;overflow:hidden}.ai-chat-widget.dark .ai-chat-media-card,.chakra-ui-dark .ai-chat-media-card,.dark .ai-chat-media-card,[data-theme=dark] .ai-chat-media-card{background:var(--bg-secondary,#3a3a3a);border-color:hsla(0,0%,100%,.08)}.ai-chat-media-card__media{background:#000;position:relative;width:100%}.ai-chat-media-card__media.video{aspect-ratio:16/9}.ai-chat-media-card__media.image{aspect-ratio:auto;max-height:400px}.ai-chat-media-card__image{display:block;height:100%;object-fit:cover;width:100%}.ai-chat-media-card__image-error{align-items:center;background:var(--bg-muted,#e5e7eb);color:var(--text-muted,#71717a);display:flex;font-size:13px;height:200px;justify-content:center;width:100%}.ai-chat-media-card__video-placeholder{cursor:pointer;height:100%;position:relative;width:100%}.ai-chat-media-card__video-placeholder img{height:100%;object-fit:cover;width:100%}.ai-chat-media-card__video-bg{background:linear-gradient(135deg,#1a1a2e,#16213e);height:100%;width:100%}.ai-chat-media-card__play-btn{align-items:center;background:rgba(0,0,0,.7);border:none;border-radius:50%;color:#fff;cursor:pointer;display:flex;height:56px;justify-content:center;left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);transition:background .2s,transform .2s;width:56px}.ai-chat-media-card__play-btn:hover{background:rgba(0,0,0,.9);transform:translate(-50%,-50%) scale(1.05)}.ai-chat-media-card__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-media-card__iframe,.ai-chat-media-card__video{border:none;height:100%;left:0;position:absolute;top:0;width:100%}.ai-chat-media-card__content{padding:12px}.ai-chat-media-card__title{color:var(--text-primary,#3e3e3e);font-size:15px;font-weight:600;line-height:1.3;margin:0 0 4px}.ai-chat-widget.dark .ai-chat-media-card__title,.chakra-ui-dark .ai-chat-media-card__title,.dark .ai-chat-media-card__title,[data-theme=dark] .ai-chat-media-card__title{color:#fff}.ai-chat-media-card__description{color:var(--text-muted,#71717a);font-size:13px;line-height:1.4;margin:0 0 8px}.ai-chat-media-card__link-btn{align-items:center;background:var(--action-accent);border:none;border-radius:6px;color:#fff;cursor:pointer;display:inline-flex;font-size:13px;font-weight:500;gap:6px;padding:6px 12px;transition:opacity .2s}.ai-chat-media-card__link-btn:hover{opacity:.9}.ai-chat-image-gallery{--action-accent:var(--primary-color,#3b82f6)}.ai-chat-image-gallery__grid{display:grid;gap:8px;grid-template-columns:repeat(2,1fr)}@media (max-width:480px){.ai-chat-image-gallery__grid{grid-template-columns:1fr}.ai-chat-image-gallery__grid .ai-chat-image-gallery__item:first-child{grid-column:span 1}}.ai-chat-image-gallery__grid:has(.ai-chat-image-gallery__item:only-child){grid-template-columns:1fr}.ai-chat-image-gallery__grid:has(.ai-chat-image-gallery__item:nth-child(3):last-child) .ai-chat-image-gallery__item:first-child{grid-column:span 2}.ai-chat-image-gallery__grid:has(.ai-chat-image-gallery__item:nth-child(5):last-child) .ai-chat-image-gallery__item:first-child{grid-column:span 2}.ai-chat-image-gallery__grid:has(.ai-chat-image-gallery__item:nth-child(7):last-child) .ai-chat-image-gallery__item:first-child{grid-column:span 2}.ai-chat-image-gallery__item.span-full{grid-column:span 2}.ai-chat-image-gallery[data-item-count=\"1\"] .ai-chat-image-gallery__grid{grid-template-columns:1fr}.ai-chat-image-gallery__item{position:relative}.ai-chat-image-gallery__item-media{aspect-ratio:4/3;background:var(--bg-muted,#e5e7eb);border-radius:var(--radius-lg,12px);overflow:hidden;position:relative}.ai-chat-image-gallery__item-media img{height:100%;object-fit:cover;width:100%}.ai-chat-image-gallery__item-overlay{background:linear-gradient(0deg,rgba(0,0,0,.85) 0,rgba(0,0,0,.4) 60%,transparent);border-radius:0 0 var(--radius-lg,12px) var(--radius-lg,12px);bottom:0;left:0;padding:24px 12px 10px;position:absolute;right:0}.ai-chat-image-gallery__item-title{-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;color:#fff;display:-webkit-box;font-size:12px;font-weight:600;line-height:1.3;margin:0;overflow:hidden;text-shadow:0 1px 3px rgba(0,0,0,.5)}.ai-chat-image-gallery__error{align-items:center;color:var(--text-muted,#71717a);display:flex;font-size:12px;height:100%;justify-content:center;width:100%}.ai-chat-media-gallery{--action-accent:var(--primary-color,#3b82f6)}.ai-chat-media-gallery__grid{display:grid;gap:8px;grid-template-columns:repeat(2,1fr)}.ai-chat-image-gallery__lightbox,.ai-chat-media-gallery__lightbox{align-items:center;background:rgba(0,0,0,.95);bottom:0;display:flex;justify-content:center;left:0;padding:40px;position:fixed;right:0;top:0;z-index:10000}.ai-chat-image-gallery__lightbox-close,.ai-chat-media-gallery__lightbox-close{align-items:center;background:hsla(0,0%,100%,.1);border:none;border-radius:50%;color:#fff;cursor:pointer;display:flex;height:40px;justify-content:center;position:absolute;right:16px;top:16px;transition:background .2s;width:40px}.ai-chat-image-gallery__lightbox-close:hover,.ai-chat-media-gallery__lightbox-close:hover{background:hsla(0,0%,100%,.2)}.ai-chat-image-gallery__lightbox-nav,.ai-chat-media-gallery__lightbox-nav{align-items:center;background:hsla(0,0%,100%,.1);border:none;border-radius:50%;color:#fff;cursor:pointer;display:flex;height:48px;justify-content:center;position:absolute;top:50%;transform:translateY(-50%);transition:background .2s;width:48px}.ai-chat-image-gallery__lightbox-nav:hover,.ai-chat-media-gallery__lightbox-nav:hover{background:hsla(0,0%,100%,.2)}.ai-chat-image-gallery__lightbox-nav.prev,.ai-chat-media-gallery__lightbox-nav.prev{left:16px}.ai-chat-image-gallery__lightbox-nav.next,.ai-chat-media-gallery__lightbox-nav.next{right:16px}.ai-chat-image-gallery__lightbox-content,.ai-chat-media-gallery__lightbox-content{align-items:center;display:flex;flex-direction:column;max-height:80vh;max-width:90vw}.ai-chat-image-gallery__lightbox-content img,.ai-chat-media-gallery__lightbox-content img{border-radius:4px;max-height:70vh;max-width:100%;object-fit:contain}.ai-chat-image-gallery__lightbox-caption,.ai-chat-media-gallery__lightbox-caption{color:#fff;margin-top:16px;text-align:center}.ai-chat-image-gallery__lightbox-caption h4,.ai-chat-media-gallery__lightbox-caption h4{font-size:16px;font-weight:600;margin:0 0 4px}.ai-chat-image-gallery__lightbox-caption p,.ai-chat-media-gallery__lightbox-caption p{color:hsla(0,0%,100%,.7);font-size:14px;margin:0}.ai-chat-image-gallery__lightbox-counter,.ai-chat-media-gallery__lightbox-counter{background:rgba(0,0,0,.6);border-radius:20px;bottom:16px;color:#fff;font-size:13px;left:50%;padding:6px 12px;position:absolute;transform:translateX(-50%)}.ai-chat-project-card{--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);display:flex;flex-direction:column;overflow:hidden;transition:border-color .2s,box-shadow .2s,transform .2s}.ai-chat-project-card.clickable{cursor:pointer}.ai-chat-project-card.clickable:hover{border-color:var(--action-accent);box-shadow:0 4px 12px rgba(0,0,0,.1);transform:translateY(-2px)}.ai-chat-widget.dark .ai-chat-project-card,.chakra-ui-dark .ai-chat-project-card,.dark .ai-chat-project-card,[data-theme=dark] .ai-chat-project-card{background:var(--bg-secondary,#2a2a2a);border-color:hsla(0,0%,100%,.1)}.ai-chat-widget.dark .ai-chat-project-card.clickable:hover,.chakra-ui-dark .ai-chat-project-card.clickable:hover,.dark .ai-chat-project-card.clickable:hover,[data-theme=dark] .ai-chat-project-card.clickable:hover{border-color:var(--action-accent);box-shadow:0 4px 16px rgba(0,0,0,.3)}.ai-chat-project-card--image-only{background:transparent;border:none}.ai-chat-project-card--image-only .ai-chat-project-card__media{aspect-ratio:16/9;border-radius:var(--radius-lg,12px)}.ai-chat-project-card--image-only.clickable:hover{border:none;box-shadow:0 4px 16px rgba(0,0,0,.2)}.ai-chat-project-card--overlay{background:transparent;border:none;border-radius:var(--radius-lg,12px);overflow:hidden}.ai-chat-project-card--overlay .ai-chat-project-card__media{aspect-ratio:4/3;border-radius:var(--radius-lg,12px)}.ai-chat-project-card__overlay{background:linear-gradient(0deg,rgba(0,0,0,.85) 0,rgba(0,0,0,.5) 50%,transparent);border-radius:0 0 var(--radius-lg,12px) var(--radius-lg,12px);bottom:0;left:0;padding:10px 12px;position:absolute;right:0}.ai-chat-project-card__overlay-title{-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;color:#fff;display:-webkit-box;font-size:12px;font-weight:600;line-height:1.3;margin:0;overflow:hidden;text-shadow:0 1px 3px rgba(0,0,0,.5)}.ai-chat-project-card--full .ai-chat-project-card__media{aspect-ratio:16/10;border-bottom:1px solid var(--border-subtle,rgba(0,0,0,.06))}.ai-chat-widget.dark .ai-chat-project-card--full .ai-chat-project-card__media,.chakra-ui-dark .ai-chat-project-card--full .ai-chat-project-card__media,.dark .ai-chat-project-card--full .ai-chat-project-card__media,[data-theme=dark] .ai-chat-project-card--full .ai-chat-project-card__media{border-bottom-color:hsla(0,0%,100%,.08)}.ai-chat-project-card__media{aspect-ratio:16/10;background:var(--bg-muted,#e5e7eb);overflow:hidden;position:relative;width:100%}.ai-chat-widget.dark .ai-chat-project-card__media,.chakra-ui-dark .ai-chat-project-card__media,.dark .ai-chat-project-card__media,[data-theme=dark] .ai-chat-project-card__media{background:hsla(0,0%,100%,.05)}.ai-chat-project-card__media img{height:100%;object-fit:cover;transition:transform .3s ease;width:100%}.ai-chat-project-card.clickable:hover .ai-chat-project-card__media img{transform:scale(1.03)}.ai-chat-project-card__iframe,.ai-chat-project-card__video{border:none;height:100%;left:0;position:absolute;top:0;width:100%}.ai-chat-project-card__placeholder{align-items:center;color:var(--text-muted,#71717a);display:flex;font-size:13px;height:100%;justify-content:center;width:100%}.ai-chat-project-card__play-btn{align-items:center;background:rgba(0,0,0,.7);border:none;border-radius:50%;color:#fff;cursor:pointer;display:flex;height:48px;justify-content:center;left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);transition:background .2s,transform .2s;width:48px}.ai-chat-project-card__play-btn:hover{background:rgba(0,0,0,.9);transform:translate(-50%,-50%) scale(1.1)}.ai-chat-project-card__content{background:var(--bg-secondary,#f4f4f4);padding:12px 14px 14px}.ai-chat-widget.dark .ai-chat-project-card__content,.chakra-ui-dark .ai-chat-project-card__content,.dark .ai-chat-project-card__content,[data-theme=dark] .ai-chat-project-card__content{background:var(--bg-secondary,#2a2a2a)}.ai-chat-project-card__title{color:var(--text-primary,#1a1a1a);font-size:14px;font-weight:600;line-height:1.35;margin:0 0 6px}.ai-chat-widget.dark .ai-chat-project-card__title,.chakra-ui-dark .ai-chat-project-card__title,.dark .ai-chat-project-card__title,[data-theme=dark] .ai-chat-project-card__title{color:#fff}.ai-chat-project-card__description{-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;color:var(--text-muted,#6b7280);display:-webkit-box;font-size:13px;line-height:1.45;margin:0 0 10px;overflow:hidden}.ai-chat-project-card__link{align-items:center;color:var(--action-accent);display:inline-flex;font-size:13px;font-weight:500;gap:4px;transition:opacity .2s}.ai-chat-project-card__link:hover{opacity:.8}.ai-chat-media-carousel{--action-accent:var(--primary-color,#3b82f6);border-radius:var(--radius-lg,12px);overflow:hidden;position:relative}.ai-chat-media-carousel__track{display:flex;transition:transform .3s ease-out}.ai-chat-media-carousel__slide{flex:0 0 100%;min-width:0}.ai-chat-media-carousel__slide .ai-chat-project-card{border-radius:var(--radius-md,8px);margin:0 4px}.ai-chat-media-carousel__nav{align-items:center;background:hsla(0,0%,100%,.9);border:none;border-radius:50%;box-shadow:0 2px 8px rgba(0,0,0,.15);color:#333;cursor:pointer;display:flex;height:32px;justify-content:center;position:absolute;top:50%;transform:translateY(-50%);transition:background .2s;width:32px;z-index:2}.ai-chat-media-carousel__nav:hover{background:#fff}.ai-chat-media-carousel__nav.prev{left:8px}.ai-chat-media-carousel__nav.next{right:8px}.ai-chat-widget.dark .ai-chat-media-carousel__nav,.chakra-ui-dark .ai-chat-media-carousel__nav,.dark .ai-chat-media-carousel__nav,[data-theme=dark] .ai-chat-media-carousel__nav{background:rgba(0,0,0,.7);color:#fff}.ai-chat-widget.dark .ai-chat-media-carousel__nav:hover,.chakra-ui-dark .ai-chat-media-carousel__nav:hover,.dark .ai-chat-media-carousel__nav:hover,[data-theme=dark] .ai-chat-media-carousel__nav:hover{background:rgba(0,0,0,.9)}.ai-chat-media-carousel__dots{display:flex;gap:6px;justify-content:center;padding:12px 0 4px}.ai-chat-media-carousel__dot{background:var(--border-subtle,rgba(0,0,0,.2));border:none;border-radius:50%;cursor:pointer;height:8px;padding:0;transition:background .2s,transform .2s;width:8px}.ai-chat-media-carousel__dot:hover{transform:scale(1.2)}.ai-chat-media-carousel__dot.active{background:var(--action-accent)}.ai-chat-widget.dark .ai-chat-media-carousel__dot,.chakra-ui-dark .ai-chat-media-carousel__dot,.dark .ai-chat-media-carousel__dot,[data-theme=dark] .ai-chat-media-carousel__dot{background:hsla(0,0%,100%,.3)}.ai-chat-widget.dark .ai-chat-media-carousel__dot.active,.chakra-ui-dark .ai-chat-media-carousel__dot.active,.dark .ai-chat-media-carousel__dot.active,[data-theme=dark] .ai-chat-media-carousel__dot.active{background:var(--action-accent)}@media (max-width:480px){.ai-chat-media-carousel__nav{height:28px;width:28px}.ai-chat-media-carousel__nav.prev{left:4px}.ai-chat-media-carousel__nav.next{right:4px}.ai-chat-image-gallery__lightbox-nav,.ai-chat-media-gallery__lightbox-nav{height:40px;width:40px}.ai-chat-image-gallery__lightbox,.ai-chat-media-gallery__lightbox{padding:16px}.ai-chat-project-card__content{padding:10px 12px 12px}.ai-chat-project-card__title{font-size:13px}.ai-chat-project-card__description{font-size:12px}}.chat-fullpage{--fp-max-width:800px;--fp-padding-x:16px;--fp-padding-top-mobile:64px;--fp-padding-top-desktop:24px;--fp-padding-bottom:200px;--fp-input-bottom:0}.chat-fullpage .ai-chat-messages{background:transparent;flex:1 1 auto;height:auto;justify-content:flex-start;margin:0 auto;max-height:none;max-width:var(--fp-max-width);min-height:0!important;overflow-x:hidden!important;overflow-y:scroll!important;padding:var(--fp-padding-top-desktop) var(--fp-padding-x) var(--fp-padding-bottom);position:relative}@media (max-width:767px){.chat-fullpage .ai-chat-messages{padding-top:var(--fp-padding-top-mobile)!important}}@media (max-width:480px){.chat-fullpage .ai-chat-messages{padding-top:calc(var(--fp-padding-top-mobile) + env(safe-area-inset-top, 0px))}}.chat-fullpage .ai-chat-messages>.ai-chat-message:first-child,.chat-fullpage .ai-chat-messages>.ai-chat-welcome:first-child{margin-top:0}.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:12px 0 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:110px}}.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}.chat-fullpage .ai-chat-message-meta{align-items:center;display:flex;gap:8px;margin-top:4px;padding:0 16px}.chat-fullpage .ai-chat-message-timestamp{color:var(--text-muted,#71717a);font-size:12px}.chat-fullpage .ai-chat-feedback{display:inline-flex}.chat-fullpage .ai-chat-feedback-button{display:flex}@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:not(.is-open).bottom-right .ai-chat-button{bottom:12px!important;right:12px!important}.ai-chat-widget-container:not(.is-open).bottom-left .ai-chat-button{bottom:12px!important;left:12px!important}.ai-chat-widget-container:not(.is-open).top-right .ai-chat-button{right:12px!important;top:12px!important}.ai-chat-widget-container:not(.is-open).top-left .ai-chat-button{left:12px!important;top:12px!important}.ai-chat-widget-container.trigger-pill-text.container-mode:not(.is-open).bottom-right .ai-chat-trigger-pill{bottom:12px!important;right:12px!important}.ai-chat-widget-container.trigger-pill-text.container-mode:not(.is-open).bottom-left .ai-chat-trigger-pill{bottom:12px!important;left:12px!important}.ai-chat-widget-container.trigger-pill-text.container-mode:not(.is-open).top-right .ai-chat-trigger-pill{right:12px!important;top:12px!important}.ai-chat-widget-container.trigger-pill-text.container-mode:not(.is-open).top-left .ai-chat-trigger-pill{left:12px!important;top:12px!important}.ai-chat-widget-container.trigger-input-bar.container-mode:not(.is-open) .ai-chat-trigger-input-container{bottom:12px!important;left:12px!important;right:12px!important;width:calc(100% - 24px)!important}.ai-chat-widget-container.trigger-input-bar:not(.is-open) .ai-chat-trigger-input-wrapper{max-width:100%!important;width:100%!important}.ai-chat-widget-container.trigger-input-bar:not(.is-open) .ai-chat-trigger-input-row{display:flex;gap:8px;max-width:calc(100vw - 24px)!important;width:calc(100vw - 24px)!important}.ai-chat-widget-container.trigger-input-bar.is-collapsed:not(.is-open) .ai-chat-trigger-input-container{bottom:12px!important;right:12px!important;width:auto!important}.ai-chat-widget-container.trigger-input-bar.is-collapsed:not(.is-open) .ai-chat-button{margin-left:0!important;margin-right:0!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%}.ai-chat-widget-container.is-open .ai-chat-message-meta{align-items:center;display:flex;gap:8px;margin-top:4px}.ai-chat-widget-container.is-open .ai-chat-message-timestamp{font-size:11px}.ai-chat-widget-container.is-open .ai-chat-feedback{display:inline-flex}.ai-chat-widget-container.is-open .ai-chat-feedback-button{display:flex;padding:4px}.ai-chat-widget-container.is-open .ai-chat-input{font-size:16px!important}}@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)}}.ai-chat-widget-container.desktop-mode.container-mode.is-open{height:100%!important;inset:0!important;position:absolute!important;width:100%!important}.ai-chat-widget-container.desktop-mode.container-mode.is-open .ai-chat-window{border:1px solid var(--border-color,rgba(0,0,0,.08))!important;border-radius:var(--radius-window-top,22px) var(--radius-window-top,22px) var(--radius-window-bottom,44px) var(--radius-window-bottom,44px)!important;box-shadow:var(--window-shadow,0 20px 60px -10px rgba(0,0,0,.25))!important;position:absolute!important}.ai-chat-widget-container.desktop-mode.container-mode.size-small.is-open .ai-chat-window{height:min(500px,calc(100% - 120px))!important;max-height:500px!important;max-width:380px!important;min-height:300px!important;min-width:280px!important;width:min(380px,calc(100% - 40px))!important}.ai-chat-widget-container.desktop-mode.container-mode.size-medium.is-open .ai-chat-window{height:min(65%,calc(100% - 120px))!important;max-height:680px!important;max-width:440px!important;min-height:400px!important;min-width:320px!important;width:min(440px,calc(100% - 40px))!important}.ai-chat-widget-container.desktop-mode.container-mode.size-large.is-open .ai-chat-window{height:calc(100% - 100px)!important;max-height:calc(100% - 100px)!important;max-width:560px!important;min-height:500px!important;min-width:380px!important;width:min(560px,calc(100% - 40px))!important}.ai-chat-widget-container.desktop-mode.container-mode.is-open.bottom-right .ai-chat-window{bottom:88px!important;left:auto!important;right:20px!important;top:auto!important}.ai-chat-widget-container.desktop-mode.container-mode.is-open.bottom-left .ai-chat-window{bottom:88px!important;left:20px!important;right:auto!important;top:auto!important}.ai-chat-widget-container.desktop-mode.container-mode.is-open.top-right .ai-chat-window{bottom:auto!important;left:auto!important;right:20px!important;top:88px!important}.ai-chat-widget-container.desktop-mode.container-mode.is-open.top-left .ai-chat-window{bottom:auto!important;left:20px!important;right:auto!important;top:88px!important}.ai-chat-widget-container.desktop-mode.is-open .ai-chat-button{display:flex!important;opacity:1!important;pointer-events:auto!important;visibility:visible!important}.ai-chat-widget-container.desktop-mode.container-mode:not(.trigger-input-bar).bottom-right .ai-chat-button{bottom:20px!important;left:auto!important;right:20px!important;top:auto!important}.ai-chat-widget-container.desktop-mode.container-mode:not(.trigger-input-bar).bottom-left .ai-chat-button{bottom:20px!important;left:20px!important;right:auto!important;top:auto!important}.ai-chat-widget-container.desktop-mode.container-mode:not(.trigger-input-bar).top-right .ai-chat-button{bottom:auto!important;left:auto!important;right:20px!important;top:20px!important}.ai-chat-widget-container.desktop-mode.container-mode:not(.trigger-input-bar).top-left .ai-chat-button{bottom:auto!important;left:20px!important;right:auto!important;top:20px!important}.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.bottom-right .ai-chat-trigger-input-container{bottom:20px!important;left:auto!important;right:20px!important;top:auto!important}.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.bottom-left .ai-chat-trigger-input-container{bottom:20px!important;left:20px!important;right:auto!important;top:auto!important}.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.top-right .ai-chat-trigger-input-container{bottom:auto!important;left:auto!important;right:20px!important;top:20px!important}.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.top-left .ai-chat-trigger-input-container{bottom:auto!important;left:20px!important;right:auto!important;top:20px!important}.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.size-small .ai-chat-trigger-input-container{width:min(380px,calc(100% - 40px))!important}.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.size-medium .ai-chat-trigger-input-container{width:min(440px,calc(100% - 40px))!important}.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.size-large .ai-chat-trigger-input-container{width:min(560px,calc(100% - 40px))!important}.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.is-open.bottom-left .ai-chat-window,.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.is-open.bottom-right .ai-chat-window{bottom:20px!important}.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.is-open.top-left .ai-chat-window,.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.is-open.top-right .ai-chat-window{top:20px!important}.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.is-open.bottom-right .ai-chat-window,.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.is-open.top-right .ai-chat-window{left:auto!important;right:20px!important}.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.is-open.bottom-left .ai-chat-window,.ai-chat-widget-container.desktop-mode.container-mode.trigger-input-bar.is-open.top-left .ai-chat-window{left:20px!important;right:auto!important}";
|
|
30413
31239
|
styleInject(css_248z$1);
|
|
30414
31240
|
|
|
30415
|
-
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{
|
|
31241
|
+
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}.ai-chat-widget-embedded .ai-chat-data-policy-view{-webkit-overflow-scrolling:touch;overflow-y:auto}.ai-chat-widget-embedded .ai-chat-data-policy-content{margin:0 auto;max-width:640px;overflow-y:visible;padding:32px 24px 60px}.ai-chat-widget-embedded .ai-chat-data-policy-intro p{font-size:14px;line-height:1.7}.ai-chat-widget-embedded .ai-chat-data-policy-section h3{font-size:15px;margin-bottom:14px}.ai-chat-widget-embedded .ai-chat-data-policy-section p{font-size:13px;line-height:1.7}.ai-chat-widget-embedded.dark .ai-chat-data-policy-intro p,.ai-chat-widget-embedded.dark .ai-chat-data-policy-intro strong,.ai-chat-widget-embedded.dark .ai-chat-data-policy-section h3{color:var(--text-primary,#fafafa)}.ai-chat-widget-embedded.dark .ai-chat-data-policy-section p{color:var(--text-secondary,#a1a1aa)}.ai-chat-widget-embedded.dark .ai-chat-data-policy-section p strong{color:var(--text-primary,#fafafa)}";
|
|
30416
31242
|
styleInject(css_248z);
|
|
30417
31243
|
|
|
30418
|
-
|
|
30419
|
-
const iconComponents = {
|
|
30420
|
-
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" }) })),
|
|
30421
|
-
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" }) })),
|
|
30422
|
-
};
|
|
30423
|
-
const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = false, previewConfig, position = 'bottom-right', primaryColor, size, headerTitle, welcomeTitle, welcomeMessage, placeholder, welcomeBubbleText, theme, suggestedQuestions, customStyles, currentRoute, defaultOpen = false, zIndex, containerMode = false, onOpen, onClose, onMessage, onError, mode = 'bubble', }) => {
|
|
31244
|
+
const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = false, previewConfig, position = 'bottom-right', primaryColor, size, headerTitle, welcomeTitle, welcomeMessage, placeholder, welcomeBubbleText, triggerType, triggerText, theme, suggestedQuestions, customStyles, currentRoute, defaultOpen = false, zIndex, containerMode = false, mobileMode = false, desktopMode = false, onOpen, onClose, onMessage, onError, mode = 'bubble', }) => {
|
|
30424
31245
|
const [isOpen, setIsOpen] = React.useState(defaultOpen);
|
|
30425
|
-
const [
|
|
31246
|
+
const [inputBarValue, setInputBarValue] = React.useState('');
|
|
30426
31247
|
const [showWelcomeBubble, setShowWelcomeBubble] = React.useState(false);
|
|
31248
|
+
const [isInputBarCollapsed, setIsInputBarCollapsed] = React.useState(false);
|
|
30427
31249
|
const widgetRef = React.useRef(null);
|
|
30428
31250
|
const containerRef = React.useRef(null);
|
|
30429
31251
|
// Determine mode
|
|
@@ -30472,13 +31294,14 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
30472
31294
|
},
|
|
30473
31295
|
};
|
|
30474
31296
|
// Always call useChat hook (React rules), but skip initialization in preview mode
|
|
31297
|
+
const shouldSkipInit = previewMode;
|
|
30475
31298
|
const chatHook = useChat({
|
|
30476
31299
|
widgetId: previewMode ? '__preview__' : (widgetId || '__preview__'),
|
|
30477
31300
|
apiUrl,
|
|
30478
31301
|
currentRoute,
|
|
30479
|
-
onMessage:
|
|
30480
|
-
onError:
|
|
30481
|
-
skipInitialization:
|
|
31302
|
+
onMessage: shouldSkipInit ? undefined : onMessage,
|
|
31303
|
+
onError: shouldSkipInit ? undefined : onError,
|
|
31304
|
+
skipInitialization: shouldSkipInit,
|
|
30482
31305
|
});
|
|
30483
31306
|
// Extract values from hook or use preview defaults
|
|
30484
31307
|
const messages = previewMode ? [] : chatHook.messages;
|
|
@@ -30487,37 +31310,32 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
30487
31310
|
const config = previewMode ? mergedPreviewConfig : chatHook.config;
|
|
30488
31311
|
const sendMessage = previewMode ? (() => Promise.resolve()) : chatHook.sendMessage;
|
|
30489
31312
|
const submitFeedback = previewMode ? (() => Promise.resolve()) : chatHook.submitFeedback;
|
|
31313
|
+
const dismissAction = previewMode ? (() => Promise.resolve()) : chatHook.dismissAction;
|
|
30490
31314
|
const conversations = previewMode ? [] : chatHook.conversations;
|
|
30491
31315
|
const loadConversations = previewMode ? (() => { }) : chatHook.loadConversations;
|
|
30492
31316
|
const switchConversation = previewMode ? (() => Promise.resolve()) : chatHook.switchConversation;
|
|
30493
31317
|
const startNewConversation = previewMode ? (() => { }) : chatHook.startNewConversation;
|
|
30494
31318
|
const deleteConversation = previewMode ? (() => { }) : chatHook.deleteConversation;
|
|
30495
31319
|
const conversationId = previewMode ? '' : chatHook.conversationId;
|
|
30496
|
-
|
|
30497
|
-
|
|
30498
|
-
|
|
30499
|
-
|
|
30500
|
-
|
|
30501
|
-
|
|
30502
|
-
|
|
30503
|
-
|
|
30504
|
-
|
|
30505
|
-
|
|
30506
|
-
|
|
30507
|
-
|
|
30508
|
-
|
|
30509
|
-
|
|
30510
|
-
|
|
30511
|
-
|
|
30512
|
-
|
|
30513
|
-
|
|
30514
|
-
|
|
30515
|
-
mediaQuery.addEventListener('change', handleMediaChange);
|
|
30516
|
-
return () => {
|
|
30517
|
-
observer.disconnect();
|
|
30518
|
-
mediaQuery.removeEventListener('change', handleMediaChange);
|
|
30519
|
-
};
|
|
30520
|
-
}, [config]);
|
|
31320
|
+
const callActionEndpoint = previewMode ? (async () => { throw new Error('Not available in preview mode'); }) : chatHook.callActionEndpoint;
|
|
31321
|
+
const { effectiveTheme, effectivePosition, accentColor, iconContrastColor, effectiveSize, effectiveHeaderTitle, effectiveWelcomeTitle, effectiveWelcomeMessage, effectivePlaceholder, effectiveWelcomeBubbleText, effectiveTriggerType, effectiveTriggerText, mergedStyles, } = useWidgetAppearance({
|
|
31322
|
+
containerRef,
|
|
31323
|
+
config,
|
|
31324
|
+
theme,
|
|
31325
|
+
primaryColor,
|
|
31326
|
+
position,
|
|
31327
|
+
size,
|
|
31328
|
+
headerTitle,
|
|
31329
|
+
welcomeTitle,
|
|
31330
|
+
welcomeMessage,
|
|
31331
|
+
placeholder,
|
|
31332
|
+
welcomeBubbleText,
|
|
31333
|
+
triggerType,
|
|
31334
|
+
triggerText,
|
|
31335
|
+
customStyles,
|
|
31336
|
+
zIndex,
|
|
31337
|
+
previewMode,
|
|
31338
|
+
});
|
|
30521
31339
|
// Check if device is mobile
|
|
30522
31340
|
const isMobile = typeof window !== 'undefined' && window.innerWidth <= 480;
|
|
30523
31341
|
// Handle auto-open (only for bubble mode, disabled on mobile)
|
|
@@ -30562,8 +31380,8 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
30562
31380
|
document.body.classList.remove('ai-chat-widget-open');
|
|
30563
31381
|
};
|
|
30564
31382
|
}, [isOpen, isEmbedded]);
|
|
30565
|
-
// Handle welcome bubble visibility
|
|
30566
|
-
//
|
|
31383
|
+
// Handle welcome bubble visibility based on frequency setting
|
|
31384
|
+
// Frequency options: 'always' (every page visit), 'session', 'weekly', 'monthly'
|
|
30567
31385
|
React.useEffect(() => {
|
|
30568
31386
|
if (isEmbedded || previewMode)
|
|
30569
31387
|
return;
|
|
@@ -30572,42 +31390,72 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
30572
31390
|
setShowWelcomeBubble(false);
|
|
30573
31391
|
return;
|
|
30574
31392
|
}
|
|
30575
|
-
|
|
31393
|
+
const frequency = config?.appearance?.welcomeBubbleFrequency ?? 'session';
|
|
30576
31394
|
const storageKey = `ai-chat-bubble-dismissed-${widgetId || 'default'}`;
|
|
30577
|
-
|
|
30578
|
-
|
|
31395
|
+
// Check if bubble should be shown based on frequency
|
|
31396
|
+
const shouldShowBubble = () => {
|
|
31397
|
+
if (frequency === 'always') {
|
|
31398
|
+
// Always show on every page visit (no storage check)
|
|
31399
|
+
return true;
|
|
31400
|
+
}
|
|
31401
|
+
if (frequency === 'session') {
|
|
31402
|
+
// Show once per session
|
|
31403
|
+
return sessionStorage.getItem(storageKey) !== 'true';
|
|
31404
|
+
}
|
|
31405
|
+
// For weekly/monthly, use localStorage with timestamp
|
|
31406
|
+
try {
|
|
31407
|
+
const stored = localStorage.getItem(storageKey);
|
|
31408
|
+
if (!stored)
|
|
31409
|
+
return true;
|
|
31410
|
+
const dismissedAt = parseInt(stored, 10);
|
|
31411
|
+
if (isNaN(dismissedAt))
|
|
31412
|
+
return true;
|
|
31413
|
+
const now = Date.now();
|
|
31414
|
+
const weekMs = 7 * 24 * 60 * 60 * 1000;
|
|
31415
|
+
const monthMs = 30 * 24 * 60 * 60 * 1000;
|
|
31416
|
+
if (frequency === 'weekly') {
|
|
31417
|
+
return now - dismissedAt > weekMs;
|
|
31418
|
+
}
|
|
31419
|
+
if (frequency === 'monthly') {
|
|
31420
|
+
return now - dismissedAt > monthMs;
|
|
31421
|
+
}
|
|
31422
|
+
}
|
|
31423
|
+
catch {
|
|
31424
|
+
return true;
|
|
31425
|
+
}
|
|
31426
|
+
return true;
|
|
31427
|
+
};
|
|
31428
|
+
if (shouldShowBubble() && !isOpen) {
|
|
30579
31429
|
setShowWelcomeBubble(true);
|
|
30580
31430
|
}
|
|
30581
31431
|
}, [widgetId, welcomeBubbleText, config, isOpen, isEmbedded, previewMode]);
|
|
30582
|
-
|
|
30583
|
-
|
|
30584
|
-
|
|
30585
|
-
|
|
30586
|
-
|
|
30587
|
-
|
|
30588
|
-
|
|
30589
|
-
|
|
30590
|
-
|
|
30591
|
-
|
|
30592
|
-
|
|
30593
|
-
|
|
30594
|
-
|
|
30595
|
-
|
|
30596
|
-
|
|
30597
|
-
|
|
30598
|
-
|
|
30599
|
-
|
|
30600
|
-
|
|
30601
|
-
|
|
30602
|
-
|
|
30603
|
-
|
|
30604
|
-
|
|
30605
|
-
|
|
30606
|
-
|
|
30607
|
-
|
|
30608
|
-
|
|
30609
|
-
...customStyles,
|
|
30610
|
-
...(zIndex !== undefined ? { '--widget-z-index': String(zIndex) } : {}),
|
|
31432
|
+
const handleStartNewConversation = React.useCallback(() => {
|
|
31433
|
+
startNewConversation();
|
|
31434
|
+
}, [startNewConversation]);
|
|
31435
|
+
const handleSendMessage = React.useCallback((content) => {
|
|
31436
|
+
sendMessage(content);
|
|
31437
|
+
}, [sendMessage]);
|
|
31438
|
+
// Dismiss bubble and store based on frequency setting
|
|
31439
|
+
const dismissBubble = () => {
|
|
31440
|
+
setShowWelcomeBubble(false);
|
|
31441
|
+
const frequency = config?.appearance?.welcomeBubbleFrequency ?? 'session';
|
|
31442
|
+
const storageKey = `ai-chat-bubble-dismissed-${widgetId || 'default'}`;
|
|
31443
|
+
try {
|
|
31444
|
+
if (frequency === 'always') {
|
|
31445
|
+
// For 'always', use sessionStorage so it only hides until page refresh
|
|
31446
|
+
sessionStorage.setItem(storageKey, 'true');
|
|
31447
|
+
}
|
|
31448
|
+
else if (frequency === 'session') {
|
|
31449
|
+
sessionStorage.setItem(storageKey, 'true');
|
|
31450
|
+
}
|
|
31451
|
+
else {
|
|
31452
|
+
// For weekly/monthly, store timestamp in localStorage
|
|
31453
|
+
localStorage.setItem(storageKey, String(Date.now()));
|
|
31454
|
+
}
|
|
31455
|
+
}
|
|
31456
|
+
catch {
|
|
31457
|
+
// Ignore storage errors
|
|
31458
|
+
}
|
|
30611
31459
|
};
|
|
30612
31460
|
const handleToggle = () => {
|
|
30613
31461
|
if (isEmbedded)
|
|
@@ -30616,15 +31464,7 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
30616
31464
|
setIsOpen(newState);
|
|
30617
31465
|
// Dismiss welcome bubble when chat is opened
|
|
30618
31466
|
if (newState && showWelcomeBubble) {
|
|
30619
|
-
|
|
30620
|
-
// Store in sessionStorage so it doesn't show again this session
|
|
30621
|
-
const storageKey = `ai-chat-bubble-dismissed-${widgetId || 'default'}`;
|
|
30622
|
-
try {
|
|
30623
|
-
sessionStorage.setItem(storageKey, 'true');
|
|
30624
|
-
}
|
|
30625
|
-
catch {
|
|
30626
|
-
// Ignore storage errors
|
|
30627
|
-
}
|
|
31467
|
+
dismissBubble();
|
|
30628
31468
|
}
|
|
30629
31469
|
if (newState) {
|
|
30630
31470
|
onOpen?.();
|
|
@@ -30633,6 +31473,24 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
30633
31473
|
onClose?.();
|
|
30634
31474
|
}
|
|
30635
31475
|
};
|
|
31476
|
+
const handleDismissBubble = (e) => {
|
|
31477
|
+
e.stopPropagation();
|
|
31478
|
+
dismissBubble();
|
|
31479
|
+
};
|
|
31480
|
+
// Handle input bar submit - opens widget and sends message
|
|
31481
|
+
const handleInputBarSubmit = React.useCallback((e) => {
|
|
31482
|
+
e.preventDefault();
|
|
31483
|
+
if (!inputBarValue.trim() || previewMode)
|
|
31484
|
+
return;
|
|
31485
|
+
// Open the widget
|
|
31486
|
+
setIsOpen(true);
|
|
31487
|
+
onOpen?.();
|
|
31488
|
+
// Send the message after a brief delay to allow widget to open
|
|
31489
|
+
setTimeout(() => {
|
|
31490
|
+
sendMessage(inputBarValue.trim());
|
|
31491
|
+
setInputBarValue('');
|
|
31492
|
+
}, 100);
|
|
31493
|
+
}, [inputBarValue, previewMode, onOpen, sendMessage]);
|
|
30636
31494
|
const handleFeedback = async (messageId, feedback) => {
|
|
30637
31495
|
await submitFeedback(messageId, feedback);
|
|
30638
31496
|
};
|
|
@@ -30648,24 +31506,39 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
30648
31506
|
sendMessage(actionInstruction);
|
|
30649
31507
|
};
|
|
30650
31508
|
// Don't render until config is loaded to avoid flash of unstyled content
|
|
31509
|
+
// Exceptions that allow immediate rendering:
|
|
31510
|
+
// 1. triggerType prop provided - user wants specific trigger shown immediately
|
|
31511
|
+
// 2. primaryColor prop provided - we have initial styling, no flash will occur
|
|
31512
|
+
// This improves perceived loading speed - users see the correctly styled trigger while config loads
|
|
30651
31513
|
// In preview mode, config is always available
|
|
30652
|
-
|
|
31514
|
+
const hasInitialStyling = !!primaryColor;
|
|
31515
|
+
const canRenderWithoutConfig = !!triggerType || hasInitialStyling;
|
|
31516
|
+
if (!config && !previewMode && !canRenderWithoutConfig) {
|
|
30653
31517
|
return null;
|
|
30654
31518
|
}
|
|
30655
31519
|
// Get button icon based on state
|
|
30656
31520
|
const IconComponent = isOpen ? iconComponents.FiChevronDown : iconComponents.FiMessageCircle;
|
|
30657
31521
|
// Embedded mode renders directly without wrapper positioning
|
|
30658
31522
|
if (isEmbedded) {
|
|
30659
|
-
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:
|
|
30660
|
-
}
|
|
30661
|
-
|
|
31523
|
+
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: handleSendMessage, onClose: () => { }, onFeedback: handleFeedback, onActionClick: handleActionClick, onActionDismiss: dismissAction, onCallEndpoint: callActionEndpoint, conversations: conversations, onLoadConversations: loadConversations, onSwitchConversation: switchConversation, onStartNewConversation: handleStartNewConversation, onDeleteConversation: deleteConversation, currentConversationId: conversationId, sizeOverride: effectiveSize, headerTitleOverride: effectiveHeaderTitle, welcomeTitleOverride: effectiveWelcomeTitle, welcomeMessageOverride: effectiveWelcomeMessage, placeholderOverride: effectivePlaceholder, suggestedQuestionsOverride: suggestedQuestions }) }));
|
|
31524
|
+
}
|
|
31525
|
+
// Determine trigger class for container
|
|
31526
|
+
const triggerClass = effectiveTriggerType === 'pill-text'
|
|
31527
|
+
? 'trigger-pill-text'
|
|
31528
|
+
: effectiveTriggerType === 'input-bar'
|
|
31529
|
+
? 'trigger-input-bar'
|
|
31530
|
+
: 'trigger-button';
|
|
31531
|
+
// Size class for CSS targeting (used by input-bar trigger for width matching)
|
|
31532
|
+
const sizeClass = `size-${effectiveSize}`;
|
|
31533
|
+
// Collapsed class for input bar
|
|
31534
|
+
const collapsedClass = isInputBarCollapsed ? 'is-collapsed' : '';
|
|
31535
|
+
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' : ''} ${mobileMode ? 'mobile-mode' : ''} ${desktopMode ? 'desktop-mode' : ''} ${triggerClass} ${sizeClass} ${collapsedClass}`, children: [isOpen && (jsxRuntime.jsx(ChatWindow, { messages: messages, isLoading: isLoading, isTyping: isTyping, config: config, onSendMessage: handleSendMessage, onClose: handleToggle, onFeedback: handleFeedback, onActionClick: handleActionClick, onActionDismiss: dismissAction, onCallEndpoint: callActionEndpoint,
|
|
30662
31536
|
// Chat history props (only active when persistConversation is true)
|
|
30663
|
-
conversations: conversations, onLoadConversations: loadConversations, onSwitchConversation: switchConversation, onStartNewConversation:
|
|
30664
|
-
// Override props for live preview
|
|
30665
|
-
headerTitleOverride: effectiveHeaderTitle, welcomeTitleOverride: effectiveWelcomeTitle, welcomeMessageOverride: effectiveWelcomeMessage, placeholderOverride: effectivePlaceholder, suggestedQuestionsOverride: suggestedQuestions })), !isOpen && effectiveWelcomeBubbleText && (previewMode || showWelcomeBubble) && (jsxRuntime.jsx("div", { className: "ai-chat-welcome-bubble", onClick: handleToggle, children: jsxRuntime.jsx("span", { children: effectiveWelcomeBubbleText }) })), 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, {}) }) })] }) }));
|
|
31537
|
+
conversations: conversations, onLoadConversations: loadConversations, onSwitchConversation: switchConversation, onStartNewConversation: handleStartNewConversation, onDeleteConversation: deleteConversation, currentConversationId: conversationId, sizeOverride: effectiveSize, headerTitleOverride: effectiveHeaderTitle, welcomeTitleOverride: effectiveWelcomeTitle, welcomeMessageOverride: effectiveWelcomeMessage, placeholderOverride: effectivePlaceholder, suggestedQuestionsOverride: suggestedQuestions })), jsxRuntime.jsx(WidgetTriggers, { triggerType: effectiveTriggerType, isOpen: isOpen, onToggle: handleToggle, triggerText: effectiveTriggerText, placeholder: effectivePlaceholder, IconComponent: IconComponent, showWelcomeBubble: showWelcomeBubble, welcomeBubbleText: effectiveWelcomeBubbleText, previewMode: previewMode, onDismissBubble: handleDismissBubble, isInputBarCollapsed: isInputBarCollapsed, setIsInputBarCollapsed: setIsInputBarCollapsed, inputBarValue: inputBarValue, setInputBarValue: setInputBarValue, onSubmitInputBar: handleInputBarSubmit, accentColor: accentColor, iconColor: iconContrastColor })] }) }));
|
|
30666
31538
|
};
|
|
30667
31539
|
|
|
30668
31540
|
exports.ApiError = ApiError;
|
|
30669
31541
|
exports.ChatWidget = ChatWidget;
|
|
31542
|
+
exports.DataPolicyView = DataPolicyView;
|
|
30670
31543
|
exports.useChat = useChat;
|
|
30671
31544
|
//# sourceMappingURL=index.js.map
|