@returningai/widget-sdk 1.8.1 → 1.8.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.
@@ -12689,12 +12689,31 @@ function waitWithTimeout(promise, timeoutMs, timeoutMessage) {
12689
12689
  );
12690
12690
  });
12691
12691
  }
12692
- async function waitForBundleMountReadiness(mountResult, firstPaint, timeoutMs = BUNDLE_READY_TIMEOUT_MS) {
12693
- const resolvedMountResult = isPromiseLike(mountResult) ? await waitWithTimeout(
12692
+ function resolveBundleMountResult(mountResult, timeoutMs = BUNDLE_READY_TIMEOUT_MS) {
12693
+ return isPromiseLike(mountResult) ? waitWithTimeout(
12694
12694
  mountResult,
12695
12695
  timeoutMs,
12696
12696
  `Widget bundle mount did not resolve within ${timeoutMs}ms`
12697
- ) : mountResult;
12697
+ ) : Promise.resolve(mountResult);
12698
+ }
12699
+ function createBundleMountCleanup(mountResult) {
12700
+ if (typeof mountResult !== "object" || mountResult === null || !("unmount" in mountResult)) {
12701
+ return void 0;
12702
+ }
12703
+ const unmount = mountResult.unmount;
12704
+ if (typeof unmount !== "function") return void 0;
12705
+ let unmounted = false;
12706
+ return () => {
12707
+ if (unmounted) return;
12708
+ unmounted = true;
12709
+ unmount.call(mountResult);
12710
+ };
12711
+ }
12712
+ async function waitForBundleMountReadiness(mountResult, firstPaint, timeoutMs = BUNDLE_READY_TIMEOUT_MS) {
12713
+ const resolvedMountResult = await resolveBundleMountResult(
12714
+ mountResult,
12715
+ timeoutMs
12716
+ );
12698
12717
  if (!isReadyHandle(resolvedMountResult)) {
12699
12718
  await firstPaint;
12700
12719
  return "first-paint";
@@ -12706,11 +12725,51 @@ async function waitForBundleMountReadiness(mountResult, firstPaint, timeoutMs =
12706
12725
  );
12707
12726
  return "deep-ready";
12708
12727
  }
12728
+ const MAX_PRELOAD_ASSETS = 24;
12729
+ function resolveBundlePreloadAssets(bundleUrl, manifest) {
12730
+ if (manifest.version !== 1) return [];
12731
+ const normalizedBundleUrl = new URL(bundleUrl);
12732
+ const bundleDirectory = new URL("./", normalizedBundleUrl);
12733
+ const candidates = [
12734
+ ...(Array.isArray(manifest.scripts) ? manifest.scripts : []).map((value) => ({
12735
+ value,
12736
+ rel: "modulepreload",
12737
+ as: "script",
12738
+ extension: ".js"
12739
+ })),
12740
+ ...(Array.isArray(manifest.styles) ? manifest.styles : []).map((value) => ({
12741
+ value,
12742
+ rel: "preload",
12743
+ as: "style",
12744
+ extension: ".css"
12745
+ }))
12746
+ ];
12747
+ const seen = /* @__PURE__ */ new Set();
12748
+ const assets = [];
12749
+ for (const candidate of candidates) {
12750
+ if (assets.length >= MAX_PRELOAD_ASSETS) break;
12751
+ if (typeof candidate.value !== "string") continue;
12752
+ let resolved;
12753
+ try {
12754
+ resolved = new URL(candidate.value, bundleDirectory);
12755
+ } catch {
12756
+ continue;
12757
+ }
12758
+ if (resolved.origin !== bundleDirectory.origin || !resolved.pathname.startsWith(bundleDirectory.pathname) || !resolved.pathname.endsWith(candidate.extension) || seen.has(resolved.href)) {
12759
+ continue;
12760
+ }
12761
+ seen.add(resolved.href);
12762
+ assets.push({ url: resolved.href, rel: candidate.rel, as: candidate.as });
12763
+ }
12764
+ return assets;
12765
+ }
12709
12766
  function createBundleRuntimeWarmer({
12710
12767
  preloadScript: preloadScript2,
12768
+ preloadBundleAssets: preloadBundleAssets2,
12711
12769
  baseUrl
12712
12770
  }) {
12713
12771
  const preloadPromises = /* @__PURE__ */ new Map();
12772
+ const deepPreloadPromises = /* @__PURE__ */ new Map();
12714
12773
  return function warmBundleRuntime2(globalName, bundleUrl) {
12715
12774
  const normalizedUrl = normalizeBundleUrl(bundleUrl, baseUrl());
12716
12775
  if (preloadPromises.has(normalizedUrl)) return;
@@ -12718,6 +12777,12 @@ function createBundleRuntimeWarmer({
12718
12777
  preloadPromises.delete(normalizedUrl);
12719
12778
  });
12720
12779
  preloadPromises.set(normalizedUrl, preloadPromise);
12780
+ if (preloadBundleAssets2 && !deepPreloadPromises.has(normalizedUrl)) {
12781
+ const deepPreloadPromise = preloadBundleAssets2(normalizedUrl).catch(() => {
12782
+ deepPreloadPromises.delete(normalizedUrl);
12783
+ });
12784
+ deepPreloadPromises.set(normalizedUrl, deepPreloadPromise);
12785
+ }
12721
12786
  };
12722
12787
  }
12723
12788
  function isWidgetBundleRuntime(value) {
@@ -12797,6 +12862,35 @@ function preloadScript(url) {
12797
12862
  document.head.appendChild(link);
12798
12863
  });
12799
12864
  }
12865
+ const preloadedBundleAssets = /* @__PURE__ */ new Set();
12866
+ function appendBundleAssetPreload(asset) {
12867
+ const cacheKey = `${asset.rel}:${asset.url}`;
12868
+ if (preloadedBundleAssets.has(cacheKey)) return;
12869
+ const link = document.createElement("link");
12870
+ link.rel = asset.rel;
12871
+ link.as = asset.as;
12872
+ link.href = asset.url;
12873
+ link.crossOrigin = "anonymous";
12874
+ link.onload = () => void 0;
12875
+ link.onerror = () => {
12876
+ preloadedBundleAssets.delete(cacheKey);
12877
+ link.remove();
12878
+ };
12879
+ preloadedBundleAssets.add(cacheKey);
12880
+ document.head.appendChild(link);
12881
+ }
12882
+ async function preloadBundleAssets(bundleUrl) {
12883
+ const manifestUrl = new URL("./preload-manifest.json", bundleUrl);
12884
+ const response = await fetch(manifestUrl, {
12885
+ credentials: "omit",
12886
+ headers: { Accept: "application/json" }
12887
+ });
12888
+ if (!response.ok) throw new Error(`Bundle preload manifest returned ${response.status}`);
12889
+ const manifest = await response.json();
12890
+ for (const asset of resolveBundlePreloadAssets(bundleUrl, manifest)) {
12891
+ appendBundleAssetPreload(asset);
12892
+ }
12893
+ }
12800
12894
  const loadBrowserBundleRuntime = createBundleRuntimeLoader({
12801
12895
  loadScript: appendScript,
12802
12896
  readGlobal: (globalName) => window[globalName],
@@ -12804,6 +12898,7 @@ const loadBrowserBundleRuntime = createBundleRuntimeLoader({
12804
12898
  });
12805
12899
  const warmBrowserBundleRuntime = createBundleRuntimeWarmer({
12806
12900
  preloadScript,
12901
+ preloadBundleAssets,
12807
12902
  baseUrl: () => document.baseURI
12808
12903
  });
12809
12904
  function warmBundleRuntime(globalName, bundleUrl) {
@@ -13059,6 +13154,100 @@ function dispatchHostCallback(input) {
13059
13154
  }
13060
13155
  );
13061
13156
  }
13157
+ const SUPPORTED_WIDGET_LANGUAGES = [
13158
+ "en",
13159
+ "de",
13160
+ "es",
13161
+ "fr",
13162
+ "it",
13163
+ "pt",
13164
+ "pl",
13165
+ "da",
13166
+ "nl",
13167
+ "ru",
13168
+ "chs",
13169
+ "el",
13170
+ "ko",
13171
+ "cht",
13172
+ "th",
13173
+ "ja",
13174
+ "id",
13175
+ "vt",
13176
+ "ms",
13177
+ "hi",
13178
+ "fil",
13179
+ "ta",
13180
+ "tr",
13181
+ "ar",
13182
+ "mn",
13183
+ "sw"
13184
+ ];
13185
+ const supportedWidgetLanguages = new Set(SUPPORTED_WIDGET_LANGUAGES);
13186
+ const warnedUnsupportedLanguages = /* @__PURE__ */ new Set();
13187
+ const LANGUAGE_TAG_PATTERN = /^[a-z]{2,8}(?:-[a-z0-9]{1,8})*$/;
13188
+ function resolveWidgetLanguage(language) {
13189
+ const normalized = language == null ? void 0 : language.trim().toLowerCase().replace(/_/g, "-");
13190
+ if (!normalized || !LANGUAGE_TAG_PATTERN.test(normalized)) return void 0;
13191
+ const segments = normalized.split("-");
13192
+ const primaryLanguage = segments[0];
13193
+ if (primaryLanguage === "zh") {
13194
+ if (segments.includes("hant")) {
13195
+ return "cht";
13196
+ }
13197
+ if (segments.includes("hans")) {
13198
+ return "chs";
13199
+ }
13200
+ if (segments.includes("tw") || segments.includes("hk") || segments.includes("mo")) {
13201
+ return "cht";
13202
+ }
13203
+ if (segments.includes("cn") || segments.includes("sg")) {
13204
+ return "chs";
13205
+ }
13206
+ return void 0;
13207
+ }
13208
+ if (primaryLanguage === "vi" || primaryLanguage === "vt") {
13209
+ return "vt";
13210
+ }
13211
+ return supportedWidgetLanguages.has(primaryLanguage) ? primaryLanguage : void 0;
13212
+ }
13213
+ function warnUnsupportedWidgetLanguage(language) {
13214
+ const value = language.trim();
13215
+ if (!value) return;
13216
+ const warningKey = value.toLowerCase();
13217
+ if (warnedUnsupportedLanguages.has(warningKey)) return;
13218
+ warnedUnsupportedLanguages.add(warningKey);
13219
+ console.warn(
13220
+ `[rai-widget] Unsupported language "${value.slice(
13221
+ 0,
13222
+ 80
13223
+ )}". Falling back to the browser language. Supported language codes: ${SUPPORTED_WIDGET_LANGUAGES.join(
13224
+ ", "
13225
+ )}.`
13226
+ );
13227
+ }
13228
+ const PERCENTAGE_HEIGHT = /^(\d+(?:\.\d+)?)%$/;
13229
+ function parsePercentageHeight(value) {
13230
+ const match = value.trim().match(PERCENTAGE_HEIGHT);
13231
+ if (!match) return null;
13232
+ const percentage = Number(match[1]);
13233
+ return Number.isFinite(percentage) && percentage > 0 ? percentage : null;
13234
+ }
13235
+ function resolvePercentageViewportHeight(value, viewportHeight) {
13236
+ const percentage = parsePercentageHeight(value);
13237
+ return percentage === null ? 0 : viewportHeight * percentage / 100;
13238
+ }
13239
+ function resolveCollapsedRestoredHeight({
13240
+ originalHeight,
13241
+ restoredHeight,
13242
+ resolvedHeight,
13243
+ isRendered
13244
+ }) {
13245
+ if (!isRendered || Number.isFinite(restoredHeight) && restoredHeight > 0) {
13246
+ return null;
13247
+ }
13248
+ const percentage = parsePercentageHeight(originalHeight);
13249
+ return percentage === null ? `${resolvedHeight}px` : `${percentage}vh`;
13250
+ }
13062
13251
  function debounce(fn, wait) {
13063
13252
  let timer = null;
13064
13253
  return {
@@ -13213,6 +13402,54 @@ function setupMessageListener(config, state, _shadow, iframe, onRefreshScheduled
13213
13402
  heightSetter.cancel();
13214
13403
  };
13215
13404
  }
13405
+ async function resolveStartupAuthentication(authentication, loadErrorSettings) {
13406
+ const authenticated = await authentication;
13407
+ if (authenticated) {
13408
+ void loadErrorSettings({ skipCache: true }).catch(() => {
13409
+ });
13410
+ return true;
13411
+ }
13412
+ try {
13413
+ await loadErrorSettings();
13414
+ } catch {
13415
+ }
13416
+ return false;
13417
+ }
13418
+ function resolveWidgetPreconnectOrigins(config, baseUrl) {
13419
+ const values = [
13420
+ config.apiUrl,
13421
+ config.v2ApiUrl,
13422
+ config.authUrl,
13423
+ config.bundleUrl,
13424
+ config.widgetUrl,
13425
+ config.widgetDomain
13426
+ ];
13427
+ const origins = [];
13428
+ for (const value of values) {
13429
+ if (!value) continue;
13430
+ try {
13431
+ const url = new URL(value, baseUrl);
13432
+ if ((url.protocol === "http:" || url.protocol === "https:") && !origins.includes(url.origin)) {
13433
+ origins.push(url.origin);
13434
+ }
13435
+ } catch {
13436
+ }
13437
+ }
13438
+ return origins;
13439
+ }
13440
+ function ensureWidgetPreconnects(origins, targetDocument) {
13441
+ const documentTarget = document;
13442
+ for (const origin of origins) {
13443
+ const id = `rai-widget-preconnect-${origin.replace(/[^a-z0-9]+/gi, "-")}`;
13444
+ if (documentTarget.getElementById(id)) continue;
13445
+ const link = documentTarget.createElement("link");
13446
+ link.id = id;
13447
+ link.rel = "preconnect";
13448
+ link.href = origin;
13449
+ link.crossOrigin = "anonymous";
13450
+ documentTarget.head.appendChild(link);
13451
+ }
13452
+ }
13216
13453
  const widgetCSS = ':host{display:block;position:relative;width:100%;height:100%}.rai-loader{position:absolute;top:0;left:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.75rem;width:100%;height:100%;background:var(--rai-loading-background, var(--background-5, var(--rai-loader-bg, #ffffff)));border-radius:8px;z-index:10;transition:opacity .3s ease-out;font-family:var(--rai-loader-font, "Roboto", system-ui, -apple-system, sans-serif)}.rai-loader.fade-out{opacity:0;pointer-events:none}.rai-loader-pending-settings{visibility:hidden}.rai-loader-text{margin:.5rem 0 0;font-family:var(--rai-loading-text-font, var(--rai-loader-font, "Roboto", system-ui, -apple-system, sans-serif));font-size:var(--rai-loading-text-size, 1.125rem);line-height:1.75rem;font-weight:var(--rai-loading-text-weight, 400);font-style:var(--rai-loading-text-style, normal);text-decoration:var(--rai-loading-text-decoration, none);letter-spacing:normal;color:var(--rai-loading-text-color, var(--accent-1, var(--rai-accent, #000000)));text-align:center}@media (min-width: 768px){.rai-loader-text{font-size:var(--rai-loading-text-size, 1.5rem);line-height:2rem}}.rai-error{display:none;position:absolute;top:0;left:0;width:100%;height:100%;align-items:center;justify-content:center;flex-direction:column;gap:12px;background:var(--rai-error-bg, #1a1a1a);border-radius:8px;padding:24px;box-sizing:border-box;text-align:center;color:var(--rai-error-text, #9ca3af);font-family:system-ui,-apple-system,sans-serif;z-index:10}.rai-error.visible{display:flex}iframe{display:block;border:none;opacity:0;transition:opacity .3s ease-in}iframe.loaded{opacity:1}.rai-lottie-loader{width:100px;height:100px}.rai-lottie-loader svg{width:100%!important;height:100%!important}.rai-lottie-spinner{width:32px;height:32px;margin:34px auto;border:3px solid var(--rai-text4, #6b7280);border-top-color:var(--rai-accent, #000000);border-radius:50%;animation:rai-spin .7s linear infinite}@keyframes rai-spin{to{transform:rotate(360deg)}}.rai-sdk-loader{position:relative;width:75px;height:100px}.rai-sdk-loader__bar{position:absolute;bottom:0;width:10px;height:50%;background:var(--rai-accent, #000000);transform-origin:center bottom;box-shadow:1px 1px #0003}.rai-sdk-loader__bar:nth-child(1){left:0;transform:scaleY(.2);animation:rai-sdk-barUp1 4s infinite}.rai-sdk-loader__bar:nth-child(2){left:15px;transform:scaleY(.4);animation:rai-sdk-barUp2 4s infinite}.rai-sdk-loader__bar:nth-child(3){left:30px;transform:scaleY(.6);animation:rai-sdk-barUp3 4s infinite}.rai-sdk-loader__bar:nth-child(4){left:45px;transform:scaleY(.8);animation:rai-sdk-barUp4 4s infinite}.rai-sdk-loader__bar:nth-child(5){left:60px;transform:scale(1);animation:rai-sdk-barUp5 4s infinite}.rai-sdk-loader__ball{position:absolute;bottom:10px;left:0;width:10px;height:10px;background:var(--rai-accent, #000000);border-radius:50%;animation:rai-sdk-ball 4s infinite}@keyframes rai-sdk-ball{0%{transform:translate(0)}5%{transform:translate(8px,-14px)}10%{transform:translate(15px,-10px)}17%{transform:translate(23px,-24px)}20%{transform:translate(30px,-20px)}27%{transform:translate(38px,-34px)}30%{transform:translate(45px,-30px)}37%{transform:translate(53px,-44px)}40%{transform:translate(60px,-40px)}50%{transform:translate(60px)}57%{transform:translate(53px,-14px)}60%{transform:translate(45px,-10px)}67%{transform:translate(37px,-24px)}70%{transform:translate(30px,-20px)}77%{transform:translate(22px,-34px)}80%{transform:translate(15px,-30px)}87%{transform:translate(7px,-44px)}90%{transform:translateY(-40px)}to{transform:translate(0)}}@keyframes rai-sdk-barUp1{0%{transform:scaleY(.2)}40%{transform:scaleY(.2)}50%{transform:scale(1)}90%{transform:scale(1)}to{transform:scaleY(.2)}}@keyframes rai-sdk-barUp2{0%{transform:scaleY(.4)}40%{transform:scaleY(.4)}50%{transform:scaleY(.8)}90%{transform:scaleY(.8)}to{transform:scaleY(.4)}}@keyframes rai-sdk-barUp3{0%{transform:scaleY(.6)}to{transform:scaleY(.6)}}@keyframes rai-sdk-barUp4{0%{transform:scaleY(.8)}40%{transform:scaleY(.8)}50%{transform:scaleY(.4)}90%{transform:scaleY(.4)}to{transform:scaleY(.8)}}@keyframes rai-sdk-barUp5{0%{transform:scale(1)}40%{transform:scale(1)}50%{transform:scaleY(.2)}90%{transform:scaleY(.2)}to{transform:scale(1)}}#loading-square{width:75px;aspect-ratio:1;display:flex;color:var(--rai-accent, #000000);background:linear-gradient(currentColor 0 0) right / 51% 100%,linear-gradient(currentColor 0 0) bottom / 100% 51%;background-repeat:no-repeat;animation:l16-0 2s infinite linear .25s}#loading-square>div{width:50%;height:50%;background:currentColor;animation:l16-1 .5s infinite linear}@keyframes l16-0{0%,12.49%{transform:rotate(0)}12.5%,37.49%{transform:rotate(90deg)}37.5%,62.49%{transform:rotate(180deg)}62.5%,87.49%{transform:rotate(270deg)}87.5%,to{transform:rotate(360deg)}}@keyframes l16-1{0%{transform:perspective(80px) rotate3d(-1,-1,0,0)}80%,to{transform:perspective(80px) rotate3d(-1,-1,0,-180deg)}}#loading-circle{width:75px;aspect-ratio:1;display:grid;grid:50%/50%;color:var(--rai-accent, #000000);border-radius:50%;--_g: no-repeat linear-gradient(currentColor 0 0);background:var(--_g),var(--_g),var(--_g);background-size:50.1% 50.1%;animation:l9-0 1.5s infinite steps(1) alternate,l9-0-0 3s infinite steps(1) alternate}#loading-circle>div{background:var(--rai-text4, #6b7280);border-top-left-radius:100px;transform:perspective(150px) rotateY(0) rotateX(0);transform-origin:bottom right;animation:l9-1 1.5s infinite linear alternate}@keyframes l9-0{0%{background-position:0 100%,100% 100%,100% 0}33%{background-position:100% 100%,100% 100%,100% 0}66%{background-position:100% 0,100% 0,100% 0}}@keyframes l9-0-0{0%{transform:scaleX(1) rotate(0)}50%{transform:scaleX(-1) rotate(-90deg)}}@keyframes l9-1{16.5%{transform:perspective(150px) rotateX(-90deg) rotateY(0) rotateX(0);filter:grayscale(.8)}33%{transform:perspective(150px) rotateX(-180deg) rotateY(0) rotateX(0)}66%{transform:perspective(150px) rotateX(-180deg) rotateY(-180deg) rotateX(0)}to{transform:perspective(150px) rotateX(-180deg) rotateY(-180deg) rotateX(-180deg);filter:grayscale(.8)}}.rai-custom-error{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.tiptap strong.tw-font-bold,.tiptap b.tw-font-bold{font-weight:700}.tiptap em.tw-font-italic,.tiptap i.tw-font-italic{font-style:italic}.tiptap a.tiptap-rich-text-link{color:#3b82f6;text-decoration:underline;position:relative;display:inline-block;width:fit-content}.tiptap a.tiptap-rich-text-link:hover{color:#3b82f6cc;text-decoration:none}.tiptap a.tiptap-rich-text-link:not([title=""]):before{content:attr(title);position:absolute;top:-2rem;left:50%;transform:translate(-50%);white-space:nowrap;border-radius:.25rem;background-color:#000;padding:.25rem .5rem;color:#fff;opacity:0;transition:opacity .2s;pointer-events:none}.tiptap a.tiptap-rich-text-link:not([title=""]):hover:before{opacity:1}.tiptap p[style*="text-align: left"]{text-align:left}.tiptap p[style*="text-align: center"]{text-align:center}.tiptap p[style*="text-align: right"]{text-align:right}.tiptap p[style*="text-align: justify"]{text-align:justify}.tiptap u{text-decoration:underline}.tiptap.ProseMirror{background-color:transparent;height:fit-content!important;word-wrap:break-word;white-space:pre-wrap;white-space:break-spaces;-webkit-font-variant-ligatures:none;font-variant-ligatures:none;font-feature-settings:"liga" 0;padding:0;width:100%;overflow-y:auto}.tiptap{line-height:1.6;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.tiptap p{margin:.5em 0}.tiptap p:first-child{margin-top:0}.tiptap p:last-child{margin-bottom:0}.tiptap img{max-width:100%;height:auto;display:block}.tiptap p[style*="text-align: left"] img{margin-left:0;margin-right:auto}.tiptap p[style*="text-align: center"] img{margin-left:auto;margin-right:auto}.tiptap p[style*="text-align: right"] img{margin-left:auto;margin-right:0}.rai-retry-btn{padding:8px 20px;border:none;border-radius:6px;background:var(--rai-accent, #000000);color:var(--rai-error-bg, #ffffff);font-size:14px;font-family:system-ui,-apple-system,sans-serif;cursor:pointer}.rai-retry-btn:hover{opacity:.85}';
13217
13454
  const DEFINED_ATTRS = /* @__PURE__ */ new Set([
13218
13455
  "community-id",
@@ -13233,6 +13470,7 @@ const DEFINED_ATTRS = /* @__PURE__ */ new Set([
13233
13470
  "max-retries",
13234
13471
  "retry-delay",
13235
13472
  "height-debounce",
13473
+ "language",
13236
13474
  "locale",
13237
13475
  "custom-data",
13238
13476
  "retry-label",
@@ -13493,6 +13731,13 @@ function readConfig(el2, existingId) {
13493
13731
  return void 0;
13494
13732
  }
13495
13733
  })();
13734
+ const explicitLanguage = get("language").trim();
13735
+ const legacyLocale = get("locale").trim();
13736
+ const requestedLanguage = explicitLanguage || legacyLocale || void 0;
13737
+ const language = resolveWidgetLanguage(requestedLanguage);
13738
+ if (requestedLanguage && !language) {
13739
+ warnUnsupportedWidgetLanguage(requestedLanguage);
13740
+ }
13496
13741
  return {
13497
13742
  communityId,
13498
13743
  channelId,
@@ -13518,7 +13763,12 @@ function readConfig(el2, existingId) {
13518
13763
  // #2
13519
13764
  heightDebounce: num("height-debounce", 100),
13520
13765
  // #4
13521
- locale: get("locale") || void 0,
13766
+ // `language` is the canonical forced widget locale. Keep `locale` as a
13767
+ // backwards-compatible alias only when it supplied the selected value.
13768
+ // Unsupported explicit values are omitted so the widget uses its existing
13769
+ // browser-language fallback.
13770
+ language,
13771
+ locale: !explicitLanguage && legacyLocale ? language : void 0,
13522
13772
  // #5
13523
13773
  customData,
13524
13774
  // #8
@@ -13580,6 +13830,8 @@ class BaseWidget extends HTMLElement {
13580
13830
  __publicField(this, "config");
13581
13831
  __publicField(this, "state", createInitialState());
13582
13832
  __publicField(this, "loaderEl", null);
13833
+ __publicField(this, "loaderHeightRestore", null);
13834
+ __publicField(this, "loaderHeightObserver", null);
13583
13835
  __publicField(this, "errorEl", null);
13584
13836
  __publicField(this, "msgEl", null);
13585
13837
  __publicField(this, "cleanupListener");
@@ -13591,14 +13843,21 @@ class BaseWidget extends HTMLElement {
13591
13843
  __publicField(this, "hasAttemptedInit", false);
13592
13844
  __publicField(this, "initGeneration", 0);
13593
13845
  __publicField(this, "lottieInstances", []);
13594
- __publicField(this, "renderedLoaderAnimationKey", null);
13595
13846
  __publicField(this, "callbackRegistry", new WidgetCallbackRegistry());
13596
13847
  __publicField(this, "bannerNode", null);
13597
13848
  __publicField(this, "releaseAutoThemeDisableLease");
13849
+ __publicField(this, "bundleUnmount");
13598
13850
  this.shadow = this.attachShadow({ mode: "closed" });
13599
13851
  }
13600
13852
  static get observedAttributes() {
13601
- return ["embed-token", "data-embed-token"];
13853
+ return [
13854
+ "embed-token",
13855
+ "data-embed-token",
13856
+ "language",
13857
+ "data-language",
13858
+ "locale",
13859
+ "data-locale"
13860
+ ];
13602
13861
  }
13603
13862
  registerCallback(callbackName, callback) {
13604
13863
  this.callbackRegistry.registerCallback(callbackName, callback);
@@ -13610,6 +13869,9 @@ class BaseWidget extends HTMLElement {
13610
13869
  this.config = readConfig(this, void 0);
13611
13870
  this.identitySignature = identitySignatureForConfig(this.config);
13612
13871
  this.setupIdentityAttributeObserver();
13872
+ ensureWidgetPreconnects(
13873
+ resolveWidgetPreconnectOrigins(this.config, document.baseURI)
13874
+ );
13613
13875
  const style = document.createElement("style");
13614
13876
  style.textContent = widgetCSS;
13615
13877
  this.shadow.appendChild(style);
@@ -13645,13 +13907,23 @@ class BaseWidget extends HTMLElement {
13645
13907
  (_c = this.identityAttributeObserver) == null ? void 0 : _c.disconnect();
13646
13908
  this.teardownMountedWidget();
13647
13909
  this.clearLottieInstances();
13910
+ this.restoreLoaderHeight();
13648
13911
  this.removeBanner();
13649
13912
  clearState(this.state);
13650
13913
  }
13651
13914
  attributeChangedCallback(name, oldValue, newValue) {
13652
13915
  if (oldValue === newValue) return;
13653
- if (name !== "embed-token" && name !== "data-embed-token") return;
13654
- this.handleIdentityConfigChange();
13916
+ if (name === "embed-token" || name === "data-embed-token") {
13917
+ this.handleIdentityConfigChange();
13918
+ return;
13919
+ }
13920
+ if (this.isConnected && this.config) {
13921
+ if (this.hasAttemptedInit) {
13922
+ void this.reload();
13923
+ } else {
13924
+ this.config = readConfig(this, this.config.communityId);
13925
+ }
13926
+ }
13655
13927
  }
13656
13928
  setupIdentityAttributeObserver() {
13657
13929
  var _a;
@@ -13807,22 +14079,6 @@ class BaseWidget extends HTMLElement {
13807
14079
  textEl.hidden = !loadingText.enabled;
13808
14080
  }
13809
14081
  }
13810
- getLoadingAnimationRenderKey() {
13811
- var _a, _b, _c;
13812
- const animation = this.state.loadingAnimation;
13813
- const theme = this.config.theme === "dark" ? "dark" : "light";
13814
- const custom = (animation == null ? void 0 : animation.type) === "custom" ? ((_a = animation.custom) == null ? void 0 : _a[theme]) || ((_b = animation.custom) == null ? void 0 : _b.light) || ((_c = animation.custom) == null ? void 0 : _c.dark) : void 0;
13815
- return JSON.stringify({
13816
- theme,
13817
- type: (animation == null ? void 0 : animation.type) ?? null,
13818
- preset: (animation == null ? void 0 : animation.preset) ?? null,
13819
- custom: custom ? {
13820
- type: custom.type ?? null,
13821
- html: custom.html ?? null,
13822
- lottie: custom.lottie ?? null
13823
- } : null
13824
- });
13825
- }
13826
14082
  renderShell() {
13827
14083
  this.loaderEl = document.createElement("div");
13828
14084
  this.loaderEl.className = "rai-loader";
@@ -13834,6 +14090,7 @@ class BaseWidget extends HTMLElement {
13834
14090
  textEl.textContent = this.config.loadingText;
13835
14091
  this.loaderEl.appendChild(textEl);
13836
14092
  this.shadow.appendChild(this.loaderEl);
14093
+ this.freezeIframeLoaderHeight();
13837
14094
  this.applyLoadingSettings();
13838
14095
  this.fetchAndRenderLoader();
13839
14096
  this.errorEl = document.createElement("div");
@@ -13854,11 +14111,9 @@ class BaseWidget extends HTMLElement {
13854
14111
  * Loads the community loading animation + theme palette from cache or API
13855
14112
  * and renders the configured animation above the loading text.
13856
14113
  *
13857
- * Stale-while-revalidate:
13858
- * - Cache hit → apply palette & render animation synchronously, fire
13859
- * background refresh.
13860
- * - Cache miss → retain the initial shell; await network; apply palette +
13861
- * render the resolved animation or fallback.
14114
+ * Cached settings are applied synchronously. On a first visit, settings load
14115
+ * alongside authentication so the generic loader stays hidden until the
14116
+ * configured animation is known. This request never blocks widget launch.
13862
14117
  */
13863
14118
  async fetchAndRenderLoader() {
13864
14119
  var _a;
@@ -13867,26 +14122,14 @@ class BaseWidget extends HTMLElement {
13867
14122
  applyErrorSettingsToState(this.state, cached);
13868
14123
  this.applyLoadingSettings();
13869
14124
  this.renderLoaderAnimation();
13870
- void fetchErrorSettings(this.config, this.state, { skipCache: true }).then(() => {
13871
- var _a2;
13872
- if (!this.loaderEl) return;
13873
- const theme2 = this.config.theme === "dark" ? "dark" : "light";
13874
- const palette2 = (_a2 = this.state.themePalette) == null ? void 0 : _a2[theme2];
13875
- if (palette2) this.applyPalette(palette2);
13876
- if (this.state.font) this.applyFont(this.state.font);
13877
- this.applyLoadingSettings();
13878
- if (this.renderedLoaderAnimationKey !== this.getLoadingAnimationRenderKey()) {
13879
- this.renderLoaderAnimation();
13880
- }
13881
- }).catch(() => {
13882
- });
13883
14125
  return;
13884
14126
  }
13885
14127
  try {
13886
14128
  await fetchErrorSettings(this.config, this.state);
13887
14129
  } catch {
13888
14130
  }
13889
- if (!this.loaderEl) return;
14131
+ const loaderEl = this.loaderEl;
14132
+ if (!loaderEl) return;
13890
14133
  if (!this.config.themeExplicit && (this.state.defaultColorSchema === "light" || this.state.defaultColorSchema === "dark") && this.state.defaultColorSchema !== this.config.theme) {
13891
14134
  this.config.theme = this.state.defaultColorSchema;
13892
14135
  }
@@ -13896,7 +14139,7 @@ class BaseWidget extends HTMLElement {
13896
14139
  if (this.state.font) this.applyFont(this.state.font);
13897
14140
  this.applyLoadingSettings();
13898
14141
  this.renderLoaderAnimation();
13899
- this.loaderEl.classList.remove("rai-loader-pending-settings");
14142
+ loaderEl.classList.remove("rai-loader-pending-settings");
13900
14143
  }
13901
14144
  /**
13902
14145
  * Append the loading animation above the text. Removes any previous
@@ -13905,7 +14148,6 @@ class BaseWidget extends HTMLElement {
13905
14148
  */
13906
14149
  renderLoaderAnimation() {
13907
14150
  if (!this.loaderEl) return;
13908
- this.renderedLoaderAnimationKey = this.getLoadingAnimationRenderKey();
13909
14151
  this.clearLottieInstances();
13910
14152
  const existing = this.loaderEl.querySelector(".rai-loader-anim");
13911
14153
  if (existing) existing.remove();
@@ -13992,9 +14234,82 @@ class BaseWidget extends HTMLElement {
13992
14234
  setTimeout(() => {
13993
14235
  loader.remove();
13994
14236
  lottieInstances.forEach((instance) => instance.destroy());
14237
+ this.restoreLoaderHeight();
13995
14238
  }, 300);
13996
14239
  this.loaderEl = null;
13997
14240
  }
14241
+ /**
14242
+ * Keep percentage-based iframe embeds at their resolved startup height while
14243
+ * the SDK loader is visible. The child iframe may resize during auth/data
14244
+ * loading; without this pin the loader's 100% centering moves with the host.
14245
+ */
14246
+ freezeIframeLoaderHeight() {
14247
+ var _a;
14248
+ if (this.config.bundleUrl || this.loaderHeightRestore) return;
14249
+ const measuredHeight = this.getBoundingClientRect().height;
14250
+ const isRendered = this.getClientRects().length > 0;
14251
+ const hasPercentageHeight = parsePercentageHeight(this.style.height) !== null;
14252
+ const percentageHeight = resolvePercentageViewportHeight(
14253
+ this.style.height,
14254
+ window.innerHeight
14255
+ );
14256
+ if (measuredHeight <= 0 && hasPercentageHeight && !isRendered) {
14257
+ this.deferIframeLoaderHeightFreeze();
14258
+ return;
14259
+ }
14260
+ const resolvedHeight = measuredHeight > 0 ? measuredHeight : percentageHeight;
14261
+ if (!Number.isFinite(resolvedHeight) || resolvedHeight <= 0) return;
14262
+ (_a = this.loaderHeightObserver) == null ? void 0 : _a.disconnect();
14263
+ this.loaderHeightObserver = null;
14264
+ this.loaderHeightRestore = {
14265
+ value: this.style.getPropertyValue("height"),
14266
+ priority: this.style.getPropertyPriority("height"),
14267
+ resolvedHeight
14268
+ };
14269
+ this.style.setProperty("height", `${resolvedHeight}px`);
14270
+ }
14271
+ deferIframeLoaderHeightFreeze() {
14272
+ if (this.loaderHeightObserver || typeof ResizeObserver === "undefined") {
14273
+ return;
14274
+ }
14275
+ this.loaderHeightObserver = new ResizeObserver(() => {
14276
+ var _a, _b;
14277
+ if (!this.isConnected || !this.loaderEl || this.config.bundleUrl) {
14278
+ (_a = this.loaderHeightObserver) == null ? void 0 : _a.disconnect();
14279
+ this.loaderHeightObserver = null;
14280
+ return;
14281
+ }
14282
+ if (this.getClientRects().length === 0) return;
14283
+ (_b = this.loaderHeightObserver) == null ? void 0 : _b.disconnect();
14284
+ this.loaderHeightObserver = null;
14285
+ this.freezeIframeLoaderHeight();
14286
+ });
14287
+ this.loaderHeightObserver.observe(this);
14288
+ }
14289
+ restoreLoaderHeight() {
14290
+ var _a;
14291
+ (_a = this.loaderHeightObserver) == null ? void 0 : _a.disconnect();
14292
+ this.loaderHeightObserver = null;
14293
+ if (!this.loaderHeightRestore) return;
14294
+ const { value, priority, resolvedHeight } = this.loaderHeightRestore;
14295
+ this.loaderHeightRestore = null;
14296
+ if (value) {
14297
+ this.style.setProperty("height", value, priority);
14298
+ } else {
14299
+ this.style.removeProperty("height");
14300
+ }
14301
+ const restoredHeight = this.getBoundingClientRect().height;
14302
+ const isRendered = this.isConnected && this.getClientRects().length > 0;
14303
+ const collapsedFallback = resolveCollapsedRestoredHeight({
14304
+ originalHeight: value,
14305
+ restoredHeight,
14306
+ resolvedHeight,
14307
+ isRendered
14308
+ });
14309
+ if (collapsedFallback) {
14310
+ this.style.setProperty("height", collapsedFallback, priority);
14311
+ }
14312
+ }
13998
14313
  /**
13999
14314
  * Resolve after a legacy bundle's first rendered child has reached a paint.
14000
14315
  * This remains the fallback for bundles that do not expose a deep-ready
@@ -14207,14 +14522,10 @@ class BaseWidget extends HTMLElement {
14207
14522
  this.config.bundleUrl
14208
14523
  );
14209
14524
  }
14210
- const errorSettingsPromise = fetchErrorSettings(this.config, this.state);
14211
- const finishStartup = async (authentication) => {
14212
- const [authenticated] = await Promise.all([
14213
- authentication,
14214
- errorSettingsPromise
14215
- ]);
14216
- return authenticated;
14217
- };
14525
+ const finishStartup = (authentication) => resolveStartupAuthentication(
14526
+ authentication,
14527
+ (options) => fetchErrorSettings(this.config, this.state, options)
14528
+ );
14218
14529
  const launchWidget = () => {
14219
14530
  var _a;
14220
14531
  if (!this.isCurrentInit(initId)) return;
@@ -14314,6 +14625,13 @@ class BaseWidget extends HTMLElement {
14314
14625
  }
14315
14626
  teardownMountedWidget() {
14316
14627
  var _a;
14628
+ const bundleUnmount = this.bundleUnmount;
14629
+ this.bundleUnmount = void 0;
14630
+ try {
14631
+ bundleUnmount == null ? void 0 : bundleUnmount();
14632
+ } catch (error) {
14633
+ console.error("[rai-widget] Bundle unmount failed:", error);
14634
+ }
14317
14635
  (_a = this.releaseAutoThemeDisableLease) == null ? void 0 : _a.call(this);
14318
14636
  this.releaseAutoThemeDisableLease = void 0;
14319
14637
  if (this.cleanupListener) {
@@ -14389,6 +14707,7 @@ class BaseWidget extends HTMLElement {
14389
14707
  async mountWidget(initId) {
14390
14708
  var _a;
14391
14709
  let container = null;
14710
+ let cleanupBundleMount;
14392
14711
  try {
14393
14712
  this.teardownMountedWidget();
14394
14713
  const autoHeight = this.config.height.trim().toLowerCase() === "auto";
@@ -14451,6 +14770,7 @@ class BaseWidget extends HTMLElement {
14451
14770
  // injected by SSR for the explicit `/open-widget` route.
14452
14771
  isOpenWidget: !this.state.accessToken,
14453
14772
  defaultColorSchema: this.config.widgetType === "custom" && !this.config.themeExplicit ? null : colorMode,
14773
+ language: this.config.language,
14454
14774
  // Bundle loading screens are rendered beneath an instance-scoped
14455
14775
  // provider. Do not put these values on window: pages can mount several
14456
14776
  // widget types at once.
@@ -14473,8 +14793,20 @@ class BaseWidget extends HTMLElement {
14473
14793
  transport: "bundle"
14474
14794
  })
14475
14795
  });
14476
- await waitForBundleMountReadiness(mountResult, firstPaint);
14796
+ const resolvedMountResult = await resolveBundleMountResult(mountResult);
14797
+ cleanupBundleMount = createBundleMountCleanup(resolvedMountResult);
14477
14798
  if (!this.isCurrentInit(initId) || !container.isConnected) {
14799
+ cleanupBundleMount == null ? void 0 : cleanupBundleMount();
14800
+ container.remove();
14801
+ return;
14802
+ }
14803
+ this.bundleUnmount = cleanupBundleMount;
14804
+ await waitForBundleMountReadiness(resolvedMountResult, firstPaint);
14805
+ if (!this.isCurrentInit(initId) || !container.isConnected) {
14806
+ cleanupBundleMount == null ? void 0 : cleanupBundleMount();
14807
+ if (this.bundleUnmount === cleanupBundleMount) {
14808
+ this.bundleUnmount = void 0;
14809
+ }
14478
14810
  container.remove();
14479
14811
  return;
14480
14812
  }
@@ -14482,11 +14814,16 @@ class BaseWidget extends HTMLElement {
14482
14814
  this.hideLoader();
14483
14815
  this.emit("rai-mounted");
14484
14816
  } catch (err) {
14817
+ cleanupBundleMount == null ? void 0 : cleanupBundleMount();
14818
+ if (this.bundleUnmount === cleanupBundleMount) {
14819
+ this.bundleUnmount = void 0;
14820
+ }
14485
14821
  if (!this.isCurrentInit(initId)) {
14486
14822
  container == null ? void 0 : container.remove();
14487
14823
  return;
14488
14824
  }
14489
14825
  console.error("[rai-widget] Bundle mount failed:", err);
14826
+ container == null ? void 0 : container.remove();
14490
14827
  (_a = this.releaseAutoThemeDisableLease) == null ? void 0 : _a.call(this);
14491
14828
  this.releaseAutoThemeDisableLease = void 0;
14492
14829
  this.showError();
@@ -14578,6 +14915,7 @@ class StoreWidget extends BaseWidget {
14578
14915
  url.searchParams.set("containerId", config.container);
14579
14916
  url.searchParams.set("connectType", "simple");
14580
14917
  url.searchParams.set("mode", "private");
14918
+ if (config.language) url.searchParams.set("language", config.language);
14581
14919
  if (config.locale) url.searchParams.set("locale", config.locale);
14582
14920
  return url.toString();
14583
14921
  }
@@ -14588,10 +14926,12 @@ class ChannelWidget extends BaseWidget {
14588
14926
  try {
14589
14927
  const u = new URL(base);
14590
14928
  u.searchParams.set("color", config.theme);
14929
+ if (config.language) u.searchParams.set("language", config.language);
14591
14930
  if (config.locale) u.searchParams.set("locale", config.locale);
14592
14931
  return u.toString();
14593
14932
  } catch {
14594
14933
  const params = [`color=${config.theme}`];
14934
+ if (config.language) params.push(`language=${encodeURIComponent(config.language)}`);
14595
14935
  if (config.locale) params.push(`locale=${encodeURIComponent(config.locale)}`);
14596
14936
  return `${base}?${params.join("&")}`;
14597
14937
  }
@@ -14603,10 +14943,12 @@ class MilestoneWidget extends BaseWidget {
14603
14943
  try {
14604
14944
  const u = new URL(base);
14605
14945
  u.searchParams.set("color", config.theme);
14946
+ if (config.language) u.searchParams.set("language", config.language);
14606
14947
  if (config.locale) u.searchParams.set("locale", config.locale);
14607
14948
  return u.toString();
14608
14949
  } catch {
14609
14950
  const params = [`color=${config.theme}`];
14951
+ if (config.language) params.push(`language=${encodeURIComponent(config.language)}`);
14610
14952
  if (config.locale) params.push(`locale=${encodeURIComponent(config.locale)}`);
14611
14953
  return `${base}?${params.join("&")}`;
14612
14954
  }
@@ -14618,10 +14960,12 @@ class SocialWidget extends BaseWidget {
14618
14960
  try {
14619
14961
  const u = new URL(base);
14620
14962
  u.searchParams.set("color", config.theme);
14963
+ if (config.language) u.searchParams.set("language", config.language);
14621
14964
  if (config.locale) u.searchParams.set("locale", config.locale);
14622
14965
  return u.toString();
14623
14966
  } catch {
14624
14967
  const params = [`color=${config.theme}`];
14968
+ if (config.language) params.push(`language=${encodeURIComponent(config.language)}`);
14625
14969
  if (config.locale) params.push(`locale=${encodeURIComponent(config.locale)}`);
14626
14970
  return `${base}?${params.join("&")}`;
14627
14971
  }
@@ -14633,10 +14977,12 @@ class CurrencyWidget extends BaseWidget {
14633
14977
  try {
14634
14978
  const u = new URL(base);
14635
14979
  u.searchParams.set("color", config.theme);
14980
+ if (config.language) u.searchParams.set("language", config.language);
14636
14981
  if (config.locale) u.searchParams.set("locale", config.locale);
14637
14982
  return u.toString();
14638
14983
  } catch {
14639
14984
  const params = [`color=${config.theme}`];
14985
+ if (config.language) params.push(`language=${encodeURIComponent(config.language)}`);
14640
14986
  if (config.locale) params.push(`locale=${encodeURIComponent(config.locale)}`);
14641
14987
  return `${base}?${params.join("&")}`;
14642
14988
  }
@@ -14648,10 +14994,12 @@ class ReferralWidget extends BaseWidget {
14648
14994
  try {
14649
14995
  const u = new URL(base);
14650
14996
  u.searchParams.set("color", config.theme);
14997
+ if (config.language) u.searchParams.set("language", config.language);
14651
14998
  if (config.locale) u.searchParams.set("locale", config.locale);
14652
14999
  return u.toString();
14653
15000
  } catch {
14654
15001
  const params = [`color=${config.theme}`];
15002
+ if (config.language) params.push(`language=${encodeURIComponent(config.language)}`);
14655
15003
  if (config.locale) params.push(`locale=${encodeURIComponent(config.locale)}`);
14656
15004
  return `${base}?${params.join("&")}`;
14657
15005
  }
@@ -14664,17 +15012,19 @@ class CustomWidget extends BaseWidget {
14664
15012
  const u = new URL(base);
14665
15013
  u.searchParams.set("auth", "postmessage");
14666
15014
  if (config.themeExplicit) u.searchParams.set("color", config.theme);
15015
+ if (config.language) u.searchParams.set("language", config.language);
14667
15016
  if (config.locale) u.searchParams.set("locale", config.locale);
14668
15017
  return u.toString();
14669
15018
  } catch {
14670
15019
  const params = ["auth=postmessage"];
14671
15020
  if (config.themeExplicit) params.push(`color=${config.theme}`);
15021
+ if (config.language) params.push(`language=${encodeURIComponent(config.language)}`);
14672
15022
  if (config.locale) params.push(`locale=${encodeURIComponent(config.locale)}`);
14673
15023
  return params.length > 0 ? `${base}?${params.join("&")}` : base;
14674
15024
  }
14675
15025
  }
14676
15026
  }
14677
- console.log(`[rai-widget] v${"1.8.1"}`);
15027
+ console.log(`[rai-widget] v${"1.8.2"}`);
14678
15028
  class StoreWidgetCompat extends StoreWidget {
14679
15029
  }
14680
15030
  const WIDGET_REGISTRY = [
@@ -14738,7 +15088,7 @@ function exposePublicApi() {
14738
15088
  })();
14739
15089
  const widget = container == null ? void 0 : container.querySelector(ALL_WIDGET_SELECTOR);
14740
15090
  window.ReturningAIWidget = {
14741
- version: "1.8.1",
15091
+ version: "1.8.2",
14742
15092
  reload: () => (widget == null ? void 0 : widget.reload()) ?? Promise.resolve(),
14743
15093
  logout: () => (widget == null ? void 0 : widget.logoutPublic()) ?? Promise.resolve(),
14744
15094
  isAuthenticated: () => (widget == null ? void 0 : widget.isAuthenticated()) ?? false,