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