@returningai/widget-sdk 1.7.1 → 1.7.2

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.
@@ -11741,9 +11741,10 @@ function sanitizeBannerHtml(html2) {
11741
11741
  ALLOW_DATA_ATTR: false
11742
11742
  });
11743
11743
  }
11744
- function resolveCssToken(token) {
11744
+ function resolveCssToken(token, preferredScope) {
11745
11745
  const normalizedToken = token.startsWith("--") ? token : `--${token}`;
11746
11746
  const candidates = [
11747
+ preferredScope,
11747
11748
  document.documentElement,
11748
11749
  document.body,
11749
11750
  ...Array.from(
@@ -11763,32 +11764,33 @@ function resolveCssToken(token) {
11763
11764
  )
11764
11765
  )
11765
11766
  ].filter(Boolean);
11767
+ const checked = /* @__PURE__ */ new Set();
11766
11768
  for (const node of candidates) {
11769
+ if (checked.has(node)) continue;
11770
+ checked.add(node);
11767
11771
  const value = getComputedStyle(node).getPropertyValue(normalizedToken).trim();
11768
11772
  if (value) return value;
11769
11773
  }
11770
11774
  for (const node of Array.from(document.querySelectorAll("*"))) {
11775
+ if (checked.has(node)) continue;
11771
11776
  const value = getComputedStyle(node).getPropertyValue(normalizedToken).trim();
11772
11777
  if (value) return value;
11773
11778
  }
11774
11779
  return void 0;
11775
11780
  }
11776
- const safeColor = (v) => {
11781
+ function safeColor(v, preferredScope) {
11777
11782
  if (!v) return void 0;
11778
11783
  const value = v.trim();
11779
11784
  if (/^(#|rgb|hsl)/i.test(value)) return value;
11780
11785
  if (/^(--)?[a-z0-9-]+$/i.test(value)) {
11781
- const resolved = resolveCssToken(value);
11782
- return resolved;
11786
+ return resolveCssToken(value, preferredScope);
11783
11787
  }
11784
11788
  const varMatch = value.match(/^var\((--[a-z0-9-]+)\)$/i);
11785
11789
  if (varMatch) {
11786
- const token = varMatch[1];
11787
- const resolved = resolveCssToken(token);
11788
- return resolved;
11790
+ return resolveCssToken(varMatch[1], preferredScope);
11789
11791
  }
11790
11792
  return void 0;
11791
- };
11793
+ }
11792
11794
  function el(tag, styles, attrs) {
11793
11795
  const node = document.createElement(tag);
11794
11796
  Object.assign(node.style, styles);
@@ -11872,15 +11874,15 @@ function justifyFromTextAlign(align) {
11872
11874
  if (align === "right") return "flex-end";
11873
11875
  return "flex-start";
11874
11876
  }
11875
- function renderBanner(banner, handlers) {
11877
+ function renderBanner(banner, handlers, colorScope) {
11876
11878
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
11877
11879
  const placement = banner.placement || {};
11878
11880
  const mode = placement.mode || "overlay";
11879
11881
  const position = placement.position || "top";
11880
11882
  const zIndex = String(placement.zIndex || 2147483e3);
11881
11883
  const imageBanner = isImageBanner(banner);
11882
- const bg = imageBanner ? "transparent" : safeColor((_a = banner.style) == null ? void 0 : _a.backgroundColor) || "#1f2024";
11883
- const color = safeColor((_b = banner.style) == null ? void 0 : _b.color) || "#ffffff";
11884
+ const bg = imageBanner ? "transparent" : safeColor((_a = banner.style) == null ? void 0 : _a.backgroundColor, colorScope) || "#1f2024";
11885
+ const color = safeColor((_b = banner.style) == null ? void 0 : _b.color, colorScope) || "#ffffff";
11884
11886
  const isCard = mode === "overlay" || mode === "popup";
11885
11887
  const ctaPosition = getCtaStyleValue(banner, ["position"]) || "center";
11886
11888
  const imageHorizontal = imageAlign((_c = banner.image) == null ? void 0 : _c.horizontalAlignment);
@@ -12021,9 +12023,13 @@ function renderBanner(banner, handlers) {
12021
12023
  }
12022
12024
  if (!imageBanner && ((_n = banner.cta) == null ? void 0 : _n.enabled)) {
12023
12025
  const ctaBg = safeColor(
12024
- getCtaStyleValue(banner, ["backgroundColor", "buttonColor", "aColor"])
12026
+ getCtaStyleValue(banner, ["backgroundColor", "buttonColor", "aColor"]),
12027
+ colorScope
12025
12028
  ) || "#2f6df6";
12026
- const ctaColor = safeColor(getCtaStyleValue(banner, ["textColor", "color"])) || "#ffffff";
12029
+ const ctaColor = safeColor(
12030
+ getCtaStyleValue(banner, ["textColor", "color"]),
12031
+ colorScope
12032
+ ) || "#ffffff";
12027
12033
  const btn = el("button", {
12028
12034
  cursor: "pointer",
12029
12035
  border: "none",
@@ -12498,6 +12504,33 @@ function clearState(state) {
12498
12504
  state.syncTimer = null;
12499
12505
  }
12500
12506
  }
12507
+ const AUTO_THEME_LEASE_STATE = "__RAI_AUTO_THEME_LEASE_STATE__";
12508
+ const DISABLE_AUTO_THEME = "__DISABLE_AUTO_THEME__";
12509
+ function acquireAutoThemeDisableLease(target = window) {
12510
+ let state = target[AUTO_THEME_LEASE_STATE];
12511
+ if (!state) {
12512
+ state = {
12513
+ count: 0,
12514
+ previousValue: target[DISABLE_AUTO_THEME]
12515
+ };
12516
+ target[AUTO_THEME_LEASE_STATE] = state;
12517
+ }
12518
+ state.count += 1;
12519
+ target[DISABLE_AUTO_THEME] = true;
12520
+ let released = false;
12521
+ return () => {
12522
+ if (released) return;
12523
+ released = true;
12524
+ state.count = Math.max(0, state.count - 1);
12525
+ if (state.count > 0) return;
12526
+ if (state.previousValue === void 0) {
12527
+ delete target[DISABLE_AUTO_THEME];
12528
+ } else {
12529
+ target[DISABLE_AUTO_THEME] = state.previousValue;
12530
+ }
12531
+ delete target[AUTO_THEME_LEASE_STATE];
12532
+ };
12533
+ }
12501
12534
  const BUNDLE_READY_TIMEOUT_MS = 3e4;
12502
12535
  function isPromiseLike(value) {
12503
12536
  return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.then === "function";
@@ -13133,6 +13166,7 @@ class BaseWidget extends HTMLElement {
13133
13166
  __publicField(this, "lottieInstances", []);
13134
13167
  __publicField(this, "callbackRegistry", new WidgetCallbackRegistry());
13135
13168
  __publicField(this, "bannerNode", null);
13169
+ __publicField(this, "releaseAutoThemeDisableLease");
13136
13170
  this.shadow = this.attachShadow({ mode: "closed" });
13137
13171
  }
13138
13172
  static get observedAttributes() {
@@ -13605,24 +13639,28 @@ class BaseWidget extends HTMLElement {
13605
13639
  if (!banner) return;
13606
13640
  if (((_a = banner.placement) == null ? void 0 : _a.mobileBehavior) === "hide" && device === "mobile")
13607
13641
  return;
13608
- const node = renderBanner(banner, {
13609
- onCtaClick: () => {
13610
- var _a2, _b2, _c;
13611
- fireTracking((_a2 = banner.tracking) == null ? void 0 : _a2.clickUrl);
13612
- this.emit("rai-banner-click", { bannerId: banner.id });
13613
- const url = (_b2 = banner.cta) == null ? void 0 : _b2.url;
13614
- if (!url) return;
13615
- if ((_c = banner.cta) == null ? void 0 : _c.openInNewTab)
13616
- window.open(url, "_blank", "noopener,noreferrer");
13617
- else window.location.href = url;
13642
+ const node = renderBanner(
13643
+ banner,
13644
+ {
13645
+ onCtaClick: () => {
13646
+ var _a2, _b2, _c;
13647
+ fireTracking((_a2 = banner.tracking) == null ? void 0 : _a2.clickUrl);
13648
+ this.emit("rai-banner-click", { bannerId: banner.id });
13649
+ const url = (_b2 = banner.cta) == null ? void 0 : _b2.url;
13650
+ if (!url) return;
13651
+ if ((_c = banner.cta) == null ? void 0 : _c.openInNewTab)
13652
+ window.open(url, "_blank", "noopener,noreferrer");
13653
+ else window.location.href = url;
13654
+ },
13655
+ onDismiss: () => {
13656
+ var _a2;
13657
+ fireTracking((_a2 = banner.tracking) == null ? void 0 : _a2.dismissUrl);
13658
+ this.emit("rai-banner-dismiss", { bannerId: banner.id });
13659
+ this.removeBanner();
13660
+ }
13618
13661
  },
13619
- onDismiss: () => {
13620
- var _a2;
13621
- fireTracking((_a2 = banner.tracking) == null ? void 0 : _a2.dismissUrl);
13622
- this.emit("rai-banner-dismiss", { bannerId: banner.id });
13623
- this.removeBanner();
13624
- }
13625
- });
13662
+ this
13663
+ );
13626
13664
  this.bannerNode = node;
13627
13665
  document.body.appendChild(node);
13628
13666
  fireTracking((_b = banner.tracking) == null ? void 0 : _b.impressionUrl);
@@ -13730,6 +13768,9 @@ class BaseWidget extends HTMLElement {
13730
13768
  return this.isConnected && initId === this.initGeneration;
13731
13769
  }
13732
13770
  teardownMountedWidget() {
13771
+ var _a;
13772
+ (_a = this.releaseAutoThemeDisableLease) == null ? void 0 : _a.call(this);
13773
+ this.releaseAutoThemeDisableLease = void 0;
13733
13774
  if (this.cleanupListener) {
13734
13775
  this.cleanupListener();
13735
13776
  this.cleanupListener = void 0;
@@ -13783,9 +13824,13 @@ class BaseWidget extends HTMLElement {
13783
13824
  }
13784
13825
  // ── Bundle mode (no-iframe) ──────────────────────────────────────────
13785
13826
  async mountWidget(initId) {
13827
+ var _a;
13786
13828
  let container = null;
13787
13829
  try {
13788
13830
  this.teardownMountedWidget();
13831
+ if (this.config.widgetType === "custom") {
13832
+ this.releaseAutoThemeDisableLease = acquireAutoThemeDisableLease();
13833
+ }
13789
13834
  const bundleApiUrl = this.config.v2ApiUrl || resolveV2ApiUrl(this.config.domainKey);
13790
13835
  const w = window;
13791
13836
  if (!w.DomainKey) {
@@ -13853,7 +13898,7 @@ class BaseWidget extends HTMLElement {
13853
13898
  // post. Mirrors the iframe behavior, where `isOpenWidget` is only
13854
13899
  // injected by SSR for the explicit `/open-widget` route.
13855
13900
  isOpenWidget: !this.state.accessToken,
13856
- defaultColorSchema: this.config.theme === "dark" ? "dark" : "light",
13901
+ defaultColorSchema: this.config.widgetType === "custom" && !this.config.themeExplicit ? null : colorMode,
13857
13902
  requestHostCallback: async (callbackName, payload) => {
13858
13903
  if (this.config.widgetType !== "store" && this.config.widgetType !== "custom") {
13859
13904
  throw new Error(
@@ -13881,6 +13926,8 @@ class BaseWidget extends HTMLElement {
13881
13926
  return;
13882
13927
  }
13883
13928
  console.error("[rai-widget] Bundle mount failed:", err);
13929
+ (_a = this.releaseAutoThemeDisableLease) == null ? void 0 : _a.call(this);
13930
+ this.releaseAutoThemeDisableLease = void 0;
13884
13931
  this.showError();
13885
13932
  }
13886
13933
  }
@@ -14056,17 +14103,19 @@ class CustomWidget extends BaseWidget {
14056
14103
  const base = config.widgetUrl.endsWith("custom-widget") ? `${config.widgetUrl}/${config.widgetId}` : config.widgetUrl;
14057
14104
  try {
14058
14105
  const u = new URL(base);
14059
- u.searchParams.set("color", config.theme);
14106
+ u.searchParams.set("auth", "postmessage");
14107
+ if (config.themeExplicit) u.searchParams.set("color", config.theme);
14060
14108
  if (config.locale) u.searchParams.set("locale", config.locale);
14061
14109
  return u.toString();
14062
14110
  } catch {
14063
- const params = [`color=${config.theme}`];
14111
+ const params = ["auth=postmessage"];
14112
+ if (config.themeExplicit) params.push(`color=${config.theme}`);
14064
14113
  if (config.locale) params.push(`locale=${encodeURIComponent(config.locale)}`);
14065
- return `${base}?${params.join("&")}`;
14114
+ return params.length > 0 ? `${base}?${params.join("&")}` : base;
14066
14115
  }
14067
14116
  }
14068
14117
  }
14069
- console.log(`[rai-widget] v${"1.7.1"}`);
14118
+ console.log(`[rai-widget] v${"1.7.2"}`);
14070
14119
  class StoreWidgetCompat extends StoreWidget {
14071
14120
  }
14072
14121
  const WIDGET_REGISTRY = [
@@ -14130,7 +14179,7 @@ function exposePublicApi() {
14130
14179
  })();
14131
14180
  const widget = container == null ? void 0 : container.querySelector(ALL_WIDGET_SELECTOR);
14132
14181
  window.ReturningAIWidget = {
14133
- version: "1.7.1",
14182
+ version: "1.7.2",
14134
14183
  reload: () => (widget == null ? void 0 : widget.reload()) ?? Promise.resolve(),
14135
14184
  logout: () => (widget == null ? void 0 : widget.logoutPublic()) ?? Promise.resolve(),
14136
14185
  isAuthenticated: () => (widget == null ? void 0 : widget.isAuthenticated()) ?? false,
@@ -19,6 +19,7 @@ export declare abstract class BaseWidget extends HTMLElement {
19
19
  private lottieInstances;
20
20
  private callbackRegistry;
21
21
  private bannerNode;
22
+ private releaseAutoThemeDisableLease?;
22
23
  constructor();
23
24
  registerCallback(callbackName: typeof STORE_CALLBACK_FIELD_OPTIONS_CALLBACK_NAME, callback: ReturningAIStoreCallbackFieldOptionsCallback): void;
24
25
  registerCallback(callbackName: typeof STORE_PURCHASE_SUCCESS_CALLBACK_NAME, callback: ReturningAIStorePurchaseSuccessCallback): void;
@@ -0,0 +1,3 @@
1
+ /** Resolve a CSS token from the owning widget before consulting the host page. */
2
+ export declare function resolveCssToken(token: string, preferredScope?: Element): string | undefined;
3
+ export declare function safeColor(v?: string, preferredScope?: Element): string | undefined;
@@ -7,6 +7,8 @@ export interface BannerHandlers {
7
7
  * Builds the banner DOM (plain elements + inline styles, so it renders correctly
8
8
  * on any host site without external CSS). Honors `placement.mode`:
9
9
  * overlay/popup → fixed centered card; inline → in-flow full-width bar.
10
+ * Color tokens resolve from `colorScope` first so a banner uses its owning
11
+ * widget's palette instead of an unrelated host-page or sibling-widget theme.
10
12
  * The caller decides where to attach it (body for overlay, target for inline).
11
13
  */
12
- export declare function renderBanner(banner: EligibleBanner, handlers: BannerHandlers): HTMLElement;
14
+ export declare function renderBanner(banner: EligibleBanner, handlers: BannerHandlers, colorScope?: Element): HTMLElement;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Keep automatic host theming disabled while one or more custom bundles are
3
+ * mounted, then restore the value that existed before the first mount.
4
+ */
5
+ export declare function acquireAutoThemeDisableLease(target?: Record<string, unknown>): () => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@returningai/widget-sdk",
3
- "version": "1.7.1",
3
+ "version": "1.7.2",
4
4
  "description": "Shadow DOM isolated widget SDK for ReturningAI",
5
5
  "main": "dist/rai-widget.iife.js",
6
6
  "module": "dist/rai-widget.js",