@returningai/widget-sdk 1.8.1 → 1.8.3
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/CHANGELOG.md +58 -3
- package/README.md +39 -2
- package/dist/rai-banner.iife.js +1 -1
- package/dist/rai-banner.js +1 -1
- package/dist/rai-widget.iife.js +2 -2
- package/dist/rai-widget.js +452 -59
- package/dist/types/BaseWidget.d.ts +26 -7
- package/dist/types/core/bundle-readiness.d.ts +5 -0
- package/dist/types/core/bundle-runtime-cache.d.ts +14 -1
- package/dist/types/core/language.d.ts +12 -0
- package/dist/types/core/loader-height.d.ts +13 -0
- package/dist/types/core/startup-performance.d.ts +23 -0
- package/dist/types/jsx.d.ts +2 -0
- package/dist/types/types.d.ts +6 -0
- package/package.json +5 -2
package/dist/rai-widget.js
CHANGED
|
@@ -12418,8 +12418,12 @@ async function authenticateServerless(config, state, onRefreshScheduled) {
|
|
|
12418
12418
|
setTokens(config, state, data, onRefreshScheduled);
|
|
12419
12419
|
state.isAuthenticated = true;
|
|
12420
12420
|
return true;
|
|
12421
|
-
} catch {
|
|
12421
|
+
} catch (err) {
|
|
12422
12422
|
if (is4xx || attempt === maxRetries) {
|
|
12423
|
+
state.lastAuthError ?? (state.lastAuthError = {
|
|
12424
|
+
status: 0,
|
|
12425
|
+
error: err instanceof Error ? err.message : "Authentication failed"
|
|
12426
|
+
});
|
|
12423
12427
|
state.isAuthenticated = false;
|
|
12424
12428
|
return false;
|
|
12425
12429
|
}
|
|
@@ -12438,6 +12442,7 @@ async function authenticateViaProxy(config, state, onRefreshScheduled) {
|
|
|
12438
12442
|
);
|
|
12439
12443
|
}
|
|
12440
12444
|
let is4xx = false;
|
|
12445
|
+
let status = 0;
|
|
12441
12446
|
try {
|
|
12442
12447
|
const response = await fetch(config.authUrl, {
|
|
12443
12448
|
method: "POST",
|
|
@@ -12445,6 +12450,7 @@ async function authenticateViaProxy(config, state, onRefreshScheduled) {
|
|
|
12445
12450
|
headers: { "Content-Type": "application/json" }
|
|
12446
12451
|
});
|
|
12447
12452
|
if (!response.ok) {
|
|
12453
|
+
status = response.status;
|
|
12448
12454
|
is4xx = response.status >= 400 && response.status < 500;
|
|
12449
12455
|
throw new Error(`HTTP ${response.status}`);
|
|
12450
12456
|
}
|
|
@@ -12459,12 +12465,17 @@ async function authenticateViaProxy(config, state, onRefreshScheduled) {
|
|
|
12459
12465
|
state.accessTokenExpiry = Date.now() + ttl * 1e3;
|
|
12460
12466
|
state.refreshTokenExpiry = null;
|
|
12461
12467
|
state.isAuthenticated = true;
|
|
12468
|
+
state.lastAuthError = null;
|
|
12462
12469
|
if (config.autoRefresh && onRefreshScheduled) {
|
|
12463
12470
|
onRefreshScheduled();
|
|
12464
12471
|
}
|
|
12465
12472
|
return true;
|
|
12466
|
-
} catch {
|
|
12473
|
+
} catch (err) {
|
|
12467
12474
|
if (is4xx || attempt === maxRetries) {
|
|
12475
|
+
state.lastAuthError = {
|
|
12476
|
+
status,
|
|
12477
|
+
error: err instanceof Error ? err.message : "Authentication failed"
|
|
12478
|
+
};
|
|
12468
12479
|
state.isAuthenticated = false;
|
|
12469
12480
|
return false;
|
|
12470
12481
|
}
|
|
@@ -12513,6 +12524,7 @@ async function refreshAccessToken(config, state, onRefreshScheduled, sendToken)
|
|
|
12513
12524
|
throw new Error("Invalid refresh response");
|
|
12514
12525
|
}
|
|
12515
12526
|
setTokens(config, state, data, onRefreshScheduled);
|
|
12527
|
+
state.isAuthenticated = true;
|
|
12516
12528
|
if (sendToken) sendToken();
|
|
12517
12529
|
return true;
|
|
12518
12530
|
} catch {
|
|
@@ -12689,12 +12701,31 @@ function waitWithTimeout(promise, timeoutMs, timeoutMessage) {
|
|
|
12689
12701
|
);
|
|
12690
12702
|
});
|
|
12691
12703
|
}
|
|
12692
|
-
|
|
12693
|
-
|
|
12704
|
+
function resolveBundleMountResult(mountResult, timeoutMs = BUNDLE_READY_TIMEOUT_MS) {
|
|
12705
|
+
return isPromiseLike(mountResult) ? waitWithTimeout(
|
|
12694
12706
|
mountResult,
|
|
12695
12707
|
timeoutMs,
|
|
12696
12708
|
`Widget bundle mount did not resolve within ${timeoutMs}ms`
|
|
12697
|
-
) : mountResult;
|
|
12709
|
+
) : Promise.resolve(mountResult);
|
|
12710
|
+
}
|
|
12711
|
+
function createBundleMountCleanup(mountResult) {
|
|
12712
|
+
if (typeof mountResult !== "object" || mountResult === null || !("unmount" in mountResult)) {
|
|
12713
|
+
return void 0;
|
|
12714
|
+
}
|
|
12715
|
+
const unmount = mountResult.unmount;
|
|
12716
|
+
if (typeof unmount !== "function") return void 0;
|
|
12717
|
+
let unmounted = false;
|
|
12718
|
+
return () => {
|
|
12719
|
+
if (unmounted) return;
|
|
12720
|
+
unmounted = true;
|
|
12721
|
+
unmount.call(mountResult);
|
|
12722
|
+
};
|
|
12723
|
+
}
|
|
12724
|
+
async function waitForBundleMountReadiness(mountResult, firstPaint, timeoutMs = BUNDLE_READY_TIMEOUT_MS) {
|
|
12725
|
+
const resolvedMountResult = await resolveBundleMountResult(
|
|
12726
|
+
mountResult,
|
|
12727
|
+
timeoutMs
|
|
12728
|
+
);
|
|
12698
12729
|
if (!isReadyHandle(resolvedMountResult)) {
|
|
12699
12730
|
await firstPaint;
|
|
12700
12731
|
return "first-paint";
|
|
@@ -12706,11 +12737,51 @@ async function waitForBundleMountReadiness(mountResult, firstPaint, timeoutMs =
|
|
|
12706
12737
|
);
|
|
12707
12738
|
return "deep-ready";
|
|
12708
12739
|
}
|
|
12740
|
+
const MAX_PRELOAD_ASSETS = 24;
|
|
12741
|
+
function resolveBundlePreloadAssets(bundleUrl, manifest) {
|
|
12742
|
+
if (manifest.version !== 1) return [];
|
|
12743
|
+
const normalizedBundleUrl = new URL(bundleUrl);
|
|
12744
|
+
const bundleDirectory = new URL("./", normalizedBundleUrl);
|
|
12745
|
+
const candidates = [
|
|
12746
|
+
...(Array.isArray(manifest.scripts) ? manifest.scripts : []).map((value) => ({
|
|
12747
|
+
value,
|
|
12748
|
+
rel: "modulepreload",
|
|
12749
|
+
as: "script",
|
|
12750
|
+
extension: ".js"
|
|
12751
|
+
})),
|
|
12752
|
+
...(Array.isArray(manifest.styles) ? manifest.styles : []).map((value) => ({
|
|
12753
|
+
value,
|
|
12754
|
+
rel: "preload",
|
|
12755
|
+
as: "style",
|
|
12756
|
+
extension: ".css"
|
|
12757
|
+
}))
|
|
12758
|
+
];
|
|
12759
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12760
|
+
const assets = [];
|
|
12761
|
+
for (const candidate of candidates) {
|
|
12762
|
+
if (assets.length >= MAX_PRELOAD_ASSETS) break;
|
|
12763
|
+
if (typeof candidate.value !== "string") continue;
|
|
12764
|
+
let resolved;
|
|
12765
|
+
try {
|
|
12766
|
+
resolved = new URL(candidate.value, bundleDirectory);
|
|
12767
|
+
} catch {
|
|
12768
|
+
continue;
|
|
12769
|
+
}
|
|
12770
|
+
if (resolved.origin !== bundleDirectory.origin || !resolved.pathname.startsWith(bundleDirectory.pathname) || !resolved.pathname.endsWith(candidate.extension) || seen.has(resolved.href)) {
|
|
12771
|
+
continue;
|
|
12772
|
+
}
|
|
12773
|
+
seen.add(resolved.href);
|
|
12774
|
+
assets.push({ url: resolved.href, rel: candidate.rel, as: candidate.as });
|
|
12775
|
+
}
|
|
12776
|
+
return assets;
|
|
12777
|
+
}
|
|
12709
12778
|
function createBundleRuntimeWarmer({
|
|
12710
12779
|
preloadScript: preloadScript2,
|
|
12780
|
+
preloadBundleAssets: preloadBundleAssets2,
|
|
12711
12781
|
baseUrl
|
|
12712
12782
|
}) {
|
|
12713
12783
|
const preloadPromises = /* @__PURE__ */ new Map();
|
|
12784
|
+
const deepPreloadPromises = /* @__PURE__ */ new Map();
|
|
12714
12785
|
return function warmBundleRuntime2(globalName, bundleUrl) {
|
|
12715
12786
|
const normalizedUrl = normalizeBundleUrl(bundleUrl, baseUrl());
|
|
12716
12787
|
if (preloadPromises.has(normalizedUrl)) return;
|
|
@@ -12718,6 +12789,12 @@ function createBundleRuntimeWarmer({
|
|
|
12718
12789
|
preloadPromises.delete(normalizedUrl);
|
|
12719
12790
|
});
|
|
12720
12791
|
preloadPromises.set(normalizedUrl, preloadPromise);
|
|
12792
|
+
if (preloadBundleAssets2 && !deepPreloadPromises.has(normalizedUrl)) {
|
|
12793
|
+
const deepPreloadPromise = preloadBundleAssets2(normalizedUrl).catch(() => {
|
|
12794
|
+
deepPreloadPromises.delete(normalizedUrl);
|
|
12795
|
+
});
|
|
12796
|
+
deepPreloadPromises.set(normalizedUrl, deepPreloadPromise);
|
|
12797
|
+
}
|
|
12721
12798
|
};
|
|
12722
12799
|
}
|
|
12723
12800
|
function isWidgetBundleRuntime(value) {
|
|
@@ -12797,6 +12874,35 @@ function preloadScript(url) {
|
|
|
12797
12874
|
document.head.appendChild(link);
|
|
12798
12875
|
});
|
|
12799
12876
|
}
|
|
12877
|
+
const preloadedBundleAssets = /* @__PURE__ */ new Set();
|
|
12878
|
+
function appendBundleAssetPreload(asset) {
|
|
12879
|
+
const cacheKey = `${asset.rel}:${asset.url}`;
|
|
12880
|
+
if (preloadedBundleAssets.has(cacheKey)) return;
|
|
12881
|
+
const link = document.createElement("link");
|
|
12882
|
+
link.rel = asset.rel;
|
|
12883
|
+
link.as = asset.as;
|
|
12884
|
+
link.href = asset.url;
|
|
12885
|
+
link.crossOrigin = "anonymous";
|
|
12886
|
+
link.onload = () => void 0;
|
|
12887
|
+
link.onerror = () => {
|
|
12888
|
+
preloadedBundleAssets.delete(cacheKey);
|
|
12889
|
+
link.remove();
|
|
12890
|
+
};
|
|
12891
|
+
preloadedBundleAssets.add(cacheKey);
|
|
12892
|
+
document.head.appendChild(link);
|
|
12893
|
+
}
|
|
12894
|
+
async function preloadBundleAssets(bundleUrl) {
|
|
12895
|
+
const manifestUrl = new URL("./preload-manifest.json", bundleUrl);
|
|
12896
|
+
const response = await fetch(manifestUrl, {
|
|
12897
|
+
credentials: "omit",
|
|
12898
|
+
headers: { Accept: "application/json" }
|
|
12899
|
+
});
|
|
12900
|
+
if (!response.ok) throw new Error(`Bundle preload manifest returned ${response.status}`);
|
|
12901
|
+
const manifest = await response.json();
|
|
12902
|
+
for (const asset of resolveBundlePreloadAssets(bundleUrl, manifest)) {
|
|
12903
|
+
appendBundleAssetPreload(asset);
|
|
12904
|
+
}
|
|
12905
|
+
}
|
|
12800
12906
|
const loadBrowserBundleRuntime = createBundleRuntimeLoader({
|
|
12801
12907
|
loadScript: appendScript,
|
|
12802
12908
|
readGlobal: (globalName) => window[globalName],
|
|
@@ -12804,6 +12910,7 @@ const loadBrowserBundleRuntime = createBundleRuntimeLoader({
|
|
|
12804
12910
|
});
|
|
12805
12911
|
const warmBrowserBundleRuntime = createBundleRuntimeWarmer({
|
|
12806
12912
|
preloadScript,
|
|
12913
|
+
preloadBundleAssets,
|
|
12807
12914
|
baseUrl: () => document.baseURI
|
|
12808
12915
|
});
|
|
12809
12916
|
function warmBundleRuntime(globalName, bundleUrl) {
|
|
@@ -13059,6 +13166,100 @@ function dispatchHostCallback(input) {
|
|
|
13059
13166
|
}
|
|
13060
13167
|
);
|
|
13061
13168
|
}
|
|
13169
|
+
const SUPPORTED_WIDGET_LANGUAGES = [
|
|
13170
|
+
"en",
|
|
13171
|
+
"de",
|
|
13172
|
+
"es",
|
|
13173
|
+
"fr",
|
|
13174
|
+
"it",
|
|
13175
|
+
"pt",
|
|
13176
|
+
"pl",
|
|
13177
|
+
"da",
|
|
13178
|
+
"nl",
|
|
13179
|
+
"ru",
|
|
13180
|
+
"chs",
|
|
13181
|
+
"el",
|
|
13182
|
+
"ko",
|
|
13183
|
+
"cht",
|
|
13184
|
+
"th",
|
|
13185
|
+
"ja",
|
|
13186
|
+
"id",
|
|
13187
|
+
"vt",
|
|
13188
|
+
"ms",
|
|
13189
|
+
"hi",
|
|
13190
|
+
"fil",
|
|
13191
|
+
"ta",
|
|
13192
|
+
"tr",
|
|
13193
|
+
"ar",
|
|
13194
|
+
"mn",
|
|
13195
|
+
"sw"
|
|
13196
|
+
];
|
|
13197
|
+
const supportedWidgetLanguages = new Set(SUPPORTED_WIDGET_LANGUAGES);
|
|
13198
|
+
const warnedUnsupportedLanguages = /* @__PURE__ */ new Set();
|
|
13199
|
+
const LANGUAGE_TAG_PATTERN = /^[a-z]{2,8}(?:-[a-z0-9]{1,8})*$/;
|
|
13200
|
+
function resolveWidgetLanguage(language) {
|
|
13201
|
+
const normalized = language == null ? void 0 : language.trim().toLowerCase().replace(/_/g, "-");
|
|
13202
|
+
if (!normalized || !LANGUAGE_TAG_PATTERN.test(normalized)) return void 0;
|
|
13203
|
+
const segments = normalized.split("-");
|
|
13204
|
+
const primaryLanguage = segments[0];
|
|
13205
|
+
if (primaryLanguage === "zh") {
|
|
13206
|
+
if (segments.includes("hant")) {
|
|
13207
|
+
return "cht";
|
|
13208
|
+
}
|
|
13209
|
+
if (segments.includes("hans")) {
|
|
13210
|
+
return "chs";
|
|
13211
|
+
}
|
|
13212
|
+
if (segments.includes("tw") || segments.includes("hk") || segments.includes("mo")) {
|
|
13213
|
+
return "cht";
|
|
13214
|
+
}
|
|
13215
|
+
if (segments.includes("cn") || segments.includes("sg")) {
|
|
13216
|
+
return "chs";
|
|
13217
|
+
}
|
|
13218
|
+
return void 0;
|
|
13219
|
+
}
|
|
13220
|
+
if (primaryLanguage === "vi" || primaryLanguage === "vt") {
|
|
13221
|
+
return "vt";
|
|
13222
|
+
}
|
|
13223
|
+
return supportedWidgetLanguages.has(primaryLanguage) ? primaryLanguage : void 0;
|
|
13224
|
+
}
|
|
13225
|
+
function warnUnsupportedWidgetLanguage(language) {
|
|
13226
|
+
const value = language.trim();
|
|
13227
|
+
if (!value) return;
|
|
13228
|
+
const warningKey = value.toLowerCase();
|
|
13229
|
+
if (warnedUnsupportedLanguages.has(warningKey)) return;
|
|
13230
|
+
warnedUnsupportedLanguages.add(warningKey);
|
|
13231
|
+
console.warn(
|
|
13232
|
+
`[rai-widget] Unsupported language "${value.slice(
|
|
13233
|
+
0,
|
|
13234
|
+
80
|
|
13235
|
+
)}". Falling back to the browser language. Supported language codes: ${SUPPORTED_WIDGET_LANGUAGES.join(
|
|
13236
|
+
", "
|
|
13237
|
+
)}.`
|
|
13238
|
+
);
|
|
13239
|
+
}
|
|
13240
|
+
const PERCENTAGE_HEIGHT = /^(\d+(?:\.\d+)?)%$/;
|
|
13241
|
+
function parsePercentageHeight(value) {
|
|
13242
|
+
const match = value.trim().match(PERCENTAGE_HEIGHT);
|
|
13243
|
+
if (!match) return null;
|
|
13244
|
+
const percentage = Number(match[1]);
|
|
13245
|
+
return Number.isFinite(percentage) && percentage > 0 ? percentage : null;
|
|
13246
|
+
}
|
|
13247
|
+
function resolvePercentageViewportHeight(value, viewportHeight) {
|
|
13248
|
+
const percentage = parsePercentageHeight(value);
|
|
13249
|
+
return percentage === null ? 0 : viewportHeight * percentage / 100;
|
|
13250
|
+
}
|
|
13251
|
+
function resolveCollapsedRestoredHeight({
|
|
13252
|
+
originalHeight,
|
|
13253
|
+
restoredHeight,
|
|
13254
|
+
resolvedHeight,
|
|
13255
|
+
isRendered
|
|
13256
|
+
}) {
|
|
13257
|
+
if (!isRendered || Number.isFinite(restoredHeight) && restoredHeight > 0) {
|
|
13258
|
+
return null;
|
|
13259
|
+
}
|
|
13260
|
+
const percentage = parsePercentageHeight(originalHeight);
|
|
13261
|
+
return percentage === null ? `${resolvedHeight}px` : `${percentage}vh`;
|
|
13262
|
+
}
|
|
13062
13263
|
function debounce(fn, wait) {
|
|
13063
13264
|
let timer = null;
|
|
13064
13265
|
return {
|
|
@@ -13213,6 +13414,54 @@ function setupMessageListener(config, state, _shadow, iframe, onRefreshScheduled
|
|
|
13213
13414
|
heightSetter.cancel();
|
|
13214
13415
|
};
|
|
13215
13416
|
}
|
|
13417
|
+
async function resolveStartupAuthentication(authentication, loadErrorSettings) {
|
|
13418
|
+
const authenticated = await authentication;
|
|
13419
|
+
if (authenticated) {
|
|
13420
|
+
void loadErrorSettings({ skipCache: true }).catch(() => {
|
|
13421
|
+
});
|
|
13422
|
+
return true;
|
|
13423
|
+
}
|
|
13424
|
+
try {
|
|
13425
|
+
await loadErrorSettings();
|
|
13426
|
+
} catch {
|
|
13427
|
+
}
|
|
13428
|
+
return false;
|
|
13429
|
+
}
|
|
13430
|
+
function resolveWidgetPreconnectOrigins(config, baseUrl) {
|
|
13431
|
+
const values = [
|
|
13432
|
+
config.apiUrl,
|
|
13433
|
+
config.v2ApiUrl,
|
|
13434
|
+
config.authUrl,
|
|
13435
|
+
config.bundleUrl,
|
|
13436
|
+
config.widgetUrl,
|
|
13437
|
+
config.widgetDomain
|
|
13438
|
+
];
|
|
13439
|
+
const origins = [];
|
|
13440
|
+
for (const value of values) {
|
|
13441
|
+
if (!value) continue;
|
|
13442
|
+
try {
|
|
13443
|
+
const url = new URL(value, baseUrl);
|
|
13444
|
+
if ((url.protocol === "http:" || url.protocol === "https:") && !origins.includes(url.origin)) {
|
|
13445
|
+
origins.push(url.origin);
|
|
13446
|
+
}
|
|
13447
|
+
} catch {
|
|
13448
|
+
}
|
|
13449
|
+
}
|
|
13450
|
+
return origins;
|
|
13451
|
+
}
|
|
13452
|
+
function ensureWidgetPreconnects(origins, targetDocument) {
|
|
13453
|
+
const documentTarget = document;
|
|
13454
|
+
for (const origin of origins) {
|
|
13455
|
+
const id = `rai-widget-preconnect-${origin.replace(/[^a-z0-9]+/gi, "-")}`;
|
|
13456
|
+
if (documentTarget.getElementById(id)) continue;
|
|
13457
|
+
const link = documentTarget.createElement("link");
|
|
13458
|
+
link.id = id;
|
|
13459
|
+
link.rel = "preconnect";
|
|
13460
|
+
link.href = origin;
|
|
13461
|
+
link.crossOrigin = "anonymous";
|
|
13462
|
+
documentTarget.head.appendChild(link);
|
|
13463
|
+
}
|
|
13464
|
+
}
|
|
13216
13465
|
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
13466
|
const DEFINED_ATTRS = /* @__PURE__ */ new Set([
|
|
13218
13467
|
"community-id",
|
|
@@ -13233,6 +13482,7 @@ const DEFINED_ATTRS = /* @__PURE__ */ new Set([
|
|
|
13233
13482
|
"max-retries",
|
|
13234
13483
|
"retry-delay",
|
|
13235
13484
|
"height-debounce",
|
|
13485
|
+
"language",
|
|
13236
13486
|
"locale",
|
|
13237
13487
|
"custom-data",
|
|
13238
13488
|
"retry-label",
|
|
@@ -13493,6 +13743,13 @@ function readConfig(el2, existingId) {
|
|
|
13493
13743
|
return void 0;
|
|
13494
13744
|
}
|
|
13495
13745
|
})();
|
|
13746
|
+
const explicitLanguage = get("language").trim();
|
|
13747
|
+
const legacyLocale = get("locale").trim();
|
|
13748
|
+
const requestedLanguage = explicitLanguage || legacyLocale || void 0;
|
|
13749
|
+
const language = resolveWidgetLanguage(requestedLanguage);
|
|
13750
|
+
if (requestedLanguage && !language) {
|
|
13751
|
+
warnUnsupportedWidgetLanguage(requestedLanguage);
|
|
13752
|
+
}
|
|
13496
13753
|
return {
|
|
13497
13754
|
communityId,
|
|
13498
13755
|
channelId,
|
|
@@ -13518,7 +13775,12 @@ function readConfig(el2, existingId) {
|
|
|
13518
13775
|
// #2
|
|
13519
13776
|
heightDebounce: num("height-debounce", 100),
|
|
13520
13777
|
// #4
|
|
13521
|
-
locale
|
|
13778
|
+
// `language` is the canonical forced widget locale. Keep `locale` as a
|
|
13779
|
+
// backwards-compatible alias only when it supplied the selected value.
|
|
13780
|
+
// Unsupported explicit values are omitted so the widget uses its existing
|
|
13781
|
+
// browser-language fallback.
|
|
13782
|
+
language,
|
|
13783
|
+
locale: !explicitLanguage && legacyLocale ? language : void 0,
|
|
13522
13784
|
// #5
|
|
13523
13785
|
customData,
|
|
13524
13786
|
// #8
|
|
@@ -13580,6 +13842,8 @@ class BaseWidget extends HTMLElement {
|
|
|
13580
13842
|
__publicField(this, "config");
|
|
13581
13843
|
__publicField(this, "state", createInitialState());
|
|
13582
13844
|
__publicField(this, "loaderEl", null);
|
|
13845
|
+
__publicField(this, "loaderHeightRestore", null);
|
|
13846
|
+
__publicField(this, "loaderHeightObserver", null);
|
|
13583
13847
|
__publicField(this, "errorEl", null);
|
|
13584
13848
|
__publicField(this, "msgEl", null);
|
|
13585
13849
|
__publicField(this, "cleanupListener");
|
|
@@ -13591,14 +13855,21 @@ class BaseWidget extends HTMLElement {
|
|
|
13591
13855
|
__publicField(this, "hasAttemptedInit", false);
|
|
13592
13856
|
__publicField(this, "initGeneration", 0);
|
|
13593
13857
|
__publicField(this, "lottieInstances", []);
|
|
13594
|
-
__publicField(this, "renderedLoaderAnimationKey", null);
|
|
13595
13858
|
__publicField(this, "callbackRegistry", new WidgetCallbackRegistry());
|
|
13596
13859
|
__publicField(this, "bannerNode", null);
|
|
13597
13860
|
__publicField(this, "releaseAutoThemeDisableLease");
|
|
13861
|
+
__publicField(this, "bundleUnmount");
|
|
13598
13862
|
this.shadow = this.attachShadow({ mode: "closed" });
|
|
13599
13863
|
}
|
|
13600
13864
|
static get observedAttributes() {
|
|
13601
|
-
return [
|
|
13865
|
+
return [
|
|
13866
|
+
"embed-token",
|
|
13867
|
+
"data-embed-token",
|
|
13868
|
+
"language",
|
|
13869
|
+
"data-language",
|
|
13870
|
+
"locale",
|
|
13871
|
+
"data-locale"
|
|
13872
|
+
];
|
|
13602
13873
|
}
|
|
13603
13874
|
registerCallback(callbackName, callback) {
|
|
13604
13875
|
this.callbackRegistry.registerCallback(callbackName, callback);
|
|
@@ -13610,6 +13881,9 @@ class BaseWidget extends HTMLElement {
|
|
|
13610
13881
|
this.config = readConfig(this, void 0);
|
|
13611
13882
|
this.identitySignature = identitySignatureForConfig(this.config);
|
|
13612
13883
|
this.setupIdentityAttributeObserver();
|
|
13884
|
+
ensureWidgetPreconnects(
|
|
13885
|
+
resolveWidgetPreconnectOrigins(this.config, document.baseURI)
|
|
13886
|
+
);
|
|
13613
13887
|
const style = document.createElement("style");
|
|
13614
13888
|
style.textContent = widgetCSS;
|
|
13615
13889
|
this.shadow.appendChild(style);
|
|
@@ -13645,13 +13919,23 @@ class BaseWidget extends HTMLElement {
|
|
|
13645
13919
|
(_c = this.identityAttributeObserver) == null ? void 0 : _c.disconnect();
|
|
13646
13920
|
this.teardownMountedWidget();
|
|
13647
13921
|
this.clearLottieInstances();
|
|
13922
|
+
this.restoreLoaderHeight();
|
|
13648
13923
|
this.removeBanner();
|
|
13649
13924
|
clearState(this.state);
|
|
13650
13925
|
}
|
|
13651
13926
|
attributeChangedCallback(name, oldValue, newValue) {
|
|
13652
13927
|
if (oldValue === newValue) return;
|
|
13653
|
-
if (name
|
|
13654
|
-
|
|
13928
|
+
if (name === "embed-token" || name === "data-embed-token") {
|
|
13929
|
+
this.handleIdentityConfigChange();
|
|
13930
|
+
return;
|
|
13931
|
+
}
|
|
13932
|
+
if (this.isConnected && this.config) {
|
|
13933
|
+
if (this.hasAttemptedInit) {
|
|
13934
|
+
void this.reload();
|
|
13935
|
+
} else {
|
|
13936
|
+
this.config = readConfig(this, this.config.communityId);
|
|
13937
|
+
}
|
|
13938
|
+
}
|
|
13655
13939
|
}
|
|
13656
13940
|
setupIdentityAttributeObserver() {
|
|
13657
13941
|
var _a;
|
|
@@ -13807,22 +14091,6 @@ class BaseWidget extends HTMLElement {
|
|
|
13807
14091
|
textEl.hidden = !loadingText.enabled;
|
|
13808
14092
|
}
|
|
13809
14093
|
}
|
|
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
14094
|
renderShell() {
|
|
13827
14095
|
this.loaderEl = document.createElement("div");
|
|
13828
14096
|
this.loaderEl.className = "rai-loader";
|
|
@@ -13834,6 +14102,7 @@ class BaseWidget extends HTMLElement {
|
|
|
13834
14102
|
textEl.textContent = this.config.loadingText;
|
|
13835
14103
|
this.loaderEl.appendChild(textEl);
|
|
13836
14104
|
this.shadow.appendChild(this.loaderEl);
|
|
14105
|
+
this.freezeIframeLoaderHeight();
|
|
13837
14106
|
this.applyLoadingSettings();
|
|
13838
14107
|
this.fetchAndRenderLoader();
|
|
13839
14108
|
this.errorEl = document.createElement("div");
|
|
@@ -13854,11 +14123,9 @@ class BaseWidget extends HTMLElement {
|
|
|
13854
14123
|
* Loads the community loading animation + theme palette from cache or API
|
|
13855
14124
|
* and renders the configured animation above the loading text.
|
|
13856
14125
|
*
|
|
13857
|
-
*
|
|
13858
|
-
*
|
|
13859
|
-
*
|
|
13860
|
-
* - Cache miss → retain the initial shell; await network; apply palette +
|
|
13861
|
-
* render the resolved animation or fallback.
|
|
14126
|
+
* Cached settings are applied synchronously. On a first visit, settings load
|
|
14127
|
+
* alongside authentication so the generic loader stays hidden until the
|
|
14128
|
+
* configured animation is known. This request never blocks widget launch.
|
|
13862
14129
|
*/
|
|
13863
14130
|
async fetchAndRenderLoader() {
|
|
13864
14131
|
var _a;
|
|
@@ -13867,26 +14134,14 @@ class BaseWidget extends HTMLElement {
|
|
|
13867
14134
|
applyErrorSettingsToState(this.state, cached);
|
|
13868
14135
|
this.applyLoadingSettings();
|
|
13869
14136
|
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
14137
|
return;
|
|
13884
14138
|
}
|
|
13885
14139
|
try {
|
|
13886
14140
|
await fetchErrorSettings(this.config, this.state);
|
|
13887
14141
|
} catch {
|
|
13888
14142
|
}
|
|
13889
|
-
|
|
14143
|
+
const loaderEl = this.loaderEl;
|
|
14144
|
+
if (!loaderEl) return;
|
|
13890
14145
|
if (!this.config.themeExplicit && (this.state.defaultColorSchema === "light" || this.state.defaultColorSchema === "dark") && this.state.defaultColorSchema !== this.config.theme) {
|
|
13891
14146
|
this.config.theme = this.state.defaultColorSchema;
|
|
13892
14147
|
}
|
|
@@ -13896,7 +14151,7 @@ class BaseWidget extends HTMLElement {
|
|
|
13896
14151
|
if (this.state.font) this.applyFont(this.state.font);
|
|
13897
14152
|
this.applyLoadingSettings();
|
|
13898
14153
|
this.renderLoaderAnimation();
|
|
13899
|
-
|
|
14154
|
+
loaderEl.classList.remove("rai-loader-pending-settings");
|
|
13900
14155
|
}
|
|
13901
14156
|
/**
|
|
13902
14157
|
* Append the loading animation above the text. Removes any previous
|
|
@@ -13905,7 +14160,6 @@ class BaseWidget extends HTMLElement {
|
|
|
13905
14160
|
*/
|
|
13906
14161
|
renderLoaderAnimation() {
|
|
13907
14162
|
if (!this.loaderEl) return;
|
|
13908
|
-
this.renderedLoaderAnimationKey = this.getLoadingAnimationRenderKey();
|
|
13909
14163
|
this.clearLottieInstances();
|
|
13910
14164
|
const existing = this.loaderEl.querySelector(".rai-loader-anim");
|
|
13911
14165
|
if (existing) existing.remove();
|
|
@@ -13992,9 +14246,82 @@ class BaseWidget extends HTMLElement {
|
|
|
13992
14246
|
setTimeout(() => {
|
|
13993
14247
|
loader.remove();
|
|
13994
14248
|
lottieInstances.forEach((instance) => instance.destroy());
|
|
14249
|
+
this.restoreLoaderHeight();
|
|
13995
14250
|
}, 300);
|
|
13996
14251
|
this.loaderEl = null;
|
|
13997
14252
|
}
|
|
14253
|
+
/**
|
|
14254
|
+
* Keep percentage-based iframe embeds at their resolved startup height while
|
|
14255
|
+
* the SDK loader is visible. The child iframe may resize during auth/data
|
|
14256
|
+
* loading; without this pin the loader's 100% centering moves with the host.
|
|
14257
|
+
*/
|
|
14258
|
+
freezeIframeLoaderHeight() {
|
|
14259
|
+
var _a;
|
|
14260
|
+
if (this.config.bundleUrl || this.loaderHeightRestore) return;
|
|
14261
|
+
const measuredHeight = this.getBoundingClientRect().height;
|
|
14262
|
+
const isRendered = this.getClientRects().length > 0;
|
|
14263
|
+
const hasPercentageHeight = parsePercentageHeight(this.style.height) !== null;
|
|
14264
|
+
const percentageHeight = resolvePercentageViewportHeight(
|
|
14265
|
+
this.style.height,
|
|
14266
|
+
window.innerHeight
|
|
14267
|
+
);
|
|
14268
|
+
if (measuredHeight <= 0 && hasPercentageHeight && !isRendered) {
|
|
14269
|
+
this.deferIframeLoaderHeightFreeze();
|
|
14270
|
+
return;
|
|
14271
|
+
}
|
|
14272
|
+
const resolvedHeight = measuredHeight > 0 ? measuredHeight : percentageHeight;
|
|
14273
|
+
if (!Number.isFinite(resolvedHeight) || resolvedHeight <= 0) return;
|
|
14274
|
+
(_a = this.loaderHeightObserver) == null ? void 0 : _a.disconnect();
|
|
14275
|
+
this.loaderHeightObserver = null;
|
|
14276
|
+
this.loaderHeightRestore = {
|
|
14277
|
+
value: this.style.getPropertyValue("height"),
|
|
14278
|
+
priority: this.style.getPropertyPriority("height"),
|
|
14279
|
+
resolvedHeight
|
|
14280
|
+
};
|
|
14281
|
+
this.style.setProperty("height", `${resolvedHeight}px`);
|
|
14282
|
+
}
|
|
14283
|
+
deferIframeLoaderHeightFreeze() {
|
|
14284
|
+
if (this.loaderHeightObserver || typeof ResizeObserver === "undefined") {
|
|
14285
|
+
return;
|
|
14286
|
+
}
|
|
14287
|
+
this.loaderHeightObserver = new ResizeObserver(() => {
|
|
14288
|
+
var _a, _b;
|
|
14289
|
+
if (!this.isConnected || !this.loaderEl || this.config.bundleUrl) {
|
|
14290
|
+
(_a = this.loaderHeightObserver) == null ? void 0 : _a.disconnect();
|
|
14291
|
+
this.loaderHeightObserver = null;
|
|
14292
|
+
return;
|
|
14293
|
+
}
|
|
14294
|
+
if (this.getClientRects().length === 0) return;
|
|
14295
|
+
(_b = this.loaderHeightObserver) == null ? void 0 : _b.disconnect();
|
|
14296
|
+
this.loaderHeightObserver = null;
|
|
14297
|
+
this.freezeIframeLoaderHeight();
|
|
14298
|
+
});
|
|
14299
|
+
this.loaderHeightObserver.observe(this);
|
|
14300
|
+
}
|
|
14301
|
+
restoreLoaderHeight() {
|
|
14302
|
+
var _a;
|
|
14303
|
+
(_a = this.loaderHeightObserver) == null ? void 0 : _a.disconnect();
|
|
14304
|
+
this.loaderHeightObserver = null;
|
|
14305
|
+
if (!this.loaderHeightRestore) return;
|
|
14306
|
+
const { value, priority, resolvedHeight } = this.loaderHeightRestore;
|
|
14307
|
+
this.loaderHeightRestore = null;
|
|
14308
|
+
if (value) {
|
|
14309
|
+
this.style.setProperty("height", value, priority);
|
|
14310
|
+
} else {
|
|
14311
|
+
this.style.removeProperty("height");
|
|
14312
|
+
}
|
|
14313
|
+
const restoredHeight = this.getBoundingClientRect().height;
|
|
14314
|
+
const isRendered = this.isConnected && this.getClientRects().length > 0;
|
|
14315
|
+
const collapsedFallback = resolveCollapsedRestoredHeight({
|
|
14316
|
+
originalHeight: value,
|
|
14317
|
+
restoredHeight,
|
|
14318
|
+
resolvedHeight,
|
|
14319
|
+
isRendered
|
|
14320
|
+
});
|
|
14321
|
+
if (collapsedFallback) {
|
|
14322
|
+
this.style.setProperty("height", collapsedFallback, priority);
|
|
14323
|
+
}
|
|
14324
|
+
}
|
|
13998
14325
|
/**
|
|
13999
14326
|
* Resolve after a legacy bundle's first rendered child has reached a paint.
|
|
14000
14327
|
* This remains the fallback for bundles that do not expose a deep-ready
|
|
@@ -14207,14 +14534,10 @@ class BaseWidget extends HTMLElement {
|
|
|
14207
14534
|
this.config.bundleUrl
|
|
14208
14535
|
);
|
|
14209
14536
|
}
|
|
14210
|
-
const
|
|
14211
|
-
|
|
14212
|
-
|
|
14213
|
-
|
|
14214
|
-
errorSettingsPromise
|
|
14215
|
-
]);
|
|
14216
|
-
return authenticated;
|
|
14217
|
-
};
|
|
14537
|
+
const finishStartup = (authentication) => resolveStartupAuthentication(
|
|
14538
|
+
authentication,
|
|
14539
|
+
(options) => fetchErrorSettings(this.config, this.state, options)
|
|
14540
|
+
);
|
|
14218
14541
|
const launchWidget = () => {
|
|
14219
14542
|
var _a;
|
|
14220
14543
|
if (!this.isCurrentInit(initId)) return;
|
|
@@ -14314,6 +14637,13 @@ class BaseWidget extends HTMLElement {
|
|
|
14314
14637
|
}
|
|
14315
14638
|
teardownMountedWidget() {
|
|
14316
14639
|
var _a;
|
|
14640
|
+
const bundleUnmount = this.bundleUnmount;
|
|
14641
|
+
this.bundleUnmount = void 0;
|
|
14642
|
+
try {
|
|
14643
|
+
bundleUnmount == null ? void 0 : bundleUnmount();
|
|
14644
|
+
} catch (error) {
|
|
14645
|
+
console.error("[rai-widget] Bundle unmount failed:", error);
|
|
14646
|
+
}
|
|
14317
14647
|
(_a = this.releaseAutoThemeDisableLease) == null ? void 0 : _a.call(this);
|
|
14318
14648
|
this.releaseAutoThemeDisableLease = void 0;
|
|
14319
14649
|
if (this.cleanupListener) {
|
|
@@ -14389,6 +14719,7 @@ class BaseWidget extends HTMLElement {
|
|
|
14389
14719
|
async mountWidget(initId) {
|
|
14390
14720
|
var _a;
|
|
14391
14721
|
let container = null;
|
|
14722
|
+
let cleanupBundleMount;
|
|
14392
14723
|
try {
|
|
14393
14724
|
this.teardownMountedWidget();
|
|
14394
14725
|
const autoHeight = this.config.height.trim().toLowerCase() === "auto";
|
|
@@ -14451,6 +14782,7 @@ class BaseWidget extends HTMLElement {
|
|
|
14451
14782
|
// injected by SSR for the explicit `/open-widget` route.
|
|
14452
14783
|
isOpenWidget: !this.state.accessToken,
|
|
14453
14784
|
defaultColorSchema: this.config.widgetType === "custom" && !this.config.themeExplicit ? null : colorMode,
|
|
14785
|
+
language: this.config.language,
|
|
14454
14786
|
// Bundle loading screens are rendered beneath an instance-scoped
|
|
14455
14787
|
// provider. Do not put these values on window: pages can mount several
|
|
14456
14788
|
// widget types at once.
|
|
@@ -14473,8 +14805,20 @@ class BaseWidget extends HTMLElement {
|
|
|
14473
14805
|
transport: "bundle"
|
|
14474
14806
|
})
|
|
14475
14807
|
});
|
|
14476
|
-
await
|
|
14808
|
+
const resolvedMountResult = await resolveBundleMountResult(mountResult);
|
|
14809
|
+
cleanupBundleMount = createBundleMountCleanup(resolvedMountResult);
|
|
14477
14810
|
if (!this.isCurrentInit(initId) || !container.isConnected) {
|
|
14811
|
+
cleanupBundleMount == null ? void 0 : cleanupBundleMount();
|
|
14812
|
+
container.remove();
|
|
14813
|
+
return;
|
|
14814
|
+
}
|
|
14815
|
+
this.bundleUnmount = cleanupBundleMount;
|
|
14816
|
+
await waitForBundleMountReadiness(resolvedMountResult, firstPaint);
|
|
14817
|
+
if (!this.isCurrentInit(initId) || !container.isConnected) {
|
|
14818
|
+
cleanupBundleMount == null ? void 0 : cleanupBundleMount();
|
|
14819
|
+
if (this.bundleUnmount === cleanupBundleMount) {
|
|
14820
|
+
this.bundleUnmount = void 0;
|
|
14821
|
+
}
|
|
14478
14822
|
container.remove();
|
|
14479
14823
|
return;
|
|
14480
14824
|
}
|
|
@@ -14482,11 +14826,16 @@ class BaseWidget extends HTMLElement {
|
|
|
14482
14826
|
this.hideLoader();
|
|
14483
14827
|
this.emit("rai-mounted");
|
|
14484
14828
|
} catch (err) {
|
|
14829
|
+
cleanupBundleMount == null ? void 0 : cleanupBundleMount();
|
|
14830
|
+
if (this.bundleUnmount === cleanupBundleMount) {
|
|
14831
|
+
this.bundleUnmount = void 0;
|
|
14832
|
+
}
|
|
14485
14833
|
if (!this.isCurrentInit(initId)) {
|
|
14486
14834
|
container == null ? void 0 : container.remove();
|
|
14487
14835
|
return;
|
|
14488
14836
|
}
|
|
14489
14837
|
console.error("[rai-widget] Bundle mount failed:", err);
|
|
14838
|
+
container == null ? void 0 : container.remove();
|
|
14490
14839
|
(_a = this.releaseAutoThemeDisableLease) == null ? void 0 : _a.call(this);
|
|
14491
14840
|
this.releaseAutoThemeDisableLease = void 0;
|
|
14492
14841
|
this.showError();
|
|
@@ -14510,13 +14859,15 @@ class BaseWidget extends HTMLElement {
|
|
|
14510
14859
|
() => this.scheduleRefresh()
|
|
14511
14860
|
);
|
|
14512
14861
|
if (ok) pushToken();
|
|
14862
|
+
else this.handleSessionExpired();
|
|
14513
14863
|
} else {
|
|
14514
|
-
await refreshAccessToken(
|
|
14864
|
+
const ok = await refreshAccessToken(
|
|
14515
14865
|
this.config,
|
|
14516
14866
|
this.state,
|
|
14517
14867
|
() => this.scheduleRefresh(),
|
|
14518
14868
|
pushToken
|
|
14519
14869
|
);
|
|
14870
|
+
if (!ok) this.handleSessionExpired();
|
|
14520
14871
|
}
|
|
14521
14872
|
};
|
|
14522
14873
|
if (delay > 0) {
|
|
@@ -14532,6 +14883,35 @@ class BaseWidget extends HTMLElement {
|
|
|
14532
14883
|
sendTokenToWidget(this.config, this.state, iframe);
|
|
14533
14884
|
}, 12e4);
|
|
14534
14885
|
}
|
|
14886
|
+
/**
|
|
14887
|
+
* A background refresh exhausted every recovery path — /auth/refresh failed
|
|
14888
|
+
* and the re-auth that follows it failed too, so `state.isAuthenticated` is
|
|
14889
|
+
* now false and there is no live token.
|
|
14890
|
+
*
|
|
14891
|
+
* Every startup auth path already calls showError() on failure; this one is
|
|
14892
|
+
* reached from a timer, so without it the widget stays mounted showing stale
|
|
14893
|
+
* content while isAuthenticated() reports false to the host page — which is
|
|
14894
|
+
* how a signed-in user ends up looking at content meant for signed-out users.
|
|
14895
|
+
* The event gives hosts a signal to re-mint credentials instead of polling.
|
|
14896
|
+
*/
|
|
14897
|
+
handleSessionExpired() {
|
|
14898
|
+
var _a;
|
|
14899
|
+
if (this.state.refreshTimer) {
|
|
14900
|
+
clearTimeout(this.state.refreshTimer);
|
|
14901
|
+
this.state.refreshTimer = null;
|
|
14902
|
+
}
|
|
14903
|
+
(_a = this.state).lastAuthError ?? (_a.lastAuthError = {
|
|
14904
|
+
status: 401,
|
|
14905
|
+
error: "Session expired",
|
|
14906
|
+
hint: "The widget session could not be renewed. Reload to sign in again."
|
|
14907
|
+
});
|
|
14908
|
+
this.emit("rai-session-expired", {
|
|
14909
|
+
communityId: this.config.communityId,
|
|
14910
|
+
widgetType: this.config.widgetType,
|
|
14911
|
+
lastAuthError: this.state.lastAuthError ?? void 0
|
|
14912
|
+
});
|
|
14913
|
+
this.showError();
|
|
14914
|
+
}
|
|
14535
14915
|
// ── Logout helper ─────────────────────────────────────────────────────
|
|
14536
14916
|
async logoutAndClear() {
|
|
14537
14917
|
await logout(this.config, this.state);
|
|
@@ -14578,6 +14958,7 @@ class StoreWidget extends BaseWidget {
|
|
|
14578
14958
|
url.searchParams.set("containerId", config.container);
|
|
14579
14959
|
url.searchParams.set("connectType", "simple");
|
|
14580
14960
|
url.searchParams.set("mode", "private");
|
|
14961
|
+
if (config.language) url.searchParams.set("language", config.language);
|
|
14581
14962
|
if (config.locale) url.searchParams.set("locale", config.locale);
|
|
14582
14963
|
return url.toString();
|
|
14583
14964
|
}
|
|
@@ -14588,10 +14969,12 @@ class ChannelWidget extends BaseWidget {
|
|
|
14588
14969
|
try {
|
|
14589
14970
|
const u = new URL(base);
|
|
14590
14971
|
u.searchParams.set("color", config.theme);
|
|
14972
|
+
if (config.language) u.searchParams.set("language", config.language);
|
|
14591
14973
|
if (config.locale) u.searchParams.set("locale", config.locale);
|
|
14592
14974
|
return u.toString();
|
|
14593
14975
|
} catch {
|
|
14594
14976
|
const params = [`color=${config.theme}`];
|
|
14977
|
+
if (config.language) params.push(`language=${encodeURIComponent(config.language)}`);
|
|
14595
14978
|
if (config.locale) params.push(`locale=${encodeURIComponent(config.locale)}`);
|
|
14596
14979
|
return `${base}?${params.join("&")}`;
|
|
14597
14980
|
}
|
|
@@ -14603,10 +14986,12 @@ class MilestoneWidget extends BaseWidget {
|
|
|
14603
14986
|
try {
|
|
14604
14987
|
const u = new URL(base);
|
|
14605
14988
|
u.searchParams.set("color", config.theme);
|
|
14989
|
+
if (config.language) u.searchParams.set("language", config.language);
|
|
14606
14990
|
if (config.locale) u.searchParams.set("locale", config.locale);
|
|
14607
14991
|
return u.toString();
|
|
14608
14992
|
} catch {
|
|
14609
14993
|
const params = [`color=${config.theme}`];
|
|
14994
|
+
if (config.language) params.push(`language=${encodeURIComponent(config.language)}`);
|
|
14610
14995
|
if (config.locale) params.push(`locale=${encodeURIComponent(config.locale)}`);
|
|
14611
14996
|
return `${base}?${params.join("&")}`;
|
|
14612
14997
|
}
|
|
@@ -14618,10 +15003,12 @@ class SocialWidget extends BaseWidget {
|
|
|
14618
15003
|
try {
|
|
14619
15004
|
const u = new URL(base);
|
|
14620
15005
|
u.searchParams.set("color", config.theme);
|
|
15006
|
+
if (config.language) u.searchParams.set("language", config.language);
|
|
14621
15007
|
if (config.locale) u.searchParams.set("locale", config.locale);
|
|
14622
15008
|
return u.toString();
|
|
14623
15009
|
} catch {
|
|
14624
15010
|
const params = [`color=${config.theme}`];
|
|
15011
|
+
if (config.language) params.push(`language=${encodeURIComponent(config.language)}`);
|
|
14625
15012
|
if (config.locale) params.push(`locale=${encodeURIComponent(config.locale)}`);
|
|
14626
15013
|
return `${base}?${params.join("&")}`;
|
|
14627
15014
|
}
|
|
@@ -14633,10 +15020,12 @@ class CurrencyWidget extends BaseWidget {
|
|
|
14633
15020
|
try {
|
|
14634
15021
|
const u = new URL(base);
|
|
14635
15022
|
u.searchParams.set("color", config.theme);
|
|
15023
|
+
if (config.language) u.searchParams.set("language", config.language);
|
|
14636
15024
|
if (config.locale) u.searchParams.set("locale", config.locale);
|
|
14637
15025
|
return u.toString();
|
|
14638
15026
|
} catch {
|
|
14639
15027
|
const params = [`color=${config.theme}`];
|
|
15028
|
+
if (config.language) params.push(`language=${encodeURIComponent(config.language)}`);
|
|
14640
15029
|
if (config.locale) params.push(`locale=${encodeURIComponent(config.locale)}`);
|
|
14641
15030
|
return `${base}?${params.join("&")}`;
|
|
14642
15031
|
}
|
|
@@ -14648,10 +15037,12 @@ class ReferralWidget extends BaseWidget {
|
|
|
14648
15037
|
try {
|
|
14649
15038
|
const u = new URL(base);
|
|
14650
15039
|
u.searchParams.set("color", config.theme);
|
|
15040
|
+
if (config.language) u.searchParams.set("language", config.language);
|
|
14651
15041
|
if (config.locale) u.searchParams.set("locale", config.locale);
|
|
14652
15042
|
return u.toString();
|
|
14653
15043
|
} catch {
|
|
14654
15044
|
const params = [`color=${config.theme}`];
|
|
15045
|
+
if (config.language) params.push(`language=${encodeURIComponent(config.language)}`);
|
|
14655
15046
|
if (config.locale) params.push(`locale=${encodeURIComponent(config.locale)}`);
|
|
14656
15047
|
return `${base}?${params.join("&")}`;
|
|
14657
15048
|
}
|
|
@@ -14664,17 +15055,19 @@ class CustomWidget extends BaseWidget {
|
|
|
14664
15055
|
const u = new URL(base);
|
|
14665
15056
|
u.searchParams.set("auth", "postmessage");
|
|
14666
15057
|
if (config.themeExplicit) u.searchParams.set("color", config.theme);
|
|
15058
|
+
if (config.language) u.searchParams.set("language", config.language);
|
|
14667
15059
|
if (config.locale) u.searchParams.set("locale", config.locale);
|
|
14668
15060
|
return u.toString();
|
|
14669
15061
|
} catch {
|
|
14670
15062
|
const params = ["auth=postmessage"];
|
|
14671
15063
|
if (config.themeExplicit) params.push(`color=${config.theme}`);
|
|
15064
|
+
if (config.language) params.push(`language=${encodeURIComponent(config.language)}`);
|
|
14672
15065
|
if (config.locale) params.push(`locale=${encodeURIComponent(config.locale)}`);
|
|
14673
15066
|
return params.length > 0 ? `${base}?${params.join("&")}` : base;
|
|
14674
15067
|
}
|
|
14675
15068
|
}
|
|
14676
15069
|
}
|
|
14677
|
-
console.log(`[rai-widget] v${"1.8.
|
|
15070
|
+
console.log(`[rai-widget] v${"1.8.3"}`);
|
|
14678
15071
|
class StoreWidgetCompat extends StoreWidget {
|
|
14679
15072
|
}
|
|
14680
15073
|
const WIDGET_REGISTRY = [
|
|
@@ -14738,7 +15131,7 @@ function exposePublicApi() {
|
|
|
14738
15131
|
})();
|
|
14739
15132
|
const widget = container == null ? void 0 : container.querySelector(ALL_WIDGET_SELECTOR);
|
|
14740
15133
|
window.ReturningAIWidget = {
|
|
14741
|
-
version: "1.8.
|
|
15134
|
+
version: "1.8.3",
|
|
14742
15135
|
reload: () => (widget == null ? void 0 : widget.reload()) ?? Promise.resolve(),
|
|
14743
15136
|
logout: () => (widget == null ? void 0 : widget.logoutPublic()) ?? Promise.resolve(),
|
|
14744
15137
|
isAuthenticated: () => (widget == null ? void 0 : widget.isAuthenticated()) ?? false,
|