@brainfish-ai/widgets-initiator 1.14.0 → 1.15.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/web.js CHANGED
@@ -42,7 +42,7 @@ var __async = (__this, __arguments, generator) => {
42
42
  !function() {
43
43
  try {
44
44
  var e = "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof globalThis ? globalThis : "undefined" != typeof self ? self : {}, n = new e.Error().stack;
45
- n && (e._sentryDebugIds = e._sentryDebugIds || {}, e._sentryDebugIds[n] = "cc70e533-bee1-49bc-ad8a-9e32ce8d059a", e._sentryDebugIdIdentifier = "sentry-dbid-cc70e533-bee1-49bc-ad8a-9e32ce8d059a");
45
+ n && (e._sentryDebugIds = e._sentryDebugIds || {}, e._sentryDebugIds[n] = "8cbdac46-7b15-4d3d-b1ea-fcaf3113cd88", e._sentryDebugIdIdentifier = "sentry-dbid-8cbdac46-7b15-4d3d-b1ea-fcaf3113cd88");
46
46
  } catch (e2) {
47
47
  }
48
48
  }();
@@ -113,7 +113,7 @@ function decodeProtectedHeader(token) {
113
113
  }
114
114
  }
115
115
  var _global = typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : {};
116
- _global.SENTRY_RELEASE = { id: "03aedc254757699e2d2aca22c018ef134a5de216" };
116
+ _global.SENTRY_RELEASE = { id: "a78428dd0393f7306b3a5d99b998727c646efbb9" };
117
117
  var WidgetType;
118
118
  (function(WidgetType2) {
119
119
  WidgetType2["Sidebar"] = "sidebar";
@@ -171,7 +171,7 @@ const sendErrors = (apiHost, error, widgetKey) => {
171
171
  "api-key": widgetKey
172
172
  },
173
173
  body: JSON.stringify({
174
- error: `WebWidgetVersion: ${"1.14.0"}. ${error.message}`,
174
+ error: `WebWidgetVersion: ${"1.15.0"}. ${error.message}`,
175
175
  stack: error.stack,
176
176
  cause: {
177
177
  message: errorData.message,
@@ -342,11 +342,11 @@ function initializeWidget(widget, config) {
342
342
  widget.HelpWidget.initPopup(config);
343
343
  }
344
344
  }
345
- const initializedWidgets$1 = /* @__PURE__ */ new Set();
345
+ const initializedWidgets$2 = /* @__PURE__ */ new Set();
346
346
  const initSearchWidget = (options, config) => __async(null, null, function* () {
347
347
  const apiHost = getApiHost(options.overrides);
348
348
  try {
349
- if (initializedWidgets$1.has(options.widgetKey)) {
349
+ if (initializedWidgets$2.has(options.widgetKey)) {
350
350
  return void 0;
351
351
  }
352
352
  const result = yield init({
@@ -372,7 +372,7 @@ const initSearchWidget = (options, config) => __async(null, null, function* () {
372
372
  });
373
373
  }
374
374
  initializeWidget(widget, transformedConfig);
375
- initializedWidgets$1.add(options.widgetKey);
375
+ initializedWidgets$2.add(options.widgetKey);
376
376
  return widget;
377
377
  }
378
378
  } catch (error) {
@@ -512,284 +512,38 @@ const createQueues = () => {
512
512
  };
513
513
  }
514
514
  };
515
- function createElementWithClass(tagName, className, attributes = {}) {
516
- const element = document.createElement(tagName);
517
- element.className = className;
518
- Object.entries(attributes).forEach(([key, value]) => {
519
- element.setAttribute(key, value);
520
- });
521
- return element;
522
- }
523
- const IFRAME_CONTAINER_CLASS = "bf-iframe-container";
524
- const TRIGGER_BUTTON_CLASS = "bf-trigger-button";
525
- function createTriggerButton(widgetKey, triggerButtonIcon) {
526
- const button = createElementWithClass("button", TRIGGER_BUTTON_CLASS, {
527
- id: `trigger-button-${widgetKey}`,
528
- "aria-controls": IFRAME_CONTAINER_CLASS,
529
- "aria-expanded": "false",
530
- "data-name": TRIGGER_BUTTON_CLASS
531
- });
532
- const icon = createElementWithClass("div", "trigger-button-icon", {
533
- "aria-hidden": "true"
534
- });
535
- if (!triggerButtonIcon) {
536
- icon.innerHTML = `
537
- <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256" class="close-icon">
538
- <path d="M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,53.66,90.34L128,164.69l74.34-74.35a8,8,0,0,1,11.32,11.32Z"></path>
539
- </svg>
540
- <svg width="24" height="14" viewBox="0 0 24 14" fill="none" xmlns="http://www.w3.org/2000/svg" class="fish-icon visible">
541
- <path fill-rule="evenodd" clip-rule="evenodd" d="M1.71844 10.3882L4.60606 6.98384L1.71844 3.58375C1.4014 3.21362 1.44424 2.65841 1.81269 2.33993C1.97978 2.1979 2.65242 1.98701 3.04657 2.43461L5.78425 5.65824C6.14281 5.29523 6.51693 4.95924 6.90479 4.65114C8.88976 3.07437 11.2345 2.22803 13.6931 2.22803C16.9492 2.22803 20.0039 3.71718 22.2917 6.41142C22.5702 6.74282 22.5702 7.22916 22.2917 7.56056C20.0039 10.2591 16.9492 11.744 13.6931 11.744C11.2348 11.744 8.89035 10.8948 6.90554 9.31995C6.51741 9.01199 6.14304 8.67628 5.78425 8.31374L3.04657 11.5374C2.72953 11.9075 2.18114 11.9505 1.81269 11.632C1.44852 11.3179 1.40568 10.7584 1.71844 10.3882ZM5.93026 10.4683C8.17161 12.2599 10.8546 13.25 13.6931 13.25C17.4881 13.25 21.019 11.4034 23.4447 8.52942C24.1873 7.64571 24.1843 6.31801 23.4397 5.43663C20.8944 2.43919 17.4337 0.722025 13.6931 0.722025C10.8555 0.722025 8.17194 1.70845 5.92952 3.50276L4.17682 1.43933C3.57943 0.760929 2.79325 0.630009 2.25286 0.662947C1.98116 0.679506 1.73125 0.736852 1.51895 0.811846C1.26839 0.900352 1.03017 1.02718 0.827835 1.20058C-0.165283 2.05903 -0.283916 3.561 0.574656 4.56345L2.63075 6.98445L0.568104 9.41623C-0.272133 10.4106 -0.166523 11.9125 0.827835 12.7714C1.81372 13.6243 3.34308 13.5062 4.19036 12.5171L5.93026 10.4683Z" fill="currentColor"/>
542
- </svg>
543
- `;
544
- } else {
545
- icon.innerHTML = `
546
- <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256" class="close-icon">
547
- <path d="M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,53.66,90.34L128,164.69l74.34-74.35a8,8,0,0,1,11.32,11.32Z"></path>
548
- </svg>
549
- <img src="${triggerButtonIcon}" class="fish-icon visible" alt="Trigger button icon" height="32" width="32" aria-hidden="true" aria-label="help button icon" />
550
- `;
515
+ function base64Encode(text) {
516
+ const encoder = new TextEncoder();
517
+ const uint8Array = encoder.encode(text);
518
+ if (typeof window !== "undefined" && typeof window.btoa === "function") {
519
+ const binaryString = Array.from(
520
+ uint8Array,
521
+ (byte) => String.fromCharCode(byte)
522
+ ).join("");
523
+ return btoa(binaryString);
551
524
  }
552
- button.setAttribute("aria-label", "Open Brainfish widget");
553
- button.appendChild(icon);
554
- return { button, icon };
555
- }
556
- function createIframeContainer(widgetUrl) {
557
- const iframeContainer = createElementWithClass(
558
- "div",
559
- IFRAME_CONTAINER_CLASS,
560
- {
561
- id: IFRAME_CONTAINER_CLASS,
562
- "aria-live": "polite",
563
- "aria-hidden": "true"
564
- }
565
- );
566
- const loadingIndicator = createElementWithClass("div", "iframe-loading-indicator");
567
- const spinner = createElementWithClass("div", "spinner");
568
- loadingIndicator.setAttribute("aria-label", "Loading content");
569
- loadingIndicator.setAttribute("role", "status");
570
- loadingIndicator.appendChild(spinner);
571
- iframeContainer.appendChild(loadingIndicator);
572
- const iframe = createElementWithClass("iframe", "trigger-iframe", {
573
- src: widgetUrl,
574
- sandbox: "allow-scripts allow-same-origin allow-popups allow-forms",
575
- allow: "clipboard-write",
576
- role: "dialog"
577
- });
578
- iframe.style.opacity = "0";
579
- iframe.addEventListener("load", () => {
580
- loadingIndicator.style.display = "none";
581
- iframe.style.opacity = "1";
582
- });
583
- iframe.addEventListener("error", (error) => {
584
- console.error("Error loading iframe:", error);
585
- loadingIndicator.innerHTML = "";
586
- const errorIcon = createElementWithClass("div", "loading-error");
587
- loadingIndicator.appendChild(errorIcon);
588
- loadingIndicator.setAttribute("aria-label", "Failed to load content");
589
- });
590
- iframeContainer.appendChild(iframe);
591
- return { iframeContainer, iframe };
592
- }
593
- function toggleIframeSize() {
594
- const iframeContainer = document.querySelector(
595
- `.${IFRAME_CONTAINER_CLASS}`
596
- );
597
- if (iframeContainer.classList.contains("expanded")) {
598
- iframeContainer.classList.remove("expanded");
599
- } else {
600
- iframeContainer.classList.add("expanded");
525
+ if (typeof Buffer !== "undefined") {
526
+ return Buffer.from(uint8Array).toString("base64");
601
527
  }
528
+ throw new Error("[utils.base64Encode]: Unsupported environment");
602
529
  }
603
- const MOBILE_BREAKPOINT = 576;
604
- class IframeStateManager {
605
- static get isWidgetOpen() {
606
- return this.isOpen;
607
- }
608
- static initialize({
609
- button,
610
- icon,
611
- isButtonHidden
612
- }) {
613
- var _a, _b;
614
- this.button = button || null;
615
- this.icon = icon || null;
616
- this.isButtonHidden = isButtonHidden || false;
617
- if (button) {
618
- button.addEventListener("click", () => this.toggleIframeVisibility());
619
- }
620
- if ((_a = window.Brainfish) == null ? void 0 : _a.HelpWidget) {
621
- window.Brainfish.HelpWidget.close = () => this.closeWidget();
622
- window.Brainfish.HelpWidget.open = () => this.openWidget();
623
- }
624
- if ((_b = window.Brainfish) == null ? void 0 : _b.Widgets) {
625
- window.Brainfish.Widgets.open = () => this.openWidget();
626
- window.Brainfish.Widgets.close = () => this.closeWidget();
627
- }
628
- }
629
- static toggleIframeVisibility() {
630
- this.isOpen = !this.isOpen;
631
- if (this.isOpen) {
632
- this.openWidget();
633
- } else {
634
- this.closeWidget();
635
- }
636
- }
637
- // Open the widget programmatically
638
- static openWidget() {
639
- var _a;
640
- this.isOpen = true;
641
- const iframeContainer = document.querySelector(".bf-iframe-container");
642
- const iframe = iframeContainer == null ? void 0 : iframeContainer.querySelector("iframe");
643
- iframeContainer == null ? void 0 : iframeContainer.classList.add("open");
644
- iframeContainer == null ? void 0 : iframeContainer.setAttribute("aria-hidden", "false");
645
- (_a = iframe == null ? void 0 : iframe.contentWindow) == null ? void 0 : _a.postMessage({ type: "FOCUS_SEARCH_FIELD" }, "*");
646
- if (this.button && this.icon) {
647
- const fishIconSVG = this.icon.querySelector(".fish-icon");
648
- const closeIconSVG = this.icon.querySelector(".close-icon");
649
- this.button.setAttribute("aria-expanded", "true");
650
- fishIconSVG.classList.remove("visible");
651
- closeIconSVG.classList.add("visible");
652
- if (document.body.clientWidth <= MOBILE_BREAKPOINT) {
653
- this.hideTriggerButton();
654
- }
655
- }
656
- window.dispatchEvent(new Event("onBrainfishWidgetOpen"));
657
- window.BrainfishAnalytics("track", "Open Widget");
658
- }
659
- // Close the widget programmatically
660
- static closeWidget() {
661
- this.isOpen = false;
662
- const iframeContainer = document.querySelector(".bf-iframe-container");
663
- iframeContainer == null ? void 0 : iframeContainer.classList.remove("open");
664
- iframeContainer == null ? void 0 : iframeContainer.setAttribute("aria-hidden", "true");
665
- if (this.button && this.icon) {
666
- this.button.setAttribute("aria-expanded", "false");
667
- const fishIconSVG = this.icon.querySelector(".fish-icon");
668
- const closeIconSVG = this.icon.querySelector(".close-icon");
669
- fishIconSVG.classList.add("visible");
670
- closeIconSVG.classList.remove("visible");
671
- if (document.body.clientWidth <= MOBILE_BREAKPOINT) {
672
- this.showTriggerButton();
673
- }
530
+ const base64Decode = (text) => {
531
+ if (typeof window !== "undefined" && typeof window.atob === "function") {
532
+ const binaryString = atob(text);
533
+ const len = binaryString.length;
534
+ const bytes = new Uint8Array(len);
535
+ for (let i = 0; i < len; i++) {
536
+ bytes[i] = binaryString.charCodeAt(i);
674
537
  }
675
- window.dispatchEvent(new Event("onBrainfishWidgetClose"));
676
- window.BrainfishAnalytics("track", "Close Widget");
677
- }
678
- static showTriggerButton() {
679
- var _a;
680
- (_a = this.button) == null ? void 0 : _a.classList.remove("hidden");
538
+ const decoder2 = new TextDecoder();
539
+ return decoder2.decode(bytes);
681
540
  }
682
- static hideTriggerButton() {
683
- var _a;
684
- (_a = this.button) == null ? void 0 : _a.classList.add("hidden");
541
+ if (typeof Buffer !== "undefined") {
542
+ return Buffer.from(text, "base64").toString("utf-8");
685
543
  }
686
- }
687
- __publicField(IframeStateManager, "isOpen", false);
688
- __publicField(IframeStateManager, "button", null);
689
- __publicField(IframeStateManager, "icon", null);
690
- __publicField(IframeStateManager, "isButtonHidden", false);
691
- const clientActionHandlerMap = /* @__PURE__ */ new Map();
692
- const registerClientActionHandler = (key, handler) => {
693
- clientActionHandlerMap.set(key, handler);
694
- };
695
- const formatErrorReason = (key, err) => {
696
- const details = err instanceof Error ? err.message : String(err);
697
- return `Error executing Client Action with key ${key}. Details: ${details}`;
698
- };
699
- const isPlainObject = (obj) => {
700
- return Object.prototype.toString.call(obj) === "[object Object]";
544
+ throw new Error("[utils.base64Decode]: Unsupported environment");
701
545
  };
702
- const executeClientActionHandler = (key, input) => __async(null, null, function* () {
703
- if (clientActionHandlerMap.has(key)) {
704
- const handler = clientActionHandlerMap.get(key);
705
- if (typeof handler !== "function") {
706
- return {
707
- success: false,
708
- reason: `Client Action registered with key ${key} is not a function`
709
- };
710
- }
711
- try {
712
- const result = yield handler(input);
713
- if (!isPlainObject(result)) {
714
- throw new TypeError("Invalid result, expecting object type");
715
- }
716
- return {
717
- success: true,
718
- data: result
719
- };
720
- } catch (e) {
721
- return {
722
- success: false,
723
- reason: formatErrorReason(key, e)
724
- };
725
- }
726
- }
727
- return {
728
- success: false,
729
- reason: `No Client Action registered with key ${key}`
730
- };
731
- });
732
- function initMessageListener(iframe, {
733
- widgetHost,
734
- apiHost,
735
- widgetKey,
736
- signal
737
- }) {
738
- const messageHandler = (event) => __async(null, null, function* () {
739
- var _a;
740
- if (event.origin !== widgetHost) return;
741
- const { type } = event.data;
742
- if (type === "TOGGLE_WIDGET_SIZE") {
743
- toggleIframeSize();
744
- }
745
- if (type === "TRACK_EVENT") {
746
- if (!window.BrainfishAnalytics) return;
747
- const { eventName, data } = event.data;
748
- window.BrainfishAnalytics("track", eventName, data);
749
- }
750
- if (type === "CLOSE_WIDGET") {
751
- window.BrainfishAnalytics("track", "Close Widget");
752
- IframeStateManager.toggleIframeVisibility();
753
- }
754
- if (type === "NEXT_BEST_ACTION_CLICKED") {
755
- const { action, searchQueryId, query, answer } = event.data;
756
- const context = { action, searchQueryId };
757
- if (!["function", "callback"].includes(action.type)) {
758
- console.error("Invalid action type", action);
759
- return;
760
- }
761
- window.__bfCallbacks = window.__bfCallbacks || {};
762
- if (window.__bfCallbacks[action.id]) {
763
- window.__bfCallbacks[action.id](query, answer, context);
764
- return;
765
- }
766
- const scriptUrl = `${apiHost}/api/searchWidgets.callback.codeblocks?apiKey=${widgetKey}&codeblockId=${action.id}`;
767
- const script = document.createElement("script");
768
- script.src = scriptUrl;
769
- document.head.appendChild(script);
770
- script.onload = () => {
771
- window.__bfCallbacks[action.id](query, answer, context);
772
- setTimeout(() => {
773
- document.head.removeChild(script);
774
- }, 300);
775
- };
776
- }
777
- if (type === "CLIENT_EXECUTION") {
778
- const { actionKey, inputs, messageId } = event.data;
779
- const result = yield executeClientActionHandler(actionKey, inputs);
780
- (_a = iframe.contentWindow) == null ? void 0 : _a.postMessage(
781
- {
782
- type: "CLIENT_EXECUTION_RESULT",
783
- messageId,
784
- result
785
- },
786
- "*"
787
- );
788
- }
789
- });
790
- window.addEventListener("message", messageHandler, { signal });
791
- }
792
- const styles = '.bf-trigger-button{position:fixed;bottom:16px;right:16px;width:36px;height:36px;border-radius:50%;border:none;box-shadow:0 1px 6px #0000000f,0 2px 32px #00000029;transition:transform 167ms cubic-bezier(.33,0,0,1);box-sizing:content-box;display:flex;align-items:center;justify-content:center;cursor:pointer;z-index:1000;padding:12px;gap:10px;overflow:hidden}.bf-trigger-button.hidden{display:none}.trigger-button-icon{display:flex;align-items:center;justify-content:center;width:36px;height:36px;position:relative}.trigger-button-icon svg{width:100%;height:100%;position:absolute;inset:0}.trigger-button-icon>svg>path{transition:transform .3s ease,opacity .3s ease;transform:scale(.5);transform-origin:center;opacity:0}.trigger-button-icon .fish-icon{display:none}.trigger-button-icon .fish-icon.visible{display:block}.trigger-button-icon svg.visible{display:block}.trigger-button-icon>svg.visible>path{opacity:1;transform:scale(1)}.trigger-iframe{position:relative;width:100%;height:100%;border:none;outline:none;transform:translateZ(0)}.bf-iframe-container{display:none;position:fixed;z-index:2147483000000;background-color:#fff;bottom:0;right:0;width:100vw;height:100vh;border:none;overflow:hidden;opacity:0;pointer-events:none;transform:translateY(20px);transition:opacity .3s ease,transform .3s ease,width .2s ease-in-out,height .2s ease-in-out}.bf-iframe-container.open{display:block;opacity:1;transform:translateY(0);pointer-events:auto;top:0;left:0}@media (min-width: 576px){.bf-iframe-container{bottom:80px;right:24px;width:448px;height:min(750px,calc(100vh - 80px));border-radius:8px;border:1px solid var(--Dark-300, #D4D4D4);box-shadow:0 20px 25px -5px #1212171a,0 10px 10px -5px #1212170a}.bf-iframe-container.open{top:unset;left:unset}.bf-iframe-container.expanded{width:min(calc(448px * 1.35),calc(100vw - 16px));height:min(calc(750px * 1.35),calc(100vh - 80px))}}.iframe-loading-indicator{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);display:flex;align-items:center;justify-content:center;z-index:10}.spinner{width:24px;height:24px;border:2px solid rgba(0,0,0,.1);border-radius:50%;border-top-color:#3498db;animation:spin 1s ease-in-out infinite}@keyframes spin{to{transform:rotate(360deg)}}.loading-error{width:24px;height:24px;position:relative;display:flex;align-items:center;justify-content:center}.loading-error:before,.loading-error:after{content:"";position:absolute;width:16px;height:2px;background-color:#e74c3c;border-radius:2px}.loading-error:before{transform:rotate(45deg)}.loading-error:after{transform:rotate(-45deg)}';
546
+ const styles = `.bf-trigger-button{position:fixed;bottom:16px;right:16px;width:36px;height:36px;border-radius:50%;border:none;box-shadow:0 1px 6px #0000000f,0 2px 32px #00000029;transition:transform 167ms cubic-bezier(.33,0,0,1);box-sizing:content-box;display:flex;align-items:center;justify-content:center;cursor:pointer;z-index:1000;padding:12px;gap:10px;overflow:hidden}.bf-trigger-button.hidden{display:none}.trigger-button-icon{display:flex;align-items:center;justify-content:center;width:36px;height:36px;position:relative}.trigger-button-icon svg{width:100%;height:100%;position:absolute;inset:0}.trigger-button-icon>svg>path{transition:transform .3s ease,opacity .3s ease;transform:scale(.5);transform-origin:center;opacity:0}.trigger-button-icon .fish-icon{display:none}.trigger-button-icon .fish-icon.visible{display:block}.trigger-button-icon svg.visible{display:block}.trigger-button-icon>svg.visible>path{opacity:1;transform:scale(1)}.trigger-iframe{position:relative;width:100%;height:100%;border:none;outline:none;transform:translateZ(0)}.bf-iframe-container,.bf-search-bar-iframe-container{display:none;position:fixed;z-index:2147483000000;background-color:#fff;bottom:0;right:0;width:100vw;height:calc(100vh - env(safe-area-inset-top) - env(safe-area-inset-bottom));border:none;overflow:hidden;opacity:0;pointer-events:none;transform:translateY(20px);transition:opacity .3s ease,transform .3s ease,width .2s ease-in-out,height .2s ease-in-out}.bf-iframe-container.open,.bf-search-bar-iframe-container.open{display:block;opacity:1;transform:translateY(0);pointer-events:auto;top:env(safe-area-inset-top);left:0}@media (min-width: 576px){.bf-iframe-container,.bf-search-bar-iframe-container{bottom:80px;right:24px;width:448px;height:min(750px,calc(100vh - 80px));border-radius:8px;border:1px solid var(--Dark-300, #D4D4D4);box-shadow:0 20px 25px -5px #1212171a,0 10px 10px -5px #1212170a}.bf-iframe-container.open{top:unset;left:unset}.bf-search-bar-iframe-container.open{width:100vw;height:calc(100vh - env(safe-area-inset-top) - env(safe-area-inset-bottom));position:fixed;top:0;left:0}.bf-iframe-container.expanded,.bf-search-bar-iframe-container.expanded{width:min(calc(448px * 1.35),calc(100vw - 16px));height:min(calc(750px * 1.35),calc(100vh - 80px))}}@media (min-width: 962px){.bf-search-bar-iframe-container.open{width:80vw;height:auto;top:50%;left:50%;transform:translateY(-50%) translate(-50%)}}@media (min-width: 1200px){.bf-search-bar-iframe-container.open{width:60vw;min-width:770px}}@media (min-width: 1400px){.bf-search-bar-iframe-container.open{width:50vw;min-width:770px}}brainfish-search-widget{display:flex;align-items:center;height:46px;width:100%;background-color:#fff;border-radius:8px;border:1px solid #ccc;box-shadow:0 2px 4px #0000001a;font-size:16px;color:#171717;padding-left:46px;cursor:text;&:after{content:attr(data-placeholder)}&:before{content:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"/><circle cx="112" cy="112" r="80" fill="none" stroke="%23737373" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/><line x1="168.57" y1="168.57" x2="224" y2="224" fill="none" stroke="%23737373" stroke-linecap="round" stroke-linejoin="round" stroke-width="16"/></svg>');display:block;width:24px;height:24px;position:absolute;left:12px;top:50%;transform:translateY(-50%)}}.bf-search-bar-iframe-backdrop{display:block;position:fixed;top:0;left:0;width:100%;height:100%;z-index:9999;background-color:#d9d9d905;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.iframe-loading-indicator{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);display:flex;align-items:center;justify-content:center;z-index:10}.spinner{width:24px;height:24px;border:2px solid rgba(0,0,0,.1);border-radius:50%;border-top-color:#3498db;animation:spin 1s ease-in-out infinite}@keyframes spin{to{transform:rotate(360deg)}}.loading-error{width:24px;height:24px;position:relative;display:flex;align-items:center;justify-content:center}.loading-error:before,.loading-error:after{content:"";position:absolute;width:16px;height:2px;background-color:#e74c3c;border-radius:2px}.loading-error:before{transform:rotate(45deg)}.loading-error:after{transform:rotate(-45deg)}`;
793
547
  function getDefaultExportFromCjs(x) {
794
548
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
795
549
  }
@@ -1770,6 +1524,10 @@ function requireLighten() {
1770
1524
  }
1771
1525
  var lightenExports = /* @__PURE__ */ requireLighten();
1772
1526
  const lighten = /* @__PURE__ */ getDefaultExportFromCjs(lightenExports);
1527
+ const IFRAME_CONTAINER_CLASS = "bf-iframe-container";
1528
+ const TRIGGER_BUTTON_CLASS = "bf-trigger-button";
1529
+ const SEARCH_BAR_IFRAME_CONTAINER_CLASS = "bf-search-bar-iframe-container";
1530
+ const SEARCH_BAR_IFRAME_BACKDROP_CLASS = "bf-search-bar-iframe-backdrop";
1773
1531
  function insertStyles(primaryColor = "#000", foreground = "FFF") {
1774
1532
  const style = document.createElement("style");
1775
1533
  style.attributes.setNamedItem(document.createAttribute("data-widget-styles"));
@@ -1792,18 +1550,366 @@ function insertStylesIfNeeded(config) {
1792
1550
  insertStyles(buttonColor, iconColor);
1793
1551
  }
1794
1552
  }
1795
- function initColorSchemeListener(iframe, signal) {
1796
- const updateIframeBodyClass = (isDarkMode) => {
1797
- var _a;
1798
- (_a = iframe.contentWindow) == null ? void 0 : _a.postMessage(
1799
- { type: "UPDATE_BODY_CLASS", isDarkMode },
1800
- "*"
1801
- );
1802
- };
1803
- const checkAndUpdateDarkMode = () => {
1804
- const isDarkMode = document.body.classList.contains("dark");
1805
- updateIframeBodyClass(isDarkMode);
1806
- };
1553
+ class WidgetState {
1554
+ constructor(widgetKey, widgetType) {
1555
+ __publicField(this, "isOpen", false);
1556
+ this.widgetKey = widgetKey;
1557
+ this.widgetType = widgetType;
1558
+ }
1559
+ get isWidgetOpen() {
1560
+ return this.isOpen;
1561
+ }
1562
+ openWidget() {
1563
+ this.isOpen = true;
1564
+ }
1565
+ closeWidget() {
1566
+ this.isOpen = false;
1567
+ }
1568
+ toggleWidget() {
1569
+ this.isOpen = !this.isOpen;
1570
+ return this.isOpen;
1571
+ }
1572
+ }
1573
+ const MOBILE_BREAKPOINT = 576;
1574
+ const DESKTOP_BREAKPOINT = 962;
1575
+ class WidgetDOMController {
1576
+ constructor(widgetType) {
1577
+ __publicField(this, "button", null);
1578
+ __publicField(this, "icon", null);
1579
+ this.widgetType = widgetType;
1580
+ }
1581
+ get containerClass() {
1582
+ return this.widgetType === WidgetType.Searchbar ? SEARCH_BAR_IFRAME_CONTAINER_CLASS : IFRAME_CONTAINER_CLASS;
1583
+ }
1584
+ setElements(button, icon) {
1585
+ this.button = button || null;
1586
+ this.icon = icon || null;
1587
+ }
1588
+ openWidget(options = {}) {
1589
+ var _a, _b;
1590
+ const iframeContainer = document.querySelector(`.${this.containerClass}`);
1591
+ const iframe = iframeContainer == null ? void 0 : iframeContainer.querySelector("iframe");
1592
+ if (options.newConversation) {
1593
+ (_a = iframe == null ? void 0 : iframe.contentWindow) == null ? void 0 : _a.postMessage({ type: "NEW_CONVERSATION" }, "*");
1594
+ }
1595
+ (_b = iframe == null ? void 0 : iframe.contentWindow) == null ? void 0 : _b.postMessage({ type: "FOCUS_SEARCH_FIELD" }, "*");
1596
+ iframeContainer == null ? void 0 : iframeContainer.classList.add("open");
1597
+ iframeContainer == null ? void 0 : iframeContainer.setAttribute("aria-hidden", "false");
1598
+ this.updateButtonForOpen();
1599
+ }
1600
+ closeWidget() {
1601
+ const iframeContainer = document.querySelector(`.${this.containerClass}`);
1602
+ iframeContainer == null ? void 0 : iframeContainer.classList.remove("open");
1603
+ iframeContainer == null ? void 0 : iframeContainer.setAttribute("aria-hidden", "true");
1604
+ this.updateButtonForClose();
1605
+ }
1606
+ updateButtonForOpen() {
1607
+ if (!this.button || !this.icon) return;
1608
+ const fishIconSVG = this.icon.querySelector(".fish-icon");
1609
+ const closeIconSVG = this.icon.querySelector(".close-icon");
1610
+ this.button.setAttribute("aria-expanded", "true");
1611
+ fishIconSVG == null ? void 0 : fishIconSVG.classList.remove("visible");
1612
+ closeIconSVG == null ? void 0 : closeIconSVG.classList.add("visible");
1613
+ if (this.isMobile()) {
1614
+ this.hideTriggerButton();
1615
+ }
1616
+ }
1617
+ updateButtonForClose() {
1618
+ if (!this.button || !this.icon) return;
1619
+ const fishIconSVG = this.icon.querySelector(".fish-icon");
1620
+ const closeIconSVG = this.icon.querySelector(".close-icon");
1621
+ this.button.setAttribute("aria-expanded", "false");
1622
+ fishIconSVG == null ? void 0 : fishIconSVG.classList.add("visible");
1623
+ closeIconSVG == null ? void 0 : closeIconSVG.classList.remove("visible");
1624
+ if (this.isMobile()) {
1625
+ this.showTriggerButton();
1626
+ }
1627
+ }
1628
+ showTriggerButton() {
1629
+ var _a;
1630
+ (_a = this.button) == null ? void 0 : _a.classList.remove("hidden");
1631
+ }
1632
+ hideTriggerButton() {
1633
+ var _a;
1634
+ (_a = this.button) == null ? void 0 : _a.classList.add("hidden");
1635
+ }
1636
+ isMobile() {
1637
+ return document.body.clientWidth <= MOBILE_BREAKPOINT;
1638
+ }
1639
+ setupButtonClickListener(callback) {
1640
+ if (this.button) {
1641
+ this.button.addEventListener("click", callback);
1642
+ }
1643
+ }
1644
+ }
1645
+ class WidgetRegistry {
1646
+ static register(widgetKey, instance) {
1647
+ this.instances.set(widgetKey, instance);
1648
+ }
1649
+ static getInstance(widgetKey) {
1650
+ return this.instances.get(widgetKey);
1651
+ }
1652
+ static unregister(widgetKey) {
1653
+ this.instances.delete(widgetKey);
1654
+ }
1655
+ static getAllInstances() {
1656
+ return Array.from(this.instances.values());
1657
+ }
1658
+ static getOpenInstance() {
1659
+ return this.getAllInstances().find((instance) => instance.isWidgetOpen);
1660
+ }
1661
+ static getClosedPopupInstance() {
1662
+ return this.getAllInstances().find(
1663
+ (instance) => !instance.isWidgetOpen && instance.widgetType === WidgetType.Popup
1664
+ );
1665
+ }
1666
+ static clear() {
1667
+ this.instances.clear();
1668
+ }
1669
+ }
1670
+ __publicField(WidgetRegistry, "instances", /* @__PURE__ */ new Map());
1671
+ class WidgetGlobalAPI {
1672
+ static initialize() {
1673
+ if (this.isInitialized) return;
1674
+ this.setupDeprecatedAPI();
1675
+ this.setupNewAPI();
1676
+ this.isInitialized = true;
1677
+ }
1678
+ static setupDeprecatedAPI() {
1679
+ var _a;
1680
+ if ((_a = window.Brainfish) == null ? void 0 : _a.HelpWidget) {
1681
+ window.Brainfish.HelpWidget.close = (trigger) => this.closeCurrentlyOpenWidget();
1682
+ window.Brainfish.HelpWidget.open = (trigger) => this.openCurrentlyClosedPopupWidget(trigger);
1683
+ }
1684
+ }
1685
+ static setupNewAPI() {
1686
+ var _a;
1687
+ if ((_a = window.Brainfish) == null ? void 0 : _a.Widgets) {
1688
+ window.Brainfish.Widgets.open = () => this.openCurrentlyClosedPopupWidget();
1689
+ window.Brainfish.Widgets.close = () => this.closeCurrentlyOpenWidget();
1690
+ }
1691
+ }
1692
+ static closeCurrentlyOpenWidget() {
1693
+ const openInstance = WidgetRegistry.getOpenInstance();
1694
+ if (openInstance) {
1695
+ openInstance.closeWidget();
1696
+ }
1697
+ }
1698
+ static openCurrentlyClosedPopupWidget(trigger) {
1699
+ const closedPopupInstance = WidgetRegistry.getClosedPopupInstance();
1700
+ if (closedPopupInstance) {
1701
+ const options = trigger ? { newConversation: true, trigger } : {};
1702
+ closedPopupInstance.openWidget(options);
1703
+ }
1704
+ }
1705
+ }
1706
+ __publicField(WidgetGlobalAPI, "isInitialized", false);
1707
+ class AnalyticsTracker {
1708
+ constructor(widgetType, widgetKey) {
1709
+ this.widgetType = widgetType;
1710
+ this.widgetKey = widgetKey;
1711
+ }
1712
+ trackOpenWidget(trigger) {
1713
+ this.dispatchEvent("onBrainfishWidgetOpen");
1714
+ if (trigger) {
1715
+ this.trackAnalytics("Open Widget - Custom Trigger", { trigger });
1716
+ } else {
1717
+ this.trackAnalytics("Open Widget");
1718
+ }
1719
+ }
1720
+ trackCloseWidget() {
1721
+ this.dispatchEvent("onBrainfishWidgetClose");
1722
+ this.trackAnalytics("Close Widget");
1723
+ }
1724
+ trackCustomEvent(eventName, data = {}) {
1725
+ this.trackAnalytics(eventName, data);
1726
+ }
1727
+ dispatchEvent(eventName) {
1728
+ window.dispatchEvent(new Event(eventName));
1729
+ }
1730
+ trackAnalytics(eventName, data = {}) {
1731
+ if (window.BrainfishAnalytics) {
1732
+ window.BrainfishAnalytics("track", eventName, __spreadProps(__spreadValues({}, data), {
1733
+ widgetKey: this.widgetKey
1734
+ }), {
1735
+ source: this.widgetType,
1736
+ widgetKey: this.widgetKey
1737
+ });
1738
+ }
1739
+ }
1740
+ }
1741
+ class IframeStateManager {
1742
+ constructor(widgetKey, widgetType) {
1743
+ __publicField(this, "state");
1744
+ __publicField(this, "domController");
1745
+ __publicField(this, "analyticsTracker");
1746
+ this.state = new WidgetState(widgetKey, widgetType);
1747
+ this.domController = new WidgetDOMController(widgetType);
1748
+ this.analyticsTracker = new AnalyticsTracker(widgetType, widgetKey);
1749
+ }
1750
+ static getInstance(widgetKey) {
1751
+ return WidgetRegistry.getInstance(widgetKey);
1752
+ }
1753
+ get widgetKey() {
1754
+ return this.state.widgetKey;
1755
+ }
1756
+ get widgetType() {
1757
+ return this.state.widgetType;
1758
+ }
1759
+ get isWidgetOpen() {
1760
+ return this.state.isWidgetOpen;
1761
+ }
1762
+ get containerClass() {
1763
+ return this.domController.containerClass;
1764
+ }
1765
+ initialize({
1766
+ button,
1767
+ icon,
1768
+ isButtonHidden
1769
+ }) {
1770
+ this.domController.setElements(button, icon);
1771
+ this.domController.setupButtonClickListener(() => this.toggleIframeVisibility());
1772
+ WidgetRegistry.register(this.widgetKey, this);
1773
+ WidgetGlobalAPI.initialize();
1774
+ }
1775
+ toggleIframeVisibility() {
1776
+ const wasOpen = this.state.toggleWidget();
1777
+ if (wasOpen) {
1778
+ this.openWidget();
1779
+ } else {
1780
+ this.closeWidget();
1781
+ }
1782
+ }
1783
+ // Open the widget programmatically
1784
+ openWidget(options = {}) {
1785
+ this.state.openWidget();
1786
+ this.domController.openWidget(options);
1787
+ this.analyticsTracker.trackOpenWidget(options.trigger);
1788
+ }
1789
+ // Close the widget programmatically
1790
+ closeWidget() {
1791
+ this.state.closeWidget();
1792
+ this.domController.closeWidget();
1793
+ this.analyticsTracker.trackCloseWidget();
1794
+ }
1795
+ showTriggerButton() {
1796
+ this.domController.showTriggerButton();
1797
+ }
1798
+ hideTriggerButton() {
1799
+ this.domController.hideTriggerButton();
1800
+ }
1801
+ trackCustomEvent(eventName, data = {}) {
1802
+ this.analyticsTracker.trackCustomEvent(eventName, data);
1803
+ }
1804
+ // Cleanup method to remove this instance
1805
+ destroy() {
1806
+ WidgetRegistry.unregister(this.widgetKey);
1807
+ }
1808
+ static initialize(options) {
1809
+ const instance = new IframeStateManager(options.widgetKey, options.widgetType);
1810
+ instance.initialize(options);
1811
+ return instance;
1812
+ }
1813
+ }
1814
+ function createElementWithClass(tagName, className, attributes = {}) {
1815
+ const element = document.createElement(tagName);
1816
+ element.className = className;
1817
+ Object.entries(attributes).forEach(([key, value]) => {
1818
+ element.setAttribute(key, value);
1819
+ });
1820
+ return element;
1821
+ }
1822
+ function createTriggerButton(widgetKey, triggerButtonIcon) {
1823
+ const button = createElementWithClass("button", TRIGGER_BUTTON_CLASS, {
1824
+ id: `trigger-button-${widgetKey}`,
1825
+ "aria-controls": IFRAME_CONTAINER_CLASS,
1826
+ "aria-expanded": "false",
1827
+ "data-name": TRIGGER_BUTTON_CLASS
1828
+ });
1829
+ const icon = createElementWithClass("div", "trigger-button-icon", {
1830
+ "aria-hidden": "true"
1831
+ });
1832
+ if (!triggerButtonIcon) {
1833
+ icon.innerHTML = `
1834
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256" class="close-icon">
1835
+ <path d="M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,53.66,90.34L128,164.69l74.34-74.35a8,8,0,0,1,11.32,11.32Z"></path>
1836
+ </svg>
1837
+ <svg width="24" height="14" viewBox="0 0 24 14" fill="none" xmlns="http://www.w3.org/2000/svg" class="fish-icon visible">
1838
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M1.71844 10.3882L4.60606 6.98384L1.71844 3.58375C1.4014 3.21362 1.44424 2.65841 1.81269 2.33993C1.97978 2.1979 2.65242 1.98701 3.04657 2.43461L5.78425 5.65824C6.14281 5.29523 6.51693 4.95924 6.90479 4.65114C8.88976 3.07437 11.2345 2.22803 13.6931 2.22803C16.9492 2.22803 20.0039 3.71718 22.2917 6.41142C22.5702 6.74282 22.5702 7.22916 22.2917 7.56056C20.0039 10.2591 16.9492 11.744 13.6931 11.744C11.2348 11.744 8.89035 10.8948 6.90554 9.31995C6.51741 9.01199 6.14304 8.67628 5.78425 8.31374L3.04657 11.5374C2.72953 11.9075 2.18114 11.9505 1.81269 11.632C1.44852 11.3179 1.40568 10.7584 1.71844 10.3882ZM5.93026 10.4683C8.17161 12.2599 10.8546 13.25 13.6931 13.25C17.4881 13.25 21.019 11.4034 23.4447 8.52942C24.1873 7.64571 24.1843 6.31801 23.4397 5.43663C20.8944 2.43919 17.4337 0.722025 13.6931 0.722025C10.8555 0.722025 8.17194 1.70845 5.92952 3.50276L4.17682 1.43933C3.57943 0.760929 2.79325 0.630009 2.25286 0.662947C1.98116 0.679506 1.73125 0.736852 1.51895 0.811846C1.26839 0.900352 1.03017 1.02718 0.827835 1.20058C-0.165283 2.05903 -0.283916 3.561 0.574656 4.56345L2.63075 6.98445L0.568104 9.41623C-0.272133 10.4106 -0.166523 11.9125 0.827835 12.7714C1.81372 13.6243 3.34308 13.5062 4.19036 12.5171L5.93026 10.4683Z" fill="currentColor"/>
1839
+ </svg>
1840
+ `;
1841
+ } else {
1842
+ icon.innerHTML = `
1843
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" fill="currentColor" viewBox="0 0 256 256" class="close-icon">
1844
+ <path d="M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,53.66,90.34L128,164.69l74.34-74.35a8,8,0,0,1,11.32,11.32Z"></path>
1845
+ </svg>
1846
+ <img src="${triggerButtonIcon}" class="fish-icon visible" alt="Trigger button icon" height="32" width="32" aria-hidden="true" aria-label="help button icon" />
1847
+ `;
1848
+ }
1849
+ button.setAttribute("aria-label", "Open Brainfish widget");
1850
+ button.appendChild(icon);
1851
+ return { button, icon };
1852
+ }
1853
+ function createIframeContainer({ widgetUrl, widgetType, props }) {
1854
+ const containerClass = widgetType === WidgetType.Searchbar ? SEARCH_BAR_IFRAME_CONTAINER_CLASS : IFRAME_CONTAINER_CLASS;
1855
+ const iframeContainer = createElementWithClass(
1856
+ "div",
1857
+ containerClass,
1858
+ __spreadValues({
1859
+ id: containerClass,
1860
+ "aria-live": "polite",
1861
+ "aria-hidden": "true"
1862
+ }, props || {})
1863
+ );
1864
+ const loadingIndicator = createElementWithClass("div", "iframe-loading-indicator");
1865
+ const spinner = createElementWithClass("div", "spinner");
1866
+ loadingIndicator.setAttribute("aria-label", "Loading content");
1867
+ loadingIndicator.setAttribute("role", "status");
1868
+ loadingIndicator.appendChild(spinner);
1869
+ iframeContainer.appendChild(loadingIndicator);
1870
+ const iframe = createElementWithClass("iframe", "trigger-iframe", {
1871
+ src: widgetUrl,
1872
+ sandbox: "allow-scripts allow-same-origin allow-popups allow-forms",
1873
+ allow: "clipboard-write",
1874
+ role: "dialog"
1875
+ });
1876
+ iframe.style.opacity = "0";
1877
+ iframe.addEventListener("load", () => {
1878
+ loadingIndicator.style.display = "none";
1879
+ iframe.style.opacity = "1";
1880
+ });
1881
+ iframe.addEventListener("error", (error) => {
1882
+ console.error("Error loading iframe:", error);
1883
+ loadingIndicator.innerHTML = "";
1884
+ const errorIcon = createElementWithClass("div", "loading-error");
1885
+ loadingIndicator.appendChild(errorIcon);
1886
+ loadingIndicator.setAttribute("aria-label", "Failed to load content");
1887
+ });
1888
+ iframeContainer.appendChild(iframe);
1889
+ return { iframeContainer, iframe };
1890
+ }
1891
+ function toggleIframeSize() {
1892
+ const iframeContainer = document.querySelector(
1893
+ `.${IFRAME_CONTAINER_CLASS}`
1894
+ );
1895
+ if (iframeContainer.classList.contains("expanded")) {
1896
+ iframeContainer.classList.remove("expanded");
1897
+ } else {
1898
+ iframeContainer.classList.add("expanded");
1899
+ }
1900
+ }
1901
+ function initColorSchemeListener(iframe, signal) {
1902
+ const updateIframeBodyClass = (isDarkMode) => {
1903
+ var _a;
1904
+ (_a = iframe.contentWindow) == null ? void 0 : _a.postMessage(
1905
+ { type: "UPDATE_BODY_CLASS", isDarkMode },
1906
+ "*"
1907
+ );
1908
+ };
1909
+ const checkAndUpdateDarkMode = () => {
1910
+ const isDarkMode = document.body.classList.contains("dark");
1911
+ updateIframeBodyClass(isDarkMode);
1912
+ };
1807
1913
  const observeBodyClassChanges = () => {
1808
1914
  const observer = new MutationObserver(checkAndUpdateDarkMode);
1809
1915
  observer.observe(document.body, {
@@ -1815,16 +1921,118 @@ function initColorSchemeListener(iframe, signal) {
1815
1921
  checkAndUpdateDarkMode();
1816
1922
  observeBodyClassChanges();
1817
1923
  }
1818
- function observeColorSchemeChanges(iframe, signal) {
1924
+ const clientActionHandlerMap = /* @__PURE__ */ new Map();
1925
+ const registerClientActionHandler = (key, handler) => {
1926
+ clientActionHandlerMap.set(key, handler);
1927
+ };
1928
+ const formatErrorReason = (key, err) => {
1929
+ const details = err instanceof Error ? err.message : String(err);
1930
+ return `Error executing Client Action with key ${key}. Details: ${details}`;
1931
+ };
1932
+ const isPlainObject = (obj) => {
1933
+ return Object.prototype.toString.call(obj) === "[object Object]";
1934
+ };
1935
+ const executeClientActionHandler = (key, input) => __async(null, null, function* () {
1936
+ if (clientActionHandlerMap.has(key)) {
1937
+ const handler = clientActionHandlerMap.get(key);
1938
+ if (typeof handler !== "function") {
1939
+ return {
1940
+ success: false,
1941
+ reason: `Client Action registered with key ${key} is not a function`
1942
+ };
1943
+ }
1944
+ try {
1945
+ const result = yield handler(input);
1946
+ if (!isPlainObject(result)) {
1947
+ throw new TypeError("Invalid result, expecting object type");
1948
+ }
1949
+ return {
1950
+ success: true,
1951
+ data: result
1952
+ };
1953
+ } catch (e) {
1954
+ return {
1955
+ success: false,
1956
+ reason: formatErrorReason(key, e)
1957
+ };
1958
+ }
1959
+ }
1960
+ return {
1961
+ success: false,
1962
+ reason: `No Client Action registered with key ${key}`
1963
+ };
1964
+ });
1965
+ function initMessageListener(iframe, {
1966
+ widgetHost,
1967
+ apiHost,
1968
+ widgetKey,
1969
+ widgetType,
1970
+ signal,
1971
+ stateManager
1972
+ }) {
1973
+ const messageHandler = (event) => __async(null, null, function* () {
1974
+ var _a;
1975
+ if (event.origin !== widgetHost) return;
1976
+ const { type, widgetKey: messageWidgetKey } = event.data;
1977
+ if (messageWidgetKey && messageWidgetKey !== widgetKey) return;
1978
+ if (type === "TOGGLE_WIDGET_SIZE") {
1979
+ toggleIframeSize();
1980
+ }
1981
+ if (type === "TRACK_EVENT") {
1982
+ const { eventName, data } = event.data;
1983
+ stateManager.trackCustomEvent(eventName, data);
1984
+ }
1985
+ if (type === "CLOSE_WIDGET") {
1986
+ stateManager.closeWidget();
1987
+ }
1988
+ if (type === "NEXT_BEST_ACTION_CLICKED") {
1989
+ const { action, searchQueryId, query, answer } = event.data;
1990
+ const context = { action, searchQueryId };
1991
+ if (!["function", "callback"].includes(action.type)) {
1992
+ console.error("Invalid action type", action);
1993
+ return;
1994
+ }
1995
+ window.__bfCallbacks = window.__bfCallbacks || {};
1996
+ if (window.__bfCallbacks[action.id]) {
1997
+ window.__bfCallbacks[action.id](query, answer, context);
1998
+ return;
1999
+ }
2000
+ const scriptUrl = `${apiHost}/api/searchWidgets.callback.codeblocks?apiKey=${widgetKey}&codeblockId=${action.id}`;
2001
+ const script = document.createElement("script");
2002
+ script.src = scriptUrl;
2003
+ document.head.appendChild(script);
2004
+ script.onload = () => {
2005
+ window.__bfCallbacks[action.id](query, answer, context);
2006
+ setTimeout(() => {
2007
+ document.head.removeChild(script);
2008
+ }, 300);
2009
+ };
2010
+ }
2011
+ if (type === "CLIENT_EXECUTION") {
2012
+ const { actionKey, inputs, messageId } = event.data;
2013
+ const result = yield executeClientActionHandler(actionKey, inputs);
2014
+ (_a = iframe.contentWindow) == null ? void 0 : _a.postMessage(
2015
+ {
2016
+ type: "CLIENT_EXECUTION_RESULT",
2017
+ messageId,
2018
+ result
2019
+ },
2020
+ "*"
2021
+ );
2022
+ }
2023
+ });
2024
+ window.addEventListener("message", messageHandler, { signal });
2025
+ }
2026
+ function observeColorSchemeChanges(iframe, signal, stateManager) {
1819
2027
  const updateIframeBodyClass = (isMobile) => {
1820
2028
  var _a;
1821
2029
  const message = { type: "UPDATE_BODY_CLASS", isMobile };
1822
2030
  (_a = iframe.contentWindow) == null ? void 0 : _a.postMessage(message, "*");
1823
2031
  if (isMobile) {
1824
- if (IframeStateManager.isWidgetOpen) {
1825
- IframeStateManager.hideTriggerButton();
2032
+ if (stateManager.isWidgetOpen) {
2033
+ stateManager.hideTriggerButton();
1826
2034
  } else {
1827
- IframeStateManager.showTriggerButton();
2035
+ stateManager.showTriggerButton();
1828
2036
  }
1829
2037
  }
1830
2038
  };
@@ -1847,40 +2055,9 @@ function observeColorSchemeChanges(iframe, signal) {
1847
2055
  checkAndUpdateIsMobile();
1848
2056
  observeBodyResize();
1849
2057
  }
1850
- function base64Encode(text) {
1851
- const encoder = new TextEncoder();
1852
- const uint8Array = encoder.encode(text);
1853
- if (typeof window !== "undefined" && typeof window.btoa === "function") {
1854
- const binaryString = Array.from(
1855
- uint8Array,
1856
- (byte) => String.fromCharCode(byte)
1857
- ).join("");
1858
- return btoa(binaryString);
1859
- }
1860
- if (typeof Buffer !== "undefined") {
1861
- return Buffer.from(uint8Array).toString("base64");
1862
- }
1863
- throw new Error("[utils.base64Encode]: Unsupported environment");
1864
- }
1865
- const base64Decode = (text) => {
1866
- if (typeof window !== "undefined" && typeof window.atob === "function") {
1867
- const binaryString = atob(text);
1868
- const len = binaryString.length;
1869
- const bytes = new Uint8Array(len);
1870
- for (let i = 0; i < len; i++) {
1871
- bytes[i] = binaryString.charCodeAt(i);
1872
- }
1873
- const decoder2 = new TextDecoder();
1874
- return decoder2.decode(bytes);
1875
- }
1876
- if (typeof Buffer !== "undefined") {
1877
- return Buffer.from(text, "base64").toString("utf-8");
1878
- }
1879
- throw new Error("[utils.base64Decode]: Unsupported environment");
1880
- };
1881
- const initializedWidgets = /* @__PURE__ */ new Set();
2058
+ const initializedWidgets$1 = /* @__PURE__ */ new Set();
1882
2059
  function isWidgetInitialized(widgetKey) {
1883
- return initializedWidgets.has(widgetKey);
2060
+ return initializedWidgets$1.has(widgetKey);
1884
2061
  }
1885
2062
  function isButtonExisting(widgetKey) {
1886
2063
  return !!document.getElementById(`trigger-button-${widgetKey}`);
@@ -1890,47 +2067,40 @@ function createWidgetWithButton(widgetKey, widgetUrl, {
1890
2067
  triggerButtonIcon
1891
2068
  }) {
1892
2069
  const fragment = document.createDocumentFragment();
2070
+ let stateManager;
1893
2071
  if (!isButtonHidden) {
1894
2072
  const { button, icon } = createTriggerButton(widgetKey, triggerButtonIcon);
1895
2073
  fragment.appendChild(button);
1896
- IframeStateManager.initialize({ button, icon });
2074
+ stateManager = IframeStateManager.initialize({
2075
+ button,
2076
+ icon,
2077
+ widgetType: WidgetType.Popup,
2078
+ widgetKey
2079
+ });
1897
2080
  } else {
1898
- IframeStateManager.initialize({ isButtonHidden });
2081
+ stateManager = IframeStateManager.initialize({
2082
+ isButtonHidden,
2083
+ widgetType: WidgetType.Popup,
2084
+ widgetKey
2085
+ });
1899
2086
  }
1900
- const { iframeContainer, iframe } = createIframeContainer(widgetUrl);
2087
+ const { iframeContainer, iframe } = createIframeContainer({ widgetUrl, widgetType: WidgetType.Popup });
1901
2088
  fragment.appendChild(iframeContainer);
1902
- return { fragment, iframe };
2089
+ return { fragment, iframe, stateManager };
1903
2090
  }
1904
- function handleInitializationError(apiHost, error, widgetKey) {
2091
+ function handleInitializationError$1(apiHost, error, widgetKey) {
1905
2092
  sendBrainfishWidgetError(apiHost, error, error.message, widgetKey);
1906
2093
  }
1907
- const initIframeTrigger = (options, config) => {
1908
- var _a, _b, _c;
1909
- const apiHost = getApiHost(options.overrides);
1910
- const widgetHost = getWidgetHost(options.overrides);
1911
- const { theme, settings } = config;
1912
- const encodedRules = base64Encode(
1913
- JSON.stringify((_a = options.overrides) == null ? void 0 : _a.redirectRules)
1914
- );
1915
- const widgetParams = {
1916
- theme: base64Encode(JSON.stringify(theme)),
1917
- settings: base64Encode(JSON.stringify(settings)),
1918
- redirectRules: encodedRules
1919
- };
1920
- if ((_b = options.overrides) == null ? void 0 : _b.regions) {
1921
- widgetParams.regions = base64Encode(
1922
- JSON.stringify((_c = options.overrides) == null ? void 0 : _c.regions)
1923
- );
1924
- }
1925
- const widgetUrl = `${widgetHost}/?widgetKey=${options.widgetKey}#${new URLSearchParams(widgetParams).toString()}`;
2094
+ const initWidgetButton = ({ config, widgetUrl, apiHost, widgetHost, widgetKey }) => {
2095
+ const { settings } = config;
1926
2096
  const isButtonHidden = settings == null ? void 0 : settings.hideTriggerButton;
1927
2097
  const triggerButtonIcon = settings == null ? void 0 : settings.triggerButtonIcon;
1928
2098
  try {
1929
- if (isWidgetInitialized(options.widgetKey)) return void 0;
1930
- if (isButtonExisting(options.widgetKey)) return void 0;
2099
+ if (isWidgetInitialized(widgetKey)) return void 0;
2100
+ if (isButtonExisting(widgetKey)) return void 0;
1931
2101
  insertStylesIfNeeded(config);
1932
- const { fragment, iframe } = createWidgetWithButton(
1933
- options.widgetKey,
2102
+ const { fragment, iframe, stateManager } = createWidgetWithButton(
2103
+ widgetKey,
1934
2104
  widgetUrl,
1935
2105
  {
1936
2106
  isButtonHidden: isButtonHidden || false,
@@ -1945,14 +2115,107 @@ const initIframeTrigger = (options, config) => {
1945
2115
  initMessageListener(iframe, {
1946
2116
  widgetHost,
1947
2117
  apiHost,
1948
- widgetKey: options.widgetKey,
1949
- signal
2118
+ widgetKey,
2119
+ widgetType: WidgetType.Popup,
2120
+ signal,
2121
+ stateManager
1950
2122
  });
1951
- observeColorSchemeChanges(iframe, signal);
2123
+ observeColorSchemeChanges(iframe, signal, stateManager);
1952
2124
  }, 0);
1953
2125
  };
1954
2126
  document.body.appendChild(fragment);
1955
- initializedWidgets.add(options.widgetKey);
2127
+ initializedWidgets$1.add(widgetKey);
2128
+ const cleanup = () => {
2129
+ controller.abort();
2130
+ window.removeEventListener("beforeunload", cleanup);
2131
+ };
2132
+ window.addEventListener("beforeunload", cleanup);
2133
+ return void 0;
2134
+ } catch (error) {
2135
+ handleInitializationError$1(apiHost, error, widgetKey);
2136
+ }
2137
+ };
2138
+ const SEARCH_BAR_SELECTOR = "brainfish-search-widget";
2139
+ function createSearchTrigger({ widgetKey, settings }) {
2140
+ const searchBar = document.querySelector(SEARCH_BAR_SELECTOR);
2141
+ if (!searchBar) return void 0;
2142
+ searchBar.setAttribute("data-widget-key", widgetKey);
2143
+ searchBar.setAttribute("tabindex", "0");
2144
+ const placeholderText = (settings == null ? void 0 : settings.placeholder) || "Ask your question here...";
2145
+ searchBar.setAttribute("data-placeholder", placeholderText);
2146
+ searchBar.setAttribute("aria-label", `${placeholderText} - Click to open search`);
2147
+ searchBar.setAttribute("role", "button");
2148
+ searchBar.setAttribute("aria-expanded", "false");
2149
+ searchBar.setAttribute("aria-haspopup", "dialog");
2150
+ return searchBar;
2151
+ }
2152
+ function handleInitializationError(apiHost, error, widgetKey) {
2153
+ sendBrainfishWidgetError(apiHost, error, error.message, widgetKey);
2154
+ }
2155
+ function isSearchBarExisting(widgetKey) {
2156
+ return !!document.querySelector(`.${SEARCH_BAR_IFRAME_CONTAINER_CLASS}[data-widget-key="${widgetKey}"]`);
2157
+ }
2158
+ const initSearchBar = ({ config, widgetUrl, apiHost, widgetHost, widgetKey }) => {
2159
+ const { settings } = config;
2160
+ let stateManager = IframeStateManager.getInstance(widgetKey);
2161
+ const controller = new AbortController();
2162
+ const searchBar = createSearchTrigger({ widgetKey, settings });
2163
+ if (!searchBar) return void 0;
2164
+ insertStylesIfNeeded(config);
2165
+ try {
2166
+ if (!isSearchBarExisting(widgetKey)) {
2167
+ const fragment = document.createDocumentFragment();
2168
+ if (!stateManager) {
2169
+ stateManager = IframeStateManager.initialize({
2170
+ isButtonHidden: true,
2171
+ widgetType: WidgetType.Searchbar,
2172
+ widgetKey
2173
+ });
2174
+ }
2175
+ const { iframeContainer, iframe } = createIframeContainer({
2176
+ widgetUrl,
2177
+ widgetType: WidgetType.Searchbar,
2178
+ props: {
2179
+ "data-widget-type": "searchbar",
2180
+ "data-widget-key": widgetKey
2181
+ }
2182
+ });
2183
+ const { signal } = controller;
2184
+ iframe.onload = () => {
2185
+ setTimeout(() => {
2186
+ initColorSchemeListener(iframe, signal);
2187
+ initMessageListener(iframe, {
2188
+ widgetHost,
2189
+ apiHost,
2190
+ widgetKey,
2191
+ widgetType: WidgetType.Searchbar,
2192
+ signal,
2193
+ stateManager
2194
+ });
2195
+ observeColorSchemeChanges(iframe, signal, stateManager);
2196
+ }, 0);
2197
+ };
2198
+ fragment.appendChild(iframeContainer);
2199
+ document.body.appendChild(fragment);
2200
+ }
2201
+ const displaySearchBar = () => {
2202
+ if (document.body.clientWidth >= DESKTOP_BREAKPOINT) {
2203
+ const backdrop = createElementWithClass("div", SEARCH_BAR_IFRAME_BACKDROP_CLASS);
2204
+ backdrop.onclick = () => {
2205
+ backdrop.remove();
2206
+ stateManager == null ? void 0 : stateManager.closeWidget();
2207
+ };
2208
+ document.body.appendChild(backdrop);
2209
+ }
2210
+ WidgetGlobalAPI.closeCurrentlyOpenWidget();
2211
+ stateManager == null ? void 0 : stateManager.openWidget({ newConversation: true });
2212
+ };
2213
+ searchBar.onclick = () => {
2214
+ displaySearchBar();
2215
+ };
2216
+ searchBar.onfocus = () => {
2217
+ displaySearchBar();
2218
+ };
1956
2219
  const cleanup = () => {
1957
2220
  controller.abort();
1958
2221
  window.removeEventListener("beforeunload", cleanup);
@@ -1960,7 +2223,32 @@ const initIframeTrigger = (options, config) => {
1960
2223
  window.addEventListener("beforeunload", cleanup);
1961
2224
  return void 0;
1962
2225
  } catch (error) {
1963
- handleInitializationError(apiHost, error, options.widgetKey);
2226
+ handleInitializationError(apiHost, error, widgetKey);
2227
+ }
2228
+ };
2229
+ const initIframeTrigger = (options, config) => {
2230
+ var _a, _b, _c;
2231
+ const apiHost = getApiHost(options.overrides);
2232
+ const widgetHost = getWidgetHost(options.overrides);
2233
+ const { theme, settings } = config;
2234
+ const encodedRules = base64Encode(
2235
+ JSON.stringify((_a = options.overrides) == null ? void 0 : _a.redirectRules)
2236
+ );
2237
+ const widgetParams = {
2238
+ theme: base64Encode(JSON.stringify(theme)),
2239
+ settings: base64Encode(JSON.stringify(settings)),
2240
+ redirectRules: encodedRules
2241
+ };
2242
+ if ((_b = options.overrides) == null ? void 0 : _b.regions) {
2243
+ widgetParams.regions = base64Encode(
2244
+ JSON.stringify((_c = options.overrides) == null ? void 0 : _c.regions)
2245
+ );
2246
+ }
2247
+ const widgetUrl = `${widgetHost}/?widgetKey=${options.widgetKey}#${new URLSearchParams(widgetParams).toString()}`;
2248
+ if (config.widgetType === "Search") {
2249
+ initSearchBar({ config, widgetUrl, apiHost, widgetHost, widgetKey: options.widgetKey });
2250
+ } else {
2251
+ initWidgetButton({ config, widgetUrl, apiHost, widgetHost, widgetKey: options.widgetKey });
1964
2252
  }
1965
2253
  return void 0;
1966
2254
  };
@@ -2028,6 +2316,45 @@ const initNudgeWidget = (apiHost, widgetKey) => __async(null, null, function* ()
2028
2316
  );
2029
2317
  }
2030
2318
  });
2319
+ let routeChangeController = void 0;
2320
+ let isHistoryPatched = false;
2321
+ let originalPushState;
2322
+ let originalReplaceState;
2323
+ const listenToRouteChanges = (handler) => {
2324
+ routeChangeController == null ? void 0 : routeChangeController.abort();
2325
+ routeChangeController = new AbortController();
2326
+ const { signal } = routeChangeController;
2327
+ let currentPath = window.location.pathname;
2328
+ const onRouteChange = () => {
2329
+ if (signal.aborted) return;
2330
+ const newPath = window.location.pathname;
2331
+ if (newPath !== currentPath) {
2332
+ currentPath = newPath;
2333
+ handler();
2334
+ }
2335
+ };
2336
+ window.addEventListener("popstate", onRouteChange, { signal });
2337
+ if (!isHistoryPatched) {
2338
+ originalPushState = history.pushState;
2339
+ originalReplaceState = history.replaceState;
2340
+ history.pushState = function(...args) {
2341
+ originalPushState.apply(this, args);
2342
+ setTimeout(onRouteChange, 0);
2343
+ };
2344
+ history.replaceState = function(...args) {
2345
+ originalReplaceState.apply(this, args);
2346
+ setTimeout(onRouteChange, 0);
2347
+ };
2348
+ isHistoryPatched = true;
2349
+ }
2350
+ signal.addEventListener("abort", () => {
2351
+ if (isHistoryPatched) {
2352
+ history.pushState = originalPushState;
2353
+ history.replaceState = originalReplaceState;
2354
+ isHistoryPatched = false;
2355
+ }
2356
+ });
2357
+ };
2031
2358
  let globalBrainfish = null;
2032
2359
  function getBrainfishInstance() {
2033
2360
  if (typeof window !== "undefined" && window.Brainfish) {
@@ -2042,15 +2369,35 @@ function getBrainfishInstance() {
2042
2369
  return globalBrainfish;
2043
2370
  }
2044
2371
  const Brainfish = getBrainfishInstance();
2045
- let initializationPromise = null;
2372
+ const initializationPromises = /* @__PURE__ */ new Map();
2373
+ const initializedWidgets = /* @__PURE__ */ new Map();
2374
+ let routeChangeListenerSetup = false;
2046
2375
  function initializeBrainfish(options, config, apiHost, analyticsApiHost) {
2047
2376
  return __async(this, null, function* () {
2048
2377
  var _a, _b, _c, _d, _e, _f;
2049
2378
  const enableRecording = ((_a = options.overrides) == null ? void 0 : _a.enableRecording) || ((_b = config == null ? void 0 : config.settings) == null ? void 0 : _b.enableRecording) || false;
2050
2379
  const requestedVersion = ((_c = options.overrides) == null ? void 0 : _c.version) || (config == null ? void 0 : config.version);
2380
+ initializedWidgets.set(options.widgetKey, {
2381
+ options,
2382
+ config,
2383
+ apiHost,
2384
+ analyticsApiHost
2385
+ });
2051
2386
  const initPromises = [];
2052
2387
  if (requestedVersion == null ? void 0 : requestedVersion.startsWith("2.")) {
2053
2388
  initIframeTrigger(options, config);
2389
+ if (!routeChangeListenerSetup) {
2390
+ routeChangeListenerSetup = true;
2391
+ listenToRouteChanges(() => {
2392
+ initializedWidgets.forEach(({ options: widgetOptions, config: widgetConfig }) => {
2393
+ var _a2;
2394
+ const widgetVersion = ((_a2 = widgetOptions.overrides) == null ? void 0 : _a2.version) || (widgetConfig == null ? void 0 : widgetConfig.version);
2395
+ if (widgetVersion == null ? void 0 : widgetVersion.startsWith("2.")) {
2396
+ initIframeTrigger(widgetOptions, widgetConfig);
2397
+ }
2398
+ });
2399
+ });
2400
+ }
2054
2401
  } else {
2055
2402
  initPromises.push(initSearchWidget(options, config));
2056
2403
  }
@@ -2126,6 +2473,7 @@ Brainfish.Widgets.init = (options) => __async(null, null, function* () {
2126
2473
  });
2127
2474
  config = fetchedConfig;
2128
2475
  }
2476
+ let initializationPromise = initializationPromises.get(options.widgetKey);
2129
2477
  if (!initializationPromise) {
2130
2478
  initializationPromise = initializeBrainfish(
2131
2479
  options,
@@ -2133,6 +2481,7 @@ Brainfish.Widgets.init = (options) => __async(null, null, function* () {
2133
2481
  apiHost,
2134
2482
  analyticsApiHost
2135
2483
  );
2484
+ initializationPromises.set(options.widgetKey, initializationPromise);
2136
2485
  }
2137
2486
  yield initializationPromise;
2138
2487
  yield initHandOffs(config);