@syntrologie/runtime-sdk 2.8.0-canary.55 → 2.8.0-canary.56

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/CAPABILITIES.md CHANGED
@@ -331,29 +331,46 @@ Collapsible Q&A accordion with actions, rich content, feedback, and personalizat
331
331
 
332
332
  | Goal | Action |
333
333
  |------|--------|
334
- | Mount an FAQ accordion widget | `core:mountWidget` with `widget: "adaptive-faq:accordion"` |
334
+ | Add an FAQ accordion widget | Add a **tile** in `tiles[]` with `widget: "adaptive-faq:accordion"` |
335
335
  | Scroll to and expand a specific FAQ item | `faq:scroll_to` |
336
336
  | Open, close, or toggle a FAQ item | `faq:toggle_item` |
337
337
  | Add, remove, reorder, or replace FAQ items | `faq:update` |
338
338
 
339
- ## Actions
339
+ ## Mounting an FAQ Widget
340
+
341
+ FAQ widgets are mounted via **tiles** (not actions). Add an entry to the `tiles[]` array in the config:
340
342
 
341
- ### core:mountWidget (FAQ)
343
+ ```json
344
+ {
345
+ "tiles": [
346
+ {
347
+ "id": "my-faq",
348
+ "title": "Frequently Asked Questions",
349
+ "content": {
350
+ "type": "custom",
351
+ "component": "adaptive-faq:accordion",
352
+ "props": {
353
+ "expandBehavior": "single",
354
+ "searchable": true,
355
+ "items": [ ... ]
356
+ }
357
+ }
358
+ }
359
+ ]
360
+ }
361
+ ```
342
362
 
343
- Mounts an FAQ accordion widget to a surface slot.
363
+ ### Tile Props
344
364
 
345
365
  | Property | Type | Required | Description |
346
366
  | ----------------------- | --------------------------------- | -------- | ------------------------------------------------------------------- |
347
- | `kind` | `"core:mountWidget"` | Yes | Action type |
348
- | `slot` | string | Yes | Target slot (e.g., `"drawer_right"`, `"overlay_center"`) |
349
- | `config.title` | string | No | Widget title |
350
- | `config.expandBehavior` | `"single"` \| `"multiple"` | No | Whether one or many items can be open at once (default: `"single"`) |
351
- | `config.searchable` | boolean | No | Show a search/filter input (default: `false`) |
352
- | `config.theme` | `"light"` \| `"dark"` \| `"auto"` | No | Color theme (default: `"auto"`) |
353
- | `config.items` | array | Yes | FAQ items (see below) |
354
- | `config.feedback` | boolean \| FeedbackConfig | No | Enable per-item feedback widget |
355
- | `config.ordering` | OrderingStrategy | No | Item ordering strategy (default: `"static"`) |
356
- | `config.injections` | InjectionRule[] | No | Dynamic item injection rules |
367
+ | `expandBehavior` | `"single"` \| `"multiple"` | No | Whether one or many items can be open at once (default: `"single"`) |
368
+ | `searchable` | boolean | No | Show a search/filter input (default: `false`) |
369
+ | `theme` | `"light"` \| `"dark"` \| `"auto"` | No | Color theme (default: `"auto"`) |
370
+ | `items` | array | Yes | FAQ items (see below) |
371
+ | `feedback` | boolean \| FeedbackConfig | No | Enable per-item feedback widget |
372
+ | `ordering` | OrderingStrategy | No | Item ordering strategy (default: `"static"`) |
373
+ | `injections` | InjectionRule[] | No | Dynamic item injection rules |
357
374
 
358
375
  ### FAQ Item Schema
359
376
 
@@ -370,53 +387,51 @@ Each item in the `items` array:
370
387
  | `config.answerStrategy` | AnswerStrategy | No | AI-generated answer configuration |
371
388
  | `triggerWhen` | DecisionStrategy \| null | No | Conditional visibility strategy |
372
389
 
390
+ **Full tile example with FAQ items:**
391
+
373
392
  ```json
374
393
  {
375
- "kind": "core:mountWidget",
376
- "slot": "drawer_right",
377
- "config": {
378
- "title": "Frequently Asked Questions",
379
- "expandBehavior": "single",
380
- "searchable": true,
381
- "theme": "auto",
382
- "feedback": {
383
- "style": "thumbs",
384
- "prompt": "Was this helpful?"
385
- },
386
- "ordering": "priority",
387
- "items": [
388
- {
389
- "kind": "faq:question",
390
- "config": {
391
- "id": "getting-started",
392
- "question": "How do I get started?",
393
- "answer": "Sign up for a free account and follow our quickstart guide.",
394
- "category": "General",
395
- "priority": 10
396
- }
397
- },
398
- {
399
- "kind": "faq:question",
400
- "config": {
401
- "id": "payment-methods",
402
- "question": "What payment methods do you accept?",
403
- "answer": "We accept all major credit cards and PayPal.",
404
- "category": "Billing",
405
- "priority": 5
406
- },
407
- "triggerWhen": {
408
- "type": "rules",
409
- "rules": [
394
+ "tiles": [
395
+ {
396
+ "id": "help-faq",
397
+ "title": "Frequently Asked Questions",
398
+ "content": {
399
+ "type": "custom",
400
+ "component": "adaptive-faq:accordion",
401
+ "props": {
402
+ "expandBehavior": "single",
403
+ "searchable": true,
404
+ "feedback": {
405
+ "style": "thumbs",
406
+ "prompt": "Was this helpful?"
407
+ },
408
+ "ordering": "priority",
409
+ "items": [
410
410
  {
411
- "conditions": [{ "type": "page_url", "pattern": "/pricing*" }],
412
- "value": true
411
+ "kind": "faq:question",
412
+ "config": {
413
+ "id": "getting-started",
414
+ "question": "How do I get started?",
415
+ "answer": "Sign up for a free account and follow our quickstart guide.",
416
+ "category": "General",
417
+ "priority": 10
418
+ }
419
+ },
420
+ {
421
+ "kind": "faq:question",
422
+ "config": {
423
+ "id": "payment-methods",
424
+ "question": "What payment methods do you accept?",
425
+ "answer": "We accept all major credit cards and PayPal.",
426
+ "category": "Billing",
427
+ "priority": 5
428
+ }
413
429
  }
414
- ],
415
- "default": false
430
+ ]
416
431
  }
417
432
  }
418
- ]
419
- }
433
+ }
434
+ ]
420
435
  }
421
436
  ```
422
437
 
@@ -698,7 +713,7 @@ Gamification capabilities including badges, points, and leaderboards.
698
713
  |------|--------|
699
714
  | Award a badge to a user | `gamification:awardBadge` |
700
715
  | Add points to a user's score | `gamification:addPoints` |
701
- | Mount a gamification widget (leaderboard, progress) | `core:mountWidget` |
716
+ | Mount a gamification widget (leaderboard, progress) | Add a **tile** with `component: "adaptive-gamification:leaderboard"` |
702
717
 
703
718
  ## Actions
704
719
 
@@ -734,15 +749,25 @@ Adds points to the current user's score.
734
749
  }
735
750
  ```
736
751
 
737
- ### core:mountWidget (Gamification)
752
+ ### Gamification Widget (via Tile)
738
753
 
739
- Mounts gamification UI elements (leaderboard, badge display, progress tracker).
754
+ Mount gamification UI elements (leaderboard, badge display, progress tracker) via a **tile**:
740
755
 
741
- | Property | Type | Required | Description |
742
- | -------- | ---------------------- | -------- | -------------------------- |
743
- | `kind` | `"core:mountWidget"` | Yes | Action type |
744
- | `slot` | string | Yes | Target slot |
745
- | `config` | object | Yes | Gamification configuration |
756
+ ```json
757
+ {
758
+ "tiles": [
759
+ {
760
+ "id": "gamification",
761
+ "title": "Your Progress",
762
+ "content": {
763
+ "type": "custom",
764
+ "component": "adaptive-gamification:leaderboard",
765
+ "props": { ... }
766
+ }
767
+ }
768
+ ]
769
+ }
770
+ ```
746
771
 
747
772
  ### Configuration Schema
748
773
 
@@ -767,9 +792,13 @@ Mounts gamification UI elements (leaderboard, badge display, progress tracker).
767
792
 
768
793
  ```json
769
794
  {
770
- "kind": "core:mountWidget",
771
- "slot": "overlay_corner_br",
772
- "config": {
795
+ "tiles": [{
796
+ "id": "gamification-widget",
797
+ "title": "Achievements",
798
+ "content": {
799
+ "type": "custom",
800
+ "component": "adaptive-gamification:leaderboard",
801
+ "props": {
773
802
  "badges": [
774
803
  {
775
804
  "id": "first-purchase",
@@ -813,7 +842,7 @@ Navigation tips accordion widget with conditional item visibility and toast noti
813
842
  |------|--------|
814
843
  | Scroll to an element on the page | `navigation:scrollTo` |
815
844
  | Navigate to a different URL | `navigation:navigate` |
816
- | Show contextual navigation tips widget | `core:mountWidget` with `widget: "adaptive-nav:tips"` |
845
+ | Show contextual navigation tips widget | Add a **tile** with `component: "adaptive-nav:tips"` |
817
846
 
818
847
  ## Widget: `adaptive-nav:tips`
819
848
 
@@ -18,6 +18,7 @@
18
18
  */
19
19
  import type { appRegistry } from './apps';
20
20
  import type { SmartCanvasRuntime } from './runtime';
21
+ import type { InterventionTracker } from './telemetry/InterventionTracker';
21
22
  import { decodeToken, encodeToken } from './token';
22
23
  export { collectBrowserMetadata, fetchGeo } from './bootstrap-init';
23
24
  export type { SyntroInitOptions, SyntroInitResult } from './bootstrap-types';
@@ -63,6 +64,7 @@ declare global {
63
64
  appRegistry: typeof appRegistry;
64
65
  runtime: SmartCanvasRuntime;
65
66
  version: string;
67
+ interventionTracker?: InterventionTracker;
66
68
  };
67
69
  }
68
70
  }
@@ -3456,7 +3456,7 @@ function getAntiFlickerSnippet(config = {}) {
3456
3456
  }
3457
3457
 
3458
3458
  // src/version.ts
3459
- var SDK_VERSION = "2.8.0-canary.55";
3459
+ var SDK_VERSION = "2.8.0-canary.56";
3460
3460
 
3461
3461
  // src/types.ts
3462
3462
  var SDK_SCHEMA_VERSION = "2.0";
@@ -5075,6 +5075,19 @@ function WidgetMount({ widgetId, props }) {
5075
5075
  }
5076
5076
  return /* @__PURE__ */ jsx6("div", { ref: parentRef });
5077
5077
  }
5078
+ var INTERACTION_PATTERNS = [
5079
+ ":toggled",
5080
+ ":clicked",
5081
+ ":feedback",
5082
+ ":navigate",
5083
+ ":expanded",
5084
+ ":collapsed",
5085
+ ":dismissed",
5086
+ ":submitted",
5087
+ ":interacted",
5088
+ ":tip_clicked",
5089
+ ":tip_focused"
5090
+ ];
5078
5091
  function TileCard({
5079
5092
  config,
5080
5093
  surface: _surface,
@@ -5083,10 +5096,42 @@ function TileCard({
5083
5096
  }) {
5084
5097
  const { title, subtitle, widget, props, icon } = config;
5085
5098
  const [, setTick] = useState4(0);
5099
+ const articleRef = useRef4(null);
5086
5100
  const runtime3 = useRuntime();
5087
5101
  useEffect5(() => {
5088
5102
  if (runtime3) setTick((t) => t + 1);
5089
5103
  }, [runtime3]);
5104
+ useEffect5(() => {
5105
+ var _a2;
5106
+ const tracker = typeof window !== "undefined" ? (_a2 = window.SynOS) == null ? void 0 : _a2.interventionTracker : null;
5107
+ if (!articleRef.current || !tracker) return;
5108
+ const observer = new IntersectionObserver(
5109
+ ([entry]) => {
5110
+ var _a3;
5111
+ if (entry.isIntersecting) {
5112
+ tracker.trackSeen(config.id, (_a3 = config.widget) != null ? _a3 : "unknown");
5113
+ observer.disconnect();
5114
+ }
5115
+ },
5116
+ { threshold: 0.5 }
5117
+ );
5118
+ observer.observe(articleRef.current);
5119
+ return () => observer.disconnect();
5120
+ }, [config.id, config.widget]);
5121
+ useEffect5(() => {
5122
+ var _a2;
5123
+ const tracker = typeof window !== "undefined" ? (_a2 = window.SynOS) == null ? void 0 : _a2.interventionTracker : null;
5124
+ if (!(runtime3 == null ? void 0 : runtime3.events) || !tracker) return;
5125
+ return runtime3.events.subscribe((event) => {
5126
+ var _a3, _b;
5127
+ if (!INTERACTION_PATTERNS.some((p) => {
5128
+ var _a4;
5129
+ return (_a4 = event.name) == null ? void 0 : _a4.includes(p);
5130
+ })) return;
5131
+ if (((_a3 = event.props) == null ? void 0 : _a3.instanceId) !== config.id) return;
5132
+ tracker.trackInteracted(config.id, (_b = config.widget) != null ? _b : "unknown", event.name);
5133
+ });
5134
+ }, [runtime3 == null ? void 0 : runtime3.events, config.id, config.widget]);
5090
5135
  const registration = useMemo3(
5091
5136
  () => {
5092
5137
  var _a2, _b;
@@ -5140,6 +5185,7 @@ function TileCard({
5140
5185
  return /* @__PURE__ */ jsxs2(
5141
5186
  "article",
5142
5187
  {
5188
+ ref: articleRef,
5143
5189
  "data-shadow-canvas-id": `tile-${config.id}`,
5144
5190
  style: cardStyle,
5145
5191
  onMouseEnter,
@@ -6162,6 +6208,14 @@ var sortTiles = (tiles) => [...tiles].sort((a, b) => {
6162
6208
  var _a2, _b;
6163
6209
  return ((_a2 = b.priority) != null ? _a2 : 0) - ((_b = a.priority) != null ? _b : 0);
6164
6210
  });
6211
+ function fireTriggeredForTiles(tiles) {
6212
+ var _a2, _b;
6213
+ const tracker = typeof window !== "undefined" ? (_a2 = window.SynOS) == null ? void 0 : _a2.interventionTracker : null;
6214
+ if (!tracker) return;
6215
+ for (const tile of tiles) {
6216
+ tracker.trackTriggered(tile.id, (_b = tile.widget) != null ? _b : "unknown");
6217
+ }
6218
+ }
6165
6219
  function useShadowCanvasConfig({
6166
6220
  fetcher,
6167
6221
  experiments,
@@ -6184,6 +6238,7 @@ function useShadowCanvasConfig({
6184
6238
  if (experiments) {
6185
6239
  tiles = tiles.filter((tile) => experiments.shouldRenderRectangle(tile));
6186
6240
  }
6241
+ fireTriggeredForTiles(tiles);
6187
6242
  setState((prev) => ({ ...prev, tiles: sortTiles(tiles) }));
6188
6243
  }, [runtime3, experiments]);
6189
6244
  const load = useCallback5(async () => {
@@ -6201,6 +6256,7 @@ function useShadowCanvasConfig({
6201
6256
  } else if (experiments) {
6202
6257
  tiles = tiles.filter((tile) => experiments.shouldRenderRectangle(tile));
6203
6258
  }
6259
+ fireTriggeredForTiles(tiles);
6204
6260
  debug("SmartCanvas Config", `Tile count after filtering: ${tiles.length}`);
6205
6261
  const newActions = response.actions || [];
6206
6262
  const newActionsJson = JSON.stringify(newActions);
@@ -7851,6 +7907,59 @@ function createPostHogClient(options = {}) {
7851
7907
  return new PostHogAdapter(options);
7852
7908
  }
7853
7909
 
7910
+ // src/telemetry/InterventionTracker.ts
7911
+ var InterventionTracker = class {
7912
+ constructor(telemetry, variantId) {
7913
+ __publicField(this, "telemetry");
7914
+ __publicField(this, "variantId");
7915
+ __publicField(this, "seenSet", /* @__PURE__ */ new Set());
7916
+ __publicField(this, "triggeredSet", /* @__PURE__ */ new Set());
7917
+ this.telemetry = telemetry;
7918
+ this.variantId = variantId;
7919
+ }
7920
+ trackServed(tiles, actions) {
7921
+ var _a2, _b;
7922
+ (_b = (_a2 = this.telemetry).track) == null ? void 0 : _b.call(_a2, "syntro_config_served", {
7923
+ variant_id: this.variantId,
7924
+ tiles,
7925
+ actions
7926
+ });
7927
+ }
7928
+ trackSeen(interventionId, interventionKind) {
7929
+ var _a2, _b;
7930
+ if (this.seenSet.has(interventionId)) return;
7931
+ this.seenSet.add(interventionId);
7932
+ (_b = (_a2 = this.telemetry).track) == null ? void 0 : _b.call(_a2, "syntro_intervention_seen", {
7933
+ variant_id: this.variantId,
7934
+ intervention_id: interventionId,
7935
+ intervention_kind: interventionKind
7936
+ });
7937
+ }
7938
+ trackTriggered(interventionId, interventionKind) {
7939
+ var _a2, _b;
7940
+ if (this.triggeredSet.has(interventionId)) return;
7941
+ this.triggeredSet.add(interventionId);
7942
+ (_b = (_a2 = this.telemetry).track) == null ? void 0 : _b.call(_a2, "syntro_intervention_triggered", {
7943
+ variant_id: this.variantId,
7944
+ intervention_id: interventionId,
7945
+ intervention_kind: interventionKind
7946
+ });
7947
+ }
7948
+ trackInteracted(interventionId, interventionKind, interactionType) {
7949
+ var _a2, _b;
7950
+ (_b = (_a2 = this.telemetry).track) == null ? void 0 : _b.call(_a2, "syntro_intervention_interacted", {
7951
+ variant_id: this.variantId,
7952
+ intervention_id: interventionId,
7953
+ intervention_kind: interventionKind,
7954
+ interaction_type: interactionType
7955
+ });
7956
+ }
7957
+ resetPage() {
7958
+ this.seenSet.clear();
7959
+ this.triggeredSet.clear();
7960
+ }
7961
+ };
7962
+
7854
7963
  // src/actions/executors/core-flow.ts
7855
7964
  var executeSequence = async (action, context) => {
7856
7965
  const handles = [];
@@ -12037,15 +12146,35 @@ async function _initCore(options) {
12037
12146
  }
12038
12147
  const warnedAppFailures = /* @__PURE__ */ new Set();
12039
12148
  const appLoadingFetcher = baseFetcher ? async () => {
12040
- var _a3, _b2, _c2, _d2, _e2, _f2, _g2;
12149
+ var _a3, _b2, _c2, _d2, _e2, _f2, _g2, _h, _i, _j, _k, _l;
12041
12150
  const config = await baseFetcher();
12151
+ const tileCount = (_b2 = (_a3 = config.tiles) == null ? void 0 : _a3.length) != null ? _b2 : 0;
12152
+ const actionCount = (_d2 = (_c2 = config.actions) == null ? void 0 : _c2.length) != null ? _d2 : 0;
12153
+ const variantId = (_e2 = config.meta) == null ? void 0 : _e2.variant_id;
12154
+ if (tileCount > 0 || actionCount > 0) {
12155
+ if (!variantId) {
12156
+ console.warn(
12157
+ "[Syntro] Config has content but no meta.variant_id \u2014 intervention tracking disabled"
12158
+ );
12159
+ }
12160
+ }
12161
+ if (telemetry && variantId) {
12162
+ const tracker = new InterventionTracker(telemetry, variantId);
12163
+ tracker.trackServed(tileCount, actionCount);
12164
+ if (typeof window !== "undefined") {
12165
+ window.SynOS.interventionTracker = tracker;
12166
+ }
12167
+ runtime3.navigation.subscribe(() => {
12168
+ tracker.resetPage();
12169
+ });
12170
+ }
12042
12171
  console.log(
12043
12172
  "[Syntro Bootstrap] Config fetched:",
12044
- `tiles=${(_b2 = (_a3 = config.tiles) == null ? void 0 : _a3.length) != null ? _b2 : 0},`,
12045
- `actions=${(_d2 = (_c2 = config.actions) == null ? void 0 : _c2.length) != null ? _d2 : 0},`,
12046
- `theme=${(_f2 = (_e2 = config.theme) == null ? void 0 : _e2.name) != null ? _f2 : "none"}`
12173
+ `tiles=${(_g2 = (_f2 = config.tiles) == null ? void 0 : _f2.length) != null ? _g2 : 0},`,
12174
+ `actions=${(_i = (_h = config.actions) == null ? void 0 : _h.length) != null ? _i : 0},`,
12175
+ `theme=${(_k = (_j = config.theme) == null ? void 0 : _j.name) != null ? _k : "none"}`
12047
12176
  );
12048
- if (((_g2 = config.actions) == null ? void 0 : _g2.length) > 0) {
12177
+ if (((_l = config.actions) == null ? void 0 : _l.length) > 0) {
12049
12178
  console.log(
12050
12179
  "[Syntro Bootstrap] Actions in config:",
12051
12180
  config.actions.map(
@@ -12200,6 +12329,7 @@ export {
12200
12329
  createSessionMetricTracker,
12201
12330
  createNoopClient,
12202
12331
  createPostHogClient,
12332
+ InterventionTracker,
12203
12333
  ExecutorRegistry,
12204
12334
  executorRegistry,
12205
12335
  getExecutor,
@@ -12246,4 +12376,4 @@ export {
12246
12376
  encodeToken,
12247
12377
  Syntro
12248
12378
  };
12249
- //# sourceMappingURL=chunk-EBCVWFH5.js.map
12379
+ //# sourceMappingURL=chunk-B5CPUVJB.js.map