@chatwidgetai/chat-widget 0.2.9 → 0.3.1
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 +1546 -331
- package/dist/ai-chat-widget.umd.js.map +1 -1
- package/dist/api/client.d.ts +2 -2
- package/dist/api/client.d.ts.map +1 -1
- package/dist/components/ChatWidget.d.ts +2 -1
- package/dist/components/ChatWidget.d.ts.map +1 -1
- package/dist/components/ChatWindow.d.ts +0 -1
- package/dist/components/ChatWindow.d.ts.map +1 -1
- package/dist/components/DataPolicyView.d.ts +14 -0
- package/dist/components/DataPolicyView.d.ts.map +1 -0
- package/dist/hooks/useChat/action-handler.d.ts +6 -0
- package/dist/hooks/useChat/action-handler.d.ts.map +1 -0
- package/dist/hooks/useChat/action-lifecycle.d.ts +19 -0
- package/dist/hooks/useChat/action-lifecycle.d.ts.map +1 -0
- package/dist/hooks/useChat/error-utils.d.ts +7 -0
- package/dist/hooks/useChat/error-utils.d.ts.map +1 -0
- package/dist/hooks/{useChat.d.ts → useChat/index.d.ts} +2 -2
- package/dist/hooks/useChat/index.d.ts.map +1 -0
- package/dist/hooks/useChat/message-hydration.d.ts +4 -0
- package/dist/hooks/useChat/message-hydration.d.ts.map +1 -0
- package/dist/hooks/useChat/stream-handlers.d.ts +27 -0
- package/dist/hooks/useChat/stream-handlers.d.ts.map +1 -0
- package/dist/hooks/useChat/stream-state.d.ts +8 -0
- package/dist/hooks/useChat/stream-state.d.ts.map +1 -0
- package/dist/hooks/useChat/types.d.ts +26 -0
- package/dist/hooks/useChat/types.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +1546 -331
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +1546 -331
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +10 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/sse-parser.d.ts.map +1 -1
- package/package.json +7 -3
- package/dist/hooks/useChat.d.ts.map +0 -1
package/dist/index.esm.js
CHANGED
|
@@ -8,11 +8,14 @@ async function* parseSSEStream(response, validator) {
|
|
|
8
8
|
const reader = response.body.getReader();
|
|
9
9
|
const decoder = new TextDecoder();
|
|
10
10
|
let buffer = "";
|
|
11
|
+
let eventCount = 0;
|
|
11
12
|
try {
|
|
12
13
|
while (true) {
|
|
13
14
|
const { done, value } = await reader.read();
|
|
14
|
-
if (done)
|
|
15
|
+
if (done) {
|
|
16
|
+
console.log(`[SSE Parser] Stream ended normally after ${eventCount} events`);
|
|
15
17
|
break;
|
|
18
|
+
}
|
|
16
19
|
buffer += decoder.decode(value, { stream: true });
|
|
17
20
|
const chunks = buffer.split("\n\n");
|
|
18
21
|
buffer = chunks.pop() || "";
|
|
@@ -24,6 +27,7 @@ async function* parseSSEStream(response, validator) {
|
|
|
24
27
|
const data = JSON.parse(line.slice(6));
|
|
25
28
|
if (validator) {
|
|
26
29
|
if (validator(data)) {
|
|
30
|
+
eventCount++;
|
|
27
31
|
yield data;
|
|
28
32
|
}
|
|
29
33
|
else {
|
|
@@ -31,6 +35,7 @@ async function* parseSSEStream(response, validator) {
|
|
|
31
35
|
}
|
|
32
36
|
}
|
|
33
37
|
else {
|
|
38
|
+
eventCount++;
|
|
34
39
|
yield data;
|
|
35
40
|
}
|
|
36
41
|
}
|
|
@@ -43,6 +48,10 @@ async function* parseSSEStream(response, validator) {
|
|
|
43
48
|
}
|
|
44
49
|
}
|
|
45
50
|
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
console.error(`[SSE Parser] Stream error after ${eventCount} events:`, error);
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
46
55
|
finally {
|
|
47
56
|
reader.releaseLock();
|
|
48
57
|
}
|
|
@@ -176,7 +185,7 @@ class WidgetApiClient {
|
|
|
176
185
|
const result = await response.json();
|
|
177
186
|
return result.file;
|
|
178
187
|
}
|
|
179
|
-
async *sendAgentMessageStream(conversationId, message, fileIds) {
|
|
188
|
+
async *sendAgentMessageStream(conversationId, message, fileIds, signal) {
|
|
180
189
|
const headers = {
|
|
181
190
|
'Content-Type': 'application/json',
|
|
182
191
|
};
|
|
@@ -192,6 +201,7 @@ class WidgetApiClient {
|
|
|
192
201
|
fileIds,
|
|
193
202
|
timeZone: this.getTimeZone(),
|
|
194
203
|
}),
|
|
204
|
+
signal,
|
|
195
205
|
});
|
|
196
206
|
if (!response.ok) {
|
|
197
207
|
throw await buildApiError(response, 'Failed to send agent message');
|
|
@@ -200,7 +210,7 @@ class WidgetApiClient {
|
|
|
200
210
|
return typeof data === 'object' && data !== null && 'type' in data;
|
|
201
211
|
});
|
|
202
212
|
}
|
|
203
|
-
async *continueAgentMessageStream(conversationId, toolCallId, state) {
|
|
213
|
+
async *continueAgentMessageStream(conversationId, toolCallId, state, signal) {
|
|
204
214
|
const headers = {
|
|
205
215
|
'Content-Type': 'application/json',
|
|
206
216
|
};
|
|
@@ -216,6 +226,7 @@ class WidgetApiClient {
|
|
|
216
226
|
state,
|
|
217
227
|
timeZone: this.getTimeZone(),
|
|
218
228
|
}),
|
|
229
|
+
signal,
|
|
219
230
|
});
|
|
220
231
|
if (!response.ok) {
|
|
221
232
|
throw await buildApiError(response, 'Failed to continue agent');
|
|
@@ -27120,18 +27131,14 @@ const formatToolName = (name) => {
|
|
|
27120
27131
|
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
27121
27132
|
.join(' ');
|
|
27122
27133
|
};
|
|
27123
|
-
const GearIcon = ({ spinning = false }) => (jsxs("svg", { className: `ai-chat-tool-gear ${spinning ? 'spinning' : ''}`, width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M12 20a8 8 0 1 0 0-16 8 8 0 0 0 0 16Z" }), jsx("path", { d: "M12 14a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z" }), jsx("path", { d: "M12 2v2" }), jsx("path", { d: "M12 22v-2" }), jsx("path", { d: "m17 20.66-1-1.73" }), jsx("path", { d: "M11 10.27 7 3.34" }), jsx("path", { d: "m20.66 17-1.73-1" }), jsx("path", { d: "m3.34 7 1.73 1" }), jsx("path", { d: "M14 12h8" }), jsx("path", { d: "M2 12h2" }), jsx("path", { d: "m20.66 7-1.73 1" }), jsx("path", { d: "m3.34 17 1.73-1" }), jsx("path", { d: "m17 3.34-1 1.73" }), jsx("path", { d: "m11 13.73-4 6.93" })] }));
|
|
27124
|
-
const CheckIcon$2 = () => (jsx("svg", { className: "ai-chat-tool-check", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("polyline", { points: "20 6 9 17 4 12" }) }));
|
|
27125
|
-
const ErrorIcon = () => (jsx("svg", { className: "ai-chat-tool-error", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("path", { d: "M18 6L6 18M6 6l12 12" }) }));
|
|
27126
27134
|
function ToolIndicator({ badges, className = '' }) {
|
|
27127
|
-
|
|
27128
|
-
return (jsxs("div", { className: `ai-chat-tool-row ${className}`, children: [jsx(GearIcon, { spinning: isAnyLoading }), jsx("div", { className: "ai-chat-tool-badges", children: badges.map((badge) => (jsxs("div", { className: `ai-chat-tool-badge ${badge.status}`, children: [badge.status !== 'loading' && (badge.status === 'error' ? jsx(ErrorIcon, {}) : jsx(CheckIcon$2, {})), jsx("span", { className: "tool-name", children: formatToolName(badge.name) })] }, badge.id))) })] }));
|
|
27135
|
+
return (jsx("div", { className: `ai-chat-tool-row ${className}`, children: jsx("div", { className: "ai-chat-tool-badges", children: badges.map((badge) => (jsx("div", { className: `ai-chat-tool-badge ${badge.status}`, children: jsx("span", { className: "tool-name", children: formatToolName(badge.name) }) }, badge.id))) }) }));
|
|
27129
27136
|
}
|
|
27130
27137
|
|
|
27131
27138
|
// SVG Icon components
|
|
27132
27139
|
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" }) }));
|
|
27133
27140
|
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" }) }));
|
|
27134
|
-
const CheckIcon$
|
|
27141
|
+
const CheckIcon$2 = () => (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" }) }));
|
|
27135
27142
|
const FeedbackButtons = ({ messageId, currentFeedback, onFeedback, }) => {
|
|
27136
27143
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
27137
27144
|
const [submitted, setSubmitted] = useState(false);
|
|
@@ -27152,17 +27159,10 @@ const FeedbackButtons = ({ messageId, currentFeedback, onFeedback, }) => {
|
|
|
27152
27159
|
setIsSubmitting(false);
|
|
27153
27160
|
}
|
|
27154
27161
|
};
|
|
27155
|
-
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$
|
|
27156
|
-
};
|
|
27157
|
-
|
|
27158
|
-
const Sources = ({ sources, displayMode = 'with-score' }) => {
|
|
27159
|
-
const [isExpanded, setIsExpanded] = useState(false);
|
|
27160
|
-
if (!sources || sources.length === 0 || displayMode === 'none')
|
|
27161
|
-
return null;
|
|
27162
|
-
return (jsxs("div", { className: "ai-chat-sources", children: [jsxs("button", { className: "ai-chat-sources-toggle", onClick: () => setIsExpanded(!isExpanded), "aria-expanded": isExpanded, children: [jsx("span", { className: "ai-chat-sources-icon", children: isExpanded ? '▼' : '▶' }), jsxs("span", { className: "ai-chat-sources-title", children: [sources.length, " source", sources.length > 1 ? 's' : ''] })] }), isExpanded && displayMode !== 'minimal' && (jsx("div", { className: "ai-chat-sources-list", children: sources.map((source, index) => (jsxs("div", { className: "ai-chat-source-item", children: [jsxs("div", { className: "ai-chat-source-number", children: [index + 1, "."] }), jsxs("div", { className: "ai-chat-source-details", children: [displayMode === 'with-score' && source.score && (jsxs("div", { className: "ai-chat-source-score", children: ["Score: ", (source.score * 100).toFixed(0), "%"] })), (displayMode === 'with-content' || displayMode === 'full') && source.doc.pageContent && (jsxs("div", { className: "ai-chat-source-content", children: [source.doc.pageContent.substring(0, 100), source.doc.pageContent.length > 100 ? '...' : ''] })), displayMode === 'full' && (jsxs(Fragment, { children: [source.score && (jsxs("div", { className: "ai-chat-source-score", children: ["Score: ", (source.score * 100).toFixed(0), "%"] })), source.doc.metadata && Object.keys(source.doc.metadata).length > 0 && (jsx("div", { className: "ai-chat-source-metadata", children: Object.entries(source.doc.metadata).map(([key, value]) => (jsxs("span", { className: "ai-chat-source-meta-item", children: [key, ": ", String(value)] }, key))) }))] }))] })] }, `${source.kbId}-${source.doc.id}-${index}`))) }))] }));
|
|
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$2, {}) }), jsx("span", { className: "ai-chat-feedback-text", children: "Thanks for feedback" })] }))] }));
|
|
27163
27163
|
};
|
|
27164
27164
|
|
|
27165
|
-
const Message = ({ message, showTimestamp = true, enableFeedback = true,
|
|
27165
|
+
const Message = ({ message, showTimestamp = true, enableFeedback = true, onFeedback, }) => {
|
|
27166
27166
|
const formatTime = (timestamp) => {
|
|
27167
27167
|
const date = typeof timestamp === 'string' ? new Date(timestamp) : timestamp;
|
|
27168
27168
|
return date.toLocaleTimeString('en-US', {
|
|
@@ -27182,15 +27182,15 @@ const Message = ({ message, showTimestamp = true, enableFeedback = true, showSou
|
|
|
27182
27182
|
return null;
|
|
27183
27183
|
}
|
|
27184
27184
|
// AI message rendering
|
|
27185
|
+
// Note: Actions are rendered by ToolMessageGroup for tool messages, not here
|
|
27185
27186
|
if (isAssistant) {
|
|
27186
27187
|
const aiContent = message.message.content || '';
|
|
27187
27188
|
const hasContent = aiContent.trim().length > 0;
|
|
27188
27189
|
if (!hasContent)
|
|
27189
27190
|
return null;
|
|
27190
|
-
|
|
27191
|
-
|
|
27192
|
-
|
|
27193
|
-
return (jsxs("div", { className: `ai-chat-message assistant ${isError ? 'error' : ''}`, children: [jsxs("div", { className: "ai-chat-message-content", children: [isError && (jsxs("div", { className: "ai-chat-error-indicator", children: [jsx("span", { className: "error-icon", children: "\u26A0\uFE0F" }), jsx("span", { className: "error-text", children: "Error" })] })), jsx(Markdown, { remarkPlugins: [remarkGfm], children: aiContent })] }), actionRenderer && message.action && actionRenderer(message), showTimestamp && (jsxs("div", { className: "ai-chat-message-meta", children: [jsx("span", { className: "ai-chat-message-timestamp", children: formatTime(message.timestamp) }), enableFeedback && onFeedback && (jsx(FeedbackButtons, { messageId: message.id, currentFeedback: message.feedback, onFeedback: onFeedback }))] })), showSources && message.sources?.length > 0 && (jsx(Sources, { sources: message.sources, displayMode: sourceDisplayMode }))] }));
|
|
27191
|
+
return (jsxs("div", { className: `ai-chat-message assistant ${isError ? 'error' : ''}`, children: [jsx("div", { className: "ai-chat-message-content", children: jsx(Markdown, { remarkPlugins: [remarkGfm], components: {
|
|
27192
|
+
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 }))] }))] }));
|
|
27194
27194
|
}
|
|
27195
27195
|
// System message rendering
|
|
27196
27196
|
if (isSystem) {
|
|
@@ -27204,30 +27204,85 @@ const Message = ({ message, showTimestamp = true, enableFeedback = true, showSou
|
|
|
27204
27204
|
return null;
|
|
27205
27205
|
};
|
|
27206
27206
|
|
|
27207
|
-
|
|
27207
|
+
// Centralized action state logic
|
|
27208
|
+
function isActionComplete(state) {
|
|
27209
|
+
if (!state)
|
|
27210
|
+
return false;
|
|
27211
|
+
const TERMINAL_STATUSES = [
|
|
27212
|
+
'completed', 'booked', 'scheduled', 'cancelled', 'failed', 'error',
|
|
27213
|
+
'displaying', 'clicked', 'contacted', 'submitted', 'sent'
|
|
27214
|
+
];
|
|
27215
|
+
const status = state.status;
|
|
27216
|
+
if (typeof status === 'string' && TERMINAL_STATUSES.includes(status)) {
|
|
27217
|
+
return true;
|
|
27218
|
+
}
|
|
27219
|
+
// For non-halting actions (query_contact_directory, etc.) with results/success
|
|
27220
|
+
if (!status && (state.success !== undefined || state.results !== undefined)) {
|
|
27221
|
+
return true;
|
|
27222
|
+
}
|
|
27223
|
+
return false;
|
|
27224
|
+
}
|
|
27225
|
+
function isActionLoading(message) {
|
|
27226
|
+
if (!message.action)
|
|
27227
|
+
return false;
|
|
27228
|
+
if (message.action.done)
|
|
27229
|
+
return false;
|
|
27230
|
+
if (message.isStreaming)
|
|
27231
|
+
return true;
|
|
27232
|
+
const state = message.action.state;
|
|
27233
|
+
return !isActionComplete(state);
|
|
27234
|
+
}
|
|
27235
|
+
const ToolMessageGroup = ({ messages, getActionRenderer, showToolIndicator = true, accentColor, variant }) => {
|
|
27208
27236
|
const actionMessages = messages.filter(message => message.action);
|
|
27237
|
+
// Debug logging
|
|
27238
|
+
console.log('[DEBUG ToolMessageGroup] ========================================');
|
|
27239
|
+
console.log('[DEBUG ToolMessageGroup] Total messages:', messages.length);
|
|
27240
|
+
console.log('[DEBUG ToolMessageGroup] Messages with action:', actionMessages.length);
|
|
27241
|
+
console.log('[DEBUG ToolMessageGroup] hasGetActionRenderer:', !!getActionRenderer);
|
|
27242
|
+
messages.forEach((msg, i) => {
|
|
27243
|
+
console.log(`[DEBUG ToolMessageGroup] Message ${i}:`, {
|
|
27244
|
+
id: msg.id,
|
|
27245
|
+
role: msg.message.role,
|
|
27246
|
+
hasAction: !!msg.action,
|
|
27247
|
+
actionImpl: msg.action?.implementation,
|
|
27248
|
+
toolExecuting: msg.toolExecuting,
|
|
27249
|
+
});
|
|
27250
|
+
});
|
|
27251
|
+
actionMessages.forEach((msg, i) => {
|
|
27252
|
+
const impl = msg.action?.implementation || '';
|
|
27253
|
+
const renderer = getActionRenderer?.(impl);
|
|
27254
|
+
console.log(`[DEBUG ToolMessageGroup] Action ${i}:`, {
|
|
27255
|
+
implementation: impl,
|
|
27256
|
+
hasRenderer: !!renderer,
|
|
27257
|
+
rendererType: renderer ? typeof renderer : 'undefined',
|
|
27258
|
+
state: msg.action?.state,
|
|
27259
|
+
});
|
|
27260
|
+
});
|
|
27261
|
+
// If tool indicator is hidden AND there are no action cards to render, don't render anything
|
|
27262
|
+
if (!showToolIndicator && actionMessages.length === 0) {
|
|
27263
|
+
return null;
|
|
27264
|
+
}
|
|
27209
27265
|
const badges = messages.map((message) => {
|
|
27210
27266
|
const toolName = message.toolExecuting || message.message.name || 'Tool';
|
|
27211
27267
|
const hasError = message.isError || false;
|
|
27212
|
-
const
|
|
27213
|
-
const actionStatus = actionState?.status;
|
|
27214
|
-
const terminalStatuses = ['completed', 'booked', 'scheduled', 'failed', 'cancelled'];
|
|
27215
|
-
const isTerminalStatus = actionStatus && terminalStatuses.includes(actionStatus);
|
|
27216
|
-
const isDone = message.action ? (message.action.done ?? isTerminalStatus ?? false) : !message.isStreaming;
|
|
27217
|
-
const isLoading = !isDone;
|
|
27268
|
+
const loading = isActionLoading(message);
|
|
27218
27269
|
return {
|
|
27219
27270
|
id: message.id,
|
|
27220
27271
|
name: toolName,
|
|
27221
|
-
status:
|
|
27272
|
+
status: loading ? 'loading' : hasError ? 'error' : 'completed',
|
|
27222
27273
|
};
|
|
27223
27274
|
});
|
|
27224
|
-
return (jsxs("div", { className: "ai-chat-message tool", children: [jsx(ToolIndicator, { badges: badges }), actionMessages.map((message) => {
|
|
27225
|
-
if (!message.action || !getActionRenderer)
|
|
27275
|
+
return (jsxs("div", { className: "ai-chat-message tool", children: [showToolIndicator && jsx(ToolIndicator, { badges: badges }), actionMessages.map((message) => {
|
|
27276
|
+
if (!message.action || !getActionRenderer) {
|
|
27277
|
+
console.log('[ToolMessageGroup] Skipping - no action or renderer:', { hasAction: !!message.action, hasGetRenderer: !!getActionRenderer });
|
|
27226
27278
|
return null;
|
|
27279
|
+
}
|
|
27227
27280
|
const renderer = getActionRenderer(message.action.implementation);
|
|
27228
|
-
if (!renderer)
|
|
27281
|
+
if (!renderer) {
|
|
27282
|
+
console.log('[ToolMessageGroup] No renderer for:', message.action.implementation);
|
|
27229
27283
|
return null;
|
|
27230
|
-
|
|
27284
|
+
}
|
|
27285
|
+
return (jsx("div", { className: "ai-chat-tool-action", children: renderer(message, accentColor, variant) }, `action-${message.id}`));
|
|
27231
27286
|
})] }));
|
|
27232
27287
|
};
|
|
27233
27288
|
|
|
@@ -27289,12 +27344,16 @@ const FollowUpSuggestions = ({ suggestions, onQuestionClick, onActionClick, acce
|
|
|
27289
27344
|
};
|
|
27290
27345
|
|
|
27291
27346
|
const MessageList = (props) => {
|
|
27292
|
-
const { messages, isTyping = false, showTypingIndicator = true, showTimestamps = true,
|
|
27347
|
+
const { messages, isTyping = false, showTypingIndicator = true, showTimestamps = true, showToolCalls = false, enableFeedback = true, welcomeTitle, welcomeMessage, suggestedQuestions, accentColor, onSuggestedQuestionClick, onActionClick, onFeedback, onScrollStateChange, getActionRenderer, variant, } = props;
|
|
27293
27348
|
const containerRef = useRef(null);
|
|
27294
27349
|
const messagesEndRef = useRef(null);
|
|
27295
27350
|
const [showScrollButton, setShowScrollButton] = useState(false);
|
|
27296
27351
|
const prevMessageCountRef = useRef(0);
|
|
27297
|
-
const hasActiveAction = useMemo(() =>
|
|
27352
|
+
const hasActiveAction = useMemo(() => {
|
|
27353
|
+
// Find the last tool message and check if its action is still active (not done)
|
|
27354
|
+
const lastToolMsg = [...messages].reverse().find(msg => msg.message.role === 'tool');
|
|
27355
|
+
return lastToolMsg?.action && lastToolMsg.action.done !== true;
|
|
27356
|
+
}, [messages]);
|
|
27298
27357
|
const checkScrollPosition = useCallback(() => {
|
|
27299
27358
|
const c = containerRef.current;
|
|
27300
27359
|
if (!c)
|
|
@@ -27330,39 +27389,70 @@ const MessageList = (props) => {
|
|
|
27330
27389
|
checkScrollPosition();
|
|
27331
27390
|
}, [messages, isTyping, checkScrollPosition]);
|
|
27332
27391
|
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
|
+
});
|
|
27333
27403
|
const result = [];
|
|
27334
27404
|
let toolGroup = [];
|
|
27335
|
-
const flush = () => {
|
|
27336
|
-
|
|
27337
|
-
|
|
27338
|
-
|
|
27405
|
+
const flush = () => {
|
|
27406
|
+
if (toolGroup.length) {
|
|
27407
|
+
console.log('[DEBUG MessageList] Flushing tool group with', toolGroup.length, 'messages');
|
|
27408
|
+
result.push({ type: 'tool-group', messages: [...toolGroup] });
|
|
27409
|
+
toolGroup = [];
|
|
27410
|
+
}
|
|
27411
|
+
};
|
|
27339
27412
|
for (const m of messages) {
|
|
27340
|
-
if (m.message.role === 'tool')
|
|
27413
|
+
if (m.message.role === 'tool') {
|
|
27414
|
+
console.log('[DEBUG MessageList] Adding to tool group:', m.id);
|
|
27341
27415
|
toolGroup.push(m);
|
|
27416
|
+
}
|
|
27342
27417
|
else if (m.message.role === 'user') {
|
|
27343
27418
|
flush();
|
|
27344
27419
|
result.push({ type: 'message', message: m });
|
|
27345
27420
|
}
|
|
27346
|
-
else if (m.message.role === 'assistant'
|
|
27347
|
-
|
|
27348
|
-
|
|
27421
|
+
else if (m.message.role === 'assistant') {
|
|
27422
|
+
// Include assistant messages if they have content OR are still streaming (content may arrive)
|
|
27423
|
+
const hasContent = (m.message.content || '').trim().length > 0;
|
|
27424
|
+
if (hasContent || m.isStreaming) {
|
|
27425
|
+
flush();
|
|
27426
|
+
result.push({ type: 'message', message: m });
|
|
27427
|
+
}
|
|
27428
|
+
// Don't flush on empty assistant messages - let tools continue grouping
|
|
27349
27429
|
}
|
|
27350
|
-
else
|
|
27430
|
+
else {
|
|
27351
27431
|
flush();
|
|
27352
27432
|
result.push({ type: 'message', message: m });
|
|
27353
27433
|
}
|
|
27354
27434
|
}
|
|
27355
27435
|
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
|
+
});
|
|
27356
27445
|
return result;
|
|
27357
27446
|
}, [messages]);
|
|
27358
27447
|
const hasSuggestions = messages.length === 0 && onSuggestedQuestionClick && suggestedQuestions?.length;
|
|
27359
27448
|
const showWelcome = welcomeTitle || welcomeMessage || hasSuggestions;
|
|
27360
27449
|
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) => {
|
|
27361
|
-
if (item.type === 'tool-group')
|
|
27362
|
-
return jsx(ToolMessageGroup, { messages: item.messages, getActionRenderer: getActionRenderer }, `tg-${i}`);
|
|
27450
|
+
if (item.type === 'tool-group') {
|
|
27451
|
+
return jsx(ToolMessageGroup, { messages: item.messages, getActionRenderer: getActionRenderer, showToolIndicator: showToolCalls, accentColor: accentColor, variant: variant }, `tg-${i}`);
|
|
27452
|
+
}
|
|
27363
27453
|
const isLast = i === groupedMessages.length - 1;
|
|
27364
27454
|
const hasFollowUp = item.message.message.role === 'assistant' && item.message.suggestions?.length && isLast && !isTyping;
|
|
27365
|
-
return (jsxs(React.Fragment, { children: [jsx(Message, { message: item.message, showTimestamp: showTimestamps,
|
|
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));
|
|
27366
27456
|
}), isTyping && showTypingIndicator && !hasActiveAction && messages.length > 0 && jsx(TypingIndicator, {}), jsx("div", { ref: messagesEndRef })] }));
|
|
27367
27457
|
};
|
|
27368
27458
|
|
|
@@ -27377,7 +27467,7 @@ const formatFileSize = (bytes) => {
|
|
|
27377
27467
|
return (bytes / 1024).toFixed(1) + ' KB';
|
|
27378
27468
|
return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
|
|
27379
27469
|
};
|
|
27380
|
-
const MessageInput = ({ onSend, placeholder = 'Type your message...', disabled = false, enableFileUpload = false, separateFromChat = true, }) => {
|
|
27470
|
+
const MessageInput = ({ onSend, placeholder = 'Type your message...', disabled = false, enableFileUpload = false, separateFromChat = true, showDataPolicy = true, onDataPolicyClick, }) => {
|
|
27381
27471
|
const [value, setValue] = useState('');
|
|
27382
27472
|
const [selectedFiles, setSelectedFiles] = useState([]);
|
|
27383
27473
|
const textareaRef = useRef(null);
|
|
@@ -27412,10 +27502,10 @@ const MessageInput = ({ onSend, placeholder = 'Type your message...', disabled =
|
|
|
27412
27502
|
}
|
|
27413
27503
|
};
|
|
27414
27504
|
const canSend = value.trim() || selectedFiles.length > 0;
|
|
27415
|
-
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" })] }) })] })] }));
|
|
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: "KI-generierte Antworten k\u00F6nnen unzutreffend sein." }), onDataPolicyClick && (jsxs(Fragment, { children: [' ', jsx("button", { type: "button", className: "ai-chat-data-policy-link", onClick: onDataPolicyClick, children: "Datenschutzhinweis" })] }))] }))] }));
|
|
27416
27506
|
};
|
|
27417
27507
|
|
|
27418
|
-
function groupSlotsByDate(slots) {
|
|
27508
|
+
function groupSlotsByDate$1(slots) {
|
|
27419
27509
|
const grouped = new Map();
|
|
27420
27510
|
for (const slot of slots) {
|
|
27421
27511
|
if (!slot || typeof slot !== 'object' || !slot.startTime || typeof slot.startTime !== 'string') {
|
|
@@ -27429,7 +27519,7 @@ function groupSlotsByDate(slots) {
|
|
|
27429
27519
|
}
|
|
27430
27520
|
return grouped;
|
|
27431
27521
|
}
|
|
27432
|
-
function formatDate(dateStr) {
|
|
27522
|
+
function formatDate$1(dateStr) {
|
|
27433
27523
|
try {
|
|
27434
27524
|
const date = new Date(dateStr);
|
|
27435
27525
|
return new Intl.DateTimeFormat("en-US", {
|
|
@@ -27442,16 +27532,16 @@ function formatDate(dateStr) {
|
|
|
27442
27532
|
return dateStr;
|
|
27443
27533
|
}
|
|
27444
27534
|
}
|
|
27445
|
-
function CalendarIcon() {
|
|
27535
|
+
function CalendarIcon$1() {
|
|
27446
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" }) }));
|
|
27447
27537
|
}
|
|
27448
|
-
function CheckIcon() {
|
|
27538
|
+
function CheckIcon$1() {
|
|
27449
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" }) }));
|
|
27450
27540
|
}
|
|
27451
|
-
function ExternalLinkIcon() {
|
|
27541
|
+
function ExternalLinkIcon$2() {
|
|
27452
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" })] }));
|
|
27453
27543
|
}
|
|
27454
|
-
function Skeleton({ width, height, borderRadius = '4px' }) {
|
|
27544
|
+
function Skeleton$1({ width, height, borderRadius = '4px' }) {
|
|
27455
27545
|
return (jsx("div", { className: "ai-chat-action-skeleton-item", style: { width, height, borderRadius } }));
|
|
27456
27546
|
}
|
|
27457
27547
|
function GoogleCalendarCard({ action, onComplete, accentColor, className = '' }) {
|
|
@@ -27468,13 +27558,12 @@ function GoogleCalendarCard({ action, onComplete, accentColor, className = '' })
|
|
|
27468
27558
|
: [];
|
|
27469
27559
|
const allowTopic = state.allowTopic !== false;
|
|
27470
27560
|
const isBooked = state.status === "booked";
|
|
27471
|
-
const slotsByDate = groupSlotsByDate(availableSlots);
|
|
27561
|
+
const slotsByDate = groupSlotsByDate$1(availableSlots);
|
|
27472
27562
|
const dates = Array.from(slotsByDate.keys()).sort();
|
|
27473
27563
|
const [selectedDate, setSelectedDate] = useState(dates[0] ?? "");
|
|
27474
27564
|
const [selectedSlot, setSelectedSlot] = useState(null);
|
|
27475
27565
|
const [topic, setTopic] = useState("");
|
|
27476
27566
|
const [error, setError] = useState(null);
|
|
27477
|
-
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
27478
27567
|
const slotsForSelectedDate = selectedDate ? slotsByDate.get(selectedDate) ?? [] : [];
|
|
27479
27568
|
const accentStyle = accentColor ? { '--action-accent': accentColor } : {};
|
|
27480
27569
|
const onConfirm = () => {
|
|
@@ -27487,8 +27576,245 @@ function GoogleCalendarCard({ action, onComplete, accentColor, className = '' })
|
|
|
27487
27576
|
return;
|
|
27488
27577
|
}
|
|
27489
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);
|
|
27490
27813
|
setIsSubmitting(true);
|
|
27491
|
-
|
|
27814
|
+
console.log('[MicrosoftCalendarCard] Confirming appointment:', {
|
|
27815
|
+
slot: selectedSlot,
|
|
27816
|
+
topic: topic.trim()
|
|
27817
|
+
});
|
|
27492
27818
|
setTimeout(() => {
|
|
27493
27819
|
onComplete?.(action.toolCallId, {
|
|
27494
27820
|
...action.state,
|
|
@@ -27500,22 +27826,566 @@ function GoogleCalendarCard({ action, onComplete, accentColor, className = '' })
|
|
|
27500
27826
|
});
|
|
27501
27827
|
}, 50);
|
|
27502
27828
|
};
|
|
27503
|
-
//
|
|
27504
|
-
|
|
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") {
|
|
27505
27851
|
const bookedSlot = state.selectedSlot;
|
|
27506
27852
|
const bookedTopic = state.topic;
|
|
27507
27853
|
const eventLink = state.bookedEventLink;
|
|
27508
|
-
|
|
27509
|
-
|
|
27510
|
-
|
|
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
|
|
27511
27886
|
if (isSubmitting) {
|
|
27512
|
-
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" })] }),
|
|
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) {
|
|
27975
|
+
if (text.length <= maxLength)
|
|
27976
|
+
return text;
|
|
27977
|
+
return text.slice(0, maxLength).trim() + '...';
|
|
27978
|
+
}
|
|
27979
|
+
function ExternalLinkIcon() {
|
|
27980
|
+
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
|
+
}
|
|
27982
|
+
function SingleLinkPreview({ link, onLinkClick, accentColor }) {
|
|
27983
|
+
const domain = (() => {
|
|
27984
|
+
if (!link.url)
|
|
27985
|
+
return '';
|
|
27986
|
+
try {
|
|
27987
|
+
return new URL(link.url).hostname.replace('www.', '');
|
|
27988
|
+
}
|
|
27989
|
+
catch {
|
|
27990
|
+
return link.url;
|
|
27991
|
+
}
|
|
27992
|
+
})();
|
|
27993
|
+
const style = accentColor ? { '--action-accent': accentColor } : undefined;
|
|
27994
|
+
return (jsxs("div", { className: "ai-chat-action-card ai-chat-link-preview", onClick: onLinkClick, style: style, role: "link", tabIndex: 0, onKeyDown: (e) => e.key === 'Enter' && onLinkClick(), children: [link.image && (jsx("div", { className: "ai-chat-link-preview__image", children: jsx("img", { src: link.image, alt: link.title, onError: (e) => {
|
|
27995
|
+
e.currentTarget.parentElement.style.display = 'none';
|
|
27996
|
+
} }) })), 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
|
+
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, {}) })] }));
|
|
27999
|
+
}
|
|
28000
|
+
function LinkPreviewCard({ action, onComplete, accentColor }) {
|
|
28001
|
+
const rawState = action.state;
|
|
28002
|
+
const hasCompletedRef = useRef(false);
|
|
28003
|
+
// Provide safe defaults if state is missing
|
|
28004
|
+
const state = {
|
|
28005
|
+
links: rawState?.links || [],
|
|
28006
|
+
context: rawState?.context,
|
|
28007
|
+
status: rawState?.status || 'displaying',
|
|
28008
|
+
error: rawState?.error,
|
|
28009
|
+
};
|
|
28010
|
+
const isError = state.status === 'error';
|
|
28011
|
+
// Auto-complete on mount so AI can continue generating text response
|
|
28012
|
+
useEffect(() => {
|
|
28013
|
+
if (!action.done && !hasCompletedRef.current && onComplete && state.links.length > 0) {
|
|
28014
|
+
hasCompletedRef.current = true;
|
|
28015
|
+
// Signal completion immediately - the card is displayed, AI can continue
|
|
28016
|
+
onComplete(action.toolCallId, { ...state, status: 'displaying' });
|
|
28017
|
+
}
|
|
28018
|
+
}, [action.done, action.toolCallId, onComplete, state]);
|
|
28019
|
+
const handleLinkClick = (url) => {
|
|
28020
|
+
if (url) {
|
|
28021
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
28022
|
+
}
|
|
28023
|
+
onComplete?.(action.toolCallId, { ...state, status: 'clicked' });
|
|
28024
|
+
};
|
|
28025
|
+
if (isError) {
|
|
28026
|
+
return (jsx("div", { className: "ai-chat-action-card ai-chat-link-preview ai-chat-link-preview--error", children: jsx("div", { className: "ai-chat-link-preview__content", children: jsx("p", { className: "ai-chat-link-preview__error-text", children: state.error || 'Failed to load preview' }) }) }));
|
|
27513
28027
|
}
|
|
27514
|
-
|
|
27515
|
-
|
|
27516
|
-
|
|
27517
|
-
|
|
27518
|
-
|
|
28028
|
+
if (state.links.length === 0) {
|
|
28029
|
+
return null;
|
|
28030
|
+
}
|
|
28031
|
+
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: "ai-chat-link-preview-grid", style: {
|
|
28032
|
+
display: 'grid',
|
|
28033
|
+
gridTemplateColumns: state.links.length === 1 ? '1fr' : state.links.length === 2 ? 'repeat(2, 1fr)' : 'repeat(3, 1fr)',
|
|
28034
|
+
gap: '12px',
|
|
28035
|
+
}, children: state.links.map((link, index) => (jsx(SingleLinkPreview, { link: link, onLinkClick: () => handleLinkClick(link.url), accentColor: accentColor }, index))) })] }));
|
|
28036
|
+
}
|
|
28037
|
+
|
|
28038
|
+
function PlayIcon() {
|
|
28039
|
+
return (jsx("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "currentColor", stroke: "none", children: jsx("path", { d: "M8 5v14l11-7z" }) }));
|
|
28040
|
+
}
|
|
28041
|
+
function getProviderLabel(provider) {
|
|
28042
|
+
switch (provider) {
|
|
28043
|
+
case 'youtube':
|
|
28044
|
+
return 'YouTube';
|
|
28045
|
+
case 'vimeo':
|
|
28046
|
+
return 'Vimeo';
|
|
28047
|
+
case 'loom':
|
|
28048
|
+
return 'Loom';
|
|
28049
|
+
case 'direct':
|
|
28050
|
+
return 'Video';
|
|
28051
|
+
default:
|
|
28052
|
+
return 'Video';
|
|
28053
|
+
}
|
|
28054
|
+
}
|
|
28055
|
+
function VideoPlayerCard({ action, onComplete, accentColor }) {
|
|
28056
|
+
const rawState = action.state;
|
|
28057
|
+
const hasCompletedRef = useRef(false);
|
|
28058
|
+
const [isPlaying, setIsPlaying] = useState(false);
|
|
28059
|
+
// Provide safe defaults if state is missing
|
|
28060
|
+
const state = {
|
|
28061
|
+
url: rawState?.url || '',
|
|
28062
|
+
title: rawState?.title,
|
|
28063
|
+
context: rawState?.context,
|
|
28064
|
+
provider: rawState?.provider || 'direct',
|
|
28065
|
+
embedUrl: rawState?.embedUrl || '',
|
|
28066
|
+
thumbnailUrl: rawState?.thumbnailUrl,
|
|
28067
|
+
videoId: rawState?.videoId,
|
|
28068
|
+
status: rawState?.status || 'displaying',
|
|
28069
|
+
error: rawState?.error,
|
|
28070
|
+
};
|
|
28071
|
+
const isError = state.status === 'error';
|
|
28072
|
+
// Auto-complete on mount so AI can continue generating text response
|
|
28073
|
+
useEffect(() => {
|
|
28074
|
+
if (!action.done && !hasCompletedRef.current && onComplete && state.embedUrl) {
|
|
28075
|
+
hasCompletedRef.current = true;
|
|
28076
|
+
onComplete(action.toolCallId, { ...state, status: 'displaying' });
|
|
28077
|
+
}
|
|
28078
|
+
}, [action.done, action.toolCallId, onComplete, state]);
|
|
28079
|
+
const handlePlay = () => {
|
|
28080
|
+
setIsPlaying(true);
|
|
28081
|
+
onComplete?.(action.toolCallId, { ...state, status: 'played' });
|
|
28082
|
+
};
|
|
28083
|
+
const style = accentColor ? { '--action-accent': accentColor } : undefined;
|
|
28084
|
+
// Build embed URL with autoplay when playing
|
|
28085
|
+
const getEmbedSrc = () => {
|
|
28086
|
+
if (!isPlaying)
|
|
28087
|
+
return '';
|
|
28088
|
+
let src = state.embedUrl;
|
|
28089
|
+
const separator = src.includes('?') ? '&' : '?';
|
|
28090
|
+
switch (state.provider) {
|
|
28091
|
+
case 'youtube':
|
|
28092
|
+
return `${src}${separator}autoplay=1&rel=0`;
|
|
28093
|
+
case 'vimeo':
|
|
28094
|
+
return `${src}${separator}autoplay=1`;
|
|
28095
|
+
case 'loom':
|
|
28096
|
+
return `${src}${separator}autoplay=1`;
|
|
28097
|
+
default:
|
|
28098
|
+
return src;
|
|
28099
|
+
}
|
|
28100
|
+
};
|
|
28101
|
+
return (jsxs("div", { className: "ai-chat-action-card ai-chat-video-player", style: style, children: [jsx("div", { className: "ai-chat-video-player__container", children: isError ? (jsx("div", { className: "ai-chat-video-player__error", children: jsx("span", { children: state.error || 'Could not load video' }) })) : !isPlaying && state.thumbnailUrl ? (jsxs("div", { className: "ai-chat-video-player__thumbnail", onClick: handlePlay, children: [jsx("img", { src: state.thumbnailUrl, alt: state.title || 'Video thumbnail' }), jsx("button", { className: "ai-chat-video-player__play-btn", "aria-label": "Play video", children: jsx(PlayIcon, {}) }), jsx("div", { className: "ai-chat-video-player__provider-badge", children: getProviderLabel(state.provider) })] })) : !isPlaying ? (jsxs("div", { className: "ai-chat-video-player__placeholder", onClick: handlePlay, children: [jsx("button", { className: "ai-chat-video-player__play-btn", "aria-label": "Play video", children: jsx(PlayIcon, {}) }), jsx("span", { className: "ai-chat-video-player__click-text", children: "Click to play" }), jsx("div", { className: "ai-chat-video-player__provider-badge", children: getProviderLabel(state.provider) })] })) : state.provider === 'direct' ? (jsx("video", { src: state.embedUrl, controls: true, autoPlay: true, className: "ai-chat-video-player__video" })) : (jsx("iframe", { src: getEmbedSrc(), className: "ai-chat-video-player__iframe", allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share", allowFullScreen: true, title: state.title || 'Video player' })) }), state.context && (jsx("div", { className: "ai-chat-video-player__context", children: state.context }))] }));
|
|
28102
|
+
}
|
|
28103
|
+
|
|
28104
|
+
function MapPinIcon() {
|
|
28105
|
+
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: "M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" }), jsx("circle", { cx: "12", cy: "10", r: "3" })] }));
|
|
28106
|
+
}
|
|
28107
|
+
function PhoneIcon() {
|
|
28108
|
+
return (jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("path", { d: "M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z" }) }));
|
|
28109
|
+
}
|
|
28110
|
+
function ClockIcon() {
|
|
28111
|
+
return (jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("circle", { cx: "12", cy: "12", r: "10" }), jsx("polyline", { points: "12 6 12 12 16 14" })] }));
|
|
28112
|
+
}
|
|
28113
|
+
function NavigationIcon() {
|
|
28114
|
+
return (jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("polygon", { points: "3 11 22 2 13 21 11 13 3 11" }) }));
|
|
28115
|
+
}
|
|
28116
|
+
function GlobeIcon() {
|
|
28117
|
+
return (jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("circle", { cx: "12", cy: "12", r: "10" }), jsx("line", { x1: "2", y1: "12", x2: "22", y2: "12" }), jsx("path", { d: "M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" })] }));
|
|
28118
|
+
}
|
|
28119
|
+
function ChevronIcon({ direction }) {
|
|
28120
|
+
return (jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { transform: direction === 'up' ? 'rotate(180deg)' : 'none' }, children: jsx("polyline", { points: "6 9 12 15 18 9" }) }));
|
|
28121
|
+
}
|
|
28122
|
+
function getOpenStatus(hours) {
|
|
28123
|
+
if (!hours || hours.length === 0)
|
|
28124
|
+
return null;
|
|
28125
|
+
const now = new Date();
|
|
28126
|
+
const dayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
|
28127
|
+
const today = dayNames[now.getDay()];
|
|
28128
|
+
const todayHours = hours.find(h => h.day === today);
|
|
28129
|
+
if (!todayHours || todayHours.closed)
|
|
28130
|
+
return false;
|
|
28131
|
+
const currentTime = now.getHours() * 100 + now.getMinutes();
|
|
28132
|
+
const openTime = parseInt(todayHours.open.replace(':', ''));
|
|
28133
|
+
const closeTime = parseInt(todayHours.close.replace(':', ''));
|
|
28134
|
+
return currentTime >= openTime && currentTime < closeTime;
|
|
28135
|
+
}
|
|
28136
|
+
function HoursDisplay({ hours, compact = false }) {
|
|
28137
|
+
const [expanded, setExpanded] = useState(false);
|
|
28138
|
+
const dayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
|
28139
|
+
const today = dayNames[new Date().getDay()];
|
|
28140
|
+
const todayHours = hours.find(h => h.day === today);
|
|
28141
|
+
if (compact && !expanded) {
|
|
28142
|
+
return (jsxs("div", { className: "ai-chat-location-card__hours", children: [jsx(ClockIcon, {}), jsx("span", { children: todayHours?.closed ? 'Closed today' : `${todayHours?.open} - ${todayHours?.close}` })] }));
|
|
28143
|
+
}
|
|
28144
|
+
return (jsxs("div", { className: "ai-chat-location-card__hours", children: [jsx(ClockIcon, {}), jsxs("div", { className: "ai-chat-location-card__hours-list", children: [jsxs("button", { type: "button", onClick: () => setExpanded(!expanded), className: "ai-chat-location-card__hours-toggle", children: ["Today: ", todayHours?.closed ? 'Closed' : `${todayHours?.open} - ${todayHours?.close}`, jsx(ChevronIcon, { direction: expanded ? 'up' : 'down' })] }), expanded && (jsx("ul", { className: "ai-chat-location-card__hours-full", children: hours.map(h => (jsxs("li", { className: h.day === today ? 'ai-chat-location-card__hours-today' : '', children: [jsx("span", { children: h.day }), jsx("span", { children: h.closed ? 'Closed' : `${h.open} - ${h.close}` })] }, h.day))) }))] })] }));
|
|
28145
|
+
}
|
|
28146
|
+
function LocationItem({ location, settings, accentColor, onDirections, showMap = true, compact = false, }) {
|
|
28147
|
+
const openStatus = getOpenStatus(location.hours);
|
|
28148
|
+
const mapHeight = settings.mapHeight || 180;
|
|
28149
|
+
const mapZoom = settings.mapZoom || 15;
|
|
28150
|
+
const getMapEmbedUrl = () => {
|
|
28151
|
+
const query = location.lat && location.lng
|
|
28152
|
+
? `${location.lat},${location.lng}`
|
|
28153
|
+
: encodeURIComponent(location.address);
|
|
28154
|
+
return `https://www.google.com/maps/embed/v1/place?key=AIzaSyBFw0Qbyq9zTFTd-tUY6dZWTgaQzuU17R8&q=${query}&zoom=${mapZoom}`;
|
|
28155
|
+
};
|
|
28156
|
+
const handleCall = () => {
|
|
28157
|
+
if (location.phone) {
|
|
28158
|
+
window.location.href = `tel:${location.phone}`;
|
|
28159
|
+
}
|
|
28160
|
+
};
|
|
28161
|
+
const style = accentColor ? { '--action-accent': accentColor } : undefined;
|
|
28162
|
+
// Use smaller map height in compact mode
|
|
28163
|
+
const effectiveMapHeight = compact ? Math.min(mapHeight, 140) : mapHeight;
|
|
28164
|
+
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
|
+
}
|
|
28166
|
+
function LocationCard({ action, onComplete, accentColor, maxColumns = 3 }) {
|
|
28167
|
+
const rawState = action.state;
|
|
28168
|
+
const hasCompletedRef = useRef(false);
|
|
28169
|
+
const state = {
|
|
28170
|
+
locations: rawState?.locations || [],
|
|
28171
|
+
settings: rawState?.settings || {},
|
|
28172
|
+
matchInfo: rawState?.matchInfo,
|
|
28173
|
+
status: rawState?.status || 'displaying',
|
|
28174
|
+
};
|
|
28175
|
+
const { locations, settings } = state;
|
|
28176
|
+
const layout = settings.multiLocationLayout || 'stack';
|
|
28177
|
+
const isSingleLocation = locations.length === 1;
|
|
28178
|
+
const stackColumns = Math.min(Math.max(locations.length, 1), maxColumns);
|
|
28179
|
+
useEffect(() => {
|
|
28180
|
+
if (!action.done && !hasCompletedRef.current && onComplete && locations.length > 0) {
|
|
28181
|
+
hasCompletedRef.current = true;
|
|
28182
|
+
onComplete(action.toolCallId, { ...state, status: 'displaying' });
|
|
28183
|
+
}
|
|
28184
|
+
}, [action.done, action.toolCallId, onComplete, state, locations.length]);
|
|
28185
|
+
const handleDirections = (location) => {
|
|
28186
|
+
const url = location.lat && location.lng
|
|
28187
|
+
? `https://www.google.com/maps/dir/?api=1&destination=${location.lat},${location.lng}`
|
|
28188
|
+
: `https://www.google.com/maps/dir/?api=1&destination=${encodeURIComponent(location.address)}`;
|
|
28189
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
28190
|
+
onComplete?.(action.toolCallId, { ...state, status: 'directions_opened' });
|
|
28191
|
+
};
|
|
28192
|
+
if (locations.length === 0) {
|
|
28193
|
+
return (jsx("div", { className: "ai-chat-location-card ai-chat-location-card--error", children: jsx("p", { children: "No locations to display" }) }));
|
|
28194
|
+
}
|
|
28195
|
+
if (isSingleLocation) {
|
|
28196
|
+
return (jsx(LocationItem, { location: locations[0], settings: settings, accentColor: accentColor, onDirections: () => handleDirections(locations[0]), showMap: true }));
|
|
28197
|
+
}
|
|
28198
|
+
return (jsxs("div", { className: `ai-chat-location-card-list ai-chat-location-card-list--${layout}`, children: [jsxs("div", { className: "ai-chat-location-card-list__header", children: [jsx(MapPinIcon, {}), jsxs("span", { children: [locations.length, " Locations"] })] }), layout === 'stack' && (jsx("div", { className: `ai-chat-location-card-list__stack ai-chat-location-card-list__stack--cols-${stackColumns}`, children: locations.map((location) => (jsx(LocationItem, { location: location, settings: settings, accentColor: accentColor, onDirections: () => handleDirections(location), showMap: settings.showMap !== false, compact: locations.length > 2 }, location.id))) })), layout === 'grid' && (jsx("div", { className: "ai-chat-location-card-list__grid", children: locations.map((location) => (jsx(LocationItem, { location: location, settings: settings, accentColor: accentColor, onDirections: () => handleDirections(location), showMap: false, compact: true }, location.id))) })), layout === 'carousel' && (jsx("div", { className: "ai-chat-location-card-list__carousel", children: locations.map((location) => (jsx(LocationItem, { location: location, settings: settings, accentColor: accentColor, onDirections: () => handleDirections(location), showMap: true, compact: false }, location.id))) }))] }));
|
|
28199
|
+
}
|
|
28200
|
+
|
|
28201
|
+
function UsersIcon() {
|
|
28202
|
+
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: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" }), jsx("circle", { cx: "9", cy: "7", r: "4" }), jsx("path", { d: "M22 21v-2a4 4 0 0 0-3-3.87" }), jsx("path", { d: "M16 3.13a4 4 0 0 1 0 7.75" })] }));
|
|
28203
|
+
}
|
|
28204
|
+
const AVATAR_COLORS = [
|
|
28205
|
+
{ bg: '#E8F5E9', text: '#2E7D32' }, // Green
|
|
28206
|
+
{ bg: '#E3F2FD', text: '#1565C0' }, // Blue
|
|
28207
|
+
{ bg: '#FFF3E0', text: '#E65100' }, // Orange
|
|
28208
|
+
{ bg: '#F3E5F5', text: '#7B1FA2' }, // Purple
|
|
28209
|
+
{ bg: '#FFEBEE', text: '#C62828' }, // Red
|
|
28210
|
+
{ bg: '#E0F7FA', text: '#00838F' }, // Cyan
|
|
28211
|
+
{ bg: '#FFF8E1', text: '#F9A825' }, // Amber
|
|
28212
|
+
{ bg: '#FCE4EC', text: '#AD1457' }, // Pink
|
|
28213
|
+
];
|
|
28214
|
+
function getInitials(name) {
|
|
28215
|
+
const parts = name.trim().split(/\s+/);
|
|
28216
|
+
if (parts.length === 1) {
|
|
28217
|
+
return parts[0].substring(0, 2).toUpperCase();
|
|
28218
|
+
}
|
|
28219
|
+
return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
|
|
28220
|
+
}
|
|
28221
|
+
function getColorFromName(name) {
|
|
28222
|
+
let hash = 0;
|
|
28223
|
+
for (let i = 0; i < name.length; i++) {
|
|
28224
|
+
hash = name.charCodeAt(i) + ((hash << 5) - hash);
|
|
28225
|
+
}
|
|
28226
|
+
return AVATAR_COLORS[Math.abs(hash) % AVATAR_COLORS.length];
|
|
28227
|
+
}
|
|
28228
|
+
function ContactItem({ contact, settings, accentColor, onEmail, onPhone, compact = false, layout = 'vertical', }) {
|
|
28229
|
+
const style = accentColor ? { '--action-accent': accentColor } : undefined;
|
|
28230
|
+
const layoutClass = layout === 'horizontal'
|
|
28231
|
+
? 'ai-chat-contact-card--horizontal'
|
|
28232
|
+
: 'ai-chat-contact-card--vertical';
|
|
28233
|
+
return (jsxs("div", { className: `ai-chat-action-card ai-chat-contact-card ${layoutClass} ${compact ? 'ai-chat-contact-card--compact' : ''}`, style: style, children: [jsxs("div", { className: "ai-chat-contact-card__image-section", children: [contact.profilePictureUrl ? (jsx("img", { src: contact.profilePictureUrl, alt: contact.name, className: "ai-chat-contact-card__image", onError: (e) => {
|
|
28234
|
+
e.currentTarget.style.display = 'none';
|
|
28235
|
+
const placeholder = e.currentTarget.parentElement?.querySelector('.ai-chat-contact-card__initials');
|
|
28236
|
+
if (placeholder) {
|
|
28237
|
+
placeholder.style.display = 'flex';
|
|
28238
|
+
}
|
|
28239
|
+
} })) : null, (() => {
|
|
28240
|
+
const colors = getColorFromName(contact.name);
|
|
28241
|
+
return (jsx("div", { className: "ai-chat-contact-card__initials", style: {
|
|
28242
|
+
display: contact.profilePictureUrl ? 'none' : 'flex',
|
|
28243
|
+
backgroundColor: colors.bg,
|
|
28244
|
+
color: colors.text,
|
|
28245
|
+
}, children: getInitials(contact.name) }));
|
|
28246
|
+
})()] }), 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
|
+
}
|
|
28248
|
+
function ContactCard({ action, onComplete, accentColor, maxColumns = 3 }) {
|
|
28249
|
+
const rawState = action.state;
|
|
28250
|
+
const hasCompletedRef = useRef(false);
|
|
28251
|
+
const state = {
|
|
28252
|
+
contacts: rawState?.contacts || [],
|
|
28253
|
+
settings: rawState?.settings || {},
|
|
28254
|
+
query: rawState?.query,
|
|
28255
|
+
status: rawState?.status || 'displaying',
|
|
28256
|
+
};
|
|
28257
|
+
const { contacts, settings } = state;
|
|
28258
|
+
const isSingleContact = contacts.length === 1;
|
|
28259
|
+
const stackColumns = Math.min(Math.max(contacts.length, 1), maxColumns);
|
|
28260
|
+
useEffect(() => {
|
|
28261
|
+
if (!action.done && !hasCompletedRef.current && onComplete) {
|
|
28262
|
+
hasCompletedRef.current = true;
|
|
28263
|
+
onComplete(action.toolCallId, { ...state, status: 'displaying' });
|
|
28264
|
+
}
|
|
28265
|
+
}, [action.done, action.toolCallId, onComplete, state]);
|
|
28266
|
+
const handleContact = () => {
|
|
28267
|
+
onComplete?.(action.toolCallId, { ...state, status: 'contacted' });
|
|
28268
|
+
};
|
|
28269
|
+
if (contacts.length === 0) {
|
|
28270
|
+
return (jsxs("div", { className: "ai-chat-contact-card ai-chat-contact-card--empty", children: [jsx("div", { className: "ai-chat-contact-card__empty-icon", children: jsx(UsersIcon, {}) }), jsxs("p", { className: "ai-chat-contact-card__empty-text", children: ["No contacts found", state.query ? ` for "${state.query}"` : ''] })] }));
|
|
28271
|
+
}
|
|
28272
|
+
if (isSingleContact) {
|
|
28273
|
+
return (jsx(ContactItem, { contact: contacts[0], settings: settings, accentColor: accentColor, onEmail: handleContact, onPhone: handleContact, layout: settings.layout || 'horizontal' }));
|
|
28274
|
+
}
|
|
28275
|
+
const isWidget = maxColumns === 1;
|
|
28276
|
+
const stackClassName = isWidget
|
|
28277
|
+
? 'ai-chat-contact-card-list__stack ai-chat-contact-card-list__stack--widget'
|
|
28278
|
+
: 'ai-chat-contact-card-list__stack';
|
|
28279
|
+
return (jsxs("div", { className: "ai-chat-contact-card-list", style: { containerType: 'inline-size' }, children: [jsxs("div", { className: "ai-chat-contact-card-list__header", children: [jsx(UsersIcon, {}), jsxs("span", { children: [contacts.length, " Contacts"] })] }), jsx("div", { className: stackClassName, style: isWidget ? undefined : {
|
|
28280
|
+
gridTemplateColumns: `repeat(${stackColumns}, minmax(0, 1fr))`,
|
|
28281
|
+
}, 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
|
+
}
|
|
28283
|
+
|
|
28284
|
+
function FormCard({ action, onComplete, accentColor }) {
|
|
28285
|
+
const state = action.state;
|
|
28286
|
+
const [currentStep, setCurrentStep] = useState(0);
|
|
28287
|
+
const [answers, setAnswers] = useState({});
|
|
28288
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
28289
|
+
const questions = state.questions || [];
|
|
28290
|
+
const currentQuestion = questions[currentStep];
|
|
28291
|
+
const totalQuestions = questions.length;
|
|
28292
|
+
const handleAnswerChange = (questionId, value) => {
|
|
28293
|
+
setAnswers((prev) => ({ ...prev, [questionId]: value }));
|
|
28294
|
+
};
|
|
28295
|
+
const handleNext = () => {
|
|
28296
|
+
if (currentStep < totalQuestions - 1) {
|
|
28297
|
+
setCurrentStep((prev) => prev + 1);
|
|
28298
|
+
}
|
|
28299
|
+
};
|
|
28300
|
+
const handlePrev = () => {
|
|
28301
|
+
if (currentStep > 0) {
|
|
28302
|
+
setCurrentStep((prev) => prev - 1);
|
|
28303
|
+
}
|
|
28304
|
+
};
|
|
28305
|
+
const handleSubmit = () => {
|
|
28306
|
+
if (!onComplete)
|
|
28307
|
+
return;
|
|
28308
|
+
setIsSubmitting(true);
|
|
28309
|
+
const formattedAnswers = Object.entries(answers).map(([questionId, value]) => ({
|
|
28310
|
+
questionId,
|
|
28311
|
+
value,
|
|
28312
|
+
}));
|
|
28313
|
+
onComplete(action.toolCallId, {
|
|
28314
|
+
...state,
|
|
28315
|
+
status: 'submitted',
|
|
28316
|
+
answers: formattedAnswers,
|
|
28317
|
+
});
|
|
28318
|
+
};
|
|
28319
|
+
const handleSkip = () => {
|
|
28320
|
+
if (!onComplete || !state.settings.allowSkip)
|
|
28321
|
+
return;
|
|
28322
|
+
onComplete(action.toolCallId, {
|
|
28323
|
+
...state,
|
|
28324
|
+
status: 'skipped',
|
|
28325
|
+
});
|
|
28326
|
+
};
|
|
28327
|
+
const isCurrentAnswered = () => {
|
|
28328
|
+
if (!currentQuestion)
|
|
28329
|
+
return false;
|
|
28330
|
+
const answer = answers[currentQuestion.id];
|
|
28331
|
+
if (!answer)
|
|
28332
|
+
return !currentQuestion.required;
|
|
28333
|
+
if (Array.isArray(answer))
|
|
28334
|
+
return answer.length > 0 || !currentQuestion.required;
|
|
28335
|
+
return answer.trim() !== '' || !currentQuestion.required;
|
|
28336
|
+
};
|
|
28337
|
+
const canSubmit = () => {
|
|
28338
|
+
return questions.every((q) => {
|
|
28339
|
+
const answer = answers[q.id];
|
|
28340
|
+
if (!q.required)
|
|
28341
|
+
return true;
|
|
28342
|
+
if (!answer)
|
|
28343
|
+
return false;
|
|
28344
|
+
if (Array.isArray(answer))
|
|
28345
|
+
return answer.length > 0;
|
|
28346
|
+
return answer.trim() !== '';
|
|
28347
|
+
});
|
|
28348
|
+
};
|
|
28349
|
+
// Error state
|
|
28350
|
+
if (state.status === 'error') {
|
|
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: "\u26A0\uFE0F" }), 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
|
+
}
|
|
28353
|
+
// Submitted state
|
|
28354
|
+
if (state.status === 'submitted' || action.done) {
|
|
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: "\u2713" }), 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
|
+
}
|
|
28357
|
+
// Skipped state
|
|
28358
|
+
if (state.status === 'skipped') {
|
|
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: "\u21B7" }), jsx("span", { className: "ai-chat-form-card__title", children: state.title })] }), jsx("p", { className: "ai-chat-form-card__skipped-text", children: "Form skipped" })] }));
|
|
28360
|
+
}
|
|
28361
|
+
// No questions
|
|
28362
|
+
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: "\uD83D\uDCCB" }), 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
|
+
}
|
|
28365
|
+
return (jsxs("div", { className: "ai-chat-form-card", children: [jsxs("div", { className: "ai-chat-form-card__header", children: [jsx("span", { className: "ai-chat-form-card__icon", children: "\uD83D\uDCCB" }), jsx("span", { className: "ai-chat-form-card__title", children: state.title })] }), 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
|
+
width: `${((currentStep + 1) / totalQuestions) * 100}%`,
|
|
28367
|
+
backgroundColor: accentColor || 'var(--ai-chat-accent-color, #3b82f6)',
|
|
28368
|
+
} }), 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) => {
|
|
28369
|
+
const currentAnswers = answers[currentQuestion.id] || [];
|
|
28370
|
+
const isChecked = currentAnswers.includes(option.value);
|
|
28371
|
+
return (jsxs("label", { className: "ai-chat-form-card__option", children: [jsx("input", { type: "checkbox", value: option.value, checked: isChecked, onChange: () => {
|
|
28372
|
+
const newAnswers = isChecked
|
|
28373
|
+
? currentAnswers.filter((v) => v !== option.value)
|
|
28374
|
+
: [...currentAnswers, option.value];
|
|
28375
|
+
handleAnswerChange(currentQuestion.id, newAnswers);
|
|
28376
|
+
} }), jsx("span", { className: "ai-chat-form-card__option-text", children: option.text })] }, option.id));
|
|
28377
|
+
}) })), currentQuestion.type === 'rating' && (jsx("div", { className: "ai-chat-form-card__rating", children: Array.from({ length: (currentQuestion.maxRating || 5) - (currentQuestion.minRating || 1) + 1 }, (_, i) => (currentQuestion.minRating || 1) + i).map((rating) => (jsx("button", { type: "button", className: `ai-chat-form-card__rating-btn ${answers[currentQuestion.id] === String(rating) ? 'ai-chat-form-card__rating-btn--selected' : ''}`, onClick: () => handleAnswerChange(currentQuestion.id, String(rating)), style: {
|
|
28378
|
+
borderColor: answers[currentQuestion.id] === String(rating)
|
|
28379
|
+
? (accentColor || 'var(--ai-chat-accent-color, #3b82f6)')
|
|
28380
|
+
: undefined,
|
|
28381
|
+
backgroundColor: answers[currentQuestion.id] === String(rating)
|
|
28382
|
+
? (accentColor || 'var(--ai-chat-accent-color, #3b82f6)')
|
|
28383
|
+
: undefined,
|
|
28384
|
+
}, children: rating }, rating))) }))] })] }), jsxs("div", { className: "ai-chat-form-card__actions", children: [currentStep > 0 && (jsx("button", { type: "button", className: "ai-chat-form-card__btn ai-chat-form-card__btn--secondary", onClick: handlePrev, children: "Back" })), state.settings.allowSkip && currentStep === 0 && (jsx("button", { type: "button", className: "ai-chat-form-card__btn ai-chat-form-card__btn--ghost", onClick: handleSkip, children: "Skip" })), jsx("div", { className: "ai-chat-form-card__actions-spacer" }), currentStep < totalQuestions - 1 ? (jsx("button", { type: "button", className: "ai-chat-form-card__btn ai-chat-form-card__btn--primary", onClick: handleNext, disabled: !isCurrentAnswered(), style: {
|
|
28385
|
+
backgroundColor: accentColor || 'var(--ai-chat-accent-color, #3b82f6)',
|
|
28386
|
+
}, children: "Next" })) : (jsx("button", { type: "button", className: "ai-chat-form-card__btn ai-chat-form-card__btn--primary", onClick: handleSubmit, disabled: !canSubmit() || isSubmitting, style: {
|
|
28387
|
+
backgroundColor: accentColor || 'var(--ai-chat-accent-color, #3b82f6)',
|
|
28388
|
+
}, children: isSubmitting ? 'Submitting...' : (state.settings.submitButtonText || 'Submit') }))] })] }));
|
|
27519
28389
|
}
|
|
27520
28390
|
|
|
27521
28391
|
const pendingResolvers = new Map();
|
|
@@ -27528,12 +28398,6 @@ function getFrontendActionHandler(implementation) {
|
|
|
27528
28398
|
function getActionRenderer(implementation) {
|
|
27529
28399
|
return actionRenderers[implementation];
|
|
27530
28400
|
}
|
|
27531
|
-
function getActionPrompt(implementation) {
|
|
27532
|
-
if (implementation === "google-calendar-appointment") {
|
|
27533
|
-
return "Select a date to continue.";
|
|
27534
|
-
}
|
|
27535
|
-
return "Action input required.";
|
|
27536
|
-
}
|
|
27537
28401
|
function waitForActionState(toolCallId) {
|
|
27538
28402
|
return new Promise((resolve) => {
|
|
27539
28403
|
pendingResolvers.set(toolCallId, resolve);
|
|
@@ -27560,26 +28424,272 @@ function unregisterActionResumeCallback(toolCallId) {
|
|
|
27560
28424
|
resumeCallbacks.delete(toolCallId);
|
|
27561
28425
|
}
|
|
27562
28426
|
|
|
27563
|
-
|
|
27564
|
-
|
|
27565
|
-
|
|
28427
|
+
function registerGoogleCalendarHandler() {
|
|
28428
|
+
frontendActionHandlers["google-calendar-appointment"] = async (_input, _state, context) => {
|
|
28429
|
+
return waitForActionState(context.toolCallId);
|
|
28430
|
+
};
|
|
28431
|
+
}
|
|
27566
28432
|
|
|
27567
|
-
|
|
27568
|
-
|
|
27569
|
-
|
|
27570
|
-
|
|
27571
|
-
|
|
27572
|
-
|
|
28433
|
+
/**
|
|
28434
|
+
* Register google-calendar-appointment action handler and renderer.
|
|
28435
|
+
* Called by initializeActionHandlers to prevent tree-shaking.
|
|
28436
|
+
*/
|
|
28437
|
+
function registerGoogleCalendarAction() {
|
|
28438
|
+
// Register the handler
|
|
28439
|
+
registerGoogleCalendarHandler();
|
|
28440
|
+
// Register the renderer
|
|
28441
|
+
actionRenderers["google-calendar-appointment"] = (message, accentColor) => {
|
|
28442
|
+
const action = message.action;
|
|
28443
|
+
if (!action)
|
|
28444
|
+
return null;
|
|
28445
|
+
const handleComplete = (toolCallId, newState) => {
|
|
28446
|
+
resolveActionState(toolCallId, newState);
|
|
28447
|
+
};
|
|
28448
|
+
return (jsx(GoogleCalendarCard, { action: {
|
|
28449
|
+
implementation: action.implementation,
|
|
28450
|
+
toolCallId: action.toolCallId,
|
|
28451
|
+
actionId: action.actionId,
|
|
28452
|
+
input: action.input,
|
|
28453
|
+
state: action.state,
|
|
28454
|
+
done: action.done ?? false,
|
|
28455
|
+
}, onComplete: handleComplete, accentColor: accentColor }));
|
|
27573
28456
|
};
|
|
27574
|
-
|
|
27575
|
-
|
|
27576
|
-
|
|
27577
|
-
|
|
27578
|
-
|
|
27579
|
-
|
|
27580
|
-
|
|
27581
|
-
|
|
27582
|
-
|
|
28457
|
+
}
|
|
28458
|
+
|
|
28459
|
+
function registerMicrosoftCalendarHandler() {
|
|
28460
|
+
frontendActionHandlers["microsoft-calendar-appointment"] = async (_input, _state, context) => {
|
|
28461
|
+
return waitForActionState(context.toolCallId);
|
|
28462
|
+
};
|
|
28463
|
+
}
|
|
28464
|
+
|
|
28465
|
+
/**
|
|
28466
|
+
* Register microsoft-calendar-appointment action handler and renderer.
|
|
28467
|
+
* Called by initializeActionHandlers to prevent tree-shaking.
|
|
28468
|
+
*/
|
|
28469
|
+
function registerMicrosoftCalendarAction() {
|
|
28470
|
+
// Register the handler
|
|
28471
|
+
registerMicrosoftCalendarHandler();
|
|
28472
|
+
// Register the renderer
|
|
28473
|
+
actionRenderers["microsoft-calendar-appointment"] = (message, accentColor) => {
|
|
28474
|
+
const action = message.action;
|
|
28475
|
+
if (!action)
|
|
28476
|
+
return null;
|
|
28477
|
+
const handleComplete = (toolCallId, newState) => {
|
|
28478
|
+
resolveActionState(toolCallId, newState);
|
|
28479
|
+
};
|
|
28480
|
+
return (jsx(MicrosoftCalendarCard, { action: {
|
|
28481
|
+
implementation: action.implementation,
|
|
28482
|
+
toolCallId: action.toolCallId,
|
|
28483
|
+
actionId: action.actionId,
|
|
28484
|
+
input: action.input,
|
|
28485
|
+
state: action.state,
|
|
28486
|
+
done: action.done ?? false,
|
|
28487
|
+
}, onComplete: handleComplete, accentColor: accentColor }));
|
|
28488
|
+
};
|
|
28489
|
+
}
|
|
28490
|
+
|
|
28491
|
+
/**
|
|
28492
|
+
* Register link-preview action handler and renderer.
|
|
28493
|
+
* Called by initializeActionHandlers to prevent tree-shaking.
|
|
28494
|
+
*/
|
|
28495
|
+
function registerLinkPreviewAction() {
|
|
28496
|
+
// Handler - auto-completes immediately since no user input is needed
|
|
28497
|
+
frontendActionHandlers["link-preview"] = async (_input, state, _context) => {
|
|
28498
|
+
return { ...state, status: "displaying" };
|
|
28499
|
+
};
|
|
28500
|
+
// Renderer - displays the link preview card
|
|
28501
|
+
actionRenderers["link-preview"] = (message, accentColor) => {
|
|
28502
|
+
const action = message.action;
|
|
28503
|
+
if (!action)
|
|
28504
|
+
return null;
|
|
28505
|
+
const handleComplete = (toolCallId, newState) => {
|
|
28506
|
+
resolveActionState(toolCallId, newState);
|
|
28507
|
+
};
|
|
28508
|
+
// Check if action state indicates it's already complete (displaying or clicked)
|
|
28509
|
+
const state = action.state;
|
|
28510
|
+
const status = state?.status;
|
|
28511
|
+
const isDone = action.done || status === "displaying" || status === "clicked";
|
|
28512
|
+
return (jsx(LinkPreviewCard, { action: {
|
|
28513
|
+
implementation: action.implementation,
|
|
28514
|
+
toolCallId: action.toolCallId,
|
|
28515
|
+
actionId: action.actionId,
|
|
28516
|
+
input: action.input,
|
|
28517
|
+
state: action.state,
|
|
28518
|
+
done: isDone,
|
|
28519
|
+
}, onComplete: handleComplete, accentColor: accentColor }));
|
|
28520
|
+
};
|
|
28521
|
+
}
|
|
28522
|
+
|
|
28523
|
+
/**
|
|
28524
|
+
* Register video-player action handler and renderer.
|
|
28525
|
+
* Called by initializeActionHandlers to prevent tree-shaking.
|
|
28526
|
+
*/
|
|
28527
|
+
function registerVideoPlayerAction() {
|
|
28528
|
+
// Handler - auto-completes immediately since no user input is needed
|
|
28529
|
+
frontendActionHandlers["video-player"] = async (_input, state, _context) => {
|
|
28530
|
+
return { ...state, status: "displaying" };
|
|
28531
|
+
};
|
|
28532
|
+
// Renderer - displays the embedded video player card
|
|
28533
|
+
actionRenderers["video-player"] = (message, accentColor) => {
|
|
28534
|
+
const action = message.action;
|
|
28535
|
+
if (!action)
|
|
28536
|
+
return null;
|
|
28537
|
+
const handleComplete = (toolCallId, newState) => {
|
|
28538
|
+
resolveActionState(toolCallId, newState);
|
|
28539
|
+
};
|
|
28540
|
+
// Check if action state indicates it's already complete (displaying or played)
|
|
28541
|
+
const state = action.state;
|
|
28542
|
+
const status = state?.status;
|
|
28543
|
+
const isDone = action.done || status === "displaying" || status === "played";
|
|
28544
|
+
return (jsx(VideoPlayerCard, { action: {
|
|
28545
|
+
implementation: action.implementation,
|
|
28546
|
+
toolCallId: action.toolCallId,
|
|
28547
|
+
actionId: action.actionId,
|
|
28548
|
+
input: action.input,
|
|
28549
|
+
state: action.state,
|
|
28550
|
+
done: isDone,
|
|
28551
|
+
}, onComplete: handleComplete, accentColor: accentColor }));
|
|
28552
|
+
};
|
|
28553
|
+
}
|
|
28554
|
+
|
|
28555
|
+
/**
|
|
28556
|
+
* Register location-card action handler and renderer.
|
|
28557
|
+
* Called by initializeActionHandlers to prevent tree-shaking.
|
|
28558
|
+
*/
|
|
28559
|
+
function registerLocationCardAction() {
|
|
28560
|
+
// Handler - auto-completes immediately since no user input is needed
|
|
28561
|
+
frontendActionHandlers["location-card"] = async (_input, state, _context) => {
|
|
28562
|
+
return { ...state, status: "displaying" };
|
|
28563
|
+
};
|
|
28564
|
+
// Renderer - displays the location card
|
|
28565
|
+
actionRenderers["location-card"] = (message, accentColor, variant) => {
|
|
28566
|
+
const action = message.action;
|
|
28567
|
+
if (!action)
|
|
28568
|
+
return null;
|
|
28569
|
+
const handleComplete = (toolCallId, newState) => {
|
|
28570
|
+
resolveActionState(toolCallId, newState);
|
|
28571
|
+
};
|
|
28572
|
+
// Check if action state indicates it's already complete
|
|
28573
|
+
const state = action.state;
|
|
28574
|
+
const status = state?.status;
|
|
28575
|
+
const isDone = action.done || status === "displaying" || status === "directions_opened";
|
|
28576
|
+
return (jsx(LocationCard, { action: {
|
|
28577
|
+
implementation: action.implementation,
|
|
28578
|
+
toolCallId: action.toolCallId,
|
|
28579
|
+
actionId: action.actionId,
|
|
28580
|
+
input: action.input,
|
|
28581
|
+
state: action.state,
|
|
28582
|
+
done: isDone,
|
|
28583
|
+
}, onComplete: handleComplete, accentColor: accentColor, maxColumns: variant === 'fullpage' ? 3 : 1 }));
|
|
28584
|
+
};
|
|
28585
|
+
}
|
|
28586
|
+
|
|
28587
|
+
function registerContactCardAction() {
|
|
28588
|
+
// Handler - auto-completes immediately since no user input is needed
|
|
28589
|
+
frontendActionHandlers['contact-card'] = async (_input, state, _context) => {
|
|
28590
|
+
return { ...state, status: 'displaying' };
|
|
28591
|
+
};
|
|
28592
|
+
// Renderer - displays the contact card
|
|
28593
|
+
actionRenderers['contact-card'] = (message, accentColor, variant) => {
|
|
28594
|
+
const action = message.action;
|
|
28595
|
+
if (!action)
|
|
28596
|
+
return null;
|
|
28597
|
+
const handleComplete = (toolCallId, newState) => {
|
|
28598
|
+
resolveActionState(toolCallId, newState);
|
|
28599
|
+
};
|
|
28600
|
+
// Check if action state indicates it's already complete
|
|
28601
|
+
const state = action.state;
|
|
28602
|
+
const status = state?.status;
|
|
28603
|
+
const isDone = action.done || status === 'displaying' || status === 'contacted';
|
|
28604
|
+
return (jsx(ContactCard, { action: {
|
|
28605
|
+
implementation: action.implementation,
|
|
28606
|
+
toolCallId: action.toolCallId,
|
|
28607
|
+
actionId: action.actionId,
|
|
28608
|
+
input: action.input,
|
|
28609
|
+
state: action.state,
|
|
28610
|
+
done: isDone,
|
|
28611
|
+
}, onComplete: handleComplete, accentColor: accentColor, maxColumns: variant === 'fullpage' ? 3 : 1 }));
|
|
28612
|
+
};
|
|
28613
|
+
}
|
|
28614
|
+
|
|
28615
|
+
function registerQueryContactDirectoryAction() {
|
|
28616
|
+
// Handler - auto-completes immediately since no user input is needed
|
|
28617
|
+
frontendActionHandlers['query-contact-directory'] = async (_input, state, _context) => {
|
|
28618
|
+
return { ...state, status: 'displaying' };
|
|
28619
|
+
};
|
|
28620
|
+
// Renderer - displays the contact card with search results
|
|
28621
|
+
actionRenderers['query-contact-directory'] = (message, accentColor, variant) => {
|
|
28622
|
+
const action = message.action;
|
|
28623
|
+
if (!action)
|
|
28624
|
+
return null;
|
|
28625
|
+
// Handle completion - triggers agent to continue with text response
|
|
28626
|
+
const handleComplete = (toolCallId, newState) => {
|
|
28627
|
+
resolveActionState(toolCallId, newState);
|
|
28628
|
+
};
|
|
28629
|
+
// Check if action state indicates it's already complete
|
|
28630
|
+
const state = action.state;
|
|
28631
|
+
const status = state?.status;
|
|
28632
|
+
const isDone = action.done || status === 'displaying' || status === 'contacted';
|
|
28633
|
+
return (jsx(ContactCard, { action: {
|
|
28634
|
+
implementation: action.implementation,
|
|
28635
|
+
toolCallId: action.toolCallId,
|
|
28636
|
+
actionId: action.actionId,
|
|
28637
|
+
input: action.input,
|
|
28638
|
+
state: action.state,
|
|
28639
|
+
done: isDone,
|
|
28640
|
+
}, onComplete: handleComplete, accentColor: accentColor, maxColumns: variant === 'fullpage' ? 3 : 1 }));
|
|
28641
|
+
};
|
|
28642
|
+
}
|
|
28643
|
+
|
|
28644
|
+
function registerDisplayFormAction() {
|
|
28645
|
+
// Handler - handles form submission and state updates
|
|
28646
|
+
frontendActionHandlers['display-form'] = async (_input, _state, context) => {
|
|
28647
|
+
return waitForActionState(context.toolCallId);
|
|
28648
|
+
};
|
|
28649
|
+
// Renderer - displays the form card
|
|
28650
|
+
actionRenderers['display-form'] = (message, accentColor, variant) => {
|
|
28651
|
+
const action = message.action;
|
|
28652
|
+
if (!action)
|
|
28653
|
+
return null;
|
|
28654
|
+
const handleComplete = (toolCallId, newState) => {
|
|
28655
|
+
resolveActionState(toolCallId, newState);
|
|
28656
|
+
};
|
|
28657
|
+
// Check if action state indicates it's already complete
|
|
28658
|
+
const state = action.state;
|
|
28659
|
+
const status = state?.status;
|
|
28660
|
+
const isDone = action.done || status === 'completed' || status === 'submitted';
|
|
28661
|
+
return (jsx(FormCard, { action: {
|
|
28662
|
+
implementation: action.implementation,
|
|
28663
|
+
toolCallId: action.toolCallId,
|
|
28664
|
+
actionId: action.actionId,
|
|
28665
|
+
input: action.input,
|
|
28666
|
+
state: action.state,
|
|
28667
|
+
done: isDone,
|
|
28668
|
+
}, onComplete: handleComplete, accentColor: accentColor }));
|
|
28669
|
+
};
|
|
28670
|
+
}
|
|
28671
|
+
|
|
28672
|
+
// Track if handlers have been initialized
|
|
28673
|
+
let initialized = false;
|
|
28674
|
+
/**
|
|
28675
|
+
* Initialize all action handlers.
|
|
28676
|
+
* Call this to ensure handlers are registered (prevents tree-shaking in production builds).
|
|
28677
|
+
* Safe to call multiple times - will only register once.
|
|
28678
|
+
*/
|
|
28679
|
+
function initializeActionHandlers() {
|
|
28680
|
+
if (initialized)
|
|
28681
|
+
return;
|
|
28682
|
+
initialized = true;
|
|
28683
|
+
// Explicitly call each registration function to prevent tree-shaking
|
|
28684
|
+
registerGoogleCalendarAction();
|
|
28685
|
+
registerMicrosoftCalendarAction();
|
|
28686
|
+
registerLinkPreviewAction();
|
|
28687
|
+
registerVideoPlayerAction();
|
|
28688
|
+
registerLocationCardAction();
|
|
28689
|
+
registerQueryContactDirectoryAction();
|
|
28690
|
+
registerContactCardAction();
|
|
28691
|
+
registerDisplayFormAction();
|
|
28692
|
+
}
|
|
27583
28693
|
|
|
27584
28694
|
/**
|
|
27585
28695
|
* Local Storage Utilities
|
|
@@ -27798,10 +28908,6 @@ function isStorageAvailable() {
|
|
|
27798
28908
|
}
|
|
27799
28909
|
}
|
|
27800
28910
|
|
|
27801
|
-
/**
|
|
27802
|
-
* useChat Hook
|
|
27803
|
-
* Main state management for chat functionality
|
|
27804
|
-
*/
|
|
27805
28911
|
function hydrateToolNames(messages) {
|
|
27806
28912
|
const toolCallNameById = new Map();
|
|
27807
28913
|
for (const entry of messages) {
|
|
@@ -27845,81 +28951,77 @@ function hydrateToolNames(messages) {
|
|
|
27845
28951
|
};
|
|
27846
28952
|
});
|
|
27847
28953
|
}
|
|
27848
|
-
function hydrateActionContent(messages) {
|
|
27849
|
-
return messages.map((entry) => {
|
|
27850
|
-
if (entry.message.role !== "assistant" || !entry.action) {
|
|
27851
|
-
return entry;
|
|
27852
|
-
}
|
|
27853
|
-
const content = typeof entry.message.content === "string" ? entry.message.content : "";
|
|
27854
|
-
if (content.trim().length > 0) {
|
|
27855
|
-
return entry;
|
|
27856
|
-
}
|
|
27857
|
-
return {
|
|
27858
|
-
...entry,
|
|
27859
|
-
message: { ...entry.message, content: getActionPrompt(entry.action.implementation) },
|
|
27860
|
-
};
|
|
27861
|
-
});
|
|
27862
|
-
}
|
|
27863
28954
|
function hydrateMessages(messages) {
|
|
27864
|
-
return
|
|
28955
|
+
return hydrateToolNames(messages);
|
|
27865
28956
|
}
|
|
27866
|
-
|
|
27867
|
-
|
|
27868
|
-
|
|
27869
|
-
|
|
27870
|
-
|
|
27871
|
-
|
|
27872
|
-
|
|
27873
|
-
|
|
27874
|
-
|
|
27875
|
-
|
|
27876
|
-
|
|
27877
|
-
|
|
27878
|
-
|
|
27879
|
-
|
|
27880
|
-
|
|
27881
|
-
|
|
27882
|
-
}
|
|
27883
|
-
: m),
|
|
27884
|
-
isTyping: true,
|
|
27885
|
-
}));
|
|
27886
|
-
const streamState = createStreamState();
|
|
27887
|
-
// Continue the agent stream with the new state
|
|
27888
|
-
for await (const event of client.continueAgentMessageStream(conversationId, toolCallId, newState)) {
|
|
27889
|
-
if (event.type === "done") {
|
|
27890
|
-
finalizeToolMessage(streamState, setState, toolCallId, toolName);
|
|
27891
|
-
streamState.sources = event.sources;
|
|
27892
|
-
streamState.toolCallToActionId = event.tool_call_to_action_id;
|
|
27893
|
-
finalizeStreamMessages(setState, streamState.newMessageIds, event.sources, event.tool_call_to_action_id, event.suggestions);
|
|
27894
|
-
continue;
|
|
27895
|
-
}
|
|
27896
|
-
if (event.type === "error") {
|
|
27897
|
-
const errorMessage = {
|
|
27898
|
-
id: generateMessageId(),
|
|
27899
|
-
message: {
|
|
27900
|
-
role: "assistant",
|
|
27901
|
-
content: "Sorry, an error occurred. Please try again later.",
|
|
27902
|
-
},
|
|
27903
|
-
timestamp: new Date().toISOString(),
|
|
27904
|
-
sources: [],
|
|
27905
|
-
isError: true,
|
|
27906
|
-
};
|
|
27907
|
-
upsertMessage(setState, errorMessage, false);
|
|
27908
|
-
setState(prev => ({ ...prev, isTyping: false }));
|
|
27909
|
-
return;
|
|
27910
|
-
}
|
|
27911
|
-
handleStreamEvent(event, streamState, onMessageUpdate, setState);
|
|
27912
|
-
}
|
|
27913
|
-
setState(prev => ({ ...prev, isTyping: false }));
|
|
28957
|
+
|
|
28958
|
+
function deriveErrorInfo(error) {
|
|
28959
|
+
if (error instanceof ApiError) {
|
|
28960
|
+
const retryAfterSeconds = typeof error.retryAfterMs === 'number'
|
|
28961
|
+
? Math.max(1, Math.ceil(error.retryAfterMs / 1000))
|
|
28962
|
+
: undefined;
|
|
28963
|
+
const lowerMessage = (error.message || '').toLowerCase();
|
|
28964
|
+
let message;
|
|
28965
|
+
switch (error.status) {
|
|
28966
|
+
case 429: {
|
|
28967
|
+
const isPerUser = lowerMessage.includes('user');
|
|
28968
|
+
const base = isPerUser
|
|
28969
|
+
? 'You have reached the per-user rate limit.'
|
|
28970
|
+
: 'This widget has received too many requests.';
|
|
28971
|
+
if (retryAfterSeconds) {
|
|
28972
|
+
message = `${base} Please wait ${retryAfterSeconds} second${retryAfterSeconds === 1 ? '' : 's'} before trying again.`;
|
|
27914
28973
|
}
|
|
27915
|
-
|
|
27916
|
-
|
|
27917
|
-
|
|
28974
|
+
else {
|
|
28975
|
+
message = `${base} Please wait a moment and try again.`;
|
|
28976
|
+
}
|
|
28977
|
+
break;
|
|
28978
|
+
}
|
|
28979
|
+
case 401:
|
|
28980
|
+
message = 'Authentication failed. Please refresh the page or verify your API key.';
|
|
28981
|
+
break;
|
|
28982
|
+
case 403:
|
|
28983
|
+
message = 'Access to this widget is restricted. Please contact the site owner if you believe this is an error.';
|
|
28984
|
+
break;
|
|
28985
|
+
case 404:
|
|
28986
|
+
if (lowerMessage.includes('not active')) {
|
|
28987
|
+
message = 'This widget is currently inactive. Please contact the site owner.';
|
|
28988
|
+
}
|
|
28989
|
+
else {
|
|
28990
|
+
message = 'We could not find this widget. It may have been removed.';
|
|
28991
|
+
}
|
|
28992
|
+
break;
|
|
28993
|
+
default:
|
|
28994
|
+
if (error.status >= 500) {
|
|
28995
|
+
message = 'The server encountered an error. Please try again shortly.';
|
|
28996
|
+
}
|
|
28997
|
+
else if (error.status > 0) {
|
|
28998
|
+
message = error.message || 'Something went wrong. Please try again.';
|
|
28999
|
+
}
|
|
29000
|
+
else {
|
|
29001
|
+
message = error.message || 'Unable to connect to the server. Please check your internet connection.';
|
|
27918
29002
|
}
|
|
27919
|
-
});
|
|
27920
29003
|
}
|
|
29004
|
+
return { message, retryAfterSeconds, status: error.status };
|
|
27921
29005
|
}
|
|
29006
|
+
if (error instanceof Error) {
|
|
29007
|
+
const lower = error.message.toLowerCase();
|
|
29008
|
+
if (lower.includes('network')) {
|
|
29009
|
+
return { message: 'Unable to connect to the server. Please check your internet connection.' };
|
|
29010
|
+
}
|
|
29011
|
+
if (lower.includes('timeout')) {
|
|
29012
|
+
return { message: 'The request timed out. Please try again.' };
|
|
29013
|
+
}
|
|
29014
|
+
if (lower.includes('unauthorized') || lower.includes('401')) {
|
|
29015
|
+
return { message: 'Authentication failed. Please refresh the page or verify your API key.' };
|
|
29016
|
+
}
|
|
29017
|
+
if (lower.includes('internal server error') || lower.includes('500')) {
|
|
29018
|
+
return { message: 'The server encountered an error. Please try again shortly.' };
|
|
29019
|
+
}
|
|
29020
|
+
return { message: error.message || 'Something went wrong. Please try again.' };
|
|
29021
|
+
}
|
|
29022
|
+
return { message: 'Something went wrong. Please try again.' };
|
|
27922
29023
|
}
|
|
29024
|
+
|
|
27923
29025
|
function createStreamState() {
|
|
27924
29026
|
return {
|
|
27925
29027
|
currentContent: "",
|
|
@@ -27928,6 +29030,7 @@ function createStreamState() {
|
|
|
27928
29030
|
newMessageIds: new Set(),
|
|
27929
29031
|
sources: [],
|
|
27930
29032
|
toolCallToActionId: {},
|
|
29033
|
+
requestId: generateMessageId(),
|
|
27931
29034
|
};
|
|
27932
29035
|
}
|
|
27933
29036
|
function upsertMessage(setState, message, isTyping) {
|
|
@@ -27963,15 +29066,40 @@ function finalizeStreamMessages(setState, messageIds, sources, toolCallToActionI
|
|
|
27963
29066
|
return msg;
|
|
27964
29067
|
}
|
|
27965
29068
|
// Attach suggestions only to the last assistant message
|
|
27966
|
-
|
|
27967
|
-
|
|
27968
|
-
|
|
27969
|
-
return { ...msg, sources, toolCallToActionId };
|
|
29069
|
+
const withSuggestions = index === lastAssistantIndex && suggestions && suggestions.length > 0
|
|
29070
|
+
? { suggestions }
|
|
29071
|
+
: {};
|
|
29072
|
+
return { ...msg, sources, toolCallToActionId, ...withSuggestions };
|
|
27970
29073
|
}),
|
|
27971
29074
|
isTyping: false,
|
|
27972
29075
|
};
|
|
27973
29076
|
});
|
|
27974
29077
|
}
|
|
29078
|
+
function finalizeToolMessage(streamState, setState, toolCallId, toolName) {
|
|
29079
|
+
setState(prev => {
|
|
29080
|
+
const messages = prev.messages.map((entry) => {
|
|
29081
|
+
const matchesToolCall = entry.message.role === "tool" && entry.message.tool_call_id === toolCallId;
|
|
29082
|
+
if (!matchesToolCall) {
|
|
29083
|
+
return entry;
|
|
29084
|
+
}
|
|
29085
|
+
const existingName = entry.message.name || toolName;
|
|
29086
|
+
return {
|
|
29087
|
+
...entry,
|
|
29088
|
+
message: {
|
|
29089
|
+
role: "tool",
|
|
29090
|
+
content: typeof entry.message.content === "string" ? entry.message.content : "",
|
|
29091
|
+
tool_call_id: toolCallId,
|
|
29092
|
+
name: existingName,
|
|
29093
|
+
},
|
|
29094
|
+
isStreaming: false,
|
|
29095
|
+
toolExecuting: existingName,
|
|
29096
|
+
};
|
|
29097
|
+
});
|
|
29098
|
+
return { ...prev, messages, isTyping: false, isLoading: false };
|
|
29099
|
+
});
|
|
29100
|
+
streamState.activeToolCallCount = Math.max(0, streamState.activeToolCallCount - 1);
|
|
29101
|
+
}
|
|
29102
|
+
|
|
27975
29103
|
function handleContentEvent(event, streamState, onMessageUpdate, setState) {
|
|
27976
29104
|
streamState.currentContent += event.content;
|
|
27977
29105
|
const assistantMessage = {
|
|
@@ -28018,8 +29146,6 @@ function handleToolStartEvent(event, streamState, onMessageUpdate, setState) {
|
|
|
28018
29146
|
}
|
|
28019
29147
|
function handleToolEndEvent(event, streamState, _onMessageUpdate, setState) {
|
|
28020
29148
|
streamState.activeToolCallCount = Math.max(0, streamState.activeToolCallCount - 1);
|
|
28021
|
-
// Update state and mark action as done in a single setState call
|
|
28022
|
-
// Keep isTyping: true because the agent may continue generating content after tool completion
|
|
28023
29149
|
setState(prev => {
|
|
28024
29150
|
const messages = prev.messages.map((msg) => {
|
|
28025
29151
|
const matchesToolCall = msg.message.role === "tool" && msg.message.tool_call_id === event.tool_call_id;
|
|
@@ -28027,7 +29153,26 @@ function handleToolEndEvent(event, streamState, _onMessageUpdate, setState) {
|
|
|
28027
29153
|
return msg;
|
|
28028
29154
|
}
|
|
28029
29155
|
const existingName = msg.message.name || event.tool_name;
|
|
28030
|
-
|
|
29156
|
+
let action = msg.action;
|
|
29157
|
+
if (event.action_id && event.implementation) {
|
|
29158
|
+
action = {
|
|
29159
|
+
implementation: event.implementation,
|
|
29160
|
+
toolCallId: event.tool_call_id,
|
|
29161
|
+
actionId: event.action_id,
|
|
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 = {
|
|
28031
29176
|
...msg,
|
|
28032
29177
|
message: {
|
|
28033
29178
|
role: "tool",
|
|
@@ -28037,14 +29182,10 @@ function handleToolEndEvent(event, streamState, _onMessageUpdate, setState) {
|
|
|
28037
29182
|
},
|
|
28038
29183
|
isStreaming: false,
|
|
28039
29184
|
toolExecuting: existingName,
|
|
28040
|
-
action
|
|
28041
|
-
...msg.action,
|
|
28042
|
-
state: event.state || msg.action.state,
|
|
28043
|
-
done: true, // Mark action as completed
|
|
28044
|
-
} : undefined,
|
|
29185
|
+
action,
|
|
28045
29186
|
};
|
|
29187
|
+
return updatedMsg;
|
|
28046
29188
|
});
|
|
28047
|
-
// Keep typing indicator visible - it will be hidden by done/finalizeStreamMessages
|
|
28048
29189
|
return { ...prev, messages, isTyping: true, isLoading: false };
|
|
28049
29190
|
});
|
|
28050
29191
|
}
|
|
@@ -28076,34 +29217,6 @@ function handleToolErrorEvent(event, streamState, _onMessageUpdate, setState) {
|
|
|
28076
29217
|
return { ...prev, messages, isTyping: true, isLoading: false };
|
|
28077
29218
|
});
|
|
28078
29219
|
}
|
|
28079
|
-
function finalizeToolMessage(streamState, setState, toolCallId, toolName) {
|
|
28080
|
-
setState(prev => {
|
|
28081
|
-
const messages = prev.messages.map((entry) => {
|
|
28082
|
-
const matchesToolCall = entry.message.role === "tool" && entry.message.tool_call_id === toolCallId;
|
|
28083
|
-
if (!matchesToolCall) {
|
|
28084
|
-
return entry;
|
|
28085
|
-
}
|
|
28086
|
-
const existingName = entry.message.name || toolName;
|
|
28087
|
-
return {
|
|
28088
|
-
...entry,
|
|
28089
|
-
message: {
|
|
28090
|
-
role: "tool",
|
|
28091
|
-
content: typeof entry.message.content === "string" ? entry.message.content : "",
|
|
28092
|
-
tool_call_id: toolCallId,
|
|
28093
|
-
name: existingName,
|
|
28094
|
-
},
|
|
28095
|
-
isStreaming: false,
|
|
28096
|
-
toolExecuting: existingName,
|
|
28097
|
-
action: entry.action ? {
|
|
28098
|
-
...entry.action,
|
|
28099
|
-
done: true, // Mark action as completed
|
|
28100
|
-
} : undefined,
|
|
28101
|
-
};
|
|
28102
|
-
});
|
|
28103
|
-
return { ...prev, messages, isTyping: false, isLoading: false };
|
|
28104
|
-
});
|
|
28105
|
-
streamState.activeToolCallCount = Math.max(0, streamState.activeToolCallCount - 1);
|
|
28106
|
-
}
|
|
28107
29220
|
function handleDoneEvent(event, streamState, _onMessageUpdate, setState) {
|
|
28108
29221
|
streamState.sources = event.sources;
|
|
28109
29222
|
streamState.toolCallToActionId = event.tool_call_to_action_id;
|
|
@@ -28162,6 +29275,7 @@ function handleStreamEvent(event, streamState, onMessageUpdate, setState) {
|
|
|
28162
29275
|
console.warn('[Chat] Unknown event type:', event.type);
|
|
28163
29276
|
}
|
|
28164
29277
|
}
|
|
29278
|
+
|
|
28165
29279
|
async function handleActionLoop(client, initialEvent, streamState, onMessageUpdate, setState, widgetId, conversationId, getMessages) {
|
|
28166
29280
|
let pendingEvent = initialEvent;
|
|
28167
29281
|
while (pendingEvent) {
|
|
@@ -28188,7 +29302,7 @@ async function handleActionLoop(client, initialEvent, streamState, onMessageUpda
|
|
|
28188
29302
|
actionId: pendingEvent.action_id,
|
|
28189
29303
|
input: pendingEvent.input,
|
|
28190
29304
|
state: pendingEvent.state,
|
|
28191
|
-
done: false,
|
|
29305
|
+
done: pendingEvent.done ?? false,
|
|
28192
29306
|
},
|
|
28193
29307
|
};
|
|
28194
29308
|
if (streamState.activeToolCallCount === 0) {
|
|
@@ -28202,7 +29316,7 @@ async function handleActionLoop(client, initialEvent, streamState, onMessageUpda
|
|
|
28202
29316
|
id: generateMessageId(),
|
|
28203
29317
|
message: {
|
|
28204
29318
|
role: "assistant",
|
|
28205
|
-
content: "Sorry, an error occurred.
|
|
29319
|
+
content: "Sorry, an error occurred.",
|
|
28206
29320
|
},
|
|
28207
29321
|
timestamp: new Date().toISOString(),
|
|
28208
29322
|
sources: [],
|
|
@@ -28226,7 +29340,7 @@ async function handleActionLoop(client, initialEvent, streamState, onMessageUpda
|
|
|
28226
29340
|
console.error("[Widget] Frontend action failed:", error);
|
|
28227
29341
|
const errorMessageEntry = {
|
|
28228
29342
|
id: generateMessageId(),
|
|
28229
|
-
message: { role: "assistant", content: "Sorry, an error occurred.
|
|
29343
|
+
message: { role: "assistant", content: "Sorry, an error occurred." },
|
|
28230
29344
|
timestamp: new Date().toISOString(),
|
|
28231
29345
|
sources: [],
|
|
28232
29346
|
isError: true,
|
|
@@ -28238,12 +29352,7 @@ async function handleActionLoop(client, initialEvent, streamState, onMessageUpda
|
|
|
28238
29352
|
pendingEvent = null;
|
|
28239
29353
|
const updatedToolMessage = {
|
|
28240
29354
|
...toolMessage,
|
|
28241
|
-
action: toolMessage.action
|
|
28242
|
-
? {
|
|
28243
|
-
...toolMessage.action,
|
|
28244
|
-
state: nextState,
|
|
28245
|
-
}
|
|
28246
|
-
: undefined,
|
|
29355
|
+
action: toolMessage.action ? { ...toolMessage.action, state: nextState } : toolMessage.action,
|
|
28247
29356
|
};
|
|
28248
29357
|
upsertMessage(setState, updatedToolMessage, true);
|
|
28249
29358
|
let streamEnded = false;
|
|
@@ -28253,22 +29362,20 @@ async function handleActionLoop(client, initialEvent, streamState, onMessageUpda
|
|
|
28253
29362
|
break;
|
|
28254
29363
|
}
|
|
28255
29364
|
if (event.type === "done") {
|
|
28256
|
-
//
|
|
28257
|
-
// updated by tool_end event or by the user's frontend action.
|
|
29365
|
+
// Finalize tool message and stream messages
|
|
28258
29366
|
finalizeToolMessage(streamState, setState, resumeToolCallId, toolName);
|
|
28259
|
-
// Handle the done event but skip the tool finalization part since we already did it
|
|
28260
29367
|
streamState.sources = event.sources;
|
|
28261
29368
|
streamState.toolCallToActionId = event.tool_call_to_action_id;
|
|
28262
29369
|
finalizeStreamMessages(setState, streamState.newMessageIds, event.sources, event.tool_call_to_action_id, event.suggestions);
|
|
28263
29370
|
streamEnded = true;
|
|
28264
|
-
continue;
|
|
29371
|
+
continue;
|
|
28265
29372
|
}
|
|
28266
29373
|
if (event.type === "error") {
|
|
28267
29374
|
const errorMessage = {
|
|
28268
29375
|
id: generateMessageId(),
|
|
28269
29376
|
message: {
|
|
28270
29377
|
role: "assistant",
|
|
28271
|
-
content: "Sorry, an error occurred.
|
|
29378
|
+
content: "Sorry, an error occurred.",
|
|
28272
29379
|
},
|
|
28273
29380
|
timestamp: new Date().toISOString(),
|
|
28274
29381
|
sources: [],
|
|
@@ -28279,73 +29386,79 @@ async function handleActionLoop(client, initialEvent, streamState, onMessageUpda
|
|
|
28279
29386
|
}
|
|
28280
29387
|
handleStreamEvent(event, streamState, onMessageUpdate, setState);
|
|
28281
29388
|
}
|
|
28282
|
-
// If stream ended without a done event
|
|
29389
|
+
// If stream ended without a done event, finalize the tool message
|
|
28283
29390
|
if (!streamEnded && !pendingEvent) {
|
|
28284
29391
|
finalizeToolMessage(streamState, setState, resumeToolCallId, toolName);
|
|
28285
29392
|
}
|
|
28286
29393
|
}
|
|
28287
29394
|
}
|
|
28288
|
-
function
|
|
28289
|
-
|
|
28290
|
-
|
|
28291
|
-
|
|
28292
|
-
|
|
28293
|
-
|
|
28294
|
-
|
|
28295
|
-
|
|
28296
|
-
|
|
28297
|
-
|
|
28298
|
-
|
|
28299
|
-
|
|
28300
|
-
|
|
28301
|
-
|
|
28302
|
-
|
|
28303
|
-
|
|
28304
|
-
|
|
28305
|
-
|
|
28306
|
-
|
|
28307
|
-
|
|
28308
|
-
|
|
28309
|
-
|
|
28310
|
-
|
|
28311
|
-
|
|
28312
|
-
|
|
28313
|
-
|
|
28314
|
-
|
|
28315
|
-
|
|
28316
|
-
|
|
28317
|
-
|
|
28318
|
-
|
|
28319
|
-
|
|
28320
|
-
|
|
28321
|
-
|
|
28322
|
-
|
|
28323
|
-
|
|
29395
|
+
function setupActionResumeCallbacks(messages, client, conversationId, setState, onMessageUpdate, createStreamState, registerCallback) {
|
|
29396
|
+
// Find all incomplete actions and register resume callbacks
|
|
29397
|
+
for (const message of messages) {
|
|
29398
|
+
if (message.action && !message.action.done) {
|
|
29399
|
+
const toolCallId = message.action.toolCallId;
|
|
29400
|
+
const toolName = message.message.name || message.toolExecuting || "tool";
|
|
29401
|
+
registerCallback(toolCallId, async (newState) => {
|
|
29402
|
+
// When user interacts with the action after reload, continue the stream
|
|
29403
|
+
try {
|
|
29404
|
+
// Update the action message with the new state and check completion
|
|
29405
|
+
setState(prev => ({
|
|
29406
|
+
...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
|
+
isTyping: true,
|
|
29417
|
+
}));
|
|
29418
|
+
const streamState = createStreamState();
|
|
29419
|
+
// Continue the agent stream with the new state
|
|
29420
|
+
for await (const event of client.continueAgentMessageStream(conversationId, toolCallId, newState)) {
|
|
29421
|
+
if (event.type === "done") {
|
|
29422
|
+
finalizeToolMessage(streamState, setState, toolCallId, toolName);
|
|
29423
|
+
streamState.sources = event.sources;
|
|
29424
|
+
streamState.toolCallToActionId = event.tool_call_to_action_id;
|
|
29425
|
+
finalizeStreamMessages(setState, streamState.newMessageIds, event.sources, event.tool_call_to_action_id, event.suggestions);
|
|
29426
|
+
continue;
|
|
29427
|
+
}
|
|
29428
|
+
if (event.type === "error") {
|
|
29429
|
+
const errorMessage = {
|
|
29430
|
+
id: generateMessageId(),
|
|
29431
|
+
message: {
|
|
29432
|
+
role: "assistant",
|
|
29433
|
+
content: "Sorry, an error occurred. Please try again later.",
|
|
29434
|
+
},
|
|
29435
|
+
timestamp: new Date().toISOString(),
|
|
29436
|
+
sources: [],
|
|
29437
|
+
isError: true,
|
|
29438
|
+
};
|
|
29439
|
+
upsertMessage(setState, errorMessage, false);
|
|
29440
|
+
setState(prev => ({ ...prev, isTyping: false }));
|
|
29441
|
+
return;
|
|
29442
|
+
}
|
|
29443
|
+
handleStreamEvent(event, streamState, onMessageUpdate, setState);
|
|
29444
|
+
}
|
|
29445
|
+
setState(prev => ({ ...prev, isTyping: false }));
|
|
28324
29446
|
}
|
|
28325
|
-
|
|
28326
|
-
|
|
29447
|
+
catch (error) {
|
|
29448
|
+
console.error("[Action Resume] Failed to continue stream:", error);
|
|
29449
|
+
setState(prev => ({ ...prev, isTyping: false }));
|
|
28327
29450
|
}
|
|
29451
|
+
});
|
|
28328
29452
|
}
|
|
28329
|
-
return { message, retryAfterSeconds, status: error.status };
|
|
28330
|
-
}
|
|
28331
|
-
if (error instanceof Error) {
|
|
28332
|
-
const lower = error.message.toLowerCase();
|
|
28333
|
-
if (lower.includes('network')) {
|
|
28334
|
-
return { message: 'Unable to connect to the server. Please check your internet connection.' };
|
|
28335
|
-
}
|
|
28336
|
-
if (lower.includes('timeout')) {
|
|
28337
|
-
return { message: 'The request timed out. Please try again.' };
|
|
28338
|
-
}
|
|
28339
|
-
if (lower.includes('unauthorized') || lower.includes('401')) {
|
|
28340
|
-
return { message: 'Authentication failed. Please refresh the page or verify your API key.' };
|
|
28341
|
-
}
|
|
28342
|
-
if (lower.includes('internal server error') || lower.includes('500')) {
|
|
28343
|
-
return { message: 'The server encountered an error. Please try again shortly.' };
|
|
28344
|
-
}
|
|
28345
|
-
return { message: error.message || 'Something went wrong. Please try again.' };
|
|
28346
29453
|
}
|
|
28347
|
-
return { message: 'Something went wrong. Please try again.' };
|
|
28348
29454
|
}
|
|
29455
|
+
|
|
29456
|
+
/**
|
|
29457
|
+
* useChat Hook
|
|
29458
|
+
* Main state management for chat functionality
|
|
29459
|
+
*/
|
|
29460
|
+
// Initialize action handlers immediately to prevent tree-shaking
|
|
29461
|
+
initializeActionHandlers();
|
|
28349
29462
|
function useChat(options) {
|
|
28350
29463
|
const { widgetId, apiUrl, currentRoute, onMessage, onError, skipInitialization = false, } = options;
|
|
28351
29464
|
const [state, setState] = useState({
|
|
@@ -28354,23 +29467,24 @@ function useChat(options) {
|
|
|
28354
29467
|
isLoading: false,
|
|
28355
29468
|
isTyping: false,
|
|
28356
29469
|
error: null,
|
|
28357
|
-
conversationId: '',
|
|
29470
|
+
conversationId: '',
|
|
28358
29471
|
config: null,
|
|
28359
29472
|
});
|
|
28360
29473
|
const stateRef = useRef(state);
|
|
28361
29474
|
useEffect(() => {
|
|
28362
29475
|
stateRef.current = state;
|
|
28363
29476
|
}, [state]);
|
|
28364
|
-
// Chat history state
|
|
28365
29477
|
const [conversations, setConversations] = useState([]);
|
|
29478
|
+
const abortControllerRef = useRef(null);
|
|
29479
|
+
const currentRequestIdRef = useRef(null);
|
|
29480
|
+
const lastNewChatTimeRef = useRef(0);
|
|
29481
|
+
const NEW_CHAT_COOLDOWN_MS = 5000;
|
|
28366
29482
|
const apiClient = useRef(new WidgetApiClient({ widgetId, apiUrl, currentRoute }));
|
|
28367
|
-
// Update API client when currentRoute changes
|
|
28368
29483
|
useEffect(() => {
|
|
28369
29484
|
apiClient.current = new WidgetApiClient({ widgetId, apiUrl, currentRoute });
|
|
28370
29485
|
}, [widgetId, apiUrl, currentRoute]);
|
|
28371
29486
|
// Load configuration on mount and hydrate with existing conversation if available
|
|
28372
29487
|
useEffect(() => {
|
|
28373
|
-
// Skip initialization in preview mode
|
|
28374
29488
|
if (skipInitialization) {
|
|
28375
29489
|
return;
|
|
28376
29490
|
}
|
|
@@ -28407,7 +29521,7 @@ function useChat(options) {
|
|
|
28407
29521
|
}));
|
|
28408
29522
|
// Setup resume callbacks for incomplete actions
|
|
28409
29523
|
if (conversationId) {
|
|
28410
|
-
setupActionResumeCallbacks(hydratedMessages, apiClient.current, conversationId, setState, onMessage ?? (() => { }));
|
|
29524
|
+
setupActionResumeCallbacks(hydratedMessages, apiClient.current, conversationId, setState, onMessage ?? (() => { }), createStreamState, registerActionResumeCallback);
|
|
28411
29525
|
}
|
|
28412
29526
|
}
|
|
28413
29527
|
catch (error) {
|
|
@@ -28424,7 +29538,6 @@ function useChat(options) {
|
|
|
28424
29538
|
initialize();
|
|
28425
29539
|
return () => {
|
|
28426
29540
|
isMounted = false;
|
|
28427
|
-
// Cleanup resume callbacks
|
|
28428
29541
|
state.messages.forEach(message => {
|
|
28429
29542
|
if (message.action?.toolCallId) {
|
|
28430
29543
|
unregisterActionResumeCallback(message.action.toolCallId);
|
|
@@ -28447,7 +29560,15 @@ function useChat(options) {
|
|
|
28447
29560
|
const hasFiles = !!files && files.length > 0;
|
|
28448
29561
|
if (!trimmedContent && !hasFiles)
|
|
28449
29562
|
return;
|
|
28450
|
-
|
|
29563
|
+
if (stateRef.current.isTyping) {
|
|
29564
|
+
console.warn('[Widget] Cannot send message while streaming is in progress');
|
|
29565
|
+
return;
|
|
29566
|
+
}
|
|
29567
|
+
if (abortControllerRef.current) {
|
|
29568
|
+
abortControllerRef.current.abort();
|
|
29569
|
+
abortControllerRef.current = null;
|
|
29570
|
+
}
|
|
29571
|
+
abortControllerRef.current = new AbortController();
|
|
28451
29572
|
const displayContent = trimmedContent.replace(/^\[EXECUTE_ACTION:[^\]]+\]\s*/, '');
|
|
28452
29573
|
const userMessage = {
|
|
28453
29574
|
id: generateMessageId(),
|
|
@@ -28458,12 +29579,11 @@ function useChat(options) {
|
|
|
28458
29579
|
timestamp: new Date().toISOString(),
|
|
28459
29580
|
sources: [],
|
|
28460
29581
|
};
|
|
28461
|
-
// Add user message immediately
|
|
28462
29582
|
setState(prev => ({
|
|
28463
29583
|
...prev,
|
|
28464
29584
|
messages: [...prev.messages, userMessage],
|
|
28465
|
-
isLoading: false,
|
|
28466
|
-
isTyping: true,
|
|
29585
|
+
isLoading: false,
|
|
29586
|
+
isTyping: true,
|
|
28467
29587
|
error: null,
|
|
28468
29588
|
}));
|
|
28469
29589
|
onMessage?.(userMessage);
|
|
@@ -28503,16 +29623,29 @@ function useChat(options) {
|
|
|
28503
29623
|
}
|
|
28504
29624
|
catch (uploadError) {
|
|
28505
29625
|
console.error('Failed to upload file:', uploadError);
|
|
28506
|
-
// Continue with other files
|
|
28507
29626
|
}
|
|
28508
29627
|
}
|
|
28509
29628
|
}
|
|
28510
|
-
// Stream the response
|
|
28511
29629
|
let lastStreamedMessage = null;
|
|
28512
29630
|
const streamState = createStreamState();
|
|
28513
|
-
|
|
29631
|
+
currentRequestIdRef.current = streamState.requestId;
|
|
29632
|
+
const currentAbortController = abortControllerRef.current;
|
|
29633
|
+
const streamConversationId = conversationId;
|
|
29634
|
+
const streamRequestId = streamState.requestId;
|
|
29635
|
+
const stream = apiClient.current.sendAgentMessageStream(conversationId, trimmedContent, fileIds, currentAbortController?.signal);
|
|
28514
29636
|
for await (const event of stream) {
|
|
29637
|
+
if (currentAbortController?.signal.aborted ||
|
|
29638
|
+
stateRef.current.conversationId !== streamConversationId ||
|
|
29639
|
+
currentRequestIdRef.current !== streamRequestId) {
|
|
29640
|
+
console.log('[Widget] Stream aborted, conversation changed, or superseded by new request');
|
|
29641
|
+
break;
|
|
29642
|
+
}
|
|
28515
29643
|
if (event.type === "action_request") {
|
|
29644
|
+
if (currentAbortController?.signal.aborted ||
|
|
29645
|
+
stateRef.current.conversationId !== streamConversationId ||
|
|
29646
|
+
currentRequestIdRef.current !== streamRequestId) {
|
|
29647
|
+
break;
|
|
29648
|
+
}
|
|
28516
29649
|
await handleActionLoop(apiClient.current, event, streamState, (message) => {
|
|
28517
29650
|
lastStreamedMessage = message;
|
|
28518
29651
|
}, setState, widgetId, conversationId, () => stateRef.current.messages);
|
|
@@ -28522,25 +29655,26 @@ function useChat(options) {
|
|
|
28522
29655
|
lastStreamedMessage = message;
|
|
28523
29656
|
}, setState);
|
|
28524
29657
|
}
|
|
28525
|
-
|
|
29658
|
+
if (currentAbortController?.signal.aborted ||
|
|
29659
|
+
stateRef.current.conversationId !== streamConversationId ||
|
|
29660
|
+
currentRequestIdRef.current !== streamRequestId) {
|
|
29661
|
+
console.log('[Widget] Stream was aborted or superseded, skipping finalization');
|
|
29662
|
+
return;
|
|
29663
|
+
}
|
|
28526
29664
|
setState(prev => ({
|
|
28527
29665
|
...prev,
|
|
28528
29666
|
isLoading: false,
|
|
28529
29667
|
isTyping: false,
|
|
28530
29668
|
}));
|
|
28531
|
-
// Notify about final message
|
|
28532
29669
|
if (lastStreamedMessage) {
|
|
28533
29670
|
onMessage?.(lastStreamedMessage);
|
|
28534
29671
|
}
|
|
28535
|
-
// Generate follow-up suggestions asynchronously
|
|
28536
29672
|
const enableFollowUps = state.config?.settings.enableFollowUpSuggestions !== false;
|
|
28537
29673
|
const actionIds = state.config?.actions || [];
|
|
28538
29674
|
if (enableFollowUps) {
|
|
28539
|
-
// Don't await - let it run in background
|
|
28540
29675
|
apiClient.current.generateFollowUps(stateRef.current.messages, actionIds)
|
|
28541
29676
|
.then(suggestions => {
|
|
28542
29677
|
if (suggestions.length > 0) {
|
|
28543
|
-
// Attach suggestions to the last assistant message
|
|
28544
29678
|
setState(prev => {
|
|
28545
29679
|
const messages = [...prev.messages];
|
|
28546
29680
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
@@ -28569,7 +29703,7 @@ function useChat(options) {
|
|
|
28569
29703
|
},
|
|
28570
29704
|
timestamp: new Date().toISOString(),
|
|
28571
29705
|
sources: [],
|
|
28572
|
-
isError: !fallbackMessage,
|
|
29706
|
+
isError: !fallbackMessage,
|
|
28573
29707
|
};
|
|
28574
29708
|
setState(prev => ({
|
|
28575
29709
|
...prev,
|
|
@@ -28581,9 +29715,6 @@ function useChat(options) {
|
|
|
28581
29715
|
onError?.(err);
|
|
28582
29716
|
}
|
|
28583
29717
|
}, [state.conversationId, state.config, state.messages, onMessage, onError]);
|
|
28584
|
-
/**
|
|
28585
|
-
* Clear all messages
|
|
28586
|
-
*/
|
|
28587
29718
|
const clearMessages = useCallback(() => {
|
|
28588
29719
|
setState(prev => ({
|
|
28589
29720
|
...prev,
|
|
@@ -28596,9 +29727,6 @@ function useChat(options) {
|
|
|
28596
29727
|
clearConversation(widgetId);
|
|
28597
29728
|
}
|
|
28598
29729
|
}, [widgetId, state.config?.settings.persistConversation]);
|
|
28599
|
-
/**
|
|
28600
|
-
* Submit feedback for a message
|
|
28601
|
-
*/
|
|
28602
29730
|
const submitFeedback = useCallback(async (messageId, feedback) => {
|
|
28603
29731
|
try {
|
|
28604
29732
|
const message = state.messages.find(msg => msg.id === messageId);
|
|
@@ -28608,7 +29736,6 @@ function useChat(options) {
|
|
|
28608
29736
|
: undefined;
|
|
28609
29737
|
console.log('Submitting feedback:', { conversationId: state.conversationId, messageId, feedback });
|
|
28610
29738
|
await apiClient.current.submitFeedback(state.conversationId, messageId, feedback, meta);
|
|
28611
|
-
// Update message with feedback
|
|
28612
29739
|
setState(prev => ({
|
|
28613
29740
|
...prev,
|
|
28614
29741
|
messages: prev.messages.map(msg => msg.id === messageId
|
|
@@ -28623,9 +29750,6 @@ function useChat(options) {
|
|
|
28623
29750
|
onError?.(err);
|
|
28624
29751
|
}
|
|
28625
29752
|
}, [state.conversationId, onError]);
|
|
28626
|
-
/**
|
|
28627
|
-
* Load conversation history list from localStorage
|
|
28628
|
-
*/
|
|
28629
29753
|
const loadConversations = useCallback(() => {
|
|
28630
29754
|
const persistConversation = state.config?.settings.persistConversation ?? true;
|
|
28631
29755
|
if (!persistConversation || !isStorageAvailable()) {
|
|
@@ -28642,8 +29766,11 @@ function useChat(options) {
|
|
|
28642
29766
|
})));
|
|
28643
29767
|
}, [widgetId, state.config?.settings.persistConversation]);
|
|
28644
29768
|
const switchConversation = useCallback(async (conversationId) => {
|
|
29769
|
+
if (abortControllerRef.current) {
|
|
29770
|
+
abortControllerRef.current.abort();
|
|
29771
|
+
abortControllerRef.current = null;
|
|
29772
|
+
}
|
|
28645
29773
|
const persistConversation = state.config?.settings.persistConversation ?? true;
|
|
28646
|
-
// First try to load from localStorage
|
|
28647
29774
|
if (persistConversation && isStorageAvailable()) {
|
|
28648
29775
|
const stored = loadConversationById(widgetId, conversationId);
|
|
28649
29776
|
if (stored) {
|
|
@@ -28651,16 +29778,17 @@ function useChat(options) {
|
|
|
28651
29778
|
...prev,
|
|
28652
29779
|
conversationId: stored.conversationId,
|
|
28653
29780
|
messages: hydrateMessages(stored.messages),
|
|
29781
|
+
isTyping: false,
|
|
29782
|
+
isLoading: false,
|
|
28654
29783
|
}));
|
|
28655
29784
|
setActiveConversation(widgetId, conversationId);
|
|
28656
29785
|
return;
|
|
28657
29786
|
}
|
|
28658
29787
|
}
|
|
28659
|
-
setState(prev => ({ ...prev, isLoading: true, error: null }));
|
|
29788
|
+
setState(prev => ({ ...prev, isLoading: true, isTyping: false, error: null }));
|
|
28660
29789
|
try {
|
|
28661
29790
|
const conversation = await apiClient.current.getOrCreateConversation(conversationId);
|
|
28662
29791
|
const hydratedMessages = hydrateMessages(conversation.messages);
|
|
28663
|
-
// Clear old resume callbacks
|
|
28664
29792
|
state.messages.forEach(message => {
|
|
28665
29793
|
if (message.action?.toolCallId) {
|
|
28666
29794
|
unregisterActionResumeCallback(message.action.toolCallId);
|
|
@@ -28672,9 +29800,7 @@ function useChat(options) {
|
|
|
28672
29800
|
messages: hydratedMessages,
|
|
28673
29801
|
isLoading: false,
|
|
28674
29802
|
}));
|
|
28675
|
-
|
|
28676
|
-
setupActionResumeCallbacks(hydratedMessages, apiClient.current, conversation.id, setState, onMessage ?? (() => { }));
|
|
28677
|
-
// Save to local storage
|
|
29803
|
+
setupActionResumeCallbacks(hydratedMessages, apiClient.current, conversation.id, setState, onMessage ?? (() => { }), createStreamState, registerActionResumeCallback);
|
|
28678
29804
|
if (persistConversation && isStorageAvailable()) {
|
|
28679
29805
|
saveConversation(widgetId, conversation.id, hydratedMessages);
|
|
28680
29806
|
}
|
|
@@ -28685,11 +29811,23 @@ function useChat(options) {
|
|
|
28685
29811
|
}
|
|
28686
29812
|
}, [widgetId, state.config?.settings.persistConversation]);
|
|
28687
29813
|
const startNewConversation = useCallback(() => {
|
|
29814
|
+
const now = Date.now();
|
|
29815
|
+
if (now - lastNewChatTimeRef.current < NEW_CHAT_COOLDOWN_MS) {
|
|
29816
|
+
console.warn('[Widget] New chat rate limited - please wait');
|
|
29817
|
+
return;
|
|
29818
|
+
}
|
|
29819
|
+
lastNewChatTimeRef.current = now;
|
|
29820
|
+
if (abortControllerRef.current) {
|
|
29821
|
+
abortControllerRef.current.abort();
|
|
29822
|
+
abortControllerRef.current = null;
|
|
29823
|
+
}
|
|
28688
29824
|
setState(prev => ({
|
|
28689
29825
|
...prev,
|
|
28690
29826
|
messages: [],
|
|
28691
29827
|
conversationId: '',
|
|
28692
29828
|
error: null,
|
|
29829
|
+
isTyping: false,
|
|
29830
|
+
isLoading: false,
|
|
28693
29831
|
}));
|
|
28694
29832
|
const persistConversation = state.config?.settings.persistConversation ?? true;
|
|
28695
29833
|
if (persistConversation && isStorageAvailable()) {
|
|
@@ -28701,11 +29839,8 @@ function useChat(options) {
|
|
|
28701
29839
|
if (!persistConversation || !isStorageAvailable()) {
|
|
28702
29840
|
return;
|
|
28703
29841
|
}
|
|
28704
|
-
// Delete from storage
|
|
28705
29842
|
deleteConversation(widgetId, conversationId);
|
|
28706
|
-
// Update local state
|
|
28707
29843
|
setConversations(prev => prev.filter(c => c.id !== conversationId));
|
|
28708
|
-
// If we deleted the current conversation, clear it
|
|
28709
29844
|
if (state.conversationId === conversationId) {
|
|
28710
29845
|
setState(prev => ({
|
|
28711
29846
|
...prev,
|
|
@@ -28725,7 +29860,6 @@ function useChat(options) {
|
|
|
28725
29860
|
sendMessage,
|
|
28726
29861
|
clearMessages,
|
|
28727
29862
|
submitFeedback,
|
|
28728
|
-
// Chat history features
|
|
28729
29863
|
conversations,
|
|
28730
29864
|
loadConversations,
|
|
28731
29865
|
switchConversation,
|
|
@@ -28734,11 +29868,20 @@ function useChat(options) {
|
|
|
28734
29868
|
};
|
|
28735
29869
|
}
|
|
28736
29870
|
|
|
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
|
+
const DataPolicyView = ({ config, widgetName, }) => {
|
|
29873
|
+
const headerTitle = widgetName || config?.appearance?.headerTitle || 'AI Assistant';
|
|
29874
|
+
const hasFileUpload = config?.settings?.enableFileUpload ?? false;
|
|
29875
|
+
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: [jsxs("div", { className: "ai-chat-data-policy-intro", children: [jsx("div", { className: "ai-chat-data-policy-icon", children: jsx(ShieldIcon, {}) }), jsxs("p", { children: ["Dieser Datenschutzhinweis informiert dich gem\u00E4\u00DF Art. 13 DSGVO dar\u00FCber, wie ", jsx("strong", { children: headerTitle }), " Daten verarbeitet, wenn du diesen Chat nutzt. Bitte beachte, dass der konkrete Umfang der Verarbeitung vom Betreiber dieser Website/Anwendung (dem Verantwortlichen) sowie von den jeweils aktivierten Funktionen abh\u00E4ngt."] })] }), jsxs("div", { className: "ai-chat-data-policy-section", children: [jsx("h3", { children: "Verantwortlicher / Kontakt" }), jsx("p", { children: "Verantwortlicher im Sinne von Art. 4 Nr. 7 DSGVO ist der Betreiber dieser Website/Anwendung. Die Kontaktdaten (und ggf. die Kontaktdaten eines Datenschutzbeauftragten) findest du in der Datenschutzerkl\u00E4rung bzw. im Impressum der Website, in die dieses Chat-Widget eingebunden ist." })] }), jsxs("div", { className: "ai-chat-data-policy-section", children: [jsx("h3", { children: "Verarbeitete Daten (Kategorien)" }), jsxs("ul", { children: [jsxs("li", { children: [jsx("strong", { children: "Chat-Inhalte:" }), " Nachrichten (Text) sowie ggf. Kontextinformationen, die du im Chat angibst. Diese Inhalte werden verarbeitet, um Antworten zu generieren und die Unterhaltung bereitzustellen."] }), hasFileUpload && (jsxs("li", { children: [jsx("strong", { children: "Hochgeladene Dateien:" }), " Dateien, die du an den Chat \u00FCbermittelst, werden zur Bearbeitung des Anliegens verarbeitet. Die Verarbeitung kann das Extrahieren von Text/Informationen umfassen."] })), jsxs("li", { children: [jsx("strong", { children: "Technische Nutzungsdaten:" }), " z.B. Zeitstempel, Sitzungs-/Request-Informationen sowie technische Metadaten, die f\u00FCr den Betrieb, die Sicherheit (Missbrauchspr\u00E4vention) und Fehleranalyse erforderlich sind."] })] })] }), jsxs("div", { className: "ai-chat-data-policy-section", children: [jsx("h3", { children: "Zwecke und Rechtsgrundlagen" }), jsxs("ul", { children: [jsxs("li", { children: [jsx("strong", { children: "Bereitstellung des Chats und Beantwortung von Anfragen" }), " (Art. 6 Abs. 1 lit. b DSGVO, soweit Vertrags-/vorvertragliche Ma\u00DFnahmen; andernfalls Art. 6 Abs. 1 lit. f DSGVO \u2013 berechtigtes Interesse an effizienter Kommunikation und Support)."] }), jsxs("li", { children: [jsx("strong", { children: "Qualit\u00E4tssicherung, Betrieb und Sicherheit" }), " (Art. 6 Abs. 1 lit. f DSGVO), z.B. zur Stabilit\u00E4t, Missbrauchserkennung und Fehlerbehebung."] }), jsxs("li", { children: [jsx("strong", { children: "Einwilligungsbasierte Verarbeitung" }), " kann erfolgen, sofern der Betreiber dies vorsieht (Art. 6 Abs. 1 lit. a DSGVO)."] })] })] }), jsxs("div", { className: "ai-chat-data-policy-section", children: [jsx("h3", { children: "Empf\u00E4nger und Auftragsverarbeiter" }), jsxs("ul", { children: [jsxs("li", { children: [jsx("strong", { children: "Hosting/IT-Dienstleister:" }), " Der Betreiber kann Dienstleister f\u00FCr Hosting, Logging, Monitoring und Infrastruktur einsetzen."] }), jsxs("li", { children: [jsx("strong", { children: "KI-Dienstleister:" }), " Zur Generierung von Antworten k\u00F6nnen Chat-Inhalte an KI-Modelle bzw. Anbieter von KI-Infrastruktur \u00FCbertragen werden. Soweit erforderlich, erfolgt dies auf Grundlage eines Auftragsverarbeitungsvertrags (Art. 28 DSGVO)."] }), jsxs("li", { children: [jsx("strong", { children: "Drittlandtransfer:" }), " Falls Empf\u00E4nger au\u00DFerhalb der EU/des EWR sitzen, kann ein Transfer in Drittl\u00E4nder stattfinden. In diesem Fall werden geeignete Garantien (z.B. EU-Standardvertragsklauseln) eingesetzt, soweit erforderlich."] })] })] }), jsxs("div", { className: "ai-chat-data-policy-section", children: [jsx("h3", { children: "Speicherdauer" }), jsxs("ul", { children: [persistsConversation ? (jsxs("li", { children: [jsx("strong", { children: "Chatverlauf:" }), " Der Chatverlauf kann gespeichert werden, um die Unterhaltung \u00FCber mehrere Sitzungen hinweg fortzusetzen."] })) : (jsxs("li", { children: [jsx("strong", { children: "Chatverlauf:" }), " Der Chatverlauf wird nicht dauerhaft gespeichert und wird beim Schlie\u00DFen des Chats beendet."] })), hasFileUpload && (jsxs("li", { children: [jsx("strong", { children: "Dateien:" }), " Hochgeladene Dateien werden nur so lange verarbeitet, wie dies f\u00FCr die Bearbeitung erforderlich ist, und anschlie\u00DFend gel\u00F6scht, sofern keine l\u00E4ngere Speicherung gesetzlich erforderlich ist."] })), jsxs("li", { children: [jsx("strong", { children: "Technische Protokolle:" }), " Technische Logdaten k\u00F6nnen f\u00FCr einen begrenzten Zeitraum gespeichert werden, um den sicheren Betrieb zu gew\u00E4hrleisten."] })] })] }), jsxs("div", { className: "ai-chat-data-policy-section", children: [jsx("h3", { children: "Deine Rechte (Betroffenenrechte)" }), jsxs("ul", { children: [jsxs("li", { children: [jsx("strong", { children: "Auskunft" }), " (Art. 15 DSGVO)"] }), jsxs("li", { children: [jsx("strong", { children: "Berichtigung" }), " (Art. 16 DSGVO)"] }), jsxs("li", { children: [jsx("strong", { children: "L\u00F6schung" }), " (Art. 17 DSGVO) und ", jsx("strong", { children: "Einschr\u00E4nkung der Verarbeitung" }), " (Art. 18 DSGVO)"] }), jsxs("li", { children: [jsx("strong", { children: "Daten\u00FCbertragbarkeit" }), " (Art. 20 DSGVO), soweit anwendbar"] }), jsxs("li", { children: [jsx("strong", { children: "Widerspruch" }), " gegen Verarbeitungen auf Grundlage berechtigter Interessen (Art. 21 DSGVO)"] }), jsxs("li", { children: [jsx("strong", { children: "Beschwerderecht" }), " bei einer Datenschutzaufsichtsbeh\u00F6rde (Art. 77 DSGVO)"] })] }), jsx("p", { children: "Hinweis: Ohne eindeutige Identifikationsmerkmale kann der Betreiber einzelne Chatverl\u00E4ufe ggf. nicht einer Person zuordnen. F\u00FCr Anfragen wende dich bitte an den Betreiber dieser Website/Anwendung." })] }), jsxs("div", { className: "ai-chat-data-policy-section", children: [jsx("h3", { children: "Wichtiger Hinweis" }), jsx("p", { className: "ai-chat-data-policy-warning", children: "Bitte gib keine besonderen Kategorien personenbezogener Daten (z.B. Gesundheitsdaten), Passw\u00F6rter, Kreditkarten-/Bankdaten oder vertrauliche Gesch\u00E4ftsgeheimnisse in den Chat ein. KI-generierte Antworten k\u00F6nnen unzutreffend sein und sollten vor einer Nutzung eigenverantwortlich gepr\u00FCft werden." })] })] }) }));
|
|
29877
|
+
};
|
|
29878
|
+
|
|
28737
29879
|
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" })] }));
|
|
28738
29880
|
const PlusIcon = () => (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: "12", y1: "5", x2: "12", y2: "19" }), jsx("line", { x1: "5", y1: "12", x2: "19", y2: "12" })] }));
|
|
28739
29881
|
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" })] }));
|
|
28740
29882
|
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" })] }));
|
|
28741
|
-
const
|
|
29883
|
+
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,
|
|
28742
29885
|
// Chat history props (only active when persistConversation is true)
|
|
28743
29886
|
conversations = [], onLoadConversations, onSwitchConversation, onStartNewConversation, onDeleteConversation, currentConversationId,
|
|
28744
29887
|
// Override props for live preview
|
|
@@ -28755,6 +29898,8 @@ headerTitleOverride, welcomeTitleOverride, welcomeMessageOverride, placeholderOv
|
|
|
28755
29898
|
const inputPlaceholder = placeholderOverride ?? appearance?.placeholder ?? 'Ask me anything...';
|
|
28756
29899
|
// Track if history panel is open
|
|
28757
29900
|
const [showHistory, setShowHistory] = useState(false);
|
|
29901
|
+
// Track if data policy view is open
|
|
29902
|
+
const [showDataPolicy, setShowDataPolicy] = useState(false);
|
|
28758
29903
|
// Scroll button state (managed by MessageList)
|
|
28759
29904
|
const [showScrollButton, setShowScrollButton] = useState(false);
|
|
28760
29905
|
const [scrollToBottom, setScrollToBottom] = useState(null);
|
|
@@ -28764,6 +29909,13 @@ headerTitleOverride, welcomeTitleOverride, welcomeMessageOverride, placeholderOv
|
|
|
28764
29909
|
}, []);
|
|
28765
29910
|
// History exit animation when starting a new chat from overview
|
|
28766
29911
|
const [isHistoryExiting, setIsHistoryExiting] = useState(false);
|
|
29912
|
+
// Handle data policy click
|
|
29913
|
+
const handleDataPolicyClick = useCallback(() => {
|
|
29914
|
+
setShowDataPolicy(true);
|
|
29915
|
+
}, []);
|
|
29916
|
+
const handleDataPolicyBack = useCallback(() => {
|
|
29917
|
+
setShowDataPolicy(false);
|
|
29918
|
+
}, []);
|
|
28767
29919
|
// Load conversations when history panel opens
|
|
28768
29920
|
const handleOpenHistory = () => {
|
|
28769
29921
|
setShowHistory(true);
|
|
@@ -28811,10 +29963,22 @@ headerTitleOverride, welcomeTitleOverride, welcomeMessageOverride, placeholderOv
|
|
|
28811
29963
|
// The backend will detect and trigger the action based on the message
|
|
28812
29964
|
onSendMessage(question);
|
|
28813
29965
|
};
|
|
28814
|
-
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' : ''}`, children: 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, {}) })] })] })) }),
|
|
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: "Datenschutzhinweis" })] })) : 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
|
+
/* History Panel */
|
|
29968
|
+
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) => {
|
|
28815
29969
|
e.stopPropagation();
|
|
28816
29970
|
onDeleteConversation(conv.id);
|
|
28817
|
-
}, "aria-label": "Delete conversation", children: jsx(TrashIcon, {}) }))] }) }, conv.id))) })), jsx(MessageInput, { onSend: (text) => handleSendFromOverview(text), placeholder: inputPlaceholder, disabled: isLoading, enableFileUpload: settings?.enableFileUpload })] })) : (jsxs(Fragment, { children: [
|
|
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." })), (() => {
|
|
29972
|
+
console.log('[DEBUG ChatWindow] Rendering MessageList with', messages.length, 'messages');
|
|
29973
|
+
messages.forEach((m, i) => {
|
|
29974
|
+
console.log(`[DEBUG ChatWindow] msg ${i}:`, { role: m.message.role, hasAction: !!m.action, impl: m.action?.implementation });
|
|
29975
|
+
});
|
|
29976
|
+
console.log('[DEBUG ChatWindow] getActionRenderer available:', !!getActionRenderer);
|
|
29977
|
+
if (getActionRenderer) {
|
|
29978
|
+
console.log('[DEBUG ChatWindow] Testing renderer for query-contact-directory:', !!getActionRenderer('query-contact-directory'));
|
|
29979
|
+
}
|
|
29980
|
+
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 })] }))] }));
|
|
28818
29982
|
};
|
|
28819
29983
|
|
|
28820
29984
|
/**
|
|
@@ -29220,7 +30384,7 @@ function styleInject(css, ref) {
|
|
|
29220
30384
|
if ( ref === void 0 ) ref = {};
|
|
29221
30385
|
var insertAt = ref.insertAt;
|
|
29222
30386
|
|
|
29223
|
-
if (typeof document === 'undefined') { return; }
|
|
30387
|
+
if (!css || typeof document === 'undefined') { return; }
|
|
29224
30388
|
|
|
29225
30389
|
var head = document.head || document.getElementsByTagName('head')[0];
|
|
29226
30390
|
var style = document.createElement('style');
|
|
@@ -29243,7 +30407,10 @@ function styleInject(css, ref) {
|
|
|
29243
30407
|
}
|
|
29244
30408
|
}
|
|
29245
30409
|
|
|
29246
|
-
var css_248z = ".ai-chat-message{animation:ai-chat-message-appear .2s var(--chat-ease-bounce);max-width:85%}.ai-chat-message-content{border-radius:var(--chat-radius-bubble,14px);font-size:var(--chat-text-md,15px);line-height:var(--chat-line-relaxed,1.6);padding:var(--chat-space-sm,8px) var(--chat-space-md,16px)}.ai-chat-message.user .ai-chat-message-content{background:var(--chat-user-bg,#f4f3f0);border-bottom-right-radius:var(--chat-radius-sm,4px);color:var(--chat-user-text,#000)}.ai-chat-message.assistant .ai-chat-message-content{background:var(--chat-assistant-bg,transparent);color:var(--chat-assistant-text,#000)}.ai-chat-message-timestamp{color:var(--chat-text-muted,#71717a);font-size:var(--chat-text-xs,12px);margin-top:var(--chat-space-xs,4px);padding:0 var(--chat-space-xs,4px)}.ai-chat-message.streaming .ai-chat-message-content:after{animation:ai-chat-cursor-blink .8s infinite;content:\"▋\";margin-left:2px;opacity:.7}@keyframes ai-chat-message-appear{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes ai-chat-cursor-blink{0%,50%{opacity:1}51%,to{opacity:0}}.ai-chat-message.fullpage .ai-chat-message-content{font-size:var(--chat-text-lg,18px);padding:var(--chat-space-md,16px) var(--chat-space-lg,24px)}.ai-chat-typing{gap:var(--chat-space-xs,4px);padding:var(--chat-space-sm,8px) var(--chat-space-md,16px)}.ai-chat-typing-dot{background:var(--chat-text-muted,#71717a)}.ai-chat-tool-gear{color:var(--text-primary,#3e3e3e)}.ai-chat-tool-gear.spinning{animation:ai-chat-spin 1.5s linear infinite}.ai-chat-tool-badge{border-radius:8px}.ai-chat-tool-badge.loading{background:#e5e7eb;border:1px solid #d1d5db;color:#1f2937}.ai-chat-tool-badge.error{background:rgba(239,68,68,.1);color:#ef4444}.ai-chat-tool-check{color:#22c55e;flex-shrink:0}.ai-chat-tool-error{color:#ef4444;flex-shrink:0}.ai-chat-tool-badge .tool-name{max-width:150px;overflow:hidden;text-overflow:ellipsis}.ai-chat-widget.dark .ai-chat-tool-gear,.ai-chat-widget[data-theme=dark] .ai-chat-tool-gear,.dark .ai-chat-tool-gear,[data-theme=dark] .ai-chat-tool-gear{color:#fff}.ai-chat-widget.dark .ai-chat-tool-badge,.ai-chat-widget[data-theme=dark] .ai-chat-tool-badge,.dark .ai-chat-tool-badge,[data-theme=dark] .ai-chat-tool-badge{background:hsla(0,0%,100%,.12);border:1px solid hsla(0,0%,100%,.2);color:hsla(0,0%,100%,.9)}.ai-chat-widget.dark .ai-chat-tool-badge.error,.ai-chat-widget[data-theme=dark] .ai-chat-tool-badge.error,.dark .ai-chat-tool-badge.error,[data-theme=dark] .ai-chat-tool-badge.error{background:rgba(239,68,68,.2);color:#f87171}.chakra-ui-dark .ai-chat-tool-gear,html.dark .ai-chat-tool-gear{color:#fff}.chakra-ui-dark .ai-chat-tool-badge,html.dark .ai-chat-tool-badge{background:hsla(0,0%,100%,.12);border:1px solid hsla(0,0%,100%,.2);color:hsla(0,0%,100%,.9)}.chakra-ui-dark .ai-chat-tool-badge.error,html.dark .ai-chat-tool-badge.error{background:rgba(239,68,68,.2);color:#f87171}@keyframes ai-chat-skeleton-pulse{0%,to{opacity:.4}50%{opacity:.7}}.ai-chat-action-skeleton-item{animation:ai-chat-skeleton-pulse 1.5s ease-in-out infinite;background:var(--bg-secondary,#e5e7eb)}.ai-chat-widget.dark .ai-chat-action-skeleton-item,.chakra-ui-dark .ai-chat-action-skeleton-item,.dark .ai-chat-action-skeleton-item,[data-theme=dark] .ai-chat-action-skeleton-item{background:hsla(0,0%,100%,.1)}.ai-chat-action-skeleton-content{display:flex;flex-direction:column;gap:16px}.ai-chat-action-skeleton-header{align-items:center;display:flex;gap:10px}.ai-chat-action-skeleton-box{background:rgba(0,0,0,.08);border-radius:10px;display:flex;flex-direction:column;gap:8px;padding:14px}.ai-chat-widget.dark .ai-chat-action-skeleton-box,.chakra-ui-dark .ai-chat-action-skeleton-box,.dark .ai-chat-action-skeleton-box,[data-theme=dark] .ai-chat-action-skeleton-box{background:rgba(0,0,0,.25)}.ai-chat-action-card{--action-accent:var(--primary-color,#3b82f6);background:var(--bg-secondary,#f4f4f4);border:none;border-radius:var(--radius-lg,12px);box-sizing:border-box;margin-top:var(--space-sm,8px);max-width:100%;padding:var(--space-md,16px);transition:all var(--duration-normal,.25s) ease;width:100%}.ai-chat-widget.dark .ai-chat-action-card,.chakra-ui-dark .ai-chat-action-card,.dark .ai-chat-action-card,[data-theme=dark] .ai-chat-action-card{background:var(--bg-secondary,#3a3a3a)}.ai-chat-action-booked{background:var(--bg-secondary,#f4f4f4);border:none}.ai-chat-widget.dark .ai-chat-action-booked,.chakra-ui-dark .ai-chat-action-booked,.dark .ai-chat-action-booked,[data-theme=dark] .ai-chat-action-booked{background:var(--bg-secondary,#3a3a3a)}.ai-chat-action-header{align-items:center;color:var(--text-primary,#3e3e3e);display:flex;font-size:var(--text-md,15px);font-weight:var(--font-weight-semibold,600);gap:var(--space-sm,8px);margin-bottom:var(--space-md,16px)}.ai-chat-widget.dark .ai-chat-action-header,.chakra-ui-dark .ai-chat-action-header,.dark .ai-chat-action-header,[data-theme=dark] .ai-chat-action-header{color:var(--text-primary,#fff)}.ai-chat-action-icon{color:var(--action-accent,var(--primary-color,#3b82f6));flex-shrink:0;height:20px;width:20px}.ai-chat-action-success-icon-wrapper{align-items:center;background:var(--action-accent,var(--primary-color,#22c55e));border-radius:50%;color:#fff;display:flex;flex-shrink:0;height:24px;justify-content:center;width:24px}.ai-chat-action-icon-success{color:currentColor;height:14px;width:14px}.ai-chat-action-detail-box{background:var(--bg-primary,#fff);border:1px solid var(--border-subtle,rgba(0,0,0,.08));border-radius:var(--radius-md,8px);display:flex;flex-direction:column;gap:4px;padding:12px 16px}.ai-chat-widget.dark .ai-chat-action-detail-box,.chakra-ui-dark .ai-chat-action-detail-box,.dark .ai-chat-action-detail-box,[data-theme=dark] .ai-chat-action-detail-box{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.05)}.ai-chat-action-label-small{color:var(--text-muted,#71717a);font-size:11px;font-weight:600;letter-spacing:.5px;text-transform:uppercase}.ai-chat-action-value-large{color:var(--text-primary,#3e3e3e);font-size:15px;font-weight:500}.ai-chat-widget.dark .ai-chat-action-value-large,.chakra-ui-dark .ai-chat-action-value-large,.dark .ai-chat-action-value-large,[data-theme=dark] .ai-chat-action-value-large{color:#fff}.ai-chat-action-link-button{align-items:center;background:var(--action-accent,var(--primary-color,#3b82f6));border:none;border-radius:9999px;box-sizing:border-box;color:#fff;display:flex;font-size:14px;font-weight:600;gap:6px;justify-content:center;margin-top:8px;padding:12px;text-decoration:none;transition:all .2s ease;width:100%}.ai-chat-action-link-button:hover{opacity:.9;transform:translateY(-1px)}.ai-chat-action-body{display:flex;flex-direction:column;gap:var(--space-md,16px)}.ai-chat-action-field{display:flex;flex-direction:column;gap:var(--space-xs,6px)}.ai-chat-action-label{color:var(--text-secondary,#6b7280);font-size:var(--text-sm,13px);font-weight:var(--font-weight-medium,500)}.ai-chat-widget.dark .ai-chat-action-label,.chakra-ui-dark .ai-chat-action-label,.dark .ai-chat-action-label,[data-theme=dark] .ai-chat-action-label{color:var(--text-secondary,#a1a1aa)}.ai-chat-action-input{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:var(--radius-md,8px);color:var(--text-primary,#3e3e3e);font-size:var(--text-sm,13px);outline:none;padding:10px 12px;transition:border-color .2s ease,box-shadow .2s ease}.ai-chat-action-input:focus{border-color:var(--action-accent,var(--primary-color,#3b82f6));box-shadow:0 0 0 3px rgba(59,130,246,.1)}.ai-chat-action-input::placeholder{color:var(--text-muted,#9ca3af)}.ai-chat-widget.dark .ai-chat-action-input,.chakra-ui-dark .ai-chat-action-input,.dark .ai-chat-action-input,[data-theme=dark] .ai-chat-action-input{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-action-input:focus,.chakra-ui-dark .ai-chat-action-input:focus,.dark .ai-chat-action-input:focus,[data-theme=dark] .ai-chat-action-input:focus{border-color:var(--action-accent,var(--primary-color,#3b82f6));box-shadow:0 0 0 3px rgba(59,130,246,.2)}.ai-chat-action-date-grid{display:grid;gap:var(--space-xs,6px);grid-template-columns:repeat(auto-fill,minmax(90px,1fr))}.ai-chat-action-date-btn{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:var(--radius-md,8px);color:var(--text-primary,#3e3e3e);cursor:pointer;font-size:var(--text-xs,12px);font-weight:var(--font-weight-medium,500);padding:8px 10px;text-align:center;transition:all .15s ease}.ai-chat-action-date-btn:hover{background:rgba(59,130,246,.05);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-action-date-btn.active{background:var(--action-accent,var(--primary-color,#3b82f6));border-color:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-widget.dark .ai-chat-action-date-btn,.chakra-ui-dark .ai-chat-action-date-btn,.dark .ai-chat-action-date-btn,[data-theme=dark] .ai-chat-action-date-btn{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-action-date-btn:hover,.chakra-ui-dark .ai-chat-action-date-btn:hover,.dark .ai-chat-action-date-btn:hover,[data-theme=dark] .ai-chat-action-date-btn:hover{background:rgba(59,130,246,.15);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-widget.dark .ai-chat-action-date-btn.active,.chakra-ui-dark .ai-chat-action-date-btn.active,.dark .ai-chat-action-date-btn.active,[data-theme=dark] .ai-chat-action-date-btn.active{background:var(--action-accent,var(--primary-color,#3b82f6));border-color:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-action-time-grid{display:grid;gap:var(--space-xs,6px);grid-template-columns:repeat(auto-fill,minmax(100px,1fr))}.ai-chat-action-time-btn{background:var(--bg-primary,#fff);border:1px solid var(--border-default,#e5e7eb);border-radius:var(--radius-md,8px);color:var(--text-primary,#3e3e3e);cursor:pointer;font-size:var(--text-xs,12px);font-weight:var(--font-weight-medium,500);padding:8px 10px;text-align:center;transition:all .15s ease}.ai-chat-action-time-btn:hover{background:rgba(59,130,246,.05);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-action-time-btn.active{background:var(--action-accent,var(--primary-color,#3b82f6));border-color:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-widget.dark .ai-chat-action-time-btn,.chakra-ui-dark .ai-chat-action-time-btn,.dark .ai-chat-action-time-btn,[data-theme=dark] .ai-chat-action-time-btn{background:rgba(0,0,0,.2);border-color:hsla(0,0%,100%,.1);color:#fff}.ai-chat-widget.dark .ai-chat-action-time-btn:hover,.chakra-ui-dark .ai-chat-action-time-btn:hover,.dark .ai-chat-action-time-btn:hover,[data-theme=dark] .ai-chat-action-time-btn:hover{background:rgba(59,130,246,.15);border-color:var(--action-accent,var(--primary-color,#3b82f6))}.ai-chat-widget.dark .ai-chat-action-time-btn.active,.chakra-ui-dark .ai-chat-action-time-btn.active,.dark .ai-chat-action-time-btn.active,[data-theme=dark] .ai-chat-action-time-btn.active{background:var(--action-accent,var(--primary-color,#3b82f6));border-color:var(--action-accent,var(--primary-color,#3b82f6));color:#fff}.ai-chat-action-button{background:var(--action-accent,var(--primary-color,#3b82f6));border:none;border-radius:9999px;color:#fff;cursor:pointer;font-size:var(--text-sm,13px);font-weight:var(--font-weight-semibold,600);padding:12px 16px;transition:all .2s ease;width:100%}.ai-chat-action-button:hover:not(:disabled){opacity:.9;transform:translateY(-1px)}.ai-chat-action-button:disabled{cursor:not-allowed;opacity:.5}.ai-chat-action-error{background:rgba(239,68,68,.1);border-radius:var(--radius-md,8px);color:#dc2626;font-size:var(--text-sm,13px);padding:10px 12px}.ai-chat-widget.dark .ai-chat-action-error,.chakra-ui-dark .ai-chat-action-error,.dark .ai-chat-action-error,[data-theme=dark] .ai-chat-action-error{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-action-hint{color:var(--text-muted,#9ca3af);font-size:var(--text-sm,13px);padding:var(--space-sm,8px);text-align:center}.ai-chat-widget,.chat-ui{--radius-sm:4px;--radius-md:8px;--radius-lg:12px;--radius-xl:16px;--radius-2xl:18px;--radius-pill:9999px;--radius-window-top:22px;--radius-window-bottom:44px;--radius-window-gutter:16px;--radius-chat-bubble:14px;--radius-preset-badge:13px;--radius-history-item:14px;--radius-action-badge:8px;--radius-input:62px;--space-xs:4px;--space-sm:8px;--space-md:16px;--space-lg:24px;--space-xl:32px;--text-xs:12px;--text-sm:14px;--text-md:15px;--text-lg:18px;--text-xl:22px;--text-2xl:28px;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--line-height-tight:1.3;--line-height-normal:1.4;--line-height-relaxed:1.6;--bg-primary:#fff;--bg-secondary:#f4f4f4;--bg-tertiary:#e5e7eb;--bg-hover:#e5e7eb;--text-primary:#3e3e3e;--text-secondary:#000;--text-muted:#71717a;--text-placeholder:#a1a1aa;--border-default:#d3d3d3;--border-subtle:#e5e7eb;--border-muted:#f4f4f5;--user-bg:#f4f3f0;--user-text:#000;--user-bg-hover:#e8e7e4;--agent-bg:transparent;--agent-text:#000;--input-bg:#f4f4f4;--input-border:#d3d3d3;--input-text:#000;--btn-primary-bg:#151515;--btn-primary-text:#f4f4f4;--btn-secondary-bg:transparent;--btn-secondary-text:#71717a;--spring-bounce:cubic-bezier(0.34,1.56,0.64,1);--spring-smooth:cubic-bezier(0.4,0,0.2,1);--spring-snappy:cubic-bezier(0.2,0,0,1);--duration-fast:0.15s;--duration-normal:0.25s;--duration-slow:0.35s;--shadow-sm:0 1px 2px rgba(0,0,0,.05);--shadow-md:0 2px 8px rgba(0,0,0,.1);--shadow-lg:0 4px 16px rgba(0,0,0,.12);--shadow-window:0px 0px 15px 9px rgba(0,0,0,.1);--shadow-button:0px 0px 15px 9px rgba(0,0,0,.03)}.ai-chat-widget.dark,.chat-ui.dark{--bg-primary:#282625;--bg-secondary:#4a4846;--bg-tertiary:#484848;--bg-hover:#484848;--text-primary:#fff;--text-secondary:#fff;--text-muted:#a1a1aa;--text-placeholder:#71717a;--border-default:#5d5b5b;--border-subtle:#5d5b5b;--border-muted:#5d5b5b;--user-bg:#484848;--user-text:#fff;--user-bg-hover:#5a5a5a;--agent-bg:transparent;--agent-text:#fff;--input-bg:#4a4846;--input-border:#5d5b5b;--input-text:#fff;--btn-primary-bg:#fff;--btn-primary-text:#312f2d;--btn-secondary-bg:transparent;--btn-secondary-text:#a1a1aa;--shadow-window:0px 0px 15px 9px rgba(0,0,0,.2);--shadow-button:0px 0px 15px 9px rgba(0,0,0,.03);--shadow-input:0px 0px 10px rgba(0,0,0,.15)}.ai-chat-widget,.chat-ui{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}.ai-chat-widget-container{font-size:var(--text-sm);line-height:1.5;position:fixed;z-index:9999}.ai-chat-widget-container.bottom-right{bottom:20px;right:20px}.ai-chat-widget-container.bottom-left{bottom:20px;left:20px}.ai-chat-widget-container.top-right{right:20px;top:20px}.ai-chat-widget-container.top-left{left:20px;top:20px}@keyframes ai-chat-window-open{0%{opacity:0;transform:scale(.9) translateY(20px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes ai-chat-window-close{0%{opacity:1;transform:scale(1) translateY(0)}to{opacity:0;transform:scale(.9) translateY(20px)}}@keyframes ai-chat-message-slide-in{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}@keyframes ai-chat-welcome-fade-in{0%{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}@keyframes ai-chat-typing-pulse{0%,60%,to{opacity:.4;transform:translateY(0) scale(1)}30%{opacity:1;transform:translateY(-4px) scale(1.1)}}@keyframes ai-chat-gear-spin{to{transform:rotate(1turn)}}@keyframes ai-chat-tool-active{0%,to{background:var(--bg-secondary);opacity:1}50%{background:var(--bg-tertiary);opacity:1}}@keyframes ai-chat-feedback-morph{0%{opacity:.5;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@keyframes ai-chat-checkmark-pop{0%{opacity:0;transform:scale(0) rotate(-45deg)}50%{transform:scale(1.3) rotate(0deg)}to{opacity:1;transform:scale(1) rotate(0deg)}}@keyframes ai-chat-history-exit{to{opacity:0;transform:translateY(-18px)}}@keyframes ai-chat-tool-gradient{0%{background-position:200% 0}to{background-position:-200% 0}}@media (max-width:480px){.ai-chat-widget-container.is-open{height:100vh!important;inset:0!important;width:100vw!important}.ai-chat-widget-container.is-open .ai-chat-window{animation:none!important;border-radius:0!important;height:100%!important;inset:0!important;max-height:100%!important;max-width:100%!important;position:absolute!important;width:100%!important}.ai-chat-widget-container.is-open .ai-chat-button{display:none!important;visibility:hidden!important}.ai-chat-header{padding-top:max(16px,env(safe-area-inset-top))}.ai-chat-input-container{padding-bottom:max(20px,env(safe-area-inset-bottom))}}.ai-chat-window{animation:ai-chat-window-open var(--duration-slow,.35s) var(--spring-bounce);background:var(--bg-primary,#fff);border:1px solid var(--border-default,#d3d3d3);border-radius:var(--radius-window-top,22px) var(--radius-window-top,22px) var(--radius-window-bottom,44px) var(--radius-window-bottom,44px);box-shadow:var(--shadow-window,0 0 15px 5px rgba(0,0,0,.08));display:flex;flex-direction:column;overflow:hidden;position:absolute;transform-origin:bottom right;z-index:2}.ai-chat-widget.dark .ai-chat-window{background:var(--bg-primary,#282625);border-color:var(--border-default,#5d5b5b);border-width:.7px}.ai-chat-window.closing{animation:ai-chat-window-close var(--duration-normal) var(--spring-smooth) forwards}.ai-chat-window.size-small{height:500px;width:380px}.ai-chat-window.size-medium,.ai-chat-window.size-small{max-height:calc(100vh - 100px);max-width:calc(100vw - 40px)}.ai-chat-window.size-medium{height:600px;width:420px}.ai-chat-window.size-large{height:700px;max-height:calc(100vh - 100px);max-width:calc(100vw - 40px);width:480px}.ai-chat-widget-container.bottom-right .ai-chat-window{bottom:calc(var(--button-size, 56px) + 8px);right:0}.ai-chat-widget-container.bottom-left .ai-chat-window{bottom:calc(var(--button-size, 56px) + 8px);left:0}.ai-chat-widget-container.top-right .ai-chat-window{right:0;top:calc(var(--button-size, 56px) + 8px)}.ai-chat-widget-container.top-left .ai-chat-window{left:0;top:calc(var(--button-size, 56px) + 8px)}.ai-chat-header{align-items:center;background:var(--bg-primary,#fff);border-bottom:1px solid var(--border-default,#d3d3d3);border-radius:var(--radius-window-top,22px) var(--radius-window-top,22px) 0 0;display:flex;justify-content:space-between;padding:18px var(--space-md,16px);position:relative;z-index:10}.ai-chat-widget.dark .ai-chat-header{background:var(--bg-primary,#282625);border-bottom-color:var(--border-default,#5d5b5b);border-bottom-width:.7px}.ai-chat-header.is-history{padding-left:var(--space-md)}.ai-chat-header.is-history .ai-chat-title{flex:1;min-width:0;overflow:hidden;padding-right:var(--space-lg);text-overflow:ellipsis;white-space:nowrap}.ai-chat-header-content{align-items:center;display:flex;flex:1;gap:var(--space-lg)}.ai-chat-header-actions{align-items:center;display:flex;gap:var(--space-sm)}.ai-chat-logo{border-radius:10px;height:36px;object-fit:cover;width:36px}.ai-chat-title{color:var(--text-primary,#3e3e3e);font-size:var(--text-xl,22px);font-weight:var(--font-weight-bold,700);letter-spacing:-.02em}.ai-chat-widget.dark .ai-chat-title{color:var(--text-primary,#fff)}.ai-chat-close-button,.ai-chat-header-button{align-items:center;background:transparent;border:none;border-radius:var(--radius-md);color:var(--text-muted);cursor:pointer;display:flex;height:32px;justify-content:center;padding:0;transition:color var(--duration-fast) ease;width:32px}.ai-chat-close-button:hover,.ai-chat-header-button:hover{color:var(--text-primary)}.ai-chat-close-button:active,.ai-chat-header-button:active{transform:scale(.95)}.ai-chat-close-button svg,.ai-chat-header-button svg{height:22px;width:22px}.ai-chat-button{align-items:center;background:var(--button-color,var(--btn-primary-bg));border:1px solid var(--border-default,#d3d3d3);border-radius:50%;box-shadow:var(--shadow-button,0 0 15px 9px rgba(0,0,0,.03));color:var(--button-icon-color,var(--btn-primary-text));cursor:pointer;display:flex;height:var(--button-size,56px);justify-content:center;overflow:hidden;position:relative;transition:opacity var(--duration-fast) ease;width:var(--button-size,56px);z-index:1}.ai-chat-button:hover{opacity:.9}.ai-chat-button:active{opacity:.8}.ai-chat-button-svg{height:50%;min-height:24px;min-width:24px;transition:transform var(--duration-fast) ease;width:50%}.ai-chat-button.is-open .ai-chat-button-svg{transform:rotate(0deg)}.ai-chat-button-icon{font-size:1.5em;line-height:1}.ai-chat-input-container{background:linear-gradient(to bottom,transparent 0,var(--bg-primary,#fff) 50%,var(--bg-primary,#fff) 100%);bottom:0;left:0;padding-top:30px;position:absolute;right:0;z-index:10}.ai-chat-widget.dark .ai-chat-input-container{background:linear-gradient(to bottom,transparent 0,var(--bg-primary,#282625) 50%,var(--bg-primary,#282625) 100%)}.ai-chat-input-container.separate{padding:0 var(--radius-window-gutter,16px) var(--radius-window-gutter,16px);padding-top:30px}.ai-chat-input-wrapper{align-items:flex-end;background:var(--input-bg,#f4f4f4);border:1px solid var(--input-border,#d3d3d3);border-radius:var(--radius-input,62px);box-sizing:border-box;display:flex;gap:0;height:52px;overflow:hidden;padding:6px 6px 6px 16px;position:relative;transition:all var(--duration-fast,.15s) ease;z-index:5}.ai-chat-input-wrapper.multiline{border-radius:14px!important;min-height:64px;padding:10px 10px 10px 14px}.ai-chat-widget.dark .ai-chat-input-wrapper{background:var(--input-bg,#4a4846);border-color:var(--input-border,#5d5b5b);border-width:.7px;box-shadow:var(--shadow-input,0 0 10px rgba(0,0,0,.15))}.ai-chat-input-wrapper:focus-within{border-color:var(--text-muted,#a1a1aa)}.ai-chat-input{word-wrap:break-word!important;background:transparent!important;border:none!important;border-radius:0!important;box-shadow:none!important;box-sizing:border-box!important;color:var(--input-text,#000)!important;flex:1!important;font-family:inherit!important;font-size:var(--text-md,15px)!important;height:40px!important;line-height:20px!important;margin:0!important;max-height:40px!important;min-height:40px!important;min-width:0!important;outline:none!important;overflow-wrap:anywhere!important;overflow-x:hidden!important;overflow-y:auto!important;padding:10px var(--space-sm,8px)!important;resize:none!important;white-space:pre-wrap!important;width:0!important;word-break:break-word!important}.ai-chat-widget.dark .ai-chat-input{color:var(--input-text,#fff)}.ai-chat-input::placeholder{color:var(--text-placeholder,#a1a1aa)}.ai-chat-widget.dark .ai-chat-input::placeholder{color:var(--text-placeholder,#52525b)}.ai-chat-file-button{align-items:center;align-self:center;background:transparent;border:none;color:var(--text-placeholder);cursor:pointer;display:flex;flex-shrink:0;height:28px;justify-content:center;padding:0;transition:color var(--duration-fast) ease;width:28px}.ai-chat-file-button:hover{color:var(--text-secondary)}.ai-chat-file-button:disabled{cursor:not-allowed;opacity:.5}.ai-chat-send-button{align-items:center;align-self:center;background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#151515))));border:none;border-radius:50%;color:var(--button-icon-color,var(--btn-primary-text,#f4f4f4));cursor:pointer;display:flex;flex-shrink:0;height:40px;justify-content:center;min-height:40px;min-width:40px;padding:0;transition:all var(--duration-fast,.15s) ease;width:40px}.ai-chat-widget.dark .ai-chat-send-button{background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#fff))));color:var(--button-icon-color,var(--btn-primary-text,#312f2d))}.ai-chat-send-button.active{background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#151515))));color:var(--button-icon-color,var(--btn-primary-text,#f4f4f4))}.ai-chat-widget.dark .ai-chat-send-button.active{background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#fff))));color:var(--button-icon-color,var(--btn-primary-text,#312f2d))}.ai-chat-send-button:hover:not(:disabled){opacity:.8}.ai-chat-send-button:active:not(:disabled){transform:scale(.95)}.ai-chat-send-button:disabled{cursor:not-allowed;opacity:.3}.ai-chat-file-list{display:flex;flex-wrap:wrap;gap:var(--space-sm);padding:var(--space-sm) var(--space-sm)}.ai-chat-file-item{align-items:center;background:rgba(0,0,0,.05);border-radius:6px;display:flex;font-size:var(--text-xs);gap:var(--space-sm);padding:6px 10px}.ai-chat-file-extension{background:var(--btn-primary-bg);border-radius:3px;color:var(--btn-primary-text);display:inline-block;font-size:10px;font-weight:var(--font-weight-semibold);min-width:40px;padding:2px 6px;text-align:center;text-transform:uppercase}.ai-chat-file-info{display:flex;flex:1;flex-direction:column;gap:2px;min-width:0}.ai-chat-file-name{font-weight:var(--font-weight-medium);max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ai-chat-file-size{color:var(--text-muted);font-size:10px;opacity:.7}.ai-chat-file-remove{align-items:center;background:none;border:none;color:inherit;cursor:pointer;display:flex;justify-content:center;opacity:.5;padding:var(--space-xs);transition:opacity var(--duration-fast) ease}.ai-chat-file-remove:hover{opacity:1}.ai-chat-messages{-webkit-overflow-scrolling:touch;-ms-overflow-style:none;align-items:stretch;background:var(--bg-primary,#fff);display:flex;flex:1;flex-direction:column;gap:var(--space-md,16px);justify-content:flex-start;overflow-x:hidden;overflow-y:auto;padding:var(--space-lg,24px) var(--space-md,16px) 100px;position:relative;scroll-behavior:smooth;scrollbar-width:none}.ai-chat-widget.dark .ai-chat-messages{background:var(--bg-primary,#18181b)}.ai-chat-messages::-webkit-scrollbar{display:none}.ai-chat-message{animation:ai-chat-message-slide-in .2s var(--spring-bounce);display:flex;flex-direction:column;max-width:90%}.ai-chat-message.user{align-items:flex-end;align-self:flex-end}.ai-chat-message.assistant{align-items:flex-start;align-self:flex-start;max-width:100%}.ai-chat-message.tool{align-self:flex-start;margin:0 -16px;max-width:100%;padding:0;width:calc(100% + 32px)}.ai-chat-message-content{word-wrap:break-word;border-radius:18px;font-size:var(--text-md,15px);line-height:var(--line-height-relaxed,1.6);overflow-wrap:break-word;padding:8px 14px}.ai-chat-message.user .ai-chat-message-content{background:var(--user-bg,#f4f3f0);border-radius:18px;color:var(--user-text,#000)}.ai-chat-widget.dark .ai-chat-message.user .ai-chat-message-content{background:var(--user-bg,#484848);color:var(--user-text,#fff)}.ai-chat-message.assistant .ai-chat-message-content{background:var(--agent-bg,transparent);color:var(--agent-text,#000);padding:0}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content{color:var(--agent-text,#fff)}.ai-chat-message-timestamp{color:var(--text-muted,#71717a);font-size:var(--text-xs,12px);margin-top:var(--space-xs,4px);padding:0 var(--space-xs,4px)}.ai-chat-welcome{animation:ai-chat-welcome-fade-in .3s var(--spring-smooth);display:flex;flex-direction:column;gap:var(--space-md,16px);padding:var(--space-lg,24px) 0}.ai-chat-welcome-title{color:var(--text-primary,#3e3e3e);font-size:var(--text-2xl,28px);font-weight:var(--font-weight-bold,700);line-height:var(--line-height-tight,1.3)}.ai-chat-widget.dark .ai-chat-welcome-title{color:var(--text-primary,#fff)}.ai-chat-welcome-text{color:var(--text-secondary,#000);font-size:var(--text-md,15px);line-height:var(--line-height-relaxed,1.6);max-width:100%}.ai-chat-widget.dark .ai-chat-welcome-text{color:var(--text-secondary,#fff)}.ai-chat-typing{align-items:center;display:flex;gap:var(--space-xs,4px);padding:var(--space-sm,8px) var(--space-md,16px)}.ai-chat-typing-dot{animation:ai-chat-typing-bounce 1.4s ease-in-out infinite both;background:var(--text-muted,#71717a);border-radius:50%;height:8px;width:8px}.ai-chat-typing-dot:first-child{animation-delay:-.32s}.ai-chat-typing-dot:nth-child(2){animation-delay:-.16s}.ai-chat-typing-dot:nth-child(3){animation-delay:0s}@keyframes ai-chat-typing-bounce{0%,80%,to{opacity:.4;transform:scale(.6)}40%{opacity:1;transform:scale(1)}}.ai-chat-scroll-button{align-items:center;background:var(--bg-secondary,#f4f4f5);border:1px solid var(--border-subtle,rgba(0,0,0,.1));border-radius:50%;bottom:80px;box-shadow:0 2px 8px rgba(0,0,0,.1);color:var(--text-secondary,#71717a);cursor:pointer;display:flex;height:36px;justify-content:center;left:50%;opacity:0;pointer-events:none;position:absolute;transform:translateX(-50%);transition:background .15s ease,box-shadow .15s ease,opacity .15s ease,visibility .15s ease;visibility:hidden;width:36px;z-index:15}.ai-chat-scroll-button.visible{opacity:1;pointer-events:auto;visibility:visible}.ai-chat-scroll-button:hover{background:var(--bg-tertiary,#e4e4e7);box-shadow:0 4px 12px rgba(0,0,0,.15)}.ai-chat-scroll-button:active{background:var(--bg-tertiary,#d4d4d8)}.ai-chat-widget.dark .ai-chat-scroll-button{background:var(--bg-secondary,#3f3f46);border-color:var(--border-subtle,hsla(0,0%,100%,.1));box-shadow:0 2px 8px rgba(0,0,0,.3);color:var(--text-secondary,#a1a1aa)}.ai-chat-widget.dark .ai-chat-scroll-button:hover{background:var(--bg-tertiary,#52525b);box-shadow:0 4px 12px rgba(0,0,0,.4)}.ai-chat-error{background:var(--bg-secondary);border-radius:var(--radius-chat-bubble);color:var(--text-primary);font-size:var(--text-md);margin:0 auto;padding:10px var(--space-md)}.ai-chat-message.assistant .ai-chat-message-content p{margin:0 0 var(--space-sm) 0}.ai-chat-message.assistant .ai-chat-message-content p:last-child{margin-bottom:0}.ai-chat-message.assistant .ai-chat-message-content ol,.ai-chat-message.assistant .ai-chat-message-content ul{margin:var(--space-sm) 0;padding-left:var(--space-lg)}.ai-chat-message.assistant .ai-chat-message-content li{margin-bottom:var(--space-xs)}.ai-chat-message.assistant .ai-chat-message-content code{background:rgba(0,0,0,.05);border-radius:var(--radius-sm);font-family:SF Mono,Monaco,Cascadia Code,monospace;font-size:.9em;padding:2px 6px}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content code{background:hsla(0,0%,100%,.1)}.ai-chat-message.assistant .ai-chat-message-content pre{background:rgba(0,0,0,.05);border-radius:var(--radius-md);margin:var(--space-sm) 0;overflow-x:auto;padding:var(--space-sm)}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content pre{background:hsla(0,0%,100%,.05)}.ai-chat-message.assistant .ai-chat-message-content pre code{background:transparent;padding:0}.ai-chat-message.assistant .ai-chat-message-content a{color:var(--btn-primary-bg);text-decoration:underline}.ai-chat-message.assistant .ai-chat-message-content strong{font-weight:var(--font-weight-semibold)}.ai-chat-message.assistant .ai-chat-message-content blockquote{border-left:3px solid var(--border-default);color:var(--text-muted);margin:var(--space-sm) 0;padding-left:var(--space-md)}.ai-chat-message.assistant .ai-chat-message-content hr{border:none;border-top:1px solid var(--border-subtle,rgba(0,0,0,.1));margin:var(--space-lg,24px) 0}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content hr{border-top-color:var(--border-subtle,hsla(0,0%,100%,.1))}.ai-chat-message.assistant .ai-chat-message-content table{border-collapse:collapse;font-size:var(--text-sm);margin:var(--space-sm) 0;width:100%}.ai-chat-message.assistant .ai-chat-message-content td,.ai-chat-message.assistant .ai-chat-message-content th{border:1px solid var(--border-subtle);padding:var(--space-sm);text-align:left}.ai-chat-message.assistant .ai-chat-message-content th{font-weight:var(--font-weight-semibold);white-space:nowrap}.ai-chat-message.assistant .ai-chat-message-content tbody tr:nth-child(2n){background:rgba(0,0,0,.02)}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content tbody tr:nth-child(2n){background:hsla(0,0%,100%,.03)}.ai-chat-suggested-questions{align-self:flex-end;margin:0;padding:16px 0 0;width:100%}.ai-chat-suggested-questions-list{align-items:center;display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end}.ai-chat-suggested-question{align-items:center;background:transparent;border:1px solid var(--border-default,#d4d4d8);border-radius:var(--radius-preset-badge,18px);color:var(--text-primary,#18181b);cursor:pointer;display:inline-flex;font-size:14px;font-weight:400;gap:6px;justify-content:center;line-height:1.3;max-width:100%;overflow:hidden;padding:8px 14px;text-overflow:ellipsis;transition:background .15s ease,border-color .15s ease,transform .1s ease;white-space:nowrap}.ai-chat-widget.dark .ai-chat-suggested-question{background:transparent;border-color:var(--border-subtle,#52525b);color:var(--text-primary,#fafafa)}.ai-chat-suggested-question-text{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ai-chat-suggested-question:hover{background:var(--bg-hover,#f4f4f5);border-color:var(--border-default,#d4d4d8)}.ai-chat-widget.dark .ai-chat-suggested-question:hover{background:var(--bg-hover,#3f3f46);border-color:var(--border-subtle,#52525b)}.ai-chat-suggested-question:active{transform:scale(.98)}.ai-chat-suggested-question.action-type{border:none}.ai-chat-suggested-question.action-type,.ai-chat-widget.dark .ai-chat-suggested-question.action-type{background:var(--primary-color,var(--button-color,#ef4444));color:var(--button-icon-color,#fff)}.ai-chat-suggested-question.action-type:hover{background:var(--primary-color,var(--button-color,#ef4444));opacity:.9}.ai-chat-suggested-question-icon{align-items:center;display:flex;flex-shrink:0;justify-content:center}.ai-chat-suggested-question:not(.action-type) .ai-chat-suggested-question-icon{display:none}.ai-chat-follow-up-suggestions{box-sizing:border-box;margin:0;padding:8px 16px 0;width:100%}.ai-chat-follow-up-list{align-items:flex-end;display:flex;flex-direction:column;gap:6px}.ai-chat-follow-up-item{align-items:center;border:none;border-radius:var(--radius-preset-badge,18px);cursor:pointer;display:inline-flex;font-size:14px;font-weight:400;gap:6px;justify-content:center;line-height:1.3;max-width:100%;overflow:hidden;padding:8px 14px;text-overflow:ellipsis;transition:opacity .15s ease,transform .1s ease;white-space:nowrap}.ai-chat-follow-up-item,.ai-chat-widget.dark .ai-chat-follow-up-item{background:var(--primary-color,var(--button-color,#07f));color:var(--button-icon-color,#fff)}.ai-chat-follow-up-item:hover{opacity:.9}.ai-chat-follow-up-item:active{transform:scale(.98)}.ai-chat-follow-up-item.question-type{background:transparent;border:1px solid var(--border-default,#d4d4d8);color:var(--text-primary,#18181b)}.ai-chat-widget.dark .ai-chat-follow-up-item.question-type{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{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-md) var(--space-md) 120px;scrollbar-width:none}.ai-chat-history-list::-webkit-scrollbar{display:none}.ai-chat-history-list.exiting{animation:ai-chat-history-exit .22s var(--spring-smooth) forwards}.ai-chat-history-item{align-items:center;background:var(--user-bg,#f4f4f5);border-radius:var(--radius-history-item,15px);display:flex;flex:0 0 auto;flex-direction:row;height:var(--history-item-height,44px);margin:0;overflow:hidden;transition:background var(--duration-fast,.15s) ease;width:100%}.ai-chat-history-item-content{align-items:center;background:transparent;border:none;cursor:pointer;display:flex;flex:1;flex-direction:row;height:100%;min-width:0;padding:0 var(--space-xs,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,8px);color:var(--text-muted,#71717a);cursor:pointer;display:flex;flex-shrink:0;height:32px;justify-content:center;margin-left:auto;margin-right:var(--space-2xs,2px);opacity:0;transition:opacity var(--duration-fast,.15s) ease,background var(--duration-fast,.15s) ease,color var(--duration-fast,.15s) ease;width:32px}.ai-chat-history-item:hover .ai-chat-history-item-delete{opacity:1}.ai-chat-history-item-delete:hover{background:rgba(239,68,68,.1);color:#ef4444}.ai-chat-widget.dark .ai-chat-history-item-delete:hover{background:rgba(239,68,68,.2);color:#f87171}.ai-chat-tool-row{align-items:center;display:flex;gap:10px;margin:2px 0;padding:0 16px}.ai-chat-tool-gear{color:#1f2937;flex-shrink:0;height:20px;width:20px}.ai-chat-tool-gear.spinning{animation:ai-chat-gear-spin 1.5s linear infinite}.ai-chat-tool-badges{align-items:center;display:flex;flex-wrap:wrap;gap:8px}.ai-chat-tool-badge{align-items:center;background:#e5e7eb;border:1px solid #d1d5db;border-radius:var(--radius-action-badge,8px);color:#1f2937;display:inline-flex;font-size:12px;font-weight:500;gap:4px;line-height:1.2;padding:5px 12px;transition:all .2s ease;white-space:nowrap}.ai-chat-tool-badge.loading{animation:ai-chat-tool-gradient 2s linear infinite;background:linear-gradient(90deg,var(--tool-loading-bg-1,#e0e0e0) 0,var(--tool-loading-bg-2,#f0f0f0) 25%,var(--tool-loading-bg-3,#fff) 50%,var(--tool-loading-bg-2,#f0f0f0) 75%,var(--tool-loading-bg-1,#e0e0e0) 100%);background-size:200% 100%;color:var(--tool-loading-text,#1a1a1a);position:relative}.ai-chat-widget:not(.dark) .ai-chat-tool-badge.loading{--tool-loading-bg-1:#2a2a2a;--tool-loading-bg-2:#3a3a3a;--tool-loading-bg-3:#4a4a4a;--tool-loading-text:#fff}.ai-chat-tool-badge.completed{background:#e5e7eb;border:1px solid #d1d5db;color:#1f2937}.ai-chat-widget.dark .ai-chat-tool-badge,.ai-chat-widget[data-theme=dark] .ai-chat-tool-badge,.chakra-ui-dark .ai-chat-tool-badge,.dark .ai-chat-tool-badge,[data-theme=dark] .ai-chat-tool-badge,html.dark .ai-chat-tool-badge{background:hsla(0,0%,100%,.12);border:1px solid hsla(0,0%,100%,.2);color:hsla(0,0%,100%,.9)}.ai-chat-widget.dark .ai-chat-tool-gear,.ai-chat-widget[data-theme=dark] .ai-chat-tool-gear,.chakra-ui-dark .ai-chat-tool-gear,.dark .ai-chat-tool-gear,[data-theme=dark] .ai-chat-tool-gear,html.dark .ai-chat-tool-gear{color:#fff}.ai-chat-tool-badge.error{background:var(--tool-error-bg,rgba(239,68,68,.15));color:var(--tool-error-text,#ef4444)}.ai-chat-tool-badge .ai-chat-tool-check{color:#22c55e;flex-shrink:0}.ai-chat-tool-badge .ai-chat-tool-error{color:#ef4444;flex-shrink:0}.tool-name{font-weight:500;line-height:1.2;white-space:nowrap}.ai-chat-tool-action{box-sizing:border-box;padding:0 16px;width:100%}.ai-chat-sources{background:rgba(0,0,0,.02);border-radius:6px;font-size:var(--text-xs);margin-top:var(--space-sm);overflow:hidden}.ai-chat-sources-toggle{align-items:center;background:none;border:none;cursor:pointer;display:flex;gap:6px;padding:var(--space-sm) 10px;text-align:left;transition:background var(--duration-fast) ease;width:100%}.ai-chat-sources-toggle:hover{background:rgba(0,0,0,.03)}.ai-chat-sources-icon{color:var(--text-muted);font-size:10px;transition:transform var(--duration-fast) ease}.ai-chat-sources-title{color:var(--text-primary);flex:1;font-size:11px;font-weight:var(--font-weight-semibold);letter-spacing:.5px;text-transform:uppercase}.ai-chat-source-item{border-top:1px solid rgba(0,0,0,.05);color:var(--text-muted);display:flex;gap:var(--space-sm);padding:var(--space-sm) 10px}.ai-chat-source-item:last-child{border-bottom:none}.ai-chat-source-number{color:var(--btn-primary-bg);flex-shrink:0;font-weight:var(--font-weight-semibold)}.ai-chat-source-details{display:flex;flex:1;flex-direction:column;gap:var(--space-xs)}.ai-chat-source-score{color:var(--text-placeholder);font-size:11px}.ai-chat-source-content{color:var(--text-muted);font-size:11px;font-style:italic;line-height:var(--line-height-normal)}.ai-chat-source-metadata{display:flex;flex-wrap:wrap;gap:6px;margin-top:2px}.ai-chat-source-meta-item{background:rgba(0,0,0,.05);border-radius:3px;color:var(--text-muted);font-size:10px;padding:2px 6px}";
|
|
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)}}";
|
|
30411
|
+
styleInject(css_248z$1);
|
|
30412
|
+
|
|
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{align-items:center;background:var(--bg-subtle,rgba(0,0,0,.02));border-radius:12px;display:flex;flex-direction:column;margin-bottom:20px;padding:16px;text-align:center}.ai-chat-widget.dark .ai-chat-data-policy-intro{background:var(--bg-subtle,hsla(0,0%,100%,.04))}.ai-chat-data-policy-icon{align-items:center;background:var(--primary-color,#07f);border-radius:12px;color:#fff;display:flex;height:48px;justify-content:center;margin-bottom:12px;width:48px}.ai-chat-data-policy-intro p{color:var(--text-secondary,#52525b);font-size:13px;line-height:1.5;margin:0}.ai-chat-widget.dark .ai-chat-data-policy-intro p{color:var(--text-secondary,#a1a1aa)}.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:13px;font-weight:600;letter-spacing:.02em;margin:0 0 8px;text-transform:uppercase}.ai-chat-widget.dark .ai-chat-data-policy-section h3{color:var(--text-primary,#fafafa)}.ai-chat-data-policy-section ul{list-style:none;margin:0;padding:0}.ai-chat-data-policy-section li{color:var(--text-secondary,#52525b);font-size:12px;line-height:1.5;padding:8px 0 8px 16px;position:relative}.ai-chat-widget.dark .ai-chat-data-policy-section li{color:var(--text-secondary,#a1a1aa)}.ai-chat-data-policy-section li:before{background:var(--text-muted,#a1a1aa);border-radius:50%;content:\"\";height:4px;left:0;position:absolute;top:14px;width:4px}.ai-chat-data-policy-section li strong{color:var(--text-primary,#18181b)}.ai-chat-widget.dark .ai-chat-data-policy-section li strong{color:var(--text-primary,#fafafa)}.ai-chat-data-policy-section p{color:var(--text-secondary,#52525b);font-size:12px;line-height:1.5;margin:0}.ai-chat-widget.dark .ai-chat-data-policy-section p{color:var(--text-secondary,#a1a1aa)}.ai-chat-data-policy-warning{background:rgba(234,179,8,.1);border:1px solid rgba(234,179,8,.3);border-radius:8px;color:#92400e!important;padding:12px}.ai-chat-widget.dark .ai-chat-data-policy-warning{background:rgba(234,179,8,.15);border-color:rgba(234,179,8,.25);color:#fbbf24!important}";
|
|
29247
30414
|
styleInject(css_248z);
|
|
29248
30415
|
|
|
29249
30416
|
// Icon components mapping
|
|
@@ -29251,9 +30418,10 @@ const iconComponents = {
|
|
|
29251
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" }) })),
|
|
29252
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" }) })),
|
|
29253
30420
|
};
|
|
29254
|
-
const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = false, previewConfig, position = 'bottom-right', primaryColor, size, headerTitle, welcomeTitle, welcomeMessage, placeholder, theme, suggestedQuestions, customStyles, currentRoute, defaultOpen = false, zIndex, onOpen, onClose, onMessage, onError, mode = 'bubble', }) => {
|
|
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', }) => {
|
|
29255
30422
|
const [isOpen, setIsOpen] = useState(defaultOpen);
|
|
29256
30423
|
const [autoDetectedTheme, setAutoDetectedTheme] = useState('light');
|
|
30424
|
+
const [showWelcomeBubble, setShowWelcomeBubble] = useState(false);
|
|
29257
30425
|
const widgetRef = useRef(null);
|
|
29258
30426
|
const containerRef = useRef(null);
|
|
29259
30427
|
// Determine mode
|
|
@@ -29275,10 +30443,9 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
29275
30443
|
showChatHistory: true,
|
|
29276
30444
|
showTimestamps: true,
|
|
29277
30445
|
showTypingIndicator: true,
|
|
30446
|
+
showToolCalls: false,
|
|
29278
30447
|
enableFileUpload: false,
|
|
29279
30448
|
enableFeedback: true,
|
|
29280
|
-
showSources: false,
|
|
29281
|
-
sourceDisplayMode: 'none',
|
|
29282
30449
|
},
|
|
29283
30450
|
behavior: {
|
|
29284
30451
|
agentic: false,
|
|
@@ -29315,7 +30482,6 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
29315
30482
|
const messages = previewMode ? [] : chatHook.messages;
|
|
29316
30483
|
const isLoading = previewMode ? false : chatHook.isLoading;
|
|
29317
30484
|
const isTyping = previewMode ? false : chatHook.isTyping;
|
|
29318
|
-
const error = previewMode ? null : chatHook.error;
|
|
29319
30485
|
const config = previewMode ? mergedPreviewConfig : chatHook.config;
|
|
29320
30486
|
const sendMessage = previewMode ? (() => Promise.resolve()) : chatHook.sendMessage;
|
|
29321
30487
|
const submitFeedback = previewMode ? (() => Promise.resolve()) : chatHook.submitFeedback;
|
|
@@ -29350,8 +30516,13 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
29350
30516
|
mediaQuery.removeEventListener('change', handleMediaChange);
|
|
29351
30517
|
};
|
|
29352
30518
|
}, [config]);
|
|
29353
|
-
//
|
|
30519
|
+
// Check if device is mobile
|
|
30520
|
+
const isMobile = typeof window !== 'undefined' && window.innerWidth <= 480;
|
|
30521
|
+
// Handle auto-open (only for bubble mode, disabled on mobile)
|
|
29354
30522
|
useEffect(() => {
|
|
30523
|
+
// Never auto-open on mobile devices
|
|
30524
|
+
if (isMobile)
|
|
30525
|
+
return undefined;
|
|
29355
30526
|
if (!isEmbedded && config?.settings.autoOpen) {
|
|
29356
30527
|
const delay = config.settings.autoOpenDelay || 0;
|
|
29357
30528
|
const timer = setTimeout(() => {
|
|
@@ -29361,7 +30532,7 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
29361
30532
|
return () => clearTimeout(timer);
|
|
29362
30533
|
}
|
|
29363
30534
|
return undefined;
|
|
29364
|
-
}, [config, onOpen, isEmbedded]);
|
|
30535
|
+
}, [config, onOpen, isEmbedded, isMobile]);
|
|
29365
30536
|
// Handle close on Escape key (only for bubble mode)
|
|
29366
30537
|
useEffect(() => {
|
|
29367
30538
|
if (!isOpen || isEmbedded)
|
|
@@ -29375,6 +30546,37 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
29375
30546
|
document.addEventListener('keydown', handleEscapeKey);
|
|
29376
30547
|
return () => document.removeEventListener('keydown', handleEscapeKey);
|
|
29377
30548
|
}, [isOpen, onClose, isEmbedded]);
|
|
30549
|
+
// Handle body scroll lock on mobile when widget is open
|
|
30550
|
+
useEffect(() => {
|
|
30551
|
+
if (!isOpen || isEmbedded)
|
|
30552
|
+
return;
|
|
30553
|
+
// Only apply scroll lock on mobile
|
|
30554
|
+
const checkMobile = window.innerWidth <= 480;
|
|
30555
|
+
if (!checkMobile)
|
|
30556
|
+
return;
|
|
30557
|
+
// Add class to body to lock scrolling
|
|
30558
|
+
document.body.classList.add('ai-chat-widget-open');
|
|
30559
|
+
return () => {
|
|
30560
|
+
document.body.classList.remove('ai-chat-widget-open');
|
|
30561
|
+
};
|
|
30562
|
+
}, [isOpen, isEmbedded]);
|
|
30563
|
+
// Handle welcome bubble visibility per session
|
|
30564
|
+
// Shows on each new session if welcomeBubbleText is configured
|
|
30565
|
+
useEffect(() => {
|
|
30566
|
+
if (isEmbedded || previewMode)
|
|
30567
|
+
return;
|
|
30568
|
+
const bubbleText = welcomeBubbleText ?? config?.appearance?.welcomeBubbleText;
|
|
30569
|
+
if (!bubbleText) {
|
|
30570
|
+
setShowWelcomeBubble(false);
|
|
30571
|
+
return;
|
|
30572
|
+
}
|
|
30573
|
+
// Check if bubble was already dismissed this session
|
|
30574
|
+
const storageKey = `ai-chat-bubble-dismissed-${widgetId || 'default'}`;
|
|
30575
|
+
const wasDismissed = sessionStorage.getItem(storageKey) === 'true';
|
|
30576
|
+
if (!wasDismissed && !isOpen) {
|
|
30577
|
+
setShowWelcomeBubble(true);
|
|
30578
|
+
}
|
|
30579
|
+
}, [widgetId, welcomeBubbleText, config, isOpen, isEmbedded, previewMode]);
|
|
29378
30580
|
// Determine theme - use prop override if provided, otherwise auto-detect
|
|
29379
30581
|
const appearanceConfig = config?.appearance;
|
|
29380
30582
|
const effectiveTheme = theme ?? autoDetectedTheme;
|
|
@@ -29388,6 +30590,7 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
29388
30590
|
const effectiveWelcomeTitle = welcomeTitle ?? appearanceConfig?.welcomeTitle ?? '';
|
|
29389
30591
|
const effectiveWelcomeMessage = welcomeMessage ?? appearanceConfig?.welcomeMessage ?? '';
|
|
29390
30592
|
const effectivePlaceholder = placeholder ?? appearanceConfig?.placeholder ?? '';
|
|
30593
|
+
const effectiveWelcomeBubbleText = welcomeBubbleText ?? appearanceConfig?.welcomeBubbleText ?? '';
|
|
29391
30594
|
// Generate styles using simplified theme system
|
|
29392
30595
|
const simpleAppearance = {
|
|
29393
30596
|
accentColor};
|
|
@@ -29409,6 +30612,18 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
29409
30612
|
return;
|
|
29410
30613
|
const newState = !isOpen;
|
|
29411
30614
|
setIsOpen(newState);
|
|
30615
|
+
// Dismiss welcome bubble when chat is opened
|
|
30616
|
+
if (newState && showWelcomeBubble) {
|
|
30617
|
+
setShowWelcomeBubble(false);
|
|
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
|
+
}
|
|
30626
|
+
}
|
|
29412
30627
|
if (newState) {
|
|
29413
30628
|
onOpen?.();
|
|
29414
30629
|
}
|
|
@@ -29439,13 +30654,13 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = f
|
|
|
29439
30654
|
const IconComponent = isOpen ? iconComponents.FiChevronDown : iconComponents.FiMessageCircle;
|
|
29440
30655
|
// Embedded mode renders directly without wrapper positioning
|
|
29441
30656
|
if (isEmbedded) {
|
|
29442
|
-
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,
|
|
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: sendMessage, onClose: () => { }, onFeedback: handleFeedback, onActionClick: handleActionClick, conversations: conversations, onLoadConversations: loadConversations, onSwitchConversation: switchConversation, onStartNewConversation: startNewConversation, onDeleteConversation: deleteConversation, currentConversationId: conversationId, headerTitleOverride: effectiveHeaderTitle, welcomeTitleOverride: effectiveWelcomeTitle, welcomeMessageOverride: effectiveWelcomeMessage, placeholderOverride: effectivePlaceholder, suggestedQuestionsOverride: suggestedQuestions }) }));
|
|
29443
30658
|
}
|
|
29444
|
-
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' : ''}`, children: [isOpen && (jsx(ChatWindow, { messages: messages, isLoading: isLoading, isTyping: isTyping,
|
|
30659
|
+
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' : ''}`, children: [isOpen && (jsx(ChatWindow, { messages: messages, isLoading: isLoading, isTyping: isTyping, config: config, onSendMessage: sendMessage, onClose: handleToggle, onFeedback: handleFeedback, onActionClick: handleActionClick,
|
|
29445
30660
|
// Chat history props (only active when persistConversation is true)
|
|
29446
30661
|
conversations: conversations, onLoadConversations: loadConversations, onSwitchConversation: switchConversation, onStartNewConversation: startNewConversation, onDeleteConversation: deleteConversation, currentConversationId: conversationId,
|
|
29447
30662
|
// Override props for live preview
|
|
29448
|
-
headerTitleOverride: effectiveHeaderTitle, welcomeTitleOverride: effectiveWelcomeTitle, welcomeMessageOverride: effectiveWelcomeMessage, placeholderOverride: effectivePlaceholder, suggestedQuestionsOverride: suggestedQuestions })), 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, {}) }) })] }) }));
|
|
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, {}) }) })] }) }));
|
|
29449
30664
|
};
|
|
29450
30665
|
|
|
29451
30666
|
export { ApiError, ChatWidget, useChat };
|