@xapp/chat-widget 1.84.2 → 1.85.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/index.js CHANGED
@@ -4,6 +4,384 @@ var require$$1 = require('react');
4
4
  var require$$0 = require('react/jsx-runtime');
5
5
  var reactRedux = require('react-redux');
6
6
 
7
+ var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
8
+
9
+ function getDefaultExportFromCjs (x) {
10
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
11
+ }
12
+
13
+ var lib = {};
14
+
15
+ var ActionBar$1 = {};
16
+
17
+ /*! Copyright (c) 2021, XAPPmedia */
18
+ Object.defineProperty(ActionBar$1, "__esModule", { value: true });
19
+ ActionBar$1.isActionBarChatButton = isActionBarChatButton;
20
+ ActionBar$1.isActionBarFormButton = isActionBarFormButton;
21
+ ActionBar$1.isActionBarScheduleButton = isActionBarScheduleButton;
22
+ ActionBar$1.isActionBarPhoneButton = isActionBarPhoneButton;
23
+ ActionBar$1.isActionBarUrlButton = isActionBarUrlButton;
24
+ ActionBar$1.isActionBarEmailButton = isActionBarEmailButton;
25
+ ActionBar$1.isActionBarHelpButton = isActionBarHelpButton;
26
+ ActionBar$1.isActionBarPaymentButton = isActionBarPaymentButton;
27
+ /**
28
+ * Type guard for ActionBarChatButton
29
+ */
30
+ function isActionBarChatButton(button) {
31
+ return button.type === "chat";
32
+ }
33
+ /**
34
+ * Type guard for ActionBarFormButton
35
+ */
36
+ function isActionBarFormButton(button) {
37
+ return button.type === "form";
38
+ }
39
+ /**
40
+ * Type guard for ActionBarScheduleButton
41
+ */
42
+ function isActionBarScheduleButton(button) {
43
+ return button.type === "schedule";
44
+ }
45
+ /**
46
+ * Type guard for ActionBarPhoneButton
47
+ */
48
+ function isActionBarPhoneButton(button) {
49
+ return button.type === "phone";
50
+ }
51
+ /**
52
+ * Type guard for ActionBarUrlButton
53
+ */
54
+ function isActionBarUrlButton(button) {
55
+ return button.type === "url";
56
+ }
57
+ /**
58
+ * Type guard for ActionBarEmailButton
59
+ */
60
+ function isActionBarEmailButton(button) {
61
+ return button.type === "email";
62
+ }
63
+ /**
64
+ * Type guard for ActionBarHelpButton
65
+ */
66
+ function isActionBarHelpButton(button) {
67
+ return button.type === "help";
68
+ }
69
+ /**
70
+ * Type guard for ActionBarPaymentButton
71
+ */
72
+ function isActionBarPaymentButton(button) {
73
+ return button.type === "payment";
74
+ }
75
+
76
+ var ChannelData = {};
77
+
78
+ Object.defineProperty(ChannelData, "__esModule", { value: true });
79
+
80
+ var Constants = {};
81
+
82
+ Object.defineProperty(Constants, "__esModule", { value: true });
83
+ Constants.CHAT_WIDGET_CHANNEL = void 0;
84
+ Constants.CHAT_WIDGET_CHANNEL = "chat-widget";
85
+
86
+ var Types = {};
87
+
88
+ /*! Copyright (c) 2021, XAPPmedia */
89
+ Object.defineProperty(Types, "__esModule", { value: true });
90
+
91
+ var UserInfo = {};
92
+
93
+ Object.defineProperty(UserInfo, "__esModule", { value: true });
94
+
95
+ var WidgetEnv = {};
96
+
97
+ Object.defineProperty(WidgetEnv, "__esModule", { value: true });
98
+
99
+ var WidgetTheme = {};
100
+
101
+ Object.defineProperty(WidgetTheme, "__esModule", { value: true });
102
+
103
+ var guards = {};
104
+
105
+ Object.defineProperty(guards, "__esModule", { value: true });
106
+ guards.isStaticImageMenuItem = isStaticImageMenuItem;
107
+ guards.isStaticTextMenuItem = isStaticTextMenuItem;
108
+ guards.isStandardMenuItem = isStandardMenuItem;
109
+ guards.isOpenURLMenuItem = isOpenURLMenuItem;
110
+ function isStaticImageMenuItem(item) {
111
+ return !!item && !!item.imageUrl;
112
+ }
113
+ function isStaticTextMenuItem(item) {
114
+ return !!item && !!item.body;
115
+ }
116
+ function isStandardMenuItem(item) {
117
+ return !!item && !!item.label;
118
+ }
119
+ function isOpenURLMenuItem(item) {
120
+ return !!item && !!item.url;
121
+ }
122
+
123
+ (function (exports$1) {
124
+ var __createBinding = (commonjsGlobal && commonjsGlobal.__createBinding) || (Object.create ? (function(o, m, k, k2) {
125
+ if (k2 === undefined) k2 = k;
126
+ var desc = Object.getOwnPropertyDescriptor(m, k);
127
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
128
+ desc = { enumerable: true, get: function() { return m[k]; } };
129
+ }
130
+ Object.defineProperty(o, k2, desc);
131
+ }) : (function(o, m, k, k2) {
132
+ if (k2 === undefined) k2 = k;
133
+ o[k2] = m[k];
134
+ }));
135
+ var __exportStar = (commonjsGlobal && commonjsGlobal.__exportStar) || function(m, exports$1) {
136
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports$1, p)) __createBinding(exports$1, m, p);
137
+ };
138
+ Object.defineProperty(exports$1, "__esModule", { value: true });
139
+ /*! Copyright (c) 2021, XAPPmedia */
140
+ __exportStar(ActionBar$1, exports$1);
141
+ __exportStar(ChannelData, exports$1);
142
+ __exportStar(Constants, exports$1);
143
+ __exportStar(Types, exports$1);
144
+ __exportStar(UserInfo, exports$1);
145
+ __exportStar(WidgetEnv, exports$1);
146
+ __exportStar(WidgetTheme, exports$1);
147
+ __exportStar(guards, exports$1);
148
+
149
+ } (lib));
150
+
151
+ var ChatIcon = function () { return (require$$0.jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "currentColor", children: require$$0.jsx("path", { d: "M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z" }) })); };
152
+ var FormIcon = function () { return (require$$0.jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "currentColor", children: require$$0.jsx("path", { d: "M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zM6 20V4h7v5h5v11H6zm2-6h8v2H8v-2zm0-3h8v2H8v-2zm0 6h5v2H8v-2z" }) })); };
153
+ var ScheduleIcon = function () { return (require$$0.jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "currentColor", children: require$$0.jsx("path", { d: "M19 4h-1V2h-2v2H8V2H6v2H5c-1.11 0-1.99.9-1.99 2L3 20c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V10h14v10zm0-12H5V6h14v2zM9 14H7v-2h2v2zm4 0h-2v-2h2v2zm4 0h-2v-2h2v2zm-8 4H7v-2h2v2zm4 0h-2v-2h2v2zm4 0h-2v-2h2v2z" }) })); };
154
+ var PhoneIcon = function () { return (require$$0.jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "currentColor", children: require$$0.jsx("path", { d: "M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27.67-.36 1.02-.24 1.12.37 2.33.57 3.57.57.55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17 0-.55.45-1 1-1h3.5c.55 0 1 .45 1 1 0 1.25.2 2.45.57 3.57.11.35.03.74-.25 1.02l-2.2 2.2z" }) })); };
155
+ var LinkIcon = function () { return (require$$0.jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "currentColor", children: require$$0.jsx("path", { d: "M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z" }) })); };
156
+ var EmailIcon = function () { return (require$$0.jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "currentColor", children: require$$0.jsx("path", { d: "M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z" }) })); };
157
+ var HelpIcon = function () { return (require$$0.jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "currentColor", children: require$$0.jsx("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z" }) })); };
158
+ var PaymentIcon = function () { return (require$$0.jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "currentColor", children: require$$0.jsx("path", { d: "M20 4H4c-1.11 0-1.99.89-1.99 2L2 18c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6c0-1.11-.89-2-2-2zm0 14H4v-6h16v6zm0-10H4V6h16v2z" }) })); };
159
+ var iconComponents = {
160
+ chat: ChatIcon,
161
+ form: FormIcon,
162
+ schedule: ScheduleIcon,
163
+ phone: PhoneIcon,
164
+ url: LinkIcon,
165
+ email: EmailIcon,
166
+ help: HelpIcon,
167
+ payment: PaymentIcon,
168
+ };
169
+ var ActionBarIcon = function (_a) {
170
+ var type = _a.type, icon = _a.icon, iconUrl = _a.iconUrl, className = _a.className;
171
+ if (iconUrl) {
172
+ return (require$$0.jsx("div", { className: className, children: require$$0.jsx("img", { src: iconUrl, alt: "".concat(icon || type, " icon") }) }));
173
+ }
174
+ // Use explicit icon override if provided, otherwise fall back to type
175
+ var iconType = icon || type;
176
+ var IconComponent = iconComponents[iconType];
177
+ return (require$$0.jsx("div", { className: className, children: require$$0.jsx(IconComponent, {}) }));
178
+ };
179
+
180
+ /**
181
+ * Allowed URL protocols for security. Prevents javascript: and other potentially dangerous protocols.
182
+ */
183
+ var ALLOWED_PROTOCOLS = ["http:", "https:", "tel:", "mailto:"];
184
+ /**
185
+ * Validates a URL to ensure it uses an allowed protocol.
186
+ * Returns true if the URL is safe to navigate to.
187
+ */
188
+ function isValidUrl(url) {
189
+ try {
190
+ var parsed = new URL(url, window.location.href);
191
+ return ALLOWED_PROTOCOLS.includes(parsed.protocol);
192
+ }
193
+ catch (_a) {
194
+ // Invalid URL
195
+ return false;
196
+ }
197
+ }
198
+ /**
199
+ * Safely navigates to a URL after validation.
200
+ * Returns false if the URL was invalid and navigation was blocked.
201
+ */
202
+ function safeNavigate(url, target, windowOptions) {
203
+ if (!isValidUrl(url)) {
204
+ console.warn("[ActionBar] Blocked navigation to unsafe URL:", url);
205
+ return false;
206
+ }
207
+ if (target === "sameWindow") {
208
+ window.location.href = url;
209
+ }
210
+ else if (target === "newWindow" && windowOptions) {
211
+ var features = "width=".concat(windowOptions.width || 600, ",height=").concat(windowOptions.height || 400);
212
+ window.open(url, "_blank", features);
213
+ }
214
+ else {
215
+ // newTab (default)
216
+ window.open(url, "_blank", "noopener,noreferrer");
217
+ }
218
+ return true;
219
+ }
220
+ var defaultLabels = {
221
+ chat: "Chat",
222
+ form: "Contact",
223
+ schedule: "Book",
224
+ phone: "Call",
225
+ url: "Link",
226
+ email: "Email",
227
+ help: "Help",
228
+ payment: "Pay",
229
+ };
230
+ var ActionBarButton = function (_a) {
231
+ var _b;
232
+ var button = _a.button, chatActive = _a.chatActive, onChatClick = _a.onChatClick, onChatMinimize = _a.onChatMinimize, onFormClick = _a.onFormClick, isMobile = _a.isMobile;
233
+ var handleClick = require$$1.useCallback(function () {
234
+ if (lib.isActionBarChatButton(button)) {
235
+ // Toggle chat: if active, minimize; if not active, open
236
+ if (chatActive) {
237
+ onChatMinimize();
238
+ }
239
+ else {
240
+ onChatClick();
241
+ }
242
+ }
243
+ else if (lib.isActionBarFormButton(button) || lib.isActionBarScheduleButton(button)) {
244
+ // Both form and schedule buttons open the form widget modal
245
+ onFormClick(button);
246
+ }
247
+ else if (lib.isActionBarPhoneButton(button)) {
248
+ // Phone: Use tel: link
249
+ var phoneNumber = button.phoneNumber.replace(/\s/g, "");
250
+ window.location.href = "tel:".concat(phoneNumber);
251
+ }
252
+ else if (lib.isActionBarUrlButton(button)) {
253
+ // URL: Open according to target (with validation)
254
+ var url = button.url, _a = button.target, target = _a === void 0 ? "newTab" : _a, windowOptions = button.windowOptions;
255
+ safeNavigate(url, target, windowOptions);
256
+ }
257
+ else if (lib.isActionBarEmailButton(button)) {
258
+ // Email: Build mailto: link with optional subject and body
259
+ var emailAddress = button.emailAddress, subject = button.subject, body = button.body;
260
+ var mailto = "mailto:".concat(emailAddress);
261
+ var params = [];
262
+ if (subject) {
263
+ params.push("subject=".concat(encodeURIComponent(subject)));
264
+ }
265
+ if (body) {
266
+ params.push("body=".concat(encodeURIComponent(body)));
267
+ }
268
+ if (params.length > 0) {
269
+ mailto += "?".concat(params.join("&"));
270
+ }
271
+ window.location.href = mailto;
272
+ }
273
+ else if (lib.isActionBarHelpButton(button)) {
274
+ // Help: Open URL according to target (with validation)
275
+ var url = button.url, _b = button.target, target = _b === void 0 ? "newTab" : _b;
276
+ safeNavigate(url, target);
277
+ }
278
+ else if (lib.isActionBarPaymentButton(button)) {
279
+ // Payment: Open URL according to target (with validation)
280
+ var url = button.url, _c = button.target, target = _c === void 0 ? "newTab" : _c;
281
+ safeNavigate(url, target);
282
+ }
283
+ }, [button, chatActive, onChatClick, onChatMinimize, onFormClick]);
284
+ // Handle visibility
285
+ var visibility = button.visibility || "all";
286
+ if (visibility === "mobile-only" && !isMobile) {
287
+ return null;
288
+ }
289
+ if (visibility === "desktop-only" && isMobile) {
290
+ return null;
291
+ }
292
+ var ariaLabel = button.label || defaultLabels[button.type] || button.type;
293
+ var tabIndex = (_b = button.tabIndex) !== null && _b !== void 0 ? _b : 0;
294
+ // Chat button shows active state when chat is open
295
+ var isActive = lib.isActionBarChatButton(button) && chatActive;
296
+ var buttonClassName = [
297
+ "xapp-action-bar__button",
298
+ isActive ? "xapp-action-bar__button--active" : "",
299
+ ].filter(Boolean).join(" ");
300
+ return (require$$0.jsxs("button", { className: buttonClassName, onClick: handleClick, "aria-label": ariaLabel, "aria-pressed": isActive, tabIndex: tabIndex, children: [require$$0.jsx(ActionBarIcon, { type: button.type, icon: button.icon, iconUrl: button.iconUrl, className: "xapp-action-bar__icon" }), button.label && (require$$0.jsx("span", { className: "xapp-action-bar__label", children: button.label }))] }));
301
+ };
302
+
303
+ var DEFAULT_MOBILE_BREAKPOINT = 768;
304
+ var RESIZE_DEBOUNCE_MS = 150;
305
+ var ActionBar = function (_a) {
306
+ var _b, _c;
307
+ var config = _a.config, visible = _a.visible, chatDisabled = _a.chatDisabled, onChatClick = _a.onChatClick, onChatMinimize = _a.onChatMinimize, onFormClick = _a.onFormClick;
308
+ var _d = require$$1.useState(false), isMobile = _d[0], setIsMobile = _d[1];
309
+ var mobileBreakpoint = (_b = config.mobileBreakpoint) !== null && _b !== void 0 ? _b : DEFAULT_MOBILE_BREAKPOINT;
310
+ var position = (_c = config.position) !== null && _c !== void 0 ? _c : "right";
311
+ var resizeTimeoutRef = require$$1.useRef(null);
312
+ // Debounced resize handler
313
+ var handleResize = require$$1.useCallback(function () {
314
+ if (resizeTimeoutRef.current) {
315
+ window.clearTimeout(resizeTimeoutRef.current);
316
+ }
317
+ resizeTimeoutRef.current = window.setTimeout(function () {
318
+ setIsMobile(window.innerWidth <= mobileBreakpoint);
319
+ }, RESIZE_DEBOUNCE_MS);
320
+ }, [mobileBreakpoint]);
321
+ require$$1.useEffect(function () {
322
+ // Initial check (immediate, not debounced)
323
+ setIsMobile(window.innerWidth <= mobileBreakpoint);
324
+ window.addEventListener("resize", handleResize);
325
+ return function () {
326
+ window.removeEventListener("resize", handleResize);
327
+ if (resizeTimeoutRef.current) {
328
+ window.clearTimeout(resizeTimeoutRef.current);
329
+ }
330
+ };
331
+ }, [mobileBreakpoint, handleResize]);
332
+ // Filter out chat buttons if chat is disabled
333
+ var filteredButtons = chatDisabled
334
+ ? config.buttons.filter(function (button) { return !lib.isActionBarChatButton(button); })
335
+ : config.buttons;
336
+ // Build class names - ActionBar stays visible even when chat is open
337
+ var classNames = [
338
+ "xapp-action-bar",
339
+ "xapp-action-bar--".concat(position),
340
+ isMobile ? "xapp-action-bar--mobile" : "xapp-action-bar--desktop",
341
+ ]
342
+ .filter(Boolean)
343
+ .join(" ");
344
+ return (require$$0.jsx("div", { className: classNames, role: "toolbar", "aria-label": "Chat actions", children: filteredButtons.map(function (button) { return (require$$0.jsx(ActionBarButton, { button: button, chatActive: visible, onChatClick: onChatClick, onChatMinimize: onChatMinimize, onFormClick: onFormClick, isMobile: isMobile }, button.id)); }) }));
345
+ };
346
+
347
+ /**
348
+ * ActionBarPreview is a simplified version of ActionBar designed for use in Studio
349
+ * configuration UI. It renders a visual preview of the action bar without requiring
350
+ * interactive callbacks.
351
+ *
352
+ * This component provides no-op handlers for all button interactions, making it
353
+ * suitable for configuration previews where you want to show how the action bar
354
+ * will look without actual functionality.
355
+ *
356
+ * @example
357
+ * ```tsx
358
+ * <ActionBarPreview
359
+ * config={{
360
+ * enabled: true,
361
+ * buttons: [
362
+ * { id: '1', type: 'chat', label: 'Chat', icon: 'chat' },
363
+ * { id: '2', type: 'phone', label: 'Call', icon: 'phone', phoneNumber: '555-1234' }
364
+ * ],
365
+ * position: 'right'
366
+ * }}
367
+ * />
368
+ * ```
369
+ */
370
+ var ActionBarPreview = function (_a) {
371
+ var config = _a.config, _b = _a.chatActive, chatActive = _b === void 0 ? false : _b;
372
+ // No-op handlers for preview mode
373
+ var handleChatClick = require$$1.useCallback(function () {
374
+ // Preview only - no action
375
+ }, []);
376
+ var handleChatMinimize = require$$1.useCallback(function () {
377
+ // Preview only - no action
378
+ }, []);
379
+ var handleFormClick = require$$1.useCallback(function (_button) {
380
+ // Preview only - no action
381
+ }, []);
382
+ return (require$$0.jsx(ActionBar, { config: config, visible: chatActive, chatDisabled: false, onChatClick: handleChatClick, onChatMinimize: handleChatMinimize, onFormClick: handleFormClick }));
383
+ };
384
+
7
385
  var ActionButton = function (_a) {
8
386
  var label = _a.label, disable = _a.disable, type = _a.type, addClass = _a.addClass, onClick = _a.onClick;
9
387
  function handleClick(ev) {
@@ -305,6 +683,193 @@ function getTimeAgo(date) {
305
683
  return date.toLocaleString();
306
684
  }
307
685
 
686
+ /*! Copyright (c) 2024, XAPPmedia */
687
+ var FORM_WIDGET_SCRIPT_ID = "xapp-form-js";
688
+ var FORM_WIDGET_BASE_URL = "https://form.xapp.ai/xapp-form-widget.js";
689
+ /** Default retry configuration */
690
+ var DEFAULT_MAX_RETRIES = 3;
691
+ var DEFAULT_RETRY_DELAY_MS = 1000;
692
+ /**
693
+ * Builds the form-widget script URL with the provided key
694
+ */
695
+ function getFormWidgetScriptUrl(key) {
696
+ return "".concat(FORM_WIDGET_BASE_URL, "?key=").concat(encodeURIComponent(key));
697
+ }
698
+ /**
699
+ * Checks if the form-widget script is already loaded on the page.
700
+ * Checks both by ID and by src URL to handle various installation methods.
701
+ */
702
+ function isFormWidgetScriptLoaded(key) {
703
+ // Check by standard form-widget ID
704
+ if (document.getElementById(FORM_WIDGET_SCRIPT_ID)) {
705
+ return true;
706
+ }
707
+ // Check by src URL if key is provided
708
+ if (key) {
709
+ var scriptUrl = getFormWidgetScriptUrl(key);
710
+ var scripts = document.querySelectorAll("script[src]");
711
+ for (var i = 0; i < scripts.length; i++) {
712
+ var src = scripts[i].getAttribute("src");
713
+ if (src && (src === scriptUrl || src.startsWith(FORM_WIDGET_BASE_URL))) {
714
+ return true;
715
+ }
716
+ }
717
+ }
718
+ return false;
719
+ }
720
+ /**
721
+ * Removes any failed script elements to allow retry
722
+ */
723
+ function removeFailedScript() {
724
+ var existingScript = document.getElementById(FORM_WIDGET_SCRIPT_ID);
725
+ if (existingScript) {
726
+ existingScript.remove();
727
+ }
728
+ }
729
+ /**
730
+ * Delays execution for a specified number of milliseconds
731
+ */
732
+ function delay(ms) {
733
+ return new Promise(function (resolve) { return setTimeout(resolve, ms); });
734
+ }
735
+ /**
736
+ * Attempts to load the script once
737
+ */
738
+ function attemptScriptLoad(key) {
739
+ return new Promise(function (resolve, reject) {
740
+ var script = document.createElement("script");
741
+ script.id = FORM_WIDGET_SCRIPT_ID;
742
+ script.src = getFormWidgetScriptUrl(key);
743
+ script.async = true;
744
+ script.onload = function () {
745
+ resolve();
746
+ };
747
+ script.onerror = function () {
748
+ reject(new Error("Failed to load form-widget script: ".concat(script.src)));
749
+ };
750
+ document.head.appendChild(script);
751
+ });
752
+ }
753
+ /**
754
+ * Injects the form-widget script into the page with retry logic.
755
+ * Returns a promise that resolves when the script is loaded.
756
+ * Does nothing if the script is already loaded.
757
+ *
758
+ * @param key The form widget key
759
+ * @param maxRetries Maximum number of retry attempts (default: 3)
760
+ * @param retryDelayMs Delay between retries in milliseconds (default: 1000)
761
+ */
762
+ function loadFormWidgetScript(key_1) {
763
+ return __awaiter$1(this, arguments, void 0, function (key, maxRetries, retryDelayMs) {
764
+ var lastError, attempt, error_1;
765
+ if (maxRetries === void 0) { maxRetries = DEFAULT_MAX_RETRIES; }
766
+ if (retryDelayMs === void 0) { retryDelayMs = DEFAULT_RETRY_DELAY_MS; }
767
+ return __generator$1(this, function (_a) {
768
+ switch (_a.label) {
769
+ case 0:
770
+ // Check if already loaded
771
+ if (isFormWidgetScriptLoaded(key)) {
772
+ return [2 /*return*/];
773
+ }
774
+ attempt = 0;
775
+ _a.label = 1;
776
+ case 1:
777
+ if (!(attempt <= maxRetries)) return [3 /*break*/, 8];
778
+ _a.label = 2;
779
+ case 2:
780
+ _a.trys.push([2, 6, , 7]);
781
+ if (!(attempt > 0)) return [3 /*break*/, 4];
782
+ removeFailedScript();
783
+ console.log("[chat-widget] Retrying form-widget script load (attempt ".concat(attempt + 1, "/").concat(maxRetries + 1, ")"));
784
+ return [4 /*yield*/, delay(retryDelayMs)];
785
+ case 3:
786
+ _a.sent();
787
+ _a.label = 4;
788
+ case 4: return [4 /*yield*/, attemptScriptLoad(key)];
789
+ case 5:
790
+ _a.sent();
791
+ return [2 /*return*/]; // Success
792
+ case 6:
793
+ error_1 = _a.sent();
794
+ lastError = error_1;
795
+ if (attempt === maxRetries) {
796
+ console.error("[chat-widget] Failed to load form-widget script after all retries:", error_1);
797
+ }
798
+ return [3 /*break*/, 7];
799
+ case 7:
800
+ attempt++;
801
+ return [3 /*break*/, 1];
802
+ case 8: throw lastError || new Error("Failed to load form-widget script");
803
+ }
804
+ });
805
+ });
806
+ }
807
+ /**
808
+ * Pre-loads the form-widget script if a key is provided.
809
+ * This is a fire-and-forget operation - errors are logged but not thrown.
810
+ */
811
+ function preloadFormWidgetScript(key) {
812
+ if (!key) {
813
+ return;
814
+ }
815
+ loadFormWidgetScript(key).catch(function (error) {
816
+ console.warn("[chat-widget] Failed to preload form-widget script:", error);
817
+ });
818
+ }
819
+ /**
820
+ * Checks if the form-widget control API is available
821
+ */
822
+ function isFormWidgetReady() {
823
+ var _a;
824
+ return typeof ((_a = window.xafwControl) === null || _a === void 0 ? void 0 : _a.openForm) === "function";
825
+ }
826
+ /**
827
+ * Opens the form widget if it's available.
828
+ * Returns true if the form was opened, false otherwise.
829
+ *
830
+ * @param options Optional configuration for opening the form
831
+ */
832
+ function openFormWidget(options) {
833
+ if (isFormWidgetReady()) {
834
+ window.xafwControl.openForm(options);
835
+ return true;
836
+ }
837
+ return false;
838
+ }
839
+ /**
840
+ * Attempts to open the form widget, waiting for it to be ready if necessary.
841
+ * Useful when the script is still loading.
842
+ *
843
+ * @param options Optional configuration for opening the form
844
+ * @param maxAttempts Maximum number of attempts to check for readiness
845
+ * @param intervalMs Time between attempts in milliseconds
846
+ */
847
+ function openFormWidgetWhenReady(options, maxAttempts, intervalMs) {
848
+ if (maxAttempts === void 0) { maxAttempts = 20; }
849
+ if (intervalMs === void 0) { intervalMs = 100; }
850
+ return new Promise(function (resolve) {
851
+ // Try immediately first
852
+ if (openFormWidget(options)) {
853
+ resolve(true);
854
+ return;
855
+ }
856
+ var attempts = 0;
857
+ var checkInterval = setInterval(function () {
858
+ attempts++;
859
+ if (openFormWidget(options)) {
860
+ clearInterval(checkInterval);
861
+ resolve(true);
862
+ return;
863
+ }
864
+ if (attempts >= maxAttempts) {
865
+ clearInterval(checkInterval);
866
+ console.warn("[chat-widget] Form widget not ready after waiting");
867
+ resolve(false);
868
+ }
869
+ }, intervalMs);
870
+ });
871
+ }
872
+
308
873
  var EMAIL_REGEX = "^\\w+([\\.-]?\\w+)*@\\w+([\\.-]?\\w+)*(\\.\\w{2,3})+$";
309
874
  // Initialize log level from localStorage on module load
310
875
  var STORAGE_KEY = 'xaLogLevel';
@@ -1405,12 +1970,6 @@ var LogChat = /** @class */ (function () {
1405
1970
  return LogChat;
1406
1971
  }());
1407
1972
 
1408
- var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
1409
-
1410
- function getDefaultExportFromCjs (x) {
1411
- return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
1412
- }
1413
-
1414
1973
  var jsxRuntime = require$$0;
1415
1974
  var react = require$$1;
1416
1975
 
@@ -6543,11 +7102,11 @@ class BinaryReconstructor {
6543
7102
  }
6544
7103
 
6545
7104
  var parser = /*#__PURE__*/Object.freeze({
6546
- __proto__: null,
6547
- Decoder: Decoder,
6548
- Encoder: Encoder,
6549
- get PacketType () { return PacketType; },
6550
- protocol: protocol
7105
+ __proto__: null,
7106
+ Decoder: Decoder,
7107
+ Encoder: Encoder,
7108
+ get PacketType () { return PacketType; },
7109
+ protocol: protocol
6551
7110
  });
6552
7111
 
6553
7112
  function on(obj, ev, fn) {
@@ -8216,82 +8775,6 @@ var CancelButton = function (props) {
8216
8775
  return (require$$0.jsx("div", { id: "xapp-widget-close", "aria-label": "close widget", "aria-hidden": false, tabIndex: props.tabIndex ? Number(props.tabIndex) : 0, className: "cancel-button", onClick: props.onClick }));
8217
8776
  };
8218
8777
 
8219
- var lib = {};
8220
-
8221
- var ChannelData = {};
8222
-
8223
- Object.defineProperty(ChannelData, "__esModule", { value: true });
8224
-
8225
- var Constants = {};
8226
-
8227
- Object.defineProperty(Constants, "__esModule", { value: true });
8228
- Constants.CHAT_WIDGET_CHANNEL = void 0;
8229
- Constants.CHAT_WIDGET_CHANNEL = "chat-widget";
8230
-
8231
- var Types = {};
8232
-
8233
- /*! Copyright (c) 2021, XAPPmedia */
8234
- Object.defineProperty(Types, "__esModule", { value: true });
8235
-
8236
- var UserInfo = {};
8237
-
8238
- Object.defineProperty(UserInfo, "__esModule", { value: true });
8239
-
8240
- var WidgetEnv = {};
8241
-
8242
- Object.defineProperty(WidgetEnv, "__esModule", { value: true });
8243
-
8244
- var WidgetTheme = {};
8245
-
8246
- Object.defineProperty(WidgetTheme, "__esModule", { value: true });
8247
-
8248
- var guards = {};
8249
-
8250
- Object.defineProperty(guards, "__esModule", { value: true });
8251
- guards.isStaticImageMenuItem = isStaticImageMenuItem;
8252
- guards.isStaticTextMenuItem = isStaticTextMenuItem;
8253
- guards.isStandardMenuItem = isStandardMenuItem;
8254
- guards.isOpenURLMenuItem = isOpenURLMenuItem;
8255
- function isStaticImageMenuItem(item) {
8256
- return !!item && !!item.imageUrl;
8257
- }
8258
- function isStaticTextMenuItem(item) {
8259
- return !!item && !!item.body;
8260
- }
8261
- function isStandardMenuItem(item) {
8262
- return !!item && !!item.label;
8263
- }
8264
- function isOpenURLMenuItem(item) {
8265
- return !!item && !!item.url;
8266
- }
8267
-
8268
- (function (exports$1) {
8269
- var __createBinding = (commonjsGlobal && commonjsGlobal.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8270
- if (k2 === undefined) k2 = k;
8271
- var desc = Object.getOwnPropertyDescriptor(m, k);
8272
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8273
- desc = { enumerable: true, get: function() { return m[k]; } };
8274
- }
8275
- Object.defineProperty(o, k2, desc);
8276
- }) : (function(o, m, k, k2) {
8277
- if (k2 === undefined) k2 = k;
8278
- o[k2] = m[k];
8279
- }));
8280
- var __exportStar = (commonjsGlobal && commonjsGlobal.__exportStar) || function(m, exports$1) {
8281
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports$1, p)) __createBinding(exports$1, m, p);
8282
- };
8283
- Object.defineProperty(exports$1, "__esModule", { value: true });
8284
- /*! Copyright (c) 2021, XAPPmedia */
8285
- __exportStar(ChannelData, exports$1);
8286
- __exportStar(Constants, exports$1);
8287
- __exportStar(Types, exports$1);
8288
- __exportStar(UserInfo, exports$1);
8289
- __exportStar(WidgetEnv, exports$1);
8290
- __exportStar(WidgetTheme, exports$1);
8291
- __exportStar(guards, exports$1);
8292
-
8293
- } (lib));
8294
-
8295
8778
  var ChatMenuItem = function (props) {
8296
8779
  function handleClick() {
8297
8780
  if (props.onClick) {
@@ -9670,6 +10153,150 @@ var ChatFooter = function (props) {
9670
10153
  onFileUpload: props.onFileUpload }) }), brandingEnabled && brandingText && require$$0.jsx(ChatBranding, { text: brandingText })] }));
9671
10154
  };
9672
10155
 
10156
+ /**
10157
+ * Build the form-widget configuration from ActionBar button config
10158
+ */
10159
+ function buildFormConfig(buttonConfig, widgetEnv) {
10160
+ var _a;
10161
+ // Option 1: Use dedicated form config if provided
10162
+ if (buttonConfig.formConfig) {
10163
+ return {
10164
+ api: buttonConfig.formConfig.api,
10165
+ intentId: buttonConfig.formConfig.intentId,
10166
+ autoGreeting: buttonConfig.formConfig.autoGreeting,
10167
+ };
10168
+ }
10169
+ // Option 2: Use shared chat-widget connection settings
10170
+ if ((_a = widgetEnv === null || widgetEnv === void 0 ? void 0 : widgetEnv.connection) === null || _a === void 0 ? void 0 : _a.serverUrl) {
10171
+ return {
10172
+ api: {
10173
+ url: widgetEnv.connection.serverUrl,
10174
+ key: widgetEnv.connection.accountKey,
10175
+ },
10176
+ intentId: buttonConfig.intentId,
10177
+ autoGreeting: buttonConfig.autoGreeting,
10178
+ };
10179
+ }
10180
+ // No valid configuration available
10181
+ return null;
10182
+ }
10183
+ var FormModal = function (_a) {
10184
+ var _b, _c;
10185
+ var config = _a.config, widgetEnv = _a.widgetEnv, onClose = _a.onClose;
10186
+ var modalRef = require$$1.useRef(null);
10187
+ var contentRef = require$$1.useRef(null);
10188
+ var formConfig = buildFormConfig(config, widgetEnv);
10189
+ var _d = require$$1.useState("idle"), loadingState = _d[0], setLoadingState = _d[1];
10190
+ var _e = require$$1.useState(""), errorMessage = _e[0], setErrorMessage = _e[1];
10191
+ // Get the form widget key from config or widgetEnv
10192
+ var formWidgetKey = ((_c = (_b = config.formConfig) === null || _b === void 0 ? void 0 : _b.api) === null || _c === void 0 ? void 0 : _c.key) || (widgetEnv === null || widgetEnv === void 0 ? void 0 : widgetEnv.formWidgetKey);
10193
+ // Attempt to load and open the form widget
10194
+ var attemptOpenFormWidget = require$$1.useCallback(function () { return __awaiter$1(void 0, void 0, void 0, function () {
10195
+ var options, opened, options, opened;
10196
+ return __generator$1(this, function (_a) {
10197
+ switch (_a.label) {
10198
+ case 0:
10199
+ if (!isFormWidgetReady()) return [3 /*break*/, 2];
10200
+ options = config.preselectChipLabel
10201
+ ? { preselectChipLabel: config.preselectChipLabel }
10202
+ : undefined;
10203
+ return [4 /*yield*/, openFormWidgetWhenReady(options, 5, 100)];
10204
+ case 1:
10205
+ opened = _a.sent();
10206
+ if (opened) {
10207
+ setLoadingState("success");
10208
+ onClose();
10209
+ return [2 /*return*/];
10210
+ }
10211
+ _a.label = 2;
10212
+ case 2:
10213
+ if (!formWidgetKey) return [3 /*break*/, 8];
10214
+ setLoadingState("loading");
10215
+ _a.label = 3;
10216
+ case 3:
10217
+ _a.trys.push([3, 6, , 7]);
10218
+ return [4 /*yield*/, loadFormWidgetScript(formWidgetKey)];
10219
+ case 4:
10220
+ _a.sent();
10221
+ options = config.preselectChipLabel
10222
+ ? { preselectChipLabel: config.preselectChipLabel }
10223
+ : undefined;
10224
+ return [4 /*yield*/, openFormWidgetWhenReady(options, 30, 100)];
10225
+ case 5:
10226
+ opened = _a.sent();
10227
+ if (opened) {
10228
+ setLoadingState("success");
10229
+ onClose();
10230
+ }
10231
+ else {
10232
+ setLoadingState("error");
10233
+ setErrorMessage("Form widget loaded but failed to open. Please try again.");
10234
+ }
10235
+ return [3 /*break*/, 7];
10236
+ case 6:
10237
+ _a.sent();
10238
+ setLoadingState("error");
10239
+ setErrorMessage("Failed to load form. Please check your connection and try again.");
10240
+ return [3 /*break*/, 7];
10241
+ case 7: return [3 /*break*/, 9];
10242
+ case 8:
10243
+ setLoadingState("error");
10244
+ setErrorMessage("Form widget key not configured. Please contact support.");
10245
+ _a.label = 9;
10246
+ case 9: return [2 /*return*/];
10247
+ }
10248
+ });
10249
+ }); }, [config.preselectChipLabel, formWidgetKey, onClose]);
10250
+ // Attempt to open form widget on mount
10251
+ require$$1.useEffect(function () {
10252
+ attemptOpenFormWidget();
10253
+ }, [attemptOpenFormWidget]);
10254
+ // Handle Escape key to close
10255
+ require$$1.useEffect(function () {
10256
+ var handleKeyDown = function (event) {
10257
+ if (event.key === "Escape") {
10258
+ onClose();
10259
+ }
10260
+ };
10261
+ document.addEventListener("keydown", handleKeyDown);
10262
+ return function () {
10263
+ document.removeEventListener("keydown", handleKeyDown);
10264
+ };
10265
+ }, [onClose]);
10266
+ // Prevent body scroll when modal is open
10267
+ require$$1.useEffect(function () {
10268
+ var originalOverflow = document.body.style.overflow;
10269
+ document.body.style.overflow = "hidden";
10270
+ return function () {
10271
+ document.body.style.overflow = originalOverflow;
10272
+ };
10273
+ }, []);
10274
+ // Focus trap - focus the modal content on mount
10275
+ require$$1.useEffect(function () {
10276
+ if (contentRef.current) {
10277
+ contentRef.current.focus();
10278
+ }
10279
+ }, []);
10280
+ var handleBackdropClick = require$$1.useCallback(function (event) {
10281
+ // Only close if clicking the backdrop, not the content
10282
+ if (event.target === modalRef.current) {
10283
+ onClose();
10284
+ }
10285
+ }, [onClose]);
10286
+ var handleRetry = require$$1.useCallback(function () {
10287
+ setLoadingState("idle");
10288
+ setErrorMessage("");
10289
+ attemptOpenFormWidget();
10290
+ }, [attemptOpenFormWidget]);
10291
+ // If form widget opened successfully, the modal will close automatically
10292
+ // Show loading/error states in the modal
10293
+ if (!formConfig && !formWidgetKey) {
10294
+ // No valid configuration available at all
10295
+ return (require$$0.jsx("div", { className: "xapp-form-modal", ref: modalRef, onClick: handleBackdropClick, role: "dialog", "aria-modal": "true", "aria-label": "Form", children: require$$0.jsxs("div", { className: "xapp-form-modal__content", ref: contentRef, tabIndex: -1, children: [require$$0.jsx("button", { className: "xapp-form-modal__close", onClick: onClose, "aria-label": "Close form", children: "\u00D7" }), require$$0.jsxs("div", { className: "xapp-form-modal__error", children: [require$$0.jsx("p", { children: "Form configuration not available." }), require$$0.jsx("p", { children: "Please configure the form button with valid settings." })] })] }) }));
10296
+ }
10297
+ return (require$$0.jsx("div", { className: "xapp-form-modal", ref: modalRef, onClick: handleBackdropClick, role: "dialog", "aria-modal": "true", "aria-label": "Form", children: require$$0.jsxs("div", { className: "xapp-form-modal__content", ref: contentRef, tabIndex: -1, children: [require$$0.jsx("button", { className: "xapp-form-modal__close", onClick: onClose, "aria-label": "Close form", children: "\u00D7" }), require$$0.jsxs("div", { className: "xapp-form-modal__form-container", children: [loadingState === "loading" && (require$$0.jsxs("div", { className: "xapp-form-modal__loading", children: [require$$0.jsx("div", { className: "xapp-form-modal__spinner", "aria-hidden": "true" }), require$$0.jsx("p", { children: "Loading form..." })] })), loadingState === "error" && (require$$0.jsxs("div", { className: "xapp-form-modal__error", children: [require$$0.jsx("p", { children: errorMessage }), require$$0.jsx("button", { className: "xapp-form-modal__retry", onClick: handleRetry, children: "Try Again" })] })), loadingState === "idle" && (require$$0.jsxs("div", { className: "xapp-form-modal__loading", children: [require$$0.jsx("div", { className: "xapp-form-modal__spinner", "aria-hidden": "true" }), require$$0.jsx("p", { children: "Initializing..." })] }))] })] }) }));
10298
+ };
10299
+
9673
10300
  var noop = function () { };
9674
10301
  var MiddlewareContextFactory = /** @class */ (function () {
9675
10302
  function MiddlewareContextFactory(template) {
@@ -31601,7 +32228,7 @@ var ServerOffline = function () {
31601
32228
 
31602
32229
  function buildStyleContent(theme) {
31603
32230
  var _a, _b, _c, _d, _e;
31604
- return "\n:root {\n".concat(buildVariables(withPrefix("\t--xapp-", union(single("primary-color", theme === null || theme === void 0 ? void 0 : theme.primaryColor), withPrefix("widget-", union(getSize(theme === null || theme === void 0 ? void 0 : theme.size), getMargins(theme === null || theme === void 0 ? void 0 : theme.margin), getBorderStyle(theme === null || theme === void 0 ? void 0 : theme.border), getZIndex(theme === null || theme === void 0 ? void 0 : theme.zIndex))), getChatButtonStyle(theme === null || theme === void 0 ? void 0 : theme.chatButton), withPrefix("header-", getHeaderStyle(theme === null || theme === void 0 ? void 0 : theme.header)), withPrefix("footer-", getFooterStyle(theme === null || theme === void 0 ? void 0 : theme.footer)), withPrefix("content-", union(getBackgroundStyle((_a = theme === null || theme === void 0 ? void 0 : theme.content) === null || _a === void 0 ? void 0 : _a.background))), withPrefix("messages-", getMessagesStyle(theme === null || theme === void 0 ? void 0 : theme.messages)), withPrefix("text-typing-status-", getTextStyle((_b = theme === null || theme === void 0 ? void 0 : theme.textTypingStatus) === null || _b === void 0 ? void 0 : _b.text)), withPrefix("send-", getButtonStyle(theme === null || theme === void 0 ? void 0 : theme.sendButton)), withPrefix("menu-", getButtonStyle(theme === null || theme === void 0 ? void 0 : theme.menuButton)), withPrefix("menu-", getMenuStyle(theme === null || theme === void 0 ? void 0 : theme.menu)), withPrefix("refresh-", getButtonStyle(theme === null || theme === void 0 ? void 0 : theme.refreshButton)), withPrefix("minimize-", getButtonStyle(theme === null || theme === void 0 ? void 0 : theme.minimizeButton)), withPrefix("cancel-", getButtonStyle(theme === null || theme === void 0 ? void 0 : theme.cancelButton)), withPrefix("carousel-", union(withPrefix("title-", getTextStyle((_c = theme === null || theme === void 0 ? void 0 : theme.carousel) === null || _c === void 0 ? void 0 : _c.title)), withPrefix("subtitle-", getTextStyle((_d = theme === null || theme === void 0 ? void 0 : theme.carousel) === null || _d === void 0 ? void 0 : _d.subtitle)), withPrefix("action-", getButtonStyle((_e = theme === null || theme === void 0 ? void 0 : theme.carousel) === null || _e === void 0 ? void 0 : _e.button)))), withPrefix("input-", getInputStyle(theme === null || theme === void 0 ? void 0 : theme.input)), withPrefix("cta-", getCtaStyle(theme === null || theme === void 0 ? void 0 : theme.cta))))), "\n}");
32231
+ return "\n:root {\n".concat(buildVariables(withPrefix("\t--xapp-", union(single("primary-color", theme === null || theme === void 0 ? void 0 : theme.primaryColor), withPrefix("widget-", union(getSize(theme === null || theme === void 0 ? void 0 : theme.size), getMargins(theme === null || theme === void 0 ? void 0 : theme.margin), getBorderStyle(theme === null || theme === void 0 ? void 0 : theme.border), getZIndex(theme === null || theme === void 0 ? void 0 : theme.zIndex))), getChatButtonStyle(theme === null || theme === void 0 ? void 0 : theme.chatButton), withPrefix("header-", getHeaderStyle(theme === null || theme === void 0 ? void 0 : theme.header)), withPrefix("footer-", getFooterStyle(theme === null || theme === void 0 ? void 0 : theme.footer)), withPrefix("content-", union(getBackgroundStyle((_a = theme === null || theme === void 0 ? void 0 : theme.content) === null || _a === void 0 ? void 0 : _a.background))), withPrefix("messages-", getMessagesStyle(theme === null || theme === void 0 ? void 0 : theme.messages)), withPrefix("text-typing-status-", getTextStyle((_b = theme === null || theme === void 0 ? void 0 : theme.textTypingStatus) === null || _b === void 0 ? void 0 : _b.text)), withPrefix("send-", getButtonStyle(theme === null || theme === void 0 ? void 0 : theme.sendButton)), withPrefix("menu-", getButtonStyle(theme === null || theme === void 0 ? void 0 : theme.menuButton)), withPrefix("menu-", getMenuStyle(theme === null || theme === void 0 ? void 0 : theme.menu)), withPrefix("refresh-", getButtonStyle(theme === null || theme === void 0 ? void 0 : theme.refreshButton)), withPrefix("minimize-", getButtonStyle(theme === null || theme === void 0 ? void 0 : theme.minimizeButton)), withPrefix("cancel-", getButtonStyle(theme === null || theme === void 0 ? void 0 : theme.cancelButton)), withPrefix("carousel-", union(withPrefix("title-", getTextStyle((_c = theme === null || theme === void 0 ? void 0 : theme.carousel) === null || _c === void 0 ? void 0 : _c.title)), withPrefix("subtitle-", getTextStyle((_d = theme === null || theme === void 0 ? void 0 : theme.carousel) === null || _d === void 0 ? void 0 : _d.subtitle)), withPrefix("action-", getButtonStyle((_e = theme === null || theme === void 0 ? void 0 : theme.carousel) === null || _e === void 0 ? void 0 : _e.button)))), withPrefix("input-", getInputStyle(theme === null || theme === void 0 ? void 0 : theme.input)), withPrefix("cta-", getCtaStyle(theme === null || theme === void 0 ? void 0 : theme.cta)), withPrefix("action-bar-", getActionBarStyle(theme === null || theme === void 0 ? void 0 : theme.actionBar))))), "\n}");
31605
32232
  }
31606
32233
  function WidgetStylesheet(props) {
31607
32234
  var theme = props.theme;
@@ -31915,6 +32542,18 @@ function getCtaStyle(style) {
31915
32542
  }
31916
32543
  return union(getBackgroundStyle(style.background), getTextStyle(style.text));
31917
32544
  }
32545
+ function getActionBarStyle(style) {
32546
+ if (!style) {
32547
+ return empty();
32548
+ }
32549
+ return union(getBackgroundStyle(style.background), getBorderStyle(style.border), getPaddings(style.padding), getMargins(style.margin), single("box-shadow", style.boxShadow), single("border-radius", style.borderRadius), withPrefix("button-", getActionBarButtonStyle(style.button)));
32550
+ }
32551
+ function getActionBarButtonStyle(style) {
32552
+ if (!style) {
32553
+ return empty();
32554
+ }
32555
+ return union(getBackgroundStyle(style.background), single("hover-background", style.hoverBackground), withPrefix("icon-", getSize(style.iconSize)), single("icon-color", style.iconColor), withPrefix("label-", getTextStyle(style.label)), single("gap", style.gap), single("border-radius", style.borderRadius), getPaddings(style.padding));
32556
+ }
31918
32557
 
31919
32558
  /**
31920
32559
  * Safely stringify objects, handling circular references and errors
@@ -32744,9 +33383,10 @@ typeof process !== "undefined" &&
32744
33383
  * This allows preview mode to use the existing message rendering infrastructure.
32745
33384
  *
32746
33385
  * @param messages - Array of preview messages to convert
33386
+ * @param config - Widget configuration containing avatarUrl and botName
32747
33387
  * @returns Array of ChatDetail objects suitable for MessageList rendering
32748
33388
  */
32749
- function convertPreviewMessagesToChatDetails(messages) {
33389
+ function convertPreviewMessagesToChatDetails(messages, config) {
32750
33390
  var timestamp = Date.now();
32751
33391
  return messages.map(function (msg, index) {
32752
33392
  var _a;
@@ -32754,7 +33394,8 @@ function convertPreviewMessagesToChatDetails(messages) {
32754
33394
  type: "chat.msg",
32755
33395
  user: {
32756
33396
  nick: "agent:robot",
32757
- display_name: "Assistant",
33397
+ display_name: (config === null || config === void 0 ? void 0 : config.botName) || "Assistant",
33398
+ avatarPath: config === null || config === void 0 ? void 0 : config.avatarUrl,
32758
33399
  },
32759
33400
  timestamp: timestamp + index,
32760
33401
  msg: {
@@ -32804,8 +33445,8 @@ var ChatWidgetPreview = function (props) {
32804
33445
  var mode = (_a = props.mode) !== null && _a !== void 0 ? _a : "preview";
32805
33446
  // Memoize preview message conversion to avoid recalculating on every render
32806
33447
  var previewChatDetails = require$$1.useMemo(function () {
32807
- return convertPreviewMessagesToChatDetails(props.previewMessages || []);
32808
- }, [props.previewMessages]);
33448
+ return convertPreviewMessagesToChatDetails(props.previewMessages || [], props.config);
33449
+ }, [props.previewMessages, props.config]);
32809
33450
  return (require$$0.jsx(StaticMessagesChatWidgetContainer, { messages: previewChatDetails, mode: mode, config: props.config, disableAutoScroll: props.disableAutoScroll, preview: true }));
32810
33451
  };
32811
33452
  /**
@@ -32901,7 +33542,7 @@ var ChatWidgetConnected = function (props) {
32901
33542
  * Exported for use by StaticChatWidgetContainer to avoid infinite loops.
32902
33543
  */
32903
33544
  var ChatWidgetUI = function (props) {
32904
- 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;
33545
+ 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;
32905
33546
  var innerDispatch = useChatDispatch();
32906
33547
  var dispatch = useChatServerDispatch();
32907
33548
  // From Redux
@@ -32931,12 +33572,12 @@ var ChatWidgetUI = function (props) {
32931
33572
  chatState.visuals = {};
32932
33573
  }
32933
33574
  // Our state - pull from storage
32934
- var _9 = require$$1.useState((!canMinimize && !canCancel) ||
33575
+ var _10 = require$$1.useState((!canMinimize && !canCancel) ||
32935
33576
  // !!get("visible") ||
32936
33577
  chatState.visuals.visible ||
32937
33578
  (((_m = props.config) === null || _m === void 0 ? void 0 : _m.autoOpenOnWidth) &&
32938
- window.matchMedia("(min-width: ".concat((_o = props.config) === null || _o === void 0 ? void 0 : _o.autoOpenOnWidth, ")")).matches)), visible = _9[0], setVisibleState = _9[1];
32939
- var _10 = require$$1.useState(false); _10[0]; var setTypingState = _10[1]; // false initially - state kept for potential external observers
33579
+ window.matchMedia("(min-width: ".concat((_o = props.config) === null || _o === void 0 ? void 0 : _o.autoOpenOnWidth, ")")).matches)), visible = _10[0], setVisibleState = _10[1];
33580
+ var _11 = require$$1.useState(false); _11[0]; var setTypingState = _11[1]; // false initially - state kept for potential external observers
32940
33581
  // Ref to track typing state for use in timeout callbacks
32941
33582
  var typingRef = require$$1.useRef(false);
32942
33583
  // Timeout ref for debouncing "stop typing" events
@@ -32982,7 +33623,7 @@ var ChatWidgetUI = function (props) {
32982
33623
  };
32983
33624
  // eslint-disable-next-line react-hooks/exhaustive-deps
32984
33625
  }, []);
32985
- var _11 = require$$1.useState(!document.hidden), isTabVisible = _11[0], setIsTabVisible = _11[1];
33626
+ var _12 = require$$1.useState(!document.hidden), isTabVisible = _12[0], setIsTabVisible = _12[1];
32986
33627
  require$$1.useEffect(function () {
32987
33628
  var handleVisibilityChange = function () {
32988
33629
  setIsTabVisible(!document.hidden);
@@ -33184,6 +33825,28 @@ var ChatWidgetUI = function (props) {
33184
33825
  hasInteracted: true,
33185
33826
  }));
33186
33827
  }
33828
+ // Action Bar state and handlers
33829
+ var _13 = require$$1.useState(false), formModalOpen = _13[0], setFormModalOpen = _13[1];
33830
+ var _14 = require$$1.useState(null), activeFormConfig = _14[0], setActiveFormConfig = _14[1];
33831
+ var handleFormButtonClick = require$$1.useCallback(function (formButton) {
33832
+ // If form-widget is configured, use its openForm API
33833
+ // The form-widget script should already be pre-loaded
33834
+ var options = formButton.preselectChipLabel
33835
+ ? { preselectChipLabel: formButton.preselectChipLabel }
33836
+ : undefined;
33837
+ openFormWidgetWhenReady(options).then(function (opened) {
33838
+ if (!opened) {
33839
+ // Fallback to modal if form-widget isn't available
33840
+ log("Form widget not available, showing modal fallback");
33841
+ setActiveFormConfig(formButton);
33842
+ setFormModalOpen(true);
33843
+ }
33844
+ });
33845
+ }, []);
33846
+ var handleFormModalClose = require$$1.useCallback(function () {
33847
+ setFormModalOpen(false);
33848
+ setActiveFormConfig(null);
33849
+ }, []);
33187
33850
  function handleWsButtonPress(buttonId) {
33188
33851
  var _a, _b;
33189
33852
  log("WS Button pressed: ".concat(buttonId));
@@ -33231,6 +33894,7 @@ var ChatWidgetUI = function (props) {
33231
33894
  var messages = chatState && chatState.chats;
33232
33895
  log("Rendering - accountStatus: \"".concat(chatState.accountStatus, "\", connectionStatus: \"").concat(chatState.connection.connectionStatus, "\""));
33233
33896
  var config = props.config, onConnectionStatusChange = props.onConnectionStatusChange;
33897
+ var actionBarEnabled = ((_t = config === null || config === void 0 ? void 0 : config.actionBar) === null || _t === void 0 ? void 0 : _t.enabled) === true;
33234
33898
  // Disable greeting in preview mode
33235
33899
  useGreeting(!isOffline && !props.preChatFormEnabled && visible && !previewMode);
33236
33900
  var connectionStatus = chatState.connection.connectionStatus;
@@ -33239,15 +33903,45 @@ var ChatWidgetUI = function (props) {
33239
33903
  onConnectionStatusChange(connectionStatus);
33240
33904
  }
33241
33905
  }, [connectionStatus, onConnectionStatusChange]);
33242
- useExternalScript((_t = props.config) === null || _t === void 0 ? void 0 : _t.middlewareUrl);
33906
+ useExternalScript((_u = props.config) === null || _u === void 0 ? void 0 : _u.middlewareUrl);
33907
+ // Pre-load form-widget script if formWidgetKey is provided
33908
+ require$$1.useEffect(function () {
33909
+ if (config === null || config === void 0 ? void 0 : config.formWidgetKey) {
33910
+ preloadFormWidgetScript(config.formWidgetKey);
33911
+ }
33912
+ }, [config === null || config === void 0 ? void 0 : config.formWidgetKey]);
33913
+ // Set body attribute when actionBar is enabled (used by CSS to hide form-widget side button on mobile)
33914
+ // Uses reference counting to handle multiple widget instances
33915
+ require$$1.useEffect(function () {
33916
+ if (actionBarEnabled) {
33917
+ // Increment reference count
33918
+ var currentCount = parseInt(document.body.getAttribute("data-xapp-action-bar-count") || "0", 10);
33919
+ document.body.setAttribute("data-xapp-action-bar-count", String(currentCount + 1));
33920
+ document.body.setAttribute("data-xapp-action-bar-enabled", "true");
33921
+ }
33922
+ return function () {
33923
+ if (actionBarEnabled) {
33924
+ // Decrement reference count
33925
+ var currentCount = parseInt(document.body.getAttribute("data-xapp-action-bar-count") || "0", 10);
33926
+ var newCount = Math.max(0, currentCount - 1);
33927
+ if (newCount === 0) {
33928
+ document.body.removeAttribute("data-xapp-action-bar-enabled");
33929
+ document.body.removeAttribute("data-xapp-action-bar-count");
33930
+ }
33931
+ else {
33932
+ document.body.setAttribute("data-xapp-action-bar-count", String(newCount));
33933
+ }
33934
+ }
33935
+ };
33936
+ }, [actionBarEnabled]);
33243
33937
  // This is a pseudo agent. It represent's the widget (shown in the header avatar for instance)
33244
- var widgetAgent = ((_u = chatState.agents["agent:robot"]) === null || _u === void 0 ? void 0 : _u.user) ||
33938
+ var widgetAgent = ((_v = chatState.agents["agent:robot"]) === null || _v === void 0 ? void 0 : _v.user) ||
33245
33939
  (config === null || config === void 0 ? void 0 : config.agent) || {
33246
33940
  nick: "agent:robot",
33247
33941
  avatarPath: config.avatarUrl,
33248
33942
  display_name: "Agent",
33249
33943
  };
33250
- return (require$$0.jsxs(require$$0.Fragment, { children: [require$$0.jsxs("div", { className: "widget-container ".concat(modeClass, " ").concat(getVisibilityClass()), 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, 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: (_w = (_v = props.config) === null || _v === void 0 ? void 0 : _v.typingStatus) === null || _w === void 0 ? void 0 : _w.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: (_x = config === null || config === void 0 ? void 0 : config.input) === null || _x === void 0 ? void 0 : _x.placeholder, sendButtonIcon: (_z = (_y = config === null || config === void 0 ? void 0 : config.footer) === null || _y === void 0 ? void 0 : _y.sendButton) === null || _z === void 0 ? void 0 : _z.icon, sendButtonIconHover: (_1 = (_0 = config === null || config === void 0 ? void 0 : config.footer) === null || _0 === void 0 ? void 0 : _0.sendButton) === null || _1 === void 0 ? void 0 : _1.iconHover, sendButtonIconDisabled: (_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.iconDisabled, visible: visible, hasWsButton: !!chatState.wsButton, menuConfig: (_4 = props.config) === null || _4 === void 0 ? void 0 : _4.menu, footerConfig: (_5 = props.config) === null || _5 === void 0 ? void 0 : _5.footer, inputConfig: (_6 = props.config) === null || _6 === void 0 ? void 0 : _6.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 }) })] }), require$$0.jsx(ChatButton, { addClass: getVisibilityClass(), onClick: chatButtonOnClick, config: config === null || config === void 0 ? void 0 : config.cta, imageUrl: (_7 = config === null || config === void 0 ? void 0 : config.chatButton) === null || _7 === void 0 ? void 0 : _7.imageUrl, visible: visible, hasInteracted: (_8 = chatState.visuals) === null || _8 === void 0 ? void 0 : _8.hasInteracted, onCtaDismiss: handleCtaDismiss }), require$$0.jsx(ErrorOverlay, { enableErrorOverlay: config === null || config === void 0 ? void 0 : config.enableErrorOverlay })] }));
33944
+ return (require$$0.jsxs(require$$0.Fragment, { children: [require$$0.jsxs("div", { className: "widget-container ".concat(modeClass, " ").concat(getVisibilityClass()).concat(actionBarEnabled ? " widget-container--with-action-bar" : ""), children: [require$$0.jsx(WidgetStylesheet, { theme: config === null || config === void 0 ? void 0 : config.theme }), require$$0.jsx(ChatHeader, { accountStatus: chatState.accountStatus, refreshOnClick: handleRestartClick, minimizeOnClick: handleMinimizeClick, cancelOnClick: handleCancelClick, agent: widgetAgent, canRefresh: canRefresh, canMinimize: canMinimize, canCancel: canCancel, config: config === null || config === void 0 ? void 0 : config.header, menuConfig: config.menu, 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: (_x = (_w = props.config) === null || _w === void 0 ? void 0 : _w.typingStatus) === null || _x === void 0 ? void 0 : _x.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: (_y = config === null || config === void 0 ? void 0 : config.input) === null || _y === void 0 ? void 0 : _y.placeholder, sendButtonIcon: (_0 = (_z = config === null || config === void 0 ? void 0 : config.footer) === null || _z === void 0 ? void 0 : _z.sendButton) === null || _0 === void 0 ? void 0 : _0.icon, sendButtonIconHover: (_2 = (_1 = config === null || config === void 0 ? void 0 : config.footer) === null || _1 === void 0 ? void 0 : _1.sendButton) === null || _2 === void 0 ? void 0 : _2.iconHover, sendButtonIconDisabled: (_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.iconDisabled, visible: visible, hasWsButton: !!chatState.wsButton, menuConfig: (_5 = props.config) === null || _5 === void 0 ? void 0 : _5.menu, footerConfig: (_6 = props.config) === null || _6 === void 0 ? void 0 : _6.footer, inputConfig: (_7 = props.config) === null || _7 === void 0 ? void 0 : _7.input, disabled: previewMode, onChange: handleOnChange, onSubmit: handleOnSubmit, onFileUpload: handleFileUpload }), require$$0.jsx("div", { className: "restartModal", ref: modalRef, onClick: handleRestartModalCloseClick, children: require$$0.jsx(ModalContent, { onClose: handleRestartModalCloseClick, onReset: handleReset }) })] }), actionBarEnabled && config.actionBar ? (require$$0.jsxs(require$$0.Fragment, { children: [require$$0.jsx(ActionBar, { config: config.actionBar, visible: visible, chatDisabled: config.disabled, onChatClick: chatButtonOnClick, onChatMinimize: handleMinimizeClick, onFormClick: handleFormButtonClick }), formModalOpen && activeFormConfig && (require$$0.jsx(FormModal, { config: activeFormConfig, widgetEnv: config, onClose: handleFormModalClose }))] })) : (require$$0.jsx(ChatButton, { addClass: getVisibilityClass(), onClick: chatButtonOnClick, config: config === null || config === void 0 ? void 0 : config.cta, imageUrl: (_8 = config === null || config === void 0 ? void 0 : config.chatButton) === null || _8 === void 0 ? void 0 : _8.imageUrl, visible: visible, hasInteracted: (_9 = chatState.visuals) === null || _9 === void 0 ? void 0 : _9.hasInteracted, onCtaDismiss: handleCtaDismiss })), require$$0.jsx(ErrorOverlay, { enableErrorOverlay: config === null || config === void 0 ? void 0 : config.enableErrorOverlay })] }));
33251
33945
  };
33252
33946
  /**
33253
33947
  * Top-level wrapper that dispatches between preview mode and connected mode.
@@ -33758,6 +34452,8 @@ var ChatWidgetContainer = function (props) {
33758
34452
  };
33759
34453
 
33760
34454
  exports.React = require$$1;
34455
+ exports.ActionBar = ActionBar;
34456
+ exports.ActionBarPreview = ActionBarPreview;
33761
34457
  exports.ActionButton = ActionButton;
33762
34458
  exports.Avatar = Avatar;
33763
34459
  exports.CardMiddleware = CardMiddleware;
@@ -33776,6 +34472,7 @@ exports.ChatMessagePart = ChatMessagePart;
33776
34472
  exports.ChatWidget = ChatWidgetWrapper;
33777
34473
  exports.CtaBubble = CtaBubble;
33778
34474
  exports.CtaBubbleContainer = CtaBubbleContainer;
34475
+ exports.FormModal = FormModal;
33779
34476
  exports.List = List;
33780
34477
  exports.ListItem = ListItem;
33781
34478
  exports.ListMiddleware = ListMiddleware;
@@ -33783,6 +34480,10 @@ exports.MessageList = MessageList;
33783
34480
  exports.MiddlewareContextFactory = MiddlewareContextFactory;
33784
34481
  exports.StaticChatWidgetContainer = StaticChatWidgetContainer;
33785
34482
  exports.StaticMessagesChatWidgetContainer = StaticMessagesChatWidgetContainer;
34483
+ exports.isActionBarChatButton = lib.isActionBarChatButton;
34484
+ exports.isActionBarFormButton = lib.isActionBarFormButton;
34485
+ exports.isActionBarPhoneButton = lib.isActionBarPhoneButton;
34486
+ exports.isActionBarUrlButton = lib.isActionBarUrlButton;
33786
34487
  exports.joinMiddlewares = joinMiddlewares;
33787
34488
  exports.responseToMessage = responseToMessage;
33788
34489
  exports.useLateMiddleware = useLateMiddleware;