@xapp/chat-widget 1.101.0 → 1.102.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/components/ChatTextMessage/ChatTextMessage.d.ts +0 -0
- package/dist/hooks/useArrivedViaChatLink.d.ts +0 -0
- package/dist/hooks/useOpenUrlCallback.d.ts +0 -0
- package/dist/index.css +1 -1
- package/dist/index.es.js +243 -42
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +243 -42
- package/dist/index.js.map +1 -1
- package/dist/utils/chatContinuation.d.ts +0 -0
- package/dist/utils/checkSessionExpiration.d.ts +0 -0
- package/dist/utils/isMobileViewport.d.ts +0 -0
- package/dist/xapp/ChatServer.d.ts +0 -0
- package/dist/xapp/MCPChat.d.ts +0 -0
- package/dist/xapp-chat-widget.css +1 -1
- package/dist/xapp-chat-widget.js +4 -4
- package/dist/xapp-chat-widget.js.map +1 -1
- package/package.json +1 -1
package/dist/index.es.js
CHANGED
|
@@ -919,6 +919,15 @@ var ActionBarCta = function (_a) {
|
|
|
919
919
|
* in `_mixins.scss`. Keep this in step with the `mobile()` mixin. XappMedia/chat-widget#1594.
|
|
920
920
|
*/
|
|
921
921
|
var MOBILE_MEDIA_QUERY = "(max-width: 640px), (max-height: 480px) and (pointer: coarse)";
|
|
922
|
+
/**
|
|
923
|
+
* Whether the chat panel renders full-screen right now. False where matchMedia is missing
|
|
924
|
+
* (jsdom), matching ActionBar's assume-desktop fallback.
|
|
925
|
+
*/
|
|
926
|
+
function isMobileViewport(win) {
|
|
927
|
+
if (win === void 0) { win = window; }
|
|
928
|
+
return typeof win.matchMedia === "function" && win.matchMedia(MOBILE_MEDIA_QUERY).matches;
|
|
929
|
+
}
|
|
930
|
+
|
|
922
931
|
/**
|
|
923
932
|
* ChatWidget.scss sizes the chat panel against this variable. Nothing used to set it, so it always
|
|
924
933
|
* fell back to the hardcoded 80px in the SCSS - and the real mobile bar is taller than that once
|
|
@@ -2322,8 +2331,100 @@ function setVisualStatus(status) {
|
|
|
2322
2331
|
};
|
|
2323
2332
|
}
|
|
2324
2333
|
|
|
2325
|
-
|
|
2334
|
+
/**
|
|
2335
|
+
* Carries "this visitor just clicked a link in the chat" across a same-tab navigation.
|
|
2336
|
+
*
|
|
2337
|
+
* Persisted widget state already survives the page load, but it cannot tell a chat link
|
|
2338
|
+
* click from a visitor coming back to the site later. The param can; it is read on arrival
|
|
2339
|
+
* and stripped from the address bar so it is never bookmarked or shared.
|
|
2340
|
+
*/
|
|
2341
|
+
var CHAT_CONTINUE_PARAM = "xa_chat";
|
|
2342
|
+
var CHAT_CONTINUE_VALUE = "resume";
|
|
2343
|
+
var CONTINUE_CTA_MESSAGE = "Continue your conversation";
|
|
2344
|
+
/**
|
|
2345
|
+
* Marks a link the widget is about to follow in this tab. Only same-origin links: widget
|
|
2346
|
+
* state lives in this origin's localStorage, so the widget on any other origin - a
|
|
2347
|
+
* subdomain included - has no conversation to continue.
|
|
2348
|
+
*/
|
|
2349
|
+
function withChatContinueParam(url, currentOrigin) {
|
|
2350
|
+
try {
|
|
2351
|
+
var urlObj = new URL(url);
|
|
2352
|
+
if (urlObj.origin !== currentOrigin || urlObj.searchParams.get(CHAT_CONTINUE_PARAM) === CHAT_CONTINUE_VALUE) {
|
|
2353
|
+
return url;
|
|
2354
|
+
}
|
|
2355
|
+
urlObj.searchParams.set(CHAT_CONTINUE_PARAM, CHAT_CONTINUE_VALUE);
|
|
2356
|
+
return urlObj.toString();
|
|
2357
|
+
}
|
|
2358
|
+
catch (_) {
|
|
2359
|
+
return url;
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
/**
|
|
2363
|
+
* True when this page was reached through a chat link. Pure, so it is safe inside a
|
|
2364
|
+
* render-time initializer that React may call twice (StrictMode); strip the param
|
|
2365
|
+
* separately with `stripChatContinueParam`, from an effect.
|
|
2366
|
+
*/
|
|
2367
|
+
function hasChatContinueParam(win) {
|
|
2368
|
+
if (win === void 0) { win = window; }
|
|
2369
|
+
try {
|
|
2370
|
+
return new URL(win.location.href).searchParams.get(CHAT_CONTINUE_PARAM) === CHAT_CONTINUE_VALUE;
|
|
2371
|
+
}
|
|
2372
|
+
catch (_) {
|
|
2373
|
+
return false;
|
|
2374
|
+
}
|
|
2375
|
+
}
|
|
2376
|
+
/**
|
|
2377
|
+
* Removes the param from the address bar with replaceState, keeping the rest of the URL
|
|
2378
|
+
* and any SPA history state. A no-op when it is absent, so repeating it is harmless.
|
|
2379
|
+
*/
|
|
2380
|
+
function stripChatContinueParam(win) {
|
|
2381
|
+
if (win === void 0) { win = window; }
|
|
2382
|
+
try {
|
|
2383
|
+
var url = new URL(win.location.href);
|
|
2384
|
+
if (!url.searchParams.has(CHAT_CONTINUE_PARAM)) {
|
|
2385
|
+
return;
|
|
2386
|
+
}
|
|
2387
|
+
url.searchParams.delete(CHAT_CONTINUE_PARAM);
|
|
2388
|
+
win.history.replaceState(win.history.state, "", url.toString());
|
|
2389
|
+
}
|
|
2390
|
+
catch (_) {
|
|
2391
|
+
// An address bar we cannot rewrite only leaves the param visible.
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
/**
|
|
2395
|
+
* A conversation worth continuing: any message on screen. Join notices do not count - the
|
|
2396
|
+
* MCP connection posts one before the greeting, so a brand-new session has one too.
|
|
2397
|
+
*/
|
|
2398
|
+
function hasConversation(chats) {
|
|
2399
|
+
return !!(chats === null || chats === void 0 ? void 0 : chats.some(function (chat) { return chat.type === "chat.msg"; }));
|
|
2400
|
+
}
|
|
2401
|
+
/**
|
|
2402
|
+
* The CTA to show, given whether there is a conversation to go back to.
|
|
2403
|
+
*
|
|
2404
|
+
* With one, the first-visit copy ("Need Help? Click Here") becomes a prompt to continue,
|
|
2405
|
+
* keeping the tenant's timing and styling. A tenant with no CTA gets none on an ordinary
|
|
2406
|
+
* page load; right after a chat link it does, immediately - on mobile that prompt is the
|
|
2407
|
+
* only sign the conversation is still there, because the panel stays closed over the page
|
|
2408
|
+
* they asked for.
|
|
2409
|
+
*/
|
|
2410
|
+
function resolveCta(cta, options) {
|
|
2411
|
+
var arrivedViaChatLink = options.arrivedViaChatLink;
|
|
2412
|
+
if (!options.hasConversation || (!(cta === null || cta === void 0 ? void 0 : cta.message) && !arrivedViaChatLink)) {
|
|
2413
|
+
return cta;
|
|
2414
|
+
}
|
|
2415
|
+
var immediately = arrivedViaChatLink ? { delay: 0 } : {};
|
|
2416
|
+
var mobile = cta === null || cta === void 0 ? void 0 : cta.mobile;
|
|
2417
|
+
return __assign$1(__assign$1(__assign$1(__assign$1({}, cta), { message: CONTINUE_CTA_MESSAGE }), immediately), (mobile && {
|
|
2418
|
+
mobile: __assign$1(__assign$1(__assign$1({}, mobile), ((typeof mobile.message === "string" || arrivedViaChatLink) && { message: CONTINUE_CTA_MESSAGE })), immediately),
|
|
2419
|
+
}));
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2422
|
+
/**
|
|
2423
|
+
* @internal Exported for unit testing; use `useOpenUrlCallback`.
|
|
2424
|
+
*/
|
|
2425
|
+
function executeOpenUrl(url, dispatch, visuals, behavior, isMobile) {
|
|
2326
2426
|
var _a, _b;
|
|
2427
|
+
if (isMobile === void 0) { isMobile = isMobileViewport(); }
|
|
2327
2428
|
if (!dispatch || !visuals) {
|
|
2328
2429
|
throw new Error("OpenUrl: No dispatch and/or visual.");
|
|
2329
2430
|
}
|
|
@@ -2390,12 +2491,23 @@ function execute(url, dispatch, visuals, behavior) {
|
|
|
2390
2491
|
window.open(url, "_blank");
|
|
2391
2492
|
break;
|
|
2392
2493
|
case "sameWindow":
|
|
2393
|
-
|
|
2494
|
+
// Desktop: the next page reopens the chat beside the content, as before.
|
|
2495
|
+
// Mobile: the panel is full-screen, so reopening it would cover the page they
|
|
2496
|
+
// just asked for. Leave it closed; the param lets that page offer to continue.
|
|
2497
|
+
// Unconditional: callers can hand in a `visuals` captured before the chat opened
|
|
2498
|
+
// (ChatMarkdownMessage's click handler does), and skipping on a stale `opened`
|
|
2499
|
+
// leaves the persisted state open.
|
|
2500
|
+
if (isMobile) {
|
|
2501
|
+
dispatch(setVisualStatus({
|
|
2502
|
+
opened: false
|
|
2503
|
+
}));
|
|
2504
|
+
}
|
|
2505
|
+
else if (!visuals.opened) {
|
|
2394
2506
|
dispatch(setVisualStatus({
|
|
2395
2507
|
opened: true
|
|
2396
2508
|
}));
|
|
2397
2509
|
}
|
|
2398
|
-
window.open(url, "_self");
|
|
2510
|
+
window.open(withChatContinueParam(url, window.location.origin), "_self");
|
|
2399
2511
|
break;
|
|
2400
2512
|
default:
|
|
2401
2513
|
throwBadKind$1(type);
|
|
@@ -2407,7 +2519,7 @@ function useOpenUrlCallback() {
|
|
|
2407
2519
|
if (!behavior) {
|
|
2408
2520
|
behavior = (env === null || env === void 0 ? void 0 : env.urls) ? resolveUrlPolicy(url, env.urls) : defaultBehavior;
|
|
2409
2521
|
}
|
|
2410
|
-
|
|
2522
|
+
executeOpenUrl(url, dispatch, visuals, behavior);
|
|
2411
2523
|
}, [env]);
|
|
2412
2524
|
}
|
|
2413
2525
|
|
|
@@ -4300,6 +4412,10 @@ var MCPChat = /** @class */ (function () {
|
|
|
4300
4412
|
this.isInitializingSession = false;
|
|
4301
4413
|
// Session validation state (silent handshake - no stream dispatch)
|
|
4302
4414
|
this.isValidatingSession = false;
|
|
4415
|
+
// A conversation is on screen that the server should keep: restored from a previous page,
|
|
4416
|
+
// or confirmed by the server on this one. Sent as `continueConversation`, which also lets
|
|
4417
|
+
// the server resume a session idle longer than its default window.
|
|
4418
|
+
this.conversationOnScreen = false;
|
|
4303
4419
|
// Tool call tracking for debug mode
|
|
4304
4420
|
this.currentToolCalls = [];
|
|
4305
4421
|
this.isReplaying = false;
|
|
@@ -4387,8 +4503,12 @@ var MCPChat = /** @class */ (function () {
|
|
|
4387
4503
|
_a.label = 1;
|
|
4388
4504
|
case 1:
|
|
4389
4505
|
_a.trys.push([1, 3, 4, 5]);
|
|
4390
|
-
|
|
4506
|
+
// With a conversation on screen (e.g. after a same-tab link click), ask the
|
|
4507
|
+
// server to continue it: without the flag it clears the history and greets.
|
|
4508
|
+
return [4 /*yield*/, this.performSSERequest(this.buildRequestPayload(__assign$1({ resumeSession: true }, (this.conversationOnScreen && { continueConversation: true }))))];
|
|
4391
4509
|
case 2:
|
|
4510
|
+
// With a conversation on screen (e.g. after a same-tab link click), ask the
|
|
4511
|
+
// server to continue it: without the flag it clears the history and greets.
|
|
4392
4512
|
_a.sent();
|
|
4393
4513
|
log("Session validation complete");
|
|
4394
4514
|
return [3 /*break*/, 5];
|
|
@@ -5068,7 +5188,7 @@ var MCPChat = /** @class */ (function () {
|
|
|
5068
5188
|
return [2 /*return*/];
|
|
5069
5189
|
}
|
|
5070
5190
|
log("[attemptSSERequest] Sending message: \"".concat(messageText, "\", userId: ").concat(this._userId, ", sessionId: ").concat(this._sessionId));
|
|
5071
|
-
return [2 /*return*/, this.performSSERequest(this.buildRequestPayload({ message: messageText }))];
|
|
5191
|
+
return [2 /*return*/, this.performSSERequest(this.buildRequestPayload(__assign$1({ message: messageText }, (this.conversationOnScreen && { continueConversation: true }))))];
|
|
5072
5192
|
});
|
|
5073
5193
|
});
|
|
5074
5194
|
};
|
|
@@ -5150,6 +5270,7 @@ var MCPChat = /** @class */ (function () {
|
|
|
5150
5270
|
case "connected":
|
|
5151
5271
|
// Connection established
|
|
5152
5272
|
log("[SSE] Connected event received, messageId: ".concat(messageId, ", isReturningUser: ").concat(data.isReturningUser, ", isReplay: ").concat(data.isReplay));
|
|
5273
|
+
_this.conversationOnScreen = true;
|
|
5153
5274
|
// Track if this is a replay connection
|
|
5154
5275
|
if (data.isReplay) {
|
|
5155
5276
|
_this.isReplaying = true;
|
|
@@ -5185,10 +5306,27 @@ var MCPChat = /** @class */ (function () {
|
|
|
5185
5306
|
}
|
|
5186
5307
|
_this.startStreamThrottle(bubbleId);
|
|
5187
5308
|
break;
|
|
5309
|
+
case "session_resumed":
|
|
5310
|
+
// The server continued the conversation: no greeting follows,
|
|
5311
|
+
// so adopt the identity without opening a stream bubble.
|
|
5312
|
+
log("[SSE] Session resumed, sessionId: ".concat(data.sessionId, ", hasHistory: ").concat(data.hasHistory));
|
|
5313
|
+
_this.conversationOnScreen = true;
|
|
5314
|
+
if (data.userId) {
|
|
5315
|
+
_this._userId = data.userId;
|
|
5316
|
+
_this.userIdIsServerAssigned = true;
|
|
5317
|
+
}
|
|
5318
|
+
if (data.sessionId) {
|
|
5319
|
+
_this._sessionId = data.sessionId;
|
|
5320
|
+
_this.dispatch(setSessionId(_this._sessionId));
|
|
5321
|
+
}
|
|
5322
|
+
storeIdentity(data.userId, data.sessionId);
|
|
5323
|
+
_this.stopTyping();
|
|
5324
|
+
break;
|
|
5188
5325
|
case "session_expired":
|
|
5189
5326
|
// Session no longer exists on server - need to start fresh
|
|
5190
5327
|
log("[SSE] Session expired: ".concat(data.reason || "Session not found"));
|
|
5191
5328
|
// Clear stored session (and lastSeq) but keep userId for returning user identification
|
|
5329
|
+
_this.conversationOnScreen = false;
|
|
5192
5330
|
clearStoredSession(_this._sessionId);
|
|
5193
5331
|
_this._sessionId = "";
|
|
5194
5332
|
_this.lastSeq = undefined;
|
|
@@ -5439,6 +5577,7 @@ var MCPChat = /** @class */ (function () {
|
|
|
5439
5577
|
MCPChat.prototype.setVisitorInfo = function (visitorInfo, sessionId, cb) {
|
|
5440
5578
|
this.visitorInfo = visitorInfo;
|
|
5441
5579
|
this._attributes = __assign$1({}, this.visitorInfo.attributes);
|
|
5580
|
+
this.conversationOnScreen = visitorInfo.hasConversation === true;
|
|
5442
5581
|
// do not set currentUrl if localhost
|
|
5443
5582
|
var href = new URL(window.location.href);
|
|
5444
5583
|
if (href === null || href === void 0 ? void 0 : href.host.toLowerCase().startsWith("localhost")) {
|
|
@@ -12953,7 +13092,21 @@ var ChatTextMessage = function (props) {
|
|
|
12953
13092
|
var wasStreamed = message.wasStreamed;
|
|
12954
13093
|
var showTail = agentMessage && !props.sibling && !isStreaming && !wasStreamed;
|
|
12955
13094
|
var hasToolCalls = props.toolCalls && props.toolCalls.length > 0;
|
|
12956
|
-
|
|
13095
|
+
// Delegated, because the links are inside dangerouslySetInnerHTML. Modified clicks stay
|
|
13096
|
+
// with the browser so ctrl/cmd-click still opens a background tab.
|
|
13097
|
+
var onLinkClick = props.onLinkClick;
|
|
13098
|
+
var handleContentClick = function (ev) {
|
|
13099
|
+
var _a, _b;
|
|
13100
|
+
if (!onLinkClick || ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) {
|
|
13101
|
+
return;
|
|
13102
|
+
}
|
|
13103
|
+
var link = (_b = (_a = ev.target).closest) === null || _b === void 0 ? void 0 : _b.call(_a, "a");
|
|
13104
|
+
if (link && ev.currentTarget.contains(link)) {
|
|
13105
|
+
ev.preventDefault();
|
|
13106
|
+
onLinkClick(link.href);
|
|
13107
|
+
}
|
|
13108
|
+
};
|
|
13109
|
+
return (jsxs("div", { className: "chat-msg".concat(isStreaming ? " chat-msg--streaming" : ""), children: [hasToolCalls && (jsx(ToolCallDisplay, { toolCalls: props.toolCalls, debugMode: props.debugMode })), jsx("div", { className: "chat-text-container", children: jsxs(ChatMessageBubble, { owner: agentMessage ? "others" : "mine", hasTail: showTail, children: [jsx("span", { className: "message-sr-only", children: "at " + time + (agentMessage ? (isStreaming ? " the bot is typing" : " the bot said") : " the user said") }), parsedHtml ? (jsx("span", { className: "chat-msg__content", onClick: handleContentClick, dangerouslySetInnerHTML: { __html: parsedHtml } })) : (jsx("span", { className: "chat-msg__content", children: message.msg.text })), isStreaming && jsx("span", { className: "streaming-cursor", "aria-hidden": "true" })] }) })] }));
|
|
12957
13110
|
};
|
|
12958
13111
|
|
|
12959
13112
|
var ChatScheduleWidget = function (props) {
|
|
@@ -12996,14 +13149,21 @@ var avaKeys = ["text", "html", "card", "list"];
|
|
|
12996
13149
|
* @returns
|
|
12997
13150
|
*/
|
|
12998
13151
|
var ChatMessage = function (props) {
|
|
12999
|
-
var _a, _b, _c;
|
|
13152
|
+
var _a, _b, _c, _d;
|
|
13000
13153
|
var middleware = props.messageMiddleware || StandardMiddlewares;
|
|
13001
13154
|
var chatConfig = useContext(ChatConfigContext);
|
|
13002
13155
|
// console.log(`########### chatConfig: ${JSON.stringify(chatConfig, null, 2)}`);
|
|
13003
13156
|
var ctx = props.middlewareContext;
|
|
13004
13157
|
var user = props.agent;
|
|
13158
|
+
var chatDispatch = useChatDispatch();
|
|
13159
|
+
var visuals = useSelector(function (state) { return state.visuals; });
|
|
13160
|
+
// Text links follow the tenant's URL policy when it set one (utm_source, same-tab
|
|
13161
|
+
// continuation, ...). Tenants without one keep the plain new-tab anchor.
|
|
13162
|
+
var handleTextLinkClick = ((_a = chatConfig === null || chatConfig === void 0 ? void 0 : chatConfig.env) === null || _a === void 0 ? void 0 : _a.urls) && (ctx === null || ctx === void 0 ? void 0 : ctx.openUrl)
|
|
13163
|
+
? function (href) { return ctx.openUrl(href, chatDispatch, visuals); }
|
|
13164
|
+
: undefined;
|
|
13005
13165
|
// Get hideUserInfo from the agent info based on the message sender's nick
|
|
13006
|
-
var agentInfo = (
|
|
13166
|
+
var agentInfo = (_b = props.agents) === null || _b === void 0 ? void 0 : _b[props.message.user.nick];
|
|
13007
13167
|
var hideUserInfo = (agentInfo === null || agentInfo === void 0 ? void 0 : agentInfo.hideUserInfo) || false;
|
|
13008
13168
|
function renderByType() {
|
|
13009
13169
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
|
|
@@ -13022,7 +13182,7 @@ var ChatMessage = function (props) {
|
|
|
13022
13182
|
var debugMode = (_d = (_c = (_b = (_a = chatConfig === null || chatConfig === void 0 ? void 0 : chatConfig.env) === null || _a === void 0 ? void 0 : _a.connection) === null || _b === void 0 ? void 0 : _b.mcp) === null || _c === void 0 ? void 0 : _c.debugMode) !== null && _d !== void 0 ? _d : false;
|
|
13023
13183
|
// Show tool progress with avatar when no text yet, or above text when text exists
|
|
13024
13184
|
var showToolProgressWithAvatar = hasToolCalls && !msg.text;
|
|
13025
|
-
return (jsxs(Fragment, { children: [showToolProgressWithAvatar && (jsx(ChatMessagePart, { showAvatar: true, user: user, avatarPosition: (_g = (_f = (_e = chatConfig === null || chatConfig === void 0 ? void 0 : chatConfig.env) === null || _e === void 0 ? void 0 : _e.theme) === null || _f === void 0 ? void 0 : _f.messages) === null || _g === void 0 ? void 0 : _g.avatarPosition, hideUserInfo: hideUserInfo, isStreaming: true, children: jsx("div", { className: "chat-msg", children: jsx(ToolCallDisplay, { toolCalls: toolCalls, debugMode: debugMode }) }) })), msg.text && (jsx(ChatMessagePart, { showAvatar: avaKey === "text", user: user, avatarPosition: (_k = (_j = (_h = chatConfig === null || chatConfig === void 0 ? void 0 : chatConfig.env) === null || _h === void 0 ? void 0 : _h.theme) === null || _j === void 0 ? void 0 : _j.messages) === null || _k === void 0 ? void 0 : _k.avatarPosition, hideUserInfo: hideUserInfo, isStreaming: isStreaming_1 || wasStreamed, children: jsx(ChatTextMessage, { message: props.message, sibling: props.sibling, user: user, toolCalls: hasToolCalls ? toolCalls : undefined, debugMode: debugMode }) })), msg.html &&
|
|
13185
|
+
return (jsxs(Fragment, { children: [showToolProgressWithAvatar && (jsx(ChatMessagePart, { showAvatar: true, user: user, avatarPosition: (_g = (_f = (_e = chatConfig === null || chatConfig === void 0 ? void 0 : chatConfig.env) === null || _e === void 0 ? void 0 : _e.theme) === null || _f === void 0 ? void 0 : _f.messages) === null || _g === void 0 ? void 0 : _g.avatarPosition, hideUserInfo: hideUserInfo, isStreaming: true, children: jsx("div", { className: "chat-msg", children: jsx(ToolCallDisplay, { toolCalls: toolCalls, debugMode: debugMode }) }) })), msg.text && (jsx(ChatMessagePart, { showAvatar: avaKey === "text", user: user, avatarPosition: (_k = (_j = (_h = chatConfig === null || chatConfig === void 0 ? void 0 : chatConfig.env) === null || _h === void 0 ? void 0 : _h.theme) === null || _j === void 0 ? void 0 : _j.messages) === null || _k === void 0 ? void 0 : _k.avatarPosition, hideUserInfo: hideUserInfo, isStreaming: isStreaming_1 || wasStreamed, children: jsx(ChatTextMessage, { message: props.message, sibling: props.sibling, user: user, toolCalls: hasToolCalls ? toolCalls : undefined, debugMode: debugMode, onLinkClick: handleTextLinkClick }) })), msg.html &&
|
|
13026
13186
|
jsx(ChatMessagePart, { showAvatar: avaKey === "html", user: user, avatarPosition: (_o = (_m = (_l = chatConfig === null || chatConfig === void 0 ? void 0 : chatConfig.env) === null || _l === void 0 ? void 0 : _l.theme) === null || _m === void 0 ? void 0 : _m.messages) === null || _o === void 0 ? void 0 : _o.avatarPosition, hideUserInfo: hideUserInfo, children: jsx(ChatMarkdownMessage, { message: props.message, sibling: props.sibling, onOpenUrl: (_p = props.middlewareContext) === null || _p === void 0 ? void 0 : _p.openUrl }) }), msg.displays && middleware && msg.displays.map(function (display, index) {
|
|
13027
13187
|
if (display.type === "ScheduleButton") {
|
|
13028
13188
|
return (jsx(ChatScheduleWidget, { minimizeOnClick: props.minimizeOnClick, display: display }, index));
|
|
@@ -13046,7 +13206,7 @@ var ChatMessage = function (props) {
|
|
|
13046
13206
|
}
|
|
13047
13207
|
// Don't show timestamp/sources while streaming or if message has no content yet
|
|
13048
13208
|
var isStreaming = props.message.isStreaming;
|
|
13049
|
-
var hasContent = !!(((
|
|
13209
|
+
var hasContent = !!(((_c = props.message.msg) === null || _c === void 0 ? void 0 : _c.text) || ((_d = props.message.msg) === null || _d === void 0 ? void 0 : _d.html));
|
|
13050
13210
|
var showFooter = !isStreaming && hasContent;
|
|
13051
13211
|
// empty
|
|
13052
13212
|
return (jsx("div", { className: "chat-msg-container-wrapper ".concat(isAgent(props.message.user.nick) ? "agent" : "visitor", " ").concat(props.sibling ? "sibling" : ""), children: jsxs("div", { className: "chat-msg-container ".concat(getClassName(props.message)), children: [renderByType(), showFooter && renderTimestampAndSources()] }) }));
|
|
@@ -13056,9 +13216,6 @@ var ChatMessage = function (props) {
|
|
|
13056
13216
|
* value must be like "60m", "2h" etc.
|
|
13057
13217
|
*/
|
|
13058
13218
|
function convertToSeconds(s) {
|
|
13059
|
-
if (!s) {
|
|
13060
|
-
return 0;
|
|
13061
|
-
}
|
|
13062
13219
|
var groups = s.match(/(\d+)([dhms]?)/);
|
|
13063
13220
|
if (!groups) {
|
|
13064
13221
|
err("Invalid time string: ".concat(s));
|
|
@@ -13081,15 +13238,50 @@ function convertToSeconds(s) {
|
|
|
13081
13238
|
}
|
|
13082
13239
|
}
|
|
13083
13240
|
|
|
13084
|
-
|
|
13085
|
-
|
|
13086
|
-
|
|
13087
|
-
|
|
13241
|
+
/**
|
|
13242
|
+
* How long a conversation must sit idle, on top of the calendar day changing, before it
|
|
13243
|
+
* expires when the widget config does not say.
|
|
13244
|
+
*/
|
|
13245
|
+
var DEFAULT_SESSION_EXPIRATION = "4h";
|
|
13246
|
+
function isSameLocalDay(a, b) {
|
|
13247
|
+
var dayA = new Date(a);
|
|
13248
|
+
var dayB = new Date(b);
|
|
13249
|
+
return dayA.getFullYear() === dayB.getFullYear()
|
|
13250
|
+
&& dayA.getMonth() === dayB.getMonth()
|
|
13251
|
+
&& dayA.getDate() === dayB.getDate();
|
|
13252
|
+
}
|
|
13253
|
+
/**
|
|
13254
|
+
* A conversation expires only when BOTH the visitor's local calendar day has changed since
|
|
13255
|
+
* the last message AND at least `duration` has passed. So a visitor who browses the site
|
|
13256
|
+
* over an afternoon keeps one conversation, and one chatting at 11pm is not greeted afresh
|
|
13257
|
+
* at 1am - but tomorrow's visit starts clean.
|
|
13258
|
+
*/
|
|
13259
|
+
function checkSessionExpiration(duration, lastMessageTimestamp, _lastTimestamp, now) {
|
|
13260
|
+
if (now === void 0) { now = Date.now(); }
|
|
13261
|
+
if (!lastMessageTimestamp) {
|
|
13088
13262
|
return false;
|
|
13089
13263
|
}
|
|
13090
|
-
|
|
13091
|
-
|
|
13092
|
-
|
|
13264
|
+
var durationMs = convertToSeconds(duration || DEFAULT_SESSION_EXPIRATION) * 1000;
|
|
13265
|
+
return now - lastMessageTimestamp >= durationMs && !isSameLocalDay(lastMessageTimestamp, now);
|
|
13266
|
+
}
|
|
13267
|
+
|
|
13268
|
+
/**
|
|
13269
|
+
* Whether this page was reached through a same-tab link in the chat, read once on mount.
|
|
13270
|
+
*
|
|
13271
|
+
* The read is pure and the strip runs in an effect. StrictMode calls state initializers
|
|
13272
|
+
* twice in development, and React 18 (still allowed by our peer range) keeps the second
|
|
13273
|
+
* result - which an initializer that also stripped the param would get wrong. React 19
|
|
13274
|
+
* reuses the first result, so the test suite here cannot show that failure.
|
|
13275
|
+
*/
|
|
13276
|
+
function useArrivedViaChatLink(disabled) {
|
|
13277
|
+
if (disabled === void 0) { disabled = false; }
|
|
13278
|
+
var arrived = useState(function () { return !disabled && hasChatContinueParam(); })[0];
|
|
13279
|
+
useEffect(function () {
|
|
13280
|
+
if (arrived) {
|
|
13281
|
+
stripChatContinueParam();
|
|
13282
|
+
}
|
|
13283
|
+
}, [arrived]);
|
|
13284
|
+
return arrived;
|
|
13093
13285
|
}
|
|
13094
13286
|
|
|
13095
13287
|
function useExternalScript(url) {
|
|
@@ -13210,6 +13402,7 @@ function useGreeting(active) {
|
|
|
13210
13402
|
// Handle null context (e.g., in preview mode)
|
|
13211
13403
|
var isAdmin = (_b = (_a = ctx === null || ctx === void 0 ? void 0 : ctx.env) === null || _a === void 0 ? void 0 : _a.isAdmin) !== null && _b !== void 0 ? _b : false;
|
|
13212
13404
|
var sessionId = useSelector(function (state) { return state.sessionId; });
|
|
13405
|
+
var conversationOnScreen = useSelector(function (state) { return hasConversation(state.chats); });
|
|
13213
13406
|
useEffect(function () {
|
|
13214
13407
|
if (active) {
|
|
13215
13408
|
if (snapshotRef.current !== curr) {
|
|
@@ -13220,7 +13413,8 @@ function useGreeting(active) {
|
|
|
13220
13413
|
visitorId: curr.visitorId,
|
|
13221
13414
|
userId: curr.userId,
|
|
13222
13415
|
accessToken: curr.accessToken,
|
|
13223
|
-
attributes: __assign$1(__assign$1({}, curr.attributes), { currentUrl: window.location.href })
|
|
13416
|
+
attributes: __assign$1(__assign$1({}, curr.attributes), { currentUrl: window.location.href }),
|
|
13417
|
+
hasConversation: conversationOnScreen
|
|
13224
13418
|
}, sessionId));
|
|
13225
13419
|
if (!isAdmin) {
|
|
13226
13420
|
var timeoutId_1 = setTimeout(function () {
|
|
@@ -37076,7 +37270,7 @@ var ChatWidgetConnected = function (props) {
|
|
|
37076
37270
|
* Exported for use by StaticChatWidgetContainer to avoid infinite loops.
|
|
37077
37271
|
*/
|
|
37078
37272
|
var ChatWidgetUI = function (props) {
|
|
37079
|
-
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;
|
|
37273
|
+
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;
|
|
37080
37274
|
var innerDispatch = useChatDispatch();
|
|
37081
37275
|
var dispatch = useChatServerDispatch();
|
|
37082
37276
|
// Publish the visual viewport's geometry so the panel can size around the soft keyboard.
|
|
@@ -37097,14 +37291,18 @@ var ChatWidgetUI = function (props) {
|
|
|
37097
37291
|
// Preview mode can be enabled via `preview` prop (recommended) or legacy `mode="preview"`
|
|
37098
37292
|
var previewMode = props.preview === true || mode === "preview";
|
|
37099
37293
|
var modeClass = "widget-container--".concat(mode);
|
|
37100
|
-
|
|
37294
|
+
// Reached this page through a link in the chat.
|
|
37295
|
+
var arrivedViaChatLink = useArrivedViaChatLink(previewMode);
|
|
37296
|
+
var conversationOnScreen = hasConversation(chatState.chats);
|
|
37297
|
+
var chatButtonCta = useMemo(function () { var _a; return resolveCta((_a = props.config) === null || _a === void 0 ? void 0 : _a.cta, { hasConversation: conversationOnScreen, arrivedViaChatLink: arrivedViaChatLink }); }, [(_b = props.config) === null || _b === void 0 ? void 0 : _b.cta, conversationOnScreen, arrivedViaChatLink]);
|
|
37298
|
+
var canRefresh = (_d = (_c = props.config.header) === null || _c === void 0 ? void 0 : _c.actions) === null || _d === void 0 ? void 0 : _d.refresh;
|
|
37101
37299
|
// can't minimize in docked mode, static mode, or preview mode.
|
|
37102
|
-
var canMinimize = !dockedMode && !staticMode && !previewMode && ((
|
|
37103
|
-
log("docked: ".concat(dockedMode, " static: ").concat(staticMode, " preview: ").concat(previewMode, " minimized: ").concat((
|
|
37300
|
+
var canMinimize = !dockedMode && !staticMode && !previewMode && ((_f = (_e = props.config.header) === null || _e === void 0 ? void 0 : _e.actions) === null || _f === void 0 ? void 0 : _f.minimize);
|
|
37301
|
+
log("docked: ".concat(dockedMode, " static: ").concat(staticMode, " preview: ").concat(previewMode, " minimized: ").concat((_h = (_g = props.config.header) === null || _g === void 0 ? void 0 : _g.actions) === null || _h === void 0 ? void 0 : _h.minimize));
|
|
37104
37302
|
var canCancel;
|
|
37105
37303
|
// To preserve legacy behavior, cancel needs a little more checks
|
|
37106
|
-
if (typeof ((
|
|
37107
|
-
canCancel = !dockedMode && !staticMode && !previewMode && ((
|
|
37304
|
+
if (typeof ((_k = (_j = props.config.header) === null || _j === void 0 ? void 0 : _j.actions) === null || _k === void 0 ? void 0 : _k.cancel) === "boolean") {
|
|
37305
|
+
canCancel = !dockedMode && !staticMode && !previewMode && ((_m = (_l = props.config.header) === null || _l === void 0 ? void 0 : _l.actions) === null || _m === void 0 ? void 0 : _m.cancel);
|
|
37108
37306
|
}
|
|
37109
37307
|
else {
|
|
37110
37308
|
canCancel = !dockedMode && !staticMode && !previewMode;
|
|
@@ -37115,16 +37313,16 @@ var ChatWidgetUI = function (props) {
|
|
|
37115
37313
|
}
|
|
37116
37314
|
// Our state - pull from storage
|
|
37117
37315
|
// Parse autoOpenOnWidth once for use in both initial state and useEffect
|
|
37118
|
-
var autoOpenOnWidthValue = ((
|
|
37316
|
+
var autoOpenOnWidthValue = ((_o = props.config) === null || _o === void 0 ? void 0 : _o.autoOpenOnWidth)
|
|
37119
37317
|
? parseInt(props.config.autoOpenOnWidth, 10)
|
|
37120
37318
|
: NaN;
|
|
37121
37319
|
var autoOpenOnWidthMatch = !isNaN(autoOpenOnWidthValue) && window.innerWidth >= autoOpenOnWidthValue;
|
|
37122
37320
|
// When autoOpenOnWidth is configured, use width check instead of persisted visible state
|
|
37123
37321
|
// to prevent greeting requests on viewports below the threshold
|
|
37124
|
-
var
|
|
37322
|
+
var _15 = useState((!canMinimize && !canCancel) ||
|
|
37125
37323
|
// !!get("visible") ||
|
|
37126
|
-
(!isNaN(autoOpenOnWidthValue) ? autoOpenOnWidthMatch : chatState.visuals.visible)), visible =
|
|
37127
|
-
var
|
|
37324
|
+
(!isNaN(autoOpenOnWidthValue) ? autoOpenOnWidthMatch : chatState.visuals.visible)), visible = _15[0], setVisibleState = _15[1];
|
|
37325
|
+
var _16 = useState(false); _16[0]; var setTypingState = _16[1]; // false initially - state kept for potential external observers
|
|
37128
37326
|
// Ref to track typing state for use in timeout callbacks
|
|
37129
37327
|
var typingRef = useRef(false);
|
|
37130
37328
|
// Timeout ref for debouncing "stop typing" events
|
|
@@ -37132,11 +37330,11 @@ var ChatWidgetUI = function (props) {
|
|
|
37132
37330
|
// Track whether autoOpenOnWidth has already fired so it doesn't re-open after user closes
|
|
37133
37331
|
var autoOpenFiredRef = useRef(autoOpenOnWidthMatch);
|
|
37134
37332
|
var chatServer = useContext(ChatServerContext);
|
|
37135
|
-
var patternsConfig = (
|
|
37333
|
+
var patternsConfig = (_q = (_p = props.config) === null || _p === void 0 ? void 0 : _p.autoOpenOnPattern) === null || _q === void 0 ? void 0 : _q.patterns;
|
|
37136
37334
|
var currentUrl = window.location.href;
|
|
37137
37335
|
var patternExist = patternsConfig && patternsConfig.length > 0;
|
|
37138
37336
|
var patternMatches = patternsConfig === null || patternsConfig === void 0 ? void 0 : patternsConfig.some(function (pattern) { return currentUrl.includes(pattern); });
|
|
37139
|
-
var configWidth = (
|
|
37337
|
+
var configWidth = (_s = (_r = props.config) === null || _r === void 0 ? void 0 : _r.autoOpenOnPattern) === null || _s === void 0 ? void 0 : _s.minimumWidth;
|
|
37140
37338
|
var currentWidth = window.innerWidth;
|
|
37141
37339
|
var setVisible = useCallback(function (newVisible) {
|
|
37142
37340
|
if (staticMode || previewMode) {
|
|
@@ -37171,7 +37369,7 @@ var ChatWidgetUI = function (props) {
|
|
|
37171
37369
|
};
|
|
37172
37370
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
37173
37371
|
}, []);
|
|
37174
|
-
var
|
|
37372
|
+
var _17 = useState(!document.hidden), isTabVisible = _17[0], setIsTabVisible = _17[1];
|
|
37175
37373
|
useEffect(function () {
|
|
37176
37374
|
var handleVisibilityChange = function () {
|
|
37177
37375
|
setIsTabVisible(!document.hidden);
|
|
@@ -37395,8 +37593,8 @@ var ChatWidgetUI = function (props) {
|
|
|
37395
37593
|
}));
|
|
37396
37594
|
}
|
|
37397
37595
|
// Action Bar state and handlers
|
|
37398
|
-
var
|
|
37399
|
-
var
|
|
37596
|
+
var _18 = useState(false), formModalOpen = _18[0], setFormModalOpen = _18[1];
|
|
37597
|
+
var _19 = useState(null), activeFormConfig = _19[0], setActiveFormConfig = _19[1];
|
|
37400
37598
|
var handleFormButtonClick = useCallback(function (formButton) {
|
|
37401
37599
|
// If form-widget is configured, use its openForm API
|
|
37402
37600
|
// The form-widget script should already be pre-loaded
|
|
@@ -37463,7 +37661,7 @@ var ChatWidgetUI = function (props) {
|
|
|
37463
37661
|
var messages = chatState && chatState.chats;
|
|
37464
37662
|
log("Rendering - accountStatus: \"".concat(chatState.accountStatus, "\", connectionStatus: \"").concat(chatState.connection.connectionStatus, "\""));
|
|
37465
37663
|
var config = props.config, onConnectionStatusChange = props.onConnectionStatusChange;
|
|
37466
|
-
var actionBarEnabled = ((
|
|
37664
|
+
var actionBarEnabled = ((_t = config === null || config === void 0 ? void 0 : config.actionBar) === null || _t === void 0 ? void 0 : _t.enabled) === true;
|
|
37467
37665
|
// The host page declares this; the widget cannot work it out. On the standalone
|
|
37468
37666
|
// contact/booking page the form already *is* the page, which makes every button that
|
|
37469
37667
|
// only opens one redundant. XappMedia/chat-widget#1626.
|
|
@@ -37487,7 +37685,7 @@ var ChatWidgetUI = function (props) {
|
|
|
37487
37685
|
onConnectionStatusChange(connectionStatus);
|
|
37488
37686
|
}
|
|
37489
37687
|
}, [connectionStatus, onConnectionStatusChange]);
|
|
37490
|
-
useExternalScript((
|
|
37688
|
+
useExternalScript((_u = props.config) === null || _u === void 0 ? void 0 : _u.middlewareUrl);
|
|
37491
37689
|
// Only reach for form.xapp.ai when something on this page can still open the form.
|
|
37492
37690
|
//
|
|
37493
37691
|
// Loading it is not free: the script mounts a second form-widget instance whose wrapper
|
|
@@ -37527,15 +37725,18 @@ var ChatWidgetUI = function (props) {
|
|
|
37527
37725
|
};
|
|
37528
37726
|
}, [actionBarVisible]);
|
|
37529
37727
|
// This is a pseudo agent. It represent's the widget (shown in the header avatar for instance)
|
|
37530
|
-
var widgetAgent = ((
|
|
37728
|
+
var widgetAgent = ((_v = chatState.agents["agent:robot"]) === null || _v === void 0 ? void 0 : _v.user) ||
|
|
37531
37729
|
(config === null || config === void 0 ? void 0 : config.agent) || {
|
|
37532
37730
|
nick: "agent:robot",
|
|
37533
37731
|
avatarPath: config.avatarUrl,
|
|
37534
37732
|
display_name: "Agent",
|
|
37535
37733
|
};
|
|
37536
|
-
return (jsxs(Fragment, { children: [jsxs("div", { className: "widget-container ".concat(modeClass, " ").concat(getVisibilityClass()).concat(actionBarVisible ? " widget-container--with-action-bar" : ""), children: [jsx(WidgetStylesheet, { theme: config === null || config === void 0 ? void 0 : config.theme }), 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: (
|
|
37734
|
+
return (jsxs(Fragment, { children: [jsxs("div", { className: "widget-container ".concat(modeClass, " ").concat(getVisibilityClass()).concat(actionBarVisible ? " widget-container--with-action-bar" : ""), children: [jsx(WidgetStylesheet, { theme: config === null || config === void 0 ? void 0 : config.theme }), 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: (_y = (_x = (_w = props.config) === null || _w === void 0 ? void 0 : _w.connection) === null || _x === void 0 ? void 0 : _x.mcp) === null || _y === void 0 ? void 0 : _y.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: (_0 = (_z = props.config) === null || _z === void 0 ? void 0 : _z.typingStatus) === null || _0 === void 0 ? void 0 : _0.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: (_1 = config === null || config === void 0 ? void 0 : config.input) === null || _1 === void 0 ? void 0 : _1.placeholder, sendButtonIcon: (_3 = (_2 = config === null || config === void 0 ? void 0 : config.footer) === null || _2 === void 0 ? void 0 : _2.sendButton) === null || _3 === void 0 ? void 0 : _3.icon, sendButtonIconHover: (_5 = (_4 = config === null || config === void 0 ? void 0 : config.footer) === null || _4 === void 0 ? void 0 : _4.sendButton) === null || _5 === void 0 ? void 0 : _5.iconHover, sendButtonIconDisabled: (_7 = (_6 = config === null || config === void 0 ? void 0 : config.footer) === null || _6 === void 0 ? void 0 : _6.sendButton) === null || _7 === void 0 ? void 0 : _7.iconDisabled, visible: visible, hasWsButton: !!chatState.wsButton, menuConfig: (_8 = props.config) === null || _8 === void 0 ? void 0 : _8.menu, footerConfig: (_9 = props.config) === null || _9 === void 0 ? void 0 : _9.footer, inputConfig: (_10 = props.config) === null || _10 === void 0 ? void 0 : _10.input, disabled: previewMode, onChange: handleOnChange, onSubmit: handleOnSubmit, onFileUpload: handleFileUpload }), jsx("div", { className: "restartModal", ref: modalRef, onClick: handleRestartModalCloseClick, children: jsx(ModalContent, { onClose: handleRestartModalCloseClick, onReset: handleReset }) })] }), actionBarVisible && config.actionBar ? (jsxs(Fragment, { children: [jsx(ActionBar, { config: __assign$1(__assign$1({}, config.actionBar), {
|
|
37537
37735
|
// Use actionBar.cta if set, otherwise fall back to widget-level cta
|
|
37538
|
-
cta: (
|
|
37736
|
+
cta: resolveCta((_11 = config.actionBar.cta) !== null && _11 !== void 0 ? _11 : config.cta, {
|
|
37737
|
+
hasConversation: conversationOnScreen,
|
|
37738
|
+
arrivedViaChatLink: arrivedViaChatLink,
|
|
37739
|
+
}) }), visible: visible, chatDisabled: config.disabled, formDisabled: isFormPage, hasUserInteracted: (_12 = chatState.visuals) === null || _12 === void 0 ? void 0 : _12.hasInteracted, onChatClick: chatButtonOnClick, onChatMinimize: handleMinimizeClick, onFormClick: handleFormButtonClick, onCtaDismiss: handleCtaDismiss }), formModalOpen && activeFormConfig && (jsx(FormModal, { config: activeFormConfig, widgetEnv: config, onClose: handleFormModalClose }))] })) : (jsx(ChatButton, { addClass: visible ? "visible" : "", onClick: chatButtonOnClick, config: chatButtonCta, imageUrl: (_13 = config === null || config === void 0 ? void 0 : config.chatButton) === null || _13 === void 0 ? void 0 : _13.imageUrl, visible: visible, hasInteracted: (_14 = chatState.visuals) === null || _14 === void 0 ? void 0 : _14.hasInteracted, onCtaDismiss: handleCtaDismiss })), jsx(ErrorOverlay, { enableErrorOverlay: config === null || config === void 0 ? void 0 : config.enableErrorOverlay })] }));
|
|
37539
37740
|
};
|
|
37540
37741
|
/**
|
|
37541
37742
|
* Top-level wrapper that dispatches between preview mode and connected mode.
|