@chatwidgetai/chat-widget 0.1.7 → 0.1.9

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/index.esm.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
- import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
2
+ import { useState, useRef, useEffect, useCallback } from 'react';
3
3
 
4
4
  /**
5
5
  * API Client for Widget Communication
@@ -193,6 +193,124 @@ class WidgetApiClient {
193
193
  throw error;
194
194
  }
195
195
  }
196
+ /**
197
+ * Stream chat message responses
198
+ */
199
+ async *sendMessageStream(conversationId, message, fileIds) {
200
+ const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/chat`, {
201
+ method: 'POST',
202
+ headers: {
203
+ 'Content-Type': 'application/json',
204
+ },
205
+ body: JSON.stringify({
206
+ conversationId: conversationId,
207
+ message,
208
+ fileIds,
209
+ timeZone: this.getTimeZone(),
210
+ }),
211
+ });
212
+ if (!response.ok) {
213
+ throw await buildApiError(response, 'Failed to send message');
214
+ }
215
+ if (!response.body) {
216
+ throw new Error('Response body is null');
217
+ }
218
+ const reader = response.body.getReader();
219
+ const decoder = new TextDecoder();
220
+ let buffer = '';
221
+ try {
222
+ while (true) {
223
+ const { done, value } = await reader.read();
224
+ if (done)
225
+ break;
226
+ buffer += decoder.decode(value, { stream: true });
227
+ const lines = buffer.split('\n');
228
+ buffer = lines.pop() || '';
229
+ for (const line of lines) {
230
+ if (line.startsWith('data: ')) {
231
+ try {
232
+ const data = JSON.parse(line.slice(6));
233
+ // Handle error events
234
+ if (data.type === 'error') {
235
+ throw new Error(data.error || 'Stream error');
236
+ }
237
+ // Yield ConversationMessage objects
238
+ if (data.id) {
239
+ yield data;
240
+ }
241
+ }
242
+ catch (e) {
243
+ console.error('[Widget API Client] Failed to parse SSE data:', line, e);
244
+ throw e;
245
+ }
246
+ }
247
+ }
248
+ }
249
+ }
250
+ finally {
251
+ reader.releaseLock();
252
+ }
253
+ }
254
+ /**
255
+ * Stream agent message responses with tool execution
256
+ * Handles streaming events from backend and yields ConversationMessage updates
257
+ */
258
+ async *sendAgentMessageStream(conversationId, message, fileIds) {
259
+ const response = await fetch(`${this.config.apiUrl}/api/widget/${this.config.widgetId}/agent`, {
260
+ method: 'POST',
261
+ headers: {
262
+ 'Content-Type': 'application/json',
263
+ },
264
+ body: JSON.stringify({
265
+ conversationId: conversationId,
266
+ message,
267
+ fileIds,
268
+ timeZone: this.getTimeZone(),
269
+ }),
270
+ });
271
+ if (!response.ok) {
272
+ throw await buildApiError(response, 'Failed to send agent message');
273
+ }
274
+ if (!response.body) {
275
+ throw new Error('Response body is null');
276
+ }
277
+ const reader = response.body.getReader();
278
+ const decoder = new TextDecoder();
279
+ let buffer = '';
280
+ try {
281
+ while (true) {
282
+ const { done, value } = await reader.read();
283
+ if (done)
284
+ break;
285
+ buffer += decoder.decode(value, { stream: true });
286
+ const lines = buffer.split('\n');
287
+ buffer = lines.pop() || '';
288
+ for (const line of lines) {
289
+ if (line.startsWith('data: ')) {
290
+ try {
291
+ const data = JSON.parse(line.slice(6));
292
+ // Handle error events
293
+ if (data.type === 'error') {
294
+ throw new Error(data.error || 'Stream error');
295
+ }
296
+ // Yield ConversationMessage objects that come from the backend
297
+ // The backend yields full ConversationMessage objects during streaming
298
+ if (data.id) {
299
+ yield data;
300
+ }
301
+ }
302
+ catch (e) {
303
+ console.error('[Widget API Client] Failed to parse SSE data:', line, e);
304
+ throw e;
305
+ }
306
+ }
307
+ }
308
+ }
309
+ }
310
+ finally {
311
+ reader.releaseLock();
312
+ }
313
+ }
196
314
  /**
197
315
  * Submit feedback for a message
198
316
  */
@@ -259,8 +377,10 @@ function generateMessageId() {
259
377
  /**
260
378
  * Local Storage Utilities
261
379
  * Handles conversation persistence in browser localStorage
380
+ * Supports multiple conversations per widget with history
262
381
  */
263
382
  const STORAGE_PREFIX = 'ai-chat-widget';
383
+ const MAX_STORED_CONVERSATIONS = 20;
264
384
  /**
265
385
  * Generate a storage key for a widget
266
386
  */
@@ -268,33 +388,113 @@ function getStorageKey(widgetId) {
268
388
  return `${STORAGE_PREFIX}:${widgetId}`;
269
389
  }
270
390
  /**
271
- * Save conversation to localStorage
391
+ * Get widget storage data
272
392
  */
273
- function saveConversation(widgetId, conversationId, messages) {
393
+ function getWidgetStorage(widgetId) {
394
+ try {
395
+ const key = getStorageKey(widgetId);
396
+ const data = localStorage.getItem(key);
397
+ if (!data) {
398
+ return { activeConversationId: null, conversations: {}, history: [] };
399
+ }
400
+ const parsed = JSON.parse(data);
401
+ // Handle legacy format (single conversation)
402
+ if (parsed.conversationId && parsed.messages) {
403
+ const legacy = parsed;
404
+ return {
405
+ activeConversationId: legacy.conversationId,
406
+ conversations: { [legacy.conversationId]: legacy },
407
+ history: [{
408
+ id: legacy.conversationId,
409
+ preview: getConversationPreview(legacy.messages),
410
+ lastUpdated: legacy.lastUpdated,
411
+ messageCount: legacy.messages.length,
412
+ }],
413
+ };
414
+ }
415
+ return parsed;
416
+ }
417
+ catch (error) {
418
+ console.error('Failed to get widget storage:', error);
419
+ return { activeConversationId: null, conversations: {}, history: [] };
420
+ }
421
+ }
422
+ /**
423
+ * Save widget storage data
424
+ */
425
+ function setWidgetStorage(widgetId, storage) {
274
426
  try {
275
427
  const key = getStorageKey(widgetId);
276
- const data = {
428
+ localStorage.setItem(key, JSON.stringify(storage));
429
+ }
430
+ catch (error) {
431
+ console.error('Failed to save widget storage:', error);
432
+ }
433
+ }
434
+ /**
435
+ * Get preview text from messages
436
+ */
437
+ function getConversationPreview(messages) {
438
+ const firstUserMessage = messages.find(m => m.message.type === 'human');
439
+ if (firstUserMessage) {
440
+ return firstUserMessage.message.content.slice(0, 100);
441
+ }
442
+ return 'New conversation';
443
+ }
444
+ /**
445
+ * Save conversation to localStorage (updates both conversation and history)
446
+ */
447
+ function saveConversation(widgetId, conversationId, messages) {
448
+ try {
449
+ const storage = getWidgetStorage(widgetId);
450
+ const now = new Date().toISOString();
451
+ // Update conversation data
452
+ storage.conversations[conversationId] = {
277
453
  conversationId,
278
454
  messages,
279
- lastUpdated: new Date().toISOString(),
455
+ lastUpdated: now,
456
+ };
457
+ // Update active conversation
458
+ storage.activeConversationId = conversationId;
459
+ // Update history entry
460
+ const existingIndex = storage.history.findIndex(h => h.id === conversationId);
461
+ const historyEntry = {
462
+ id: conversationId,
463
+ preview: getConversationPreview(messages),
464
+ lastUpdated: now,
465
+ messageCount: messages.length,
280
466
  };
281
- localStorage.setItem(key, JSON.stringify(data));
467
+ if (existingIndex >= 0) {
468
+ storage.history[existingIndex] = historyEntry;
469
+ }
470
+ else {
471
+ storage.history.unshift(historyEntry);
472
+ }
473
+ // Sort by lastUpdated (most recent first)
474
+ storage.history.sort((a, b) => new Date(b.lastUpdated).getTime() - new Date(a.lastUpdated).getTime());
475
+ // Limit stored conversations
476
+ if (storage.history.length > MAX_STORED_CONVERSATIONS) {
477
+ const toRemove = storage.history.slice(MAX_STORED_CONVERSATIONS);
478
+ storage.history = storage.history.slice(0, MAX_STORED_CONVERSATIONS);
479
+ toRemove.forEach(entry => {
480
+ delete storage.conversations[entry.id];
481
+ });
482
+ }
483
+ setWidgetStorage(widgetId, storage);
282
484
  }
283
485
  catch (error) {
284
486
  console.error('Failed to save conversation:', error);
285
487
  }
286
488
  }
287
489
  /**
288
- * Load conversation from localStorage
490
+ * Load active conversation from localStorage (legacy compatibility)
289
491
  */
290
492
  function loadConversation(widgetId) {
291
493
  try {
292
- const key = getStorageKey(widgetId);
293
- const data = localStorage.getItem(key);
294
- if (!data)
494
+ const storage = getWidgetStorage(widgetId);
495
+ if (!storage.activeConversationId)
295
496
  return null;
296
- const parsed = JSON.parse(data);
297
- return parsed;
497
+ return storage.conversations[storage.activeConversationId] || null;
298
498
  }
299
499
  catch (error) {
300
500
  console.error('Failed to load conversation:', error);
@@ -302,12 +502,52 @@ function loadConversation(widgetId) {
302
502
  }
303
503
  }
304
504
  /**
305
- * Clear conversation from localStorage
505
+ * Load a specific conversation by ID
506
+ */
507
+ function loadConversationById(widgetId, conversationId) {
508
+ try {
509
+ const storage = getWidgetStorage(widgetId);
510
+ return storage.conversations[conversationId] || null;
511
+ }
512
+ catch (error) {
513
+ console.error('Failed to load conversation by ID:', error);
514
+ return null;
515
+ }
516
+ }
517
+ /**
518
+ * Get conversation history list
519
+ */
520
+ function getConversationHistory(widgetId) {
521
+ try {
522
+ const storage = getWidgetStorage(widgetId);
523
+ return storage.history;
524
+ }
525
+ catch (error) {
526
+ console.error('Failed to get conversation history:', error);
527
+ return [];
528
+ }
529
+ }
530
+ /**
531
+ * Set active conversation
532
+ */
533
+ function setActiveConversation(widgetId, conversationId) {
534
+ try {
535
+ const storage = getWidgetStorage(widgetId);
536
+ storage.activeConversationId = conversationId;
537
+ setWidgetStorage(widgetId, storage);
538
+ }
539
+ catch (error) {
540
+ console.error('Failed to set active conversation:', error);
541
+ }
542
+ }
543
+ /**
544
+ * Clear current conversation (start fresh, but keep history)
306
545
  */
307
546
  function clearConversation(widgetId) {
308
547
  try {
309
- const key = getStorageKey(widgetId);
310
- localStorage.removeItem(key);
548
+ const storage = getWidgetStorage(widgetId);
549
+ storage.activeConversationId = null;
550
+ setWidgetStorage(widgetId, storage);
311
551
  }
312
552
  catch (error) {
313
553
  console.error('Failed to clear conversation:', error);
@@ -404,6 +644,8 @@ function useChat(options) {
404
644
  conversationId: '', // Will be set after loading conversation
405
645
  config: null,
406
646
  });
647
+ // Chat history state
648
+ const [conversations, setConversations] = useState([]);
407
649
  const apiClient = useRef(new WidgetApiClient({ widgetId, apiUrl }));
408
650
  // Load configuration on mount and hydrate with existing conversation if available
409
651
  useEffect(() => {
@@ -416,7 +658,7 @@ function useChat(options) {
416
658
  console.log('[useChat] Config fetched successfully:', {
417
659
  hasAppearance: !!config.appearance,
418
660
  });
419
- const persistConversation = config.behavior.persistConversation ?? true;
661
+ const persistConversation = config.settings.persistConversation ?? true;
420
662
  let conversationId = '';
421
663
  let messages = [];
422
664
  if (persistConversation && isStorageAvailable()) {
@@ -464,14 +706,14 @@ function useChat(options) {
464
706
  }, [widgetId, apiUrl, onError]);
465
707
  // Save conversation when messages change
466
708
  useEffect(() => {
467
- const persistConversation = state.config?.behavior.persistConversation ?? true;
709
+ const persistConversation = state.config?.settings.persistConversation ?? true;
468
710
  if (persistConversation &&
469
711
  isStorageAvailable() &&
470
712
  state.messages.length > 0 &&
471
713
  state.conversationId) {
472
714
  saveConversation(widgetId, state.conversationId, state.messages);
473
715
  }
474
- }, [widgetId, state.messages, state.conversationId, state.config?.behavior.persistConversation]);
716
+ }, [widgetId, state.messages, state.conversationId, state.config?.settings.persistConversation]);
475
717
  /**
476
718
  * Send a message
477
719
  */
@@ -493,8 +735,8 @@ function useChat(options) {
493
735
  setState(prev => ({
494
736
  ...prev,
495
737
  messages: [...prev.messages, userMessage],
496
- isLoading: true,
497
- isTyping: true,
738
+ isLoading: false, // Don't show loading, will show typing when stream starts
739
+ isTyping: true, // Show typing indicator immediately
498
740
  error: null,
499
741
  }));
500
742
  onMessage?.(userMessage);
@@ -540,71 +782,56 @@ function useChat(options) {
540
782
  }
541
783
  // Determine if widget has actions (use agent endpoint)
542
784
  const useAgent = state.config?.behavior.agentic || (state.config?.actions && state.config.actions.length > 0);
543
- let response;
544
- if (useAgent) {
545
- // Use agent endpoint - returns ConversationMessage[]
546
- response = await apiClient.current.sendAgentMessage(conversationId, trimmedContent, fileIds);
547
- }
548
- else {
549
- // Use standard chat endpoint
550
- response = await apiClient.current.sendMessage(conversationId, trimmedContent, fileIds);
551
- }
552
- // Both endpoints now return ConversationMessage[] array (FULL conversation)
553
- if (Array.isArray(response)) {
554
- // Response is already ConversationMessage[]
555
- const allMessages = response;
556
- // Find new messages (those not in current state)
557
- const existingIds = new Set(state.messages.map(m => m.id));
558
- const newMessages = allMessages.filter(msg => !existingIds.has(msg.id));
559
- setState(prev => ({
560
- ...prev,
561
- messages: allMessages, // Replace with full conversation
562
- isLoading: false,
563
- isTyping: false,
564
- }));
565
- // Only notify about new messages
566
- newMessages.forEach(msg => onMessage?.(msg));
567
- }
568
- else if (response.type === 'error') {
569
- // Error response from agent
570
- const errorContent = response.message || response.error || 'An error occurred';
571
- const errorMessage = {
572
- id: response.message_id || generateMessageId(),
573
- message: {
574
- type: 'ai',
575
- content: `⚠️ ${errorContent}`,
576
- },
577
- timestamp: response.timestamp || new Date().toISOString(),
578
- sources: [],
579
- };
580
- setState(prev => ({
581
- ...prev,
582
- messages: [...prev.messages, errorMessage],
583
- isLoading: false,
584
- isTyping: false,
585
- error: errorContent,
586
- }));
587
- onMessage?.(errorMessage);
785
+ // Stream the response
786
+ let lastStreamedMessage = null;
787
+ const stream = useAgent
788
+ ? apiClient.current.sendAgentMessageStream(conversationId, trimmedContent, fileIds)
789
+ : apiClient.current.sendMessageStream(conversationId, trimmedContent, fileIds);
790
+ for await (const message of stream) {
791
+ lastStreamedMessage = message;
792
+ // Update state with streamed message
793
+ setState(prev => {
794
+ const existingIndex = prev.messages.findIndex(m => m.id === message.id);
795
+ if (existingIndex >= 0) {
796
+ // Update existing streaming message
797
+ const newMessages = [...prev.messages];
798
+ newMessages[existingIndex] = message;
799
+ // Show typing indicator if:
800
+ // 1. Message is streaming AND has no content (waiting for first token or after tool call)
801
+ // 2. Message is a tool execution (shows loading spinner on the tool)
802
+ const isAIMessage = message.message.type === 'ai';
803
+ const hasContent = isAIMessage && message.message.content.trim().length > 0;
804
+ const isToolExecuting = message.toolExecuting !== undefined;
805
+ return {
806
+ ...prev,
807
+ messages: newMessages,
808
+ isTyping: (message.isStreaming && !hasContent && isAIMessage) || isToolExecuting,
809
+ isLoading: false,
810
+ };
811
+ }
812
+ else {
813
+ // Add new streaming message
814
+ const isAIMessage = message.message.type === 'ai';
815
+ const hasContent = isAIMessage && message.message.content.trim().length > 0;
816
+ const isToolExecuting = message.toolExecuting !== undefined;
817
+ return {
818
+ ...prev,
819
+ messages: [...prev.messages, message],
820
+ isTyping: (message.isStreaming && !hasContent && isAIMessage) || isToolExecuting,
821
+ isLoading: false,
822
+ };
823
+ }
824
+ });
588
825
  }
589
- else {
590
- // Standard message response (type: 'message' or undefined)
591
- const messageContent = response.content || response.message || '';
592
- const assistantMessage = {
593
- id: response.message_id || response.messageId || generateMessageId(),
594
- message: {
595
- type: 'ai',
596
- content: messageContent,
597
- },
598
- timestamp: response.timestamp || new Date().toISOString(),
599
- sources: response.sources || [],
600
- };
601
- setState(prev => ({
602
- ...prev,
603
- messages: [...prev.messages, assistantMessage],
604
- isLoading: false,
605
- isTyping: false,
606
- }));
607
- onMessage?.(assistantMessage);
826
+ // Stream completed - finalize state
827
+ setState(prev => ({
828
+ ...prev,
829
+ isLoading: false,
830
+ isTyping: false,
831
+ }));
832
+ // Notify about final message
833
+ if (lastStreamedMessage) {
834
+ onMessage?.(lastStreamedMessage);
608
835
  }
609
836
  }
610
837
  catch (error) {
@@ -662,11 +889,11 @@ function useChat(options) {
662
889
  conversationId: '',
663
890
  error: null,
664
891
  }));
665
- const persistConversation = state.config?.behavior.persistConversation ?? true;
892
+ const persistConversation = state.config?.settings.persistConversation ?? true;
666
893
  if (persistConversation && isStorageAvailable()) {
667
894
  clearConversation(widgetId);
668
895
  }
669
- }, [widgetId, state.config?.behavior.persistConversation]);
896
+ }, [widgetId, state.config?.settings.persistConversation]);
670
897
  /**
671
898
  * Submit feedback for a message
672
899
  */
@@ -689,6 +916,78 @@ function useChat(options) {
689
916
  onError?.(err);
690
917
  }
691
918
  }, [state.conversationId, onError]);
919
+ /**
920
+ * Load conversation history list from localStorage
921
+ */
922
+ const loadConversations = useCallback(() => {
923
+ const persistConversation = state.config?.settings.persistConversation ?? true;
924
+ if (!persistConversation || !isStorageAvailable()) {
925
+ setConversations([]);
926
+ return;
927
+ }
928
+ const history = getConversationHistory(widgetId);
929
+ setConversations(history.map(entry => ({
930
+ id: entry.id,
931
+ preview: entry.preview,
932
+ lastMessageAt: entry.lastUpdated,
933
+ messageCount: entry.messageCount,
934
+ startedAt: entry.lastUpdated,
935
+ })));
936
+ }, [widgetId, state.config?.settings.persistConversation]);
937
+ /**
938
+ * Switch to a different conversation (from localStorage first, then fetch from server if needed)
939
+ */
940
+ const switchConversation = useCallback(async (conversationId) => {
941
+ const persistConversation = state.config?.settings.persistConversation ?? true;
942
+ // First try to load from localStorage
943
+ if (persistConversation && isStorageAvailable()) {
944
+ const stored = loadConversationById(widgetId, conversationId);
945
+ if (stored) {
946
+ setState(prev => ({
947
+ ...prev,
948
+ conversationId: stored.conversationId,
949
+ messages: stored.messages,
950
+ }));
951
+ setActiveConversation(widgetId, conversationId);
952
+ return;
953
+ }
954
+ }
955
+ // If not in localStorage, fetch from server
956
+ setState(prev => ({ ...prev, isLoading: true, error: null }));
957
+ try {
958
+ const conversation = await apiClient.current.getOrCreateConversation(conversationId);
959
+ setState(prev => ({
960
+ ...prev,
961
+ conversationId: conversation.id,
962
+ messages: conversation.messages,
963
+ isLoading: false,
964
+ }));
965
+ // Save to local storage
966
+ if (persistConversation && isStorageAvailable()) {
967
+ saveConversation(widgetId, conversation.id, conversation.messages);
968
+ }
969
+ }
970
+ catch (error) {
971
+ const errorInfo = deriveErrorInfo(error);
972
+ setState(prev => ({ ...prev, isLoading: false, error: errorInfo.message }));
973
+ }
974
+ }, [widgetId, state.config?.settings.persistConversation]);
975
+ /**
976
+ * Start a new conversation (keeps history)
977
+ */
978
+ const startNewConversation = useCallback(() => {
979
+ setState(prev => ({
980
+ ...prev,
981
+ messages: [],
982
+ conversationId: '',
983
+ error: null,
984
+ }));
985
+ // Clear active conversation but keep history
986
+ const persistConversation = state.config?.settings.persistConversation ?? true;
987
+ if (persistConversation && isStorageAvailable()) {
988
+ clearConversation(widgetId);
989
+ }
990
+ }, [widgetId, state.config?.settings.persistConversation]);
692
991
  return {
693
992
  messages: state.messages,
694
993
  isLoading: state.isLoading,
@@ -699,6 +998,11 @@ function useChat(options) {
699
998
  sendMessage,
700
999
  clearMessages,
701
1000
  submitFeedback,
1001
+ // Chat history features
1002
+ conversations,
1003
+ loadConversations,
1004
+ switchConversation,
1005
+ startNewConversation,
702
1006
  };
703
1007
  }
704
1008
 
@@ -21391,7 +21695,7 @@ const Sources = ({ sources, displayMode = 'with-score' }) => {
21391
21695
  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}`))) }))] }));
21392
21696
  };
21393
21697
 
21394
- const Message = ({ message, showTimestamp = true, enableFeedback = true, showSources = true, sourceDisplayMode = 'with-score', onFeedback, toolCallNameById, }) => {
21698
+ const Message = ({ message, showTimestamp = true, enableFeedback = true, showSources = true, sourceDisplayMode = 'with-score', onFeedback, }) => {
21395
21699
  const formatTime = (timestamp) => {
21396
21700
  const date = typeof timestamp === 'string' ? new Date(timestamp) : timestamp;
21397
21701
  return date.toLocaleTimeString('en-US', {
@@ -21400,72 +21704,84 @@ const Message = ({ message, showTimestamp = true, enableFeedback = true, showSou
21400
21704
  hour12: true,
21401
21705
  });
21402
21706
  };
21403
- // Map message type to CSS class
21404
- const messageClass = message.message.type === 'human' ? 'user' :
21405
- message.message.type === 'ai' ? 'assistant' :
21406
- message.message.type === 'system' ? 'system' : 'tool';
21407
- const isAssistant = message.message.type === 'ai';
21408
- const isSystem = message.message.type === 'system';
21409
- const isTool = message.message.type === 'tool';
21410
- const isHuman = message.message.type === 'human';
21411
- const userMessage = isHuman ? message.message.content.split('--- File Content ---')[0] : '';
21412
- const aiContent = isAssistant ? (message.message.content || '') : '';
21413
- const aiHasContent = isAssistant ? aiContent.trim().length > 0 : false;
21414
- const renderAssistant = () => {
21415
- // Only render assistant bubble if there's textual content
21416
- return jsx(Markdown, { children: aiContent });
21417
- };
21418
21707
  const formatToolName = (name) => {
21419
- // Remove common prefixes like "action_" or "tool_"
21420
- let formatted = name.replace(/^(action_|tool_)/, '');
21421
- // Convert snake_case to Title Case
21422
- formatted = formatted
21708
+ return name
21709
+ .replace(/^(action_|tool_)/, '')
21423
21710
  .split('_')
21424
21711
  .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
21425
21712
  .join(' ');
21426
- return formatted;
21427
21713
  };
21428
- const renderTool = () => {
21429
- const tool = message.message;
21430
- const rawToolName = (toolCallNameById && toolCallNameById[tool.tool_call_id]) || 'Tool';
21431
- const displayName = formatToolName(rawToolName);
21432
- return (jsxs("div", { className: "ai-chat-tool-message", title: rawToolName, children: [jsx("span", { className: "tool-finished", children: jsx("span", { className: "tool-check", children: "\u2713" }) }), jsx("span", { className: "tool-name", children: displayName })] }));
21433
- };
21434
- // If assistant message has no content (e.g., only started a tool), render nothing at all
21435
- if (isAssistant && !aiHasContent) {
21436
- return null;
21714
+ const SpinnerIcon = () => (jsx("svg", { className: "ai-chat-tool-spinner", width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) }));
21715
+ const CheckIcon = () => (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" }) }));
21716
+ 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" }) }));
21717
+ const msgType = message.message.type;
21718
+ const isError = message.isError || false;
21719
+ const isTool = msgType === 'tool';
21720
+ const isAssistant = msgType === 'ai';
21721
+ const isSystem = msgType === 'system';
21722
+ const isHuman = msgType === 'human';
21723
+ // Tool message rendering - show as a minimal pill
21724
+ if (isTool) {
21725
+ // Get tool name from toolExecuting (during execution) or message.name (persisted)
21726
+ const toolName = message.toolExecuting || message.message.name || 'Tool';
21727
+ const hasError = isError;
21728
+ return (jsx("div", { className: "ai-chat-message tool", children: jsxs("div", { className: `ai-chat-tool-message ${hasError ? 'error' : ''}`, title: toolName, children: [message.isStreaming ? (jsx(SpinnerIcon, {})) : hasError ? (jsx(ErrorIcon, {})) : (jsx(CheckIcon, {})), jsx("span", { className: "tool-name", children: formatToolName(toolName) })] }) }));
21729
+ }
21730
+ // AI message rendering
21731
+ if (isAssistant) {
21732
+ const aiContent = message.message.content || '';
21733
+ const hasContent = aiContent.trim().length > 0;
21734
+ if (!hasContent)
21735
+ return null;
21736
+ 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, { 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 }))] })), showSources && message.sources?.length > 0 && (jsx(Sources, { sources: message.sources, displayMode: sourceDisplayMode }))] }));
21737
+ }
21738
+ // System message rendering
21739
+ if (isSystem) {
21740
+ return (jsxs("div", { className: "ai-chat-message system", children: [jsx("div", { className: "ai-chat-message-content", children: jsxs("div", { className: "ai-chat-system-message", children: [jsx("span", { className: "system-icon", children: "\u2139\uFE0F" }), message.message.content] }) }), showTimestamp && (jsx("div", { className: "ai-chat-message-meta", children: jsx("span", { className: "ai-chat-message-timestamp", children: formatTime(message.timestamp) }) }))] }));
21437
21741
  }
21438
- const showTimestampForThis = showTimestamp && !isTool && !(isAssistant && !aiHasContent);
21439
- return (jsxs("div", { className: `ai-chat-message ${messageClass}`, children: [isTool ? (
21440
- // Render tool call completion as a standalone, non-bubble event
21441
- renderTool()) : (jsx("div", { className: "ai-chat-message-content", children: isAssistant ? (renderAssistant()) : isSystem ? (jsxs("div", { className: "ai-chat-system-message", children: [jsx("span", { className: "system-icon", children: "\u2139\uFE0F" }), message.message.content] })) : (userMessage) })), showTimestampForThis && (jsxs("div", { className: "ai-chat-message-meta", children: [jsx("span", { className: "ai-chat-message-timestamp", children: formatTime(message.timestamp) }), isAssistant && aiHasContent && enableFeedback && onFeedback && (jsx(FeedbackButtons, { messageId: message.id, currentFeedback: message.feedback, onFeedback: onFeedback }))] })), isAssistant && aiHasContent && showSources && message.sources && message.sources.length > 0 && (jsx(Sources, { sources: message.sources, displayMode: sourceDisplayMode }))] }));
21742
+ // Human message rendering
21743
+ if (isHuman) {
21744
+ const userContent = message.message.content.split('--- File Content ---')[0];
21745
+ return (jsxs("div", { className: "ai-chat-message user", children: [jsx("div", { className: "ai-chat-message-content", children: userContent }), showTimestamp && (jsx("div", { className: "ai-chat-message-meta", children: jsx("span", { className: "ai-chat-message-timestamp", children: formatTime(message.timestamp) }) }))] }));
21746
+ }
21747
+ return null;
21442
21748
  };
21443
21749
 
21444
21750
  const TypingIndicator = () => {
21445
21751
  return (jsx("div", { className: "ai-chat-message assistant", children: jsxs("div", { className: "ai-chat-typing", children: [jsx("span", { className: "ai-chat-typing-dot" }), jsx("span", { className: "ai-chat-typing-dot" }), jsx("span", { className: "ai-chat-typing-dot" })] }) }));
21446
21752
  };
21447
21753
 
21448
- const MessageList = ({ messages, isTyping = false, showTypingIndicator = true, showTimestamps = true, enableFeedback = true, showSources = true, sourceDisplayMode = 'with-score', welcomeTitle, welcomeMessage, onFeedback, }) => {
21754
+ const SuggestedQuestions = ({ questions, onQuestionClick, }) => {
21755
+ if (!questions || questions.length === 0) {
21756
+ return null;
21757
+ }
21758
+ // Filter out empty questions
21759
+ const validQuestions = questions.filter(q => q && q.trim());
21760
+ if (validQuestions.length === 0) {
21761
+ return null;
21762
+ }
21763
+ return (jsx("div", { className: "ai-chat-suggested-questions", children: jsx("div", { className: "ai-chat-suggested-questions-list", children: validQuestions.slice(0, 5).map((question, index) => (jsx("button", { className: "ai-chat-suggested-question", onClick: () => onQuestionClick(question), type: "button", children: jsx("span", { className: "ai-chat-suggested-question-text", children: question }) }, index))) }) }));
21764
+ };
21765
+
21766
+ const MessageList = ({ messages, isTyping = false, showTypingIndicator = true, showTimestamps = true, enableFeedback = true, showSources = true, sourceDisplayMode = 'with-score', welcomeTitle, welcomeMessage, suggestedQuestions, onSuggestedQuestionClick, onFeedback, }) => {
21767
+ const containerRef = useRef(null);
21449
21768
  const messagesEndRef = useRef(null);
21450
- // Auto-scroll to bottom when new messages arrive
21769
+ // Auto-scroll to bottom only on initial mount/load
21451
21770
  useEffect(() => {
21452
- messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
21453
- }, [messages, isTyping]);
21454
- // Build a map from tool_call_id -> tool name from AI messages
21455
- const toolCallNameById = useMemo(() => {
21456
- const map = {};
21457
- for (const m of messages) {
21458
- if (m.message.type === 'ai' && Array.isArray(m.message.tool_calls)) {
21459
- for (const tc of m.message.tool_calls) {
21460
- if (tc?.id && tc?.name) {
21461
- map[tc.id] = tc.name;
21462
- }
21463
- }
21464
- }
21771
+ const container = containerRef.current;
21772
+ if (!container)
21773
+ return;
21774
+ // Only scroll if content actually overflows AND the user is near the bottom.
21775
+ // This prevents the "first message" case from being pushed to the very bottom
21776
+ // (which looks like a huge empty gap above the first user message).
21777
+ const overflow = container.scrollHeight - container.clientHeight;
21778
+ const isOverflowing = overflow > 20;
21779
+ const nearBottom = container.scrollTop + container.clientHeight >= container.scrollHeight - 80;
21780
+ if (isOverflowing && nearBottom) {
21781
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
21465
21782
  }
21466
- return map;
21467
- }, [messages]);
21468
- return (jsxs("div", { className: "ai-chat-messages", role: "log", "aria-live": "polite", "aria-atomic": "false", children: [messages.length === 0 && (welcomeTitle || welcomeMessage) && (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 }))] })), messages.map((message) => (jsx(Message, { message: message, showTimestamp: showTimestamps, enableFeedback: enableFeedback, showSources: showSources, sourceDisplayMode: sourceDisplayMode, toolCallNameById: toolCallNameById, onFeedback: onFeedback }, message.id))), isTyping && showTypingIndicator && jsx(TypingIndicator, {}), jsx("div", { ref: messagesEndRef })] }));
21783
+ }, [messages, isTyping]);
21784
+ return (jsxs("div", { ref: containerRef, className: "ai-chat-messages", role: "log", "aria-live": "polite", "aria-atomic": "false", children: [(welcomeTitle || welcomeMessage) && (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 })), messages.length === 0 && onSuggestedQuestionClick && suggestedQuestions && suggestedQuestions.length > 0 && (jsx(SuggestedQuestions, { questions: suggestedQuestions, onQuestionClick: onSuggestedQuestionClick }))] })), messages.map((message) => (jsx(Message, { message: message, showTimestamp: showTimestamps, enableFeedback: enableFeedback, showSources: showSources, sourceDisplayMode: sourceDisplayMode, onFeedback: onFeedback }, message.id))), isTyping && showTypingIndicator && jsx(TypingIndicator, {}), jsx("div", { ref: messagesEndRef })] }));
21469
21785
  };
21470
21786
 
21471
21787
  // Allowed file types
@@ -21553,58 +21869,98 @@ const MessageInput = ({ onSend, placeholder = 'Type your message...', disabled =
21553
21869
  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) => {
21554
21870
  const ext = getFileExtension(file.name);
21555
21871
  return (jsxs("div", { className: "ai-chat-file-item", children: [jsx("span", { className: "ai-chat-file-extension", children: ext }), 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", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("path", { d: "M18 6L6 18M6 6l12 12" }) }) })] }, index));
21556
- }) })), 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(','), "aria-label": "File input" }), 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", strokeLinecap: "round", strokeLinejoin: "round", 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: handleChange, onKeyDown: handleKeyDown, placeholder: placeholder, disabled: disabled, rows: 1, "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", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M22 2L11 13" }), jsx("path", { d: "M22 2L15 22L11 13L2 9L22 2Z" })] }) })] })] }));
21872
+ }) })), 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(','), "aria-label": "File input" }), 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", strokeLinecap: "round", strokeLinejoin: "round", 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: handleChange, onKeyDown: handleKeyDown, placeholder: placeholder, disabled: disabled, rows: 1, "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", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M12 19V5" }), jsx("path", { d: "M5 12l7-7 7 7" })] }) })] })] }));
21557
21873
  };
21558
21874
 
21559
- const ArrowIcon = () => (jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("path", { d: "M5 12h14M12 5l7 7-7 7" }) }));
21560
- const SuggestedQuestions = ({ questions, onQuestionClick, }) => {
21561
- if (!questions || questions.length === 0) {
21562
- return null;
21563
- }
21564
- // Filter out empty questions
21565
- const validQuestions = questions.filter(q => q && q.trim());
21566
- if (validQuestions.length === 0) {
21567
- return null;
21568
- }
21569
- return (jsx("div", { className: "ai-chat-suggested-questions", children: jsx("div", { className: "ai-chat-suggested-questions-list", children: validQuestions.slice(0, 3).map((question, index) => (jsxs("button", { className: "ai-chat-suggested-question", onClick: () => onQuestionClick(question), type: "button", children: [jsx("span", { className: "ai-chat-suggested-question-text", children: question }), jsx("span", { className: "ai-chat-suggested-question-icon", children: jsx(ArrowIcon, {}) })] }, index))) }) }));
21570
- };
21571
-
21572
- const MinimizeIcon = () => (jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsx("path", { d: "M5 12h14" }) }));
21573
- const ChatWindow = ({ messages, isLoading, isTyping, error, config, onSendMessage, onClose, onFeedback, }) => {
21875
+ const MenuIcon = () => (jsxs("svg", { width: "26", height: "26", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M4 10h16" }), jsx("path", { d: "M10 14h10" })] }));
21876
+ const PlusIcon = () => (jsxs("svg", { width: "26", height: "26", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [jsx("path", { d: "M12 5v14" }), jsx("path", { d: "M5 12h14" })] }));
21877
+ const ChatWindow = ({ messages, isLoading, isTyping, error, config, onSendMessage, onClose: _onClose, onFeedback,
21878
+ // Chat history props (only active when persistConversation is true)
21879
+ conversations = [], onLoadConversations, onSwitchConversation, onStartNewConversation, currentConversationId,
21880
+ // Override props for live preview
21881
+ headerTitleOverride, welcomeTitleOverride, welcomeMessageOverride, placeholderOverride, suggestedQuestionsOverride, }) => {
21574
21882
  console.log('[ChatWindow] Rendering ChatWindow component');
21575
21883
  const appearance = config?.appearance;
21576
- const behavior = config?.behavior;
21577
- // Use simplified appearance fields
21884
+ const settings = config?.settings;
21885
+ // Check if chat history should be shown (requires both persistConversation AND showChatHistory)
21886
+ const canShowHistory = (settings?.persistConversation ?? true) && (settings?.showChatHistory ?? true);
21887
+ // Apply overrides for live preview (overrides take priority over saved config)
21578
21888
  const size = appearance?.size || 'medium';
21579
- const headerTitle = appearance?.headerTitle || 'AI Assistant';
21580
- const welcomeMessage = appearance?.welcomeMessage || '👋 Hi there! How can I assist you today?';
21581
- const inputPlaceholder = appearance?.placeholder || 'Ask me anything...';
21889
+ const headerTitle = headerTitleOverride ?? appearance?.headerTitle ?? 'AI Assistant';
21890
+ const welcomeTitle = welcomeTitleOverride ?? appearance?.welcomeTitle ?? '';
21891
+ const welcomeMessage = welcomeMessageOverride ?? appearance?.welcomeMessage ?? '';
21892
+ const inputPlaceholder = placeholderOverride ?? appearance?.placeholder ?? 'Ask me anything...';
21582
21893
  console.log('[ChatWindow] Appearance values:', {
21583
21894
  size,
21584
21895
  headerTitle,
21896
+ welcomeTitle,
21585
21897
  welcomeMessage,
21586
21898
  inputPlaceholder,
21587
21899
  });
21588
- // Track if suggested questions should be shown
21589
- const [showSuggestedQuestions, setShowSuggestedQuestions] = useState(true);
21590
- // Hide suggested questions after first user message
21591
- useEffect(() => {
21592
- const userMessages = messages.filter(m => m.message.type === 'human');
21593
- if (userMessages.length > 0) {
21594
- setShowSuggestedQuestions(false);
21900
+ // Track if history panel is open
21901
+ const [showHistory, setShowHistory] = useState(false);
21902
+ // History exit animation when starting a new chat from overview
21903
+ const [isHistoryExiting, setIsHistoryExiting] = useState(false);
21904
+ // Load conversations when history panel opens
21905
+ const handleOpenHistory = () => {
21906
+ setShowHistory(true);
21907
+ if (onLoadConversations) {
21908
+ onLoadConversations();
21909
+ }
21910
+ };
21911
+ // Handle conversation selection
21912
+ const handleSelectConversation = async (conversationId) => {
21913
+ if (onSwitchConversation) {
21914
+ await onSwitchConversation(conversationId);
21915
+ setShowHistory(false);
21595
21916
  }
21596
- }, [messages]);
21917
+ };
21918
+ const handleNewConversation = () => {
21919
+ if (!onStartNewConversation)
21920
+ return;
21921
+ onStartNewConversation();
21922
+ setShowHistory(false);
21923
+ };
21924
+ const handleSendFromOverview = (content) => {
21925
+ const trimmed = content.trim();
21926
+ if (!trimmed)
21927
+ return;
21928
+ if (!onStartNewConversation) {
21929
+ // Fallback: just go back to chat and send
21930
+ setShowHistory(false);
21931
+ onSendMessage(trimmed);
21932
+ return;
21933
+ }
21934
+ setIsHistoryExiting(true);
21935
+ window.setTimeout(() => {
21936
+ onStartNewConversation();
21937
+ setShowHistory(false);
21938
+ setIsHistoryExiting(false);
21939
+ onSendMessage(trimmed);
21940
+ }, 240);
21941
+ };
21597
21942
  // Check if message limit is reached
21598
- const maxMessages = behavior?.maxMessagesPerSession;
21943
+ const maxMessages = settings?.maxMessagesPerSession;
21599
21944
  const userMessageCount = messages.filter(m => m.message.type === 'human').length;
21600
21945
  const isLimitReached = maxMessages ? userMessageCount >= maxMessages : false;
21601
- // Handle suggested question click
21602
21946
  const handleQuestionClick = (question) => {
21603
- setShowSuggestedQuestions(false);
21604
21947
  onSendMessage(question);
21605
21948
  };
21606
- const hasMessages = messages.length > 0;
21607
- return (jsxs("div", { className: `ai-chat-window size-${size}`, role: "dialog", "aria-label": "Chat window", children: [jsxs("div", { className: "ai-chat-header", 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 })] }), jsx("button", { className: "ai-chat-close-button", onClick: onClose, "aria-label": "Minimize chat", children: jsx(MinimizeIcon, {}) })] }), error && (jsx("div", { className: "ai-chat-error", role: "alert", children: error })), 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." })), jsx(MessageList, { messages: messages, isTyping: isTyping, showTypingIndicator: behavior?.showTypingIndicator, showTimestamps: behavior?.showTimestamps, enableFeedback: behavior?.enableFeedback, showSources: behavior?.showSources, sourceDisplayMode: behavior?.sourceDisplayMode, welcomeMessage: welcomeMessage, onFeedback: onFeedback }), showSuggestedQuestions && !hasMessages && behavior?.suggestedQuestions && behavior.suggestedQuestions.length > 0 && (jsx(SuggestedQuestions, { questions: behavior.suggestedQuestions, onQuestionClick: handleQuestionClick })), jsx(MessageInput, { onSend: onSendMessage, placeholder: isLimitReached ? 'Message limit reached' : inputPlaceholder, disabled: isLoading || isLimitReached, enableFileUpload: behavior?.enableFileUpload })] }));
21949
+ // Format date for conversation list
21950
+ const formatDate = (dateString) => {
21951
+ const date = new Date(dateString);
21952
+ const now = new Date();
21953
+ const diffMs = now.getTime() - date.getTime();
21954
+ const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
21955
+ if (diffDays === 0)
21956
+ return 'Today';
21957
+ if (diffDays === 1)
21958
+ return 'Yesterday';
21959
+ if (diffDays < 7)
21960
+ return `${diffDays} days ago`;
21961
+ return date.toLocaleDateString();
21962
+ };
21963
+ 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 })] }), canShowHistory && (jsx("div", { className: "ai-chat-header-actions", children: jsx("button", { className: "ai-chat-close-button", onClick: handleOpenHistory, "aria-label": "Chat overview", children: jsx(MenuIcon, {}) }) }))] })) }), showHistory ? (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) => (jsxs("button", { className: `ai-chat-history-item ${conv.id === currentConversationId ? 'active' : ''}`, onClick: () => handleSelectConversation(conv.id), children: [jsx("div", { className: "ai-chat-history-item-preview", children: conv.preview }), jsxs("div", { className: "ai-chat-history-item-meta", children: [jsx("span", { children: formatDate(conv.lastMessageAt) }), jsxs("span", { children: [conv.messageCount, " messages"] })] })] }, conv.id))) })), jsx(MessageInput, { onSend: (text) => handleSendFromOverview(text), placeholder: inputPlaceholder, disabled: isLoading, enableFileUpload: settings?.enableFileUpload })] })) : (jsxs(Fragment, { children: [error && (jsx("div", { className: "ai-chat-error", role: "alert", children: error })), 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." })), jsx(MessageList, { messages: messages, isTyping: isTyping, showTypingIndicator: settings?.showTypingIndicator, showTimestamps: settings?.showTimestamps, enableFeedback: settings?.enableFeedback, showSources: settings?.showSources, sourceDisplayMode: settings?.sourceDisplayMode, welcomeTitle: welcomeTitle || 'Welcome Message', welcomeMessage: welcomeMessage, suggestedQuestions: suggestedQuestionsOverride ?? settings?.suggestedQuestions, onSuggestedQuestionClick: handleQuestionClick, onFeedback: onFeedback }), jsx(MessageInput, { onSend: onSendMessage, placeholder: isLimitReached ? 'Message limit reached' : inputPlaceholder, disabled: isLoading || isLimitReached, enableFileUpload: settings?.enableFileUpload })] }))] }));
21608
21964
  };
21609
21965
 
21610
21966
  /**
@@ -21787,22 +22143,18 @@ function generateDarkPalette(accentColor) {
21787
22143
  * Generate all CSS custom properties for the widget
21788
22144
  */
21789
22145
  function generateThemeStyles(appearance, theme) {
21790
- const palette = theme === 'dark'
21791
- ? generateDarkPalette(appearance.accentColor)
21792
- : generateLightPalette(appearance.accentColor);
22146
+ const hasAccentColor = appearance.accentColor && appearance.accentColor.trim() !== '';
21793
22147
  // Liquid glass design - frosted glass with transparency
21794
22148
  const isLight = theme === 'light';
21795
- return {
22149
+ // Base styles that don't depend on accent color
22150
+ const styles = {
21796
22151
  // ========================================================================
21797
- // BUTTON (FAB) - Liquid Glass Style
22152
+ // BUTTON (FAB) - Base styles
21798
22153
  // ========================================================================
21799
- '--button-color': palette.accent,
21800
22154
  '--button-opacity': '1',
21801
22155
  '--button-size': '56px',
21802
- '--button-icon-color': palette.userBubbleText,
21803
22156
  '--button-border-radius': '50%',
21804
22157
  '--button-border-width': '0px',
21805
- '--button-border-color': palette.accent,
21806
22158
  '--button-border-opacity': '1',
21807
22159
  '--button-backdrop-blur': '20px',
21808
22160
  '--button-shadow': `0 4px 24px rgba(0, 0, 0, 0.15), 0 2px 8px rgba(0, 0, 0, 0.1)`,
@@ -21825,7 +22177,6 @@ function generateThemeStyles(appearance, theme) {
21825
22177
  // ========================================================================
21826
22178
  '--header-background': 'transparent',
21827
22179
  '--header-opacity': '1',
21828
- '--header-text-color': palette.accent,
21829
22180
  '--header-border-bottom-width': '0px',
21830
22181
  '--header-border-bottom-color': 'transparent',
21831
22182
  '--header-border-bottom-opacity': '0',
@@ -21835,17 +22186,13 @@ function generateThemeStyles(appearance, theme) {
21835
22186
  // ========================================================================
21836
22187
  '--chat-background': 'transparent',
21837
22188
  '--chat-opacity': '1',
21838
- '--welcome-color': palette.text,
21839
- '--message-text-color': palette.text,
21840
22189
  // ========================================================================
21841
22190
  // MESSAGE BUBBLES - Glass style
21842
22191
  // ========================================================================
21843
- '--bubble-user-color': palette.userBubble,
21844
22192
  '--bubble-user-opacity': '1',
21845
- '--user-message-text': palette.userBubbleText,
21846
22193
  '--bubble-assistant-color': isLight ? 'rgba(255, 255, 255, 0.8)' : 'rgba(255, 255, 255, 0.1)',
21847
22194
  '--bubble-assistant-opacity': '1',
21848
- '--assistant-message-text': isLight ? palette.text : '#f0f0f0',
22195
+ '--assistant-message-text': isLight ? '#000000' : '#f0f0f0',
21849
22196
  '--bubble-border-radius': '20px',
21850
22197
  '--bubble-border-width': isLight ? '1px' : '1px',
21851
22198
  '--bubble-border-color': isLight ? 'rgba(0, 0, 0, 0.05)' : 'rgba(255, 255, 255, 0.08)',
@@ -21867,38 +22214,48 @@ function generateThemeStyles(appearance, theme) {
21867
22214
  '--input-border-color': isLight ? 'rgba(0, 0, 0, 0.08)' : 'rgba(255, 255, 255, 0.1)',
21868
22215
  '--input-border-opacity': '1',
21869
22216
  '--input-shadow': `inset 0 1px 3px rgba(0, 0, 0, 0.04), 0 2px 8px rgba(0, 0, 0, 0.04)`,
21870
- '--send-button-background': palette.accent,
21871
22217
  '--send-button-opacity': '1',
21872
22218
  '--send-button-border-radius': '50%',
21873
22219
  '--send-button-border-width': '0px',
21874
- '--send-button-border-color': palette.accent,
21875
22220
  '--send-button-border-opacity': '1',
21876
22221
  // ========================================================================
21877
22222
  // HOVER STATES
21878
22223
  // ========================================================================
21879
22224
  '--hover-button-scale': '1.08',
21880
22225
  '--hover-button-opacity': '1',
21881
- '--hover-input-border-color': palette.accent,
21882
22226
  '--hover-send-button-opacity': '1',
21883
22227
  '--hover-close-button-opacity': '1',
21884
22228
  // ========================================================================
21885
- // ACTIVE STATES
21886
- // ========================================================================
21887
- '--active-input-border-color': palette.accent,
21888
- '--active-input-shadow': `0 0 0 4px ${withAlpha(palette.accent, 0.15)}`,
21889
- // ========================================================================
21890
- // GENERAL
21891
- // ========================================================================
21892
- '--primary-color': palette.accent,
21893
- '--background-color': palette.background,
21894
- '--text-color': palette.text,
21895
- '--border-color': palette.border,
21896
- // ========================================================================
21897
22229
  // GLASS EFFECTS
21898
22230
  // ========================================================================
21899
22231
  '--glass-blur': '20px',
21900
22232
  '--glass-saturation': '180%',
21901
22233
  };
22234
+ // Only add accent-color-based styles if an accent color is provided
22235
+ if (hasAccentColor) {
22236
+ const palette = theme === 'dark'
22237
+ ? generateDarkPalette(appearance.accentColor)
22238
+ : generateLightPalette(appearance.accentColor);
22239
+ // Add accent color styles
22240
+ styles['--button-color'] = palette.accent;
22241
+ styles['--button-icon-color'] = palette.userBubbleText;
22242
+ styles['--button-border-color'] = palette.accent;
22243
+ styles['--header-text-color'] = palette.accent;
22244
+ styles['--welcome-color'] = palette.text;
22245
+ styles['--message-text-color'] = palette.text;
22246
+ styles['--bubble-user-color'] = palette.userBubble;
22247
+ styles['--user-message-text'] = palette.userBubbleText;
22248
+ styles['--send-button-background'] = palette.accent;
22249
+ styles['--send-button-border-color'] = palette.accent;
22250
+ styles['--hover-input-border-color'] = palette.accent;
22251
+ styles['--active-input-border-color'] = palette.accent;
22252
+ styles['--active-input-shadow'] = `0 0 0 4px ${withAlpha(palette.accent, 0.15)}`;
22253
+ styles['--primary-color'] = palette.accent;
22254
+ styles['--background-color'] = palette.background;
22255
+ styles['--text-color'] = palette.text;
22256
+ styles['--border-color'] = palette.border;
22257
+ }
22258
+ return styles;
21902
22259
  }
21903
22260
 
21904
22261
  /**
@@ -22009,7 +22366,7 @@ function styleInject(css, ref) {
22009
22366
  if ( ref === void 0 ) ref = {};
22010
22367
  var insertAt = ref.insertAt;
22011
22368
 
22012
- if (!css || typeof document === 'undefined') { return; }
22369
+ if (typeof document === 'undefined') { return; }
22013
22370
 
22014
22371
  var head = document.head || document.getElementsByTagName('head')[0];
22015
22372
  var style = document.createElement('style');
@@ -22032,10 +22389,7 @@ function styleInject(css, ref) {
22032
22389
  }
22033
22390
  }
22034
22391
 
22035
- var css_248z$1 = ".ai-chat-widget{--primary-color:#07f;--background-color:#fff;--text-color:#1f2937;--border-color:#e5e7eb;--user-message-bg:var(--primary-color);--user-message-text:#fff;--assistant-message-bg:#f3f4f6;--assistant-message-text:#374151;--input-bg:#fff;--input-border:#d1d5db;--shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);--shadow-lg:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);--button-color:#07f;--button-size:56px;--button-border-radius:28px;--button-border-width:0px;--button-border-color:#07f;--button-opacity:1;--button-backdrop-blur:0px;--button-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);--card-background:#fff;--card-border-radius:16px;--card-border-width:0px;--card-border-color:#e5e7eb;--card-opacity:1;--card-backdrop-blur:0px;--card-shadow:0 25px 50px -12px rgba(0,0,0,.25);--header-background:#07f;--header-text-color:#fff;--header-font-size:18px;--header-border-bottom-width:0px;--header-border-bottom-color:#e5e7eb;--header-opacity:1;--header-backdrop-blur:0px;--chat-background:#fff;--chat-opacity:1;--chat-backdrop-blur:0px;--welcome-font-size:16px;--welcome-color:#1f2937;--welcome-opacity:1;--bubble-user-color:#07f;--bubble-assistant-color:#f3f4f6;--bubble-border-radius:16px;--bubble-border-width:0px;--bubble-border-color:#e5e7eb;--bubble-opacity:1;--typing-animation-color:#f3f4f6;--typing-animation-opacity:1;--typing-animation-border-width:0px;--typing-animation-border-color:#e5e7eb;--typing-animation-border-radius:16px;--footer-background:#fff;--footer-border-top-width:1px;--footer-border-top-color:#e5e7eb;--footer-opacity:1;--footer-backdrop-blur:0px;--input-background:#fff;--input-border-radius:24px;--input-border-width:1.5px;--input-border-color:#d1d5db;--input-opacity:1;--input-shadow:0 1px 2px 0 rgba(0,0,0,.05);--send-button-background:#07f;--send-button-border-radius:20px;--send-button-border-width:0px;--send-button-border-color:#07f;--send-button-opacity:1;--hover-button-scale:1.05;--hover-button-opacity:0.9;--hover-input-border-color:#9ca3af;--hover-send-button-opacity:0.85;--hover-close-button-opacity:1;--active-input-border-color:#07f;--active-input-shadow:0 0 0 3px rgba(0,119,255,.1);-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.dark{--background-color:#001d3d;--text-color:#f9fafb;--border-color:#374151;--assistant-message-bg:#036;--assistant-message-text:#e5e7eb;--input-bg:#002855;--input-border:#374151}.ai-chat-widget.dark .ai-chat-message.system .ai-chat-message-content{background-color:#78350f;color:#fef3c7}.ai-chat-widget.dark .ai-chat-message.tool .ai-chat-message-content{background-color:#1e3a8a;color:#dbeafe}.ai-chat-widget-container{font-size:14px;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}.ai-chat-button{align-items:center;backdrop-filter:blur(var(--button-backdrop-blur));-webkit-backdrop-filter:blur(var(--button-backdrop-blur));background-color:var(--button-color);border:var(--button-border-width) solid var(--button-border-color);border-radius:var(--button-border-radius);box-shadow:var(--button-shadow);color:#fff;cursor:pointer;display:flex;height:var(--button-size);justify-content:center;opacity:var(--button-opacity);transition:all .3s cubic-bezier(.4,0,.2,1);width:var(--button-size)}.ai-chat-button:hover{opacity:.9}.ai-chat-button:active{opacity:.8}.ai-chat-button-svg{height:50%;min-height:24px;min-width:24px;width:50%}.ai-chat-button-icon{font-size:1.5em;line-height:1}.ai-chat-window{border-radius:var(--card-border-radius);box-shadow:0 0 0 var(--card-border-width) var(--card-border-color-rgba,var(--card-border-color)),var(--card-shadow);display:flex;flex-direction:column;overflow:hidden;position:absolute}.ai-chat-window>*{position:relative;z-index:1}.ai-chat-window:before{backdrop-filter:blur(var(--card-backdrop-blur));-webkit-backdrop-filter:blur(var(--card-backdrop-blur));background-color:var(--card-background);border-radius:var(--card-border-radius);content:\"\";inset:0;opacity:var(--card-opacity);pointer-events:none;position:absolute;z-index:0}.ai-chat-widget-container.bottom-right .ai-chat-window{bottom:calc(var(--button-size, 60px) + 16px);right:0}.ai-chat-widget-container.bottom-left .ai-chat-window{bottom:calc(var(--button-size, 60px) + 16px);left:0}.ai-chat-widget-container.top-right .ai-chat-window{right:0;top:calc(var(--button-size, 60px) + 16px)}.ai-chat-widget-container.top-left .ai-chat-window{left:0;top:calc(var(--button-size, 60px) + 16px)}.ai-chat-button{z-index:1}.ai-chat-window{z-index:2}.ai-chat-window.size-small{height:500px;width:380px}.ai-chat-window.size-medium{height:650px;width:440px}.ai-chat-window.size-large{height:750px;width:520px}.ai-chat-logo{border-radius:50%;height:32px;object-fit:cover;width:32px}.ai-chat-messages::-webkit-scrollbar{width:6px}.ai-chat-messages::-webkit-scrollbar-track{background:transparent}.ai-chat-messages::-webkit-scrollbar-thumb{background:var(--border-color);border-radius:3px}.ai-chat-messages::-webkit-scrollbar-thumb:hover{background:var(--input-border)}.ai-chat-message{display:flex;flex-direction:column;gap:4px}.ai-chat-message.user{align-items:flex-end}.ai-chat-message.assistant{align-items:flex-start}.ai-chat-message.system{align-items:center}.ai-chat-message.tool{align-items:flex-start}.ai-chat-message-content{word-wrap:break-word;border:var(--bubble-border-width) solid var(--bubble-border-color);border-radius:var(--bubble-border-radius);font-size:15px;line-height:1.6;max-width:80%;opacity:var(--bubble-opacity);padding:12px 16px}.ai-chat-message.user .ai-chat-message-content{background-color:var(--bubble-user-color);border-bottom-right-radius:4px;box-shadow:0 1px 2px rgba(0,0,0,.1);color:var(--user-message-text)}.ai-chat-message.assistant .ai-chat-message-content{background-color:var(--bubble-assistant-color,#f3f4f6);border:1px solid var(--bubble-border-color,rgba(0,0,0,.08));border-radius:var(--bubble-border-radius,16px);border-bottom-left-radius:4px;box-shadow:none;color:var(--assistant-message-text);max-width:85%;padding:12px 16px}.ai-chat-message.assistant .ai-chat-message-content p{margin:0 0 12px}.ai-chat-message.assistant .ai-chat-message-content p:last-child{margin-bottom:0}.ai-chat-message.assistant .ai-chat-message-content ol,.ai-chat-message.assistant .ai-chat-message-content ul{margin:8px 0 12px;padding-left:24px}.ai-chat-message.assistant .ai-chat-message-content li{line-height:1.5;margin:6px 0}.ai-chat-message.assistant .ai-chat-message-content li::marker{color:var(--primary-color,#07f)}.ai-chat-message.assistant .ai-chat-message-content ol li::marker{font-weight:600}.ai-chat-message.assistant .ai-chat-message-content strong{font-weight:600}.ai-chat-message.assistant .ai-chat-message-content em{font-style:italic}.ai-chat-message.assistant .ai-chat-message-content code{background-color:rgba(0,0,0,.06);border-radius:4px;font-family:SF Mono,Consolas,Monaco,monospace;font-size:.9em;padding:2px 6px}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content code{background-color:hsla(0,0%,100%,.1)}.ai-chat-message.assistant .ai-chat-message-content pre{background-color:rgba(0,0,0,.06);border-radius:8px;margin:8px 0 12px;overflow-x:auto;padding:12px}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content pre{background-color:hsla(0,0%,100%,.08)}.ai-chat-message.assistant .ai-chat-message-content pre code{background-color:transparent;border-radius:0;padding:0}.ai-chat-message.assistant .ai-chat-message-content blockquote{border-left:3px solid var(--primary-color,#07f);color:var(--text-muted,#6b7280);margin:8px 0 12px;padding:4px 0 4px 12px}.ai-chat-message.assistant .ai-chat-message-content a{color:var(--primary-color,#07f);text-decoration:underline}.ai-chat-message.assistant .ai-chat-message-content a:hover{opacity:.8}.ai-chat-message.assistant .ai-chat-message-content h1,.ai-chat-message.assistant .ai-chat-message-content h2,.ai-chat-message.assistant .ai-chat-message-content h3,.ai-chat-message.assistant .ai-chat-message-content h4,.ai-chat-message.assistant .ai-chat-message-content h5,.ai-chat-message.assistant .ai-chat-message-content h6{font-weight:600;line-height:1.3;margin:16px 0 8px}.ai-chat-message.assistant .ai-chat-message-content h1:first-child,.ai-chat-message.assistant .ai-chat-message-content h2:first-child,.ai-chat-message.assistant .ai-chat-message-content h3:first-child{margin-top:0}.ai-chat-message.assistant .ai-chat-message-content hr{border:none;border-top:1px solid var(--border-color,#e5e7eb);margin:12px 0}.ai-chat-message.system .ai-chat-message-content{background-color:#fef3c7;border-radius:8px;color:#92400e;font-size:12px;font-style:italic;max-width:90%;text-align:center}.ai-chat-message.tool .ai-chat-message-content{background-color:#dbeafe;border-bottom-left-radius:4px;color:#1e40af;font-family:Courier New,monospace;font-size:13px}.ai-chat-tool-indicators{display:flex;gap:6px;margin-top:6px}.tool-indicator{align-items:center;background:#dbeafe;border-radius:50%;color:#1e40af;display:inline-flex;height:18px;justify-content:center;overflow:hidden;position:relative;width:18px}.tool-indicator .icon{font-size:12px;line-height:1;z-index:1}.tool-indicator.started:after{animation:ai-spin 1s linear infinite;border:2px solid rgba(30,64,175,.25);border-radius:50%;border-top-color:#1e40af;content:\"\";inset:0;position:absolute}@keyframes ai-spin{to{transform:rotate(1turn)}}.ai-chat-tool-message{align-items:center;background:rgba(16,185,129,.1);border:1px solid rgba(16,185,129,.2);border-radius:20px;color:#059669;display:inline-flex;gap:8px;padding:8px 14px}.ai-chat-widget.dark .ai-chat-tool-message{background:rgba(16,185,129,.15);border-color:rgba(16,185,129,.25);color:#34d399}.tool-finished{align-items:center;display:inline-flex;font-size:14px;justify-content:center}.tool-finished .tool-icon{display:none}.tool-finished .tool-check{font-size:14px;font-weight:700}.tool-name{font-size:13px;font-weight:500}.ai-chat-message-timestamp{color:rgba(0,0,0,.6);filter:invert(1) grayscale(1) contrast(1.2);mix-blend-mode:difference;padding:0 4px}.ai-chat-welcome{align-items:center;color:var(--welcome-color);display:flex;flex-direction:column;justify-content:center;min-height:200px;opacity:var(--welcome-opacity);padding:60px 32px 40px;text-align:center}.ai-chat-welcome-title{color:var(--primary-color);font-size:28px;font-weight:700;letter-spacing:-.03em;margin-bottom:12px}.ai-chat-welcome-text{color:var(--assistant-message-text);font-size:16px;line-height:1.6;max-width:280px;opacity:.7}.ai-chat-typing{align-items:center;background-color:var(--assistant-message-bg);border-radius:12px;border-bottom-left-radius:4px;display:flex;gap:4px;max-width:80px;padding:10px 14px}.ai-chat-typing-dot{animation:typingBounce 1.4s infinite;background-color:#9ca3af;border-radius:50%;height:8px;width:8px}.ai-chat-typing-dot:nth-child(2){animation-delay:.2s}.ai-chat-typing-dot:nth-child(3){animation-delay:.4s}@keyframes typingBounce{0%,60%,to{transform:translateY(0)}30%{transform:translateY(-8px)}}.ai-chat-file-button{align-items:center;background:none;border:none;border-radius:6px;color:var(--text-color);cursor:pointer;display:flex;justify-content:center;padding:8px;transition:background-color .2s}.ai-chat-file-button:hover:not(:disabled){background-color:rgba(0,0,0,.05)}.ai-chat-file-button:disabled{cursor:not-allowed;opacity:.5}.ai-chat-file-list{display:flex;flex-wrap:wrap;gap:8px;padding:8px 12px}.ai-chat-file-item{align-items:center;background-color:rgba(0,0,0,.05);border-radius:6px;display:flex;font-size:12px;gap:8px;padding:6px 10px}.ai-chat-file-extension{background-color:var(--primary-color);border-radius:3px;color:#fff;display:inline-block;font-size:10px;font-weight:600;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:500;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:4px;transition:opacity .15s ease}.ai-chat-file-remove:hover{opacity:1}.ai-chat-message-attachments{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}.ai-chat-message-attachment{align-items:center;background-color:rgba(0,0,0,.08);border-radius:4px;display:inline-flex;font-size:11px;gap:4px;padding:3px 8px}.ai-chat-attachment-icon{font-size:12px}.ai-chat-attachment-ext{background-color:var(--primary-color);border-radius:2px;color:#fff;display:inline-block;font-size:9px;font-weight:600;padding:1px 4px;text-transform:uppercase}.ai-chat-attachment-name{max-width:120px;opacity:.8;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ai-chat-sources{background-color:rgba(0,0,0,.02);border-radius:6px;font-size:12px;margin-top:8px;overflow:hidden}.ai-chat-sources-toggle{align-items:center;background:none;border:none;cursor:pointer;display:flex;gap:6px;padding:8px 10px;text-align:left;transition:background-color .2s;width:100%}.ai-chat-sources-toggle:hover{background-color:rgba(0,0,0,.03)}.ai-chat-sources-icon{color:var(--text-muted);font-size:10px;transition:transform .2s}.ai-chat-sources-title{color:var(--text-color);flex:1;font-size:11px;font-weight:600;letter-spacing:.5px;text-transform:uppercase}.ai-chat-source-item{border-top:1px solid rgba(0,0,0,.05);color:#6b7280;display:flex;gap:8px;padding:8px 10px}.ai-chat-source-item:last-child{border-bottom:none}.ai-chat-source-number{color:var(--primary-color);flex-shrink:0;font-weight:600}.ai-chat-source-details{display:flex;flex:1;flex-direction:column;gap:4px}.ai-chat-source-score{color:#9ca3af;font-size:11px}.ai-chat-source-content{color:#6b7280;font-size:11px;font-style:italic;line-height:1.4}.ai-chat-source-metadata{display:flex;flex-wrap:wrap;gap:6px;margin-top:2px}.ai-chat-source-meta-item{background-color:rgba(0,0,0,.05);border-radius:3px;color:#6b7280;font-size:10px;padding:2px 6px}.ai-chat-message-meta{align-items:center;display:inline-flex;gap:6px;height:20px}.ai-chat-message-timestamp{color:#71717a;font-size:11px;line-height:1}.ai-chat-feedback{gap:0}.ai-chat-feedback,.ai-chat-feedback-button{align-items:center;display:inline-flex;height:20px}.ai-chat-feedback-button{background:none!important;border:none;border-radius:0;color:#71717a;cursor:pointer;justify-content:center;padding:0 4px;transition:color .15s ease}.ai-chat-feedback-button:hover:not(:disabled){background:none!important;color:#52525b}.ai-chat-feedback-button:active:not(:disabled){opacity:.7}.ai-chat-feedback-button:disabled{cursor:not-allowed;opacity:.4}.ai-chat-feedback-button.active{background:none!important;color:var(--primary-color,#07f)}.ai-chat-feedback-submitted{align-items:center;animation:feedbackMorph .3s cubic-bezier(.34,1.56,.64,1);display:flex;gap:6px}.ai-chat-feedback-checkmark{animation:checkmarkPop .3s cubic-bezier(.34,1.56,.64,1);color:#10b981;font-size:16px;font-weight:700}.ai-chat-feedback-text{animation:textSlideIn .3s ease;color:#10b981;font-size:13px;font-weight:500}@keyframes feedbackMorph{0%{opacity:.5;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@keyframes checkmarkPop{0%{opacity:0;transform:scale(0) rotate(-45deg)}50%{transform:scale(1.3) rotate(0deg)}to{opacity:1;transform:scale(1) rotate(0deg)}}@keyframes textSlideIn{0%{opacity:0;transform:translateX(-10px)}to{opacity:1;transform:translateX(0)}}.ai-chat-error{align-items:flex-start;background-color:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.2);border-radius:12px;color:#ef4444;display:flex;font-size:14px;font-weight:500;gap:10px;line-height:1.5;margin:8px 16px;padding:12px 16px}.ai-chat-widget.dark .ai-chat-error{background-color:rgba(239,68,68,.15);border-color:rgba(239,68,68,.25);color:#fca5a5}.ai-chat-error:before{align-items:center;background:rgba(239,68,68,.2);border-radius:50%;content:\"⚠\";display:flex;flex-shrink:0;font-size:14px;font-weight:700;height:20px;justify-content:center;width:20px}.ai-chat-widget.dark .ai-chat-error:before{background:rgba(239,68,68,.25)}.ai-chat-warning{background-color:rgba(245,158,11,.1);border:1px solid rgba(245,158,11,.2);border-radius:12px;color:#d97706;font-size:13px;margin:8px 16px;padding:12px 16px}.ai-chat-widget.dark .ai-chat-warning{background-color:rgba(245,158,11,.15);border-color:rgba(245,158,11,.25);color:#fbbf24}.ai-chat-suggested-questions{bottom:80px;left:0;padding:0 20px 16px;position:absolute;right:0;z-index:5}.ai-chat-suggested-questions-list{display:flex;flex-direction:column;gap:8px}.ai-chat-suggested-question{align-items:center;background:#fff;border:1px solid rgba(0,0,0,.08);border-radius:14px;box-shadow:0 1px 3px rgba(0,0,0,.04);color:#374151;cursor:pointer;display:flex;font-size:14px;font-weight:500;gap:12px;justify-content:space-between;padding:14px 16px;text-align:left;transition:all .15s ease;width:100%}.ai-chat-suggested-question:hover{background:#f9fafb;border-color:rgba(0,0,0,.12);box-shadow:0 2px 8px rgba(0,0,0,.06)}.ai-chat-suggested-question:active{transform:scale(.98)}.ai-chat-suggested-question-text{flex:1;line-height:1.4}.ai-chat-suggested-question-icon{color:var(--primary-color,#07f);flex-shrink:0;opacity:.7;transition:transform .15s ease,opacity .15s ease}.ai-chat-suggested-question:hover .ai-chat-suggested-question-icon{opacity:1;transform:translateX(3px)}@media (max-width:480px){.ai-chat-window{border-radius:0!important;bottom:0!important;height:100%!important;left:0!important;position:fixed!important;right:0!important;top:0!important;width:100%!important}.ai-chat-widget-container{bottom:20px!important;right:20px!important}.ai-chat-suggested-question{font-size:13px;padding:9px 10px}}.ai-chat-action-approval{background:linear-gradient(135deg,#07f,#001d3d);border-radius:16px;box-shadow:0 4px 12px rgba(0,119,255,.3);color:#fff;margin:16px;padding:16px}.ai-chat-action-approval-content{align-items:flex-start;display:flex;gap:12px;margin-bottom:16px}.ai-chat-action-approval-icon{align-items:center;background:hsla(0,0%,100%,.2);border-radius:8px;display:flex;flex-shrink:0;height:40px;justify-content:center;width:40px}.ai-chat-action-approval-text{flex:1}.ai-chat-action-approval-title{font-size:15px;font-weight:600;margin-bottom:4px}.ai-chat-action-approval-description{font-size:13px;line-height:1.4;opacity:.95}.ai-chat-action-approval-buttons{display:flex;gap:8px;justify-content:flex-end}.ai-chat-action-button{align-items:center;border:none;border-radius:8px;cursor:pointer;display:flex;font-family:inherit;font-size:14px;font-weight:500;gap:6px;padding:8px 16px;transition:all .2s ease}.ai-chat-action-button:disabled{cursor:not-allowed;opacity:.6}.ai-chat-action-button-reject{background:hsla(0,0%,100%,.2);color:#fff}.ai-chat-action-button-reject:hover:not(:disabled){background:hsla(0,0%,100%,.3)}.ai-chat-action-button-approve{background:#fff;color:#07f}.ai-chat-action-button-approve:hover:not(:disabled){background:#f0f0f0;box-shadow:0 2px 8px rgba(0,0,0,.15);transform:translateY(-1px)}.ai-chat-action-spinner{animation:ai-chat-spin .6s linear infinite;border:2px solid rgba(0,119,255,.3);border-radius:50%;border-top-color:#07f;display:inline-block;height:14px;width:14px}@keyframes ai-chat-spin{to{transform:rotate(1turn)}}";
22036
- styleInject(css_248z$1);
22037
-
22038
- var css_248z = ".ai-chat-widget{--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.2s;--duration-normal:0.35s;--duration-slow:0.5s}.ai-chat-button{align-items:center;background:var(--button-color,var(--primary-color,#07f));border:none;border-radius:50%;box-shadow:0 2px 8px rgba(0,0,0,.15);cursor:pointer;display:flex;height:48px;justify-content:center;overflow:hidden;position:relative;transition:opacity .15s ease;width:48px}.ai-chat-button:hover{opacity:.9}.ai-chat-button:active{opacity:.8}.ai-chat-button-svg{height:20px;transition:transform .15s ease;width:20px}.ai-chat-button.is-open .ai-chat-button-svg{transform:rotate(0deg)}.ai-chat-window{animation:windowOpen var(--duration-slow) var(--spring-bounce);background:#fff;border:1px solid #e5e7eb;border-radius:16px;box-shadow:0 4px 24px rgba(0,0,0,.12),0 1px 3px rgba(0,0,0,.08);display:flex;flex-direction:column;overflow:hidden;position:absolute;transform-origin:bottom right}.ai-chat-widget.dark .ai-chat-window{background:#18181b;border-color:#27272a}@keyframes windowOpen{0%{opacity:0;transform:scale(.9) translateY(20px)}to{opacity:1;transform:scale(1) translateY(0)}}.ai-chat-window.closing{animation:windowClose var(--duration-normal) var(--spring-smooth) forwards}@keyframes windowClose{0%{opacity:1;transform:scale(1) translateY(0)}to{opacity:0;transform:scale(.9) translateY(20px)}}.ai-chat-header{align-items:center;background:#fff;border-bottom:1px solid #e5e7eb;display:flex;justify-content:space-between;padding:16px 20px;position:relative;z-index:10}.ai-chat-widget.dark .ai-chat-header{background:#18181b;border-bottom-color:#27272a}.ai-chat-header-content{align-items:center;display:flex;flex:1;gap:12px}.ai-chat-logo{border-radius:10px;height:36px;object-fit:cover;width:36px}.ai-chat-title{color:#18181b;font-size:15px;font-weight:600;letter-spacing:-.01em}.ai-chat-widget.dark .ai-chat-title{color:#fafafa}.ai-chat-close-button{align-items:center;background:transparent;border:none;border-radius:8px;color:#a1a1aa;cursor:pointer;display:flex;height:32px;justify-content:center;padding:0;transition:all .15s ease;width:32px}.ai-chat-widget.dark .ai-chat-close-button{color:#71717a}.ai-chat-close-button:hover{background:#f4f4f5;color:#52525b}.ai-chat-widget.dark .ai-chat-close-button:hover{background:#27272a;color:#a1a1aa}.ai-chat-close-button:active{transform:scale(.95)}.ai-chat-messages{-webkit-overflow-scrolling:touch;background:#f4f4f5;display:flex;flex:1;flex-direction:column;gap:12px;overflow-x:hidden;overflow-y:auto;padding:16px 16px 90px;position:relative;scroll-behavior:smooth}.ai-chat-widget.dark .ai-chat-messages{background:#18181b}.ai-chat-messages::-webkit-scrollbar{width:4px}.ai-chat-messages::-webkit-scrollbar-track{background:transparent}.ai-chat-messages::-webkit-scrollbar-thumb{background:rgba(0,0,0,.15);border-radius:4px}.ai-chat-messages::-webkit-scrollbar-thumb:hover{background:rgba(0,0,0,.25)}.ai-chat-message{animation:messageSlideIn var(--duration-normal) var(--spring-bounce);display:flex;flex-direction:column;gap:6px}@keyframes messageSlideIn{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}.ai-chat-message-content{word-wrap:break-word;border-radius:12px;font-size:14px;line-height:1.5;max-width:85%;padding:8px 14px}.ai-chat-message.user .ai-chat-message-content{background:var(--bubble-user-color,var(--primary-color,#07f));border-bottom-right-radius:4px;color:#fff;margin-left:auto}.ai-chat-message.assistant .ai-chat-message-content{background:#fff;border:1px solid #e5e7eb;border-bottom-left-radius:4px;color:#374151}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content{background:#18181b;border-color:#27272a;color:#e4e4e7}.ai-chat-typing{align-items:center;animation:messageSlideIn var(--duration-normal) var(--spring-bounce);background:#fff;border:1px solid #e5e7eb;border-radius:12px;border-bottom-left-radius:4px;display:flex;gap:4px;max-width:64px;padding:12px 16px}.ai-chat-widget.dark .ai-chat-typing{background:#18181b;border-color:#27272a}.ai-chat-typing-dot{animation:typingPulse 1.4s ease-in-out infinite;background:linear-gradient(135deg,var(--primary-color) 0,color-mix(in srgb,var(--primary-color) 70%,#000) 100%);border-radius:50%;height:8px;width:8px}.ai-chat-typing-dot:nth-child(2){animation-delay:.15s}.ai-chat-typing-dot:nth-child(3){animation-delay:.3s}@keyframes typingPulse{0%,60%,to{opacity:.4;transform:translateY(0) scale(1)}30%{opacity:1;transform:translateY(-6px) scale(1.1)}}.ai-chat-input-container{background:transparent;bottom:0;left:0;padding:0 16px 16px;position:absolute;right:0;z-index:10}.ai-chat-input-container:before{background:linear-gradient(0deg,#f4f4f5 80%,transparent);bottom:0;content:\"\";height:48px;left:0;pointer-events:none;position:absolute;right:0;z-index:-1}.ai-chat-widget.dark .ai-chat-input-container:before{background:linear-gradient(0deg,#18181b 80%,transparent)}.ai-chat-input-wrapper{align-items:center;background:#f4f4f5;border:1px solid #e5e7eb;border-radius:9999px;display:flex;gap:0;padding:4px 4px 4px 16px;position:relative;transition:all .15s ease;z-index:5}.ai-chat-widget.dark .ai-chat-input-wrapper{background:#27272a;border-color:#3f3f46}.ai-chat-input-wrapper:focus-within{border-color:var(--primary-color);box-shadow:0 0 0 2px rgba(var(--primary-color-rgb,0,119,255),.15)}.ai-chat-input{background:transparent;border:none;color:var(--text-color);flex:1;font-family:inherit;font-size:14px;line-height:1.4;max-height:100px;min-height:20px;outline:none;padding:8px 0;resize:none}.ai-chat-input::placeholder{color:rgba(0,0,0,.35)}.ai-chat-widget.dark .ai-chat-input::placeholder{color:hsla(0,0%,100%,.35)}.ai-chat-send-button{align-items:center;background:transparent;border:none;border-radius:8px;color:#a1a1aa;cursor:pointer;display:flex;flex-shrink:0;height:36px;justify-content:center;padding:0;transition:all .15s ease;width:36px}.ai-chat-widget.dark .ai-chat-send-button{color:#71717a}.ai-chat-send-button.active{background:var(--primary-color,#07f);color:#fff}.ai-chat-send-button.active:hover:not(:disabled){opacity:.9}.ai-chat-send-button:active:not(:disabled){transform:scale(.95)}.ai-chat-send-button:disabled{cursor:not-allowed;opacity:.4}.ai-chat-tool-message{align-items:center;animation:toolPulse 2s ease-in-out infinite;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);background:linear-gradient(135deg,rgba(16,185,129,.15),rgba(16,185,129,.08));border:1px solid rgba(16,185,129,.2);border-radius:16px;display:inline-flex;gap:10px;padding:10px 16px}@keyframes toolPulse{0%,to{box-shadow:0 0 0 0 rgba(16,185,129,.2)}50%{box-shadow:0 0 0 8px rgba(16,185,129,0)}}.tool-finished{align-items:center;animation:toolComplete .5s var(--spring-bounce);background:linear-gradient(135deg,#10b981,#059669);border-radius:50%;color:#fff;display:inline-flex;height:28px;justify-content:center;position:relative;width:28px}@keyframes toolComplete{0%{transform:scale(0) rotate(-180deg)}to{transform:scale(1) rotate(0deg)}}.tool-indicator{align-items:center;background:linear-gradient(135deg,rgba(59,130,246,.2),rgba(59,130,246,.1));border-radius:50%;color:#3b82f6;display:inline-flex;height:24px;justify-content:center;position:relative;width:24px}.tool-indicator.started:after{animation:toolSpin .8s linear infinite;border:2px solid transparent;border-radius:50%;border-top-color:#3b82f6;content:\"\";inset:-2px;position:absolute}@keyframes toolSpin{to{transform:rotate(1turn)}}.ai-chat-welcome{align-items:center;animation:welcomeFadeIn var(--duration-slow) var(--spring-smooth);display:flex;flex-direction:column;justify-content:center;min-height:200px;padding:60px 32px 40px;text-align:center}@keyframes welcomeFadeIn{0%{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}.ai-chat-welcome-title{color:var(--primary-color);font-size:28px;font-weight:700;letter-spacing:-.03em;margin-bottom:12px}.ai-chat-welcome-text{color:var(--text-color);font-size:16px;line-height:1.6;max-width:280px;opacity:.6}.ai-chat-suggested-questions{bottom:76px;left:0;padding:0 16px 12px;position:absolute;right:0;z-index:5}.ai-chat-suggested-questions-list{display:flex;flex-direction:column;gap:6px}.ai-chat-suggested-question{align-items:center;background:#fff;border:1px solid #e5e7eb;border-radius:10px;color:#374151;cursor:pointer;display:flex;font-size:13px;font-weight:500;gap:10px;justify-content:space-between;padding:10px 14px;text-align:left;transition:all .15s ease;width:100%}.ai-chat-widget.dark .ai-chat-suggested-question{background:#18181b;border-color:#27272a;color:#e4e4e7}.ai-chat-suggested-question:hover{background:#f4f4f5;border-color:#d4d4d8}.ai-chat-widget.dark .ai-chat-suggested-question:hover{background:#27272a;border-color:#3f3f46}.ai-chat-suggested-question:active{transform:scale(.98)}.ai-chat-suggested-question-text{flex:1;line-height:1.4}.ai-chat-suggested-question-icon{color:var(--primary-color,#07f);flex-shrink:0;opacity:.7;transition:transform .15s ease,opacity .15s ease}.ai-chat-suggested-question:hover .ai-chat-suggested-question-icon{opacity:1;transform:translateX(3px)}.ai-chat-feedback-button{align-items:center;background:rgba(0,0,0,.04);border:none;border-radius:8px;cursor:pointer;display:flex;font-size:16px;gap:4px;padding:6px 10px;transition:all var(--duration-fast) var(--spring-bounce)}.ai-chat-feedback-button:hover:not(:disabled){background:rgba(0,0,0,.08);transform:scale(1.15)}.ai-chat-feedback-button:active:not(:disabled){transform:scale(.9)}.ai-chat-feedback-button.active{background:rgba(var(--primary-color-rgb,0,119,255),.15)}@media (max-width:480px){.ai-chat-window{animation:mobileSlideUp var(--duration-normal) var(--spring-smooth);border-radius:0!important;bottom:0!important;height:100%!important;left:0!important;position:fixed!important;right:0!important;top:0!important;width:100%!important}@keyframes mobileSlideUp{0%{transform:translateY(100%)}to{transform:translateY(0)}}.ai-chat-header{padding-top:max(16px,env(safe-area-inset-top))}.ai-chat-input-container{padding-bottom:max(20px,env(safe-area-inset-bottom))}}";
22392
+ var css_248z = ".ai-chat-widget{--radius-sm:4px;--radius-md:8px;--radius-lg:12px;--radius-xl:16px;--radius-2xl:18px;--radius-pill:9999px;--radius-window-top:22px;--radius-window-bottom:44px;--radius-window-gutter:16px;--radius-chat-bubble:14px;--radius-preset-badge:13px;--radius-history-item:14px;--radius-action-badge:14px;--radius-input:62px;--space-xs:4px;--space-sm:8px;--space-md:16px;--space-lg:24px;--space-xl:32px;--text-xs:12px;--text-sm:14px;--text-md:15px;--text-lg:18px;--text-xl:22px;--text-2xl:28px;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--line-height-tight:1.3;--line-height-normal:1.4;--line-height-relaxed:1.6;--bg-primary:#fff;--bg-secondary:#f4f4f4;--bg-tertiary:#e5e7eb;--bg-hover:#e5e7eb;--text-primary:#3e3e3e;--text-secondary:#000;--text-muted:#71717a;--text-placeholder:#a1a1aa;--border-default:#d3d3d3;--border-subtle:#e5e7eb;--border-muted:#f4f4f5;--user-bg:#f4f3f0;--user-text:#000;--user-bg-hover:#e8e7e4;--agent-bg:transparent;--agent-text:#000;--input-bg:#f4f4f4;--input-border:#d3d3d3;--input-text:#000;--btn-primary-bg:#151515;--btn-primary-text:#f4f4f4;--btn-secondary-bg:transparent;--btn-secondary-text:#71717a;--spring-bounce:cubic-bezier(0.34,1.56,0.64,1);--spring-smooth:cubic-bezier(0.4,0,0.2,1);--spring-snappy:cubic-bezier(0.2,0,0,1);--duration-fast:0.15s;--duration-normal:0.25s;--duration-slow:0.35s;--shadow-sm:0 1px 2px rgba(0,0,0,.05);--shadow-md:0 2px 8px rgba(0,0,0,.1);--shadow-lg:0 4px 16px rgba(0,0,0,.12);--shadow-window:0px 0px 15px 9px rgba(0,0,0,.1);--shadow-button:0px 0px 15px 9px rgba(0,0,0,.03)}.ai-chat-widget.dark{--bg-primary:#282625;--bg-secondary:#4a4846;--bg-tertiary:#484848;--bg-hover:#484848;--text-primary:#fff;--text-secondary:#fff;--text-muted:#a1a1aa;--text-placeholder:#71717a;--border-default:#5d5b5b;--border-subtle:#5d5b5b;--border-muted:#5d5b5b;--user-bg:#484848;--user-text:#fff;--user-bg-hover:#5a5a5a;--agent-bg:transparent;--agent-text:#fff;--input-bg:#4a4846;--input-border:#5d5b5b;--input-text:#fff;--btn-primary-bg:#fff;--btn-primary-text:#312f2d;--btn-secondary-bg:transparent;--btn-secondary-text:#a1a1aa;--shadow-window:0px 0px 15px 9px rgba(0,0,0,.2);--shadow-button:0px 0px 15px 9px rgba(0,0,0,.03);--shadow-input:0px 0px 10px rgba(0,0,0,.15)}.ai-chat-widget{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif}.ai-chat-widget-container{font-size:var(--text-sm);line-height:1.5;position:fixed;z-index:9999}.ai-chat-widget-container.bottom-right{bottom:20px;right:20px}.ai-chat-widget-container.bottom-left{bottom:20px;left:20px}.ai-chat-widget-container.top-right{right:20px;top:20px}.ai-chat-widget-container.top-left{left:20px;top:20px}@keyframes windowOpen{0%{opacity:0;transform:scale(.9) translateY(20px)}to{opacity:1;transform:scale(1) translateY(0)}}@keyframes windowClose{0%{opacity:1;transform:scale(1) translateY(0)}to{opacity:0;transform:scale(.9) translateY(20px)}}@keyframes messageSlideIn{0%{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}@keyframes welcomeFadeIn{0%{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}}@keyframes typingPulse{0%,60%,to{opacity:.4;transform:translateY(0) scale(1)}30%{opacity:1;transform:translateY(-4px) scale(1.1)}}@keyframes ai-chat-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 feedbackMorph{0%{opacity:.5;transform:scale(.8)}to{opacity:1;transform:scale(1)}}@keyframes checkmarkPop{0%{opacity:0;transform:scale(0) rotate(-45deg)}50%{transform:scale(1.3) rotate(0deg)}to{opacity:1;transform:scale(1) rotate(0deg)}}@keyframes ai-chat-history-exit{to{opacity:0;transform:translateY(-18px)}}@media (max-width:480px){.ai-chat-window{animation:mobileSlideUp var(--duration-normal) var(--spring-smooth);border-radius:0!important;bottom:0!important;height:100%!important;left:0!important;position:fixed!important;right:0!important;top:0!important;width:100%!important}@keyframes mobileSlideUp{0%{transform:translateY(100%)}to{transform:translateY(0)}}.ai-chat-header{padding-top:max(16px,env(safe-area-inset-top))}.ai-chat-input-container{padding-bottom:max(20px,env(safe-area-inset-bottom))}}.ai-chat-button{align-items:center;background:var(--button-color,var(--btn-primary-bg));border:1px solid var(--button-border-color,var(--button-color,var(--btn-primary-bg)));border-radius:50%;box-shadow:var(--shadow-button,0 0 15px 9px rgba(0,0,0,.03));color:var(--button-icon-color,var(--btn-primary-text));cursor:pointer;display:flex;height:var(--button-size,56px);justify-content:center;overflow:hidden;position:relative;transition:filter var(--duration-fast) ease,transform var(--duration-fast) ease;width:var(--button-size,56px);z-index:1}.ai-chat-button:hover{transform:scale(1.02)}.ai-chat-button:active{transform:scale(.98)}.ai-chat-button-svg{height:50%;min-height:24px;min-width:24px;transition:transform var(--duration-fast) ease;width:50%}.ai-chat-button.is-open .ai-chat-button-svg{transform:rotate(0deg)}.ai-chat-button-icon{font-size:1.5em;line-height:1}.ai-chat-window{animation:windowOpen var(--duration-slow,.35s) var(--spring-bounce,cubic-bezier(.34,1.56,.64,1));background:var(--bg-primary,#fff);border:1px solid var(--border-default,#d3d3d3);border-radius:var(--radius-window-top,22px) var(--radius-window-top,22px) var(--radius-window-bottom,44px) var(--radius-window-bottom,44px);box-shadow:var(--shadow-window,0 0 15px 5px rgba(0,0,0,.08));display:flex;flex-direction:column;overflow:hidden;position:absolute;transform-origin:bottom right;z-index:2}.ai-chat-widget.dark .ai-chat-window{background:var(--bg-primary,#282625);border-color:var(--border-default,#5d5b5b);border-width:.7px}.ai-chat-window.closing{animation:windowClose var(--duration-normal) var(--spring-smooth) forwards}.ai-chat-window.size-small{height:580px;width:380px}.ai-chat-window.size-medium{height:720px;width:440px}.ai-chat-window.size-large{height:820px;width:520px}.ai-chat-widget-container.bottom-right .ai-chat-window{bottom:calc(var(--button-size, 56px) + 8px);right:0}.ai-chat-widget-container.bottom-left .ai-chat-window{bottom:calc(var(--button-size, 56px) + 8px);left:0}.ai-chat-widget-container.top-right .ai-chat-window{right:0;top:calc(var(--button-size, 56px) + 8px)}.ai-chat-widget-container.top-left .ai-chat-window{left:0;top:calc(var(--button-size, 56px) + 8px)}.ai-chat-header{align-items:center;background:var(--bg-primary,#fff);border-bottom:1px solid var(--border-default,#d3d3d3);border-radius:var(--radius-window-top,22px) var(--radius-window-top,22px) 0 0;display:flex;justify-content:space-between;padding:18px var(--space-md,16px);position:relative;z-index:10}.ai-chat-widget.dark .ai-chat-header{background:var(--bg-primary,#282625);border-bottom-color:var(--border-default,#5d5b5b);border-bottom-width:.7px}.ai-chat-header.is-history{padding-left:var(--space-md)}.ai-chat-header.is-history .ai-chat-title{flex:1;min-width:0;overflow:hidden;padding-right:var(--space-lg);text-overflow:ellipsis;white-space:nowrap}.ai-chat-header-content{align-items:center;display:flex;flex:1;gap:var(--space-lg)}.ai-chat-header-actions{align-items:center;display:flex;gap:var(--space-sm)}.ai-chat-logo{border-radius:10px;height:36px;object-fit:cover;width:36px}.ai-chat-title{color:var(--text-primary,#3e3e3e);font-size:var(--text-xl,22px);font-weight:var(--font-weight-bold,700);letter-spacing:-.02em}.ai-chat-widget.dark .ai-chat-title{color:var(--text-primary,#fff)}.ai-chat-close-button{align-items:center;background:transparent;border:none;border-radius:var(--radius-md);color:var(--text-primary);cursor:pointer;display:flex;height:32px;justify-content:center;padding:0;transition:color var(--duration-fast) ease;width:32px}.ai-chat-close-button:hover{color:var(--text-muted)}.ai-chat-close-button:active{transform:scale(.95)}.ai-chat-header-button{align-items:center;background:transparent;border:none;border-radius:var(--radius-md);color:var(--text-muted);cursor:pointer;display:flex;height:32px;justify-content:center;padding:0;transition:all var(--duration-fast) ease;width:32px}.ai-chat-header-button:hover{background:var(--bg-secondary);color:var(--text-secondary)}.ai-chat-header-button svg{height:18px;width:18px}.ai-chat-messages{-webkit-overflow-scrolling:touch;-ms-overflow-style:none;align-items:stretch;background:var(--bg-primary,#fff);display:flex;flex:1;flex-direction:column;gap:var(--space-md,16px);justify-content:flex-start;overflow-x:hidden;overflow-y:auto;padding:var(--space-lg,24px) var(--space-md,16px) 100px;position:relative;scroll-behavior:smooth;scrollbar-width:none}.ai-chat-widget.dark .ai-chat-messages{background:var(--bg-primary,#18181b)}.ai-chat-messages::-webkit-scrollbar{display:none}.ai-chat-message{animation:messageSlideIn var(--duration-normal) var(--spring-bounce);display:flex;flex-direction:column;gap:6px}.ai-chat-message-content{word-wrap:break-word;font-size:var(--text-md);line-height:var(--line-height-relaxed);max-width:85%}.ai-chat-message.user{align-items:flex-end}.ai-chat-message.user .ai-chat-message-content{background:var(--user-bg,#f4f3f0);border:none;border-radius:var(--radius-chat-bubble,15px);box-shadow:none;color:var(--user-text,#000);padding:var(--space-sm,8px) var(--space-md,16px)}.ai-chat-widget.dark .ai-chat-message.user .ai-chat-message-content{background:var(--user-bg,#484848);color:var(--user-text,#fff)}.ai-chat-message.user .ai-chat-message-meta{justify-content:flex-end;padding-right:var(--space-xs)}.ai-chat-message.assistant{align-items:flex-start}.ai-chat-message.assistant .ai-chat-message-content{background:var(--agent-bg,transparent);border:none;color:var(--agent-text,#18181b);padding:0}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content{color:var(--agent-text,#fafafa)}.ai-chat-message.system{align-items:center}.ai-chat-message.system .ai-chat-message-content{background:hsla(48,96%,89%,.8);border-radius:var(--radius-md);color:#92400e;font-size:var(--text-xs);font-style:italic;max-width:90%;padding:var(--space-sm) var(--space-md);text-align:center}.ai-chat-widget.dark .ai-chat-message.system .ai-chat-message-content{background:rgba(120,53,15,.5);color:#fef3c7}.ai-chat-message.tool{align-items:flex-start}.ai-chat-message.tool .ai-chat-message-content{background:rgba(219,234,254,.8);border-radius:var(--radius-chat-bubble);border-bottom-left-radius:var(--radius-xs);color:#1e40af;font-family:Courier New,monospace;font-size:var(--text-sm);padding:var(--space-sm) var(--space-md)}.ai-chat-widget.dark .ai-chat-message.tool .ai-chat-message-content{background:rgba(30,58,138,.5);color:#dbeafe}.ai-chat-message-meta{align-items:center;color:var(--text-muted);display:flex;font-size:var(--text-xs);gap:var(--space-sm);padding-left:var(--space-xs)}.ai-chat-message-timestamp{font-size:var(--text-xs);line-height:1}.ai-chat-typing{align-items:center;animation:messageSlideIn var(--duration-normal) var(--spring-bounce);background:transparent;display:flex;gap:5px;padding:0}.ai-chat-typing-dot{animation:typingPulse 1.4s ease-in-out infinite;background:var(--text-muted);border-radius:50%;height:6px;width:6px}.ai-chat-typing-dot:nth-child(2){animation-delay:.15s}.ai-chat-typing-dot:nth-child(3){animation-delay:.3s}.ai-chat-welcome{align-items:stretch;animation:welcomeFadeIn var(--duration-slow) var(--spring-smooth);display:flex;flex-direction:column;justify-content:flex-start;padding:0;text-align:left}.ai-chat-welcome-text,.ai-chat-welcome-title{align-self:flex-start}.ai-chat-welcome-title{color:var(--text-primary);font-size:var(--text-2xl);font-weight:var(--font-weight-semibold);letter-spacing:-.02em;margin-bottom:var(--space-md)}.ai-chat-welcome-text{color:var(--text-secondary);font-size:var(--text-md);line-height:var(--line-height-relaxed);max-width:100%}.ai-chat-error{align-items:flex-start;align-self:center;background:var(--bg-secondary);border:none;border-radius:var(--radius-chat-bubble);color:var(--text-primary);display:flex;font-size:var(--text-md);font-weight:var(--font-weight-normal);gap:10px;line-height:1.5;margin:0 auto;max-width:90%;padding:10px var(--space-md)}.ai-chat-error:before{align-items:center;background:rgba(239,68,68,.15);border-radius:50%;color:#ef4444;content:\"⚠\";display:flex;flex-shrink:0;font-size:var(--text-xs);font-weight:700;height:18px;justify-content:center;margin-top:2px;width:18px}.ai-chat-widget.dark .ai-chat-error:before{background:rgba(239,68,68,.2);color:#fca5a5}.ai-chat-message.assistant .ai-chat-message-content p{margin:0 0 12px}.ai-chat-message.assistant .ai-chat-message-content p:last-child{margin-bottom:0}.ai-chat-message.assistant .ai-chat-message-content ol,.ai-chat-message.assistant .ai-chat-message-content ul{margin:8px 0 12px;padding-left:24px}.ai-chat-message.assistant .ai-chat-message-content li{line-height:1.5;margin:6px 0}.ai-chat-message.assistant .ai-chat-message-content strong{font-weight:var(--font-weight-semibold)}.ai-chat-message.assistant .ai-chat-message-content em{font-style:italic}.ai-chat-message.assistant .ai-chat-message-content code{background:rgba(0,0,0,.06);border-radius:var(--radius-sm);font-family:SF Mono,Consolas,Monaco,monospace;font-size:.9em;padding:2px 6px}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content code{background:hsla(0,0%,100%,.1)}.ai-chat-message.assistant .ai-chat-message-content pre{background:rgba(0,0,0,.06);border-radius:var(--radius-md);margin:8px 0 12px;overflow-x:auto;padding:var(--space-sm)}.ai-chat-widget.dark .ai-chat-message.assistant .ai-chat-message-content pre{background:hsla(0,0%,100%,.08)}.ai-chat-message.assistant .ai-chat-message-content pre code{background:transparent;border-radius:0;padding:0}.ai-chat-message.assistant .ai-chat-message-content blockquote{border-left:3px solid var(--btn-primary-bg);color:var(--text-muted);margin:8px 0 12px;padding:4px 0 4px 12px}.ai-chat-message.assistant .ai-chat-message-content a{color:var(--btn-primary-bg);text-decoration:underline}.ai-chat-message.assistant .ai-chat-message-content a:hover{opacity:.8}.ai-chat-message.assistant .ai-chat-message-content h1,.ai-chat-message.assistant .ai-chat-message-content h2,.ai-chat-message.assistant .ai-chat-message-content h3,.ai-chat-message.assistant .ai-chat-message-content h4,.ai-chat-message.assistant .ai-chat-message-content h5,.ai-chat-message.assistant .ai-chat-message-content h6{font-weight:var(--font-weight-semibold);line-height:var(--line-height-tight);margin:16px 0 8px}.ai-chat-message.assistant .ai-chat-message-content h1:first-child,.ai-chat-message.assistant .ai-chat-message-content h2:first-child,.ai-chat-message.assistant .ai-chat-message-content h3:first-child{margin-top:0}.ai-chat-message.assistant .ai-chat-message-content hr{border:none;border-top:1px solid var(--border-subtle);margin:12px 0}.ai-chat-input-container{background:linear-gradient(to bottom,transparent 0,var(--bg-primary,#fff) 50%,var(--bg-primary,#fff) 100%);bottom:0;left:0;padding-top:30px;position:absolute;right:0;z-index:10}.ai-chat-widget.dark .ai-chat-input-container{background:linear-gradient(to bottom,transparent 0,var(--bg-primary,#282625) 50%,var(--bg-primary,#282625) 100%)}.ai-chat-input-container.separate{padding:0 var(--radius-window-gutter,16px) var(--radius-window-gutter,16px);padding-top:30px}.ai-chat-input-wrapper{align-items:center;background:var(--input-bg,#f4f4f4);border:1px solid var(--input-border,#d3d3d3);border-radius:var(--radius-input,62px);display:flex;gap:0;height:52px;padding:6px 6px 6px 12px;position:relative;transition:all var(--duration-fast,.15s) ease;z-index:5}.ai-chat-widget.dark .ai-chat-input-wrapper{background:var(--input-bg,#4a4846);border-color:var(--input-border,#5d5b5b);border-width:.7px;box-shadow:var(--shadow-input,0 0 10px rgba(0,0,0,.15))}.ai-chat-input-wrapper:focus-within{border-color:var(--text-muted,#a1a1aa)}.ai-chat-input{background:transparent;border:none;color:var(--input-text,#000);flex:1;font-family:inherit;font-size:var(--text-md,15px);height:20px;line-height:var(--line-height-normal,1.4);max-height:20px;min-height:20px;outline:none;overflow:hidden;padding:0 var(--space-sm,8px);resize:none}.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;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;background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#151515))));border:none;border-radius:50%;color:var(--button-icon-color,var(--btn-primary-text,#f4f4f4));cursor:pointer;display:flex;flex-shrink:0;height:40px;justify-content:center;padding:0;transition:all var(--duration-fast,.15s) ease;width:40px}.ai-chat-widget.dark .ai-chat-send-button{background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#fff))));color:var(--button-icon-color,var(--btn-primary-text,#312f2d))}.ai-chat-send-button.active{background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#151515))));color:var(--button-icon-color,var(--btn-primary-text,#f4f4f4))}.ai-chat-widget.dark .ai-chat-send-button.active{background:var(--send-button-background,var(--primary-color,var(--button-color,var(--btn-primary-bg,#fff))));color:var(--button-icon-color,var(--btn-primary-text,#312f2d))}.ai-chat-send-button:hover:not(:disabled){opacity:.8}.ai-chat-send-button:active:not(:disabled){transform:scale(.95)}.ai-chat-send-button:disabled{cursor:not-allowed;opacity:.3}.ai-chat-file-list{display:flex;flex-wrap:wrap;gap:var(--space-sm);padding:var(--space-sm) var(--space-sm)}.ai-chat-file-item{align-items:center;background:rgba(0,0,0,.05);border-radius:6px;display:flex;font-size:var(--text-xs);gap:var(--space-sm);padding:6px 10px}.ai-chat-file-extension{background:var(--btn-primary-bg);border-radius:3px;color:var(--btn-primary-text);display:inline-block;font-size:10px;font-weight:var(--font-weight-semibold);min-width:40px;padding:2px 6px;text-align:center;text-transform:uppercase}.ai-chat-file-info{display:flex;flex:1;flex-direction:column;gap:2px;min-width:0}.ai-chat-file-name{font-weight:var(--font-weight-medium);max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ai-chat-file-size{color:var(--text-muted);font-size:10px;opacity:.7}.ai-chat-file-remove{align-items:center;background:none;border:none;color:inherit;cursor:pointer;display:flex;justify-content:center;opacity:.5;padding:var(--space-xs);transition:opacity var(--duration-fast) ease}.ai-chat-file-remove:hover{opacity:1}.ai-chat-suggested-questions{align-self:flex-end;margin:0;padding:16px 0 0;width:100%}.ai-chat-suggested-questions-list{align-items:center;display:flex;flex-wrap:wrap;gap:6px;justify-content:flex-end}.ai-chat-suggested-question{background:var(--primary-color,var(--button-color,var(--user-bg,#f4f3f0)));border:none;border-radius:var(--radius-preset-badge,13px);color:var(--button-icon-color,var(--user-text,#000));cursor:pointer;font-size:14px;font-weight:400;line-height:1.3;padding:8px 14px;transition:background .15s ease,opacity .15s ease;white-space:nowrap}.ai-chat-widget.dark .ai-chat-suggested-question{background:var(--primary-color,var(--button-color,var(--user-bg,#484848)));color:var(--button-icon-color,var(--user-text,#fff))}.ai-chat-suggested-question-text{white-space:nowrap}.ai-chat-suggested-question:hover{filter:brightness(.9)}.ai-chat-widget.dark .ai-chat-suggested-question:hover{filter:brightness(1.15)}.ai-chat-suggested-question:active{transform:scale(.98)}.ai-chat-suggested-question-icon{display:none}.ai-chat-feedback-buttons{align-items:center;display:flex;gap:var(--space-xs)}.ai-chat-feedback{align-items:center;display:inline-flex;gap:0;height:20px}.ai-chat-feedback-button{align-items:center;background:transparent!important;border:none;border-radius:var(--radius-sm);color:var(--text-placeholder);cursor:pointer;display:flex;font-size:var(--text-sm);height:20px;justify-content:center;padding:var(--space-xs);transition:all var(--duration-fast) var(--spring-bounce)}.ai-chat-feedback-button:hover:not(:disabled){background:none!important;color:var(--text-secondary)}.ai-chat-feedback-button:active:not(:disabled){transform:scale(.9)}.ai-chat-feedback-button:disabled{cursor:not-allowed;opacity:.4}.ai-chat-feedback-button.active{background:none!important;color:var(--text-primary)}.ai-chat-feedback-submitted{align-items:center;animation:feedbackMorph .3s var(--spring-bounce);display:flex;gap:6px}.ai-chat-feedback-checkmark{animation:checkmarkPop .3s var(--spring-bounce);color:#10b981;font-size:var(--text-md);font-weight:700}.ai-chat-feedback-text{color:#10b981;font-size:var(--text-sm);font-weight:var(--font-weight-medium)}.ai-chat-history-panel{background:var(--bg-primary,#fff);display:flex;flex:1;flex-direction:column;overflow:hidden}.ai-chat-widget.dark .ai-chat-history-panel{background:var(--bg-primary,#18181b)}.ai-chat-history-empty,.ai-chat-history-loading{align-items:center;color:var(--text-muted);display:flex;flex:1;font-size:var(--text-sm);justify-content:center;padding:var(--space-lg);text-align:center}.ai-chat-history-list{-ms-overflow-style:none;display:flex;flex:1;flex-direction:column;gap:var(--space-sm);overflow-y:auto;padding:var(--space-md) var(--space-md) 120px;scrollbar-width:none}.ai-chat-history-list::-webkit-scrollbar{display:none}.ai-chat-history-list.exiting{animation:ai-chat-history-exit .22s var(--spring-smooth) forwards}.ai-chat-history-item{align-items:stretch;background:var(--user-bg,#f4f4f5);border:none;border-radius:var(--radius-history-item,15px);cursor:pointer;display:flex;flex-direction:column;margin:0;padding:var(--space-sm,8px) var(--space-md,16px);text-align:left;transition:background var(--duration-fast,.15s) ease;width:100%}.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);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-tool-row{align-items:center;display:flex;gap:10px;margin:var(--space-sm) 0 6px}.ai-chat-tool-gear{align-items:center;color:var(--text-primary);display:inline-flex;height:26px;justify-content:center;width:26px}.ai-chat-tool-gear.spinning{animation:ai-chat-gear-spin .9s linear infinite}.ai-chat-tool-pills{display:flex;flex-wrap:wrap;gap:var(--space-sm)}.ai-chat-tool-pill{align-items:center;border-radius:var(--radius-pill);color:var(--button-icon-color,var(--btn-primary-text,var(--text-primary)));display:inline-flex;font-size:var(--text-sm);font-weight:var(--font-weight-medium);justify-content:center;line-height:1;padding:var(--space-sm) 14px}.ai-chat-tool-pill,.ai-chat-tool-pill.active{background:var(--primary-color,var(--button-color,var(--btn-primary-bg,var(--bg-secondary))))}.ai-chat-tool-pill.active{animation:ai-chat-tool-active 1.1s ease-in-out infinite;overflow:hidden;position:relative}.ai-chat-tool-pill.done{background:var(--primary-color,var(--button-color,var(--btn-primary-bg)));color:var(--button-icon-color,var(--btn-primary-text))}.ai-chat-tool-indicators{display:flex;gap:6px;margin-top:6px}.tool-indicator{align-items:center;background:linear-gradient(135deg,rgba(59,130,246,.2),rgba(59,130,246,.1));border-radius:50%;color:#3b82f6;display:inline-flex;height:24px;justify-content:center;position:relative;width:24px}.tool-indicator .icon{font-size:var(--text-xs);line-height:1;z-index:1}.tool-indicator.started:after{animation:ai-chat-gear-spin .8s linear infinite;border:2px solid transparent;border-radius:50%;border-top-color:#3b82f6;content:\"\";inset:-2px;position:absolute}.ai-chat-tool-message{align-items:center;background:hsla(0,0%,50%,.1);border:none;border-radius:var(--radius-pill);color:hsla(0,0%,50%,.8);display:inline-flex;font-size:12px;gap:5px;padding:4px 10px}.ai-chat-tool-message .ai-chat-tool-spinner{animation:ai-chat-tool-spin 1s linear infinite;flex-shrink:0;opacity:.6}.ai-chat-tool-message .ai-chat-tool-check{flex-shrink:0;opacity:.7}.ai-chat-tool-message .ai-chat-tool-error{color:#ef4444;flex-shrink:0;opacity:.7}.ai-chat-tool-message.error{background:rgba(239,68,68,.1);color:rgba(239,68,68,.9)}.tool-name{font-size:12px;font-weight:var(--font-weight-regular);line-height:1.2;white-space:nowrap}@keyframes ai-chat-tool-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@keyframes ai-chat-tool-check-appear{0%{opacity:0;transform:scale(.5)}to{opacity:.7;transform:scale(1)}}.ai-chat-sources{background:rgba(0,0,0,.02);border-radius:6px;font-size:var(--text-xs);margin-top:var(--space-sm);overflow:hidden}.ai-chat-sources-toggle{align-items:center;background:none;border:none;cursor:pointer;display:flex;gap:6px;padding:var(--space-sm) 10px;text-align:left;transition:background var(--duration-fast) ease;width:100%}.ai-chat-sources-toggle:hover{background:rgba(0,0,0,.03)}.ai-chat-sources-icon{color:var(--text-muted);font-size:10px;transition:transform var(--duration-fast) ease}.ai-chat-sources-title{color:var(--text-primary);flex:1;font-size:11px;font-weight:var(--font-weight-semibold);letter-spacing:.5px;text-transform:uppercase}.ai-chat-source-item{border-top:1px solid rgba(0,0,0,.05);color:var(--text-muted);display:flex;gap:var(--space-sm);padding:var(--space-sm) 10px}.ai-chat-source-item:last-child{border-bottom:none}.ai-chat-source-number{color:var(--btn-primary-bg);flex-shrink:0;font-weight:var(--font-weight-semibold)}.ai-chat-source-details{display:flex;flex:1;flex-direction:column;gap:var(--space-xs)}.ai-chat-source-score{color:var(--text-placeholder);font-size:11px}.ai-chat-source-content{color:var(--text-muted);font-size:11px;font-style:italic;line-height:var(--line-height-normal)}.ai-chat-source-metadata{display:flex;flex-wrap:wrap;gap:6px;margin-top:2px}.ai-chat-source-meta-item{background:rgba(0,0,0,.05);border-radius:3px;color:var(--text-muted);font-size:10px;padding:2px 6px}";
22039
22393
  styleInject(css_248z);
22040
22394
 
22041
22395
  // Icon components mapping
@@ -22043,17 +22397,76 @@ const iconComponents = {
22043
22397
  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" }) })),
22044
22398
  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" }) })),
22045
22399
  };
22046
- const ChatWidget = ({ widgetId, apiUrl = window.location.origin, position = 'bottom-right', primaryColor, onOpen, onClose, onMessage, onError, }) => {
22400
+ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, previewMode = false, previewConfig, position = 'bottom-right', primaryColor, size, headerTitle, welcomeTitle, welcomeMessage, placeholder, theme, suggestedQuestions, customStyles, onOpen, onClose, onMessage, onError, }) => {
22047
22401
  const [isOpen, setIsOpen] = useState(false);
22048
22402
  const [autoDetectedTheme, setAutoDetectedTheme] = useState('light');
22049
22403
  const widgetRef = useRef(null);
22050
22404
  const containerRef = useRef(null);
22051
- const { messages, isLoading, isTyping, error, config, sendMessage, submitFeedback, } = useChat({
22052
- widgetId,
22405
+ // Default config for preview mode
22406
+ const defaultPreviewConfig = {
22407
+ appearance: {
22408
+ primaryColor: primaryColor || '#0077FF',
22409
+ position: position || 'bottom-right',
22410
+ size: size || 'small',
22411
+ headerTitle: headerTitle || 'AI Assistant',
22412
+ welcomeTitle: welcomeTitle || 'Welcome!',
22413
+ welcomeMessage: welcomeMessage || 'Hi there! How can I help you today?',
22414
+ placeholder: placeholder || 'Ask me anything...',
22415
+ },
22416
+ settings: {
22417
+ autoOpen: false,
22418
+ persistConversation: true,
22419
+ showChatHistory: true,
22420
+ showTimestamps: true,
22421
+ showTypingIndicator: true,
22422
+ enableFileUpload: false,
22423
+ enableFeedback: true,
22424
+ showSources: false,
22425
+ sourceDisplayMode: 'none',
22426
+ },
22427
+ behavior: {
22428
+ agentic: false,
22429
+ },
22430
+ knowledgeBases: [],
22431
+ };
22432
+ // Merge preview config with defaults
22433
+ const mergedPreviewConfig = {
22434
+ ...defaultPreviewConfig,
22435
+ ...previewConfig,
22436
+ appearance: {
22437
+ ...defaultPreviewConfig.appearance,
22438
+ ...previewConfig?.appearance,
22439
+ },
22440
+ settings: {
22441
+ ...defaultPreviewConfig.settings,
22442
+ ...previewConfig?.settings,
22443
+ },
22444
+ behavior: {
22445
+ ...defaultPreviewConfig.behavior,
22446
+ ...previewConfig?.behavior,
22447
+ },
22448
+ };
22449
+ // Always call useChat hook (React rules), but use a dummy widgetId in preview mode
22450
+ // The hook will fail to load config, but we don't use its results in preview mode anyway
22451
+ const chatHook = useChat({
22452
+ widgetId: previewMode ? '__preview__' : (widgetId || '__preview__'),
22053
22453
  apiUrl,
22054
- onMessage,
22055
- onError,
22454
+ onMessage: previewMode ? undefined : onMessage,
22455
+ onError: previewMode ? undefined : onError,
22056
22456
  });
22457
+ // Extract values from hook or use preview defaults
22458
+ const messages = previewMode ? [] : chatHook.messages;
22459
+ const isLoading = previewMode ? false : chatHook.isLoading;
22460
+ const isTyping = previewMode ? false : chatHook.isTyping;
22461
+ const error = previewMode ? null : chatHook.error;
22462
+ const config = previewMode ? mergedPreviewConfig : chatHook.config;
22463
+ const sendMessage = previewMode ? (() => Promise.resolve()) : chatHook.sendMessage;
22464
+ const submitFeedback = previewMode ? (() => Promise.resolve()) : chatHook.submitFeedback;
22465
+ const conversations = previewMode ? [] : chatHook.conversations;
22466
+ const loadConversations = previewMode ? (() => { }) : chatHook.loadConversations;
22467
+ const switchConversation = previewMode ? (() => Promise.resolve()) : chatHook.switchConversation;
22468
+ const startNewConversation = previewMode ? (() => { }) : chatHook.startNewConversation;
22469
+ const conversationId = previewMode ? '' : chatHook.conversationId;
22057
22470
  // Auto-detect theme from background
22058
22471
  useEffect(() => {
22059
22472
  if (!containerRef.current)
@@ -22091,8 +22504,8 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, position = 'bot
22091
22504
  }, [config, autoDetectedTheme]);
22092
22505
  // Handle auto-open
22093
22506
  useEffect(() => {
22094
- if (config?.behavior.autoOpen) {
22095
- const delay = config.behavior.autoOpenDelay || 0;
22507
+ if (config?.settings.autoOpen) {
22508
+ const delay = config.settings.autoOpenDelay || 0;
22096
22509
  const timer = setTimeout(() => {
22097
22510
  setIsOpen(true);
22098
22511
  onOpen?.();
@@ -22101,26 +22514,6 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, position = 'bot
22101
22514
  }
22102
22515
  return undefined;
22103
22516
  }, [config, onOpen]);
22104
- // Handle close on outside click - always enabled for better UX
22105
- useEffect(() => {
22106
- if (!isOpen)
22107
- return;
22108
- const handleClickOutside = (event) => {
22109
- // Check if click is outside the widget container
22110
- if (widgetRef.current && !widgetRef.current.contains(event.target)) {
22111
- setIsOpen(false);
22112
- onClose?.();
22113
- }
22114
- };
22115
- // Small delay to prevent immediate close on open
22116
- const timer = setTimeout(() => {
22117
- document.addEventListener('mousedown', handleClickOutside);
22118
- }, 150);
22119
- return () => {
22120
- clearTimeout(timer);
22121
- document.removeEventListener('mousedown', handleClickOutside);
22122
- };
22123
- }, [isOpen, onClose]);
22124
22517
  // Handle close on Escape key
22125
22518
  useEffect(() => {
22126
22519
  if (!isOpen)
@@ -22134,31 +22527,33 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, position = 'bot
22134
22527
  document.addEventListener('keydown', handleEscapeKey);
22135
22528
  return () => document.removeEventListener('keydown', handleEscapeKey);
22136
22529
  }, [isOpen, onClose]);
22137
- // Determine theme - always auto-detect from background
22530
+ // Determine theme - use prop override if provided, otherwise auto-detect
22138
22531
  const appearanceConfig = config?.appearance;
22139
- const effectiveTheme = autoDetectedTheme;
22140
- // Determine position (config takes priority over prop)
22141
- const effectivePosition = config?.appearance.position || position;
22142
- // Get accent color from config or prop
22143
- const accentColor = primaryColor || appearanceConfig?.primaryColor || '#0077FF';
22532
+ const effectiveTheme = theme ?? autoDetectedTheme;
22533
+ // Determine position (prop override takes priority for live preview)
22534
+ const effectivePosition = position || config?.appearance.position || 'bottom-right';
22535
+ // Get accent color from prop or config (empty string means no accent color / vanilla mode)
22536
+ const accentColor = primaryColor ?? appearanceConfig?.primaryColor ?? '';
22537
+ // Apply prop overrides for live preview (props take priority over config)
22538
+ size || appearanceConfig?.size || 'small';
22539
+ const effectiveHeaderTitle = headerTitle ?? appearanceConfig?.headerTitle ?? '';
22540
+ const effectiveWelcomeTitle = welcomeTitle ?? appearanceConfig?.welcomeTitle ?? '';
22541
+ const effectiveWelcomeMessage = welcomeMessage ?? appearanceConfig?.welcomeMessage ?? '';
22542
+ const effectivePlaceholder = placeholder ?? appearanceConfig?.placeholder ?? '';
22144
22543
  // Generate styles using simplified theme system
22145
22544
  const simpleAppearance = {
22146
- accentColor,
22147
- size: appearanceConfig?.size || 'small',
22148
- welcomeMessage: appearanceConfig?.welcomeMessage || '',
22149
- placeholder: appearanceConfig?.placeholder || '',
22150
- headerTitle: appearanceConfig?.headerTitle || '',
22151
- };
22545
+ accentColor};
22152
22546
  // Generate theme styles from accent color
22153
22547
  const generatedStyles = generateThemeStyles(simpleAppearance, effectiveTheme);
22154
22548
  // Also apply legacy styles for backward compatibility
22155
22549
  const legacyStyles = appearanceConfig
22156
22550
  ? applyAppearanceStyles(appearanceConfig)
22157
22551
  : {};
22158
- // Merge styles (generated takes priority for new simplified system)
22159
- const customStyles = {
22552
+ // Merge styles (generated takes priority for new simplified system, then custom overrides)
22553
+ const mergedStyles = {
22160
22554
  ...legacyStyles,
22161
22555
  ...generatedStyles,
22556
+ ...customStyles,
22162
22557
  };
22163
22558
  // Debug logging for theme and styles
22164
22559
  useEffect(() => {
@@ -22183,14 +22578,19 @@ const ChatWidget = ({ widgetId, apiUrl = window.location.origin, position = 'bot
22183
22578
  await submitFeedback(messageId, feedback);
22184
22579
  };
22185
22580
  // Don't render until config is loaded to avoid flash of unstyled content
22186
- if (!config) {
22581
+ // In preview mode, config is always available
22582
+ if (!config && !previewMode) {
22187
22583
  console.log('[ChatWidget] Not rendering - config not loaded yet');
22188
22584
  return null;
22189
22585
  }
22190
22586
  console.log('[ChatWidget] Rendering widget', { isOpen, hasConfig: !!config });
22191
22587
  // Get button icon based on state
22192
22588
  const IconComponent = isOpen ? iconComponents.FiChevronDown : iconComponents.FiMessageCircle;
22193
- return (jsx("div", { ref: containerRef, className: `ai-chat-widget ${effectiveTheme}`, style: customStyles, children: jsxs("div", { ref: widgetRef, className: `ai-chat-widget-container ${effectivePosition}`, children: [isOpen && (jsx(ChatWindow, { messages: messages, isLoading: isLoading, isTyping: isTyping, error: error, config: config, onSendMessage: sendMessage, onClose: handleToggle, onFeedback: handleFeedback })), 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, {}) }) })] }) }));
22589
+ return (jsx("div", { ref: containerRef, className: `ai-chat-widget ${effectiveTheme}`, style: mergedStyles, children: jsxs("div", { ref: widgetRef, className: `ai-chat-widget-container ${effectivePosition}`, children: [isOpen && (jsx(ChatWindow, { messages: messages, isLoading: isLoading, isTyping: isTyping, error: error, config: config, onSendMessage: sendMessage, onClose: handleToggle, onFeedback: handleFeedback,
22590
+ // Chat history props (only active when persistConversation is true)
22591
+ conversations: conversations, onLoadConversations: loadConversations, onSwitchConversation: switchConversation, onStartNewConversation: startNewConversation, currentConversationId: conversationId,
22592
+ // Override props for live preview
22593
+ 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, {}) }) })] }) }));
22194
22594
  };
22195
22595
 
22196
22596
  export { ApiError, ChatWidget, useChat };