@syntrologie/runtime-sdk 2.8.0-canary.183 → 2.8.0-canary.185
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/dist/defaults/clickIds.d.ts +14 -0
- package/dist/defaults/identifyOnEmail.d.ts +23 -0
- package/dist/defaults/identifyOnUrlParam.d.ts +29 -0
- package/dist/defaults/index.d.ts +51 -0
- package/dist/defaults/initialProperties.d.ts +26 -0
- package/dist/defaults/jsonLd.d.ts +17 -0
- package/dist/index.js +729 -1
- package/dist/index.js.map +4 -4
- package/dist/smart-canvas.esm.js +100 -100
- package/dist/smart-canvas.esm.js.map +4 -4
- package/dist/smart-canvas.js +711 -2
- package/dist/smart-canvas.js.map +4 -4
- package/dist/smart-canvas.min.js +101 -101
- package/dist/smart-canvas.min.js.map +4 -4
- package/dist/telemetry/adapters/posthog.d.ts +26 -0
- package/dist/telemetry/consent/ConsentDetector.d.ts +26 -0
- package/dist/telemetry/{consent.d.ts → consent/ConsentGate.d.ts} +33 -1
- package/dist/telemetry/consent/adapters/gtmConsentMode.d.ts +18 -0
- package/dist/telemetry/consent/adapters/iabTcf.d.ts +21 -0
- package/dist/telemetry/consent/adapters/shopify.d.ts +17 -0
- package/dist/telemetry/consent/index.d.ts +17 -0
- package/dist/telemetry/consent/types.d.ts +47 -0
- package/dist/telemetry/types.d.ts +24 -0
- package/dist/version.d.ts +1 -1
- package/package.json +3 -5
- package/schema/canvas-config.schema.json +352 -1
- package/CAPABILITIES.md +0 -1450
package/CAPABILITIES.md
DELETED
|
@@ -1,1450 +0,0 @@
|
|
|
1
|
-
# SmartCanvas SDK Capabilities
|
|
2
|
-
|
|
3
|
-
This document describes all available operations and capabilities of the SmartCanvas SDK for DOM manipulation, interventions, and UI rendering.
|
|
4
|
-
|
|
5
|
-
> **Auto-generated**: This file is assembled from individual adaptive package capabilities during build.
|
|
6
|
-
|
|
7
|
-
## Table of Contents
|
|
8
|
-
- [Overview](#overview)
|
|
9
|
-
- [Quick Start Example](#quick-start-example)
|
|
10
|
-
- [Adaptive Packages](#adaptive-packages)
|
|
11
|
-
- [Surfaces](#surfaces)
|
|
12
|
-
- [Anchor Resolution](#anchor-resolution)
|
|
13
|
-
- [Route Scoping](#route-scoping)
|
|
14
|
-
- [Decision Strategies](#decision-strategies)
|
|
15
|
-
- [Best Practices](#best-practices)
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## Overview
|
|
20
|
-
|
|
21
|
-
The SmartCanvas SDK provides three main systems for modifying web pages:
|
|
22
|
-
|
|
23
|
-
1. **ActionEngine** - Unified execution layer for interventions (highlight, tooltip, badge, DOM modifications)
|
|
24
|
-
2. **Surfaces** - Managed surface system for rendering UI into named slots
|
|
25
|
-
3. **Runtime** - Context, events, state, and decision management
|
|
26
|
-
|
|
27
|
-
All modifications are reversible and publish events to the EventBus for tracking.
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
## Quick Start Example
|
|
32
|
-
|
|
33
|
-
### Simple Header Text Change
|
|
34
|
-
|
|
35
|
-
Change a page header when the element is visible:
|
|
36
|
-
|
|
37
|
-
```json
|
|
38
|
-
{
|
|
39
|
-
"id": "welcome-header-change",
|
|
40
|
-
"activation": {
|
|
41
|
-
"routes": { "include": ["/", "/home"] },
|
|
42
|
-
"strategy": {
|
|
43
|
-
"type": "rules",
|
|
44
|
-
"rules": [
|
|
45
|
-
{
|
|
46
|
-
"conditions": [
|
|
47
|
-
{ "type": "anchor_visible", "anchorId": "h1.hero-title", "state": "visible" }
|
|
48
|
-
],
|
|
49
|
-
"value": true
|
|
50
|
-
}
|
|
51
|
-
],
|
|
52
|
-
"default": false
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
"actions": [
|
|
56
|
-
{
|
|
57
|
-
"kind": "content:setText",
|
|
58
|
-
"anchorId": { "selector": "h1.hero-title", "route": "/" },
|
|
59
|
-
"text": "Welcome to Our New Experience"
|
|
60
|
-
}
|
|
61
|
-
]
|
|
62
|
-
}
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
---
|
|
66
|
-
|
|
67
|
-
## Adaptive Packages
|
|
68
|
-
|
|
69
|
-
The SDK includes the following adaptive packages, each providing specific capabilities:
|
|
70
|
-
|
|
71
|
-
- [@syntrologie/adapt-content](#syntrologieadapt-content)
|
|
72
|
-
- [@syntrologie/adapt-faq](#syntrologieadapt-faq)
|
|
73
|
-
- [@syntrologie/adapt-gamification](#syntrologieadapt-gamification)
|
|
74
|
-
- [@syntrologie/adapt-mcp](#syntrologieadapt-mcp)
|
|
75
|
-
- [@syntrologie/adapt-nav](#syntrologieadapt-nav)
|
|
76
|
-
- [@syntrologie/adapt-overlays](#syntrologieadapt-overlays)
|
|
77
|
-
- [@syntrologie/adapt-viz](#syntrologieadapt-viz)
|
|
78
|
-
|
|
79
|
-
---
|
|
80
|
-
|
|
81
|
-
# @syntrologie/adapt-content
|
|
82
|
-
|
|
83
|
-
DOM content modification capabilities for text, attributes, styles, HTML, and classes.
|
|
84
|
-
|
|
85
|
-
## When to use
|
|
86
|
-
|
|
87
|
-
| Goal | Action |
|
|
88
|
-
|------|--------|
|
|
89
|
-
| Replace an element's text | `content:setText` |
|
|
90
|
-
| Change an HTML attribute (href, src, data-*) | `content:setAttr` |
|
|
91
|
-
| Modify inline styles (color, size, spacing) | `content:setStyle` |
|
|
92
|
-
| Inject new HTML before/after/inside an element | `content:insertHtml` |
|
|
93
|
-
| Add a CSS class (show/hide, animate) | `content:addClass` |
|
|
94
|
-
| Remove a CSS class | `content:removeClass` |
|
|
95
|
-
|
|
96
|
-
## Actions
|
|
97
|
-
|
|
98
|
-
### content:setText
|
|
99
|
-
|
|
100
|
-
Replaces the text content of an element.
|
|
101
|
-
|
|
102
|
-
| Property | Type | Required | Description |
|
|
103
|
-
| ---------- | ------------------- | -------- | ---------------- |
|
|
104
|
-
| `kind` | `"content:setText"` | Yes | Action type |
|
|
105
|
-
| `anchorId` | object | Yes | `{ selector, route }` |
|
|
106
|
-
| `text` | string | Yes | New text content |
|
|
107
|
-
|
|
108
|
-
```json
|
|
109
|
-
{
|
|
110
|
-
"kind": "content:setText",
|
|
111
|
-
"anchorId": { "selector": "h1.hero-title", "route": "/" },
|
|
112
|
-
"text": "Start Your Free Trial Today"
|
|
113
|
-
}
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
### content:setAttr
|
|
117
|
-
|
|
118
|
-
Sets an HTML attribute on an element.
|
|
119
|
-
|
|
120
|
-
| Property | Type | Required | Description |
|
|
121
|
-
| ---------- | ------------------- | -------- | ---------------- |
|
|
122
|
-
| `kind` | `"content:setAttr"` | Yes | Action type |
|
|
123
|
-
| `anchorId` | object | Yes | `{ selector, route }` |
|
|
124
|
-
| `attr` | string | Yes | Attribute name |
|
|
125
|
-
| `value` | string | Yes | Attribute value |
|
|
126
|
-
|
|
127
|
-
**Blocked attributes:** Event handlers (`onclick`, `onerror`, etc.) are not allowed.
|
|
128
|
-
|
|
129
|
-
```json
|
|
130
|
-
{
|
|
131
|
-
"kind": "content:setAttr",
|
|
132
|
-
"anchorId": { "selector": "#signup-form", "route": "/" },
|
|
133
|
-
"attr": "data-experiment",
|
|
134
|
-
"value": "signup-v2"
|
|
135
|
-
}
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### content:setStyle
|
|
139
|
-
|
|
140
|
-
Sets inline CSS styles on an element.
|
|
141
|
-
|
|
142
|
-
| Property | Type | Required | Description |
|
|
143
|
-
| ---------- | -------------------- | -------- | ------------------------ |
|
|
144
|
-
| `kind` | `"content:setStyle"` | Yes | Action type |
|
|
145
|
-
| `anchorId` | object | Yes | `{ selector, route }` |
|
|
146
|
-
| `styles` | object | Yes | CSS property/value pairs |
|
|
147
|
-
|
|
148
|
-
```json
|
|
149
|
-
{
|
|
150
|
-
"kind": "content:setStyle",
|
|
151
|
-
"anchorId": { "selector": ".hero-section", "route": "/" },
|
|
152
|
-
"styles": {
|
|
153
|
-
"background-color": "#1e40af",
|
|
154
|
-
"padding": "2rem"
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
### content:insertHtml
|
|
160
|
-
|
|
161
|
-
Inserts HTML content relative to an element.
|
|
162
|
-
|
|
163
|
-
| Property | Type | Required | Description |
|
|
164
|
-
| ---------- | --------------------- | -------- | ----------------------------------------------------------- |
|
|
165
|
-
| `kind` | `"content:insertHtml"` | Yes | Action type |
|
|
166
|
-
| `anchorId` | object | Yes | `{ selector, route }` |
|
|
167
|
-
| `html` | string | Yes | HTML content (sanitized) |
|
|
168
|
-
| `position` | string | Yes | `"before"`, `"after"`, `"prepend"`, `"append"`, `"replace"` |
|
|
169
|
-
| `deepLink` | object | No | Makes the entire inserted element clickable to open the canvas panel and navigate to a specific tile |
|
|
170
|
-
|
|
171
|
-
**Positions:**
|
|
172
|
-
|
|
173
|
-
- `before` - Insert before the element
|
|
174
|
-
- `after` - Insert after the element
|
|
175
|
-
- `prepend` - Insert inside, before first child
|
|
176
|
-
- `append` - Insert inside, after last child
|
|
177
|
-
- `replace` - Replace the entire element
|
|
178
|
-
|
|
179
|
-
```json
|
|
180
|
-
{
|
|
181
|
-
"kind": "content:insertHtml",
|
|
182
|
-
"anchorId": { "selector": ".cta-button", "route": "/" },
|
|
183
|
-
"html": "<span class=\"badge\">NEW</span>",
|
|
184
|
-
"position": "append"
|
|
185
|
-
}
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
**Deep-linking to canvas tiles:**
|
|
189
|
-
|
|
190
|
-
Use `deepLink` to make inserted content open the canvas panel and navigate to a specific tile when clicked. This is the correct way to connect inserted buttons/links to canvas tiles — do NOT use `onclick` handlers with `window.SynOS`.
|
|
191
|
-
|
|
192
|
-
| Property | Type | Required | Description |
|
|
193
|
-
| ---------------- | ------ | -------- | ---------------------------------- |
|
|
194
|
-
| `deepLink.tileId` | string | Yes | ID of the tile to open |
|
|
195
|
-
| `deepLink.itemId` | string | No | Specific item within the tile |
|
|
196
|
-
|
|
197
|
-
```json
|
|
198
|
-
{
|
|
199
|
-
"kind": "content:insertHtml",
|
|
200
|
-
"anchorId": { "selector": "[data-id='pricing-heading']", "route": "/" },
|
|
201
|
-
"html": "<button style='background: #4a90e2; color: white; border: none; padding: 8px 16px; border-radius: 20px; cursor: pointer;'>Help Me Choose</button>",
|
|
202
|
-
"position": "after",
|
|
203
|
-
"deepLink": { "tileId": "plan_selector_faq" }
|
|
204
|
-
}
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
The SDK automatically handles opening the canvas, setting the cursor to pointer, and publishing a `notification.deep_link` event. The click handler is wired up and cleaned up by the SDK — no JavaScript in the HTML is needed.
|
|
208
|
-
|
|
209
|
-
### content:addClass
|
|
210
|
-
|
|
211
|
-
Adds a CSS class to an element.
|
|
212
|
-
|
|
213
|
-
| Property | Type | Required | Description |
|
|
214
|
-
| ----------- | -------------------- | -------- | ----------------- |
|
|
215
|
-
| `kind` | `"content:addClass"` | Yes | Action type |
|
|
216
|
-
| `anchorId` | object | Yes | `{ selector, route }` |
|
|
217
|
-
| `className` | string | Yes | Class name to add |
|
|
218
|
-
|
|
219
|
-
```json
|
|
220
|
-
{
|
|
221
|
-
"kind": "content:addClass",
|
|
222
|
-
"anchorId": { "selector": ".pricing-card", "route": "/pricing" },
|
|
223
|
-
"className": "highlighted"
|
|
224
|
-
}
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
### content:removeClass
|
|
228
|
-
|
|
229
|
-
Removes a CSS class from an element.
|
|
230
|
-
|
|
231
|
-
| Property | Type | Required | Description |
|
|
232
|
-
| ----------- | ----------------------- | -------- | -------------------- |
|
|
233
|
-
| `kind` | `"content:removeClass"` | Yes | Action type |
|
|
234
|
-
| `anchorId` | object | Yes | `{ selector, route }` |
|
|
235
|
-
| `className` | string | Yes | Class name to remove |
|
|
236
|
-
|
|
237
|
-
```json
|
|
238
|
-
{
|
|
239
|
-
"kind": "content:removeClass",
|
|
240
|
-
"anchorId": { "selector": ".pricing-card", "route": "/pricing" },
|
|
241
|
-
"className": "hidden"
|
|
242
|
-
}
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
---
|
|
247
|
-
|
|
248
|
-
# @syntrologie/adapt-faq
|
|
249
|
-
|
|
250
|
-
Collapsible Q&A accordion with actions, rich content, feedback, and personalization.
|
|
251
|
-
|
|
252
|
-
## When to use
|
|
253
|
-
|
|
254
|
-
| Goal | Action |
|
|
255
|
-
|------|--------|
|
|
256
|
-
| Add an FAQ accordion widget | Add a **tile** in `tiles[]` with `widget: "adaptive-faq:accordion"` |
|
|
257
|
-
| Scroll to and expand a specific FAQ item | `faq:scroll_to` |
|
|
258
|
-
| Open, close, or toggle a FAQ item | `faq:toggle_item` |
|
|
259
|
-
| Add, remove, reorder, or replace FAQ items | `faq:update` |
|
|
260
|
-
|
|
261
|
-
## Mounting an FAQ Widget
|
|
262
|
-
|
|
263
|
-
FAQ widgets are mounted via **tiles** (not actions). Add an entry to the `tiles[]` array in the config:
|
|
264
|
-
|
|
265
|
-
```json
|
|
266
|
-
{
|
|
267
|
-
"tiles": [
|
|
268
|
-
{
|
|
269
|
-
"id": "my-faq",
|
|
270
|
-
"title": "Frequently Asked Questions",
|
|
271
|
-
"content": {
|
|
272
|
-
"type": "custom",
|
|
273
|
-
"component": "adaptive-faq:accordion",
|
|
274
|
-
"props": {
|
|
275
|
-
"expandBehavior": "single",
|
|
276
|
-
"searchable": true,
|
|
277
|
-
"items": [ ... ]
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
]
|
|
282
|
-
}
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
### Tile Props
|
|
286
|
-
|
|
287
|
-
| Property | Type | Required | Description |
|
|
288
|
-
| ----------------------- | --------------------------------- | -------- | ------------------------------------------------------------------- |
|
|
289
|
-
| `expandBehavior` | `"single"` \| `"multiple"` | No | Whether one or many items can be open at once (default: `"single"`) |
|
|
290
|
-
| `searchable` | boolean | No | Show a search/filter input (default: `false`) |
|
|
291
|
-
| `theme` | `"light"` \| `"dark"` \| `"auto"` | No | Color theme (default: `"auto"`) |
|
|
292
|
-
| `items` | array | Yes | FAQ items (see below) |
|
|
293
|
-
| `feedback` | boolean \| FeedbackConfig | No | Enable per-item feedback widget |
|
|
294
|
-
| `ordering` | OrderingStrategy | No | Item ordering strategy (default: `"static"`) |
|
|
295
|
-
| `injections` | InjectionRule[] | No | Dynamic item injection rules |
|
|
296
|
-
|
|
297
|
-
### FAQ Item Schema
|
|
298
|
-
|
|
299
|
-
Each item in the `items` array:
|
|
300
|
-
|
|
301
|
-
| Property | Type | Required | Description |
|
|
302
|
-
| ----------------------- | ------------------------ | -------- | ----------------------------------------------- |
|
|
303
|
-
| `kind` | `"faq:question"` | Yes | Compositional action type |
|
|
304
|
-
| `config.id` | string | Yes | Unique identifier for this question |
|
|
305
|
-
| `config.question` | string | Yes | The question text |
|
|
306
|
-
| `config.answer` | FAQAnswer | Yes | Answer content (string, rich HTML, or markdown) |
|
|
307
|
-
| `config.category` | string | No | Category for grouping items |
|
|
308
|
-
| `config.priority` | number | No | Priority weight for ordering |
|
|
309
|
-
| `config.answerStrategy` | AnswerStrategy | No | AI-generated answer configuration |
|
|
310
|
-
| `triggerWhen` | DecisionStrategy \| null | No | Conditional visibility strategy |
|
|
311
|
-
|
|
312
|
-
**Full tile example with FAQ items:**
|
|
313
|
-
|
|
314
|
-
```json
|
|
315
|
-
{
|
|
316
|
-
"tiles": [
|
|
317
|
-
{
|
|
318
|
-
"id": "help-faq",
|
|
319
|
-
"title": "Frequently Asked Questions",
|
|
320
|
-
"content": {
|
|
321
|
-
"type": "custom",
|
|
322
|
-
"component": "adaptive-faq:accordion",
|
|
323
|
-
"props": {
|
|
324
|
-
"expandBehavior": "single",
|
|
325
|
-
"searchable": true,
|
|
326
|
-
"feedback": {
|
|
327
|
-
"style": "thumbs",
|
|
328
|
-
"prompt": "Was this helpful?"
|
|
329
|
-
},
|
|
330
|
-
"ordering": "priority",
|
|
331
|
-
"items": [
|
|
332
|
-
{
|
|
333
|
-
"kind": "faq:question",
|
|
334
|
-
"config": {
|
|
335
|
-
"id": "getting-started",
|
|
336
|
-
"question": "How do I get started?",
|
|
337
|
-
"answer": "Sign up for a free account and follow our quickstart guide.",
|
|
338
|
-
"category": "General",
|
|
339
|
-
"priority": 10
|
|
340
|
-
}
|
|
341
|
-
},
|
|
342
|
-
{
|
|
343
|
-
"kind": "faq:question",
|
|
344
|
-
"config": {
|
|
345
|
-
"id": "payment-methods",
|
|
346
|
-
"question": "What payment methods do you accept?",
|
|
347
|
-
"answer": "We accept all major credit cards and PayPal.",
|
|
348
|
-
"category": "Billing",
|
|
349
|
-
"priority": 5
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
]
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
]
|
|
357
|
-
}
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
### faq:scroll_to
|
|
361
|
-
|
|
362
|
-
Scrolls the viewport to a specific FAQ item and optionally expands it.
|
|
363
|
-
|
|
364
|
-
| Property | Type | Required | Default | Description |
|
|
365
|
-
| -------------- | ----------------- | -------- | ---------- | ------------------------------------------ |
|
|
366
|
-
| `kind` | `"faq:scroll_to"` | Yes | | Action type |
|
|
367
|
-
| `itemId` | string | No\* | | Target item ID |
|
|
368
|
-
| `itemQuestion` | string | No\* | | Target item question text (fuzzy match) |
|
|
369
|
-
| `expand` | boolean | No | `true` | Whether to expand the item after scrolling |
|
|
370
|
-
| `behavior` | string | No | `"smooth"` | `"smooth"`, `"instant"`, `"auto"` |
|
|
371
|
-
|
|
372
|
-
\* Either `itemId` or `itemQuestion` is required.
|
|
373
|
-
|
|
374
|
-
```json
|
|
375
|
-
{
|
|
376
|
-
"kind": "faq:scroll_to",
|
|
377
|
-
"itemId": "payment-methods",
|
|
378
|
-
"expand": true,
|
|
379
|
-
"behavior": "smooth"
|
|
380
|
-
}
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
### faq:toggle_item
|
|
384
|
-
|
|
385
|
-
Opens, closes, or toggles a FAQ item's expanded state.
|
|
386
|
-
|
|
387
|
-
| Property | Type | Required | Default | Description |
|
|
388
|
-
| -------------- | ------------------- | -------- | ---------- | --------------------------------------- |
|
|
389
|
-
| `kind` | `"faq:toggle_item"` | Yes | | Action type |
|
|
390
|
-
| `itemId` | string | No\* | | Target item ID |
|
|
391
|
-
| `itemQuestion` | string | No\* | | Target item question text (fuzzy match) |
|
|
392
|
-
| `state` | string | No | `"toggle"` | `"open"`, `"closed"`, `"toggle"` |
|
|
393
|
-
|
|
394
|
-
\* Either `itemId` or `itemQuestion` is required.
|
|
395
|
-
|
|
396
|
-
```json
|
|
397
|
-
{
|
|
398
|
-
"kind": "faq:toggle_item",
|
|
399
|
-
"itemId": "getting-started",
|
|
400
|
-
"state": "open"
|
|
401
|
-
}
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
### faq:update
|
|
405
|
-
|
|
406
|
-
Dynamically adds, removes, reorders, or replaces FAQ items at runtime.
|
|
407
|
-
|
|
408
|
-
| Property | Type | Required | Description |
|
|
409
|
-
| ----------- | ------------------- | -------- | ------------------------------------------------------- |
|
|
410
|
-
| `kind` | `"faq:update"` | Yes | Action type |
|
|
411
|
-
| `operation` | string | Yes | `"add"`, `"remove"`, `"reorder"`, `"replace"` |
|
|
412
|
-
| `items` | FAQQuestionAction[] | No | Items to add or replace with (required for add/replace) |
|
|
413
|
-
| `itemId` | string | No | Item to remove (required for remove) |
|
|
414
|
-
| `order` | string[] | No | Ordered list of item IDs (required for reorder) |
|
|
415
|
-
| `position` | string | No | `"prepend"`, `"append"`, `"before"`, `"after"` |
|
|
416
|
-
| `anchorId` | string | No | Reference item for before/after positioning |
|
|
417
|
-
|
|
418
|
-
**Add items:**
|
|
419
|
-
|
|
420
|
-
```json
|
|
421
|
-
{
|
|
422
|
-
"kind": "faq:update",
|
|
423
|
-
"operation": "add",
|
|
424
|
-
"position": "append",
|
|
425
|
-
"items": [
|
|
426
|
-
{
|
|
427
|
-
"kind": "faq:question",
|
|
428
|
-
"config": {
|
|
429
|
-
"id": "new-feature",
|
|
430
|
-
"question": "What is the new feature?",
|
|
431
|
-
"answer": "Our latest release includes AI-powered recommendations."
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
]
|
|
435
|
-
}
|
|
436
|
-
```
|
|
437
|
-
|
|
438
|
-
**Remove an item:**
|
|
439
|
-
|
|
440
|
-
```json
|
|
441
|
-
{
|
|
442
|
-
"kind": "faq:update",
|
|
443
|
-
"operation": "remove",
|
|
444
|
-
"itemId": "outdated-question"
|
|
445
|
-
}
|
|
446
|
-
```
|
|
447
|
-
|
|
448
|
-
**Reorder items:**
|
|
449
|
-
|
|
450
|
-
```json
|
|
451
|
-
{
|
|
452
|
-
"kind": "faq:update",
|
|
453
|
-
"operation": "reorder",
|
|
454
|
-
"order": ["getting-started", "new-feature", "payment-methods"]
|
|
455
|
-
}
|
|
456
|
-
```
|
|
457
|
-
|
|
458
|
-
**Replace all items:**
|
|
459
|
-
|
|
460
|
-
```json
|
|
461
|
-
{
|
|
462
|
-
"kind": "faq:update",
|
|
463
|
-
"operation": "replace",
|
|
464
|
-
"items": [
|
|
465
|
-
{
|
|
466
|
-
"kind": "faq:question",
|
|
467
|
-
"config": {
|
|
468
|
-
"id": "only-question",
|
|
469
|
-
"question": "Is this the only question?",
|
|
470
|
-
"answer": "Yes, after replacing all items."
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
]
|
|
474
|
-
}
|
|
475
|
-
```
|
|
476
|
-
|
|
477
|
-
## Compositional Pattern
|
|
478
|
-
|
|
479
|
-
The FAQ widget uses a **compositional action pattern** where `faq:question` actions serve as configuration data rendered by the widget, rather than being executed by the runtime. This allows:
|
|
480
|
-
|
|
481
|
-
- **Per-item conditional visibility** via `triggerWhen` strategies -- items can appear or hide based on page URL, user segment, viewport, or any DecisionStrategy condition
|
|
482
|
-
- **Category grouping** -- items with a `category` field are grouped under collapsible section headers
|
|
483
|
-
- **Dynamic injection** -- `injections` rules can add items when trigger conditions are met, supporting contextual FAQ content
|
|
484
|
-
- **Ordering control** -- the `ordering` strategy determines how items are sorted within categories
|
|
485
|
-
|
|
486
|
-
Items without `triggerWhen` are always visible. Items without `category` appear in an ungrouped section.
|
|
487
|
-
|
|
488
|
-
## Rich Answer Content
|
|
489
|
-
|
|
490
|
-
FAQ answers support three content formats via the `FAQAnswer` union type:
|
|
491
|
-
|
|
492
|
-
- **Plain string** -- simple text, supports basic markdown
|
|
493
|
-
- **Rich HTML** (`{ "type": "rich", "html": "<p>...</p>" }`) -- pre-rendered HTML content
|
|
494
|
-
- **Enhanced markdown** (`{ "type": "markdown", "content": "...", "assets": [...] }`) -- markdown with embedded media assets (images, videos)
|
|
495
|
-
|
|
496
|
-
```json
|
|
497
|
-
{
|
|
498
|
-
"config": {
|
|
499
|
-
"id": "rich-example",
|
|
500
|
-
"question": "How does the visual editor work?",
|
|
501
|
-
"answer": {
|
|
502
|
-
"type": "markdown",
|
|
503
|
-
"content": "The visual editor lets you create experiments with a point-and-click interface.\n\n",
|
|
504
|
-
"assets": [
|
|
505
|
-
{
|
|
506
|
-
"id": "editor-screenshot",
|
|
507
|
-
"type": "image",
|
|
508
|
-
"src": "https://cdn.example.com/editor.png",
|
|
509
|
-
"alt": "Visual editor interface",
|
|
510
|
-
"width": 800,
|
|
511
|
-
"height": 450
|
|
512
|
-
}
|
|
513
|
-
]
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
```
|
|
518
|
-
|
|
519
|
-
## Feedback
|
|
520
|
-
|
|
521
|
-
Per-item feedback allows users to rate answer helpfulness. Enable with a boolean or detailed config:
|
|
522
|
-
|
|
523
|
-
- `"feedback": true` -- enables thumbs up/down with default prompt
|
|
524
|
-
- `"feedback": { "style": "thumbs", "prompt": "Was this helpful?" }` -- thumbs with custom prompt
|
|
525
|
-
- `"feedback": { "style": "rating" }` -- numeric rating scale
|
|
526
|
-
|
|
527
|
-
Feedback events are published via `context.publishEvent` for analytics integration.
|
|
528
|
-
|
|
529
|
-
## Personalization
|
|
530
|
-
|
|
531
|
-
### Ordering Strategies
|
|
532
|
-
|
|
533
|
-
The `ordering` field controls how FAQ items are sorted:
|
|
534
|
-
|
|
535
|
-
- **`"static"`** (default) -- items appear in the order defined in the config
|
|
536
|
-
- **`"priority"`** -- items are sorted by their `priority` field (higher values first)
|
|
537
|
-
- **Segment-based** -- items are ordered differently per user segment:
|
|
538
|
-
|
|
539
|
-
```json
|
|
540
|
-
{
|
|
541
|
-
"ordering": {
|
|
542
|
-
"type": "segment",
|
|
543
|
-
"segmentWeights": {
|
|
544
|
-
"new_user": ["getting-started", "pricing", "support"],
|
|
545
|
-
"power_user": ["api-docs", "advanced-config", "integrations"]
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
```
|
|
550
|
-
|
|
551
|
-
### Contextual Hints (Companion Tooltips)
|
|
552
|
-
|
|
553
|
-
FAQ questions can be surfaced proactively on the page using companion overlay tooltips. When a tooltip CTA has an `actionId` matching `faq:open:<questionId>`, clicking it expands the corresponding question in the FAQ widget and scrolls it into view.
|
|
554
|
-
|
|
555
|
-
This is a convention-based integration — no special FAQ action is needed. Use a standard `overlays:tooltip` action with `ctaButtons`:
|
|
556
|
-
|
|
557
|
-
```json
|
|
558
|
-
{
|
|
559
|
-
"kind": "overlays:tooltip",
|
|
560
|
-
"anchorId": "#complex-feature",
|
|
561
|
-
"content": {
|
|
562
|
-
"title": "Need help?",
|
|
563
|
-
"body": "Check our FAQ for details.",
|
|
564
|
-
"ctaButtons": [
|
|
565
|
-
{ "label": "View Answer", "actionId": "faq:open:getting-started", "primary": true },
|
|
566
|
-
{ "label": "Dismiss", "actionId": "dismiss" }
|
|
567
|
-
]
|
|
568
|
-
},
|
|
569
|
-
"trigger": "immediate",
|
|
570
|
-
"placement": "bottom"
|
|
571
|
-
}
|
|
572
|
-
```
|
|
573
|
-
|
|
574
|
-
**How it works:**
|
|
575
|
-
|
|
576
|
-
1. The tooltip CTA click publishes an `action.tooltip_cta_clicked` event via the EventBus
|
|
577
|
-
2. The FAQ widget subscribes to these events and filters for `faq:open:*` actionIds
|
|
578
|
-
3. The matching question expands and scrolls into view
|
|
579
|
-
4. The widget also publishes `canvas.requestOpen` to open the canvas panel if needed
|
|
580
|
-
|
|
581
|
-
**Late-mount support:** If the FAQ widget mounts after the CTA click (e.g., canvas was closed), it checks the EventBus history for recent `faq:open:*` events (within 10 seconds) and auto-expands the target question on mount.
|
|
582
|
-
|
|
583
|
-
**`dismiss` actionId:** Destroys the tooltip without publishing an event. Use this for a "close" or "not now" button.
|
|
584
|
-
|
|
585
|
-
### Dynamic Injection
|
|
586
|
-
|
|
587
|
-
Injection rules add contextual FAQ items when conditions are met:
|
|
588
|
-
|
|
589
|
-
```json
|
|
590
|
-
{
|
|
591
|
-
"injections": [
|
|
592
|
-
{
|
|
593
|
-
"trigger": {
|
|
594
|
-
"type": "rules",
|
|
595
|
-
"rules": [
|
|
596
|
-
{
|
|
597
|
-
"conditions": [{ "type": "page_url", "pattern": "/checkout*" }],
|
|
598
|
-
"value": true
|
|
599
|
-
}
|
|
600
|
-
],
|
|
601
|
-
"default": false
|
|
602
|
-
},
|
|
603
|
-
"items": [
|
|
604
|
-
{
|
|
605
|
-
"kind": "faq:question",
|
|
606
|
-
"config": {
|
|
607
|
-
"id": "checkout-help",
|
|
608
|
-
"question": "Having trouble with checkout?",
|
|
609
|
-
"answer": "Contact support at help@example.com for immediate assistance."
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
],
|
|
613
|
-
"position": "prepend",
|
|
614
|
-
"once": true
|
|
615
|
-
}
|
|
616
|
-
]
|
|
617
|
-
}
|
|
618
|
-
```
|
|
619
|
-
|
|
620
|
-
- `trigger` -- a DecisionStrategy that evaluates to `true` when injection should occur
|
|
621
|
-
- `items` -- FAQ items to inject
|
|
622
|
-
- `position` -- `"prepend"` or `"append"` relative to existing items
|
|
623
|
-
- `once` -- if `true`, items are injected only on the first trigger match
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
---
|
|
627
|
-
|
|
628
|
-
# @syntrologie/adapt-gamification
|
|
629
|
-
|
|
630
|
-
Gamification capabilities including badges, points, and leaderboards.
|
|
631
|
-
|
|
632
|
-
## When to use
|
|
633
|
-
|
|
634
|
-
| Goal | Action |
|
|
635
|
-
|------|--------|
|
|
636
|
-
| Award a badge to a user | `gamification:awardBadge` |
|
|
637
|
-
| Add points to a user's score | `gamification:addPoints` |
|
|
638
|
-
| Mount a gamification widget (leaderboard, progress) | Add a **tile** with `component: "adaptive-gamification:leaderboard"` |
|
|
639
|
-
|
|
640
|
-
## Actions
|
|
641
|
-
|
|
642
|
-
### gamification:awardBadge
|
|
643
|
-
|
|
644
|
-
Awards a badge to the current user.
|
|
645
|
-
|
|
646
|
-
| Property | Type | Required | Description |
|
|
647
|
-
| -------- | -------------------------- | -------- | ---------------- |
|
|
648
|
-
| `kind` | `"gamification:awardBadge"` | Yes | Action type |
|
|
649
|
-
| `badgeId` | string | Yes | Badge identifier |
|
|
650
|
-
|
|
651
|
-
```json
|
|
652
|
-
{
|
|
653
|
-
"kind": "gamification:awardBadge",
|
|
654
|
-
"badgeId": "first-purchase"
|
|
655
|
-
}
|
|
656
|
-
```
|
|
657
|
-
|
|
658
|
-
### gamification:addPoints
|
|
659
|
-
|
|
660
|
-
Adds points to the current user's score.
|
|
661
|
-
|
|
662
|
-
| Property | Type | Required | Description |
|
|
663
|
-
| -------- | -------------------------- | -------- | ---------------- |
|
|
664
|
-
| `kind` | `"gamification:addPoints"` | Yes | Action type |
|
|
665
|
-
| `points` | number | Yes | Points to add |
|
|
666
|
-
|
|
667
|
-
```json
|
|
668
|
-
{
|
|
669
|
-
"kind": "gamification:addPoints",
|
|
670
|
-
"points": 50
|
|
671
|
-
}
|
|
672
|
-
```
|
|
673
|
-
|
|
674
|
-
### Gamification Widget (via Tile)
|
|
675
|
-
|
|
676
|
-
Mount gamification UI elements (leaderboard, badge display, progress tracker) via a **tile**:
|
|
677
|
-
|
|
678
|
-
```json
|
|
679
|
-
{
|
|
680
|
-
"tiles": [
|
|
681
|
-
{
|
|
682
|
-
"id": "gamification",
|
|
683
|
-
"title": "Your Progress",
|
|
684
|
-
"content": {
|
|
685
|
-
"type": "custom",
|
|
686
|
-
"component": "adaptive-gamification:leaderboard",
|
|
687
|
-
"props": { ... }
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
]
|
|
691
|
-
}
|
|
692
|
-
```
|
|
693
|
-
|
|
694
|
-
### Configuration Schema
|
|
695
|
-
|
|
696
|
-
| Property | Type | Required | Default | Description |
|
|
697
|
-
| ----------------------------- | ------- | -------- | ------- | ---------------------- |
|
|
698
|
-
| `badges` | array | No | `[]` | Badge definitions |
|
|
699
|
-
| `points.enabled` | boolean | No | `false` | Enable points system |
|
|
700
|
-
| `points.multiplier` | number | No | `1` | Points multiplier |
|
|
701
|
-
| `leaderboard.enabled` | boolean | No | `false` | Show leaderboard |
|
|
702
|
-
| `leaderboard.refreshInterval` | number | No | `60000` | Refresh interval in ms |
|
|
703
|
-
|
|
704
|
-
### Badge Schema
|
|
705
|
-
|
|
706
|
-
| Property | Type | Required | Description |
|
|
707
|
-
| -------------------- | ------ | -------- | ----------------------------- |
|
|
708
|
-
| `id` | string | Yes | Unique badge identifier |
|
|
709
|
-
| `name` | string | Yes | Display name |
|
|
710
|
-
| `icon` | string | Yes | Icon identifier |
|
|
711
|
-
| `description` | string | No | Badge description |
|
|
712
|
-
| `trigger.event` | string | Yes | Event that triggers the badge |
|
|
713
|
-
| `trigger.conditions` | array | No | Additional conditions |
|
|
714
|
-
|
|
715
|
-
```json
|
|
716
|
-
{
|
|
717
|
-
"tiles": [{
|
|
718
|
-
"id": "gamification-widget",
|
|
719
|
-
"title": "Achievements",
|
|
720
|
-
"content": {
|
|
721
|
-
"type": "custom",
|
|
722
|
-
"component": "adaptive-gamification:leaderboard",
|
|
723
|
-
"props": {
|
|
724
|
-
"badges": [
|
|
725
|
-
{
|
|
726
|
-
"id": "first-purchase",
|
|
727
|
-
"name": "First Purchase",
|
|
728
|
-
"icon": "shopping-cart",
|
|
729
|
-
"description": "Made your first purchase!",
|
|
730
|
-
"trigger": {
|
|
731
|
-
"event": "purchase_completed"
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
],
|
|
735
|
-
"points": {
|
|
736
|
-
"enabled": true,
|
|
737
|
-
"multiplier": 2
|
|
738
|
-
},
|
|
739
|
-
"leaderboard": {
|
|
740
|
-
"enabled": true,
|
|
741
|
-
"refreshInterval": 30000
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
```
|
|
746
|
-
|
|
747
|
-
## Use Cases
|
|
748
|
-
|
|
749
|
-
- **Engagement rewards**: Award badges for completing onboarding steps
|
|
750
|
-
- **Loyalty programs**: Track points for purchases or interactions
|
|
751
|
-
- **Social proof**: Display leaderboards to encourage participation
|
|
752
|
-
- **Progress tracking**: Show achievement progress to motivate users
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
---
|
|
756
|
-
|
|
757
|
-
# @syntrologie/adapt-mcp
|
|
758
|
-
|
|
759
|
-
Loader adaptive for the MCP Apps protocol.
|
|
760
|
-
|
|
761
|
-
## What it does today
|
|
762
|
-
|
|
763
|
-
Nothing observable from tile config. This adaptive is currently a pure loader:
|
|
764
|
-
it pulls [`@modelcontextprotocol/ext-apps`](https://www.npmjs.com/package/@modelcontextprotocol/ext-apps) into the page and exposes its `App`, `AppBridge`,
|
|
765
|
-
and `PostMessageTransport` classes on `window.MCPApp`. No actions, no widgets,
|
|
766
|
-
no surfaces.
|
|
767
|
-
|
|
768
|
-
## Why it exists separately from `runtime-sdk`
|
|
769
|
-
|
|
770
|
-
The MCP Apps bridge adds a non-trivial amount of code and a new dep
|
|
771
|
-
(`@modelcontextprotocol/ext-apps`) to any page that includes it. Most Syntro
|
|
772
|
-
deployments don't need it — only pages that want to render interactive MCP-app
|
|
773
|
-
cards do. Splitting it into its own adaptive means the runtime stays lean and
|
|
774
|
-
the bridge code only loads when a tile references it.
|
|
775
|
-
|
|
776
|
-
This package is deliberately **not** listed in `BUNDLED_APP_IDS` in
|
|
777
|
-
`packages/runtime-sdk/src/apps/AppLoader.ts`, so the AppLoader treats it as a
|
|
778
|
-
CDN-hosted adaptive and fetches it on demand.
|
|
779
|
-
|
|
780
|
-
## Loading
|
|
781
|
-
|
|
782
|
-
The `./cdn` entrypoint is the CDN bundle. When it runs in a browser:
|
|
783
|
-
|
|
784
|
-
1. Imports `App`, `AppBridge`, `PostMessageTransport` from
|
|
785
|
-
`@modelcontextprotocol/ext-apps/app-bridge`.
|
|
786
|
-
2. Assigns them to `window.MCPApp`.
|
|
787
|
-
3. Calls `window.SynOS.appRegistry.register(manifest)` if the host registry is
|
|
788
|
-
present.
|
|
789
|
-
|
|
790
|
-
After this runs, any subsequent page code can use `window.MCPApp.App` to
|
|
791
|
-
construct an MCP App instance without reloading the library.
|
|
792
|
-
|
|
793
|
-
## Capabilities (none yet)
|
|
794
|
-
|
|
795
|
-
| Kind | Available? | Notes |
|
|
796
|
-
|------|------------|-------|
|
|
797
|
-
| Actions | — | TBD once bridge requirements are defined. |
|
|
798
|
-
| Widgets | — | TBD. |
|
|
799
|
-
| Surfaces | — | TBD. |
|
|
800
|
-
|
|
801
|
-
This file gets aggregated into `runtime-sdk/CAPABILITIES.md` during build. When
|
|
802
|
-
actions land here, document them in the normal adaptive format (kind, description,
|
|
803
|
-
params schema, example config).
|
|
804
|
-
|
|
805
|
-
## Next steps (not in this package yet)
|
|
806
|
-
|
|
807
|
-
- Canvas/host wiring: instantiate `AppBridge` per rendered MCP-app iframe.
|
|
808
|
-
- Action registry: map `data-action` clicks to `app.callTool(...)` invocations.
|
|
809
|
-
- Per-channel MCP client proxy on the canvas server.
|
|
810
|
-
|
|
811
|
-
Those live in other PRs once we lock in the full interactivity design.
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
---
|
|
815
|
-
|
|
816
|
-
# @syntrologie/adapt-nav
|
|
817
|
-
|
|
818
|
-
Navigation tips accordion widget with conditional item visibility and toast notifications.
|
|
819
|
-
|
|
820
|
-
## When to use
|
|
821
|
-
|
|
822
|
-
| Goal | Action |
|
|
823
|
-
|------|--------|
|
|
824
|
-
| Scroll to an element on the page | `navigation:scrollTo` |
|
|
825
|
-
| Navigate to a different URL | `navigation:navigate` |
|
|
826
|
-
| Show contextual navigation tips widget | Add a **tile** with `component: "adaptive-nav:tips"` |
|
|
827
|
-
|
|
828
|
-
## Widget: `adaptive-nav:tips`
|
|
829
|
-
|
|
830
|
-
Accordion of contextual navigation tips. Each tip has a collapsible header and expanded body with optional CTA link.
|
|
831
|
-
|
|
832
|
-
### Tile Config
|
|
833
|
-
|
|
834
|
-
| Property | Type | Required | Default | Description |
|
|
835
|
-
| ---------------------- | --------------------------------- | -------- | ---------- | --------------------- |
|
|
836
|
-
| `widget` | `"adaptive-nav:tips"` | Yes | | Widget identifier |
|
|
837
|
-
| `props.expandBehavior` | `"single"` \| `"multiple"` | No | `"single"` | Accordion expand mode |
|
|
838
|
-
| `props.theme` | `"light"` \| `"dark"` \| `"auto"` | No | `"auto"` | Color theme |
|
|
839
|
-
| `props.actions` | `NavTipAction[]` | Yes | | Navigation tip items |
|
|
840
|
-
|
|
841
|
-
### Nav Tip Action (`nav:tip`)
|
|
842
|
-
|
|
843
|
-
Compositional action — rendered as an accordion item by the parent widget.
|
|
844
|
-
|
|
845
|
-
| Property | Type | Required | Description |
|
|
846
|
-
| -------------------- | --------------------------- | -------- | ------------------------------------ |
|
|
847
|
-
| `kind` | `"nav:tip"` | Yes | Action type |
|
|
848
|
-
| `config.id` | string | Yes | Unique tip identifier |
|
|
849
|
-
| `config.title` | string | Yes | Accordion header text |
|
|
850
|
-
| `config.description` | string | Yes | Expanded body text |
|
|
851
|
-
| `config.href` | string | No | Optional CTA link URL |
|
|
852
|
-
| `config.icon` | string | No | Icon (emoji or icon key) |
|
|
853
|
-
| `config.external` | boolean | No | Open link in new tab |
|
|
854
|
-
| `config.category` | string | No | Category for grouping |
|
|
855
|
-
| `showWhen` | `DecisionStrategy<boolean>` | No | Conditional visibility |
|
|
856
|
-
| `notify` | `{ title?, body?, icon? }` | No | Toast config for showWhen transition |
|
|
857
|
-
| `rationale` | `{ why, confidence? }` | No | AI reasoning for recommendation |
|
|
858
|
-
|
|
859
|
-
### Events Published
|
|
860
|
-
|
|
861
|
-
| Event | When | Props |
|
|
862
|
-
| ------------------ | ----------------------------------- | --------------------------------- |
|
|
863
|
-
| `nav:toggled` | User expands/collapses a tip | `{ instanceId, tipId, expanded }` |
|
|
864
|
-
| `nav:tip_clicked` | User clicks a tip's CTA link | `{ instanceId, href, external }` |
|
|
865
|
-
| `nav:tip_revealed` | `showWhen` transitions false → true | `{ tipId, title, body, icon }` |
|
|
866
|
-
|
|
867
|
-
### Notify Watchers
|
|
868
|
-
|
|
869
|
-
Tips with both `showWhen` and `notify` are registered as notify watchers. The runtime evaluates these continuously (even when drawer is closed). When `showWhen` transitions false → true, publishes `nav:tip_revealed` which can trigger toast notifications via tile-level `notifications` rules.
|
|
870
|
-
|
|
871
|
-
```json
|
|
872
|
-
{
|
|
873
|
-
"id": "nav",
|
|
874
|
-
"widget": "adaptive-nav:tips",
|
|
875
|
-
"notifications": [
|
|
876
|
-
{
|
|
877
|
-
"on": "nav:tip_revealed",
|
|
878
|
-
"title": "{{props.title}}",
|
|
879
|
-
"body": "{{props.body}}",
|
|
880
|
-
"icon": "🧭"
|
|
881
|
-
}
|
|
882
|
-
],
|
|
883
|
-
"props": {
|
|
884
|
-
"expandBehavior": "single",
|
|
885
|
-
"theme": "dark",
|
|
886
|
-
"actions": [
|
|
887
|
-
{
|
|
888
|
-
"kind": "nav:tip",
|
|
889
|
-
"config": {
|
|
890
|
-
"id": "tip-analytics",
|
|
891
|
-
"title": "Advanced Analytics",
|
|
892
|
-
"description": "Unlock deeper insights with our analytics suite.",
|
|
893
|
-
"href": "/analytics",
|
|
894
|
-
"icon": "📊",
|
|
895
|
-
"category": "Power User"
|
|
896
|
-
},
|
|
897
|
-
"notify": { "title": "New Tip", "body": "Advanced Analytics", "icon": "📊" },
|
|
898
|
-
"showWhen": {
|
|
899
|
-
"type": "rules",
|
|
900
|
-
"rules": [
|
|
901
|
-
{
|
|
902
|
-
"conditions": [
|
|
903
|
-
{ "type": "event_count", "key": "link-clicks", "operator": "gte", "count": 3 }
|
|
904
|
-
],
|
|
905
|
-
"value": true
|
|
906
|
-
}
|
|
907
|
-
],
|
|
908
|
-
"default": false
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
]
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
```
|
|
915
|
-
|
|
916
|
-
## Navigation Actions
|
|
917
|
-
|
|
918
|
-
### `navigation:scrollTo`
|
|
919
|
-
|
|
920
|
-
Scrolls the viewport to bring an element into view.
|
|
921
|
-
|
|
922
|
-
| Property | Type | Required | Default | Description |
|
|
923
|
-
| ---------- | ----------------------- | -------- | ---------- | ------------------------------------------- |
|
|
924
|
-
| `kind` | `"navigation:scrollTo"` | Yes | | Action type |
|
|
925
|
-
| `anchorId` | string | Yes | | Element selector |
|
|
926
|
-
| `behavior` | string | No | `"smooth"` | `"smooth"`, `"instant"`, `"auto"` |
|
|
927
|
-
| `block` | string | No | `"center"` | `"start"`, `"center"`, `"end"`, `"nearest"` |
|
|
928
|
-
|
|
929
|
-
### `navigation:navigate`
|
|
930
|
-
|
|
931
|
-
Navigates to a URL.
|
|
932
|
-
|
|
933
|
-
| Property | Type | Required | Default | Description |
|
|
934
|
-
| -------- | ----------------------- | -------- | --------- | ----------------------- |
|
|
935
|
-
| `kind` | `"navigation:navigate"` | Yes | | Action type |
|
|
936
|
-
| `url` | string | Yes | | Destination URL |
|
|
937
|
-
| `target` | string | No | `"_self"` | `"_self"` or `"_blank"` |
|
|
938
|
-
|
|
939
|
-
**Note:** `javascript:` URLs are blocked for security.
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
---
|
|
943
|
-
|
|
944
|
-
# @syntrologie/adapt-overlays
|
|
945
|
-
|
|
946
|
-
Visual overlay capabilities including highlights, tooltips, badges, pulse animations, and celebrations.
|
|
947
|
-
|
|
948
|
-
## When to use
|
|
949
|
-
|
|
950
|
-
| Goal | Action |
|
|
951
|
-
|------|--------|
|
|
952
|
-
| Draw attention to an element (spotlight) | `overlays:highlight` |
|
|
953
|
-
| Show contextual help or guidance near an element | `overlays:tooltip` |
|
|
954
|
-
| Add a notification indicator (count, "NEW") | `overlays:badge` |
|
|
955
|
-
| Subtle attention-grab animation | `overlays:pulse` |
|
|
956
|
-
| Show a blocking or non-blocking dialog | `overlays:modal` |
|
|
957
|
-
| Celebrate a user achievement | `overlays:celebrate` |
|
|
958
|
-
|
|
959
|
-
## Actions
|
|
960
|
-
|
|
961
|
-
### overlays:highlight
|
|
962
|
-
|
|
963
|
-
Creates a spotlight effect around an element with a scrim overlay.
|
|
964
|
-
|
|
965
|
-
| Property | Type | Required | Default | Description |
|
|
966
|
-
| -------------------- | ------------- | -------- | ----------- | --------------------------------------- |
|
|
967
|
-
| `kind` | `"overlays:highlight"` | Yes | | Action type |
|
|
968
|
-
| `anchorId` | string | Yes | | Element selector |
|
|
969
|
-
| `style.color` | string | No | `"#5b8cff"` | Ring color |
|
|
970
|
-
| `style.scrimOpacity` | number | No | `0.55` | Backdrop opacity 0-1 (set to 0 to hide) |
|
|
971
|
-
| `style.paddingPx` | number | No | `12` | Space around element |
|
|
972
|
-
| `style.radiusPx` | number | No | `12` | Ring corner radius |
|
|
973
|
-
|
|
974
|
-
```json
|
|
975
|
-
{
|
|
976
|
-
"kind": "overlays:highlight",
|
|
977
|
-
"anchorId": "#signup-button",
|
|
978
|
-
"style": {
|
|
979
|
-
"color": "#22c55e",
|
|
980
|
-
"scrimOpacity": 0.4
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
```
|
|
984
|
-
|
|
985
|
-
### overlays:tooltip
|
|
986
|
-
|
|
987
|
-
Shows a tooltip near an element with optional title, body, and CTA.
|
|
988
|
-
|
|
989
|
-
| Property | Type | Required | Default | Description |
|
|
990
|
-
| -------------------- | ----------- | -------- | ------------- | -------------------------------------- |
|
|
991
|
-
| `kind` | `"overlays:tooltip"` | Yes | | Action type |
|
|
992
|
-
| `anchorId` | string | Yes | | Element selector |
|
|
993
|
-
| `content.title` | string | No | | Tooltip heading |
|
|
994
|
-
| `content.body` | string | Yes | | Tooltip text |
|
|
995
|
-
| `content.cta.label` | string | No | | CTA button text (single-CTA shorthand) |
|
|
996
|
-
| `content.cta.action` | Action | No | | Action to execute on CTA click |
|
|
997
|
-
| `content.ctaButtons` | array | No | | Multiple CTA buttons (see below) |
|
|
998
|
-
| `trigger` | string | No | `"immediate"` | `"immediate"`, `"hover"`, `"click"` |
|
|
999
|
-
| `placement` | string | No | `"top"` | See placement options below |
|
|
1000
|
-
|
|
1001
|
-
**Placement options:** `top`, `top-start`, `top-end`, `bottom`, `bottom-start`, `bottom-end`, `left`, `left-start`, `left-end`, `right`, `right-start`, `right-end`
|
|
1002
|
-
|
|
1003
|
-
**Single CTA (shorthand):**
|
|
1004
|
-
|
|
1005
|
-
```json
|
|
1006
|
-
{
|
|
1007
|
-
"kind": "overlays:tooltip",
|
|
1008
|
-
"anchorId": "#pricing-toggle",
|
|
1009
|
-
"content": {
|
|
1010
|
-
"title": "Save 20%",
|
|
1011
|
-
"body": "Switch to annual billing to save on your subscription.",
|
|
1012
|
-
"cta": {
|
|
1013
|
-
"label": "Switch Now",
|
|
1014
|
-
"action": { "kind": "navigation:navigate", "url": "/billing?annual=true" }
|
|
1015
|
-
}
|
|
1016
|
-
},
|
|
1017
|
-
"placement": "bottom",
|
|
1018
|
-
"trigger": "immediate"
|
|
1019
|
-
}
|
|
1020
|
-
```
|
|
1021
|
-
|
|
1022
|
-
**Multiple CTAs (`ctaButtons`):**
|
|
1023
|
-
|
|
1024
|
-
Use `ctaButtons` for tooltips with multiple actions. Each button has:
|
|
1025
|
-
|
|
1026
|
-
- `label`: Button text
|
|
1027
|
-
- `actionId`: Identifier published with the `action.tooltip_cta_clicked` event
|
|
1028
|
-
- `primary`: Whether this is a primary button (default: false)
|
|
1029
|
-
|
|
1030
|
-
```json
|
|
1031
|
-
{
|
|
1032
|
-
"kind": "overlays:tooltip",
|
|
1033
|
-
"anchorId": "[data-nav=\"features\"]",
|
|
1034
|
-
"content": {
|
|
1035
|
-
"title": "Learn About Features",
|
|
1036
|
-
"body": "We have answers about our feature set.",
|
|
1037
|
-
"ctaButtons": [
|
|
1038
|
-
{ "label": "View Answer", "actionId": "faq:open:q-features", "primary": true },
|
|
1039
|
-
{ "label": "Dismiss", "actionId": "dismiss" }
|
|
1040
|
-
]
|
|
1041
|
-
},
|
|
1042
|
-
"trigger": "immediate",
|
|
1043
|
-
"placement": "bottom"
|
|
1044
|
-
}
|
|
1045
|
-
```
|
|
1046
|
-
|
|
1047
|
-
**Special actionId values:**
|
|
1048
|
-
|
|
1049
|
-
- `"dismiss"` — Destroys the tooltip immediately without publishing an event
|
|
1050
|
-
- `"faq:open:<questionId>"` — Convention for companion FAQ tooltips (see adaptive-faq CAPABILITIES for details). Publishes `action.tooltip_cta_clicked` which the FAQ widget listens for.
|
|
1051
|
-
- Any other value — Publishes `action.tooltip_cta_clicked` with the `actionId` in event props for custom handling
|
|
1052
|
-
|
|
1053
|
-
### overlays:badge
|
|
1054
|
-
|
|
1055
|
-
Adds a small badge indicator near an element.
|
|
1056
|
-
|
|
1057
|
-
| Property | Type | Required | Default | Description |
|
|
1058
|
-
| ---------- | --------- | -------- | ------------- | -------------------------------------------------------------- |
|
|
1059
|
-
| `kind` | `"overlays:badge"` | Yes | | Action type |
|
|
1060
|
-
| `anchorId` | string | Yes | | Element selector |
|
|
1061
|
-
| `text` | string | Yes | | Badge text (e.g., "NEW", "3") |
|
|
1062
|
-
| `position` | string | No | `"top-right"` | `"top-left"`, `"top-right"`, `"bottom-left"`, `"bottom-right"` |
|
|
1063
|
-
|
|
1064
|
-
```json
|
|
1065
|
-
{
|
|
1066
|
-
"kind": "overlays:badge",
|
|
1067
|
-
"anchorId": "#inbox-icon",
|
|
1068
|
-
"text": "5",
|
|
1069
|
-
"position": "top-right"
|
|
1070
|
-
}
|
|
1071
|
-
```
|
|
1072
|
-
|
|
1073
|
-
### overlays:pulse
|
|
1074
|
-
|
|
1075
|
-
Adds a pulsing animation to draw attention.
|
|
1076
|
-
|
|
1077
|
-
| Property | Type | Required | Default | Description |
|
|
1078
|
-
| ---------- | --------- | -------- | ------- | ------------------------ |
|
|
1079
|
-
| `kind` | `"overlays:pulse"` | Yes | | Action type |
|
|
1080
|
-
| `anchorId` | string | Yes | | Element selector |
|
|
1081
|
-
| `duration` | number | No | `2000` | Animation duration in ms |
|
|
1082
|
-
|
|
1083
|
-
```json
|
|
1084
|
-
{
|
|
1085
|
-
"kind": "overlays:pulse",
|
|
1086
|
-
"anchorId": ".notification-bell",
|
|
1087
|
-
"duration": 3000
|
|
1088
|
-
}
|
|
1089
|
-
```
|
|
1090
|
-
|
|
1091
|
-
### overlays:modal
|
|
1092
|
-
|
|
1093
|
-
Shows a centered modal dialog with optional CTA buttons.
|
|
1094
|
-
|
|
1095
|
-
| Property | Type | Required | Default | Description |
|
|
1096
|
-
| --------------------- | ------------------ | -------- | ------- | ------------------------------------------------------------- |
|
|
1097
|
-
| `kind` | `"overlays:modal"` | Yes | | Action type |
|
|
1098
|
-
| `content.title` | string | No | | Modal heading |
|
|
1099
|
-
| `content.body` | string | Yes | | Modal text |
|
|
1100
|
-
| `size` | string | No | `"md"` | `"sm"`, `"md"`, `"lg"` |
|
|
1101
|
-
| `blocking` | boolean | No | `false` | Block page interaction |
|
|
1102
|
-
| `scrim.opacity` | number | No | `0.6` | Backdrop opacity 0-1 |
|
|
1103
|
-
| `dismiss.onEsc` | boolean | No | `true` | Close on Escape key |
|
|
1104
|
-
| `dismiss.closeButton` | boolean | No | `true` | Show close button |
|
|
1105
|
-
| `dismiss.timeoutMs` | number | No | | Auto-close after timeout |
|
|
1106
|
-
| `ctaButtons` | array | No | | Array of CTA buttons |
|
|
1107
|
-
| `waitFor` | string | No | | When to complete: `"dismissed"`, `"cta-click"`, `"timeout:N"` |
|
|
1108
|
-
|
|
1109
|
-
**CTA Button properties:**
|
|
1110
|
-
|
|
1111
|
-
- `label`: Button text
|
|
1112
|
-
- `actionId`: Identifier for the action
|
|
1113
|
-
- `primary`: Whether this is a primary button (default: false)
|
|
1114
|
-
|
|
1115
|
-
```json
|
|
1116
|
-
{
|
|
1117
|
-
"kind": "overlays:modal",
|
|
1118
|
-
"content": {
|
|
1119
|
-
"title": "Welcome!",
|
|
1120
|
-
"body": "Thanks for signing up. Let us show you around."
|
|
1121
|
-
},
|
|
1122
|
-
"size": "md",
|
|
1123
|
-
"ctaButtons": [
|
|
1124
|
-
{ "label": "Skip", "actionId": "skip" },
|
|
1125
|
-
{ "label": "Start Tour", "actionId": "start", "primary": true }
|
|
1126
|
-
],
|
|
1127
|
-
"waitFor": "cta-click"
|
|
1128
|
-
}
|
|
1129
|
-
```
|
|
1130
|
-
|
|
1131
|
-
### overlays:celebrate
|
|
1132
|
-
|
|
1133
|
-
Renders a fullscreen Canvas 2D celebration effect. One action kind with a pluggable `effect` parameter supporting multiple visual presets.
|
|
1134
|
-
|
|
1135
|
-
| Property | Type | Required | Default | Description |
|
|
1136
|
-
| ----------- | ---------------------------------- | -------- | -------------------------- | --------------------------------------------- |
|
|
1137
|
-
| `kind` | `"overlays:celebrate"` | Yes | | Action type |
|
|
1138
|
-
| `effect` | string | Yes | | Effect name (see presets below) |
|
|
1139
|
-
| `duration` | number | No | `3000` | Animation duration in ms |
|
|
1140
|
-
| `intensity` | `"light"` \| `"medium"` \| `"heavy"` | No | `"medium"` | Particle density |
|
|
1141
|
-
| `colors` | string[] | No | Rainbow palette | Color palette for particles |
|
|
1142
|
-
| `props` | object | No | | Effect-specific properties (see presets below) |
|
|
1143
|
-
|
|
1144
|
-
**Available effect presets:**
|
|
1145
|
-
|
|
1146
|
-
- **`confetti`** — Falling rectangular/circular confetti pieces with gravity, air resistance, and rotation
|
|
1147
|
-
- **`fireworks`** — Multiple burst centers with radiating particles, deceleration, and glow effect
|
|
1148
|
-
- **`sparkles`** — Diamond shapes that twinkle via sine-wave opacity oscillation and float gently upward
|
|
1149
|
-
- **`emoji-rain`** — Emoji characters falling from the top with horizontal sine-wave wobble. Use `props.emoji` to set the character (default: `"🎉"`)
|
|
1150
|
-
|
|
1151
|
-
**Confetti:**
|
|
1152
|
-
|
|
1153
|
-
```json
|
|
1154
|
-
{
|
|
1155
|
-
"kind": "overlays:celebrate",
|
|
1156
|
-
"effect": "confetti",
|
|
1157
|
-
"duration": 4000,
|
|
1158
|
-
"intensity": "heavy",
|
|
1159
|
-
"colors": ["#ff0000", "#00ff00", "#0000ff", "#ffff00"]
|
|
1160
|
-
}
|
|
1161
|
-
```
|
|
1162
|
-
|
|
1163
|
-
**Fireworks:**
|
|
1164
|
-
|
|
1165
|
-
```json
|
|
1166
|
-
{
|
|
1167
|
-
"kind": "overlays:celebrate",
|
|
1168
|
-
"effect": "fireworks",
|
|
1169
|
-
"duration": 3000,
|
|
1170
|
-
"intensity": "medium"
|
|
1171
|
-
}
|
|
1172
|
-
```
|
|
1173
|
-
|
|
1174
|
-
**Sparkles:**
|
|
1175
|
-
|
|
1176
|
-
```json
|
|
1177
|
-
{
|
|
1178
|
-
"kind": "overlays:celebrate",
|
|
1179
|
-
"effect": "sparkles",
|
|
1180
|
-
"duration": 5000,
|
|
1181
|
-
"colors": ["#ffd700", "#ffffff", "#fffacd"]
|
|
1182
|
-
}
|
|
1183
|
-
```
|
|
1184
|
-
|
|
1185
|
-
**Emoji rain:**
|
|
1186
|
-
|
|
1187
|
-
```json
|
|
1188
|
-
{
|
|
1189
|
-
"kind": "overlays:celebrate",
|
|
1190
|
-
"effect": "emoji-rain",
|
|
1191
|
-
"duration": 3000,
|
|
1192
|
-
"props": { "emoji": "🔥" }
|
|
1193
|
-
}
|
|
1194
|
-
```
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
---
|
|
1198
|
-
|
|
1199
|
-
# @syntrologie/adapt-viz
|
|
1200
|
-
|
|
1201
|
-
Charts and tables for visualizing data — bar, line, and tabular layouts.
|
|
1202
|
-
|
|
1203
|
-
## Identity
|
|
1204
|
-
|
|
1205
|
-
- **App ID:** `adaptive-viz`
|
|
1206
|
-
- **Bundling:** CDN-only (lazy-loaded; not in the core SDK bundle)
|
|
1207
|
-
- **Loader:** `cdn.ts` registers `<syntro-viz-chart>` and exports the widget mountable
|
|
1208
|
-
|
|
1209
|
-
## Tile Widgets
|
|
1210
|
-
|
|
1211
|
-
| Widget ID | Purpose |
|
|
1212
|
-
|-----------|---------|
|
|
1213
|
-
| `adaptive-viz:chart` | Renders a chart given typed props (bar / line / table) |
|
|
1214
|
-
|
|
1215
|
-
## Configuration Shape (per tile)
|
|
1216
|
-
|
|
1217
|
-
```jsonc
|
|
1218
|
-
{
|
|
1219
|
-
"id": "tile-id",
|
|
1220
|
-
"widget": "adaptive-viz:chart",
|
|
1221
|
-
"props": {
|
|
1222
|
-
"layout": "bar", // or "line", "table"
|
|
1223
|
-
// ...layout-specific fields (see schema.ts)
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
```
|
|
1227
|
-
|
|
1228
|
-
## Layouts
|
|
1229
|
-
|
|
1230
|
-
### `bar`
|
|
1231
|
-
Single bar chart. Inputs: `data: { [key: string]: unknown }[]`, `xField`, `yField`, optional `colorField`.
|
|
1232
|
-
|
|
1233
|
-
### `line`
|
|
1234
|
-
Continuous line chart. Inputs: `data`, `xField`, `yField`, optional `seriesField`.
|
|
1235
|
-
|
|
1236
|
-
### `table`
|
|
1237
|
-
Tabular data display (rendered as Vega-Lite text marks in a grid). Inputs: `data`, `columns: { field, header }[]`.
|
|
1238
|
-
|
|
1239
|
-
## Actions
|
|
1240
|
-
|
|
1241
|
-
None. This widget renders only; it does not emit DOM-mutation actions.
|
|
1242
|
-
|
|
1243
|
-
## Theme Tokens Consumed
|
|
1244
|
-
|
|
1245
|
-
`--sc-color-primary`, `--sc-color-primary-hover`, `--sc-overlay-text-color`, `--sc-font-family`, `--sc-tile-background`. See `theme.ts` for the full mapping to Vega-Lite `config` properties.
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
---
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
---
|
|
1252
|
-
|
|
1253
|
-
## Surfaces
|
|
1254
|
-
|
|
1255
|
-
Surfaces are named slots where widgets can be mounted. Use the `mount_widget` action to render content into a surface slot.
|
|
1256
|
-
|
|
1257
|
-
### Static Slots
|
|
1258
|
-
|
|
1259
|
-
Fixed-position slots for common UI patterns:
|
|
1260
|
-
|
|
1261
|
-
| Slot | Position | Use Case |
|
|
1262
|
-
|------|----------|----------|
|
|
1263
|
-
| `drawer_right` | Right edge, full height | Settings panels, details |
|
|
1264
|
-
| `drawer_left` | Left edge, full height | Navigation, filters |
|
|
1265
|
-
| `drawer_bottom` | Bottom edge, full width | Action sheets, keyboards |
|
|
1266
|
-
| `overlay_center` | Centered modal | Dialogs, confirmations |
|
|
1267
|
-
| `overlay_corner_br` | Bottom-right corner | Chat widgets, help |
|
|
1268
|
-
| `overlay_corner_bl` | Bottom-left corner | Notifications |
|
|
1269
|
-
| `toast_top` | Top center | Success/error messages |
|
|
1270
|
-
| `toast_bottom` | Bottom center | Snackbar notifications |
|
|
1271
|
-
|
|
1272
|
-
### Dynamic Slots
|
|
1273
|
-
|
|
1274
|
-
Slots that position content relative to anchors:
|
|
1275
|
-
|
|
1276
|
-
- **Inline Slots**: Render content inside an anchor element. Format: `inline:{anchorId}`
|
|
1277
|
-
- **Adjacent Slots**: Render content positioned near an anchor element. Format: `adjacent:{anchorId}`
|
|
1278
|
-
|
|
1279
|
-
---
|
|
1280
|
-
|
|
1281
|
-
## Anchor Resolution
|
|
1282
|
-
|
|
1283
|
-
The `anchorId` field identifies which DOM element to target. Multiple formats are supported:
|
|
1284
|
-
|
|
1285
|
-
### CSS Selectors
|
|
1286
|
-
|
|
1287
|
-
| Format | Example | Matches |
|
|
1288
|
-
|--------|---------|---------|
|
|
1289
|
-
| Class | `".hero-title"` | `<h1 class="hero-title">` |
|
|
1290
|
-
| ID | `"#signup-button"` | `<button id="signup-button">` |
|
|
1291
|
-
| Attribute | `"[data-testid='cta']"` | `<button data-testid="cta">` |
|
|
1292
|
-
| Tag + class | `"h1.page-title"` | `<h1 class="page-title">` |
|
|
1293
|
-
| Nested | `".card .title"` | `<div class="title">` inside `.card` |
|
|
1294
|
-
|
|
1295
|
-
### Data Attributes (Recommended)
|
|
1296
|
-
|
|
1297
|
-
For stability, add `data-syntro-anchor` attributes to target elements:
|
|
1298
|
-
|
|
1299
|
-
```html
|
|
1300
|
-
<h1 data-syntro-anchor="hero-title">Welcome</h1>
|
|
1301
|
-
```
|
|
1302
|
-
|
|
1303
|
-
Then reference by the anchor name:
|
|
1304
|
-
|
|
1305
|
-
```json
|
|
1306
|
-
{ "anchorId": "hero-title" }
|
|
1307
|
-
```
|
|
1308
|
-
|
|
1309
|
-
---
|
|
1310
|
-
|
|
1311
|
-
## Route Scoping
|
|
1312
|
-
|
|
1313
|
-
Every tile and action **must** declare which pages it applies to via `activation.routes`. The config itself is always active — individual tiles and actions decide independently whether to render.
|
|
1314
|
-
|
|
1315
|
-
### Per-Tile Activation
|
|
1316
|
-
|
|
1317
|
-
```json
|
|
1318
|
-
{
|
|
1319
|
-
"id": "faq-w2-boxes",
|
|
1320
|
-
"widget": "adaptive-faq:accordion",
|
|
1321
|
-
"activation": {
|
|
1322
|
-
"routes": { "include": ["/dashboard/income"] }
|
|
1323
|
-
},
|
|
1324
|
-
"props": { ... }
|
|
1325
|
-
}
|
|
1326
|
-
```
|
|
1327
|
-
|
|
1328
|
-
### Per-Action Activation
|
|
1329
|
-
|
|
1330
|
-
```json
|
|
1331
|
-
{
|
|
1332
|
-
"kind": "content:insertHtml",
|
|
1333
|
-
"anchorId": "[data-section='refund-estimate']",
|
|
1334
|
-
"activation": {
|
|
1335
|
-
"routes": { "include": ["/dashboard"] }
|
|
1336
|
-
},
|
|
1337
|
-
"html": "...",
|
|
1338
|
-
"position": "after"
|
|
1339
|
-
}
|
|
1340
|
-
```
|
|
1341
|
-
|
|
1342
|
-
### Route Patterns
|
|
1343
|
-
|
|
1344
|
-
| Pattern | Matches | Use Case |
|
|
1345
|
-
|---------|---------|----------|
|
|
1346
|
-
| `"/dashboard/income"` | Exact path only | Page-specific |
|
|
1347
|
-
| `"/dashboard/**"` | `/dashboard` and all sub-paths | Section-wide |
|
|
1348
|
-
| `"/**"` | All pages | Global (always active) |
|
|
1349
|
-
| `"/dashboard/:id"` | Any single segment after `/dashboard/` | Dynamic routes |
|
|
1350
|
-
|
|
1351
|
-
### Pattern Matching Rules
|
|
1352
|
-
|
|
1353
|
-
- Patterns match against **pathname only** (no domain, no query string)
|
|
1354
|
-
- `**` matches anything (including `/`)
|
|
1355
|
-
- `*` matches within a single path segment (not `/`)
|
|
1356
|
-
- `exclude` takes priority over `include`
|
|
1357
|
-
|
|
1358
|
-
---
|
|
1359
|
-
|
|
1360
|
-
## Decision Strategies
|
|
1361
|
-
|
|
1362
|
-
Control when adaptives activate using `DecisionStrategy`:
|
|
1363
|
-
|
|
1364
|
-
### Rules Strategy
|
|
1365
|
-
|
|
1366
|
-
```json
|
|
1367
|
-
{
|
|
1368
|
-
"type": "rules",
|
|
1369
|
-
"rules": [
|
|
1370
|
-
{
|
|
1371
|
-
"conditions": [
|
|
1372
|
-
{ "type": "page_url", "pattern": "/pricing*" },
|
|
1373
|
-
{ "type": "viewport", "minWidth": 768 },
|
|
1374
|
-
{ "type": "dismissed", "key": "promo", "inverted": true }
|
|
1375
|
-
],
|
|
1376
|
-
"value": true
|
|
1377
|
-
}
|
|
1378
|
-
],
|
|
1379
|
-
"default": false
|
|
1380
|
-
}
|
|
1381
|
-
```
|
|
1382
|
-
|
|
1383
|
-
### Condition Types
|
|
1384
|
-
|
|
1385
|
-
| Type | Parameters | Description |
|
|
1386
|
-
|------|------------|-------------|
|
|
1387
|
-
| `page_url` | `pattern` | URL matches glob pattern |
|
|
1388
|
-
| `route` | `routeId` | Route ID matches |
|
|
1389
|
-
| `anchor_visible` | `anchorId`, `state` | Anchor visibility |
|
|
1390
|
-
| `event_occurred` | `eventName`, `withinMs?` | Recent event |
|
|
1391
|
-
| `state_equals` | `key`, `value` | State matches |
|
|
1392
|
-
| `viewport` | `minWidth?`, `maxWidth?`, `minHeight?`, `maxHeight?` | Viewport size |
|
|
1393
|
-
| `session_metric` | `key`, `operator`, `threshold` | Metric comparison |
|
|
1394
|
-
| `dismissed` | `key`, `inverted?` | Dismissal check |
|
|
1395
|
-
| `cooldown_active` | `key`, `inverted?` | Cooldown check |
|
|
1396
|
-
| `frequency_limit` | `key`, `limit`, `inverted?` | Frequency cap |
|
|
1397
|
-
|
|
1398
|
-
---
|
|
1399
|
-
|
|
1400
|
-
## Best Practices
|
|
1401
|
-
|
|
1402
|
-
### 1. Choose the Right Action Type
|
|
1403
|
-
|
|
1404
|
-
Each adaptive package has a "When to use" guide at the top of its capabilities section above. Refer to those for action selection guidance.
|
|
1405
|
-
|
|
1406
|
-
### 2. Anchor Selection
|
|
1407
|
-
|
|
1408
|
-
- **Prefer stable selectors:** `[data-testid]`, IDs over classes
|
|
1409
|
-
- **Test across states:** Element may not exist on all pages
|
|
1410
|
-
- **Be specific:** Avoid selectors matching multiple elements
|
|
1411
|
-
|
|
1412
|
-
### 3. Reversibility
|
|
1413
|
-
|
|
1414
|
-
- Always store handles if you need to revert
|
|
1415
|
-
- Use `applyBatch` for related changes (atomic rollback)
|
|
1416
|
-
- Clean up on route changes
|
|
1417
|
-
|
|
1418
|
-
### 4. Performance
|
|
1419
|
-
|
|
1420
|
-
- Batch related actions together
|
|
1421
|
-
- Use `validate()` to check actions before applying
|
|
1422
|
-
- Avoid applying many actions simultaneously
|
|
1423
|
-
|
|
1424
|
-
### 5. Events
|
|
1425
|
-
|
|
1426
|
-
- Listen for `action.failed` to handle errors
|
|
1427
|
-
- Track `action.applied` for analytics
|
|
1428
|
-
- Use EventBus for cross-adaptive coordination
|
|
1429
|
-
|
|
1430
|
-
### 6. Opening the Canvas from Inserted Content
|
|
1431
|
-
|
|
1432
|
-
When `content:insertHtml` needs to open the canvas panel (e.g., a "Help Me Choose" button that opens an FAQ tile), use the `deepLink` property — **never write `onclick` handlers or reference `window.SynOS` in HTML**.
|
|
1433
|
-
|
|
1434
|
-
```json
|
|
1435
|
-
{
|
|
1436
|
-
"kind": "content:insertHtml",
|
|
1437
|
-
"anchorId": { "selector": "[data-id='pricing-heading']", "route": "/" },
|
|
1438
|
-
"html": "<button style='background: #4a90e2; color: white; border: none; padding: 8px 16px; border-radius: 20px; cursor: pointer;'>Help Me Choose</button>",
|
|
1439
|
-
"position": "after",
|
|
1440
|
-
"deepLink": { "tileId": "plan_selector_faq" }
|
|
1441
|
-
}
|
|
1442
|
-
```
|
|
1443
|
-
|
|
1444
|
-
The `deepLink` object:
|
|
1445
|
-
- `tileId` (required) — ID of the canvas tile to navigate to
|
|
1446
|
-
- `itemId` (optional) — specific item within the tile (e.g., a FAQ question ID)
|
|
1447
|
-
|
|
1448
|
-
The SDK automatically opens the canvas, navigates to the tile, sets cursor to pointer, and wires up click/cleanup handlers.
|
|
1449
|
-
|
|
1450
|
-
**NEVER use `onclick`, `window.SynOS`, or any JavaScript in `content:insertHtml` HTML strings.** The HTML is sanitized and event handlers are stripped. Use `deepLink` instead.
|