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