@xapp/chat-widget 1.96.4 → 1.97.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
@@ -321,13 +321,14 @@ const color_1 = color;
321
321
  /** Fallback when a crawl found no branding. Deliberately neutral. */
322
322
  const DEFAULT_PRIMARY = "#1AA7DC";
323
323
  function buildChatWidgetChannel(props) {
324
+ var _a;
324
325
  const { appId, apiUrl, mcpUrl, formWidgetKey, phoneNumber } = props;
326
+ const businessName = (_a = props.businessName) === null || _a === void 0 ? void 0 : _a.trim();
325
327
  // Only a colour whose contrast can actually be computed, so the background
326
328
  // and the text on it are always a matched pair rather than one scraped
327
329
  // value and one guess.
328
330
  const primaryColor = (0, color_1.usableColorOr)(props.primaryColor, DEFAULT_PRIMARY);
329
- return {
330
- type: "chat-widget",
331
+ return Object.assign({ type: "chat-widget",
331
332
  // MCP by default. The widget runs on a public page, so homeowner mode
332
333
  // — which is unauthenticated and carries the appId in the url — is the
333
334
  // only one that can work there; business_owner needs a token, and a
@@ -338,10 +339,7 @@ function buildChatWidgetChannel(props) {
338
339
  mcp: {
339
340
  mode: "homeowner"
340
341
  }
341
- },
342
- theme: buildChatTheme(primaryColor, props.secondaryColor),
343
- actionBar: buildActionBar({ apiUrl, formWidgetKey, phoneNumber })
344
- };
342
+ }, theme: buildChatTheme(primaryColor, props.secondaryColor), actionBar: buildActionBar({ apiUrl, formWidgetKey, phoneNumber }) }, (businessName ? { botName: businessName } : {}));
345
343
  }
346
344
  /**
347
345
  * The dock of buttons that replaces the plain chat bubble.
@@ -933,9 +931,39 @@ var MOBILE_MEDIA_QUERY = "(max-width: 640px), (max-height: 480px) and (pointer:
933
931
  * Two concurrent bars would fight over it, and either one unmounting would clear it.
934
932
  */
935
933
  var ACTION_BAR_HEIGHT_VAR = "--xapp-action-bar-height";
934
+ /**
935
+ * Whether pressing this button ends up in the form widget.
936
+ *
937
+ * `schedule` is the same form-widget infrastructure with a calendar icon, so it is the
938
+ * same button as far as this question goes. XappMedia/chat-widget#1626.
939
+ */
940
+ function opensFormWidget(button) {
941
+ return lib.isActionBarFormButton(button) || lib.isActionBarScheduleButton(button);
942
+ }
943
+ /**
944
+ * Which of a bar's configured buttons would actually render.
945
+ *
946
+ * Exported because the decision is needed in two places: here, to render, and in
947
+ * `ChatWidget`, to know whether the bar has anything left to show and whether anything on
948
+ * the page can still open the form widget. Two copies of this predicate would drift.
949
+ */
950
+ function visibleActionBarButtons(buttons, options) {
951
+ if (buttons === void 0) { buttons = []; }
952
+ if (options === void 0) { options = {}; }
953
+ var chatDisabled = options.chatDisabled, formDisabled = options.formDisabled;
954
+ return buttons.filter(function (button) {
955
+ if (chatDisabled && lib.isActionBarChatButton(button)) {
956
+ return false;
957
+ }
958
+ if (formDisabled && opensFormWidget(button)) {
959
+ return false;
960
+ }
961
+ return true;
962
+ });
963
+ }
936
964
  var ActionBar = function (_a) {
937
965
  var _b, _c;
938
- var config = _a.config, visible = _a.visible, chatDisabled = _a.chatDisabled, _d = _a.hasUserInteracted, hasUserInteracted = _d === void 0 ? false : _d, onChatClick = _a.onChatClick, onChatMinimize = _a.onChatMinimize, onFormClick = _a.onFormClick, onCtaDismiss = _a.onCtaDismiss;
966
+ var config = _a.config, visible = _a.visible, chatDisabled = _a.chatDisabled, formDisabled = _a.formDisabled, _d = _a.hasUserInteracted, hasUserInteracted = _d === void 0 ? false : _d, onChatClick = _a.onChatClick, onChatMinimize = _a.onChatMinimize, onFormClick = _a.onFormClick, onCtaDismiss = _a.onCtaDismiss;
939
967
  var _e = React.useState(false), isMobile = _e[0], setIsMobile = _e[1];
940
968
  // An explicit override can only be a width; without one we ask the same question the
941
969
  // stylesheets ask, so the two cannot disagree. A survey of production on 2026-08-19
@@ -1006,10 +1034,9 @@ var ActionBar = function (_a) {
1006
1034
  document.documentElement.style.removeProperty(ACTION_BAR_HEIGHT_VAR);
1007
1035
  };
1008
1036
  }, [isMobile]);
1009
- // Filter out chat buttons if chat is disabled
1010
- var filteredButtons = chatDisabled
1011
- ? config.buttons.filter(function (button) { return !lib.isActionBarChatButton(button); })
1012
- : config.buttons;
1037
+ // Drop the buttons this page has no use for - chat when chat is disabled, form/schedule
1038
+ // when the form already is the page.
1039
+ var filteredButtons = visibleActionBarButtons(config.buttons, { chatDisabled: chatDisabled, formDisabled: formDisabled });
1013
1040
  // Build class names - ActionBar stays visible even when chat is open
1014
1041
  var classNames = [
1015
1042
  "xapp-action-bar",
@@ -1022,7 +1049,7 @@ var ActionBar = function (_a) {
1022
1049
  // - Must have CTA config with a message
1023
1050
  // - Chat must not be visible (user hasn't opened it)
1024
1051
  // - Chat must not be disabled
1025
- var hasChatButton = config.buttons.some(lib.isActionBarChatButton);
1052
+ var hasChatButton = filteredButtons.some(lib.isActionBarChatButton);
1026
1053
  var showCta = ((_c = config.cta) === null || _c === void 0 ? void 0 : _c.message) && !visible && !chatDisabled && hasChatButton;
1027
1054
  var handleCtaClick = function () {
1028
1055
  onChatClick();
@@ -1030,6 +1057,13 @@ var ActionBar = function (_a) {
1030
1057
  var handleCtaDismiss = function () {
1031
1058
  onCtaDismiss === null || onCtaDismiss === void 0 ? void 0 : onCtaDismiss();
1032
1059
  };
1060
+ // A bar with nothing in it is a bare strip of chrome the visitor cannot use. The bar
1061
+ // renders on `actionBar.enabled` alone, so an App configured with only Form + Schedule
1062
+ // filters down to zero on a form page - render nothing rather than an empty shell.
1063
+ // This is after the hooks on purpose; bailing earlier would change the hook order.
1064
+ if (filteredButtons.length === 0) {
1065
+ return null;
1066
+ }
1033
1067
  return (require$$0.jsxs("div", { ref: barRef, className: classNames, role: "toolbar", "aria-label": "Chat actions", children: [showCta && (require$$0.jsx("div", { className: "xapp-action-bar-cta-wrapper", children: require$$0.jsx(ActionBarCta, { config: config.cta, isMobile: isMobile, onClick: handleCtaClick, onDismiss: handleCtaDismiss, hasUserInteracted: hasUserInteracted }) })), require$$0.jsx("div", { className: "xapp-action-bar__buttons", children: filteredButtons.map(function (button) { return (require$$0.jsx(ActionBarButton, { button: button, chatActive: visible, onChatClick: onChatClick, onChatMinimize: onChatMinimize, onFormClick: onFormClick, isMobile: isMobile }, button.id)); }) })] }));
1034
1068
  };
1035
1069
 
@@ -12798,6 +12832,64 @@ function getSrcId(url) {
12798
12832
  return res;
12799
12833
  }
12800
12834
 
12835
+ /*! Copyright (c) 2026, XAPPmedia */
12836
+ /**
12837
+ * Body attribute a host page sets to declare "a form widget already *is* this page".
12838
+ *
12839
+ * The standalone contact/booking page (contact.xapp.ai, book.xapp.ai, sche.dule.pro)
12840
+ * renders a docked form-widget as its whole content and then injects the chat widget on
12841
+ * top of it. Without a signal, the injected widget cheerfully offers two more ways into
12842
+ * the same form - an ActionBar Contact/Book button and the form-widget's own side button.
12843
+ *
12844
+ * This is a declaration, not a detection: the page knows what it renders, the widget does
12845
+ * not. Sniffing the DOM for a docked form would be guesswork, and it would also fire on
12846
+ * pages that legitimately embed a form *and* want a separate one from the bar.
12847
+ *
12848
+ * The writer is `@xapp/form-widget`'s `useFormPageMarker` (used by `@xapp/contact-app`).
12849
+ * The two packages ship independently, so this string is the contract between them -
12850
+ * the same arrangement `[data-xapp-form-side-button]` already uses. Do not rename it on
12851
+ * one side only. XappMedia/chat-widget#1626.
12852
+ */
12853
+ var FORM_PAGE_ATTRIBUTE = "data-xapp-form-page";
12854
+ /**
12855
+ * Whether a form widget is already the page, per the marker above.
12856
+ */
12857
+ function isFormPage() {
12858
+ if (typeof document === "undefined" || !document.body) {
12859
+ return false;
12860
+ }
12861
+ return document.body.getAttribute(FORM_PAGE_ATTRIBUTE) === "true";
12862
+ }
12863
+ /**
12864
+ * Reactive form of {@link isFormPage}.
12865
+ *
12866
+ * The marker and the chat widget mount independently - the widget arrives from a
12867
+ * `<script>` tag, so it can win or lose the race against the page's own React tree.
12868
+ * Observing the attribute rather than reading it once means the filter settles correctly
12869
+ * whichever order they land in.
12870
+ */
12871
+ function useIsFormPage() {
12872
+ var _a = React.useState(isFormPage), formPage = _a[0], setFormPage = _a[1];
12873
+ React.useEffect(function () {
12874
+ if (typeof document === "undefined" || !document.body) {
12875
+ return undefined;
12876
+ }
12877
+ // Re-read on mount: the marker may have appeared between the initial state and here.
12878
+ setFormPage(isFormPage());
12879
+ // jsdom has MutationObserver, but a customer's browser might not - an old browser
12880
+ // that reads the marker once is still better than one that throws on mount.
12881
+ if (typeof MutationObserver === "undefined") {
12882
+ return undefined;
12883
+ }
12884
+ var observer = new MutationObserver(function () {
12885
+ setFormPage(isFormPage());
12886
+ });
12887
+ observer.observe(document.body, { attributes: true, attributeFilter: [FORM_PAGE_ATTRIBUTE] });
12888
+ return function () { return observer.disconnect(); };
12889
+ }, []);
12890
+ return formPage;
12891
+ }
12892
+
12801
12893
  function useChatServerVisitorId() {
12802
12894
  var dispatch = useChatServerDispatch();
12803
12895
  var clientVisitorId = reactRedux.useSelector(function (state) {
@@ -37042,6 +37134,21 @@ var ChatWidgetUI = function (props) {
37042
37134
  log("Rendering - accountStatus: \"".concat(chatState.accountStatus, "\", connectionStatus: \"").concat(chatState.connection.connectionStatus, "\""));
37043
37135
  var config = props.config, onConnectionStatusChange = props.onConnectionStatusChange;
37044
37136
  var actionBarEnabled = ((_s = config === null || config === void 0 ? void 0 : config.actionBar) === null || _s === void 0 ? void 0 : _s.enabled) === true;
37137
+ // The host page declares this; the widget cannot work it out. On the standalone
37138
+ // contact/booking page the form already *is* the page, which makes every button that
37139
+ // only opens one redundant. XappMedia/chat-widget#1626.
37140
+ var isFormPage = useIsFormPage();
37141
+ var actionBarButtons = React.useMemo(function () {
37142
+ return actionBarEnabled && (config === null || config === void 0 ? void 0 : config.actionBar)
37143
+ ? visibleActionBarButtons(config.actionBar.buttons, {
37144
+ chatDisabled: config.disabled,
37145
+ formDisabled: isFormPage,
37146
+ })
37147
+ : [];
37148
+ }, [actionBarEnabled, config === null || config === void 0 ? void 0 : config.actionBar, config === null || config === void 0 ? void 0 : config.disabled, isFormPage]);
37149
+ // A bar that filters down to nothing is a bare strip of chrome. Treat it as if the bar
37150
+ // were off, so the visitor still gets the plain chat button rather than an empty shell.
37151
+ var actionBarVisible = actionBarEnabled && actionBarButtons.length > 0;
37045
37152
  // Disable greeting in preview mode
37046
37153
  useGreeting(!isOffline && !props.preChatFormEnabled && visible && !previewMode);
37047
37154
  var connectionStatus = chatState.connection.connectionStatus;
@@ -37051,23 +37158,31 @@ var ChatWidgetUI = function (props) {
37051
37158
  }
37052
37159
  }, [connectionStatus, onConnectionStatusChange]);
37053
37160
  useExternalScript((_t = props.config) === null || _t === void 0 ? void 0 : _t.middlewareUrl);
37054
- // Pre-load form-widget script if formWidgetKey is provided
37161
+ // Only reach for form.xapp.ai when something on this page can still open the form.
37162
+ //
37163
+ // Loading it is not free: the script mounts a second form-widget instance whose wrapper
37164
+ // renders its own sticky "Contact Us" side button. On a page that already is the form
37165
+ // that button is a third way into the same thing, so not loading the script at all is
37166
+ // what actually removes it - hiding it in CSS only papers over the second instance.
37167
+ //
37168
+ // With no bar configured the side button IS the entry point, so that case still loads.
37169
+ var formWidgetNeeded = !isFormPage && (!actionBarEnabled || actionBarButtons.some(opensFormWidget));
37055
37170
  React.useEffect(function () {
37056
- if (config === null || config === void 0 ? void 0 : config.formWidgetKey) {
37171
+ if ((config === null || config === void 0 ? void 0 : config.formWidgetKey) && formWidgetNeeded) {
37057
37172
  preloadFormWidgetScript(config.formWidgetKey);
37058
37173
  }
37059
- }, [config === null || config === void 0 ? void 0 : config.formWidgetKey]);
37174
+ }, [config === null || config === void 0 ? void 0 : config.formWidgetKey, formWidgetNeeded]);
37060
37175
  // Set body attribute when actionBar is enabled (used by CSS to hide form-widget side button on mobile)
37061
37176
  // Uses reference counting to handle multiple widget instances
37062
37177
  React.useEffect(function () {
37063
- if (actionBarEnabled) {
37178
+ if (actionBarVisible) {
37064
37179
  // Increment reference count
37065
37180
  var currentCount = parseInt(document.body.getAttribute("data-xapp-action-bar-count") || "0", 10);
37066
37181
  document.body.setAttribute("data-xapp-action-bar-count", String(currentCount + 1));
37067
37182
  document.body.setAttribute("data-xapp-action-bar-enabled", "true");
37068
37183
  }
37069
37184
  return function () {
37070
- if (actionBarEnabled) {
37185
+ if (actionBarVisible) {
37071
37186
  // Decrement reference count
37072
37187
  var currentCount = parseInt(document.body.getAttribute("data-xapp-action-bar-count") || "0", 10);
37073
37188
  var newCount = Math.max(0, currentCount - 1);
@@ -37080,7 +37195,7 @@ var ChatWidgetUI = function (props) {
37080
37195
  }
37081
37196
  }
37082
37197
  };
37083
- }, [actionBarEnabled]);
37198
+ }, [actionBarVisible]);
37084
37199
  // This is a pseudo agent. It represent's the widget (shown in the header avatar for instance)
37085
37200
  var widgetAgent = ((_u = chatState.agents["agent:robot"]) === null || _u === void 0 ? void 0 : _u.user) ||
37086
37201
  (config === null || config === void 0 ? void 0 : config.agent) || {
@@ -37088,9 +37203,9 @@ var ChatWidgetUI = function (props) {
37088
37203
  avatarPath: config.avatarUrl,
37089
37204
  display_name: "Agent",
37090
37205
  };
37091
- return (require$$0.jsxs(require$$0.Fragment, { children: [require$$0.jsxs("div", { className: "widget-container ".concat(modeClass, " ").concat(getVisibilityClass()).concat(actionBarEnabled ? " widget-container--with-action-bar" : ""), children: [require$$0.jsx(WidgetStylesheet, { theme: config === null || config === void 0 ? void 0 : config.theme }), require$$0.jsx(ChatHeader, { accountStatus: chatState.accountStatus, refreshOnClick: handleRestartClick, minimizeOnClick: handleMinimizeClick, cancelOnClick: handleCancelClick, agent: widgetAgent, canRefresh: canRefresh, canMinimize: canMinimize, canCancel: canCancel, config: config === null || config === void 0 ? void 0 : config.header, menuConfig: config.menu, debugMode: (_x = (_w = (_v = props.config) === null || _v === void 0 ? void 0 : _v.connection) === null || _w === void 0 ? void 0 : _w.mcp) === null || _x === void 0 ? void 0 : _x.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: (_z = (_y = props.config) === null || _y === void 0 ? void 0 : _y.typingStatus) === null || _z === void 0 ? void 0 : _z.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: (_0 = config === null || config === void 0 ? void 0 : config.input) === null || _0 === void 0 ? void 0 : _0.placeholder, sendButtonIcon: (_2 = (_1 = config === null || config === void 0 ? void 0 : config.footer) === null || _1 === void 0 ? void 0 : _1.sendButton) === null || _2 === void 0 ? void 0 : _2.icon, sendButtonIconHover: (_4 = (_3 = config === null || config === void 0 ? void 0 : config.footer) === null || _3 === void 0 ? void 0 : _3.sendButton) === null || _4 === void 0 ? void 0 : _4.iconHover, sendButtonIconDisabled: (_6 = (_5 = config === null || config === void 0 ? void 0 : config.footer) === null || _5 === void 0 ? void 0 : _5.sendButton) === null || _6 === void 0 ? void 0 : _6.iconDisabled, visible: visible, hasWsButton: !!chatState.wsButton, menuConfig: (_7 = props.config) === null || _7 === void 0 ? void 0 : _7.menu, footerConfig: (_8 = props.config) === null || _8 === void 0 ? void 0 : _8.footer, inputConfig: (_9 = props.config) === null || _9 === void 0 ? void 0 : _9.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 }) })] }), actionBarEnabled && config.actionBar ? (require$$0.jsxs(require$$0.Fragment, { children: [require$$0.jsx(ActionBar, { config: __assign(__assign({}, config.actionBar), {
37206
+ return (require$$0.jsxs(require$$0.Fragment, { children: [require$$0.jsxs("div", { className: "widget-container ".concat(modeClass, " ").concat(getVisibilityClass()).concat(actionBarVisible ? " widget-container--with-action-bar" : ""), children: [require$$0.jsx(WidgetStylesheet, { theme: config === null || config === void 0 ? void 0 : config.theme }), require$$0.jsx(ChatHeader, { accountStatus: chatState.accountStatus, refreshOnClick: handleRestartClick, minimizeOnClick: handleMinimizeClick, cancelOnClick: handleCancelClick, agent: widgetAgent, canRefresh: canRefresh, canMinimize: canMinimize, canCancel: canCancel, config: config === null || config === void 0 ? void 0 : config.header, menuConfig: config.menu, debugMode: (_x = (_w = (_v = props.config) === null || _v === void 0 ? void 0 : _v.connection) === null || _w === void 0 ? void 0 : _w.mcp) === null || _x === void 0 ? void 0 : _x.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: (_z = (_y = props.config) === null || _y === void 0 ? void 0 : _y.typingStatus) === null || _z === void 0 ? void 0 : _z.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: (_0 = config === null || config === void 0 ? void 0 : config.input) === null || _0 === void 0 ? void 0 : _0.placeholder, sendButtonIcon: (_2 = (_1 = config === null || config === void 0 ? void 0 : config.footer) === null || _1 === void 0 ? void 0 : _1.sendButton) === null || _2 === void 0 ? void 0 : _2.icon, sendButtonIconHover: (_4 = (_3 = config === null || config === void 0 ? void 0 : config.footer) === null || _3 === void 0 ? void 0 : _3.sendButton) === null || _4 === void 0 ? void 0 : _4.iconHover, sendButtonIconDisabled: (_6 = (_5 = config === null || config === void 0 ? void 0 : config.footer) === null || _5 === void 0 ? void 0 : _5.sendButton) === null || _6 === void 0 ? void 0 : _6.iconDisabled, visible: visible, hasWsButton: !!chatState.wsButton, menuConfig: (_7 = props.config) === null || _7 === void 0 ? void 0 : _7.menu, footerConfig: (_8 = props.config) === null || _8 === void 0 ? void 0 : _8.footer, inputConfig: (_9 = props.config) === null || _9 === void 0 ? void 0 : _9.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 }) })] }), actionBarVisible && config.actionBar ? (require$$0.jsxs(require$$0.Fragment, { children: [require$$0.jsx(ActionBar, { config: __assign(__assign({}, config.actionBar), {
37092
37207
  // Use actionBar.cta if set, otherwise fall back to widget-level cta
37093
- cta: (_10 = config.actionBar.cta) !== null && _10 !== void 0 ? _10 : config.cta }), visible: visible, chatDisabled: config.disabled, hasUserInteracted: (_11 = chatState.visuals) === null || _11 === void 0 ? void 0 : _11.hasInteracted, onChatClick: 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 ? "visible" : "", onClick: chatButtonOnClick, config: config === null || config === void 0 ? void 0 : config.cta, imageUrl: (_12 = config === null || config === void 0 ? void 0 : config.chatButton) === null || _12 === void 0 ? void 0 : _12.imageUrl, visible: visible, hasInteracted: (_13 = chatState.visuals) === null || _13 === void 0 ? void 0 : _13.hasInteracted, onCtaDismiss: handleCtaDismiss })), require$$0.jsx(ErrorOverlay, { enableErrorOverlay: config === null || config === void 0 ? void 0 : config.enableErrorOverlay })] }));
37208
+ cta: (_10 = config.actionBar.cta) !== null && _10 !== void 0 ? _10 : config.cta }), visible: visible, chatDisabled: config.disabled, formDisabled: isFormPage, hasUserInteracted: (_11 = chatState.visuals) === null || _11 === void 0 ? void 0 : _11.hasInteracted, onChatClick: 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 ? "visible" : "", onClick: chatButtonOnClick, config: config === null || config === void 0 ? void 0 : config.cta, imageUrl: (_12 = config === null || config === void 0 ? void 0 : config.chatButton) === null || _12 === void 0 ? void 0 : _12.imageUrl, visible: visible, hasInteracted: (_13 = chatState.visuals) === null || _13 === void 0 ? void 0 : _13.hasInteracted, onCtaDismiss: handleCtaDismiss })), require$$0.jsx(ErrorOverlay, { enableErrorOverlay: config === null || config === void 0 ? void 0 : config.enableErrorOverlay })] }));
37094
37209
  };
37095
37210
  /**
37096
37211
  * Top-level wrapper that dispatches between preview mode and connected mode.