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