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