@fusioni/client-sdk 1.1.10 → 1.1.12

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
@@ -4955,11 +4955,12 @@ const DocumentVideoGrid = ({ videos, attachedVideosLabel, }) => {
4955
4955
  // Module-level Set to track which message IDs have been animated
4956
4956
  // This persists across component unmounts/remounts (e.g., when chat closes and reopens)
4957
4957
  const animatedMessageIds = new Set();
4958
+ const TYPING_PREVIEW_CHARACTER_LIMIT = 200;
4958
4959
  const Message = ({ message, showThoughts = false, fontSize = 'text-sm', onDelete, onConfirmation, enableButtons = true, apiBaseUrl, apiKey, agencyId, currentLanguage = 'en', onOpenGallery }) => {
4959
4960
  const { t } = useTranslation(currentLanguage);
4960
4961
  const [displayedContent, setDisplayedContent] = useState('');
4961
4962
  const typingIntervalRef = useRef(null);
4962
- const typingSpeed = 15; // milliseconds per character
4963
+ const typingSpeed = 5; // milliseconds per character
4963
4964
  const enhanceMessageContent = (content) => {
4964
4965
  if (!content)
4965
4966
  return content;
@@ -5109,6 +5110,7 @@ const Message = ({ message, showThoughts = false, fontSize = 'text-sm', onDelete
5109
5110
  // Now animate through segments
5110
5111
  let segmentIndex = 0;
5111
5112
  let textIndex = 0;
5113
+ let typedCharacterCount = 0;
5112
5114
  const nextTextStep = (text, index) => {
5113
5115
  if (index >= text.length)
5114
5116
  return { nextIndex: index, slice: '' };
@@ -5145,6 +5147,11 @@ const Message = ({ message, showThoughts = false, fontSize = 'text-sm', onDelete
5145
5147
  typingIntervalRef.current = setTimeout(typeNextChar, 0);
5146
5148
  }
5147
5149
  else {
5150
+ if (typedCharacterCount >= TYPING_PREVIEW_CHARACTER_LIMIT) {
5151
+ setDisplayedContent(fullContent);
5152
+ animatedMessageIds.add(messageId);
5153
+ return;
5154
+ }
5148
5155
  // Text segments are typed character by character
5149
5156
  if (textIndex < segment.content.length) {
5150
5157
  const step = nextTextStep(segment.content, textIndex);
@@ -5162,6 +5169,14 @@ const Message = ({ message, showThoughts = false, fontSize = 'text-sm', onDelete
5162
5169
  content += step.slice;
5163
5170
  setDisplayedContent(content);
5164
5171
  textIndex = step.nextIndex;
5172
+ typedCharacterCount++;
5173
+ if (typedCharacterCount >= TYPING_PREVIEW_CHARACTER_LIMIT) {
5174
+ typingIntervalRef.current = setTimeout(() => {
5175
+ setDisplayedContent(fullContent);
5176
+ animatedMessageIds.add(messageId);
5177
+ }, typingSpeed);
5178
+ return;
5179
+ }
5165
5180
  typingIntervalRef.current = setTimeout(typeNextChar, typingSpeed);
5166
5181
  }
5167
5182
  else {
@@ -5701,11 +5716,9 @@ const FloatingButton = ({ isOpen, onClick, position = 'bottom-right', primaryCol
5701
5716
  const [showAttentionSequence, setShowAttentionSequence] = useState(false);
5702
5717
  const [isAttentionActive, setIsAttentionActive] = useState(false);
5703
5718
  useEffect(() => {
5704
- // Sophisticated entrance animation
5705
5719
  const entranceTimer = setTimeout(() => {
5706
5720
  setIsVisible(true);
5707
5721
  }, 200);
5708
- // Periodic attention sequence (every 15 seconds)
5709
5722
  const attentionInterval = setInterval(() => {
5710
5723
  if (!isHovered && !isOpen) {
5711
5724
  setShowAttentionSequence(true);
@@ -6089,6 +6102,33 @@ function useIsMobileLayout() {
6089
6102
  return isMobile;
6090
6103
  }
6091
6104
 
6105
+ const getStoredConversationIdKey = (agencyId) => `fusioni:selectedConversationId:${agencyId}`;
6106
+ const readStoredConversationId = (agencyId) => {
6107
+ if (typeof window === 'undefined')
6108
+ return null;
6109
+ try {
6110
+ return window.sessionStorage.getItem(getStoredConversationIdKey(agencyId));
6111
+ }
6112
+ catch {
6113
+ return null;
6114
+ }
6115
+ };
6116
+ const writeStoredConversationId = (agencyId, conversationId) => {
6117
+ if (typeof window === 'undefined')
6118
+ return;
6119
+ try {
6120
+ const key = getStoredConversationIdKey(agencyId);
6121
+ if (conversationId) {
6122
+ window.sessionStorage.setItem(key, conversationId);
6123
+ }
6124
+ else {
6125
+ window.sessionStorage.removeItem(key);
6126
+ }
6127
+ }
6128
+ catch {
6129
+ // Storage can be unavailable in restricted browser contexts.
6130
+ }
6131
+ };
6092
6132
  const ChatWidget = forwardRef(function ChatWidget({ config, onMessageSent, onMessageReceived, onConversationCreated, onConversationDeleted, onError }, ref) {
6093
6133
  const [languageOverride, setLanguageOverride] = useState(null);
6094
6134
  const [isOpen, setIsOpen] = useState(false);
@@ -6103,12 +6143,15 @@ const ChatWidget = forwardRef(function ChatWidget({ config, onMessageSent, onMes
6103
6143
  const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
6104
6144
  const [messageToDelete, setMessageToDelete] = useState(null);
6105
6145
  const [isDeleteMessageDialogOpen, setIsDeleteMessageDialogOpen] = useState(false);
6146
+ const [hasLoadedConversations, setHasLoadedConversations] = useState(false);
6106
6147
  // Refs
6107
6148
  const floatingButtonRef = useRef(null);
6149
+ const restoredConversationIdRef = useRef(null);
6108
6150
  const translationDefault = languageOverride ?? mergedConfig?.language ?? config.language ?? 'en';
6109
6151
  // Translation and language management - use merged config or fallback to user config
6110
6152
  const { t, currentLanguage, changeLanguage } = useTranslation(translationDefault);
6111
- const { conversations, currentConversation, messages, streamMessages, setCurrentConversation, setStreamMessages, addMessage, updateMessage, removeMessage, removeOptimisticUserMessages, truncateMessagesAt, addConversation, updateConversation, removeConversation, loadConversations, loadMessages, clearMessages } = useChatState(mergedConfig?.agencyId || config.agencyId);
6153
+ const activeAgencyId = mergedConfig?.agencyId || config.agencyId;
6154
+ const { conversations, currentConversation, messages, streamMessages, setCurrentConversation, setStreamMessages, addMessage, updateMessage, removeMessage, removeOptimisticUserMessages, truncateMessagesAt, addConversation, updateConversation, removeConversation, loadConversations, loadMessages, clearMessages } = useChatState(activeAgencyId);
6112
6155
  const { theme, toggleTheme, setTheme } = useTheme(mergedConfig?.theme || config.theme);
6113
6156
  const isMobileLayout = useIsMobileLayout();
6114
6157
  const handleLanguageChange = useCallback((language) => {
@@ -6175,12 +6218,22 @@ const ChatWidget = forwardRef(function ChatWidget({ config, onMessageSent, onMes
6175
6218
  }, [config, onError, t]);
6176
6219
  // Load initial conversations
6177
6220
  useEffect(() => {
6178
- if (mergedConfig?.agencyId) {
6179
- loadConversations().then();
6221
+ if (activeAgencyId) {
6222
+ let isCancelled = false;
6223
+ restoredConversationIdRef.current = null;
6224
+ setHasLoadedConversations(false);
6225
+ loadConversations().finally(() => {
6226
+ if (!isCancelled) {
6227
+ setHasLoadedConversations(true);
6228
+ }
6229
+ });
6230
+ return () => {
6231
+ isCancelled = true;
6232
+ };
6180
6233
  }
6181
- }, [mergedConfig?.agencyId, loadConversations]);
6234
+ }, [activeAgencyId, loadConversations]);
6182
6235
  // SSE connection for real-time updates - only when a chat panel is open
6183
- useSSE(mergedConfig?.agencyId || config.agencyId, (data) => {
6236
+ useSSE(activeAgencyId, (data) => {
6184
6237
  // Same as fusioni-web chat.component: latest SSE line replaces stream (single loading row updates)
6185
6238
  if (data?.data != null && String(data.data).trim() !== '') {
6186
6239
  setStreamMessages([String(data.data)]);
@@ -6204,6 +6257,7 @@ const ChatWidget = forwardRef(function ChatWidget({ config, onMessageSent, onMes
6204
6257
  const handleCreateConversation = useCallback(() => {
6205
6258
  if (!mergedConfig)
6206
6259
  return;
6260
+ writeStoredConversationId(activeAgencyId, null);
6207
6261
  // Create a new conversation with null ID - will be created on server when first message is sent
6208
6262
  const newConversation = {
6209
6263
  id: null,
@@ -6219,7 +6273,7 @@ const ChatWidget = forwardRef(function ChatWidget({ config, onMessageSent, onMes
6219
6273
  // Auto-hide conversation list when a new conversation is created
6220
6274
  setIsConversationListOpen(false);
6221
6275
  onConversationCreated?.(newConversation);
6222
- }, [mergedConfig, addConversation, setCurrentConversation, clearMessages, onConversationCreated, t]);
6276
+ }, [activeAgencyId, mergedConfig, addConversation, setCurrentConversation, clearMessages, onConversationCreated, t]);
6223
6277
  const handleSelectConversation = useCallback(async (conversation) => {
6224
6278
  try {
6225
6279
  setIsLoading(true);
@@ -6227,6 +6281,9 @@ const ChatWidget = forwardRef(function ChatWidget({ config, onMessageSent, onMes
6227
6281
  clearMessages();
6228
6282
  setStreamMessages([]);
6229
6283
  setCurrentConversation(conversation);
6284
+ if (conversation.id) {
6285
+ writeStoredConversationId(activeAgencyId, conversation.id);
6286
+ }
6230
6287
  // Auto-hide conversation list when a conversation is selected
6231
6288
  setIsConversationListOpen(false);
6232
6289
  if (conversation.id) {
@@ -6241,7 +6298,51 @@ const ChatWidget = forwardRef(function ChatWidget({ config, onMessageSent, onMes
6241
6298
  finally {
6242
6299
  setIsLoading(false);
6243
6300
  }
6244
- }, [setCurrentConversation, loadMessages, clearMessages, onError]);
6301
+ }, [activeAgencyId, setCurrentConversation, loadMessages, clearMessages, onError, t]);
6302
+ useEffect(() => {
6303
+ if (!hasLoadedConversations || !activeAgencyId || currentConversation) {
6304
+ return;
6305
+ }
6306
+ const storedConversationId = readStoredConversationId(activeAgencyId);
6307
+ if (!storedConversationId || restoredConversationIdRef.current === storedConversationId) {
6308
+ return;
6309
+ }
6310
+ restoredConversationIdRef.current = storedConversationId;
6311
+ let isCancelled = false;
6312
+ const restoreConversation = async () => {
6313
+ let storedConversation = conversations.find((conversation) => conversation.id === storedConversationId);
6314
+ if (!storedConversation) {
6315
+ try {
6316
+ storedConversation = await getConversationService().getConversation(storedConversationId);
6317
+ }
6318
+ catch {
6319
+ storedConversation = undefined;
6320
+ }
6321
+ }
6322
+ if (isCancelled) {
6323
+ return;
6324
+ }
6325
+ if (!storedConversation) {
6326
+ writeStoredConversationId(activeAgencyId, null);
6327
+ return;
6328
+ }
6329
+ if (!conversations.some((conversation) => conversation.id === storedConversation?.id)) {
6330
+ addConversation(storedConversation);
6331
+ }
6332
+ handleSelectConversation(storedConversation);
6333
+ };
6334
+ restoreConversation();
6335
+ return () => {
6336
+ isCancelled = true;
6337
+ };
6338
+ }, [
6339
+ addConversation,
6340
+ activeAgencyId,
6341
+ conversations,
6342
+ currentConversation,
6343
+ handleSelectConversation,
6344
+ hasLoadedConversations
6345
+ ]);
6245
6346
  const handleDeleteConversation = useCallback((conversationId) => {
6246
6347
  setConversationToDelete(conversationId);
6247
6348
  setIsDeleteDialogOpen(true);
@@ -6254,6 +6355,9 @@ const ChatWidget = forwardRef(function ChatWidget({ config, onMessageSent, onMes
6254
6355
  const conversationService = getConversationService();
6255
6356
  await conversationService.deleteConversation(conversationToDelete);
6256
6357
  removeConversation(conversationToDelete);
6358
+ if (readStoredConversationId(activeAgencyId) === conversationToDelete) {
6359
+ writeStoredConversationId(activeAgencyId, null);
6360
+ }
6257
6361
  if (currentConversation?.id === conversationToDelete) {
6258
6362
  setCurrentConversation(null);
6259
6363
  clearMessages();
@@ -6275,7 +6379,7 @@ const ChatWidget = forwardRef(function ChatWidget({ config, onMessageSent, onMes
6275
6379
  finally {
6276
6380
  setIsLoading(false);
6277
6381
  }
6278
- }, [conversationToDelete, removeConversation, currentConversation, setCurrentConversation, clearMessages, setStreamMessages, onConversationDeleted, onError, t]);
6382
+ }, [activeAgencyId, conversationToDelete, removeConversation, currentConversation, setCurrentConversation, clearMessages, setStreamMessages, onConversationDeleted, onError, t]);
6279
6383
  const cancelDeleteConversation = useCallback(() => {
6280
6384
  setIsDeleteDialogOpen(false);
6281
6385
  setConversationToDelete(null);
@@ -6283,21 +6387,7 @@ const ChatWidget = forwardRef(function ChatWidget({ config, onMessageSent, onMes
6283
6387
  const handleSendMessage = useCallback(async (content, image, audio, messageId) => {
6284
6388
  if (!mergedConfig)
6285
6389
  return;
6286
- // Debug logging for conversation state
6287
- console.log('🔍 ChatWidget.handleSendMessage - Starting message send:', {
6288
- currentConversation: currentConversation,
6289
- conversationId: currentConversation?.id,
6290
- content: content?.substring(0, 100) + (content?.length > 100 ? '...' : ''),
6291
- hasImage: !!image,
6292
- hasAudio: !!audio,
6293
- agencyId: mergedConfig.agencyId
6294
- });
6295
6390
  if (!currentConversation || (!content.trim() && !audio)) {
6296
- console.warn('⚠️ ChatWidget.handleSendMessage - Early return:', {
6297
- hasCurrentConversation: !!currentConversation,
6298
- hasContent: !!content?.trim(),
6299
- hasAudio: !!audio
6300
- });
6301
6391
  return;
6302
6392
  }
6303
6393
  try {
@@ -6305,18 +6395,11 @@ const ChatWidget = forwardRef(function ChatWidget({ config, onMessageSent, onMes
6305
6395
  let conversationId = currentConversation.id;
6306
6396
  // If this is a new conversation (null ID), create it on the server first
6307
6397
  if (!conversationId) {
6308
- console.log('🔍 ChatWidget.handleSendMessage - Creating server conversation for new conversation:', {
6309
- agencyId: mergedConfig.agencyId
6310
- });
6311
6398
  const conversationService = getConversationService();
6312
6399
  conversationId = await conversationService.createConversation({
6313
6400
  title: t('chat.conversations.newConversation'),
6314
6401
  agency_id: mergedConfig.agencyId,
6315
6402
  });
6316
- console.log('🔍 ChatWidget.handleSendMessage - Server conversation created:', {
6317
- conversationId: conversationId,
6318
- conversationIdType: typeof conversationId
6319
- });
6320
6403
  // Update the conversation with the real server ID
6321
6404
  const updatedConversation = {
6322
6405
  ...currentConversation,
@@ -6325,10 +6408,7 @@ const ChatWidget = forwardRef(function ChatWidget({ config, onMessageSent, onMes
6325
6408
  // Update the conversation in the list and set as current
6326
6409
  updateConversation(updatedConversation.id, updatedConversation);
6327
6410
  setCurrentConversation(updatedConversation);
6328
- console.log('🔍 ChatWidget.handleSendMessage - Conversation ID updated:', {
6329
- conversationId: conversationId,
6330
- updatedConversation: updatedConversation
6331
- });
6411
+ writeStoredConversationId(activeAgencyId, conversationId);
6332
6412
  }
6333
6413
  // Clear stream messages for new conversation
6334
6414
  setStreamMessages([]);
@@ -6382,7 +6462,6 @@ const ChatWidget = forwardRef(function ChatWidget({ config, onMessageSent, onMes
6382
6462
  if (userMessage) {
6383
6463
  onMessageSent?.(userMessage);
6384
6464
  }
6385
- // Debug logging before pipeline execution
6386
6465
  const pipelineRequest = {
6387
6466
  conversation_id: conversationId,
6388
6467
  agency_id: mergedConfig.agencyId,
@@ -6391,14 +6470,6 @@ const ChatWidget = forwardRef(function ChatWidget({ config, onMessageSent, onMes
6391
6470
  audio,
6392
6471
  message_id: messageId
6393
6472
  };
6394
- console.log('🔍 ChatWidget.handleSendMessage - About to call executePipeline:', {
6395
- conversationId: conversationId,
6396
- conversationIdType: typeof conversationId,
6397
- conversationIdLength: conversationId?.length,
6398
- isNewConversation: !conversationId,
6399
- pipelineRequest: pipelineRequest,
6400
- currentConversation: currentConversation
6401
- });
6402
6473
  // Execute pipeline - this calls /pipeline/${agency_id}/exec
6403
6474
  const pipelineService = getPipelineService();
6404
6475
  const response = await pipelineService.executePipeline(pipelineRequest);
@@ -6450,6 +6521,7 @@ const ChatWidget = forwardRef(function ChatWidget({ config, onMessageSent, onMes
6450
6521
  }
6451
6522
  }, [
6452
6523
  currentConversation,
6524
+ activeAgencyId,
6453
6525
  mergedConfig,
6454
6526
  messages,
6455
6527
  addMessage,
@@ -6609,10 +6681,7 @@ const ChatWidget = forwardRef(function ChatWidget({ config, onMessageSent, onMes
6609
6681
  if (error) {
6610
6682
  return (jsxs("div", { className: "fusioni-chat-error", children: [jsxs("p", { children: [t('common.error'), ": ", error] }), jsx("button", { onClick: () => setError(null), children: t('chat.errors.retry') })] }));
6611
6683
  }
6612
- // Debug: Log the primary color and button variant being applied
6613
- console.log('Primary color being applied:', mergedConfig.primaryColor || '#6366f1');
6614
- console.log('Button variant being applied:', mergedConfig.buttonVariant || 'glass');
6615
- return (jsxs("div", { className: `fusioni-chat-widget ${theme}`, style: { '--primary-color': mergedConfig.primaryColor || '#6366f1' }, children: [jsx(FloatingButton, { isOpen: isOpen, onClick: handleToggleChat, position: mergedConfig.position || 'bottom-right', primaryColor: mergedConfig.primaryColor, buttonRef: floatingButtonRef, variant: mergedConfig.buttonVariant || 'glass', shouldDisplay: !hasConfigError && (!isMobileLayout || !isOpen) }), isOpen && (jsx(ChatPanel, { isOpen: isOpen, onClose: () => setIsOpen(false), position: mergedConfig.position || 'bottom-right', isFullscreen: isFullscreen, floatingButtonRef: floatingButtonRef, children: jsxs("div", { className: "fusioni-chat-container", children: [mergedConfig.showConversationList !== false && (jsxs(Fragment, { children: [jsx("div", { className: `fusioni-conversation-backdrop ${isConversationListOpen ? 'open' : ''}`, onClick: () => setIsConversationListOpen(false) }), jsx(ConversationList, { conversations: conversations, selectedConversationId: currentConversation?.id || undefined, onSelectConversation: handleSelectConversation, onDeleteConversation: handleDeleteConversation, onCreateConversation: handleCreateConversation, isOpen: isConversationListOpen, currentLanguage: currentLanguage })] })), jsxs("div", { className: "fusioni-chat-main", children: [jsxs("div", { className: "fusioni-chat-main-header", children: [mergedConfig.showConversationList !== false ? (jsxs("button", { type: "button", onClick: handleToggleConversationList, className: `fusioni-conversation-toggle ${isConversationListOpen ? 'open' : ''}`, children: [jsx("svg", { className: "fusioni-conversation-toggle-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: jsx("path", { d: "M3 12h18M3 6h18M3 18h18" }) }), t('chat.conversations.title')] })) : (jsx("span", { className: "fusioni-chat-main-header-title", children: t('chat.title') })), jsxs("div", { className: "fusioni-header-actions", children: [isMobileLayout && (jsx("button", { type: "button", onClick: () => setIsOpen(false), className: "fusioni-btn fusioni-btn-icon fusioni-chat-toolbar-close-mobile", title: t('common.close'), "aria-label": t('common.close'), children: jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": "true", children: jsx("path", { d: "M18 6L6 18M6 6L18 18", strokeLinecap: "round", strokeLinejoin: "round" }) }) })), mergedConfig.showThemeToggle !== false && (jsx("button", { type: "button", onClick: toggleTheme, className: "fusioni-btn fusioni-btn-icon", title: theme === 'dark' ? t('chat.theme.light') : t('chat.theme.dark'), children: theme === 'dark' ? (jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [jsx("circle", { cx: "12", cy: "12", r: "5" }), jsx("line", { x1: "12", y1: "1", x2: "12", y2: "3" }), jsx("line", { x1: "12", y1: "21", x2: "12", y2: "23" }), jsx("line", { x1: "4.22", y1: "4.22", x2: "5.64", y2: "5.64" }), jsx("line", { x1: "18.36", y1: "18.36", x2: "19.78", y2: "19.78" }), jsx("line", { x1: "1", y1: "12", x2: "3", y2: "12" }), jsx("line", { x1: "21", y1: "12", x2: "23", y2: "12" }), jsx("line", { x1: "4.22", y1: "19.78", x2: "5.64", y2: "18.36" }), jsx("line", { x1: "18.36", y1: "5.64", x2: "19.78", y2: "4.22" })] })) : (jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: jsx("path", { d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" }) })) })), mergedConfig.showFullscreenToggle !== false && (jsx("button", { type: "button", onClick: handleToggleFullscreen, className: "fusioni-btn fusioni-btn-icon", title: isFullscreen ? t('chat.fullscreen.exit') : t('chat.fullscreen.enter'), children: isFullscreen ? (jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: jsx("path", { d: "M8 3V5M8 3H5M8 3L3 8M16 3V5M16 3H19M16 3L21 8M8 21V19M8 21H5M8 21L3 16M16 21V19M16 21H19M16 21L21 16" }) })) : (jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: jsx("path", { d: "M8 3H5C4.46957 3 3.96086 3.21071 3.58579 3.58579C3.21071 3.96086 3 4.46957 3 5V8M21 8V5C21 4.46957 20.7893 3.96086 20.4142 3.58579C20.0391 3.21071 19.5304 3 19 3H16M16 21H19C19.5304 21 20.0391 20.7893 20.4142 20.4142C20.7893 20.0391 21 19.5304 21 19V16M3 16V19C3 19.5304 3.21071 20.0391 3.58579 20.4142C3.96086 20.7893 4.46957 21 5 21H8" }) })) })), mergedConfig.showLanguageSwitcher !== false && (jsx(LanguageSwitcher, { currentLanguage: currentLanguage, onLanguageChange: handleLanguageChange }))] })] }), currentConversation ? (jsxs(Fragment, { children: [jsx(MessageList, { messages: messages, streamMessages: streamMessages, showThoughts: false, onDeleteMessage: handleDeleteMessage, onEditMessage: handleEditMessage, onConfirmation: handleConfirmation, enableButtons: !isLoading, apiBaseUrl: mergedConfig.apiBaseUrl, apiKey: mergedConfig.accessToken, agencyId: mergedConfig.agencyId, currentLanguage: currentLanguage, theme: theme }), jsx(ChatInput, { onSendMessage: handleSendMessage, onFileUpload: handleFileUpload, disabled: isLoading, placeholder: t('chat.input.placeholder'), enableAudioRecording: mergedConfig.enableAudioRecording !== false, enableFileUpload: mergedConfig.enableFileUpload !== false, maxFileSize: mergedConfig.maxFileSize || 10, allowedFileTypes: mergedConfig.allowedFileTypes || ['image/*'], currentLanguage: currentLanguage })] })) : (jsx("div", { className: "fusioni-chat-welcome", children: jsxs("div", { className: "fusioni-chat-welcome-content", children: [jsx("h3", { children: t('chat.welcome.title') }), jsx("p", { children: t('chat.welcome.description') }), jsx("button", { onClick: handleCreateConversation, disabled: isLoading, className: "fusioni-btn fusioni-btn-primary", children: isLoading ? t('chat.welcome.creating') : t('chat.welcome.startButton') })] }) }))] })] }) })), jsx(ConfirmationDialog, { isOpen: isDeleteDialogOpen, title: t('chat.conversations.deleteConfirm.title'), message: t('chat.conversations.deleteConfirm.message'), confirmText: t('chat.conversations.deleteConfirm.confirm'), cancelText: t('chat.conversations.deleteConfirm.cancel'), onConfirm: confirmDeleteConversation, onCancel: cancelDeleteConversation, currentLanguage: currentLanguage, variant: "danger" }), jsx(ConfirmationDialog, { isOpen: isDeleteMessageDialogOpen, title: t('chat.messages.deleteConfirm.title'), message: t('chat.messages.deleteConfirm.message'), confirmText: t('chat.messages.deleteConfirm.confirm'), cancelText: t('chat.messages.deleteConfirm.cancel'), onConfirm: confirmDeleteMessage, onCancel: cancelDeleteMessage, currentLanguage: currentLanguage, variant: "danger" })] }));
6684
+ return (jsxs("div", { className: `fusioni-chat-widget ${theme}`, style: { '--primary-color': mergedConfig.primaryColor || '#6366f1' }, children: [jsx(FloatingButton, { isOpen: isOpen, onClick: handleToggleChat, position: mergedConfig.position || 'bottom-right', primaryColor: mergedConfig.primaryColor, buttonRef: floatingButtonRef, variant: mergedConfig.buttonVariant || 'glass', shouldDisplay: !hasConfigError && (!isMobileLayout || !isOpen) }), isOpen && (jsx(ChatPanel, { isOpen: isOpen, onClose: () => setIsOpen(false), position: mergedConfig.position || 'bottom-right', isFullscreen: isFullscreen, floatingButtonRef: floatingButtonRef, children: jsxs("div", { className: "fusioni-chat-container", children: [mergedConfig.showConversationList !== false && (jsxs(Fragment, { children: [jsx("div", { className: `fusioni-conversation-backdrop ${isConversationListOpen ? 'open' : ''}`, onClick: () => setIsConversationListOpen(false) }), jsx(ConversationList, { conversations: conversations, selectedConversationId: currentConversation?.id || undefined, onSelectConversation: handleSelectConversation, onDeleteConversation: handleDeleteConversation, onCreateConversation: handleCreateConversation, isOpen: isConversationListOpen, currentLanguage: currentLanguage })] })), jsxs("div", { className: "fusioni-chat-main", children: [jsxs("div", { className: "fusioni-chat-main-header", children: [mergedConfig.showConversationList !== false ? (jsxs("button", { type: "button", onClick: handleToggleConversationList, className: `fusioni-conversation-toggle ${isConversationListOpen ? 'open' : ''}`, children: [jsx("svg", { className: "fusioni-conversation-toggle-icon", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: jsx("path", { d: "M3 12h18M3 6h18M3 18h18" }) }), t('chat.conversations.title')] })) : (jsx("span", { className: "fusioni-chat-main-header-title", children: t('chat.title') })), jsxs("div", { className: "fusioni-header-actions", children: [mergedConfig.showThemeToggle !== false && (jsx("button", { type: "button", onClick: toggleTheme, className: "fusioni-btn fusioni-btn-icon", title: theme === 'dark' ? t('chat.theme.light') : t('chat.theme.dark'), children: theme === 'dark' ? (jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [jsx("circle", { cx: "12", cy: "12", r: "5" }), jsx("line", { x1: "12", y1: "1", x2: "12", y2: "3" }), jsx("line", { x1: "12", y1: "21", x2: "12", y2: "23" }), jsx("line", { x1: "4.22", y1: "4.22", x2: "5.64", y2: "5.64" }), jsx("line", { x1: "18.36", y1: "18.36", x2: "19.78", y2: "19.78" }), jsx("line", { x1: "1", y1: "12", x2: "3", y2: "12" }), jsx("line", { x1: "21", y1: "12", x2: "23", y2: "12" }), jsx("line", { x1: "4.22", y1: "19.78", x2: "5.64", y2: "18.36" }), jsx("line", { x1: "18.36", y1: "5.64", x2: "19.78", y2: "4.22" })] })) : (jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: jsx("path", { d: "M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" }) })) })), mergedConfig.showFullscreenToggle !== false && (jsx("button", { type: "button", onClick: handleToggleFullscreen, className: "fusioni-btn fusioni-btn-icon", title: isFullscreen ? t('chat.fullscreen.exit') : t('chat.fullscreen.enter'), children: isFullscreen ? (jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: jsx("path", { d: "M8 3V5M8 3H5M8 3L3 8M16 3V5M16 3H19M16 3L21 8M8 21V19M8 21H5M8 21L3 16M16 21V19M16 21H19M16 21L21 16" }) })) : (jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: jsx("path", { d: "M8 3H5C4.46957 3 3.96086 3.21071 3.58579 3.58579C3.21071 3.96086 3 4.46957 3 5V8M21 8V5C21 4.46957 20.7893 3.96086 20.4142 3.58579C20.0391 3.21071 19.5304 3 19 3H16M16 21H19C19.5304 21 20.0391 20.7893 20.4142 20.4142C20.7893 20.0391 21 19.5304 21 19V16M3 16V19C3 19.5304 3.21071 20.0391 3.58579 20.4142C3.96086 20.7893 4.46957 21 5 21H8" }) })) })), mergedConfig.showLanguageSwitcher !== false && (jsx(LanguageSwitcher, { currentLanguage: currentLanguage, onLanguageChange: handleLanguageChange })), jsx("button", { type: "button", onClick: () => setIsOpen(false), className: "fusioni-btn fusioni-btn-icon fusioni-chat-toolbar-close-mobile", title: t('common.close'), "aria-label": t('common.close'), children: jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": "true", children: jsx("path", { d: "M18 6L6 18M6 6L18 18", strokeLinecap: "round", strokeLinejoin: "round" }) }) })] })] }), currentConversation ? (jsxs(Fragment, { children: [jsx(MessageList, { messages: messages, streamMessages: streamMessages, showThoughts: false, onDeleteMessage: handleDeleteMessage, onEditMessage: handleEditMessage, onConfirmation: handleConfirmation, enableButtons: !isLoading, apiBaseUrl: mergedConfig.apiBaseUrl, apiKey: mergedConfig.accessToken, agencyId: mergedConfig.agencyId, currentLanguage: currentLanguage, theme: theme }), jsx(ChatInput, { onSendMessage: handleSendMessage, onFileUpload: handleFileUpload, disabled: isLoading, placeholder: t('chat.input.placeholder'), enableAudioRecording: mergedConfig.enableAudioRecording !== false, enableFileUpload: mergedConfig.enableFileUpload !== false, maxFileSize: mergedConfig.maxFileSize || 10, allowedFileTypes: mergedConfig.allowedFileTypes || ['image/*'], currentLanguage: currentLanguage })] })) : (jsx("div", { className: "fusioni-chat-welcome", children: jsxs("div", { className: "fusioni-chat-welcome-content", children: [jsx("h3", { children: t('chat.welcome.title') }), jsx("p", { children: t('chat.welcome.description') }), jsx("button", { onClick: handleCreateConversation, disabled: isLoading, className: "fusioni-btn fusioni-btn-primary", children: isLoading ? t('chat.welcome.creating') : t('chat.welcome.startButton') })] }) }))] })] }) })), jsx(ConfirmationDialog, { isOpen: isDeleteDialogOpen, title: t('chat.conversations.deleteConfirm.title'), message: t('chat.conversations.deleteConfirm.message'), confirmText: t('chat.conversations.deleteConfirm.confirm'), cancelText: t('chat.conversations.deleteConfirm.cancel'), onConfirm: confirmDeleteConversation, onCancel: cancelDeleteConversation, currentLanguage: currentLanguage, variant: "danger" }), jsx(ConfirmationDialog, { isOpen: isDeleteMessageDialogOpen, title: t('chat.messages.deleteConfirm.title'), message: t('chat.messages.deleteConfirm.message'), confirmText: t('chat.messages.deleteConfirm.confirm'), cancelText: t('chat.messages.deleteConfirm.cancel'), onConfirm: confirmDeleteMessage, onCancel: cancelDeleteMessage, currentLanguage: currentLanguage, variant: "danger" })] }));
6616
6685
  });
6617
6686
  ChatWidget.displayName = 'ChatWidget';
6618
6687