@xapp/chat-widget 1.84.3 → 1.85.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.es.js CHANGED
@@ -1,8 +1,391 @@
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
+ // Default fallback icon for unsupported types
159
+ var DefaultIcon = 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 15h-2v-2h2v2zm0-4h-2V7h2v6z" }) })); };
160
+ var iconComponents = {
161
+ chat: ChatIcon,
162
+ form: FormIcon,
163
+ calendar: ScheduleIcon, // GraphQL schema uses "calendar"
164
+ schedule: ScheduleIcon, // Keep "schedule" for backwards compatibility
165
+ phone: PhoneIcon,
166
+ link: LinkIcon, // GraphQL schema uses "link"
167
+ url: LinkIcon, // Keep "url" for backwards compatibility
168
+ email: EmailIcon,
169
+ help: HelpIcon,
170
+ payment: PaymentIcon,
171
+ // "custom" type is handled via iconUrl, no need for a component
172
+ };
173
+ var ActionBarIcon = function (_a) {
174
+ var type = _a.type, icon = _a.icon, iconUrl = _a.iconUrl, className = _a.className;
175
+ if (iconUrl) {
176
+ return (jsx("div", { className: className, children: jsx("img", { src: iconUrl, alt: "".concat(icon || type, " icon") }) }));
177
+ }
178
+ // Use explicit icon override if provided, otherwise fall back to type
179
+ var iconType = icon || type;
180
+ var IconComponent = iconComponents[iconType] || DefaultIcon;
181
+ return (jsx("div", { className: className, children: jsx(IconComponent, {}) }));
182
+ };
183
+
184
+ /**
185
+ * Allowed URL protocols for security. Prevents javascript: and other potentially dangerous protocols.
186
+ */
187
+ var ALLOWED_PROTOCOLS = ["http:", "https:", "tel:", "mailto:"];
188
+ /**
189
+ * Validates a URL to ensure it uses an allowed protocol.
190
+ * Returns true if the URL is safe to navigate to.
191
+ */
192
+ function isValidUrl(url) {
193
+ try {
194
+ var parsed = new URL(url, window.location.href);
195
+ return ALLOWED_PROTOCOLS.includes(parsed.protocol);
196
+ }
197
+ catch (_a) {
198
+ // Invalid URL
199
+ return false;
200
+ }
201
+ }
202
+ /**
203
+ * Safely navigates to a URL after validation.
204
+ * Returns false if the URL was invalid and navigation was blocked.
205
+ */
206
+ function safeNavigate(url, target, windowOptions) {
207
+ if (!isValidUrl(url)) {
208
+ console.warn("[ActionBar] Blocked navigation to unsafe URL:", url);
209
+ return false;
210
+ }
211
+ if (target === "sameWindow") {
212
+ window.location.href = url;
213
+ }
214
+ else if (target === "newWindow" && windowOptions) {
215
+ var features = "width=".concat(windowOptions.width || 600, ",height=").concat(windowOptions.height || 400);
216
+ window.open(url, "_blank", features);
217
+ }
218
+ else {
219
+ // newTab (default)
220
+ window.open(url, "_blank", "noopener,noreferrer");
221
+ }
222
+ return true;
223
+ }
224
+ var defaultLabels = {
225
+ chat: "Chat",
226
+ form: "Contact",
227
+ schedule: "Book",
228
+ phone: "Call",
229
+ url: "Link",
230
+ email: "Email",
231
+ help: "Help",
232
+ payment: "Pay",
233
+ };
234
+ var ActionBarButton = function (_a) {
235
+ var _b;
236
+ var button = _a.button, chatActive = _a.chatActive, onChatClick = _a.onChatClick, onChatMinimize = _a.onChatMinimize, onFormClick = _a.onFormClick, isMobile = _a.isMobile;
237
+ var handleClick = useCallback(function () {
238
+ if (lib.isActionBarChatButton(button)) {
239
+ // Toggle chat: if active, minimize; if not active, open
240
+ if (chatActive) {
241
+ onChatMinimize();
242
+ }
243
+ else {
244
+ onChatClick();
245
+ }
246
+ }
247
+ else if (lib.isActionBarFormButton(button) || lib.isActionBarScheduleButton(button)) {
248
+ // Both form and schedule buttons open the form widget modal
249
+ onFormClick(button);
250
+ }
251
+ else if (lib.isActionBarPhoneButton(button)) {
252
+ // Phone: Use tel: link
253
+ var phoneNumber = button.phoneNumber.replace(/\s/g, "");
254
+ window.location.href = "tel:".concat(phoneNumber);
255
+ }
256
+ else if (lib.isActionBarUrlButton(button)) {
257
+ // URL: Open according to target (with validation)
258
+ var url = button.url, _a = button.target, target = _a === void 0 ? "newTab" : _a, windowOptions = button.windowOptions;
259
+ safeNavigate(url, target, windowOptions);
260
+ }
261
+ else if (lib.isActionBarEmailButton(button)) {
262
+ // Email: Build mailto: link with optional subject and body
263
+ var emailAddress = button.emailAddress, subject = button.subject, body = button.body;
264
+ var mailto = "mailto:".concat(emailAddress);
265
+ var params = [];
266
+ if (subject) {
267
+ params.push("subject=".concat(encodeURIComponent(subject)));
268
+ }
269
+ if (body) {
270
+ params.push("body=".concat(encodeURIComponent(body)));
271
+ }
272
+ if (params.length > 0) {
273
+ mailto += "?".concat(params.join("&"));
274
+ }
275
+ window.location.href = mailto;
276
+ }
277
+ else if (lib.isActionBarHelpButton(button)) {
278
+ // Help: Open URL according to target (with validation)
279
+ var url = button.url, _b = button.target, target = _b === void 0 ? "newTab" : _b;
280
+ safeNavigate(url, target);
281
+ }
282
+ else if (lib.isActionBarPaymentButton(button)) {
283
+ // Payment: Open URL according to target (with validation)
284
+ var url = button.url, _c = button.target, target = _c === void 0 ? "newTab" : _c;
285
+ safeNavigate(url, target);
286
+ }
287
+ }, [button, chatActive, onChatClick, onChatMinimize, onFormClick]);
288
+ // Handle visibility
289
+ var visibility = button.visibility || "all";
290
+ if (visibility === "mobile-only" && !isMobile) {
291
+ return null;
292
+ }
293
+ if (visibility === "desktop-only" && isMobile) {
294
+ return null;
295
+ }
296
+ var ariaLabel = button.label || defaultLabels[button.type] || button.type;
297
+ var tabIndex = (_b = button.tabIndex) !== null && _b !== void 0 ? _b : 0;
298
+ // Chat button shows active state when chat is open
299
+ var isActive = lib.isActionBarChatButton(button) && chatActive;
300
+ var buttonClassName = [
301
+ "xapp-action-bar__button",
302
+ isActive ? "xapp-action-bar__button--active" : "",
303
+ ].filter(Boolean).join(" ");
304
+ 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 }))] }));
305
+ };
306
+
307
+ var DEFAULT_MOBILE_BREAKPOINT = 768;
308
+ var RESIZE_DEBOUNCE_MS = 150;
309
+ var ActionBar = function (_a) {
310
+ var _b, _c;
311
+ var config = _a.config, visible = _a.visible, chatDisabled = _a.chatDisabled, onChatClick = _a.onChatClick, onChatMinimize = _a.onChatMinimize, onFormClick = _a.onFormClick;
312
+ var _d = useState(false), isMobile = _d[0], setIsMobile = _d[1];
313
+ var mobileBreakpoint = (_b = config.mobileBreakpoint) !== null && _b !== void 0 ? _b : DEFAULT_MOBILE_BREAKPOINT;
314
+ var position = (_c = config.position) !== null && _c !== void 0 ? _c : "right";
315
+ var resizeTimeoutRef = useRef(null);
316
+ // Debounced resize handler
317
+ var handleResize = useCallback(function () {
318
+ if (resizeTimeoutRef.current) {
319
+ window.clearTimeout(resizeTimeoutRef.current);
320
+ }
321
+ resizeTimeoutRef.current = window.setTimeout(function () {
322
+ setIsMobile(window.innerWidth <= mobileBreakpoint);
323
+ }, RESIZE_DEBOUNCE_MS);
324
+ }, [mobileBreakpoint]);
325
+ useEffect(function () {
326
+ // Initial check (immediate, not debounced)
327
+ setIsMobile(window.innerWidth <= mobileBreakpoint);
328
+ window.addEventListener("resize", handleResize);
329
+ return function () {
330
+ window.removeEventListener("resize", handleResize);
331
+ if (resizeTimeoutRef.current) {
332
+ window.clearTimeout(resizeTimeoutRef.current);
333
+ }
334
+ };
335
+ }, [mobileBreakpoint, handleResize]);
336
+ // Filter out chat buttons if chat is disabled
337
+ var filteredButtons = chatDisabled
338
+ ? config.buttons.filter(function (button) { return !lib.isActionBarChatButton(button); })
339
+ : config.buttons;
340
+ // Build class names - ActionBar stays visible even when chat is open
341
+ var classNames = [
342
+ "xapp-action-bar",
343
+ "xapp-action-bar--".concat(position),
344
+ isMobile ? "xapp-action-bar--mobile" : "xapp-action-bar--desktop",
345
+ ]
346
+ .filter(Boolean)
347
+ .join(" ");
348
+ 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)); }) }));
349
+ };
350
+
351
+ /**
352
+ * ActionBarPreview is a simplified version of ActionBar designed for use in Studio
353
+ * configuration UI. It renders a visual preview of the action bar without requiring
354
+ * interactive callbacks.
355
+ *
356
+ * This component provides no-op handlers for all button interactions, making it
357
+ * suitable for configuration previews where you want to show how the action bar
358
+ * will look without actual functionality.
359
+ *
360
+ * @example
361
+ * ```tsx
362
+ * <ActionBarPreview
363
+ * config={{
364
+ * enabled: true,
365
+ * buttons: [
366
+ * { id: '1', type: 'chat', label: 'Chat', icon: 'chat' },
367
+ * { id: '2', type: 'phone', label: 'Call', icon: 'phone', phoneNumber: '555-1234' }
368
+ * ],
369
+ * position: 'right'
370
+ * }}
371
+ * />
372
+ * ```
373
+ */
374
+ var ActionBarPreview = function (_a) {
375
+ var config = _a.config, _b = _a.chatActive, chatActive = _b === void 0 ? false : _b;
376
+ // No-op handlers for preview mode
377
+ var handleChatClick = useCallback(function () {
378
+ // Preview only - no action
379
+ }, []);
380
+ var handleChatMinimize = useCallback(function () {
381
+ // Preview only - no action
382
+ }, []);
383
+ var handleFormClick = useCallback(function (_button) {
384
+ // Preview only - no action
385
+ }, []);
386
+ return (jsx(ActionBar, { config: config, visible: chatActive, chatDisabled: false, onChatClick: handleChatClick, onChatMinimize: handleChatMinimize, onFormClick: handleFormClick }));
387
+ };
388
+
6
389
  var ActionButton = function (_a) {
7
390
  var label = _a.label, disable = _a.disable, type = _a.type, addClass = _a.addClass, onClick = _a.onClick;
8
391
  function handleClick(ev) {
@@ -304,6 +687,193 @@ function getTimeAgo(date) {
304
687
  return date.toLocaleString();
305
688
  }
306
689
 
690
+ /*! Copyright (c) 2024, XAPPmedia */
691
+ var FORM_WIDGET_SCRIPT_ID = "xapp-form-js";
692
+ var FORM_WIDGET_BASE_URL = "https://form.xapp.ai/xapp-form-widget.js";
693
+ /** Default retry configuration */
694
+ var DEFAULT_MAX_RETRIES = 3;
695
+ var DEFAULT_RETRY_DELAY_MS = 1000;
696
+ /**
697
+ * Builds the form-widget script URL with the provided key
698
+ */
699
+ function getFormWidgetScriptUrl(key) {
700
+ return "".concat(FORM_WIDGET_BASE_URL, "?key=").concat(encodeURIComponent(key));
701
+ }
702
+ /**
703
+ * Checks if the form-widget script is already loaded on the page.
704
+ * Checks both by ID and by src URL to handle various installation methods.
705
+ */
706
+ function isFormWidgetScriptLoaded(key) {
707
+ // Check by standard form-widget ID
708
+ if (document.getElementById(FORM_WIDGET_SCRIPT_ID)) {
709
+ return true;
710
+ }
711
+ // Check by src URL if key is provided
712
+ if (key) {
713
+ var scriptUrl = getFormWidgetScriptUrl(key);
714
+ var scripts = document.querySelectorAll("script[src]");
715
+ for (var i = 0; i < scripts.length; i++) {
716
+ var src = scripts[i].getAttribute("src");
717
+ if (src && (src === scriptUrl || src.startsWith(FORM_WIDGET_BASE_URL))) {
718
+ return true;
719
+ }
720
+ }
721
+ }
722
+ return false;
723
+ }
724
+ /**
725
+ * Removes any failed script elements to allow retry
726
+ */
727
+ function removeFailedScript() {
728
+ var existingScript = document.getElementById(FORM_WIDGET_SCRIPT_ID);
729
+ if (existingScript) {
730
+ existingScript.remove();
731
+ }
732
+ }
733
+ /**
734
+ * Delays execution for a specified number of milliseconds
735
+ */
736
+ function delay(ms) {
737
+ return new Promise(function (resolve) { return setTimeout(resolve, ms); });
738
+ }
739
+ /**
740
+ * Attempts to load the script once
741
+ */
742
+ function attemptScriptLoad(key) {
743
+ return new Promise(function (resolve, reject) {
744
+ var script = document.createElement("script");
745
+ script.id = FORM_WIDGET_SCRIPT_ID;
746
+ script.src = getFormWidgetScriptUrl(key);
747
+ script.async = true;
748
+ script.onload = function () {
749
+ resolve();
750
+ };
751
+ script.onerror = function () {
752
+ reject(new Error("Failed to load form-widget script: ".concat(script.src)));
753
+ };
754
+ document.head.appendChild(script);
755
+ });
756
+ }
757
+ /**
758
+ * Injects the form-widget script into the page with retry logic.
759
+ * Returns a promise that resolves when the script is loaded.
760
+ * Does nothing if the script is already loaded.
761
+ *
762
+ * @param key The form widget key
763
+ * @param maxRetries Maximum number of retry attempts (default: 3)
764
+ * @param retryDelayMs Delay between retries in milliseconds (default: 1000)
765
+ */
766
+ function loadFormWidgetScript(key_1) {
767
+ return __awaiter$1(this, arguments, void 0, function (key, maxRetries, retryDelayMs) {
768
+ var lastError, attempt, error_1;
769
+ if (maxRetries === void 0) { maxRetries = DEFAULT_MAX_RETRIES; }
770
+ if (retryDelayMs === void 0) { retryDelayMs = DEFAULT_RETRY_DELAY_MS; }
771
+ return __generator$1(this, function (_a) {
772
+ switch (_a.label) {
773
+ case 0:
774
+ // Check if already loaded
775
+ if (isFormWidgetScriptLoaded(key)) {
776
+ return [2 /*return*/];
777
+ }
778
+ attempt = 0;
779
+ _a.label = 1;
780
+ case 1:
781
+ if (!(attempt <= maxRetries)) return [3 /*break*/, 8];
782
+ _a.label = 2;
783
+ case 2:
784
+ _a.trys.push([2, 6, , 7]);
785
+ if (!(attempt > 0)) return [3 /*break*/, 4];
786
+ removeFailedScript();
787
+ console.log("[chat-widget] Retrying form-widget script load (attempt ".concat(attempt + 1, "/").concat(maxRetries + 1, ")"));
788
+ return [4 /*yield*/, delay(retryDelayMs)];
789
+ case 3:
790
+ _a.sent();
791
+ _a.label = 4;
792
+ case 4: return [4 /*yield*/, attemptScriptLoad(key)];
793
+ case 5:
794
+ _a.sent();
795
+ return [2 /*return*/]; // Success
796
+ case 6:
797
+ error_1 = _a.sent();
798
+ lastError = error_1;
799
+ if (attempt === maxRetries) {
800
+ console.error("[chat-widget] Failed to load form-widget script after all retries:", error_1);
801
+ }
802
+ return [3 /*break*/, 7];
803
+ case 7:
804
+ attempt++;
805
+ return [3 /*break*/, 1];
806
+ case 8: throw lastError || new Error("Failed to load form-widget script");
807
+ }
808
+ });
809
+ });
810
+ }
811
+ /**
812
+ * Pre-loads the form-widget script if a key is provided.
813
+ * This is a fire-and-forget operation - errors are logged but not thrown.
814
+ */
815
+ function preloadFormWidgetScript(key) {
816
+ if (!key) {
817
+ return;
818
+ }
819
+ loadFormWidgetScript(key).catch(function (error) {
820
+ console.warn("[chat-widget] Failed to preload form-widget script:", error);
821
+ });
822
+ }
823
+ /**
824
+ * Checks if the form-widget control API is available
825
+ */
826
+ function isFormWidgetReady() {
827
+ var _a;
828
+ return typeof ((_a = window.xafwControl) === null || _a === void 0 ? void 0 : _a.openForm) === "function";
829
+ }
830
+ /**
831
+ * Opens the form widget if it's available.
832
+ * Returns true if the form was opened, false otherwise.
833
+ *
834
+ * @param options Optional configuration for opening the form
835
+ */
836
+ function openFormWidget(options) {
837
+ if (isFormWidgetReady()) {
838
+ window.xafwControl.openForm(options);
839
+ return true;
840
+ }
841
+ return false;
842
+ }
843
+ /**
844
+ * Attempts to open the form widget, waiting for it to be ready if necessary.
845
+ * Useful when the script is still loading.
846
+ *
847
+ * @param options Optional configuration for opening the form
848
+ * @param maxAttempts Maximum number of attempts to check for readiness
849
+ * @param intervalMs Time between attempts in milliseconds
850
+ */
851
+ function openFormWidgetWhenReady(options, maxAttempts, intervalMs) {
852
+ if (maxAttempts === void 0) { maxAttempts = 20; }
853
+ if (intervalMs === void 0) { intervalMs = 100; }
854
+ return new Promise(function (resolve) {
855
+ // Try immediately first
856
+ if (openFormWidget(options)) {
857
+ resolve(true);
858
+ return;
859
+ }
860
+ var attempts = 0;
861
+ var checkInterval = setInterval(function () {
862
+ attempts++;
863
+ if (openFormWidget(options)) {
864
+ clearInterval(checkInterval);
865
+ resolve(true);
866
+ return;
867
+ }
868
+ if (attempts >= maxAttempts) {
869
+ clearInterval(checkInterval);
870
+ console.warn("[chat-widget] Form widget not ready after waiting");
871
+ resolve(false);
872
+ }
873
+ }, intervalMs);
874
+ });
875
+ }
876
+
307
877
  var EMAIL_REGEX = "^\\w+([\\.-]?\\w+)*@\\w+([\\.-]?\\w+)*(\\.\\w{2,3})+$";
308
878
  // Initialize log level from localStorage on module load
309
879
  var STORAGE_KEY = 'xaLogLevel';
@@ -1404,12 +1974,6 @@ var LogChat = /** @class */ (function () {
1404
1974
  return LogChat;
1405
1975
  }());
1406
1976
 
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
1977
  var jsxRuntime = require$$0;
1414
1978
  var react = require$$1;
1415
1979
 
@@ -6542,11 +7106,11 @@ class BinaryReconstructor {
6542
7106
  }
6543
7107
 
6544
7108
  var parser = /*#__PURE__*/Object.freeze({
6545
- __proto__: null,
6546
- Decoder: Decoder,
6547
- Encoder: Encoder,
6548
- get PacketType () { return PacketType; },
6549
- protocol: protocol
7109
+ __proto__: null,
7110
+ Decoder: Decoder,
7111
+ Encoder: Encoder,
7112
+ get PacketType () { return PacketType; },
7113
+ protocol: protocol
6550
7114
  });
6551
7115
 
6552
7116
  function on(obj, ev, fn) {
@@ -8215,82 +8779,6 @@ var CancelButton = function (props) {
8215
8779
  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
8780
  };
8217
8781
 
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
8782
  var ChatMenuItem = function (props) {
8295
8783
  function handleClick() {
8296
8784
  if (props.onClick) {
@@ -9669,6 +10157,150 @@ var ChatFooter = function (props) {
9669
10157
  onFileUpload: props.onFileUpload }) }), brandingEnabled && brandingText && jsx(ChatBranding, { text: brandingText })] }));
9670
10158
  };
9671
10159
 
10160
+ /**
10161
+ * Build the form-widget configuration from ActionBar button config
10162
+ */
10163
+ function buildFormConfig(buttonConfig, widgetEnv) {
10164
+ var _a;
10165
+ // Option 1: Use dedicated form config if provided
10166
+ if (buttonConfig.formConfig) {
10167
+ return {
10168
+ api: buttonConfig.formConfig.api,
10169
+ intentId: buttonConfig.formConfig.intentId,
10170
+ autoGreeting: buttonConfig.formConfig.autoGreeting,
10171
+ };
10172
+ }
10173
+ // Option 2: Use shared chat-widget connection settings
10174
+ if ((_a = widgetEnv === null || widgetEnv === void 0 ? void 0 : widgetEnv.connection) === null || _a === void 0 ? void 0 : _a.serverUrl) {
10175
+ return {
10176
+ api: {
10177
+ url: widgetEnv.connection.serverUrl,
10178
+ key: widgetEnv.connection.accountKey,
10179
+ },
10180
+ intentId: buttonConfig.intentId,
10181
+ autoGreeting: buttonConfig.autoGreeting,
10182
+ };
10183
+ }
10184
+ // No valid configuration available
10185
+ return null;
10186
+ }
10187
+ var FormModal = function (_a) {
10188
+ var _b, _c;
10189
+ var config = _a.config, widgetEnv = _a.widgetEnv, onClose = _a.onClose;
10190
+ var modalRef = useRef(null);
10191
+ var contentRef = useRef(null);
10192
+ var formConfig = buildFormConfig(config, widgetEnv);
10193
+ var _d = useState("idle"), loadingState = _d[0], setLoadingState = _d[1];
10194
+ var _e = useState(""), errorMessage = _e[0], setErrorMessage = _e[1];
10195
+ // Get the form widget key from config or widgetEnv
10196
+ 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);
10197
+ // Attempt to load and open the form widget
10198
+ var attemptOpenFormWidget = useCallback(function () { return __awaiter$1(void 0, void 0, void 0, function () {
10199
+ var options, opened, options, opened;
10200
+ return __generator$1(this, function (_a) {
10201
+ switch (_a.label) {
10202
+ case 0:
10203
+ if (!isFormWidgetReady()) return [3 /*break*/, 2];
10204
+ options = config.preselectChipLabel
10205
+ ? { preselectChipLabel: config.preselectChipLabel }
10206
+ : undefined;
10207
+ return [4 /*yield*/, openFormWidgetWhenReady(options, 5, 100)];
10208
+ case 1:
10209
+ opened = _a.sent();
10210
+ if (opened) {
10211
+ setLoadingState("success");
10212
+ onClose();
10213
+ return [2 /*return*/];
10214
+ }
10215
+ _a.label = 2;
10216
+ case 2:
10217
+ if (!formWidgetKey) return [3 /*break*/, 8];
10218
+ setLoadingState("loading");
10219
+ _a.label = 3;
10220
+ case 3:
10221
+ _a.trys.push([3, 6, , 7]);
10222
+ return [4 /*yield*/, loadFormWidgetScript(formWidgetKey)];
10223
+ case 4:
10224
+ _a.sent();
10225
+ options = config.preselectChipLabel
10226
+ ? { preselectChipLabel: config.preselectChipLabel }
10227
+ : undefined;
10228
+ return [4 /*yield*/, openFormWidgetWhenReady(options, 30, 100)];
10229
+ case 5:
10230
+ opened = _a.sent();
10231
+ if (opened) {
10232
+ setLoadingState("success");
10233
+ onClose();
10234
+ }
10235
+ else {
10236
+ setLoadingState("error");
10237
+ setErrorMessage("Form widget loaded but failed to open. Please try again.");
10238
+ }
10239
+ return [3 /*break*/, 7];
10240
+ case 6:
10241
+ _a.sent();
10242
+ setLoadingState("error");
10243
+ setErrorMessage("Failed to load form. Please check your connection and try again.");
10244
+ return [3 /*break*/, 7];
10245
+ case 7: return [3 /*break*/, 9];
10246
+ case 8:
10247
+ setLoadingState("error");
10248
+ setErrorMessage("Form widget key not configured. Please contact support.");
10249
+ _a.label = 9;
10250
+ case 9: return [2 /*return*/];
10251
+ }
10252
+ });
10253
+ }); }, [config.preselectChipLabel, formWidgetKey, onClose]);
10254
+ // Attempt to open form widget on mount
10255
+ useEffect(function () {
10256
+ attemptOpenFormWidget();
10257
+ }, [attemptOpenFormWidget]);
10258
+ // Handle Escape key to close
10259
+ useEffect(function () {
10260
+ var handleKeyDown = function (event) {
10261
+ if (event.key === "Escape") {
10262
+ onClose();
10263
+ }
10264
+ };
10265
+ document.addEventListener("keydown", handleKeyDown);
10266
+ return function () {
10267
+ document.removeEventListener("keydown", handleKeyDown);
10268
+ };
10269
+ }, [onClose]);
10270
+ // Prevent body scroll when modal is open
10271
+ useEffect(function () {
10272
+ var originalOverflow = document.body.style.overflow;
10273
+ document.body.style.overflow = "hidden";
10274
+ return function () {
10275
+ document.body.style.overflow = originalOverflow;
10276
+ };
10277
+ }, []);
10278
+ // Focus trap - focus the modal content on mount
10279
+ useEffect(function () {
10280
+ if (contentRef.current) {
10281
+ contentRef.current.focus();
10282
+ }
10283
+ }, []);
10284
+ var handleBackdropClick = useCallback(function (event) {
10285
+ // Only close if clicking the backdrop, not the content
10286
+ if (event.target === modalRef.current) {
10287
+ onClose();
10288
+ }
10289
+ }, [onClose]);
10290
+ var handleRetry = useCallback(function () {
10291
+ setLoadingState("idle");
10292
+ setErrorMessage("");
10293
+ attemptOpenFormWidget();
10294
+ }, [attemptOpenFormWidget]);
10295
+ // If form widget opened successfully, the modal will close automatically
10296
+ // Show loading/error states in the modal
10297
+ if (!formConfig && !formWidgetKey) {
10298
+ // No valid configuration available at all
10299
+ 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." })] })] }) }));
10300
+ }
10301
+ 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..." })] }))] })] }) }));
10302
+ };
10303
+
9672
10304
  var noop = function () { };
9673
10305
  var MiddlewareContextFactory = /** @class */ (function () {
9674
10306
  function MiddlewareContextFactory(template) {
@@ -31600,7 +32232,7 @@ var ServerOffline = function () {
31600
32232
 
31601
32233
  function buildStyleContent(theme) {
31602
32234
  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}");
32235
+ 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
32236
  }
31605
32237
  function WidgetStylesheet(props) {
31606
32238
  var theme = props.theme;
@@ -31914,6 +32546,18 @@ function getCtaStyle(style) {
31914
32546
  }
31915
32547
  return union(getBackgroundStyle(style.background), getTextStyle(style.text));
31916
32548
  }
32549
+ function getActionBarStyle(style) {
32550
+ if (!style) {
32551
+ return empty();
32552
+ }
32553
+ 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)));
32554
+ }
32555
+ function getActionBarButtonStyle(style) {
32556
+ if (!style) {
32557
+ return empty();
32558
+ }
32559
+ 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));
32560
+ }
31917
32561
 
31918
32562
  /**
31919
32563
  * Safely stringify objects, handling circular references and errors
@@ -32902,7 +33546,7 @@ var ChatWidgetConnected = function (props) {
32902
33546
  * Exported for use by StaticChatWidgetContainer to avoid infinite loops.
32903
33547
  */
32904
33548
  var ChatWidgetUI = function (props) {
32905
- 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;
33549
+ 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;
32906
33550
  var innerDispatch = useChatDispatch();
32907
33551
  var dispatch = useChatServerDispatch();
32908
33552
  // From Redux
@@ -32932,12 +33576,12 @@ var ChatWidgetUI = function (props) {
32932
33576
  chatState.visuals = {};
32933
33577
  }
32934
33578
  // Our state - pull from storage
32935
- var _9 = useState((!canMinimize && !canCancel) ||
33579
+ var _10 = useState((!canMinimize && !canCancel) ||
32936
33580
  // !!get("visible") ||
32937
33581
  chatState.visuals.visible ||
32938
33582
  (((_m = props.config) === null || _m === void 0 ? void 0 : _m.autoOpenOnWidth) &&
32939
- window.matchMedia("(min-width: ".concat((_o = props.config) === null || _o === void 0 ? void 0 : _o.autoOpenOnWidth, ")")).matches)), visible = _9[0], setVisibleState = _9[1];
32940
- var _10 = useState(false); _10[0]; var setTypingState = _10[1]; // false initially - state kept for potential external observers
33583
+ window.matchMedia("(min-width: ".concat((_o = props.config) === null || _o === void 0 ? void 0 : _o.autoOpenOnWidth, ")")).matches)), visible = _10[0], setVisibleState = _10[1];
33584
+ var _11 = useState(false); _11[0]; var setTypingState = _11[1]; // false initially - state kept for potential external observers
32941
33585
  // Ref to track typing state for use in timeout callbacks
32942
33586
  var typingRef = useRef(false);
32943
33587
  // Timeout ref for debouncing "stop typing" events
@@ -32983,7 +33627,7 @@ var ChatWidgetUI = function (props) {
32983
33627
  };
32984
33628
  // eslint-disable-next-line react-hooks/exhaustive-deps
32985
33629
  }, []);
32986
- var _11 = useState(!document.hidden), isTabVisible = _11[0], setIsTabVisible = _11[1];
33630
+ var _12 = useState(!document.hidden), isTabVisible = _12[0], setIsTabVisible = _12[1];
32987
33631
  useEffect(function () {
32988
33632
  var handleVisibilityChange = function () {
32989
33633
  setIsTabVisible(!document.hidden);
@@ -33185,6 +33829,28 @@ var ChatWidgetUI = function (props) {
33185
33829
  hasInteracted: true,
33186
33830
  }));
33187
33831
  }
33832
+ // Action Bar state and handlers
33833
+ var _13 = useState(false), formModalOpen = _13[0], setFormModalOpen = _13[1];
33834
+ var _14 = useState(null), activeFormConfig = _14[0], setActiveFormConfig = _14[1];
33835
+ var handleFormButtonClick = useCallback(function (formButton) {
33836
+ // If form-widget is configured, use its openForm API
33837
+ // The form-widget script should already be pre-loaded
33838
+ var options = formButton.preselectChipLabel
33839
+ ? { preselectChipLabel: formButton.preselectChipLabel }
33840
+ : undefined;
33841
+ openFormWidgetWhenReady(options).then(function (opened) {
33842
+ if (!opened) {
33843
+ // Fallback to modal if form-widget isn't available
33844
+ log("Form widget not available, showing modal fallback");
33845
+ setActiveFormConfig(formButton);
33846
+ setFormModalOpen(true);
33847
+ }
33848
+ });
33849
+ }, []);
33850
+ var handleFormModalClose = useCallback(function () {
33851
+ setFormModalOpen(false);
33852
+ setActiveFormConfig(null);
33853
+ }, []);
33188
33854
  function handleWsButtonPress(buttonId) {
33189
33855
  var _a, _b;
33190
33856
  log("WS Button pressed: ".concat(buttonId));
@@ -33232,6 +33898,7 @@ var ChatWidgetUI = function (props) {
33232
33898
  var messages = chatState && chatState.chats;
33233
33899
  log("Rendering - accountStatus: \"".concat(chatState.accountStatus, "\", connectionStatus: \"").concat(chatState.connection.connectionStatus, "\""));
33234
33900
  var config = props.config, onConnectionStatusChange = props.onConnectionStatusChange;
33901
+ var actionBarEnabled = ((_t = config === null || config === void 0 ? void 0 : config.actionBar) === null || _t === void 0 ? void 0 : _t.enabled) === true;
33235
33902
  // Disable greeting in preview mode
33236
33903
  useGreeting(!isOffline && !props.preChatFormEnabled && visible && !previewMode);
33237
33904
  var connectionStatus = chatState.connection.connectionStatus;
@@ -33240,15 +33907,45 @@ var ChatWidgetUI = function (props) {
33240
33907
  onConnectionStatusChange(connectionStatus);
33241
33908
  }
33242
33909
  }, [connectionStatus, onConnectionStatusChange]);
33243
- useExternalScript((_t = props.config) === null || _t === void 0 ? void 0 : _t.middlewareUrl);
33910
+ useExternalScript((_u = props.config) === null || _u === void 0 ? void 0 : _u.middlewareUrl);
33911
+ // Pre-load form-widget script if formWidgetKey is provided
33912
+ useEffect(function () {
33913
+ if (config === null || config === void 0 ? void 0 : config.formWidgetKey) {
33914
+ preloadFormWidgetScript(config.formWidgetKey);
33915
+ }
33916
+ }, [config === null || config === void 0 ? void 0 : config.formWidgetKey]);
33917
+ // Set body attribute when actionBar is enabled (used by CSS to hide form-widget side button on mobile)
33918
+ // Uses reference counting to handle multiple widget instances
33919
+ useEffect(function () {
33920
+ if (actionBarEnabled) {
33921
+ // Increment reference count
33922
+ var currentCount = parseInt(document.body.getAttribute("data-xapp-action-bar-count") || "0", 10);
33923
+ document.body.setAttribute("data-xapp-action-bar-count", String(currentCount + 1));
33924
+ document.body.setAttribute("data-xapp-action-bar-enabled", "true");
33925
+ }
33926
+ return function () {
33927
+ if (actionBarEnabled) {
33928
+ // Decrement reference count
33929
+ var currentCount = parseInt(document.body.getAttribute("data-xapp-action-bar-count") || "0", 10);
33930
+ var newCount = Math.max(0, currentCount - 1);
33931
+ if (newCount === 0) {
33932
+ document.body.removeAttribute("data-xapp-action-bar-enabled");
33933
+ document.body.removeAttribute("data-xapp-action-bar-count");
33934
+ }
33935
+ else {
33936
+ document.body.setAttribute("data-xapp-action-bar-count", String(newCount));
33937
+ }
33938
+ }
33939
+ };
33940
+ }, [actionBarEnabled]);
33244
33941
  // This is a pseudo agent. It represent's the widget (shown in the header avatar for instance)
33245
- var widgetAgent = ((_u = chatState.agents["agent:robot"]) === null || _u === void 0 ? void 0 : _u.user) ||
33942
+ var widgetAgent = ((_v = chatState.agents["agent:robot"]) === null || _v === void 0 ? void 0 : _v.user) ||
33246
33943
  (config === null || config === void 0 ? void 0 : config.agent) || {
33247
33944
  nick: "agent:robot",
33248
33945
  avatarPath: config.avatarUrl,
33249
33946
  display_name: "Agent",
33250
33947
  };
33251
- 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 })] }));
33948
+ 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 })] }));
33252
33949
  };
33253
33950
  /**
33254
33951
  * Top-level wrapper that dispatches between preview mode and connected mode.
@@ -33758,5 +34455,9 @@ var ChatWidgetContainer = function (props) {
33758
34455
  return jsx(ChatWidgetConnectedContainer, __assign({}, props));
33759
34456
  };
33760
34457
 
33761
- 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 };
34458
+ var isActionBarChatButton$1 = lib.isActionBarChatButton;
34459
+ var isActionBarFormButton$1 = lib.isActionBarFormButton;
34460
+ var isActionBarPhoneButton$1 = lib.isActionBarPhoneButton;
34461
+ var isActionBarUrlButton$1 = lib.isActionBarUrlButton;
34462
+ 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 };
33762
34463
  //# sourceMappingURL=index.es.js.map