andy-widget-react 2.0.5 → 2.1.0

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
@@ -869,7 +869,7 @@ const _mapApiDataToChatbotConfig = (apiData, defaultConfig) => {
869
869
  throw new Error('Invalid chatbot configuration response.')
870
870
  }
871
871
 
872
- const { chatbot, faqs: rawFaqs } = apiData;
872
+ const { chatbot, faqs: rawFaqs, bootstrap } = apiData;
873
873
  const appearance = chatbot.appearance || {};
874
874
  const behavior = chatbot.behavior || {};
875
875
 
@@ -939,6 +939,7 @@ const _mapApiDataToChatbotConfig = (apiData, defaultConfig) => {
939
939
  )
940
940
  .map(faq => ({ question: faq.question.trim(), answer: faq.answer.trim() }));
941
941
  }
942
+ config.bootstrap = bootstrap;
942
943
 
943
944
  return config
944
945
  };
@@ -988,7 +989,7 @@ const fetchChatbotConfig = async embedId => {
988
989
  * @param {string} message - The user's message
989
990
  * @returns {Promise<string>} The bot's response text
990
991
  */
991
- const sendMessage = async (embedId, sessionId, message) => {
992
+ const sendMessageForWidget = async (embedId, sessionId, message) => {
992
993
  try {
993
994
  const apiBaseUrl = getApiBaseUrl();
994
995
  const response = await fetch(`${apiBaseUrl}/messages`, {
@@ -1009,10 +1010,13 @@ const sendMessage = async (embedId, sessionId, message) => {
1009
1010
  if (data.error || data.limitStatus) {
1010
1011
  throw new Error(data.error || 'Chat is temporarily unavailable.')
1011
1012
  }
1013
+ if (data.handoffStatus === 'paused') {
1014
+ return { status: 'waiting' }
1015
+ }
1012
1016
  if (typeof data.botResponse !== 'string' || !data.botResponse.trim()) {
1013
1017
  throw new Error('Unexpected response format from server.')
1014
1018
  }
1015
- return data.botResponse
1019
+ return { status: 'agent', text: data.botResponse }
1016
1020
  } else {
1017
1021
  // Handle HTTP errors (e.g., 500, 404)
1018
1022
  let errorData;
@@ -3429,6 +3433,34 @@ const SVG_ICONS = {
3429
3433
  <path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z" />
3430
3434
  </svg>`};
3431
3435
 
3436
+ // Localized UI Strings
3437
+ // Chrome the widget renders itself, as opposed to copy the Agent supplies.
3438
+ // Keyed by language so React and Vanilla render identical text for a visitor.
3439
+ const UI_STRINGS = {
3440
+ en: {
3441
+ inputPlaceholder: 'Type your message...',
3442
+ predefinedQuestionsTitle: 'What would you like to know?',
3443
+ sendFailure: 'Sorry, I could not answer. Please try again.',
3444
+ handoffWaiting: 'A team member will reply soon.',
3445
+ teamName: 'Team'
3446
+ },
3447
+ es: {
3448
+ inputPlaceholder: 'Escribe tu mensaje...',
3449
+ predefinedQuestionsTitle: '¿Qué te gustaría saber?',
3450
+ sendFailure: 'Lo siento, no pude responder. Inténtalo de nuevo.',
3451
+ handoffWaiting: 'Un miembro del equipo te responderá pronto.',
3452
+ teamName: 'Equipo'
3453
+ }
3454
+ };
3455
+
3456
+ /**
3457
+ * Get the widget's own UI strings for a language.
3458
+ * Unknown languages fall back to English rather than rendering blanks.
3459
+ * @param {string} [lang] - Language code ('en' or 'es')
3460
+ * @returns {typeof UI_STRINGS.en} The localized string set
3461
+ */
3462
+ const getUiStrings = lang => UI_STRINGS[lang] || UI_STRINGS.en;
3463
+
3432
3464
  /**
3433
3465
  * Keyboard detection utility for handling mobile virtual keyboards
3434
3466
  * Provides cross-browser support for detecting keyboard open/close events
@@ -3694,12 +3726,172 @@ const isMobileDevice = () => {
3694
3726
  return (hasTouch && isSmallScreen) || isMobileUserAgent || isMobileMediaQuery
3695
3727
  };
3696
3728
 
3697
- const AndyChat = ({ embedId, tooltip, name, headline, description, welcomeMessage, bubbleLabel, theme, orientation, brandColor, bubbleStyle, useBrandColor, logoUrl, previewMode = false, initialOpen = false }) => {
3729
+ const identityKey = chatbotId => `andy-browser:${chatbotId}`;
3730
+
3731
+ const randomIdentity = () => ({
3732
+ sessionId: window.crypto.randomUUID(),
3733
+ capability: btoa(String.fromCharCode(...(() => {
3734
+ const bytes = new Uint8Array(32);
3735
+ window.crypto.getRandomValues(bytes);
3736
+ return bytes
3737
+ })()))
3738
+ });
3739
+
3740
+ const callConvex = async (convexUrl, kind, path, wireArgs, keepalive = false) => {
3741
+ const response = await fetch(`${convexUrl}/api/${kind}`, {
3742
+ method: 'POST',
3743
+ headers: { 'Content-Type': 'application/json' },
3744
+ body: JSON.stringify({ path, format: 'convex_encoded_json', args: [wireArgs] }),
3745
+ keepalive
3746
+ });
3747
+ if (!response.ok) throw new Error(`Browser handoff ${kind} failed: ${response.status}`)
3748
+ const data = await response.json();
3749
+ if (data.status !== 'success') throw new Error(data.errorMessage || `Browser handoff ${path} failed`)
3750
+ return data.value
3751
+ };
3752
+
3753
+ const sortMessages = messages => [...messages].sort((left, right) =>
3754
+ left.createdAt - right.createdAt || String(left.id).localeCompare(String(right.id)));
3755
+
3756
+ const createBrowserHandoff = (chatbotId, bootstrap, onProjection) => {
3757
+ if (!bootstrap?.convexUrl || !bootstrap.surfaceKey || !bootstrap.surfaceProof) return null
3758
+ const storageKey = identityKey(chatbotId);
3759
+ let identity;
3760
+ try {
3761
+ const stored = JSON.parse(window.sessionStorage.getItem(storageKey) || 'null');
3762
+ identity = stored?.sessionId && stored?.capability && stored.surfaceKey === bootstrap.surfaceKey
3763
+ ? { sessionId: stored.sessionId, capability: stored.capability }
3764
+ : randomIdentity();
3765
+ window.sessionStorage.setItem(storageKey, JSON.stringify({ ...identity, surfaceKey: bootstrap.surfaceKey }));
3766
+ } catch {
3767
+ identity = randomIdentity();
3768
+ }
3769
+ const wireArgs = () => ({ chatbotId, sessionId: identity.sessionId, capability: identity.capability, surfaceProof: bootstrap.surfaceProof });
3770
+ let active = true;
3771
+ let visible = document.visibilityState === 'visible';
3772
+ let cursor;
3773
+ let timer;
3774
+ const pending = new Set();
3775
+ const acknowledged = new Set();
3776
+ const acknowledging = new Set();
3777
+ const localMessages = new Map();
3778
+ const emit = projection => {
3779
+ if (!active) return
3780
+ if (projection?.ok === false && projection.code === 'browser_presence_unavailable') {
3781
+ onProjection({ offline: true, refreshBootstrap: Date.now() >= bootstrap.surfaceProofExpiresAt, identity, messages: [] });
3782
+ return
3783
+ }
3784
+ if (!projection?.ok) return
3785
+ cursor = projection.cursor || cursor;
3786
+ const transcript = projection.transcript || [];
3787
+ const projected = (projection.messages || []).map(message => ({ ...message, role: 'bot', sentByHuman: true }));
3788
+ const byId = new Map(transcript.map(message => [String(message.id), message]));
3789
+ for (const message of localMessages.values()) {
3790
+ const persisted = transcript.some(candidate => candidate.role === 'user' && candidate.content === message.content && Math.abs(candidate.createdAt - message.createdAt) < 30000);
3791
+ if (persisted) localMessages.delete(message.id);
3792
+ else byId.set(message.id, message);
3793
+ }
3794
+ projected.forEach(message => byId.set(String(message.id), message));
3795
+ const messages = sortMessages([...byId.values()]);
3796
+ const pendingIds = projected.map(message => String(message.id)).filter(id => !acknowledged.has(id));
3797
+ pendingIds.forEach(id => pending.add(id));
3798
+ onProjection({ identity, control: projection.control, messages, pendingProjectionIds: pendingIds });
3799
+ };
3800
+ const project = async () => {
3801
+ if (!active || !visible) return
3802
+ if (Date.now() >= bootstrap.surfaceProofExpiresAt) {
3803
+ emit({ ok: false, code: 'browser_presence_unavailable' });
3804
+ return
3805
+ }
3806
+ try {
3807
+ const result = await callConvex(bootstrap.convexUrl, 'query', 'browserHandoff:project', { ...wireArgs(), ...(cursor ? { cursor } : {}) });
3808
+ emit(result);
3809
+ } catch {
3810
+ if (active) onProjection({ offline: true, identity, messages: [] });
3811
+ }
3812
+ };
3813
+ const connect = async () => {
3814
+ if (!active || Date.now() >= bootstrap.surfaceProofExpiresAt) {
3815
+ emit({ ok: false, code: 'browser_presence_unavailable' });
3816
+ return
3817
+ }
3818
+ try {
3819
+ const result = await callConvex(bootstrap.convexUrl, 'mutation', 'browserHandoff:connect', wireArgs());
3820
+ if (result?.ok === false) return emit(result)
3821
+ await project();
3822
+ } catch {
3823
+ if (active) onProjection({ offline: true, identity, messages: [] });
3824
+ }
3825
+ };
3826
+ const release = () => callConvex(bootstrap.convexUrl, 'mutation', 'browserHandoff:release', wireArgs(), true).catch(() => undefined);
3827
+ const onVisibility = () => {
3828
+ visible = document.visibilityState === 'visible';
3829
+ if (visible) void connect();
3830
+ else void release();
3831
+ };
3832
+ document.addEventListener('visibilitychange', onVisibility);
3833
+ window.addEventListener('pagehide', release);
3834
+ void connect();
3835
+ timer = window.setInterval(async () => {
3836
+ if (!active || !visible) return
3837
+ if (Date.now() >= bootstrap.surfaceProofExpiresAt) {
3838
+ window.clearInterval(timer);
3839
+ emit({ ok: false, code: 'browser_presence_unavailable' });
3840
+ return
3841
+ }
3842
+ try {
3843
+ const result = await callConvex(bootstrap.convexUrl, 'mutation', 'browserHandoff:heartbeat', wireArgs());
3844
+ if (result?.ok === false) emit(result);
3845
+ else await project();
3846
+ } catch {
3847
+ if (active) onProjection({ offline: true, identity, messages: [] });
3848
+ }
3849
+ }, 15000);
3850
+ return {
3851
+ identity: { sessionId: identity.sessionId },
3852
+ recordVisitorMessage: content => {
3853
+ const id = `local-${window.crypto.randomUUID()}`;
3854
+ localMessages.set(id, { id, role: 'user', content, createdAt: Date.now(), sentByHuman: false });
3855
+ },
3856
+ markRendered: async messageId => {
3857
+ if (!active || !pending.has(messageId) || acknowledged.has(messageId) || acknowledging.has(messageId)) return
3858
+ acknowledging.add(messageId);
3859
+ try {
3860
+ const result = await callConvex(bootstrap.convexUrl, 'mutation', 'browserHandoff:acknowledge', { ...wireArgs(), messageId });
3861
+ if (result?.ok) {
3862
+ acknowledged.add(messageId);
3863
+ pending.delete(messageId);
3864
+ }
3865
+ } catch {
3866
+ if (active) onProjection({ offline: true, identity, messages: [] });
3867
+ } finally {
3868
+ acknowledging.delete(messageId);
3869
+ }
3870
+ },
3871
+ stop: () => {
3872
+ if (!active) return
3873
+ active = false;
3874
+ window.clearInterval(timer);
3875
+ document.removeEventListener('visibilitychange', onVisibility);
3876
+ window.removeEventListener('pagehide', release);
3877
+ void release();
3878
+ }
3879
+ }
3880
+ };
3881
+
3882
+ const AndyChat = ({ embedId, tooltip, name, headline, description, welcomeMessage, bubbleLabel, lang, predefinedQuestions, theme, orientation, brandColor, bubbleStyle, useBrandColor, logoUrl, previewMode = false, initialOpen = false }) => {
3698
3883
  const [config, setConfig] = useState(null);
3884
+ // The visitor's language wins over the Agent's own default: a Spanish Agent
3885
+ // embedded on an English page must still render English chrome.
3886
+ const uiLang = lang || config?.defaultLang || 'en';
3887
+ const strings = getUiStrings(uiLang);
3888
+ const activeQuestions = predefinedQuestions?.length ? predefinedQuestions : config?.predefinedQuestions;
3699
3889
  const [isOpen, setIsOpen] = useState(initialOpen);
3700
3890
  const [messages, setMessages] = useState([]);
3701
3891
  const [isLoading, setIsLoading] = useState(false);
3702
3892
  const [sessionId, setSessionId] = useState(() => generateSessionId());
3893
+ const handoffRef = useRef(null);
3894
+ const pendingHandoffIdsRef = useRef([]);
3703
3895
  const [shadowReady, setShadowReady] = useState(false);
3704
3896
  const [showPredefinedQuestions, setShowPredefinedQuestions] = useState(true);
3705
3897
  const [userInfoCollected, setUserInfoCollected] = useState(false);
@@ -3713,6 +3905,29 @@ const AndyChat = ({ embedId, tooltip, name, headline, description, welcomeMessag
3713
3905
  const messagesContainerRef = useRef(null);
3714
3906
  const welcomePopupShownRef = useRef(false);
3715
3907
  const welcomePopupTimeoutRef = useRef(null);
3908
+ useEffect(() => {
3909
+ if (!config?.bootstrap || !config.id)
3910
+ return;
3911
+ handoffRef.current?.stop();
3912
+ const handoff = createBrowserHandoff(config.id, config.bootstrap, projection => {
3913
+ if (projection.offline)
3914
+ return;
3915
+ setSessionId(projection.identity.sessionId);
3916
+ pendingHandoffIdsRef.current = projection.pendingProjectionIds || [];
3917
+ const projectedMessages = projection.messages.map(message => ({ id: message.id, content: message.content, sender: message.role === 'user' ? 'user' : 'bot', timestamp: new Date(message.createdAt).toISOString(), isHuman: message.sentByHuman }));
3918
+ setMessages(projection.control === 'requested' || projection.control === 'human'
3919
+ ? [...projectedMessages, { id: 'handoff-waiting', content: strings.handoffWaiting, sender: 'bot', timestamp: new Date().toISOString(), isWaiting: true }]
3920
+ : projectedMessages);
3921
+ });
3922
+ handoffRef.current = handoff;
3923
+ return () => {
3924
+ handoff?.stop();
3925
+ handoffRef.current = null;
3926
+ };
3927
+ }, [config?.bootstrap, config?.id]);
3928
+ useEffect(() => {
3929
+ pendingHandoffIdsRef.current.forEach(messageId => void handoffRef.current?.markRendered(messageId));
3930
+ }, [messages]);
3716
3931
  // Create shadow DOM container on mount
3717
3932
  useEffect(() => {
3718
3933
  if (!containerRef.current)
@@ -3890,12 +4105,12 @@ const AndyChat = ({ embedId, tooltip, name, headline, description, welcomeMessag
3890
4105
  bubbleStyle: bubbleStyle || 'icon',
3891
4106
  bubbleLabel: bubbleLabel || null,
3892
4107
  useBrandColor: useBrandColor !== undefined ? useBrandColor : true,
3893
- defaultLang: 'en',
4108
+ defaultLang: uiLang,
3894
4109
  showBranding: true,
3895
4110
  showWelcomePopup: { enabled: false, seconds: 3, message: '' },
3896
4111
  collectUserInfo: { enabled: false, message: '' },
3897
4112
  welcomeMessage: welcomeMessage || 'Hi there! How can I help you today?',
3898
- predefinedQuestions: [],
4113
+ predefinedQuestions: predefinedQuestions || [],
3899
4114
  faqs: []
3900
4115
  };
3901
4116
  setConfig(previewConfig);
@@ -4039,16 +4254,21 @@ const AndyChat = ({ embedId, tooltip, name, headline, description, welcomeMessag
4039
4254
  sender: 'user',
4040
4255
  timestamp: new Date().toISOString()
4041
4256
  };
4257
+ handoffRef.current?.recordVisitorMessage(messageText);
4042
4258
  setMessages(prev => [...prev, userMessage]);
4043
4259
  if (hidePredefined) {
4044
4260
  setShowPredefinedQuestions(false);
4045
4261
  }
4046
4262
  try {
4047
4263
  setIsLoading(true);
4048
- const response = await sendMessage(embedId, sessionId, messageText);
4264
+ const response = await sendMessageForWidget(embedId, sessionId, messageText);
4265
+ if (response.status === 'waiting') {
4266
+ setMessages(prev => prev.some(message => message.isWaiting) ? prev : [...prev, { id: 'handoff-waiting', content: strings.handoffWaiting, sender: 'bot', timestamp: new Date().toISOString(), isWaiting: true }]);
4267
+ return;
4268
+ }
4049
4269
  const botMessage = {
4050
4270
  id: timestamp + 1,
4051
- content: response,
4271
+ content: response.text,
4052
4272
  sender: 'bot',
4053
4273
  timestamp: new Date().toISOString()
4054
4274
  };
@@ -4070,9 +4290,7 @@ const AndyChat = ({ embedId, tooltip, name, headline, description, welcomeMessag
4070
4290
  console.error('AndyChat: Message request failed.');
4071
4291
  const errorMessage = {
4072
4292
  id: timestamp + 3,
4073
- content: config?.defaultLang === 'es'
4074
- ? 'Lo siento, no pude responder. Inténtalo de nuevo.'
4075
- : 'Sorry, I could not answer. Please try again.',
4293
+ content: strings.sendFailure,
4076
4294
  sender: 'bot',
4077
4295
  timestamp: new Date().toISOString(),
4078
4296
  isError: true
@@ -4188,7 +4406,7 @@ const AndyChat = ({ embedId, tooltip, name, headline, description, welcomeMessag
4188
4406
  bubble.addEventListener('click', () => setIsOpen(prev => !prev));
4189
4407
  }
4190
4408
  const isPillStyle = config?.bubbleStyle === 'pill';
4191
- const displayLabel = bubbleLabel || config?.bubbleLabel || (isPillStyle ? getDefaultBubbleLabel(config?.defaultLang || 'en') : null);
4409
+ const displayLabel = bubbleLabel || config?.bubbleLabel || (isPillStyle ? getDefaultBubbleLabel(uiLang) : null);
4192
4410
  const bubbleLogoUrl = config?.logoUrl || CHAT_BUBBLE_DEFAULT_ICON_URL;
4193
4411
  if (bubble) {
4194
4412
  bubble.className = 'andy-chat-bubble';
@@ -4241,10 +4459,10 @@ const AndyChat = ({ embedId, tooltip, name, headline, description, welcomeMessag
4241
4459
  if (message.isLoading) {
4242
4460
  return (jsx("div", { className: 'andy-message-container andy-message-container--bot andy-message-container--loading', children: jsx("div", { className: 'andy-message andy-message--bot', children: jsxs("div", { className: 'andy-typing-indicator', children: [jsx("div", { className: 'andy-typing-indicator__dot' }), jsx("div", { className: 'andy-typing-indicator__dot' }), jsx("div", { className: 'andy-typing-indicator__dot' })] }) }) }, message.id));
4243
4461
  }
4244
- return (jsxs("div", { className: `andy-message-container andy-message-container--${message.sender}`, children: [message.sender === 'bot' && (jsxs("div", { className: `andy-message andy-message--bot ${message.isError ? 'andy-message--error' : ''}`, children: [jsxs("div", { className: 'andy-bot-header', children: [jsx("div", { className: 'andy-bot-header__icon', children: config?.logoUrl ? (jsx("img", { src: config.logoUrl, alt: 'Bot Avatar', className: 'andy-bot-header__icon-image' })) : (jsx("img", { src: CHAT_BUBBLE_DEFAULT_ICON_URL, alt: 'Bot Avatar', className: 'andy-bot-header__icon-image andy-bot-header__icon-image--default' })) }), jsx("span", { className: 'andy-bot-header__name', children: config?.name || 'Andy bot' })] }), jsx("div", { className: 'andy-message__content', children: jsx("div", { dangerouslySetInnerHTML: { __html: parseMarkdown(message.content) } }) })] })), message.sender === 'user' && (jsx("div", { className: 'andy-message andy-message--user', children: jsx("div", { className: 'andy-message__content', children: jsx("p", { children: message.content }) }) }))] }, message.id));
4245
- }), isLoading && (jsx("div", { className: 'andy-message-container andy-message-container--bot andy-message-container--typing', children: jsx("div", { className: 'andy-message andy-message--bot andy-message--typing', children: jsxs("div", { className: 'andy-typing-indicator', children: [jsx("div", { className: 'andy-typing-indicator__dot' }), jsx("div", { className: 'andy-typing-indicator__dot' }), jsx("div", { className: 'andy-typing-indicator__dot' })] }) }) }))] }), jsxs("div", { className: 'andy-chat-window__bottom', children: [config?.predefinedQuestions &&
4246
- config.predefinedQuestions.length > 0 &&
4247
- showPredefinedQuestions && (jsxs("div", { className: 'andy-predefined-questions', id: 'andy-predefined-questions', children: [jsx("div", { className: 'andy-predefined-questions__header', children: jsx("p", { className: 'andy-predefined-questions__title', children: "What would you like to know?" }) }), jsx("div", { className: 'andy-predefined-questions__list', children: config.predefinedQuestions.map((q, index) => (jsxs("button", { type: 'button', className: 'andy-predefined-question', "data-question": q.question, disabled: isCollectingUserInfo || isLoading || !config, children: [jsx("span", { className: 'andy-predefined-question__icon', children: "\uD83D\uDCAC" }), jsx("span", { className: 'andy-predefined-question__text', children: q.question })] }, index))) })] })), jsx("div", { className: 'andy-chat-window__input', children: jsx("div", { className: 'andy-input-container', children: jsxs("div", { className: 'andy-input', children: [jsx("input", { type: 'text', className: 'andy-input__field', id: 'andy-message-input', placeholder: config ? 'Type your message...' : '...', "aria-label": 'Message input', onKeyDown: e => {
4462
+ return (jsxs("div", { className: `andy-message-container andy-message-container--${message.sender} ${message.isWaiting ? 'andy-message-container--waiting' : ''}`, children: [message.sender === 'bot' && (jsxs("div", { className: `andy-message andy-message--bot ${message.isHuman ? 'andy-message--operator' : ''} ${message.isWaiting ? 'andy-message--waiting' : ''} ${message.isError ? 'andy-message--error' : ''}`, children: [jsxs("div", { className: 'andy-bot-header', children: [jsx("div", { className: 'andy-bot-header__icon', children: config?.logoUrl ? (jsx("img", { src: config.logoUrl, alt: 'Bot Avatar', className: 'andy-bot-header__icon-image' })) : (jsx("img", { src: CHAT_BUBBLE_DEFAULT_ICON_URL, alt: 'Bot Avatar', className: 'andy-bot-header__icon-image andy-bot-header__icon-image--default' })) }), jsx("span", { className: 'andy-bot-header__name', children: message.isHuman ? strings.teamName : config?.name || 'Andy bot' })] }), jsx("div", { className: 'andy-message__content', children: jsx("div", { dangerouslySetInnerHTML: { __html: parseMarkdown(message.content) } }) })] })), message.sender === 'user' && (jsx("div", { className: 'andy-message andy-message--user', children: jsx("div", { className: 'andy-message__content', children: jsx("p", { children: message.content }) }) }))] }, message.id));
4463
+ }), isLoading && (jsx("div", { className: 'andy-message-container andy-message-container--bot andy-message-container--typing', children: jsx("div", { className: 'andy-message andy-message--bot andy-message--typing', children: jsxs("div", { className: 'andy-typing-indicator', children: [jsx("div", { className: 'andy-typing-indicator__dot' }), jsx("div", { className: 'andy-typing-indicator__dot' }), jsx("div", { className: 'andy-typing-indicator__dot' })] }) }) }))] }), jsxs("div", { className: 'andy-chat-window__bottom', children: [activeQuestions &&
4464
+ activeQuestions.length > 0 &&
4465
+ showPredefinedQuestions && (jsxs("div", { className: 'andy-predefined-questions', id: 'andy-predefined-questions', children: [jsx("div", { className: 'andy-predefined-questions__header', children: jsx("p", { className: 'andy-predefined-questions__title', children: strings.predefinedQuestionsTitle }) }), jsx("div", { className: 'andy-predefined-questions__list', children: activeQuestions.map((q, index) => (jsxs("button", { type: 'button', className: 'andy-predefined-question', "data-question": q.question, disabled: isCollectingUserInfo || isLoading || !config, children: [jsx("span", { className: 'andy-predefined-question__icon', children: "\uD83D\uDCAC" }), jsx("span", { className: 'andy-predefined-question__text', children: q.question })] }, index))) })] })), jsx("div", { className: 'andy-chat-window__input', children: jsx("div", { className: 'andy-input-container', children: jsxs("div", { className: 'andy-input', children: [jsx("input", { type: 'text', className: 'andy-input__field', id: 'andy-message-input', placeholder: config ? strings.inputPlaceholder : '...', "aria-label": 'Message input', onKeyDown: e => {
4248
4466
  if (e.key === 'Enter' && e.target.value.trim()) {
4249
4467
  handleSendMessage(e.target.value);
4250
4468
  e.target.value = '';
@@ -4346,7 +4564,7 @@ const useAndyChat = (embedId) => {
4346
4564
  return newIsOpen;
4347
4565
  });
4348
4566
  }, [config, welcomePopupShown, userInfoCollected]);
4349
- const sendMessage$1 = useCallback(async (messageText) => {
4567
+ const sendMessage = useCallback(async (messageText) => {
4350
4568
  if (!messageText.trim() || !config || !sessionId) {
4351
4569
  return;
4352
4570
  }
@@ -4358,10 +4576,12 @@ const useAndyChat = (embedId) => {
4358
4576
  };
4359
4577
  setMessages(prev => [...prev, userMessage]);
4360
4578
  try {
4361
- const response = await sendMessage(embedId, sessionId, messageText);
4579
+ const response = await sendMessageForWidget(embedId, sessionId, messageText);
4580
+ if (response.status === 'waiting')
4581
+ return response;
4362
4582
  const botMessage = {
4363
4583
  id: Date.now() + 1,
4364
- content: response || 'I apologize, but I encountered an error processing your request.',
4584
+ content: response.text || 'I apologize, but I encountered an error processing your request.',
4365
4585
  sender: 'bot',
4366
4586
  timestamp: new Date().toISOString()
4367
4587
  };
@@ -4458,7 +4678,7 @@ const useAndyChat = (embedId) => {
4458
4678
  error,
4459
4679
  initializeChatbot,
4460
4680
  toggleChat,
4461
- sendMessage: sendMessage$1,
4681
+ sendMessage,
4462
4682
  showFaq,
4463
4683
  hideFaq,
4464
4684
  showWelcomePopup,