@syntrologie/runtime-sdk 2.8.0-canary.152 → 2.8.0-canary.154

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.
@@ -8040,6 +8040,14 @@ Please report this to https://github.com/markedjs/marked.`, e9) {
8040
8040
  '<path d="m3.3 7 8.7 5 8.7-5"/>',
8041
8041
  '<path d="m7.5 4.27 9 5.15"/>'
8042
8042
  ],
8043
+ "\u{1F69A}": [
8044
+ // Lucide truck
8045
+ '<path d="M14 18V6a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v11a1 1 0 0 0 1 1h2"/>',
8046
+ '<path d="M15 18H9"/>',
8047
+ '<path d="M19 18h2a1 1 0 0 0 1-1v-3.65a1 1 0 0 0-.22-.624l-3.48-4.35A1 1 0 0 0 17.52 8H14"/>',
8048
+ '<circle cx="17" cy="18" r="2"/>',
8049
+ '<circle cx="7" cy="18" r="2"/>'
8050
+ ],
8043
8051
  "\u{1F331}": [
8044
8052
  '<path d="M7 20h10"/>',
8045
8053
  '<path d="M10 20c5.5-2.5.8-6.4 3-10"/>',
@@ -13490,7 +13498,7 @@ Please report this to https://github.com/markedjs/marked.`, e9) {
13490
13498
  }
13491
13499
  if (!/^[a-zA-Z][a-zA-Z0-9-]*$/.test(String(el.tag_name ?? "")))
13492
13500
  return null;
13493
- const attrRegex = /([\w$]+)="([^"]*)"/g;
13501
+ const attrRegex = /([\w$-]+)="([^"]*)"/g;
13494
13502
  let match;
13495
13503
  while ((match = attrRegex.exec(attrPart)) !== null) {
13496
13504
  const [, key, value] = match;
@@ -13517,10 +13525,21 @@ Please report this to https://github.com/markedjs/marked.`, e9) {
13517
13525
  }
13518
13526
  return directTag;
13519
13527
  }
13520
- function extractProps(phEvent) {
13528
+ function mergeEnrichElements(phElements, enrichElements) {
13529
+ return phElements.map((phEl, i9) => {
13530
+ const enrichEl = enrichElements[i9];
13531
+ if (!enrichEl)
13532
+ return phEl;
13533
+ if (phEl.tag_name !== enrichEl.tag_name)
13534
+ return phEl;
13535
+ return { ...enrichEl, ...phEl };
13536
+ });
13537
+ }
13538
+ function extractProps(phEvent, enrichElements) {
13521
13539
  const props = {};
13522
13540
  const phProps = phEvent.properties || {};
13523
- const elements2 = phProps.$elements ?? (typeof phProps.$elements_chain === "string" ? parseElementsChain(phProps.$elements_chain) : void 0);
13541
+ const rawElements = phProps.$elements ?? (typeof phProps.$elements_chain === "string" ? parseElementsChain(phProps.$elements_chain) : void 0);
13542
+ const elements2 = rawElements && enrichElements ? mergeEnrichElements(rawElements, enrichElements) : rawElements;
13524
13543
  const directTag = phProps.$tag_name ?? elements2?.[0]?.tag_name;
13525
13544
  const isClickEvent = phEvent.event === "$autocapture" || phEvent.event === "$click";
13526
13545
  props.tagName = isClickEvent ? resolveInteractiveTag(elements2, directTag) : directTag;
@@ -13550,7 +13569,7 @@ Please report this to https://github.com/markedjs/marked.`, e9) {
13550
13569
  props.originalEvent = phEvent.event;
13551
13570
  return props;
13552
13571
  }
13553
- function normalizePostHogEvent(phEvent) {
13572
+ function normalizePostHogEvent(phEvent, enrichElements) {
13554
13573
  let ts2;
13555
13574
  if (typeof phEvent.timestamp === "number") {
13556
13575
  ts2 = phEvent.timestamp;
@@ -13563,7 +13582,7 @@ Please report this to https://github.com/markedjs/marked.`, e9) {
13563
13582
  ts: ts2,
13564
13583
  name: getEventName(phEvent),
13565
13584
  source: "posthog",
13566
- props: extractProps(phEvent),
13585
+ props: extractProps(phEvent, enrichElements),
13567
13586
  schemaVersion: EVENT_SCHEMA_VERSION
13568
13587
  };
13569
13588
  }
@@ -13623,12 +13642,29 @@ Please report this to https://github.com/markedjs/marked.`, e9) {
13623
13642
  const idle = new IdleDetector(config, emit2);
13624
13643
  const hover = new HoverTracker(config, emit2, options?.elementResolver);
13625
13644
  const clickAttrBuffer = [];
13645
+ const CLICK_ATTR_TTL_MS = 500;
13646
+ function isClickShapedEvent(phEvent) {
13647
+ if (phEvent.event === "$click")
13648
+ return true;
13649
+ if (phEvent.event !== "$autocapture")
13650
+ return false;
13651
+ const eventType = phEvent.properties?.$event_type;
13652
+ return eventType === void 0 || eventType === "click";
13653
+ }
13654
+ function consumeBufferedClickChain(eventTs) {
13655
+ const cutoff = eventTs - CLICK_ATTR_TTL_MS;
13656
+ while (clickAttrBuffer.length > 0 && clickAttrBuffer[0].ts < cutoff) {
13657
+ clickAttrBuffer.shift();
13658
+ }
13659
+ return clickAttrBuffer.shift()?.elements;
13660
+ }
13626
13661
  return {
13627
13662
  ingest(raw) {
13628
13663
  if (raw.kind === "posthog") {
13629
13664
  const { kind: _4, ...phEvent } = raw;
13630
13665
  if (shouldNormalizeEvent(phEvent)) {
13631
- emit2(normalizePostHogEvent(phEvent));
13666
+ const enrichElements = isClickShapedEvent(phEvent) ? consumeBufferedClickChain(typeof phEvent.timestamp === "number" ? phEvent.timestamp : Date.now()) : void 0;
13667
+ emit2(normalizePostHogEvent(phEvent, enrichElements));
13632
13668
  }
13633
13669
  } else if (raw.kind === "rrweb") {
13634
13670
  hesitation.ingest(raw);
@@ -13649,7 +13685,7 @@ Please report this to https://github.com/markedjs/marked.`, e9) {
13649
13685
  },
13650
13686
  enrichClickAttributes(timestamp, elements2) {
13651
13687
  clickAttrBuffer.push({ ts: timestamp, elements: elements2 });
13652
- const cutoff = timestamp - 500;
13688
+ const cutoff = timestamp - CLICK_ATTR_TTL_MS;
13653
13689
  while (clickAttrBuffer.length > 0 && clickAttrBuffer[0].ts < cutoff) {
13654
13690
  clickAttrBuffer.shift();
13655
13691
  }
@@ -14696,7 +14732,7 @@ Please report this to https://github.com/markedjs/marked.`, e9) {
14696
14732
  }
14697
14733
 
14698
14734
  // src/version.ts
14699
- var SDK_VERSION = "2.8.0-canary.152";
14735
+ var SDK_VERSION = "2.8.0-canary.154";
14700
14736
 
14701
14737
  // src/types.ts
14702
14738
  var SDK_SCHEMA_VERSION = "2.0";
@@ -15455,7 +15491,7 @@ ${cssRules}
15455
15491
  tracker.trackTriggered(tile.id, tile.widget ?? "unknown");
15456
15492
  }
15457
15493
  }
15458
- var _controller, _controllerUnsub, _overlayContainer, _portalRoot, _batchHandle, _adoptedInitial, _runVersion, _rawConfig, _prevActionsJson, _derivedFetcher, _experimentUnsub, _accumulatorUnsub, _contextUnsub, _eventBusUnsub, _onUrlChange, _themeCtrl, _runtimeProvider, _SmartCanvasElementLit_instances, rebuildFetcher_fn, buildFetcherOptions_fn, _loadConfigVersion, refilterTiles_fn, runActionLifecycle_fn, startUrlTracking_fn, stopUrlTracking_fn, subscribeRuntimeReactivity_fn, unsubscribeRuntimeReactivity_fn, subscribeCanvasRequestOpen_fn, _onCanvasToggle;
15494
+ var _controller, _controllerUnsub, _overlayContainer, _portalRoot, _batchHandle, _adoptedInitial, _runVersion, _rawConfig, _prevActionsJson, _prevTilesJson, _prevThemeJson, _prevLauncherJson, _derivedFetcher, _experimentUnsub, _accumulatorUnsub, _contextUnsub, _eventBusUnsub, _onUrlChange, _themeCtrl, _runtimeProvider, _SmartCanvasElementLit_instances, rebuildFetcher_fn, buildFetcherOptions_fn, _loadConfigVersion, refilterTiles_fn, runActionLifecycle_fn, startUrlTracking_fn, stopUrlTracking_fn, subscribeRuntimeReactivity_fn, unsubscribeRuntimeReactivity_fn, subscribeCanvasRequestOpen_fn, _onCanvasToggle;
15459
15495
  var SmartCanvasElementLit = class extends i4 {
15460
15496
  // ---------- Constructor --------------------------------------------------
15461
15497
  constructor() {
@@ -15507,6 +15543,21 @@ ${cssRules}
15507
15543
  // Config fetch state
15508
15544
  __privateAdd(this, _rawConfig, null);
15509
15545
  __privateAdd(this, _prevActionsJson, "[]");
15546
+ // Stable JSON snapshot of the rendered tile array. Skips reactive state
15547
+ // updates when a config refresh produces an identical tile set — otherwise
15548
+ // Lit re-mounts every tile on each poll, blowing away widget state like
15549
+ // open FAQ accordion items.
15550
+ __privateAdd(this, _prevTilesJson, "[]");
15551
+ // Same JSON-diff guard pattern for the rest of the reactive config fields.
15552
+ // GrowthBook's onFeaturesChanged refires every ~5s; without these guards
15553
+ // the parent template re-renders on every poll (fresh deserialized theme/
15554
+ // launcher refs), which ripples into widget re-mounts and vega-embed
15555
+ // resize-observer thrash. See SmartCanvasElementLit.test.ts
15556
+ // "reactive-state stability across identical refresh" for the contract.
15557
+ // The _isLoading spinner is gated separately via the `silent` opt on
15558
+ // `_loadConfig` — see "loading-state semantics" tests for that contract.
15559
+ __privateAdd(this, _prevThemeJson, "null");
15560
+ __privateAdd(this, _prevLauncherJson, "null");
15510
15561
  __privateAdd(this, _derivedFetcher, null);
15511
15562
  // Subscriptions
15512
15563
  __privateAdd(this, _experimentUnsub, null);
@@ -15612,7 +15663,9 @@ ${cssRules}
15612
15663
  __privateMethod(this, _SmartCanvasElementLit_instances, rebuildFetcher_fn).call(this);
15613
15664
  (_a2 = __privateGet(this, _experimentUnsub)) == null ? void 0 : _a2.call(this);
15614
15665
  if (this.experiments?.onFeaturesChanged) {
15615
- __privateSet(this, _experimentUnsub, this.experiments.onFeaturesChanged(() => this._loadConfig()));
15666
+ __privateSet(this, _experimentUnsub, this.experiments.onFeaturesChanged(
15667
+ () => this._loadConfig({ silent: true })
15668
+ ));
15616
15669
  }
15617
15670
  }
15618
15671
  if (fetcherPropsChanged || changed.has("_pageUrl")) {
@@ -15729,11 +15782,13 @@ ${cssRules}
15729
15782
  this.initialBatchActions = props.initialBatchActions;
15730
15783
  if (props.workspaceTheme !== void 0) this.workspaceTheme = props.workspaceTheme;
15731
15784
  }
15732
- async _loadConfig() {
15785
+ async _loadConfig({ silent = false } = {}) {
15733
15786
  if (!__privateGet(this, _derivedFetcher)) return;
15734
15787
  const version = ++__privateWrapper(this, _loadConfigVersion)._;
15735
15788
  try {
15736
- this._isLoading = true;
15789
+ if (!silent) {
15790
+ this._isLoading = true;
15791
+ }
15737
15792
  this._error = void 0;
15738
15793
  const response = await __privateGet(this, _derivedFetcher).call(this);
15739
15794
  if (version !== __privateGet(this, _loadConfigVersion)) return;
@@ -15760,19 +15815,41 @@ ${cssRules}
15760
15815
  if (actionsChanged) {
15761
15816
  __privateSet(this, _prevActionsJson, newActionsJson);
15762
15817
  }
15763
- this._tiles = sortTiles(tiles);
15818
+ const sortedTiles = sortTiles(tiles);
15819
+ const newTilesJson = JSON.stringify(sortedTiles);
15820
+ if (newTilesJson !== __privateGet(this, _prevTilesJson)) {
15821
+ __privateSet(this, _prevTilesJson, newTilesJson);
15822
+ this._tiles = sortedTiles;
15823
+ }
15764
15824
  if (actionsChanged) {
15765
15825
  this._configActions = newActions;
15766
15826
  }
15767
- this._isLoading = false;
15768
- this._canvasTitle = response.canvasTitle;
15769
- this._theme = response.theme;
15770
- this._launcher = response.launcher;
15771
- this._displayMode = response.displayMode ?? "standard";
15827
+ if (!silent) {
15828
+ this._isLoading = false;
15829
+ }
15830
+ const newThemeJson = JSON.stringify(response.theme ?? null);
15831
+ if (newThemeJson !== __privateGet(this, _prevThemeJson)) {
15832
+ __privateSet(this, _prevThemeJson, newThemeJson);
15833
+ this._theme = response.theme;
15834
+ }
15835
+ const newLauncherJson = JSON.stringify(response.launcher ?? null);
15836
+ if (newLauncherJson !== __privateGet(this, _prevLauncherJson)) {
15837
+ __privateSet(this, _prevLauncherJson, newLauncherJson);
15838
+ this._launcher = response.launcher;
15839
+ }
15840
+ if (response.canvasTitle !== this._canvasTitle) {
15841
+ this._canvasTitle = response.canvasTitle;
15842
+ }
15843
+ const newDisplayMode = response.displayMode ?? "standard";
15844
+ if (newDisplayMode !== this._displayMode) {
15845
+ this._displayMode = newDisplayMode;
15846
+ }
15772
15847
  } catch (err) {
15773
15848
  const message = err instanceof Error ? err.message : "Unknown error";
15774
15849
  console.error("[SmartCanvas Config] Failed to fetch/filter config:", message, err);
15775
- this._isLoading = false;
15850
+ if (!silent) {
15851
+ this._isLoading = false;
15852
+ }
15776
15853
  this._error = message;
15777
15854
  }
15778
15855
  }
@@ -15786,6 +15863,9 @@ ${cssRules}
15786
15863
  _runVersion = new WeakMap();
15787
15864
  _rawConfig = new WeakMap();
15788
15865
  _prevActionsJson = new WeakMap();
15866
+ _prevTilesJson = new WeakMap();
15867
+ _prevThemeJson = new WeakMap();
15868
+ _prevLauncherJson = new WeakMap();
15789
15869
  _derivedFetcher = new WeakMap();
15790
15870
  _experimentUnsub = new WeakMap();
15791
15871
  _accumulatorUnsub = new WeakMap();
@@ -30864,6 +30944,29 @@ ${cssRules}
30864
30944
  runtime: runtime5
30865
30945
  // Pass runtime so actions can be applied
30866
30946
  });
30947
+ if (canvas.setOverrideFetcher) {
30948
+ const originalSetOverrideFetcher = canvas.setOverrideFetcher.bind(canvas);
30949
+ canvas.setOverrideFetcher = (newFetcher) => {
30950
+ const wrappedFetcher = async () => {
30951
+ const config = await newFetcher();
30952
+ const configForLoader = config;
30953
+ const requiredApps = appLoader.getRequiredApps(configForLoader);
30954
+ if (requiredApps.length > 0) {
30955
+ const results = await appLoader.loadAppsForConfig(configForLoader);
30956
+ for (const result of results) {
30957
+ if (result.success && runtime5.apps.has(result.appId)) {
30958
+ try {
30959
+ await runtime5.apps.activate(result.appId);
30960
+ } catch {
30961
+ }
30962
+ }
30963
+ }
30964
+ }
30965
+ return config;
30966
+ };
30967
+ originalSetOverrideFetcher(wrappedFetcher);
30968
+ };
30969
+ }
30867
30970
  return { canvas, runtime: runtime5, experiments, telemetry, sessionMetrics, appLoader };
30868
30971
  }
30869
30972
 
@@ -31303,7 +31406,7 @@ ${cssRules}
31303
31406
  }
31304
31407
 
31305
31408
  // src/index.ts
31306
- var RUNTIME_SDK_BUILD = true ? `${"2026-04-27T21:12:51.466Z"} (${"2ae6c4be4f5"})` : "dev";
31409
+ var RUNTIME_SDK_BUILD = true ? `${"2026-04-28T01:38:03.462Z"} (${"0aa655e94e5"})` : "dev";
31307
31410
  if (typeof window !== "undefined") {
31308
31411
  console.log(`[Syntro Runtime] Build: ${RUNTIME_SDK_BUILD} (Lit)`);
31309
31412
  const existing = window.SynOS;