@helpai/elements 0.39.0 → 0.40.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/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { A as Asset, B as BlocksConfig, C as ConnectionConfig, a as ConnectionConfigPartial, H as HandshakeResponse, L as Link, S as ServerConfig, b as SiteConfig, W as WidgetConfig, c as WidgetConfigPartial, d as WidgetSettings, e as WidgetSettingsPartial } from './deployment-CrsbcoCF.js';
1
+ export { A as Asset, B as BlocksConfig, C as ConnectionConfig, a as ConnectionConfigPartial, H as HandshakeResponse, L as Link, S as ServerConfig, b as SiteConfig, W as WidgetConfig, c as WidgetConfigPartial, d as WidgetSettings, e as WidgetSettingsPartial } from './deployment-MbF78Pdq.js';
2
2
  import 'zod';
3
3
 
4
4
  /**
@@ -89,6 +89,11 @@ type StringsOverride = LocaleStrings | Partial<Record<string, LocaleStrings>>;
89
89
  * `visitorId` is the deliberate exception: it stays global so the same browser
90
90
  * is one anonymous visitor everywhere (`${brand.cssPrefix}.visitorId`).
91
91
  *
92
+ * A `storageVersion` (when set) folds a `.v<version>` segment into EVERY key —
93
+ * including the global `visitorId` — so bumping it force-resets the entire
94
+ * client-side namespace for all users (a full fresh start: new visitor id,
95
+ * conversation, prefs, panel state). Unset → the unversioned keys.
96
+ *
92
97
  * Storage is pluggable via {@link ClientStorage} — pass a custom adapter
93
98
  * to add encryption, logging, or use a non-localStorage backend.
94
99
  */
@@ -1248,6 +1253,14 @@ interface ResolvedOptions {
1248
1253
  interface ServerConfig {
1249
1254
  /** Data-API base override — adopted only when the host didn't set one. */
1250
1255
  dataApiBaseUrl?: string;
1256
+ /**
1257
+ * Storage-schema version — the deployment-owned force-reset lever, pushed ONLY
1258
+ * via the handshake (deliberately not an embed attribute / `init` option).
1259
+ * Pushing a new value abandons every client's prior storage namespace
1260
+ * (visitor id, conversation, prefs, panel state, caches) on next load. The
1261
+ * widget stashes it and the next mount folds `.v<version>` into every key.
1262
+ */
1263
+ storageVersion?: string;
1251
1264
  presentation?: PresentationOptions;
1252
1265
  behavior?: BehaviorOptions;
1253
1266
  theme?: ThemePreference | ThemeOverrides;
package/index.mjs CHANGED
@@ -1049,8 +1049,11 @@ function fillRandom(view) {
1049
1049
  var log2 = logger.scope("persistence");
1050
1050
  function createPersistence(widgetId, storage = defaultStorage, deploymentId) {
1051
1051
  const scope = deploymentId ? `${widgetId}.${deploymentId}` : widgetId;
1052
- const prefix = `${BRAND.cssPrefix}.${scope}`;
1053
- const KEY_VISITOR = `${BRAND.cssPrefix}.visitorId`;
1052
+ const KEY_STORAGE_VERSION = `${BRAND.cssPrefix}.storageVersion`;
1053
+ const activeVersion = storage.get(KEY_STORAGE_VERSION) ?? void 0;
1054
+ const version2 = activeVersion ? `.v${activeVersion}` : "";
1055
+ const prefix = `${BRAND.cssPrefix}.${scope}${version2}`;
1056
+ const KEY_VISITOR = `${BRAND.cssPrefix}${version2}.visitorId`;
1054
1057
  const KEY_CONVERSATION = `${prefix}.conversationId`;
1055
1058
  const KEY_USER_PREFS = `${prefix}.userPrefs`;
1056
1059
  const KEY_PANEL_OPEN = `${prefix}.panelOpen`;
@@ -1161,6 +1164,12 @@ function createPersistence(widgetId, storage = defaultStorage, deploymentId) {
1161
1164
  log2.warn("saveFormDone serialise failed", { error });
1162
1165
  }
1163
1166
  },
1167
+ rememberStorageVersion(serverVersion) {
1168
+ if (serverVersion && serverVersion !== storage.get(KEY_STORAGE_VERSION)) {
1169
+ log2.debug("rememberStorageVersion (force-reset on next load)", { version: serverVersion });
1170
+ storage.set(KEY_STORAGE_VERSION, serverVersion);
1171
+ }
1172
+ },
1164
1173
  clearConversation() {
1165
1174
  storage.remove(KEY_CONVERSATION);
1166
1175
  }
@@ -1397,7 +1406,7 @@ import { h, render as renderPreact } from "preact";
1397
1406
 
1398
1407
  // src/ui/app.tsx
1399
1408
  import { useCallback as useCallback6, useEffect as useEffect17, useMemo as useMemo3, useRef as useRef9, useState as useState13 } from "preact/hooks";
1400
- import { useComputed as useComputed7, useSignal as useSignal2 } from "@preact/signals";
1409
+ import { batch, useComputed as useComputed7, useSignal as useSignal2 } from "@preact/signals";
1401
1410
 
1402
1411
  // src/core/handshake-shape.ts
1403
1412
  function isPlainObject2(raw) {
@@ -6738,6 +6747,9 @@ function useLauncherCallout({ callout, persistence }) {
6738
6747
  import { jsx as jsx36, jsxs as jsxs31 } from "preact/jsx-runtime";
6739
6748
  var log17 = logger.scope("app");
6740
6749
  var p32 = BRAND.cssPrefix;
6750
+ function makeLocalizedWelcome(w, strings) {
6751
+ return makeInstantWelcomeMessage({ ...w, text: localizeText(strings, w.text) });
6752
+ }
6741
6753
  function App({ options, hostElement, bus }) {
6742
6754
  const [persistence] = useState13(
6743
6755
  () => createPersistence(options.widgetId, options.storage, options.aiAgentDeploymentId)
@@ -6808,6 +6820,7 @@ function App({ options, hostElement, bus }) {
6808
6820
  const [agent, setAgent] = useState13(null);
6809
6821
  const [suggestions, setSuggestions] = useState13([]);
6810
6822
  const [activeCancel, setActiveCancel] = useState13(null);
6823
+ const stringsRef = useRef9(options.strings);
6811
6824
  const [parsedSite, setParsedSite] = useState13(void 0);
6812
6825
  const [parsedBlocks, setParsedBlocks] = useState13(void 0);
6813
6826
  const [sidebarCollapsed, setSidebarCollapsed] = useState13(() => persistence.loadSidebarCollapsed() ?? false);
@@ -6892,7 +6905,7 @@ function App({ options, hostElement, bus }) {
6892
6905
  const playWelcome = useCallback6(
6893
6906
  (welcomeMessages) => {
6894
6907
  if (!welcomeMessages || welcomeMessages.length === 0) return;
6895
- messagesSig.value = welcomeMessages.map(makeInstantWelcomeMessage);
6908
+ messagesSig.value = welcomeMessages.map((w) => makeLocalizedWelcome(w, stringsRef.current));
6896
6909
  },
6897
6910
  [messagesSig]
6898
6911
  );
@@ -6925,7 +6938,7 @@ function App({ options, hostElement, bus }) {
6925
6938
  const welcomeAnchor = (timelineStamps.length ? Math.min(...timelineStamps) : Date.now()) - 1;
6926
6939
  const loadedIds = new Set(loaded.map((m) => m.id));
6927
6940
  const welcomeRows = (welcomeRef.current?.messages ?? []).filter((w) => !loadedIds.has(w.id)).map((w) => {
6928
- const row = makeInstantWelcomeMessage(w);
6941
+ const row = makeLocalizedWelcome(w, stringsRef.current);
6929
6942
  row.createdAt = welcomeAnchor;
6930
6943
  return row;
6931
6944
  });
@@ -7012,7 +7025,6 @@ function App({ options, hostElement, bus }) {
7012
7025
  });
7013
7026
  }
7014
7027
  if (res.agent) setAgent(res.agent);
7015
- if (res.welcome?.suggestions) setSuggestions(res.welcome.suggestions);
7016
7028
  if (options.mode === "page") {
7017
7029
  if (res.site && isSiteConfigShape(res.site)) setParsedSite(res.site);
7018
7030
  else if (res.site) log17.warn("invalid site config, using fallback", { site: res.site });
@@ -7027,6 +7039,17 @@ function App({ options, hostElement, bus }) {
7027
7039
  if (res.userPrefs.textSize) setActiveTextSize(res.userPrefs.textSize);
7028
7040
  }
7029
7041
  welcomeRef.current = res.welcome;
7042
+ if (!newConversation) bus.emit("handshake", res);
7043
+ if (res.welcome?.suggestions) {
7044
+ const strings = stringsRef.current;
7045
+ setSuggestions(
7046
+ res.welcome.suggestions.map((sg) => ({
7047
+ ...sg,
7048
+ label: localizeText(strings, sg.label),
7049
+ ...sg.text === void 0 ? {} : { text: localizeText(strings, sg.text) }
7050
+ }))
7051
+ );
7052
+ }
7030
7053
  const isResume = !newConversation && persistedChatId && res.conversationId === persistedChatId;
7031
7054
  if (isResume) {
7032
7055
  if (activatedRef.current) void loadThread(persistedChatId);
@@ -7036,10 +7059,7 @@ function App({ options, hostElement, bus }) {
7036
7059
  persistence.saveConversationId(res.conversationId);
7037
7060
  playWelcome(res.welcome?.messages);
7038
7061
  }
7039
- if (!newConversation) {
7040
- bus.emit("handshake", res);
7041
- setConversationReady(true);
7042
- }
7062
+ if (!newConversation) setConversationReady(true);
7043
7063
  },
7044
7064
  [
7045
7065
  transport,
@@ -7427,8 +7447,10 @@ function App({ options, hostElement, bus }) {
7427
7447
  log17.info("clear \u2192 new conversation", { wasStreaming: streaming });
7428
7448
  bus.emit("clear", void 0);
7429
7449
  if (streaming) activeCancel?.();
7430
- messagesSig.value = [];
7431
- conversationIdSig.value = void 0;
7450
+ batch(() => {
7451
+ messagesSig.value = [];
7452
+ conversationIdSig.value = void 0;
7453
+ });
7432
7454
  persistence.clearConversation();
7433
7455
  setCanSend(true);
7434
7456
  setFormMarkers([]);
@@ -7527,9 +7549,11 @@ function App({ options, hostElement, bus }) {
7527
7549
  fetchFormMarkers(targetConversationId)
7528
7550
  ]);
7529
7551
  const hydrated = res.messages.map(fromWireMessage);
7530
- messagesSig.value = withWelcomeRows(hydrated, markers);
7552
+ batch(() => {
7553
+ messagesSig.value = withWelcomeRows(hydrated, markers);
7554
+ conversationIdSig.value = res.conversationId;
7555
+ });
7531
7556
  setFormMarkers(markers);
7532
- conversationIdSig.value = res.conversationId;
7533
7557
  persistence.saveConversationId(res.conversationId);
7534
7558
  if (res.agent) setAgent(res.agent);
7535
7559
  setCanSend(res.canContinue);
@@ -7583,6 +7607,7 @@ function App({ options, hostElement, bus }) {
7583
7607
  () => applyOptionOverrides(options, activeLocale, activeThemeMode, activeTextSize),
7584
7608
  [options, activeLocale, activeThemeMode, activeTextSize]
7585
7609
  );
7610
+ stringsRef.current = effectiveOptions.strings;
7586
7611
  const enrichedFormMarkers = useMemo3(
7587
7612
  () => formMarkers.map((m) => {
7588
7613
  const def = effectiveForms.list.find((f) => f.id === m.formId);
@@ -7903,6 +7928,7 @@ function createWidgetRuntime(params) {
7903
7928
  };
7904
7929
  bus.on("handshake", (response) => {
7905
7930
  if (!response.config) return;
7931
+ if (response.config.storageVersion) persistence.rememberStorageVersion(response.config.storageVersion);
7906
7932
  serverConfig = response.config;
7907
7933
  recompute();
7908
7934
  render2();
package/package.json CHANGED
@@ -80,5 +80,5 @@
80
80
  ],
81
81
  "type": "module",
82
82
  "types": "./index.d.ts",
83
- "version": "0.39.0"
83
+ "version": "0.40.0"
84
84
  }
package/schema.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { A as Asset, B as BlocksConfig, C as ConnectionConfig, a as ConnectionConfigPartial, E as Endpoints, H as HandshakeResponse, L as Link, P as PAGE_AREA_SUGGESTIONS, f as PageContext, S as ServerConfig, b as SiteConfig, U as UserContext, W as WidgetConfig, c as WidgetConfigPartial, d as WidgetSettings, e as WidgetSettingsPartial, g as assetSchema, h as blocksConfigSchema, i as connectionConfigPartialSchema, j as connectionConfigSchema, k as cssColorSchema, l as cssLengthSchema, m as endpointsSchema, n as handshakeResponseSchema, o as linkSchema, p as localeSchema, q as pageContextSchema, s as serverConfigSchema, r as siteConfigSchema, u as userContextSchema, t as uuid7Schema, w as widgetConfigPartialSchema, v as widgetConfigSchema, x as widgetSettingsPartialSchema, y as widgetSettingsSchema } from './deployment-CrsbcoCF.js';
1
+ export { A as Asset, B as BlocksConfig, C as ConnectionConfig, a as ConnectionConfigPartial, E as Endpoints, H as HandshakeResponse, L as Link, P as PAGE_AREA_SUGGESTIONS, f as PageContext, S as ServerConfig, b as SiteConfig, U as UserContext, W as WidgetConfig, c as WidgetConfigPartial, d as WidgetSettings, e as WidgetSettingsPartial, g as assetSchema, h as blocksConfigSchema, i as connectionConfigPartialSchema, j as connectionConfigSchema, k as cssColorSchema, l as cssLengthSchema, m as endpointsSchema, n as handshakeResponseSchema, o as linkSchema, p as localeSchema, q as pageContextSchema, s as serverConfigSchema, r as siteConfigSchema, u as userContextSchema, t as uuid7Schema, w as widgetConfigPartialSchema, v as widgetConfigSchema, x as widgetSettingsPartialSchema, y as widgetSettingsSchema } from './deployment-MbF78Pdq.js';
2
2
  import { z } from 'zod';
3
3
 
4
4
  /**
package/schema.json CHANGED
@@ -3403,6 +3403,11 @@
3403
3403
  "type": "string",
3404
3404
  "minLength": 1,
3405
3405
  "maxLength": 2048
3406
+ },
3407
+ "storageVersion": {
3408
+ "type": "string",
3409
+ "minLength": 1,
3410
+ "maxLength": 64
3406
3411
  }
3407
3412
  },
3408
3413
  "additionalProperties": {}
package/schema.mjs CHANGED
@@ -758,7 +758,8 @@ var connectionConfigPartialSchema = connectionConfigSchema.partial();
758
758
  // src/schema/deployment.ts
759
759
  import { z as z17 } from "zod";
760
760
  var serverConfigSchema = widgetSettingsSchema.partial().omit({ forms: true }).extend({
761
- dataApiBaseUrl: z17.string().min(1).max(2048).optional()
761
+ dataApiBaseUrl: z17.string().min(1).max(2048).optional(),
762
+ storageVersion: z17.string().min(1).max(64).optional()
762
763
  });
763
764
  var siteConfigSchema = z17.object({
764
765
  title: z17.string().min(1).max(120).optional().describe("Brand / site name. Used as the logo's alt-text fallback."),
package/web-component.mjs CHANGED
@@ -1087,8 +1087,11 @@ function fillRandom(view) {
1087
1087
  var log2 = logger.scope("persistence");
1088
1088
  function createPersistence(widgetId, storage = defaultStorage, deploymentId) {
1089
1089
  const scope = deploymentId ? `${widgetId}.${deploymentId}` : widgetId;
1090
- const prefix = `${BRAND.cssPrefix}.${scope}`;
1091
- const KEY_VISITOR = `${BRAND.cssPrefix}.visitorId`;
1090
+ const KEY_STORAGE_VERSION = `${BRAND.cssPrefix}.storageVersion`;
1091
+ const activeVersion = storage.get(KEY_STORAGE_VERSION) ?? void 0;
1092
+ const version = activeVersion ? `.v${activeVersion}` : "";
1093
+ const prefix = `${BRAND.cssPrefix}.${scope}${version}`;
1094
+ const KEY_VISITOR = `${BRAND.cssPrefix}${version}.visitorId`;
1092
1095
  const KEY_CONVERSATION = `${prefix}.conversationId`;
1093
1096
  const KEY_USER_PREFS = `${prefix}.userPrefs`;
1094
1097
  const KEY_PANEL_OPEN = `${prefix}.panelOpen`;
@@ -1199,6 +1202,12 @@ function createPersistence(widgetId, storage = defaultStorage, deploymentId) {
1199
1202
  log2.warn("saveFormDone serialise failed", { error });
1200
1203
  }
1201
1204
  },
1205
+ rememberStorageVersion(serverVersion) {
1206
+ if (serverVersion && serverVersion !== storage.get(KEY_STORAGE_VERSION)) {
1207
+ log2.debug("rememberStorageVersion (force-reset on next load)", { version: serverVersion });
1208
+ storage.set(KEY_STORAGE_VERSION, serverVersion);
1209
+ }
1210
+ },
1202
1211
  clearConversation() {
1203
1212
  storage.remove(KEY_CONVERSATION);
1204
1213
  }
@@ -1339,7 +1348,7 @@ import { h, render as renderPreact } from "preact";
1339
1348
 
1340
1349
  // src/ui/app.tsx
1341
1350
  import { useCallback as useCallback6, useEffect as useEffect17, useMemo as useMemo3, useRef as useRef9, useState as useState13 } from "preact/hooks";
1342
- import { useComputed as useComputed7, useSignal as useSignal2 } from "@preact/signals";
1351
+ import { batch, useComputed as useComputed7, useSignal as useSignal2 } from "@preact/signals";
1343
1352
 
1344
1353
  // src/core/handshake-shape.ts
1345
1354
  function isPlainObject2(raw) {
@@ -6697,6 +6706,9 @@ function useLauncherCallout({ callout, persistence }) {
6697
6706
  import { jsx as jsx36, jsxs as jsxs31 } from "preact/jsx-runtime";
6698
6707
  var log16 = logger.scope("app");
6699
6708
  var p32 = BRAND.cssPrefix;
6709
+ function makeLocalizedWelcome(w, strings) {
6710
+ return makeInstantWelcomeMessage({ ...w, text: localizeText(strings, w.text) });
6711
+ }
6700
6712
  function App({ options, hostElement, bus }) {
6701
6713
  const [persistence] = useState13(
6702
6714
  () => createPersistence(options.widgetId, options.storage, options.aiAgentDeploymentId)
@@ -6767,6 +6779,7 @@ function App({ options, hostElement, bus }) {
6767
6779
  const [agent, setAgent] = useState13(null);
6768
6780
  const [suggestions, setSuggestions] = useState13([]);
6769
6781
  const [activeCancel, setActiveCancel] = useState13(null);
6782
+ const stringsRef = useRef9(options.strings);
6770
6783
  const [parsedSite, setParsedSite] = useState13(void 0);
6771
6784
  const [parsedBlocks, setParsedBlocks] = useState13(void 0);
6772
6785
  const [sidebarCollapsed, setSidebarCollapsed] = useState13(() => persistence.loadSidebarCollapsed() ?? false);
@@ -6851,7 +6864,7 @@ function App({ options, hostElement, bus }) {
6851
6864
  const playWelcome = useCallback6(
6852
6865
  (welcomeMessages) => {
6853
6866
  if (!welcomeMessages || welcomeMessages.length === 0) return;
6854
- messagesSig.value = welcomeMessages.map(makeInstantWelcomeMessage);
6867
+ messagesSig.value = welcomeMessages.map((w) => makeLocalizedWelcome(w, stringsRef.current));
6855
6868
  },
6856
6869
  [messagesSig]
6857
6870
  );
@@ -6884,7 +6897,7 @@ function App({ options, hostElement, bus }) {
6884
6897
  const welcomeAnchor = (timelineStamps.length ? Math.min(...timelineStamps) : Date.now()) - 1;
6885
6898
  const loadedIds = new Set(loaded.map((m) => m.id));
6886
6899
  const welcomeRows = (welcomeRef.current?.messages ?? []).filter((w) => !loadedIds.has(w.id)).map((w) => {
6887
- const row = makeInstantWelcomeMessage(w);
6900
+ const row = makeLocalizedWelcome(w, stringsRef.current);
6888
6901
  row.createdAt = welcomeAnchor;
6889
6902
  return row;
6890
6903
  });
@@ -6971,7 +6984,6 @@ function App({ options, hostElement, bus }) {
6971
6984
  });
6972
6985
  }
6973
6986
  if (res.agent) setAgent(res.agent);
6974
- if (res.welcome?.suggestions) setSuggestions(res.welcome.suggestions);
6975
6987
  if (options.mode === "page") {
6976
6988
  if (res.site && isSiteConfigShape(res.site)) setParsedSite(res.site);
6977
6989
  else if (res.site) log16.warn("invalid site config, using fallback", { site: res.site });
@@ -6986,6 +6998,17 @@ function App({ options, hostElement, bus }) {
6986
6998
  if (res.userPrefs.textSize) setActiveTextSize(res.userPrefs.textSize);
6987
6999
  }
6988
7000
  welcomeRef.current = res.welcome;
7001
+ if (!newConversation) bus.emit("handshake", res);
7002
+ if (res.welcome?.suggestions) {
7003
+ const strings = stringsRef.current;
7004
+ setSuggestions(
7005
+ res.welcome.suggestions.map((sg) => ({
7006
+ ...sg,
7007
+ label: localizeText(strings, sg.label),
7008
+ ...sg.text === void 0 ? {} : { text: localizeText(strings, sg.text) }
7009
+ }))
7010
+ );
7011
+ }
6989
7012
  const isResume = !newConversation && persistedChatId && res.conversationId === persistedChatId;
6990
7013
  if (isResume) {
6991
7014
  if (activatedRef.current) void loadThread(persistedChatId);
@@ -6995,10 +7018,7 @@ function App({ options, hostElement, bus }) {
6995
7018
  persistence.saveConversationId(res.conversationId);
6996
7019
  playWelcome(res.welcome?.messages);
6997
7020
  }
6998
- if (!newConversation) {
6999
- bus.emit("handshake", res);
7000
- setConversationReady(true);
7001
- }
7021
+ if (!newConversation) setConversationReady(true);
7002
7022
  },
7003
7023
  [
7004
7024
  transport,
@@ -7386,8 +7406,10 @@ function App({ options, hostElement, bus }) {
7386
7406
  log16.info("clear \u2192 new conversation", { wasStreaming: streaming });
7387
7407
  bus.emit("clear", void 0);
7388
7408
  if (streaming) activeCancel?.();
7389
- messagesSig.value = [];
7390
- conversationIdSig.value = void 0;
7409
+ batch(() => {
7410
+ messagesSig.value = [];
7411
+ conversationIdSig.value = void 0;
7412
+ });
7391
7413
  persistence.clearConversation();
7392
7414
  setCanSend(true);
7393
7415
  setFormMarkers([]);
@@ -7486,9 +7508,11 @@ function App({ options, hostElement, bus }) {
7486
7508
  fetchFormMarkers(targetConversationId)
7487
7509
  ]);
7488
7510
  const hydrated = res.messages.map(fromWireMessage);
7489
- messagesSig.value = withWelcomeRows(hydrated, markers);
7511
+ batch(() => {
7512
+ messagesSig.value = withWelcomeRows(hydrated, markers);
7513
+ conversationIdSig.value = res.conversationId;
7514
+ });
7490
7515
  setFormMarkers(markers);
7491
- conversationIdSig.value = res.conversationId;
7492
7516
  persistence.saveConversationId(res.conversationId);
7493
7517
  if (res.agent) setAgent(res.agent);
7494
7518
  setCanSend(res.canContinue);
@@ -7542,6 +7566,7 @@ function App({ options, hostElement, bus }) {
7542
7566
  () => applyOptionOverrides(options, activeLocale, activeThemeMode, activeTextSize),
7543
7567
  [options, activeLocale, activeThemeMode, activeTextSize]
7544
7568
  );
7569
+ stringsRef.current = effectiveOptions.strings;
7545
7570
  const enrichedFormMarkers = useMemo3(
7546
7571
  () => formMarkers.map((m) => {
7547
7572
  const def = effectiveForms.list.find((f) => f.id === m.formId);
@@ -7862,6 +7887,7 @@ function createWidgetRuntime(params) {
7862
7887
  };
7863
7888
  bus.on("handshake", (response) => {
7864
7889
  if (!response.config) return;
7890
+ if (response.config.storageVersion) persistence.rememberStorageVersion(response.config.storageVersion);
7865
7891
  serverConfig = response.config;
7866
7892
  recompute();
7867
7893
  render2();