@xapp/chat-widget 1.104.0 → 1.104.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -329,7 +329,7 @@ function buildChatWidgetChannel(props) {
329
329
  // and the text on it are always a matched pair rather than one scraped
330
330
  // value and one guess.
331
331
  const primaryColor = (0, color_1.usableColorOr)(props.primaryColor, DEFAULT_PRIMARY);
332
- return Object.assign({ type: "chat-widget",
332
+ return Object.assign(Object.assign({ type: "chat-widget",
333
333
  // MCP by default. The widget runs on a public page, so homeowner mode
334
334
  // — which is unauthenticated and carries the appId in the url — is the
335
335
  // only one that can work there; business_owner needs a token, and a
@@ -340,7 +340,7 @@ function buildChatWidgetChannel(props) {
340
340
  mcp: {
341
341
  mode: "homeowner"
342
342
  }
343
- }, theme: buildChatTheme(primaryColor, props.secondaryColor), actionBar: buildActionBar({ apiUrl, formWidgetKey, phoneNumber }) }, (businessName ? { botName: businessName } : {}));
343
+ }, theme: buildChatTheme(primaryColor, props.secondaryColor), actionBar: buildActionBar({ apiUrl, formWidgetKey, phoneNumber }) }, (formWidgetKey ? { formWidgetKey } : {})), (businessName ? { botName: businessName } : {}));
344
344
  }
345
345
  /**
346
346
  * The dock of buttons that replaces the plain chat bubble.
@@ -37293,12 +37293,25 @@ var ChatWidgetConnected = function (props) {
37293
37293
  }
37294
37294
  return (require$$0.jsx(ChatConfigContext.Provider, { value: config, children: require$$0.jsx(ChatServerContext.Provider, { value: chatServer, children: require$$0.jsx(ChatWidgetUI, __assign$1({}, props, { config: rawConfig })) }) }));
37295
37295
  };
37296
+ var HOST_FORM_WIDGET_CHECK_MS = 500;
37297
+ /** About 15s: long enough for a slow host script, short enough not to poll forever. */
37298
+ var HOST_FORM_WIDGET_MAX_CHECKS = 30;
37299
+ /** The form widget key a Form/Book button carries on its own formConfig, if any. */
37300
+ function formButtonKey(button) {
37301
+ var _a, _b;
37302
+ return opensFormWidget(button)
37303
+ ? ((_b = (_a = button.formConfig) === null || _a === void 0 ? void 0 : _a.api) === null || _b === void 0 ? void 0 : _b.key) || undefined
37304
+ : undefined;
37305
+ }
37306
+ function isHostFormWidgetOnPage() {
37307
+ return isFormWidgetScriptLoaded() || isFormWidgetReady();
37308
+ }
37296
37309
  /**
37297
37310
  * Inner ChatWidget component that renders the actual UI.
37298
37311
  * Exported for use by StaticChatWidgetContainer to avoid infinite loops.
37299
37312
  */
37300
37313
  var ChatWidgetUI = function (props) {
37301
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20;
37314
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23;
37302
37315
  var innerDispatch = useChatDispatch();
37303
37316
  var dispatch = useChatServerDispatch();
37304
37317
  // Publish the visual viewport's geometry so the panel can size around the soft keyboard.
@@ -37329,21 +37342,21 @@ var ChatWidgetUI = function (props) {
37329
37342
  var popOutEnabled = dockedMode && ((_c = props.config.popOut) === null || _c === void 0 ? void 0 : _c.enabled) === true;
37330
37343
  var popOutMinWidth = (_e = (_d = props.config.popOut) === null || _d === void 0 ? void 0 : _d.minWidth) !== null && _e !== void 0 ? _e : DEFAULT_POP_OUT_MIN_WIDTH;
37331
37344
  var anchorRef = React.useRef(null);
37332
- var _21 = React.useState(false), anchorOffScreen = _21[0], setAnchorOffScreen = _21[1];
37345
+ var _24 = React.useState(false), anchorOffScreen = _24[0], setAnchorOffScreen = _24[1];
37333
37346
  // A returning visitor's chat often sits below the fold at load. Following them before they
37334
37347
  // have even reached it would put a floating window in front of someone who never scrolled
37335
37348
  // past anything, so the chat has to have been on screen once first.
37336
- var _22 = React.useState(false), anchorSeen = _22[0], setAnchorSeen = _22[1];
37349
+ var _25 = React.useState(false), anchorSeen = _25[0], setAnchorSeen = _25[1];
37337
37350
  // Set when the visitor puts the floating window away: it stays put until their own
37338
37351
  // scrolling brings the chat's place on the page back into view.
37339
- var _23 = React.useState(false), popOutDismissed = _23[0], setPopOutDismissed = _23[1];
37352
+ var _26 = React.useState(false), popOutDismissed = _26[0], setPopOutDismissed = _26[1];
37340
37353
  // On a phone the chat offers its entry point rather than covering the page; this is set when
37341
37354
  // the visitor taps that and asks for the conversation.
37342
- var _24 = React.useState(false), mobileOpened = _24[0], setMobileOpened = _24[1];
37355
+ var _27 = React.useState(false), mobileOpened = _27[0], setMobileOpened = _27[1];
37343
37356
  // Tracked rather than read at render: a visitor who narrows the window or rotates a tablet
37344
37357
  // while the chat is popped out would otherwise keep a floating window over most of a phone
37345
37358
  // screen until some unrelated re-render happened along.
37346
- var _25 = React.useState(typeof window === "undefined" ? 0 : window.innerWidth), viewportWidth = _25[0], setViewportWidth = _25[1];
37359
+ var _28 = React.useState(typeof window === "undefined" ? 0 : window.innerWidth), viewportWidth = _28[0], setViewportWidth = _28[1];
37347
37360
  React.useEffect(function () {
37348
37361
  if (!popOutEnabled || typeof window === "undefined") {
37349
37362
  return undefined;
@@ -37509,10 +37522,10 @@ var ChatWidgetUI = function (props) {
37509
37522
  var autoOpenOnWidthMatch = !isNaN(autoOpenOnWidthValue) && window.innerWidth >= autoOpenOnWidthValue;
37510
37523
  // When autoOpenOnWidth is configured, use width check instead of persisted visible state
37511
37524
  // to prevent greeting requests on viewports below the threshold
37512
- var _26 = React.useState((!canMinimize && !canCancel) ||
37525
+ var _29 = React.useState((!canMinimize && !canCancel) ||
37513
37526
  // !!get("visible") ||
37514
- (!isNaN(autoOpenOnWidthValue) ? autoOpenOnWidthMatch : chatState.visuals.visible)), visible = _26[0], setVisibleState = _26[1];
37515
- var _27 = React.useState(false); _27[0]; var setTypingState = _27[1]; // false initially - state kept for potential external observers
37527
+ (!isNaN(autoOpenOnWidthValue) ? autoOpenOnWidthMatch : chatState.visuals.visible)), visible = _29[0], setVisibleState = _29[1];
37528
+ var _30 = React.useState(false); _30[0]; var setTypingState = _30[1]; // false initially - state kept for potential external observers
37516
37529
  // Ref to track typing state for use in timeout callbacks
37517
37530
  var typingRef = React.useRef(false);
37518
37531
  // Timeout ref for debouncing "stop typing" events
@@ -37559,7 +37572,7 @@ var ChatWidgetUI = function (props) {
37559
37572
  };
37560
37573
  // eslint-disable-next-line react-hooks/exhaustive-deps
37561
37574
  }, []);
37562
- var _28 = React.useState(!document.hidden), isTabVisible = _28[0], setIsTabVisible = _28[1];
37575
+ var _31 = React.useState(!document.hidden), isTabVisible = _31[0], setIsTabVisible = _31[1];
37563
37576
  React.useEffect(function () {
37564
37577
  var handleVisibilityChange = function () {
37565
37578
  setIsTabVisible(!document.hidden);
@@ -37783,8 +37796,8 @@ var ChatWidgetUI = function (props) {
37783
37796
  }));
37784
37797
  }
37785
37798
  // Action Bar state and handlers
37786
- var _29 = React.useState(false), formModalOpen = _29[0], setFormModalOpen = _29[1];
37787
- var _30 = React.useState(null), activeFormConfig = _30[0], setActiveFormConfig = _30[1];
37799
+ var _32 = React.useState(false), formModalOpen = _32[0], setFormModalOpen = _32[1];
37800
+ var _33 = React.useState(null), activeFormConfig = _33[0], setActiveFormConfig = _33[1];
37788
37801
  var handleFormButtonClick = React.useCallback(function (formButton) {
37789
37802
  // If form-widget is configured, use its openForm API
37790
37803
  // The form-widget script should already be pre-loaded
@@ -37856,14 +37869,51 @@ var ChatWidgetUI = function (props) {
37856
37869
  // contact/booking page the form already *is* the page, which makes every button that
37857
37870
  // only opens one redundant. XappMedia/chat-widget#1626.
37858
37871
  var isFormPage = useIsFormPage();
37872
+ // Studio can save Form/Book buttons with no form widget key anywhere. They can only open the
37873
+ // FormModal fallback, which then says "Form widget key not configured" - so drop them, unless
37874
+ // the host page installed the form widget itself. One key anywhere is enough: a page has one
37875
+ // form widget and every Form/Book button opens it through the same global.
37876
+ var configuredButtons = (_2 = (_1 = config === null || config === void 0 ? void 0 : config.actionBar) === null || _1 === void 0 ? void 0 : _1.buttons) !== null && _2 !== void 0 ? _2 : [];
37877
+ var hasKeylessFormButton = !(config === null || config === void 0 ? void 0 : config.formWidgetKey) &&
37878
+ configuredButtons.some(opensFormWidget) &&
37879
+ !configuredButtons.some(formButtonKey);
37880
+ var _34 = React.useState(function () { return hasKeylessFormButton && isHostFormWidgetOnPage(); }), hostFormWidget = _34[0], setHostFormWidget = _34[1];
37881
+ // The host's form-widget script loads independently of ours and can arrive after the first
37882
+ // render, so keep looking for a while rather than deciding once. This only runs when there is
37883
+ // no key anywhere, and with no key we never preload - so the script this finds by its fixed id
37884
+ // cannot be our own.
37885
+ React.useEffect(function () {
37886
+ if (!hasKeylessFormButton || hostFormWidget) {
37887
+ return undefined;
37888
+ }
37889
+ var checks = 0;
37890
+ var interval = setInterval(function () {
37891
+ checks++;
37892
+ if (isHostFormWidgetOnPage()) {
37893
+ setHostFormWidget(true);
37894
+ clearInterval(interval);
37895
+ }
37896
+ else if (checks >= HOST_FORM_WIDGET_MAX_CHECKS) {
37897
+ clearInterval(interval);
37898
+ }
37899
+ }, HOST_FORM_WIDGET_CHECK_MS);
37900
+ return function () { return clearInterval(interval); };
37901
+ }, [hasKeylessFormButton, hostFormWidget]);
37902
+ var actionBarConfig = React.useMemo(function () {
37903
+ var actionBar = config === null || config === void 0 ? void 0 : config.actionBar;
37904
+ if (!actionBar || !hasKeylessFormButton || hostFormWidget) {
37905
+ return actionBar;
37906
+ }
37907
+ return __assign$1(__assign$1({}, actionBar), { buttons: actionBar.buttons.filter(function (button) { return !opensFormWidget(button); }) });
37908
+ }, [config === null || config === void 0 ? void 0 : config.actionBar, hasKeylessFormButton, hostFormWidget]);
37859
37909
  var actionBarButtons = React.useMemo(function () {
37860
- return actionBarEnabled && (config === null || config === void 0 ? void 0 : config.actionBar)
37861
- ? visibleActionBarButtons(config.actionBar.buttons, {
37910
+ return actionBarEnabled && actionBarConfig
37911
+ ? visibleActionBarButtons(actionBarConfig.buttons, {
37862
37912
  chatDisabled: config.disabled,
37863
37913
  formDisabled: isFormPage,
37864
37914
  })
37865
37915
  : [];
37866
- }, [actionBarEnabled, config === null || config === void 0 ? void 0 : config.actionBar, config === null || config === void 0 ? void 0 : config.disabled, isFormPage]);
37916
+ }, [actionBarEnabled, actionBarConfig, config === null || config === void 0 ? void 0 : config.disabled, isFormPage]);
37867
37917
  // A bar that filters down to nothing is a bare strip of chrome. Treat it as if the bar
37868
37918
  // were off, so the visitor still gets the plain chat button rather than an empty shell.
37869
37919
  var actionBarVisible = actionBarEnabled && actionBarButtons.length > 0;
@@ -37875,7 +37925,7 @@ var ChatWidgetUI = function (props) {
37875
37925
  onConnectionStatusChange(connectionStatus);
37876
37926
  }
37877
37927
  }, [connectionStatus, onConnectionStatusChange]);
37878
- useExternalScript((_1 = props.config) === null || _1 === void 0 ? void 0 : _1.middlewareUrl);
37928
+ useExternalScript((_3 = props.config) === null || _3 === void 0 ? void 0 : _3.middlewareUrl);
37879
37929
  // Only reach for form.xapp.ai when something on this page can still open the form.
37880
37930
  //
37881
37931
  // Loading it is not free: the script mounts a second form-widget instance whose wrapper
@@ -37885,11 +37935,16 @@ var ChatWidgetUI = function (props) {
37885
37935
  //
37886
37936
  // With no bar configured the side button IS the entry point, so that case still loads.
37887
37937
  var formWidgetNeeded = !isFormPage && (!actionBarEnabled || actionBarButtons.some(opensFormWidget));
37938
+ // The top-level key is the documented place for it, but an App can carry the key only on
37939
+ // a Form/Book button's formConfig - buildChatWidgetChannel did exactly that. Reading only
37940
+ // the top-level key left the script unloaded, so every click waited out
37941
+ // openFormWidgetWhenReady before the modal fallback found the button's key.
37942
+ var formWidgetKey = (_4 = config === null || config === void 0 ? void 0 : config.formWidgetKey) !== null && _4 !== void 0 ? _4 : actionBarButtons.map(formButtonKey).find(Boolean);
37888
37943
  React.useEffect(function () {
37889
- if ((config === null || config === void 0 ? void 0 : config.formWidgetKey) && formWidgetNeeded) {
37890
- preloadFormWidgetScript(config.formWidgetKey);
37944
+ if (formWidgetKey && formWidgetNeeded) {
37945
+ preloadFormWidgetScript(formWidgetKey);
37891
37946
  }
37892
- }, [config === null || config === void 0 ? void 0 : config.formWidgetKey, formWidgetNeeded]);
37947
+ }, [formWidgetKey, formWidgetNeeded]);
37893
37948
  // Set body attribute when actionBar is enabled (used by CSS to hide form-widget side button on mobile)
37894
37949
  // Uses reference counting to handle multiple widget instances
37895
37950
  React.useEffect(function () {
@@ -37915,23 +37970,23 @@ var ChatWidgetUI = function (props) {
37915
37970
  };
37916
37971
  }, [actionBarVisible]);
37917
37972
  // This is a pseudo agent. It represent's the widget (shown in the header avatar for instance)
37918
- var widgetAgent = ((_2 = chatState.agents["agent:robot"]) === null || _2 === void 0 ? void 0 : _2.user) ||
37973
+ var widgetAgent = ((_5 = chatState.agents["agent:robot"]) === null || _5 === void 0 ? void 0 : _5.user) ||
37919
37974
  (config === null || config === void 0 ? void 0 : config.agent) || {
37920
37975
  nick: "agent:robot",
37921
37976
  avatarPath: config.avatarUrl,
37922
37977
  display_name: "Agent",
37923
37978
  };
37924
- var widgetContainer = (require$$0.jsxs("div", { className: "widget-container ".concat(modeClass, " ").concat(getVisibilityClass()).concat(actionBarVisible ? " widget-container--with-action-bar" : "").concat(poppedOut ? " widget-container--popped" : "").concat(collapsedOnMobile ? " widget-container--collapsed" : ""), children: [require$$0.jsx(WidgetStylesheet, { theme: config === null || config === void 0 ? void 0 : config.theme }), !headerHidden && (require$$0.jsx(ChatHeader, { accountStatus: chatState.accountStatus, refreshOnClick: handleRestartClick, minimizeOnClick: poppedOut ? handlePopOutDismiss : handleMinimizeClick, cancelOnClick: handleCancelClick, agent: widgetAgent, canRefresh: canRefresh, canMinimize: poppedOut || canMinimize, canCancel: canCancel, config: config === null || config === void 0 ? void 0 : config.header, menuConfig: config.menu, debugMode: (_5 = (_4 = (_3 = props.config) === null || _3 === void 0 ? void 0 : _3.connection) === null || _4 === void 0 ? void 0 : _4.mcp) === null || _5 === void 0 ? void 0 : _5.debugMode, onSubmit: handleOnSubmit })), require$$0.jsx(MessageList, { visible: visible, queuePosition: chatState.queuePosition, isChatting: chatState.isChatting, isOffline: isOffline, messages: messages, agents: chatState.agents, agent: config === null || config === void 0 ? void 0 : config.agent, lastRatingRequestTimestamp: chatState.lastRatingRequestTimestamp, hasRating: chatState.hasRating, visitorId: chatState.visitorId, hasWsButton: !!chatState.wsButton, messageMiddleware: props.messageMiddleware, textTypingStatusEnabled: (_7 = (_6 = props.config) === null || _6 === void 0 ? void 0 : _6.typingStatus) === null || _7 === void 0 ? void 0 : _7.textTypingStatusEnabled, disableAutoScroll: props.disableAutoScroll, onSend: handleSendMessage, onWrite: handleWriteMessage, onOpenUrl: handleOpenUrl, minimizeOnClick: handleMinimizeClick }), require$$0.jsx("div", { className: "xa-spinner-container ".concat(visible && connectionStatus === "pending" ? "visible" : ""), children: require$$0.jsx("div", { className: "xa-spinner" }) }), connectionStatus === "offline" && require$$0.jsx(ServerOffline, {}), chatState.wsButton && visible && (require$$0.jsx(WsButton, { button: chatState.wsButton, onPress: handleWsButtonPress })), require$$0.jsx(ChatFooter, { isAdmin: config === null || config === void 0 ? void 0 : config.isAdmin, isChatting: chatState.isChatting, placeholder: (_8 = config === null || config === void 0 ? void 0 : config.input) === null || _8 === void 0 ? void 0 : _8.placeholder, sendButtonIcon: (_10 = (_9 = config === null || config === void 0 ? void 0 : config.footer) === null || _9 === void 0 ? void 0 : _9.sendButton) === null || _10 === void 0 ? void 0 : _10.icon, sendButtonIconHover: (_12 = (_11 = config === null || config === void 0 ? void 0 : config.footer) === null || _11 === void 0 ? void 0 : _11.sendButton) === null || _12 === void 0 ? void 0 : _12.iconHover, sendButtonIconDisabled: (_14 = (_13 = config === null || config === void 0 ? void 0 : config.footer) === null || _13 === void 0 ? void 0 : _13.sendButton) === null || _14 === void 0 ? void 0 : _14.iconDisabled, visible: visible, hasWsButton: !!chatState.wsButton, menuConfig: menuConfig, footerConfig: (_15 = props.config) === null || _15 === void 0 ? void 0 : _15.footer, inputConfig: (_16 = props.config) === null || _16 === void 0 ? void 0 : _16.input, disabled: previewMode, onChange: handleOnChange, onSubmit: handleOnSubmit, onFileUpload: handleFileUpload }), require$$0.jsx("div", { className: "restartModal", ref: modalRef, onClick: handleRestartModalCloseClick, children: require$$0.jsx(ModalContent, { onClose: handleRestartModalCloseClick, onReset: handleReset }) })] }));
37979
+ var widgetContainer = (require$$0.jsxs("div", { className: "widget-container ".concat(modeClass, " ").concat(getVisibilityClass()).concat(actionBarVisible ? " widget-container--with-action-bar" : "").concat(poppedOut ? " widget-container--popped" : "").concat(collapsedOnMobile ? " widget-container--collapsed" : ""), children: [require$$0.jsx(WidgetStylesheet, { theme: config === null || config === void 0 ? void 0 : config.theme }), !headerHidden && (require$$0.jsx(ChatHeader, { accountStatus: chatState.accountStatus, refreshOnClick: handleRestartClick, minimizeOnClick: poppedOut ? handlePopOutDismiss : handleMinimizeClick, cancelOnClick: handleCancelClick, agent: widgetAgent, canRefresh: canRefresh, canMinimize: poppedOut || canMinimize, canCancel: canCancel, config: config === null || config === void 0 ? void 0 : config.header, menuConfig: config.menu, debugMode: (_8 = (_7 = (_6 = props.config) === null || _6 === void 0 ? void 0 : _6.connection) === null || _7 === void 0 ? void 0 : _7.mcp) === null || _8 === void 0 ? void 0 : _8.debugMode, onSubmit: handleOnSubmit })), require$$0.jsx(MessageList, { visible: visible, queuePosition: chatState.queuePosition, isChatting: chatState.isChatting, isOffline: isOffline, messages: messages, agents: chatState.agents, agent: config === null || config === void 0 ? void 0 : config.agent, lastRatingRequestTimestamp: chatState.lastRatingRequestTimestamp, hasRating: chatState.hasRating, visitorId: chatState.visitorId, hasWsButton: !!chatState.wsButton, messageMiddleware: props.messageMiddleware, textTypingStatusEnabled: (_10 = (_9 = props.config) === null || _9 === void 0 ? void 0 : _9.typingStatus) === null || _10 === void 0 ? void 0 : _10.textTypingStatusEnabled, disableAutoScroll: props.disableAutoScroll, onSend: handleSendMessage, onWrite: handleWriteMessage, onOpenUrl: handleOpenUrl, minimizeOnClick: handleMinimizeClick }), require$$0.jsx("div", { className: "xa-spinner-container ".concat(visible && connectionStatus === "pending" ? "visible" : ""), children: require$$0.jsx("div", { className: "xa-spinner" }) }), connectionStatus === "offline" && require$$0.jsx(ServerOffline, {}), chatState.wsButton && visible && (require$$0.jsx(WsButton, { button: chatState.wsButton, onPress: handleWsButtonPress })), require$$0.jsx(ChatFooter, { isAdmin: config === null || config === void 0 ? void 0 : config.isAdmin, isChatting: chatState.isChatting, placeholder: (_11 = config === null || config === void 0 ? void 0 : config.input) === null || _11 === void 0 ? void 0 : _11.placeholder, sendButtonIcon: (_13 = (_12 = config === null || config === void 0 ? void 0 : config.footer) === null || _12 === void 0 ? void 0 : _12.sendButton) === null || _13 === void 0 ? void 0 : _13.icon, sendButtonIconHover: (_15 = (_14 = config === null || config === void 0 ? void 0 : config.footer) === null || _14 === void 0 ? void 0 : _14.sendButton) === null || _15 === void 0 ? void 0 : _15.iconHover, sendButtonIconDisabled: (_17 = (_16 = config === null || config === void 0 ? void 0 : config.footer) === null || _16 === void 0 ? void 0 : _16.sendButton) === null || _17 === void 0 ? void 0 : _17.iconDisabled, visible: visible, hasWsButton: !!chatState.wsButton, menuConfig: menuConfig, footerConfig: (_18 = props.config) === null || _18 === void 0 ? void 0 : _18.footer, inputConfig: (_19 = props.config) === null || _19 === void 0 ? void 0 : _19.input, disabled: previewMode, onChange: handleOnChange, onSubmit: handleOnSubmit, onFileUpload: handleFileUpload }), require$$0.jsx("div", { className: "restartModal", ref: modalRef, onClick: handleRestartModalCloseClick, children: require$$0.jsx(ModalContent, { onClose: handleRestartModalCloseClick, onReset: handleReset }) })] }));
37925
37980
  return (require$$0.jsxs(require$$0.Fragment, { children: [popOutEnabled ? (
37926
37981
  // The anchor keeps the chat's place in the page, so the layout does not jump when
37927
37982
  // the chat pops out. The chat itself lives in popOutHost, which the effect above
37928
37983
  // moves between this anchor and the body.
37929
- require$$0.jsx("div", { className: "widget-anchor", ref: anchorRef, children: popOutHost && reactDom.createPortal(widgetContainer, popOutHost) })) : (widgetContainer), actionBarVisible && config.actionBar && !(collapsedOnMobile && mobileEntry === "button") ? (require$$0.jsxs(require$$0.Fragment, { children: [require$$0.jsx(ActionBar, { config: __assign$1(__assign$1({}, config.actionBar), {
37984
+ require$$0.jsx("div", { className: "widget-anchor", ref: anchorRef, children: popOutHost && reactDom.createPortal(widgetContainer, popOutHost) })) : (widgetContainer), actionBarVisible && actionBarConfig && !(collapsedOnMobile && mobileEntry === "button") ? (require$$0.jsxs(require$$0.Fragment, { children: [require$$0.jsx(ActionBar, { config: __assign$1(__assign$1({}, actionBarConfig), {
37930
37985
  // Use actionBar.cta if set, otherwise fall back to widget-level cta
37931
- cta: resolveCta((_17 = config.actionBar.cta) !== null && _17 !== void 0 ? _17 : config.cta, {
37986
+ cta: resolveCta((_20 = actionBarConfig.cta) !== null && _20 !== void 0 ? _20 : config.cta, {
37932
37987
  hasConversation: conversationOnScreen,
37933
37988
  arrivedViaChatLink: arrivedViaChatLink,
37934
- }) }), visible: collapsedOnMobile ? false : visible, chatDisabled: config.disabled, formDisabled: isFormPage, hasUserInteracted: (_18 = chatState.visuals) === null || _18 === void 0 ? void 0 : _18.hasInteracted, onChatClick: collapsedOnMobile ? handleMobileOpen : chatButtonOnClick, onChatMinimize: handleMinimizeClick, onFormClick: handleFormButtonClick, onCtaDismiss: handleCtaDismiss }), formModalOpen && activeFormConfig && (require$$0.jsx(FormModal, { config: activeFormConfig, widgetEnv: config, onClose: handleFormModalClose }))] })) : (require$$0.jsx(ChatButton, { addClass: visible && !collapsedOnMobile ? "visible" : "", onClick: collapsedOnMobile ? handleMobileOpen : chatButtonOnClick, config: chatButtonCta, imageUrl: (_19 = config === null || config === void 0 ? void 0 : config.chatButton) === null || _19 === void 0 ? void 0 : _19.imageUrl, visible: collapsedOnMobile ? false : visible, hasInteracted: (_20 = chatState.visuals) === null || _20 === void 0 ? void 0 : _20.hasInteracted, onCtaDismiss: handleCtaDismiss })), require$$0.jsx(ErrorOverlay, { enableErrorOverlay: config === null || config === void 0 ? void 0 : config.enableErrorOverlay })] }));
37989
+ }) }), visible: collapsedOnMobile ? false : visible, chatDisabled: config.disabled, formDisabled: isFormPage, hasUserInteracted: (_21 = chatState.visuals) === null || _21 === void 0 ? void 0 : _21.hasInteracted, onChatClick: collapsedOnMobile ? handleMobileOpen : chatButtonOnClick, onChatMinimize: handleMinimizeClick, onFormClick: handleFormButtonClick, onCtaDismiss: handleCtaDismiss }), formModalOpen && activeFormConfig && (require$$0.jsx(FormModal, { config: activeFormConfig, widgetEnv: config, onClose: handleFormModalClose }))] })) : (require$$0.jsx(ChatButton, { addClass: visible && !collapsedOnMobile ? "visible" : "", onClick: collapsedOnMobile ? handleMobileOpen : chatButtonOnClick, config: chatButtonCta, imageUrl: (_22 = config === null || config === void 0 ? void 0 : config.chatButton) === null || _22 === void 0 ? void 0 : _22.imageUrl, visible: collapsedOnMobile ? false : visible, hasInteracted: (_23 = chatState.visuals) === null || _23 === void 0 ? void 0 : _23.hasInteracted, onCtaDismiss: handleCtaDismiss })), require$$0.jsx(ErrorOverlay, { enableErrorOverlay: config === null || config === void 0 ? void 0 : config.enableErrorOverlay })] }));
37935
37990
  };
37936
37991
  /**
37937
37992
  * Top-level wrapper that dispatches between preview mode and connected mode.