@syntrologie/runtime-sdk 0.2.20 → 1.0.0-canary.1
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 +756 -284
- package/README.md +310 -68
- package/dist/RuntimeProvider.d.ts +51 -0
- package/dist/RuntimeProvider.js +113 -0
- package/dist/RuntimeProvider.js.map +1 -0
- package/dist/SmartCanvasApp.d.ts +16 -10
- package/dist/SmartCanvasApp.js +45 -49
- package/dist/SmartCanvasApp.js.map +1 -1
- package/dist/SmartCanvasElement.d.ts +5 -5
- package/dist/SmartCanvasElement.js +24 -14
- package/dist/SmartCanvasElement.js.map +1 -1
- package/dist/SmartCanvasPortal.d.ts +2 -2
- package/dist/SmartCanvasPortal.js +2 -2
- package/dist/actions/ActionEngine.d.ts +11 -0
- package/dist/actions/ActionEngine.js +274 -0
- package/dist/actions/ActionEngine.js.map +1 -0
- package/dist/actions/executors/index.d.ts +117 -0
- package/dist/actions/executors/index.js +242 -0
- package/dist/actions/executors/index.js.map +1 -0
- package/dist/actions/executors/tour.d.ts +18 -0
- package/dist/actions/executors/tour.js +332 -0
- package/dist/actions/executors/tour.js.map +1 -0
- package/dist/actions/index.d.ts +10 -0
- package/dist/actions/index.js +12 -0
- package/dist/actions/index.js.map +1 -0
- package/dist/actions/types.d.ts +399 -0
- package/dist/actions/types.js +8 -0
- package/dist/actions/types.js.map +1 -0
- package/dist/actions/validation.d.ts +14 -0
- package/dist/actions/validation.js +602 -0
- package/dist/actions/validation.js.map +1 -0
- package/dist/antiFlicker.js +1 -1
- package/dist/api.d.ts +40 -26
- package/dist/api.js +90 -59
- package/dist/api.js.map +1 -1
- package/dist/apps/AppContext.d.ts +31 -0
- package/dist/apps/AppContext.js +93 -0
- package/dist/apps/AppContext.js.map +1 -0
- package/dist/apps/AppLoader.d.ts +84 -0
- package/dist/apps/AppLoader.js +256 -0
- package/dist/apps/AppLoader.js.map +1 -0
- package/dist/apps/AppRegistry.d.ts +102 -0
- package/dist/apps/AppRegistry.js +317 -0
- package/dist/apps/AppRegistry.js.map +1 -0
- package/dist/apps/adaptive-chatbot/index.js +7 -0
- package/dist/apps/adaptive-chatbot/index.js.map +7 -0
- package/dist/apps/examples/gamification-app.example.d.ts +305 -0
- package/dist/apps/examples/gamification-app.example.js +329 -0
- package/dist/apps/examples/gamification-app.example.js.map +1 -0
- package/dist/apps/faq/index.js +11 -0
- package/dist/apps/faq/index.js.map +7 -0
- package/dist/apps/gamification/index.js +2 -0
- package/dist/apps/gamification/index.js.map +7 -0
- package/dist/apps/index.d.ts +15 -0
- package/dist/apps/index.js +17 -0
- package/dist/apps/index.js.map +1 -0
- package/dist/apps/nav/index.js +11 -0
- package/dist/apps/nav/index.js.map +7 -0
- package/dist/apps/types.d.ts +231 -0
- package/dist/apps/types.js +8 -0
- package/dist/apps/types.js.map +1 -0
- package/dist/blocks/data/ComparisonBlock.d.ts +1 -1
- package/dist/blocks/data/ComparisonBlock.js +40 -40
- package/dist/blocks/data/ComparisonBlock.js.map +1 -1
- package/dist/blocks/data/StatsBlock.d.ts +1 -1
- package/dist/blocks/data/StatsBlock.js +41 -41
- package/dist/blocks/data/StatsBlock.js.map +1 -1
- package/dist/blocks/data/index.d.ts +2 -2
- package/dist/blocks/data/index.js +2 -2
- package/dist/blocks/index.d.ts +5 -5
- package/dist/blocks/index.js +29 -29
- package/dist/blocks/index.js.map +1 -1
- package/dist/blocks/interactive/ChecklistBlock.d.ts +1 -1
- package/dist/blocks/interactive/ChecklistBlock.js +60 -60
- package/dist/blocks/interactive/ChecklistBlock.js.map +1 -1
- package/dist/blocks/interactive/RatingBlock.d.ts +1 -1
- package/dist/blocks/interactive/RatingBlock.js +75 -65
- package/dist/blocks/interactive/RatingBlock.js.map +1 -1
- package/dist/blocks/interactive/index.d.ts +2 -2
- package/dist/blocks/interactive/index.js +2 -2
- package/dist/blocks/notification/NotificationBlock.d.ts +2 -2
- package/dist/blocks/notification/NotificationBlock.js +67 -63
- package/dist/blocks/notification/NotificationBlock.js.map +1 -1
- package/dist/blocks/notification/index.d.ts +1 -1
- package/dist/blocks/notification/index.js +1 -1
- package/dist/bootstrap.d.ts +47 -9
- package/dist/bootstrap.js +237 -69
- package/dist/bootstrap.js.map +1 -1
- package/dist/components/ShadowCanvasOverlay.d.ts +6 -6
- package/dist/components/ShadowCanvasOverlay.js +144 -107
- package/dist/components/ShadowCanvasOverlay.js.map +1 -1
- package/dist/components/TileCard.d.ts +5 -5
- package/dist/components/TileCard.js +204 -154
- package/dist/components/TileCard.js.map +1 -1
- package/dist/components/TileWheel.d.ts +3 -3
- package/dist/components/TileWheel.js +7 -7
- package/dist/components/TileWheel.js.map +1 -1
- package/dist/config-validator.d.ts +49 -0
- package/dist/config-validator.js +173 -0
- package/dist/config-validator.js.map +1 -0
- package/dist/configFetcher.d.ts +2 -2
- package/dist/configFetcher.js +20 -8
- package/dist/configFetcher.js.map +1 -1
- package/dist/context/ContextManager.d.ts +66 -0
- package/dist/context/ContextManager.js +268 -0
- package/dist/context/ContextManager.js.map +1 -0
- package/dist/context/index.d.ts +7 -0
- package/dist/context/index.js +7 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/schema.d.ts +360 -0
- package/dist/context/schema.js +50 -0
- package/dist/context/schema.js.map +1 -0
- package/dist/context/types.d.ts +101 -0
- package/dist/context/types.js +8 -0
- package/dist/context/types.js.map +1 -0
- package/dist/decisions/engine.d.ts +43 -0
- package/dist/decisions/engine.js +112 -0
- package/dist/decisions/engine.js.map +1 -0
- package/dist/decisions/index.d.ts +9 -0
- package/dist/decisions/index.js +10 -0
- package/dist/decisions/index.js.map +1 -0
- package/dist/decisions/schema.d.ts +2166 -0
- package/dist/decisions/schema.js +143 -0
- package/dist/decisions/schema.js.map +1 -0
- package/dist/decisions/strategies/rules.d.ts +24 -0
- package/dist/decisions/strategies/rules.js +152 -0
- package/dist/decisions/strategies/rules.js.map +1 -0
- package/dist/decisions/strategies/score.d.ts +10 -0
- package/dist/decisions/strategies/score.js +29 -0
- package/dist/decisions/strategies/score.js.map +1 -0
- package/dist/decisions/types.d.ts +242 -0
- package/dist/decisions/types.js +2 -0
- package/dist/decisions/types.js.map +1 -0
- package/dist/earlyPatcher.d.ts +8 -20
- package/dist/earlyPatcher.js +13 -62
- package/dist/earlyPatcher.js.map +1 -1
- package/dist/editorLoader.d.ts +12 -0
- package/dist/editorLoader.js +132 -48
- package/dist/editorLoader.js.map +1 -1
- package/dist/events/EventBus.d.ts +59 -0
- package/dist/events/EventBus.js +152 -0
- package/dist/events/EventBus.js.map +1 -0
- package/dist/events/index.d.ts +9 -0
- package/dist/events/index.js +10 -0
- package/dist/events/index.js.map +1 -0
- package/dist/events/normalizers/canvas.d.ts +67 -0
- package/dist/events/normalizers/canvas.js +116 -0
- package/dist/events/normalizers/canvas.js.map +1 -0
- package/dist/events/normalizers/posthog.d.ts +53 -0
- package/dist/events/normalizers/posthog.js +163 -0
- package/dist/events/normalizers/posthog.js.map +1 -0
- package/dist/events/schema.d.ts +70 -0
- package/dist/events/schema.js +30 -0
- package/dist/events/schema.js.map +1 -0
- package/dist/events/types.d.ts +79 -0
- package/dist/events/types.js +49 -0
- package/dist/events/types.js.map +1 -0
- package/dist/experiments/adapters/growthbook.d.ts +4 -4
- package/dist/experiments/adapters/growthbook.js +5 -5
- package/dist/experiments/adapters/growthbook.js.map +1 -1
- package/dist/experiments/index.d.ts +3 -3
- package/dist/experiments/index.js +1 -1
- package/dist/experiments/registry.d.ts +2 -2
- package/dist/experiments/registry.js +2 -2
- package/dist/experiments/types.d.ts +5 -1
- package/dist/fetchers/cdnFetcher.d.ts +1 -1
- package/dist/fetchers/cdnFetcher.js +4 -8
- package/dist/fetchers/cdnFetcher.js.map +1 -1
- package/dist/fetchers/experimentsFetcher.d.ts +2 -2
- package/dist/fetchers/experimentsFetcher.js +7 -7
- package/dist/fetchers/experimentsFetcher.js.map +1 -1
- package/dist/fetchers/index.d.ts +3 -3
- package/dist/fetchers/index.js +2 -2
- package/dist/fetchers/index.js.map +1 -1
- package/dist/fetchers/registry.d.ts +1 -1
- package/dist/fetchers/registry.js +4 -4
- package/dist/fetchers/types.d.ts +1 -1
- package/dist/hooks/useCanvasOverlays.d.ts +8 -5
- package/dist/hooks/useCanvasOverlays.js +66 -17
- package/dist/hooks/useCanvasOverlays.js.map +1 -1
- package/dist/hooks/useHostPatches.d.ts +2 -2
- package/dist/hooks/useHostPatches.js +8 -8
- package/dist/hooks/useHostPatches.js.map +1 -1
- package/dist/hooks/useShadowCanvasConfig.d.ts +9 -9
- package/dist/hooks/useShadowCanvasConfig.js +24 -8
- package/dist/hooks/useShadowCanvasConfig.js.map +1 -1
- package/dist/hostPatcher/core/patcher.d.ts +1 -1
- package/dist/hostPatcher/core/patcher.js +18 -9
- package/dist/hostPatcher/core/patcher.js.map +1 -1
- package/dist/hostPatcher/core/sanitizer.js +24 -3
- package/dist/hostPatcher/core/sanitizer.js.map +1 -1
- package/dist/hostPatcher/policy/defaultPolicy.js +15 -5
- package/dist/hostPatcher/policy/defaultPolicy.js.map +1 -1
- package/dist/hostPatcher/utils/anchors.js +4 -6
- package/dist/hostPatcher/utils/anchors.js.map +1 -1
- package/dist/index.d.ts +34 -21
- package/dist/index.js +46 -19
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +29 -0
- package/dist/logger.js +81 -0
- package/dist/logger.js.map +1 -0
- package/dist/metrics/index.d.ts +1 -1
- package/dist/metrics/index.js +1 -1
- package/dist/metrics/sessionMetrics.d.ts +1 -1
- package/dist/metrics/sessionMetrics.js +6 -6
- package/dist/overlays/fetcher.d.ts +2 -2
- package/dist/overlays/fetcher.js +4 -4
- package/dist/overlays/recipeRegistry.js +2 -2
- package/dist/overlays/recipeRegistry.js.map +1 -1
- package/dist/overlays/runtime/anchor/resolve.js +1 -1
- package/dist/overlays/runtime/anchor/resolve.js.map +1 -1
- package/dist/overlays/runtime/index.d.ts +7 -7
- package/dist/overlays/runtime/index.js +7 -7
- package/dist/overlays/runtime/overlay/highlight.js +39 -39
- package/dist/overlays/runtime/overlay/highlight.js.map +1 -1
- package/dist/overlays/runtime/overlay/modal.js +5 -5
- package/dist/overlays/runtime/overlay/modal.js.map +1 -1
- package/dist/overlays/runtime/overlay/root.js +1 -1
- package/dist/overlays/runtime/overlay/runner.js +70 -23
- package/dist/overlays/runtime/overlay/runner.js.map +1 -1
- package/dist/overlays/runtime/overlay/tooltip.d.ts +1 -1
- package/dist/overlays/runtime/overlay/tooltip.js +10 -10
- package/dist/overlays/runtime/overlay/tooltip.js.map +1 -1
- package/dist/overlays/runtime/utils/dom.js +4 -1
- package/dist/overlays/runtime/utils/dom.js.map +1 -1
- package/dist/overlays/schema.d.ts +98 -98
- package/dist/overlays/schema.js +12 -8
- package/dist/overlays/schema.js.map +1 -1
- package/dist/react.d.ts +7 -7
- package/dist/react.js +4 -4
- package/dist/react.js.map +1 -1
- package/dist/render/RenderContext.d.ts +2 -2
- package/dist/render/RenderContext.js +5 -5
- package/dist/render/RenderContext.js.map +1 -1
- package/dist/render/index.d.ts +3 -3
- package/dist/render/index.js +1 -1
- package/dist/render/types.d.ts +4 -4
- package/dist/runtime.d.ts +110 -0
- package/dist/runtime.js +206 -0
- package/dist/runtime.js.map +1 -0
- package/dist/smart-canvas.esm.js +155 -78
- package/dist/smart-canvas.esm.js.map +4 -4
- package/dist/smart-canvas.js +44390 -37343
- package/dist/smart-canvas.js.map +4 -4
- package/dist/smart-canvas.min.js +156 -78
- package/dist/smart-canvas.min.js.map +4 -4
- package/dist/state/StateStore.d.ts +41 -0
- package/dist/state/StateStore.js +170 -0
- package/dist/state/StateStore.js.map +1 -0
- package/dist/state/helpers/cooldowns.d.ts +7 -0
- package/dist/state/helpers/cooldowns.js +31 -0
- package/dist/state/helpers/cooldowns.js.map +1 -0
- package/dist/state/helpers/dismissals.d.ts +7 -0
- package/dist/state/helpers/dismissals.js +34 -0
- package/dist/state/helpers/dismissals.js.map +1 -0
- package/dist/state/helpers/frequency.d.ts +8 -0
- package/dist/state/helpers/frequency.js +43 -0
- package/dist/state/helpers/frequency.js.map +1 -0
- package/dist/state/index.d.ts +7 -0
- package/dist/state/index.js +7 -0
- package/dist/state/index.js.map +1 -0
- package/dist/state/schema.d.ts +49 -0
- package/dist/state/schema.js +25 -0
- package/dist/state/schema.js.map +1 -0
- package/dist/state/types.d.ts +137 -0
- package/dist/state/types.js +9 -0
- package/dist/state/types.js.map +1 -0
- package/dist/store/example.d.ts +1 -0
- package/dist/store/example.js +43 -0
- package/dist/store/example.js.map +1 -0
- package/dist/store/mini-effector.d.ts +46 -0
- package/dist/store/mini-effector.js +88 -0
- package/dist/store/mini-effector.js.map +1 -0
- package/dist/surfaces/Surfaces.d.ts +11 -0
- package/dist/surfaces/Surfaces.js +361 -0
- package/dist/surfaces/Surfaces.js.map +1 -0
- package/dist/surfaces/index.d.ts +9 -0
- package/dist/surfaces/index.js +12 -0
- package/dist/surfaces/index.js.map +1 -0
- package/dist/surfaces/positioning.d.ts +50 -0
- package/dist/surfaces/positioning.js +228 -0
- package/dist/surfaces/positioning.js.map +1 -0
- package/dist/surfaces/types.d.ts +167 -0
- package/dist/surfaces/types.js +23 -0
- package/dist/surfaces/types.js.map +1 -0
- package/dist/telemetry/adapters/noop.d.ts +12 -0
- package/dist/telemetry/adapters/noop.js +42 -0
- package/dist/telemetry/adapters/noop.js.map +1 -0
- package/dist/telemetry/adapters/posthog.d.ts +9 -3
- package/dist/telemetry/adapters/posthog.js +36 -14
- package/dist/telemetry/adapters/posthog.js.map +1 -1
- package/dist/telemetry/index.d.ts +4 -3
- package/dist/telemetry/index.js +3 -2
- package/dist/telemetry/index.js.map +1 -1
- package/dist/telemetry/registry.d.ts +2 -2
- package/dist/telemetry/registry.js +4 -2
- package/dist/telemetry/registry.js.map +1 -1
- package/dist/telemetry/types.d.ts +1 -1
- package/dist/theme/ThemeProvider.d.ts +2 -2
- package/dist/theme/ThemeProvider.js +21 -21
- package/dist/theme/ThemeProvider.js.map +1 -1
- package/dist/theme/defaultTheme.d.ts +2 -2
- package/dist/theme/defaultTheme.js +111 -111
- package/dist/theme/defaultTheme.js.map +1 -1
- package/dist/theme/extractHostTheme.d.ts +1 -1
- package/dist/theme/extractHostTheme.js +42 -44
- package/dist/theme/extractHostTheme.js.map +1 -1
- package/dist/theme/index.d.ts +5 -5
- package/dist/theme/index.js +3 -3
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/types.d.ts +2 -2
- package/dist/token.d.ts +2 -0
- package/dist/token.js +3 -6
- package/dist/token.js.map +1 -1
- package/dist/types-only.d.ts +32 -0
- package/dist/types-only.js +11 -0
- package/dist/types-only.js.map +1 -0
- package/dist/types.d.ts +95 -54
- package/dist/types.js +15 -2
- package/dist/types.js.map +1 -1
- package/dist/version.d.ts +13 -0
- package/dist/version.js +14 -0
- package/dist/version.js.map +1 -0
- package/dist/widgets/WidgetRegistry.d.ts +139 -0
- package/dist/widgets/WidgetRegistry.js +182 -0
- package/dist/widgets/WidgetRegistry.js.map +1 -0
- package/dist/widgets/index.d.ts +7 -0
- package/dist/widgets/index.js +7 -0
- package/dist/widgets/index.js.map +1 -0
- package/package.json +27 -11
- package/schema/canvas-config.schema.json +666 -227
- package/schema/runtime-context.schema.json +127 -0
package/CAPABILITIES.md
CHANGED
|
@@ -1,400 +1,872 @@
|
|
|
1
1
|
# SmartCanvas SDK Capabilities
|
|
2
2
|
|
|
3
|
-
This document describes all available operations and capabilities of the SmartCanvas SDK for DOM manipulation and
|
|
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.
|
|
4
6
|
|
|
5
7
|
## Table of Contents
|
|
6
8
|
- [Overview](#overview)
|
|
7
|
-
- [
|
|
8
|
-
- [
|
|
9
|
-
- [
|
|
10
|
-
- [
|
|
9
|
+
- [Quick Start Example](#quick-start-example)
|
|
10
|
+
- [Adaptive Packages](#adaptive-packages)
|
|
11
|
+
- [Surfaces](#surfaces)
|
|
12
|
+
- [Anchor Resolution](#anchor-resolution)
|
|
13
|
+
- [Decision Strategies](#decision-strategies)
|
|
11
14
|
- [Best Practices](#best-practices)
|
|
12
15
|
|
|
16
|
+
---
|
|
17
|
+
|
|
13
18
|
## Overview
|
|
14
19
|
|
|
15
|
-
The SmartCanvas SDK
|
|
16
|
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
20
|
+
The SmartCanvas SDK provides three main systems for modifying web pages:
|
|
21
|
+
|
|
22
|
+
1. **ActionEngine** - Unified execution layer for interventions (highlight, tooltip, badge, DOM modifications)
|
|
23
|
+
2. **Surfaces** - Managed surface system for rendering UI into named slots
|
|
24
|
+
3. **Runtime** - Context, events, state, and decision management
|
|
25
|
+
|
|
26
|
+
All modifications are reversible and publish events to the EventBus for tracking.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Quick Start Example
|
|
31
|
+
|
|
32
|
+
### Simple Header Text Change
|
|
33
|
+
|
|
34
|
+
Change a page header when the element is visible:
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"id": "welcome-header-change",
|
|
39
|
+
"activation": {
|
|
40
|
+
"routes": { "include": ["/", "/home"] },
|
|
41
|
+
"strategy": {
|
|
42
|
+
"type": "rules",
|
|
43
|
+
"rules": [
|
|
44
|
+
{
|
|
45
|
+
"conditions": [
|
|
46
|
+
{ "type": "anchor_visible", "anchorId": "h1.hero-title", "state": "visible" }
|
|
47
|
+
],
|
|
48
|
+
"value": true
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
"default": false
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"actions": [
|
|
55
|
+
{
|
|
56
|
+
"kind": "set_text",
|
|
57
|
+
"anchorId": "h1.hero-title",
|
|
58
|
+
"text": "Welcome to Our New Experience"
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Adaptive Packages
|
|
67
|
+
|
|
68
|
+
The SDK includes the following adaptive packages, each providing specific capabilities:
|
|
69
|
+
|
|
70
|
+
- [@syntrologie/adapt-chatbot](#syntrologieadapt-chatbot)
|
|
71
|
+
- [@syntrologie/adapt-content](#syntrologieadapt-content)
|
|
72
|
+
- [@syntrologie/adapt-faq](#syntrologieadapt-faq)
|
|
73
|
+
- [@syntrologie/adapt-gamification](#syntrologieadapt-gamification)
|
|
74
|
+
- [@syntrologie/adapt-nav](#syntrologieadapt-nav)
|
|
75
|
+
- [@syntrologie/adapt-navigation](#syntrologieadapt-navigation)
|
|
76
|
+
- [@syntrologie/adapt-overlays](#syntrologieadapt-overlays)
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
# @syntrologie/adapt-chatbot
|
|
81
|
+
|
|
82
|
+
AI chat assistant widget with action execution capabilities.
|
|
83
|
+
|
|
84
|
+
## Widgets
|
|
85
|
+
|
|
86
|
+
### adaptive-chatbot:assistant
|
|
87
|
+
|
|
88
|
+
Mounts an AI-powered chat assistant that connects to a backend LLM endpoint. The assistant can parse action instructions from the LLM response and execute them on the page via the runtime's ActionEngine.
|
|
19
89
|
|
|
20
|
-
|
|
90
|
+
**Tile config example:**
|
|
21
91
|
|
|
22
|
-
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"id": "chatbot-assistant",
|
|
95
|
+
"title": "Ask Assistant",
|
|
96
|
+
"content": {
|
|
97
|
+
"type": "custom",
|
|
98
|
+
"component": "adaptive-chatbot:assistant",
|
|
99
|
+
"props": {
|
|
100
|
+
"backendUrl": "/api/chat/message",
|
|
101
|
+
"mlflowRunId": "abc123",
|
|
102
|
+
"greeting": "Hi! How can I help?"
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"size": "full",
|
|
106
|
+
"defaultExpanded": true
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Config Props
|
|
111
|
+
|
|
112
|
+
| Property | Type | Required | Default | Description |
|
|
113
|
+
| ------------- | ------ | -------- | --------------------- | --------------------------------------- |
|
|
114
|
+
| `backendUrl` | string | Yes | — | URL for the chat API endpoint |
|
|
115
|
+
| `mlflowRunId` | string | No | — | MLflow run ID for experiment tracking |
|
|
116
|
+
| `greeting` | string | No | "Hi! How can I help?" | Initial greeting message |
|
|
117
|
+
| `maxHistory` | number | No | 20 | Max messages sent as history to backend |
|
|
118
|
+
|
|
119
|
+
## Action Execution
|
|
120
|
+
|
|
121
|
+
The chatbot does **not** define its own action kinds. Instead, it parses JSON action blocks from the LLM response and executes them via `runtime.actions.applyBatch()`. This means the chatbot can trigger any action kind registered in the runtime (highlights, tooltips, text changes, navigation, tours, etc.).
|
|
122
|
+
|
|
123
|
+
### LLM Response Format
|
|
124
|
+
|
|
125
|
+
The backend returns a text reply that may contain embedded JSON action blocks in fenced code blocks:
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
Here's what I found. Let me highlight the pricing section for you.
|
|
23
129
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
130
|
+
\`\`\`json
|
|
131
|
+
{"kind": "overlays:highlight", "anchorId": "pricing-section"}
|
|
132
|
+
\`\`\`
|
|
133
|
+
|
|
134
|
+
Is there anything else you'd like to know?
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
- JSON blocks with a `kind` field are extracted as actions and removed from display text
|
|
138
|
+
- JSON blocks without `kind` are left in the display text as-is
|
|
139
|
+
- Multiple action blocks can appear in a single response
|
|
140
|
+
- Actions with unknown kinds are silently ignored by the ActionEngine
|
|
141
|
+
|
|
142
|
+
## Authentication
|
|
143
|
+
|
|
144
|
+
The widget reads auth credentials from the browser:
|
|
145
|
+
|
|
146
|
+
- **JWT**: `stytch_session_jwt` cookie
|
|
147
|
+
- **Workspace ID**: `syntrologie_workspace_id` from localStorage
|
|
148
|
+
- Headers: `Authorization: Bearer <jwt>` + `X-Workspace-Id: <id>`
|
|
149
|
+
|
|
150
|
+
## Events
|
|
151
|
+
|
|
152
|
+
| Event | Props | Description |
|
|
153
|
+
| ------------------------- | ---------------------------- | --------------------------------- |
|
|
154
|
+
| `chatbot.actions_applied` | `count`, `kinds[]`, `tileId` | Emitted when actions are executed |
|
|
29
155
|
|
|
30
|
-
### 2. **moderate**
|
|
31
|
-
- **Purpose**: Allows limited modifications to existing elements
|
|
32
|
-
- **Allowed**: Everything from additive + title attribute
|
|
33
|
-
- **Not Allowed**: Text changes, color changes
|
|
34
|
-
- **Use When**: Adding accessibility improvements or metadata
|
|
35
156
|
|
|
36
|
-
|
|
37
|
-
- **Purpose**: Full control over content modification
|
|
38
|
-
- **Allowed**: Everything including setText, background/text colors
|
|
39
|
-
- **Required For**: Changing headlines, CTAs, or any text content
|
|
40
|
-
- **Use When**: A/B testing copy variations or personalizing content
|
|
157
|
+
---
|
|
41
158
|
|
|
42
|
-
|
|
159
|
+
# @syntrologie/adapt-content
|
|
43
160
|
|
|
44
|
-
|
|
161
|
+
DOM content modification capabilities for text, attributes, styles, HTML, and classes.
|
|
162
|
+
|
|
163
|
+
## Actions
|
|
164
|
+
|
|
165
|
+
### set_text
|
|
166
|
+
|
|
167
|
+
Replaces the text content of an element.
|
|
168
|
+
|
|
169
|
+
| Property | Type | Required | Description |
|
|
170
|
+
| ---------- | ------------ | -------- | ---------------- |
|
|
171
|
+
| `kind` | `"set_text"` | Yes | Action type |
|
|
172
|
+
| `anchorId` | string | Yes | Element selector |
|
|
173
|
+
| `text` | string | Yes | New text content |
|
|
45
174
|
|
|
46
|
-
### 1. CSS Selector
|
|
47
175
|
```json
|
|
48
176
|
{
|
|
49
|
-
"
|
|
50
|
-
"
|
|
177
|
+
"kind": "set_text",
|
|
178
|
+
"anchorId": "h1.hero-title",
|
|
179
|
+
"text": "Start Your Free Trial Today"
|
|
51
180
|
}
|
|
52
181
|
```
|
|
53
|
-
- **Use**: Standard CSS selectors
|
|
54
|
-
- **Example**: `"h1"`, `".button-primary"`, `"#hero-section h2"`
|
|
55
182
|
|
|
56
|
-
###
|
|
183
|
+
### set_attr
|
|
184
|
+
|
|
185
|
+
Sets an HTML attribute on an element.
|
|
186
|
+
|
|
187
|
+
| Property | Type | Required | Description |
|
|
188
|
+
| ---------- | ------------ | -------- | ---------------- |
|
|
189
|
+
| `kind` | `"set_attr"` | Yes | Action type |
|
|
190
|
+
| `anchorId` | string | Yes | Element selector |
|
|
191
|
+
| `attr` | string | Yes | Attribute name |
|
|
192
|
+
| `value` | string | Yes | Attribute value |
|
|
193
|
+
|
|
194
|
+
**Blocked attributes:** Event handlers (`onclick`, `onerror`, etc.) are not allowed.
|
|
195
|
+
|
|
57
196
|
```json
|
|
58
197
|
{
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
198
|
+
"kind": "set_attr",
|
|
199
|
+
"anchorId": "#signup-form",
|
|
200
|
+
"attr": "data-experiment",
|
|
201
|
+
"value": "signup-v2"
|
|
62
202
|
}
|
|
63
203
|
```
|
|
64
|
-
- **Use**: Target elements by data attributes
|
|
65
|
-
- **Example**: Finds `<div data-testid="hero-banner">`
|
|
66
|
-
- **Note**: If value is omitted, matches any element with that data attribute
|
|
67
204
|
|
|
68
|
-
###
|
|
205
|
+
### set_style
|
|
206
|
+
|
|
207
|
+
Sets inline CSS styles on an element.
|
|
208
|
+
|
|
209
|
+
| Property | Type | Required | Description |
|
|
210
|
+
| ---------- | ------------- | -------- | ------------------------ |
|
|
211
|
+
| `kind` | `"set_style"` | Yes | Action type |
|
|
212
|
+
| `anchorId` | string | Yes | Element selector |
|
|
213
|
+
| `styles` | object | Yes | CSS property/value pairs |
|
|
214
|
+
|
|
69
215
|
```json
|
|
70
216
|
{
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
217
|
+
"kind": "set_style",
|
|
218
|
+
"anchorId": ".hero-section",
|
|
219
|
+
"styles": {
|
|
220
|
+
"background-color": "#1e40af",
|
|
221
|
+
"padding": "2rem"
|
|
222
|
+
}
|
|
74
223
|
}
|
|
75
224
|
```
|
|
76
|
-
- **Use**: Target accessible elements
|
|
77
|
-
- **Example**: Finds `<button role="button" aria-label="Submit">`
|
|
78
|
-
- **Note**: Can use role, label, or both
|
|
79
225
|
|
|
80
|
-
###
|
|
226
|
+
### insert_html
|
|
227
|
+
|
|
228
|
+
Inserts HTML content relative to an element.
|
|
229
|
+
|
|
230
|
+
| Property | Type | Required | Description |
|
|
231
|
+
| ---------- | --------------- | -------- | ----------------------------------------------------------- |
|
|
232
|
+
| `kind` | `"insert_html"` | Yes | Action type |
|
|
233
|
+
| `anchorId` | string | Yes | Element selector |
|
|
234
|
+
| `html` | string | Yes | HTML content (sanitized) |
|
|
235
|
+
| `position` | string | Yes | `"before"`, `"after"`, `"prepend"`, `"append"`, `"replace"` |
|
|
236
|
+
|
|
237
|
+
**Positions:**
|
|
238
|
+
|
|
239
|
+
- `before` - Insert before the element
|
|
240
|
+
- `after` - Insert after the element
|
|
241
|
+
- `prepend` - Insert inside, before first child
|
|
242
|
+
- `append` - Insert inside, after last child
|
|
243
|
+
- `replace` - Replace the entire element
|
|
244
|
+
|
|
81
245
|
```json
|
|
82
246
|
{
|
|
83
|
-
"
|
|
84
|
-
"
|
|
247
|
+
"kind": "insert_html",
|
|
248
|
+
"anchorId": ".cta-button",
|
|
249
|
+
"html": "<span class=\"badge\">NEW</span>",
|
|
250
|
+
"position": "append"
|
|
85
251
|
}
|
|
86
252
|
```
|
|
87
|
-
- **Use**: Direct element reference (programmatic use only)
|
|
88
|
-
- **Note**: Cannot be serialized in JSON configs
|
|
89
253
|
|
|
90
|
-
|
|
254
|
+
### add_class
|
|
255
|
+
|
|
256
|
+
Adds a CSS class to an element.
|
|
257
|
+
|
|
258
|
+
| Property | Type | Required | Description |
|
|
259
|
+
| ----------- | ------------- | -------- | ----------------- |
|
|
260
|
+
| `kind` | `"add_class"` | Yes | Action type |
|
|
261
|
+
| `anchorId` | string | Yes | Element selector |
|
|
262
|
+
| `className` | string | Yes | Class name to add |
|
|
91
263
|
|
|
92
|
-
### 1. setText
|
|
93
|
-
**Purpose**: Replace the text content of an element
|
|
94
264
|
```json
|
|
95
265
|
{
|
|
96
|
-
"kind": "
|
|
97
|
-
"
|
|
266
|
+
"kind": "add_class",
|
|
267
|
+
"anchorId": ".pricing-card",
|
|
268
|
+
"className": "highlighted"
|
|
98
269
|
}
|
|
99
270
|
```
|
|
100
|
-
- **Requires**: `tier: "surgical"`
|
|
101
|
-
- **Effect**: Replaces all text content in the element
|
|
102
|
-
- **Common Use**: A/B testing headlines, CTAs, product descriptions
|
|
103
|
-
- **Note**: Removes all child elements, use carefully
|
|
104
271
|
|
|
105
|
-
###
|
|
106
|
-
|
|
272
|
+
### remove_class
|
|
273
|
+
|
|
274
|
+
Removes a CSS class from an element.
|
|
275
|
+
|
|
276
|
+
| Property | Type | Required | Description |
|
|
277
|
+
| ----------- | ---------------- | -------- | -------------------- |
|
|
278
|
+
| `kind` | `"remove_class"` | Yes | Action type |
|
|
279
|
+
| `anchorId` | string | Yes | Element selector |
|
|
280
|
+
| `className` | string | Yes | Class name to remove |
|
|
281
|
+
|
|
107
282
|
```json
|
|
108
283
|
{
|
|
109
|
-
"kind": "
|
|
110
|
-
"
|
|
111
|
-
"
|
|
112
|
-
"important": true // optional, adds !important
|
|
284
|
+
"kind": "remove_class",
|
|
285
|
+
"anchorId": ".pricing-card",
|
|
286
|
+
"className": "hidden"
|
|
113
287
|
}
|
|
114
288
|
```
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
###
|
|
126
|
-
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
# @syntrologie/adapt-faq
|
|
294
|
+
|
|
295
|
+
FAQ accordion widget with conditional item visibility.
|
|
296
|
+
|
|
297
|
+
## Actions
|
|
298
|
+
|
|
299
|
+
### mount_faq
|
|
300
|
+
|
|
301
|
+
Mounts an FAQ accordion widget to a surface slot.
|
|
302
|
+
|
|
303
|
+
| Property | Type | Required | Description |
|
|
304
|
+
| -------------- | ------------- | -------- | -------------------------------------------------------- |
|
|
305
|
+
| `kind` | `"mount_faq"` | Yes | Action type |
|
|
306
|
+
| `slot` | string | Yes | Target slot (e.g., `"drawer_right"`, `"overlay_center"`) |
|
|
307
|
+
| `config.title` | string | No | Widget title |
|
|
308
|
+
| `config.items` | array | Yes | FAQ items (see below) |
|
|
309
|
+
|
|
310
|
+
### FAQ Item Schema
|
|
311
|
+
|
|
312
|
+
Each item in the `items` array:
|
|
313
|
+
|
|
314
|
+
| Property | Type | Required | Description |
|
|
315
|
+
| ---------- | ---------------- | -------- | ----------------------------------- |
|
|
316
|
+
| `question` | string | Yes | The question text |
|
|
317
|
+
| `answer` | string | Yes | The answer text (supports markdown) |
|
|
318
|
+
| `showWhen` | DecisionStrategy | No | Conditional visibility strategy |
|
|
319
|
+
|
|
127
320
|
```json
|
|
128
321
|
{
|
|
129
|
-
"kind": "
|
|
130
|
-
"
|
|
322
|
+
"kind": "mount_faq",
|
|
323
|
+
"slot": "drawer_right",
|
|
324
|
+
"config": {
|
|
325
|
+
"title": "Frequently Asked Questions",
|
|
326
|
+
"items": [
|
|
327
|
+
{
|
|
328
|
+
"question": "How do I get started?",
|
|
329
|
+
"answer": "Sign up for a free account and follow our quickstart guide."
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
"question": "What payment methods do you accept?",
|
|
333
|
+
"answer": "We accept all major credit cards and PayPal.",
|
|
334
|
+
"showWhen": {
|
|
335
|
+
"type": "rules",
|
|
336
|
+
"rules": [
|
|
337
|
+
{
|
|
338
|
+
"conditions": [{ "type": "page_url", "pattern": "/pricing*" }],
|
|
339
|
+
"value": true
|
|
340
|
+
}
|
|
341
|
+
],
|
|
342
|
+
"default": false
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
]
|
|
346
|
+
}
|
|
131
347
|
}
|
|
132
348
|
```
|
|
133
|
-
- **Requirement**: Class must start with `syntro-`, `sc-`, or `sx-`
|
|
134
|
-
- **Common Use**: Apply pre-defined styles, mark elements
|
|
135
|
-
- **Note**: Won't add duplicate classes
|
|
136
349
|
|
|
137
|
-
|
|
138
|
-
|
|
350
|
+
## Compositional Pattern
|
|
351
|
+
|
|
352
|
+
The FAQ widget supports **per-item conditional visibility** using `showWhen` strategies. This allows different FAQ items to appear based on:
|
|
353
|
+
|
|
354
|
+
- Current page/route
|
|
355
|
+
- User segment or state
|
|
356
|
+
- Viewport conditions
|
|
357
|
+
- Any other DecisionStrategy condition
|
|
358
|
+
|
|
359
|
+
Items without `showWhen` are always visible.
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
---
|
|
363
|
+
|
|
364
|
+
# @syntrologie/adapt-gamification
|
|
365
|
+
|
|
366
|
+
Gamification capabilities including badges, points, and leaderboards.
|
|
367
|
+
|
|
368
|
+
## Actions
|
|
369
|
+
|
|
370
|
+
### mount_gamification
|
|
371
|
+
|
|
372
|
+
Mounts gamification UI elements.
|
|
373
|
+
|
|
374
|
+
| Property | Type | Required | Description |
|
|
375
|
+
| -------- | ---------------------- | -------- | -------------------------- |
|
|
376
|
+
| `kind` | `"mount_gamification"` | Yes | Action type |
|
|
377
|
+
| `slot` | string | Yes | Target slot |
|
|
378
|
+
| `config` | object | Yes | Gamification configuration |
|
|
379
|
+
|
|
380
|
+
### Configuration Schema
|
|
381
|
+
|
|
382
|
+
| Property | Type | Required | Default | Description |
|
|
383
|
+
| ----------------------------- | ------- | -------- | ------- | ---------------------- |
|
|
384
|
+
| `badges` | array | No | `[]` | Badge definitions |
|
|
385
|
+
| `points.enabled` | boolean | No | `false` | Enable points system |
|
|
386
|
+
| `points.multiplier` | number | No | `1` | Points multiplier |
|
|
387
|
+
| `leaderboard.enabled` | boolean | No | `false` | Show leaderboard |
|
|
388
|
+
| `leaderboard.refreshInterval` | number | No | `60000` | Refresh interval in ms |
|
|
389
|
+
|
|
390
|
+
### Badge Schema
|
|
391
|
+
|
|
392
|
+
| Property | Type | Required | Description |
|
|
393
|
+
| -------------------- | ------ | -------- | ----------------------------- |
|
|
394
|
+
| `id` | string | Yes | Unique badge identifier |
|
|
395
|
+
| `name` | string | Yes | Display name |
|
|
396
|
+
| `icon` | string | Yes | Icon identifier |
|
|
397
|
+
| `description` | string | No | Badge description |
|
|
398
|
+
| `trigger.event` | string | Yes | Event that triggers the badge |
|
|
399
|
+
| `trigger.conditions` | array | No | Additional conditions |
|
|
400
|
+
|
|
139
401
|
```json
|
|
140
402
|
{
|
|
141
|
-
"kind": "
|
|
142
|
-
"
|
|
403
|
+
"kind": "mount_gamification",
|
|
404
|
+
"slot": "overlay_corner_br",
|
|
405
|
+
"config": {
|
|
406
|
+
"badges": [
|
|
407
|
+
{
|
|
408
|
+
"id": "first-purchase",
|
|
409
|
+
"name": "First Purchase",
|
|
410
|
+
"icon": "shopping-cart",
|
|
411
|
+
"description": "Made your first purchase!",
|
|
412
|
+
"trigger": {
|
|
413
|
+
"event": "purchase_completed"
|
|
414
|
+
}
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
"id": "explorer",
|
|
418
|
+
"name": "Explorer",
|
|
419
|
+
"icon": "compass",
|
|
420
|
+
"description": "Visited 10 different pages",
|
|
421
|
+
"trigger": {
|
|
422
|
+
"event": "page_view",
|
|
423
|
+
"conditions": [
|
|
424
|
+
{ "type": "session_metric", "key": "unique_pages", "operator": ">=", "threshold": 10 }
|
|
425
|
+
]
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
],
|
|
429
|
+
"points": {
|
|
430
|
+
"enabled": true,
|
|
431
|
+
"multiplier": 2
|
|
432
|
+
},
|
|
433
|
+
"leaderboard": {
|
|
434
|
+
"enabled": true,
|
|
435
|
+
"refreshInterval": 30000
|
|
436
|
+
}
|
|
437
|
+
}
|
|
143
438
|
}
|
|
144
439
|
```
|
|
145
|
-
- **Requirement**: Can only remove prefixed classes
|
|
146
|
-
- **Common Use**: Revealing hidden elements, removing states
|
|
147
|
-
- **Note**: Safe if class doesn't exist
|
|
148
440
|
|
|
149
|
-
|
|
150
|
-
|
|
441
|
+
## Use Cases
|
|
442
|
+
|
|
443
|
+
- **Engagement rewards**: Award badges for completing onboarding steps
|
|
444
|
+
- **Loyalty programs**: Track points for purchases or interactions
|
|
445
|
+
- **Social proof**: Display leaderboards to encourage participation
|
|
446
|
+
- **Progress tracking**: Show achievement progress to motivate users
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
# @syntrologie/adapt-nav
|
|
452
|
+
|
|
453
|
+
Navigation link list widget with conditional item visibility.
|
|
454
|
+
|
|
455
|
+
## Actions
|
|
456
|
+
|
|
457
|
+
### mount_nav
|
|
458
|
+
|
|
459
|
+
Mounts a navigation link list widget to a surface slot.
|
|
460
|
+
|
|
461
|
+
| Property | Type | Required | Description |
|
|
462
|
+
| -------------- | ------------- | -------- | ---------------------------------------------------------- |
|
|
463
|
+
| `kind` | `"mount_nav"` | Yes | Action type |
|
|
464
|
+
| `slot` | string | Yes | Target slot (e.g., `"drawer_left"`, `"inline:{anchorId}"`) |
|
|
465
|
+
| `config.title` | string | No | Widget title |
|
|
466
|
+
| `config.items` | array | Yes | Navigation items (see below) |
|
|
467
|
+
|
|
468
|
+
### Nav Item Schema
|
|
469
|
+
|
|
470
|
+
Each item in the `items` array:
|
|
471
|
+
|
|
472
|
+
| Property | Type | Required | Description |
|
|
473
|
+
| ---------- | ---------------- | -------- | ------------------------------- |
|
|
474
|
+
| `label` | string | Yes | Link text |
|
|
475
|
+
| `href` | string | Yes | Link destination |
|
|
476
|
+
| `icon` | string | No | Icon identifier |
|
|
477
|
+
| `showWhen` | DecisionStrategy | No | Conditional visibility strategy |
|
|
478
|
+
|
|
151
479
|
```json
|
|
152
480
|
{
|
|
153
|
-
"kind": "
|
|
154
|
-
"
|
|
155
|
-
"
|
|
481
|
+
"kind": "mount_nav",
|
|
482
|
+
"slot": "drawer_left",
|
|
483
|
+
"config": {
|
|
484
|
+
"title": "Quick Links",
|
|
485
|
+
"items": [
|
|
486
|
+
{
|
|
487
|
+
"label": "Dashboard",
|
|
488
|
+
"href": "/dashboard",
|
|
489
|
+
"icon": "home"
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
"label": "Admin Settings",
|
|
493
|
+
"href": "/admin",
|
|
494
|
+
"icon": "settings",
|
|
495
|
+
"showWhen": {
|
|
496
|
+
"type": "rules",
|
|
497
|
+
"rules": [
|
|
498
|
+
{
|
|
499
|
+
"conditions": [{ "type": "state_equals", "key": "user.role", "value": "admin" }],
|
|
500
|
+
"value": true
|
|
501
|
+
}
|
|
502
|
+
],
|
|
503
|
+
"default": false
|
|
504
|
+
}
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
"label": "Upgrade",
|
|
508
|
+
"href": "/pricing",
|
|
509
|
+
"icon": "sparkles",
|
|
510
|
+
"showWhen": {
|
|
511
|
+
"type": "rules",
|
|
512
|
+
"rules": [
|
|
513
|
+
{
|
|
514
|
+
"conditions": [{ "type": "state_equals", "key": "user.plan", "value": "free" }],
|
|
515
|
+
"value": true
|
|
516
|
+
}
|
|
517
|
+
],
|
|
518
|
+
"default": false
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
]
|
|
522
|
+
}
|
|
156
523
|
}
|
|
157
524
|
```
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
-
|
|
165
|
-
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
525
|
+
|
|
526
|
+
## Compositional Pattern
|
|
527
|
+
|
|
528
|
+
The nav widget supports **per-item conditional visibility** using `showWhen` strategies. This allows different navigation items to appear based on:
|
|
529
|
+
|
|
530
|
+
- User role or permissions
|
|
531
|
+
- Subscription tier
|
|
532
|
+
- Feature flags
|
|
533
|
+
- Any other DecisionStrategy condition
|
|
534
|
+
|
|
535
|
+
Items without `showWhen` are always visible.
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
---
|
|
539
|
+
|
|
540
|
+
# @syntrologie/adapt-navigation
|
|
541
|
+
|
|
542
|
+
Navigation capabilities for scrolling and URL navigation.
|
|
543
|
+
|
|
544
|
+
## Actions
|
|
545
|
+
|
|
546
|
+
### scroll_to
|
|
547
|
+
|
|
548
|
+
Scrolls the viewport to bring an element into view.
|
|
549
|
+
|
|
550
|
+
| Property | Type | Required | Default | Description |
|
|
551
|
+
| ---------- | ------------- | -------- | ----------- | ------------------------------------------- |
|
|
552
|
+
| `kind` | `"scroll_to"` | Yes | | Action type |
|
|
553
|
+
| `anchorId` | string | Yes | | Element selector |
|
|
554
|
+
| `behavior` | string | No | `"smooth"` | `"smooth"`, `"instant"`, `"auto"` |
|
|
555
|
+
| `block` | string | No | `"center"` | `"start"`, `"center"`, `"end"`, `"nearest"` |
|
|
556
|
+
| `inline` | string | No | `"nearest"` | `"start"`, `"center"`, `"end"`, `"nearest"` |
|
|
557
|
+
|
|
169
558
|
```json
|
|
170
559
|
{
|
|
171
|
-
"kind": "
|
|
172
|
-
"
|
|
560
|
+
"kind": "scroll_to",
|
|
561
|
+
"anchorId": "#pricing-section",
|
|
562
|
+
"behavior": "smooth",
|
|
563
|
+
"block": "start"
|
|
173
564
|
}
|
|
174
565
|
```
|
|
175
|
-
- **Effect**: Inserts after existing children
|
|
176
|
-
- **Common Use**: Adding badges, icons, supplementary content
|
|
177
|
-
- **Note**: HTML is sanitized for safety
|
|
178
566
|
|
|
179
|
-
###
|
|
180
|
-
|
|
567
|
+
### navigate
|
|
568
|
+
|
|
569
|
+
Navigates to a URL.
|
|
570
|
+
|
|
571
|
+
| Property | Type | Required | Default | Description |
|
|
572
|
+
| -------- | ------------ | -------- | --------- | ----------------------- |
|
|
573
|
+
| `kind` | `"navigate"` | Yes | | Action type |
|
|
574
|
+
| `url` | string | Yes | | Destination URL |
|
|
575
|
+
| `target` | string | No | `"_self"` | `"_self"` or `"_blank"` |
|
|
576
|
+
|
|
181
577
|
```json
|
|
182
578
|
{
|
|
183
|
-
"kind": "
|
|
184
|
-
"
|
|
579
|
+
"kind": "navigate",
|
|
580
|
+
"url": "/signup?ref=banner",
|
|
581
|
+
"target": "_self"
|
|
185
582
|
}
|
|
186
583
|
```
|
|
187
|
-
- **Effect**: Inserts before existing children
|
|
188
|
-
- **Common Use**: Adding icons, prefixes, notifications
|
|
189
|
-
- **Note**: HTML is sanitized for safety
|
|
190
584
|
|
|
191
|
-
|
|
192
|
-
|
|
585
|
+
**Note:** `javascript:` URLs are blocked for security.
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
---
|
|
589
|
+
|
|
590
|
+
# @syntrologie/adapt-overlays
|
|
591
|
+
|
|
592
|
+
Visual overlay capabilities including highlights, tooltips, badges, and pulse animations.
|
|
593
|
+
|
|
594
|
+
## Actions
|
|
595
|
+
|
|
596
|
+
### highlight
|
|
597
|
+
|
|
598
|
+
Creates a spotlight effect around an element with a scrim overlay.
|
|
599
|
+
|
|
600
|
+
| Property | Type | Required | Default | Description |
|
|
601
|
+
| -------------------- | ------------- | -------- | ----------- | --------------------------------------- |
|
|
602
|
+
| `kind` | `"highlight"` | Yes | | Action type |
|
|
603
|
+
| `anchorId` | string | Yes | | Element selector |
|
|
604
|
+
| `style.color` | string | No | `"#5b8cff"` | Ring color |
|
|
605
|
+
| `style.scrimOpacity` | number | No | `0.55` | Backdrop opacity 0-1 (set to 0 to hide) |
|
|
606
|
+
| `style.paddingPx` | number | No | `12` | Space around element |
|
|
607
|
+
| `style.radiusPx` | number | No | `12` | Ring corner radius |
|
|
608
|
+
|
|
193
609
|
```json
|
|
194
610
|
{
|
|
195
|
-
"kind": "
|
|
196
|
-
"
|
|
197
|
-
"
|
|
611
|
+
"kind": "highlight",
|
|
612
|
+
"anchorId": "#signup-button",
|
|
613
|
+
"style": {
|
|
614
|
+
"color": "#22c55e",
|
|
615
|
+
"scrimOpacity": 0.4
|
|
616
|
+
}
|
|
198
617
|
}
|
|
199
618
|
```
|
|
200
|
-
- **Positions**:
|
|
201
|
-
- `beforebegin`: Before the element itself
|
|
202
|
-
- `afterbegin`: Inside element, before first child
|
|
203
|
-
- `beforeend`: Inside element, after last child
|
|
204
|
-
- `afterend`: After the element itself
|
|
205
|
-
- **Common Use**: Complex insertions, wrapping elements
|
|
206
|
-
- **Note**: More flexible than append/prepend
|
|
207
619
|
|
|
208
|
-
|
|
620
|
+
### tooltip
|
|
621
|
+
|
|
622
|
+
Shows a tooltip near an element with optional title, body, and CTA.
|
|
209
623
|
|
|
210
|
-
|
|
624
|
+
| Property | Type | Required | Default | Description |
|
|
625
|
+
| -------------------- | ----------- | -------- | ------------- | ----------------------------------- |
|
|
626
|
+
| `kind` | `"tooltip"` | Yes | | Action type |
|
|
627
|
+
| `anchorId` | string | Yes | | Element selector |
|
|
628
|
+
| `content.title` | string | No | | Tooltip heading |
|
|
629
|
+
| `content.body` | string | Yes | | Tooltip text |
|
|
630
|
+
| `content.cta.label` | string | No | | CTA button text |
|
|
631
|
+
| `content.cta.action` | Action | No | | Action to execute on CTA click |
|
|
632
|
+
| `trigger` | string | No | `"immediate"` | `"immediate"`, `"hover"`, `"click"` |
|
|
633
|
+
| `placement` | string | No | `"top"` | See placement options below |
|
|
211
634
|
|
|
212
|
-
|
|
213
|
-
Floating information bubbles that appear near target elements.
|
|
635
|
+
**Placement options:** `top`, `top-start`, `top-end`, `bottom`, `bottom-start`, `bottom-end`, `left`, `left-start`, `left-end`, `right`, `right-start`, `right-end`
|
|
214
636
|
|
|
215
637
|
```json
|
|
216
638
|
{
|
|
217
639
|
"kind": "tooltip",
|
|
218
|
-
"
|
|
219
|
-
"anchor": {
|
|
220
|
-
"by": "css",
|
|
221
|
-
"value": ".pricing-card"
|
|
222
|
-
},
|
|
640
|
+
"anchorId": "#pricing-toggle",
|
|
223
641
|
"content": {
|
|
224
|
-
"title": "
|
|
225
|
-
"body": "
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
"ctaButtons": [
|
|
230
|
-
{
|
|
231
|
-
"label": "Learn More",
|
|
232
|
-
"actionId": "learn_more_click"
|
|
642
|
+
"title": "Save 20%",
|
|
643
|
+
"body": "Switch to annual billing to save on your subscription.",
|
|
644
|
+
"cta": {
|
|
645
|
+
"label": "Switch Now",
|
|
646
|
+
"action": { "kind": "navigate", "url": "/billing?annual=true" }
|
|
233
647
|
}
|
|
234
|
-
|
|
235
|
-
"
|
|
236
|
-
|
|
237
|
-
"closeButton": true
|
|
238
|
-
}
|
|
648
|
+
},
|
|
649
|
+
"placement": "bottom",
|
|
650
|
+
"trigger": "immediate"
|
|
239
651
|
}
|
|
240
652
|
```
|
|
241
653
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
- `ctaButtons`: Array of action buttons (optional)
|
|
253
|
-
- `dismiss.onEsc`: Close on Escape key
|
|
254
|
-
- `dismiss.closeButton`: Show X button
|
|
255
|
-
- `dismiss.timeoutMs`: Auto-dismiss after milliseconds
|
|
256
|
-
|
|
257
|
-
### Highlights
|
|
258
|
-
Visual emphasis rings that draw attention to elements.
|
|
654
|
+
### badge
|
|
655
|
+
|
|
656
|
+
Adds a small badge indicator near an element.
|
|
657
|
+
|
|
658
|
+
| Property | Type | Required | Default | Description |
|
|
659
|
+
| ---------- | --------- | -------- | ------------- | -------------------------------------------------------------- |
|
|
660
|
+
| `kind` | `"badge"` | Yes | | Action type |
|
|
661
|
+
| `anchorId` | string | Yes | | Element selector |
|
|
662
|
+
| `text` | string | Yes | | Badge text (e.g., "NEW", "3") |
|
|
663
|
+
| `position` | string | No | `"top-right"` | `"top-left"`, `"top-right"`, `"bottom-left"`, `"bottom-right"` |
|
|
259
664
|
|
|
260
665
|
```json
|
|
261
666
|
{
|
|
262
|
-
"kind": "
|
|
263
|
-
"
|
|
264
|
-
"
|
|
265
|
-
|
|
266
|
-
"value": ".hero-cta button"
|
|
267
|
-
},
|
|
268
|
-
"copy": "Click here to get started",
|
|
269
|
-
"ring": {
|
|
270
|
-
"paddingPx": 8,
|
|
271
|
-
"radiusPx": 4
|
|
272
|
-
},
|
|
273
|
-
"ringColor": "#3b82f6",
|
|
274
|
-
"scrim": {
|
|
275
|
-
"opacity": 0.5
|
|
276
|
-
},
|
|
277
|
-
"blocking": true,
|
|
278
|
-
"dismiss": {
|
|
279
|
-
"onClickOutside": true,
|
|
280
|
-
"onEsc": true
|
|
281
|
-
}
|
|
667
|
+
"kind": "badge",
|
|
668
|
+
"anchorId": "#inbox-icon",
|
|
669
|
+
"text": "5",
|
|
670
|
+
"position": "top-right"
|
|
282
671
|
}
|
|
283
672
|
```
|
|
284
673
|
|
|
285
|
-
|
|
286
|
-
- `kind`: Must be `"highlight"`
|
|
287
|
-
- `id`: Unique identifier for tracking
|
|
288
|
-
- `anchor`: Element selector (same as patches)
|
|
289
|
-
- `copy`: Text to display near the highlight (optional)
|
|
290
|
-
- `ring.paddingPx`: Space between element and ring
|
|
291
|
-
- `ring.radiusPx`: Corner radius of ring
|
|
292
|
-
- `ringColor`: Color of the highlight ring
|
|
293
|
-
- `scrim.opacity`: Opacity of background dimming (0-1)
|
|
294
|
-
- `blocking`: If true, blocks interaction outside highlight
|
|
295
|
-
- `dismiss.onClickOutside`: Close when clicking outside
|
|
296
|
-
- `dismiss.onEsc`: Close on Escape key
|
|
297
|
-
- `dismiss.timeoutMs`: Auto-dismiss after milliseconds
|
|
674
|
+
### pulse
|
|
298
675
|
|
|
299
|
-
|
|
676
|
+
Adds a pulsing animation to draw attention.
|
|
300
677
|
|
|
301
|
-
|
|
678
|
+
| Property | Type | Required | Default | Description |
|
|
679
|
+
| ---------- | --------- | -------- | ------- | ------------------------ |
|
|
680
|
+
| `kind` | `"pulse"` | Yes | | Action type |
|
|
681
|
+
| `anchorId` | string | Yes | | Element selector |
|
|
682
|
+
| `duration` | number | No | `2000` | Animation duration in ms |
|
|
302
683
|
|
|
303
684
|
```json
|
|
304
685
|
{
|
|
305
|
-
"
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
"routes": ["/dashboard", "/home"],
|
|
309
|
-
"steps": [
|
|
310
|
-
{ "kind": "tooltip", ... },
|
|
311
|
-
{ "kind": "highlight", ... }
|
|
312
|
-
]
|
|
313
|
-
}
|
|
686
|
+
"kind": "pulse",
|
|
687
|
+
"anchorId": ".notification-bell",
|
|
688
|
+
"duration": 3000
|
|
314
689
|
}
|
|
315
690
|
```
|
|
316
691
|
|
|
317
|
-
|
|
318
|
-
- `version`: Version number for tracking changes
|
|
319
|
-
- `routes`: URL paths where this recipe applies (optional)
|
|
320
|
-
- `steps`: Array of tooltip and highlight steps
|
|
692
|
+
### modal
|
|
321
693
|
|
|
322
|
-
|
|
694
|
+
Shows a centered modal dialog with optional CTA buttons.
|
|
323
695
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
696
|
+
| Property | Type | Required | Default | Description |
|
|
697
|
+
| --------------------- | ------------------ | -------- | ------- | ------------------------------------------------------------- |
|
|
698
|
+
| `kind` | `"overlays:modal"` | Yes | | Action type |
|
|
699
|
+
| `content.title` | string | No | | Modal heading |
|
|
700
|
+
| `content.body` | string | Yes | | Modal text |
|
|
701
|
+
| `size` | string | No | `"md"` | `"sm"`, `"md"`, `"lg"` |
|
|
702
|
+
| `blocking` | boolean | No | `false` | Block page interaction |
|
|
703
|
+
| `scrim.opacity` | number | No | `0.6` | Backdrop opacity 0-1 |
|
|
704
|
+
| `dismiss.onEsc` | boolean | No | `true` | Close on Escape key |
|
|
705
|
+
| `dismiss.closeButton` | boolean | No | `true` | Show close button |
|
|
706
|
+
| `dismiss.timeoutMs` | number | No | | Auto-close after timeout |
|
|
707
|
+
| `ctaButtons` | array | No | | Array of CTA buttons |
|
|
708
|
+
| `waitFor` | string | No | | When to complete: `"dismissed"`, `"cta-click"`, `"timeout:N"` |
|
|
328
709
|
|
|
329
|
-
|
|
330
|
-
- Start with the lowest tier that works
|
|
331
|
-
- Only use `surgical` when modifying text
|
|
332
|
-
- Document why a specific tier is needed
|
|
710
|
+
**CTA Button properties:**
|
|
333
711
|
|
|
334
|
-
|
|
335
|
-
-
|
|
336
|
-
-
|
|
337
|
-
- Test operation combinations
|
|
712
|
+
- `label`: Button text
|
|
713
|
+
- `actionId`: Identifier for the action
|
|
714
|
+
- `primary`: Whether this is a primary button (default: false)
|
|
338
715
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
716
|
+
```json
|
|
717
|
+
{
|
|
718
|
+
"kind": "overlays:modal",
|
|
719
|
+
"content": {
|
|
720
|
+
"title": "Welcome!",
|
|
721
|
+
"body": "Thanks for signing up. Let us show you around."
|
|
722
|
+
},
|
|
723
|
+
"size": "md",
|
|
724
|
+
"ctaButtons": [
|
|
725
|
+
{ "label": "Skip", "actionId": "skip" },
|
|
726
|
+
{ "label": "Start Tour", "actionId": "start", "primary": true }
|
|
727
|
+
],
|
|
728
|
+
"waitFor": "cta-click"
|
|
729
|
+
}
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
---
|
|
734
|
+
|
|
735
|
+
|
|
736
|
+
---
|
|
737
|
+
|
|
738
|
+
## Surfaces
|
|
739
|
+
|
|
740
|
+
Surfaces are named slots where widgets can be mounted. Use the `mount_widget` action to render content into a surface slot.
|
|
741
|
+
|
|
742
|
+
### Static Slots
|
|
743
|
+
|
|
744
|
+
Fixed-position slots for common UI patterns:
|
|
745
|
+
|
|
746
|
+
| Slot | Position | Use Case |
|
|
747
|
+
|------|----------|----------|
|
|
748
|
+
| `drawer_right` | Right edge, full height | Settings panels, details |
|
|
749
|
+
| `drawer_left` | Left edge, full height | Navigation, filters |
|
|
750
|
+
| `drawer_bottom` | Bottom edge, full width | Action sheets, keyboards |
|
|
751
|
+
| `overlay_center` | Centered modal | Dialogs, confirmations |
|
|
752
|
+
| `overlay_corner_br` | Bottom-right corner | Chat widgets, help |
|
|
753
|
+
| `overlay_corner_bl` | Bottom-left corner | Notifications |
|
|
754
|
+
| `toast_top` | Top center | Success/error messages |
|
|
755
|
+
| `toast_bottom` | Bottom center | Snackbar notifications |
|
|
756
|
+
|
|
757
|
+
### Dynamic Slots
|
|
758
|
+
|
|
759
|
+
Slots that position content relative to anchors:
|
|
760
|
+
|
|
761
|
+
- **Inline Slots**: Render content inside an anchor element. Format: `inline:{anchorId}`
|
|
762
|
+
- **Adjacent Slots**: Render content positioned near an anchor element. Format: `adjacent:{anchorId}`
|
|
763
|
+
|
|
764
|
+
---
|
|
765
|
+
|
|
766
|
+
## Anchor Resolution
|
|
767
|
+
|
|
768
|
+
The `anchorId` field identifies which DOM element to target. Multiple formats are supported:
|
|
769
|
+
|
|
770
|
+
### CSS Selectors
|
|
771
|
+
|
|
772
|
+
| Format | Example | Matches |
|
|
773
|
+
|--------|---------|---------|
|
|
774
|
+
| Class | `".hero-title"` | `<h1 class="hero-title">` |
|
|
775
|
+
| ID | `"#signup-button"` | `<button id="signup-button">` |
|
|
776
|
+
| Attribute | `"[data-testid='cta']"` | `<button data-testid="cta">` |
|
|
777
|
+
| Tag + class | `"h1.page-title"` | `<h1 class="page-title">` |
|
|
778
|
+
| Nested | `".card .title"` | `<div class="title">` inside `.card` |
|
|
779
|
+
|
|
780
|
+
### Data Attributes (Recommended)
|
|
781
|
+
|
|
782
|
+
For stability, add `data-syntro-anchor` attributes to target elements:
|
|
783
|
+
|
|
784
|
+
```html
|
|
785
|
+
<h1 data-syntro-anchor="hero-title">Welcome</h1>
|
|
786
|
+
```
|
|
787
|
+
|
|
788
|
+
Then reference by the anchor name:
|
|
789
|
+
|
|
790
|
+
```json
|
|
791
|
+
{ "anchorId": "hero-title" }
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
---
|
|
343
795
|
|
|
344
|
-
|
|
345
|
-
- All HTML is sanitized to prevent XSS
|
|
346
|
-
- Classes must be prefixed to avoid conflicts
|
|
347
|
-
- Test across different browsers and devices
|
|
796
|
+
## Decision Strategies
|
|
348
797
|
|
|
349
|
-
|
|
798
|
+
Control when adaptives activate using `DecisionStrategy`:
|
|
799
|
+
|
|
800
|
+
### Rules Strategy
|
|
350
801
|
|
|
351
802
|
```json
|
|
352
803
|
{
|
|
353
|
-
"
|
|
354
|
-
"
|
|
804
|
+
"type": "rules",
|
|
805
|
+
"rules": [
|
|
355
806
|
{
|
|
356
|
-
"
|
|
357
|
-
|
|
358
|
-
"
|
|
359
|
-
"
|
|
360
|
-
|
|
361
|
-
"
|
|
362
|
-
"operations": [
|
|
363
|
-
{
|
|
364
|
-
"kind": "setText",
|
|
365
|
-
"text": "Start Your Free Trial Today"
|
|
366
|
-
},
|
|
367
|
-
{
|
|
368
|
-
"kind": "addClass",
|
|
369
|
-
"className": "syntro-tested"
|
|
370
|
-
}
|
|
371
|
-
]
|
|
372
|
-
},
|
|
373
|
-
{
|
|
374
|
-
"id": "cta_enhancement",
|
|
375
|
-
"anchor": {
|
|
376
|
-
"by": "data",
|
|
377
|
-
"key": "testid",
|
|
378
|
-
"value": "primary-cta"
|
|
379
|
-
},
|
|
380
|
-
"tier": "additive",
|
|
381
|
-
"operations": [
|
|
382
|
-
{
|
|
383
|
-
"kind": "setStyle",
|
|
384
|
-
"prop": "boxShadow",
|
|
385
|
-
"value": "0 4px 6px rgba(0,0,0,0.1)"
|
|
386
|
-
},
|
|
387
|
-
{
|
|
388
|
-
"kind": "append",
|
|
389
|
-
"html": "<span class='syntro-arrow'>→</span>"
|
|
390
|
-
}
|
|
391
|
-
]
|
|
807
|
+
"conditions": [
|
|
808
|
+
{ "type": "page_url", "pattern": "/pricing*" },
|
|
809
|
+
{ "type": "viewport", "minWidth": 768 },
|
|
810
|
+
{ "type": "dismissed", "key": "promo", "inverted": true }
|
|
811
|
+
],
|
|
812
|
+
"value": true
|
|
392
813
|
}
|
|
393
|
-
]
|
|
814
|
+
],
|
|
815
|
+
"default": false
|
|
394
816
|
}
|
|
395
817
|
```
|
|
396
818
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
819
|
+
### Condition Types
|
|
820
|
+
|
|
821
|
+
| Type | Parameters | Description |
|
|
822
|
+
|------|------------|-------------|
|
|
823
|
+
| `page_url` | `pattern` | URL matches glob pattern |
|
|
824
|
+
| `route` | `routeId` | Route ID matches |
|
|
825
|
+
| `anchor_visible` | `anchorId`, `state` | Anchor visibility |
|
|
826
|
+
| `event_occurred` | `eventName`, `withinMs?` | Recent event |
|
|
827
|
+
| `state_equals` | `key`, `value` | State matches |
|
|
828
|
+
| `viewport` | `minWidth?`, `maxWidth?`, `minHeight?`, `maxHeight?` | Viewport size |
|
|
829
|
+
| `session_metric` | `key`, `operator`, `threshold` | Metric comparison |
|
|
830
|
+
| `dismissed` | `key`, `inverted?` | Dismissal check |
|
|
831
|
+
| `cooldown_active` | `key`, `inverted?` | Cooldown check |
|
|
832
|
+
| `frequency_limit` | `key`, `limit`, `inverted?` | Frequency cap |
|
|
833
|
+
|
|
834
|
+
---
|
|
835
|
+
|
|
836
|
+
## Best Practices
|
|
837
|
+
|
|
838
|
+
### 1. Choose the Right Action Type
|
|
839
|
+
|
|
840
|
+
| Goal | Action |
|
|
841
|
+
|------|--------|
|
|
842
|
+
| Change text | `set_text` |
|
|
843
|
+
| Add visual indicator | `badge`, `pulse` |
|
|
844
|
+
| Show help text | `tooltip` |
|
|
845
|
+
| Draw attention | `highlight` |
|
|
846
|
+
| Add content | `insert_html` |
|
|
847
|
+
| Navigate user | `scroll_to`, `navigate` |
|
|
848
|
+
| Show UI panel | `mount_widget` + Surfaces |
|
|
849
|
+
|
|
850
|
+
### 2. Anchor Selection
|
|
851
|
+
|
|
852
|
+
- **Prefer stable selectors:** `[data-testid]`, IDs over classes
|
|
853
|
+
- **Test across states:** Element may not exist on all pages
|
|
854
|
+
- **Be specific:** Avoid selectors matching multiple elements
|
|
855
|
+
|
|
856
|
+
### 3. Reversibility
|
|
857
|
+
|
|
858
|
+
- Always store handles if you need to revert
|
|
859
|
+
- Use `applyBatch` for related changes (atomic rollback)
|
|
860
|
+
- Clean up on route changes
|
|
861
|
+
|
|
862
|
+
### 4. Performance
|
|
863
|
+
|
|
864
|
+
- Batch related actions together
|
|
865
|
+
- Use `validate()` to check actions before applying
|
|
866
|
+
- Avoid applying many actions simultaneously
|
|
867
|
+
|
|
868
|
+
### 5. Events
|
|
869
|
+
|
|
870
|
+
- Listen for `action.failed` to handle errors
|
|
871
|
+
- Track `action.applied` for analytics
|
|
872
|
+
- Use EventBus for cross-adaptive coordination
|