@helpai/elements 0.29.1 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/elements-web-component.esm.js +24 -24
- package/elements-web-component.esm.js.map +3 -3
- package/elements.cjs.js +24 -24
- package/elements.cjs.js.map +3 -3
- package/elements.esm.js +24 -24
- package/elements.esm.js.map +3 -3
- package/elements.js +24 -24
- package/elements.js.map +3 -3
- package/index.d.ts +13 -5
- package/index.mjs +55 -8
- package/package.json +1 -1
- package/schema.d.ts +49 -49
- package/web-component.mjs +87 -15
package/web-component.mjs
CHANGED
|
@@ -509,8 +509,9 @@ function resolveOptions(rawOpts) {
|
|
|
509
509
|
const header = opts.header ?? {};
|
|
510
510
|
const { themeMode, themeOverrides } = parseTheme(opts.theme);
|
|
511
511
|
const mode = presentation.mode ?? "floating";
|
|
512
|
-
const
|
|
513
|
-
const availableLocales = i18n.availableLocales ?? [locale];
|
|
512
|
+
const defaultLocale = resolveDefaultLocale(i18n.defaultLocale);
|
|
513
|
+
const availableLocales = i18n.availableLocales ?? [i18n.locale ?? defaultLocale];
|
|
514
|
+
const locale = clampLocale(i18n.locale ?? defaultLocale, availableLocales, defaultLocale);
|
|
514
515
|
const baseUrl = (opts.baseUrl ?? BRAND.defaultBaseUrl).replace(/\/$/, "");
|
|
515
516
|
const agentApiBaseUrl = (opts.agentApiBaseUrl ?? `${baseUrl}/api/agent`).replace(/\/$/, "");
|
|
516
517
|
const dataApiBaseUrl = (opts.dataApiBaseUrl ?? `${baseUrl}/api/data`).replace(/\/$/, "");
|
|
@@ -552,6 +553,7 @@ function resolveOptions(rawOpts) {
|
|
|
552
553
|
maxCount: opts.composer?.attachments?.maxCount ?? DEFAULT_ATTACHMENTS.maxCount
|
|
553
554
|
},
|
|
554
555
|
locale,
|
|
556
|
+
defaultLocale,
|
|
555
557
|
availableLocales,
|
|
556
558
|
startMinimized: behavior.startMinimized ?? true,
|
|
557
559
|
actions: resolveActions(mode, header.actions),
|
|
@@ -582,6 +584,11 @@ function resolveDefaultLocale(input) {
|
|
|
582
584
|
if (input && input !== "auto") return input;
|
|
583
585
|
return typeof navigator !== "undefined" && navigator.language ? navigator.language : "en";
|
|
584
586
|
}
|
|
587
|
+
function clampLocale(desired, availableLocales, defaultLocale) {
|
|
588
|
+
if (availableLocales.length <= 1 || availableLocales.includes(desired)) return desired;
|
|
589
|
+
if (availableLocales.includes(defaultLocale)) return defaultLocale;
|
|
590
|
+
return availableLocales[0] ?? desired;
|
|
591
|
+
}
|
|
585
592
|
function resolveActions(mode, overrides) {
|
|
586
593
|
return overrides ?? DEFAULT_ACTIONS_BY_MODE[mode];
|
|
587
594
|
}
|
|
@@ -730,13 +737,14 @@ function resolveLauncher(overrides) {
|
|
|
730
737
|
|
|
731
738
|
// src/core/config/overrides.ts
|
|
732
739
|
function applyOptionOverrides(options, activeLocale, activeThemeMode, activeTextSize) {
|
|
733
|
-
|
|
740
|
+
const locale = clampLocale(activeLocale, options.availableLocales, options.defaultLocale);
|
|
741
|
+
if (locale === options.locale && activeThemeMode === options.themeMode && activeTextSize === options.textSize) {
|
|
734
742
|
return options;
|
|
735
743
|
}
|
|
736
744
|
const next = { ...options };
|
|
737
|
-
if (
|
|
738
|
-
next.locale =
|
|
739
|
-
next.strings = resolveStrings(
|
|
745
|
+
if (locale !== options.locale) {
|
|
746
|
+
next.locale = locale;
|
|
747
|
+
next.strings = resolveStrings(locale, options.stringsOverride);
|
|
740
748
|
}
|
|
741
749
|
if (activeThemeMode !== options.themeMode) next.themeMode = activeThemeMode;
|
|
742
750
|
if (activeTextSize !== options.textSize) next.textSize = activeTextSize;
|
|
@@ -782,6 +790,8 @@ function mergeServerConfig(user, server) {
|
|
|
782
790
|
serverI18n ?? {},
|
|
783
791
|
userI18n
|
|
784
792
|
);
|
|
793
|
+
if (serverI18n?.availableLocales !== void 0) merged.availableLocales = serverI18n.availableLocales;
|
|
794
|
+
if (serverI18n?.defaultLocale !== void 0) merged.defaultLocale = serverI18n.defaultLocale;
|
|
785
795
|
const userStrings = userI18n?.strings;
|
|
786
796
|
const serverStrings = serverI18n?.strings;
|
|
787
797
|
if (userStrings || serverStrings) {
|
|
@@ -1477,6 +1487,12 @@ var AgentTransport = class {
|
|
|
1477
1487
|
// always sees the visitor's latest context as they navigate.
|
|
1478
1488
|
__publicField(this, "userContext");
|
|
1479
1489
|
__publicField(this, "pageContext");
|
|
1490
|
+
// Active BCP-47 locale, carried as the envelope's `locale` on EVERY request
|
|
1491
|
+
// so the backend can localize replies AND the data module can return
|
|
1492
|
+
// localized content + forms. Refreshed via `setLocale` whenever the visitor
|
|
1493
|
+
// switches (no re-handshake needed). Resolved + clamped to the deployment's
|
|
1494
|
+
// availableLocales by `resolveOptions` before it reaches here.
|
|
1495
|
+
__publicField(this, "locale");
|
|
1480
1496
|
// ---- Data API (content / forms — module-help-ai-data-api) --------------
|
|
1481
1497
|
//
|
|
1482
1498
|
// The DATA surfaces live on `dataApiBaseUrl` (default `${baseUrl}/api/data`)
|
|
@@ -1502,6 +1518,15 @@ var AgentTransport = class {
|
|
|
1502
1518
|
this.userContext = userContext;
|
|
1503
1519
|
this.pageContext = pageContext;
|
|
1504
1520
|
}
|
|
1521
|
+
/**
|
|
1522
|
+
* Update the active locale carried on every subsequent request. Call on
|
|
1523
|
+
* mount (before the first handshake) and whenever the user picks a new
|
|
1524
|
+
* locale — the next content / forms / message request reflects it with no
|
|
1525
|
+
* extra round-trip.
|
|
1526
|
+
*/
|
|
1527
|
+
setLocale(locale) {
|
|
1528
|
+
this.locale = locale;
|
|
1529
|
+
}
|
|
1505
1530
|
/**
|
|
1506
1531
|
* Seed the visitor + conversation identity from persistence BEFORE the first
|
|
1507
1532
|
* `handshake` resolves. A mount-time `resumeStream()` runs in parallel
|
|
@@ -1518,6 +1543,7 @@ var AgentTransport = class {
|
|
|
1518
1543
|
const out = {};
|
|
1519
1544
|
if (this.visitorId) out.visitorId = this.visitorId;
|
|
1520
1545
|
if (this.conversationId) out.conversationId = this.conversationId;
|
|
1546
|
+
if (this.locale) out.locale = this.locale;
|
|
1521
1547
|
const context = encodeContext(this.userContext, this.pageContext);
|
|
1522
1548
|
if (context) out[CONTEXT_PARAM] = context;
|
|
1523
1549
|
return out;
|
|
@@ -6725,7 +6751,10 @@ function App({ options, hostElement, bus }) {
|
|
|
6725
6751
|
visitorId,
|
|
6726
6752
|
conversationId,
|
|
6727
6753
|
userPrefs: persistence.loadUserPrefs(),
|
|
6728
|
-
locale
|
|
6754
|
+
// The visitor's ACTIVE locale (persisted pick, else auto-detected) —
|
|
6755
|
+
// not the config baseline — so the handshake learns what the visitor
|
|
6756
|
+
// wants. The response's availableLocales then clamps it for the rest.
|
|
6757
|
+
locale: activeLocale
|
|
6729
6758
|
});
|
|
6730
6759
|
} catch (err) {
|
|
6731
6760
|
if (isStale()) return;
|
|
@@ -6787,7 +6816,18 @@ function App({ options, hostElement, bus }) {
|
|
|
6787
6816
|
setConversationReady(true);
|
|
6788
6817
|
}
|
|
6789
6818
|
},
|
|
6790
|
-
[
|
|
6819
|
+
[
|
|
6820
|
+
transport,
|
|
6821
|
+
visitorId,
|
|
6822
|
+
persistence,
|
|
6823
|
+
options,
|
|
6824
|
+
bus,
|
|
6825
|
+
conversationIdSig,
|
|
6826
|
+
feedback,
|
|
6827
|
+
playWelcome,
|
|
6828
|
+
loadThread,
|
|
6829
|
+
activeLocale
|
|
6830
|
+
]
|
|
6791
6831
|
);
|
|
6792
6832
|
const userSig = JSON.stringify(options.userContext ?? null);
|
|
6793
6833
|
const pageSig = JSON.stringify(options.pageContext ?? null);
|
|
@@ -6796,6 +6836,13 @@ function App({ options, hostElement, bus }) {
|
|
|
6796
6836
|
const merged = Object.keys(formContext).length ? { ...formContext, ...options.userContext } : options.userContext;
|
|
6797
6837
|
transport.setContext(merged, toWirePageContext(options.pageContext));
|
|
6798
6838
|
}, [transport, userSig, pageSig, formCtxSig]);
|
|
6839
|
+
const effectiveLocale = clampLocale(activeLocale, options.availableLocales, options.defaultLocale);
|
|
6840
|
+
useEffect17(() => {
|
|
6841
|
+
transport.setLocale(effectiveLocale);
|
|
6842
|
+
}, [transport, effectiveLocale]);
|
|
6843
|
+
useEffect17(() => {
|
|
6844
|
+
if (effectiveLocale !== activeLocale) setActiveLocale(effectiveLocale);
|
|
6845
|
+
}, [effectiveLocale, activeLocale]);
|
|
6799
6846
|
const runResume = useCallback6(
|
|
6800
6847
|
async (handle) => {
|
|
6801
6848
|
let bubble = null;
|
|
@@ -7446,12 +7493,26 @@ function emitMessage(bus, options, role, text) {
|
|
|
7446
7493
|
options.onMessage?.(payload);
|
|
7447
7494
|
}
|
|
7448
7495
|
|
|
7449
|
-
// src/index.ts
|
|
7450
|
-
import { h, render } from "preact";
|
|
7451
|
-
|
|
7452
7496
|
// src/ui/error-boundary.tsx
|
|
7453
7497
|
import { Component } from "preact";
|
|
7454
7498
|
var log17 = logger.scope("error-boundary");
|
|
7499
|
+
var ErrorBoundary = class extends Component {
|
|
7500
|
+
constructor() {
|
|
7501
|
+
super(...arguments);
|
|
7502
|
+
__publicField(this, "state", { crashed: false });
|
|
7503
|
+
}
|
|
7504
|
+
componentDidCatch(error, info) {
|
|
7505
|
+
log17.error("widget render crashed", { error, componentStack: info?.componentStack });
|
|
7506
|
+
this.setState({ crashed: true });
|
|
7507
|
+
this.props.onError(error, info?.componentStack);
|
|
7508
|
+
}
|
|
7509
|
+
render() {
|
|
7510
|
+
return this.state.crashed ? this.props.fallback : this.props.children;
|
|
7511
|
+
}
|
|
7512
|
+
};
|
|
7513
|
+
|
|
7514
|
+
// src/index.ts
|
|
7515
|
+
import { h, render } from "preact";
|
|
7455
7516
|
|
|
7456
7517
|
// src/shadow/iframe-mount.ts
|
|
7457
7518
|
var log18 = logger.scope("iframe");
|
|
@@ -7648,9 +7709,13 @@ var ChatElement = class extends HTMLElement {
|
|
|
7648
7709
|
* values (`tracking.enabled` / `endpoint` / `token`).
|
|
7649
7710
|
*
|
|
7650
7711
|
* This path is independent of `mount()` (it supports N elements per page
|
|
7651
|
-
* without a shared `widgetId` registry), so
|
|
7652
|
-
*
|
|
7653
|
-
*
|
|
7712
|
+
* without a shared `widgetId` registry), so everything `mount()` composes
|
|
7713
|
+
* around `<App>` must be mirrored here: the handshake-config merge, the
|
|
7714
|
+
* usage tracker, and the ErrorBoundary wrapper. This has drifted three
|
|
7715
|
+
* times now (mode-merge, tracker, ErrorBoundary). The proper fix is to
|
|
7716
|
+
* extract the shared `<App>` composition into one `bootWidget(opts,
|
|
7717
|
+
* { hostElement })` helper both entry points call — tracked as a
|
|
7718
|
+
* follow-up; until then, keep these two boot paths in lockstep.
|
|
7654
7719
|
*/
|
|
7655
7720
|
__publicField(this, "resolved", null);
|
|
7656
7721
|
/** Tracker unsubscribe — released on disconnect. */
|
|
@@ -7707,7 +7772,14 @@ var ChatElement = class extends HTMLElement {
|
|
|
7707
7772
|
const cachedThemeMode = createPersistence(resolved.widgetId, resolved.storage).loadUserPrefs().themeMode;
|
|
7708
7773
|
applyHostAttributes(host, cachedThemeMode ? { ...resolved, themeMode: cachedThemeMode } : resolved);
|
|
7709
7774
|
applyMode(host, resolveInitialHostMode(resolved));
|
|
7710
|
-
render2(
|
|
7775
|
+
render2(
|
|
7776
|
+
h2(
|
|
7777
|
+
ErrorBoundary,
|
|
7778
|
+
{ onError: (error) => this.bus.emit("error", error) },
|
|
7779
|
+
h2(App, { bus: this.bus, hostElement: host, options: resolved })
|
|
7780
|
+
),
|
|
7781
|
+
this.mountResult.appRoot
|
|
7782
|
+
);
|
|
7711
7783
|
}
|
|
7712
7784
|
};
|
|
7713
7785
|
var registered = false;
|