@syntrologie/runtime-sdk 0.2.21 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CAPABILITIES.md +944 -440
- package/README.md +395 -66
- 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 +47 -51
- 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/SmartCanvasPortal.js.map +1 -1
- package/dist/actions/ActionEngine.d.ts +11 -0
- package/dist/actions/ActionEngine.js +272 -0
- package/dist/actions/ActionEngine.js.map +1 -0
- package/dist/actions/executors/index.d.ts +116 -0
- package/dist/actions/executors/index.js +240 -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 +577 -0
- package/dist/actions/validation.js.map +1 -0
- package/dist/adaptives/adaptive-chatbot/index.js +9 -0
- package/dist/adaptives/adaptive-chatbot/index.js.map +7 -0
- package/dist/adaptives/adaptive-content/index.js +2 -0
- package/dist/adaptives/adaptive-content/index.js.map +7 -0
- package/dist/adaptives/adaptive-faq/index.js +11 -0
- package/dist/adaptives/adaptive-faq/index.js.map +7 -0
- package/dist/adaptives/adaptive-gamification/index.js +2 -0
- package/dist/adaptives/adaptive-gamification/index.js.map +7 -0
- package/dist/adaptives/adaptive-nav/index.js +11 -0
- package/dist/adaptives/adaptive-nav/index.js.map +7 -0
- package/dist/adaptives/adaptive-overlays/index.js +91 -0
- package/dist/adaptives/adaptive-overlays/index.js.map +7 -0
- package/dist/antiFlicker.js +1 -1
- package/dist/api.d.ts +40 -26
- package/dist/api.js +87 -60
- package/dist/api.js.map +1 -1
- package/dist/apps/AppContext.d.ts +31 -0
- package/dist/apps/AppContext.js +91 -0
- package/dist/apps/AppContext.js.map +1 -0
- package/dist/apps/AppLoader.d.ts +85 -0
- package/dist/apps/AppLoader.js +282 -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/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/index.d.ts +14 -0
- package/dist/apps/index.js +16 -0
- package/dist/apps/index.js.map +1 -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 +42 -44
- 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 -30
- 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 +73 -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 +68 -64
- 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 +32 -8
- package/dist/bootstrap.js +218 -102
- package/dist/bootstrap.js.map +1 -1
- package/dist/components/ShadowCanvasOverlay.d.ts +6 -6
- package/dist/components/ShadowCanvasOverlay.js +156 -118
- package/dist/components/ShadowCanvasOverlay.js.map +1 -1
- package/dist/components/TileCard.d.ts +5 -5
- package/dist/components/TileCard.js +205 -154
- package/dist/components/TileCard.js.map +1 -1
- package/dist/components/TileWheel.d.ts +3 -3
- package/dist/components/TileWheel.js +29 -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 +7 -3
- package/dist/configFetcher.js +70 -29
- package/dist/configFetcher.js.map +1 -1
- package/dist/context/ContextManager.d.ts +3 -3
- package/dist/context/ContextManager.js +19 -18
- package/dist/context/ContextManager.js.map +1 -1
- package/dist/context/index.d.ts +4 -4
- package/dist/context/index.js +3 -3
- package/dist/context/schema.d.ts +9 -9
- package/dist/context/schema.js +2 -2
- package/dist/context/schema.js.map +1 -1
- package/dist/decisions/engine.d.ts +5 -5
- package/dist/decisions/engine.js +13 -13
- package/dist/decisions/engine.js.map +1 -1
- package/dist/decisions/index.d.ts +6 -6
- package/dist/decisions/index.js +5 -5
- package/dist/decisions/schema.d.ts +131 -131
- package/dist/decisions/schema.js +21 -21
- package/dist/decisions/schema.js.map +1 -1
- package/dist/decisions/strategies/rules.d.ts +1 -1
- package/dist/decisions/strategies/rules.js +24 -24
- package/dist/decisions/strategies/rules.js.map +1 -1
- package/dist/decisions/strategies/score.d.ts +1 -1
- package/dist/decisions/strategies/score.js +3 -3
- package/dist/decisions/types.d.ts +19 -19
- 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 +19 -7
- package/dist/editorLoader.js +154 -97
- package/dist/editorLoader.js.map +1 -1
- package/dist/events/EventBus.d.ts +3 -3
- package/dist/events/EventBus.js +5 -7
- package/dist/events/EventBus.js.map +1 -1
- package/dist/events/index.d.ts +6 -6
- package/dist/events/index.js +5 -5
- package/dist/events/normalizers/canvas.d.ts +2 -2
- package/dist/events/normalizers/canvas.js +3 -3
- package/dist/events/normalizers/canvas.js.map +1 -1
- package/dist/events/normalizers/posthog.d.ts +25 -1
- package/dist/events/normalizers/posthog.js +35 -27
- package/dist/events/normalizers/posthog.js.map +1 -1
- package/dist/events/schema.d.ts +13 -13
- package/dist/events/schema.js +3 -3
- package/dist/events/schema.js.map +1 -1
- package/dist/events/types.d.ts +7 -1
- package/dist/events/types.js +29 -21
- package/dist/events/types.js.map +1 -1
- package/dist/experiments/adapters/growthbook.d.ts +5 -4
- package/dist/experiments/adapters/growthbook.js +14 -6
- 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 +10 -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 +25 -3
- package/dist/fetchers/experimentsFetcher.js +55 -8
- 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/mergeConfigs.d.ts +29 -0
- package/dist/fetchers/mergeConfigs.js +38 -0
- package/dist/fetchers/mergeConfigs.js.map +1 -0
- 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 +5 -9
- package/dist/hooks/useShadowCanvasConfig.js +7 -5
- 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 -27
- package/dist/index.js +51 -24
- 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 +13 -15
- package/dist/overlays/fetcher.js.map +1 -1
- 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 +88 -28
- 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 +13 -15
- 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 +146 -146
- 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 +32 -8
- package/dist/runtime.js +109 -13
- package/dist/runtime.js.map +1 -1
- package/dist/smart-canvas.esm.js +144 -55
- package/dist/smart-canvas.esm.js.map +4 -4
- package/dist/smart-canvas.js +14688 -11455
- package/dist/smart-canvas.js.map +4 -4
- package/dist/smart-canvas.min.js +145 -55
- package/dist/smart-canvas.min.js.map +4 -4
- package/dist/state/StateStore.d.ts +1 -7
- package/dist/state/StateStore.js +15 -9
- package/dist/state/StateStore.js.map +1 -1
- package/dist/state/helpers/cooldowns.d.ts +1 -1
- package/dist/state/helpers/cooldowns.js +1 -1
- package/dist/state/helpers/dismissals.d.ts +1 -1
- package/dist/state/helpers/dismissals.js +1 -1
- package/dist/state/helpers/frequency.d.ts +1 -1
- package/dist/state/helpers/frequency.js +1 -1
- package/dist/state/index.d.ts +4 -4
- package/dist/state/index.js +3 -3
- package/dist/state/schema.d.ts +1 -1
- package/dist/state/schema.js +1 -1
- 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 +8 -2
- 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 -9
- 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 +4 -5
- package/dist/theme/defaultTheme.js +127 -118
- package/dist/theme/defaultTheme.js.map +1 -1
- package/dist/theme/extractHostTheme.d.ts +1 -1
- package/dist/theme/extractHostTheme.js +43 -45
- 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 +89 -56
- package/dist/types.js +14 -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 +145 -0
- package/dist/widgets/WidgetRegistry.js +191 -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 +35 -15
- package/schema/canvas-config.schema.json +488 -254
- package/schema/runtime-context.schema.json +1 -5
package/CAPABILITIES.md
CHANGED
|
@@ -1,611 +1,1115 @@
|
|
|
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
|
-
- [
|
|
11
|
-
- [
|
|
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)
|
|
12
14
|
- [Best Practices](#best-practices)
|
|
13
15
|
|
|
16
|
+
---
|
|
17
|
+
|
|
14
18
|
## Overview
|
|
15
19
|
|
|
16
|
-
The SmartCanvas SDK
|
|
17
|
-
- An anchor selector to find the target element
|
|
18
|
-
- One or more operations to apply
|
|
19
|
-
- A policy tier that controls what modifications are allowed
|
|
20
|
+
The SmartCanvas SDK provides three main systems for modifying web pages:
|
|
20
21
|
|
|
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
|
|
22
25
|
|
|
23
|
-
|
|
26
|
+
All modifications are reversible and publish events to the EventBus for tracking.
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
- **Purpose**: Safest tier - only add new elements and classes
|
|
27
|
-
- **Allowed**: Adding HTML, adding prefixed classes, safe styles
|
|
28
|
-
- **Not Allowed**: Modifying existing text or attributes
|
|
29
|
-
- **Use When**: Adding badges, tooltips, or decorative elements
|
|
28
|
+
---
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
- **Purpose**: Allows limited modifications to existing elements
|
|
33
|
-
- **Allowed**: Everything from additive + title attribute
|
|
34
|
-
- **Not Allowed**: Text changes, color changes
|
|
35
|
-
- **Use When**: Adding accessibility improvements or metadata
|
|
30
|
+
## Quick Start Example
|
|
36
31
|
|
|
37
|
-
###
|
|
38
|
-
- **Purpose**: Full control over content modification
|
|
39
|
-
- **Allowed**: Everything including setText, background/text colors
|
|
40
|
-
- **Required For**: Changing headlines, CTAs, or any text content
|
|
41
|
-
- **Use When**: A/B testing copy variations or personalizing content
|
|
32
|
+
### Simple Header Text Change
|
|
42
33
|
|
|
43
|
-
|
|
34
|
+
Change a page header when the element is visible:
|
|
44
35
|
|
|
45
|
-
|
|
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-overlays](#syntrologieadapt-overlays)
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
# @syntrologie/adapt-chatbot
|
|
80
|
+
|
|
81
|
+
AI chat assistant widget with action execution capabilities.
|
|
82
|
+
|
|
83
|
+
## Widgets
|
|
84
|
+
|
|
85
|
+
### adaptive-chatbot:assistant
|
|
86
|
+
|
|
87
|
+
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.
|
|
88
|
+
|
|
89
|
+
**Tile config example:**
|
|
46
90
|
|
|
47
|
-
### 1. CSS Selector
|
|
48
91
|
```json
|
|
49
92
|
{
|
|
50
|
-
"
|
|
51
|
-
"
|
|
93
|
+
"id": "chatbot-assistant",
|
|
94
|
+
"title": "Ask Assistant",
|
|
95
|
+
"content": {
|
|
96
|
+
"type": "custom",
|
|
97
|
+
"component": "adaptive-chatbot:assistant",
|
|
98
|
+
"props": {
|
|
99
|
+
"backendUrl": "/api/chat/message",
|
|
100
|
+
"mlflowRunId": "abc123",
|
|
101
|
+
"greeting": "Hi! How can I help?"
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"size": "full",
|
|
105
|
+
"defaultExpanded": true
|
|
52
106
|
}
|
|
53
107
|
```
|
|
54
|
-
- **Use**: Standard CSS selectors
|
|
55
|
-
- **Example**: `"h1"`, `".button-primary"`, `"#hero-section h2"`
|
|
56
108
|
|
|
57
|
-
###
|
|
109
|
+
### Config Props
|
|
110
|
+
|
|
111
|
+
| Property | Type | Required | Default | Description |
|
|
112
|
+
| ------------- | ------ | -------- | --------------------- | --------------------------------------- |
|
|
113
|
+
| `backendUrl` | string | Yes | — | URL for the chat API endpoint |
|
|
114
|
+
| `mlflowRunId` | string | No | — | MLflow run ID for experiment tracking |
|
|
115
|
+
| `greeting` | string | No | "Hi! How can I help?" | Initial greeting message |
|
|
116
|
+
| `maxHistory` | number | No | 20 | Max messages sent as history to backend |
|
|
117
|
+
|
|
118
|
+
## Action Execution
|
|
119
|
+
|
|
120
|
+
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.).
|
|
121
|
+
|
|
122
|
+
### LLM Response Format
|
|
123
|
+
|
|
124
|
+
The backend returns a text reply that may contain embedded JSON action blocks in fenced code blocks:
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
Here's what I found. Let me highlight the pricing section for you.
|
|
128
|
+
|
|
129
|
+
\`\`\`json
|
|
130
|
+
{"kind": "overlays:highlight", "anchorId": "pricing-section"}
|
|
131
|
+
\`\`\`
|
|
132
|
+
|
|
133
|
+
Is there anything else you'd like to know?
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
- JSON blocks with a `kind` field are extracted as actions and removed from display text
|
|
137
|
+
- JSON blocks without `kind` are left in the display text as-is
|
|
138
|
+
- Multiple action blocks can appear in a single response
|
|
139
|
+
- Actions with unknown kinds are silently ignored by the ActionEngine
|
|
140
|
+
|
|
141
|
+
## Authentication
|
|
142
|
+
|
|
143
|
+
The widget reads auth credentials from the browser:
|
|
144
|
+
|
|
145
|
+
- **JWT**: `stytch_session_jwt` cookie
|
|
146
|
+
- **Workspace ID**: `syntrologie_workspace_id` from localStorage
|
|
147
|
+
- Headers: `Authorization: Bearer <jwt>` + `X-Workspace-Id: <id>`
|
|
148
|
+
|
|
149
|
+
## Events
|
|
150
|
+
|
|
151
|
+
| Event | Props | Description |
|
|
152
|
+
| ------------------------- | ---------------------------- | --------------------------------- |
|
|
153
|
+
| `chatbot.actions_applied` | `count`, `kinds[]`, `tileId` | Emitted when actions are executed |
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
# @syntrologie/adapt-content
|
|
159
|
+
|
|
160
|
+
DOM content modification capabilities for text, attributes, styles, HTML, and classes.
|
|
161
|
+
|
|
162
|
+
## Actions
|
|
163
|
+
|
|
164
|
+
### set_text
|
|
165
|
+
|
|
166
|
+
Replaces the text content of an element.
|
|
167
|
+
|
|
168
|
+
| Property | Type | Required | Description |
|
|
169
|
+
| ---------- | ------------ | -------- | ---------------- |
|
|
170
|
+
| `kind` | `"set_text"` | Yes | Action type |
|
|
171
|
+
| `anchorId` | string | Yes | Element selector |
|
|
172
|
+
| `text` | string | Yes | New text content |
|
|
173
|
+
|
|
58
174
|
```json
|
|
59
175
|
{
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
176
|
+
"kind": "set_text",
|
|
177
|
+
"anchorId": "h1.hero-title",
|
|
178
|
+
"text": "Start Your Free Trial Today"
|
|
63
179
|
}
|
|
64
180
|
```
|
|
65
|
-
- **Use**: Target elements by data attributes
|
|
66
|
-
- **Example**: Finds `<div data-testid="hero-banner">`
|
|
67
|
-
- **Note**: If value is omitted, matches any element with that data attribute
|
|
68
181
|
|
|
69
|
-
###
|
|
182
|
+
### set_attr
|
|
183
|
+
|
|
184
|
+
Sets an HTML attribute on an element.
|
|
185
|
+
|
|
186
|
+
| Property | Type | Required | Description |
|
|
187
|
+
| ---------- | ------------ | -------- | ---------------- |
|
|
188
|
+
| `kind` | `"set_attr"` | Yes | Action type |
|
|
189
|
+
| `anchorId` | string | Yes | Element selector |
|
|
190
|
+
| `attr` | string | Yes | Attribute name |
|
|
191
|
+
| `value` | string | Yes | Attribute value |
|
|
192
|
+
|
|
193
|
+
**Blocked attributes:** Event handlers (`onclick`, `onerror`, etc.) are not allowed.
|
|
194
|
+
|
|
70
195
|
```json
|
|
71
196
|
{
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
197
|
+
"kind": "set_attr",
|
|
198
|
+
"anchorId": "#signup-form",
|
|
199
|
+
"attr": "data-experiment",
|
|
200
|
+
"value": "signup-v2"
|
|
75
201
|
}
|
|
76
202
|
```
|
|
77
|
-
- **Use**: Target accessible elements
|
|
78
|
-
- **Example**: Finds `<button role="button" aria-label="Submit">`
|
|
79
|
-
- **Note**: Can use role, label, or both
|
|
80
203
|
|
|
81
|
-
###
|
|
204
|
+
### set_style
|
|
205
|
+
|
|
206
|
+
Sets inline CSS styles on an element.
|
|
207
|
+
|
|
208
|
+
| Property | Type | Required | Description |
|
|
209
|
+
| ---------- | ------------- | -------- | ------------------------ |
|
|
210
|
+
| `kind` | `"set_style"` | Yes | Action type |
|
|
211
|
+
| `anchorId` | string | Yes | Element selector |
|
|
212
|
+
| `styles` | object | Yes | CSS property/value pairs |
|
|
213
|
+
|
|
82
214
|
```json
|
|
83
215
|
{
|
|
84
|
-
"
|
|
85
|
-
"
|
|
216
|
+
"kind": "set_style",
|
|
217
|
+
"anchorId": ".hero-section",
|
|
218
|
+
"styles": {
|
|
219
|
+
"background-color": "#1e40af",
|
|
220
|
+
"padding": "2rem"
|
|
221
|
+
}
|
|
86
222
|
}
|
|
87
223
|
```
|
|
88
|
-
- **Use**: Direct element reference (programmatic use only)
|
|
89
|
-
- **Note**: Cannot be serialized in JSON configs
|
|
90
224
|
|
|
91
|
-
|
|
225
|
+
### insert_html
|
|
226
|
+
|
|
227
|
+
Inserts HTML content relative to an element.
|
|
228
|
+
|
|
229
|
+
| Property | Type | Required | Description |
|
|
230
|
+
| ---------- | --------------- | -------- | ----------------------------------------------------------- |
|
|
231
|
+
| `kind` | `"insert_html"` | Yes | Action type |
|
|
232
|
+
| `anchorId` | string | Yes | Element selector |
|
|
233
|
+
| `html` | string | Yes | HTML content (sanitized) |
|
|
234
|
+
| `position` | string | Yes | `"before"`, `"after"`, `"prepend"`, `"append"`, `"replace"` |
|
|
235
|
+
|
|
236
|
+
**Positions:**
|
|
237
|
+
|
|
238
|
+
- `before` - Insert before the element
|
|
239
|
+
- `after` - Insert after the element
|
|
240
|
+
- `prepend` - Insert inside, before first child
|
|
241
|
+
- `append` - Insert inside, after last child
|
|
242
|
+
- `replace` - Replace the entire element
|
|
92
243
|
|
|
93
|
-
### 1. setText
|
|
94
|
-
**Purpose**: Replace the text content of an element
|
|
95
244
|
```json
|
|
96
245
|
{
|
|
97
|
-
"kind": "
|
|
98
|
-
"
|
|
246
|
+
"kind": "insert_html",
|
|
247
|
+
"anchorId": ".cta-button",
|
|
248
|
+
"html": "<span class=\"badge\">NEW</span>",
|
|
249
|
+
"position": "append"
|
|
99
250
|
}
|
|
100
251
|
```
|
|
101
|
-
- **Requires**: `tier: "surgical"`
|
|
102
|
-
- **Effect**: Replaces all text content in the element
|
|
103
|
-
- **Common Use**: A/B testing headlines, CTAs, product descriptions
|
|
104
|
-
- **Note**: Removes all child elements, use carefully
|
|
105
252
|
|
|
106
|
-
###
|
|
107
|
-
|
|
253
|
+
### add_class
|
|
254
|
+
|
|
255
|
+
Adds a CSS class to an element.
|
|
256
|
+
|
|
257
|
+
| Property | Type | Required | Description |
|
|
258
|
+
| ----------- | ------------- | -------- | ----------------- |
|
|
259
|
+
| `kind` | `"add_class"` | Yes | Action type |
|
|
260
|
+
| `anchorId` | string | Yes | Element selector |
|
|
261
|
+
| `className` | string | Yes | Class name to add |
|
|
262
|
+
|
|
108
263
|
```json
|
|
109
264
|
{
|
|
110
|
-
"kind": "
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
"important": true // optional, adds !important
|
|
265
|
+
"kind": "add_class",
|
|
266
|
+
"anchorId": ".pricing-card",
|
|
267
|
+
"className": "highlighted"
|
|
114
268
|
}
|
|
115
269
|
```
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
**Purpose**: Add a CSS class to an element
|
|
270
|
+
|
|
271
|
+
### remove_class
|
|
272
|
+
|
|
273
|
+
Removes a CSS class from an element.
|
|
274
|
+
|
|
275
|
+
| Property | Type | Required | Description |
|
|
276
|
+
| ----------- | ---------------- | -------- | -------------------- |
|
|
277
|
+
| `kind` | `"remove_class"` | Yes | Action type |
|
|
278
|
+
| `anchorId` | string | Yes | Element selector |
|
|
279
|
+
| `className` | string | Yes | Class name to remove |
|
|
280
|
+
|
|
128
281
|
```json
|
|
129
282
|
{
|
|
130
|
-
"kind": "
|
|
131
|
-
"
|
|
283
|
+
"kind": "remove_class",
|
|
284
|
+
"anchorId": ".pricing-card",
|
|
285
|
+
"className": "hidden"
|
|
132
286
|
}
|
|
133
287
|
```
|
|
134
|
-
- **Requirement**: Class must start with `syntro-`, `sc-`, or `sx-`
|
|
135
|
-
- **Common Use**: Apply pre-defined styles, mark elements
|
|
136
|
-
- **Note**: Won't add duplicate classes
|
|
137
288
|
|
|
138
|
-
|
|
139
|
-
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
# @syntrologie/adapt-faq
|
|
293
|
+
|
|
294
|
+
Collapsible Q&A accordion with actions, rich content, feedback, and personalization. Supports mounting a full FAQ widget, scrolling to specific items, toggling item state, and dynamically updating the item list at runtime.
|
|
295
|
+
|
|
296
|
+
## Actions
|
|
297
|
+
|
|
298
|
+
### mount_faq
|
|
299
|
+
|
|
300
|
+
Mounts an FAQ accordion widget to a surface slot.
|
|
301
|
+
|
|
302
|
+
| Property | Type | Required | Description |
|
|
303
|
+
| ----------------------- | --------------------------------- | -------- | ------------------------------------------------------------------- |
|
|
304
|
+
| `kind` | `"mount_faq"` | Yes | Action type |
|
|
305
|
+
| `slot` | string | Yes | Target slot (e.g., `"drawer_right"`, `"overlay_center"`) |
|
|
306
|
+
| `config.title` | string | No | Widget title |
|
|
307
|
+
| `config.expandBehavior` | `"single"` \| `"multiple"` | No | Whether one or many items can be open at once (default: `"single"`) |
|
|
308
|
+
| `config.searchable` | boolean | No | Show a search/filter input (default: `false`) |
|
|
309
|
+
| `config.theme` | `"light"` \| `"dark"` \| `"auto"` | No | Color theme (default: `"auto"`) |
|
|
310
|
+
| `config.items` | array | Yes | FAQ items (see below) |
|
|
311
|
+
| `config.feedback` | boolean \| FeedbackConfig | No | Enable per-item feedback widget |
|
|
312
|
+
| `config.ordering` | OrderingStrategy | No | Item ordering strategy (default: `"static"`) |
|
|
313
|
+
| `config.injections` | InjectionRule[] | No | Dynamic item injection rules |
|
|
314
|
+
|
|
315
|
+
### FAQ Item Schema
|
|
316
|
+
|
|
317
|
+
Each item in the `items` array:
|
|
318
|
+
|
|
319
|
+
| Property | Type | Required | Description |
|
|
320
|
+
| ----------------------- | ------------------------ | -------- | ----------------------------------------------- |
|
|
321
|
+
| `kind` | `"faq:question"` | Yes | Compositional action type |
|
|
322
|
+
| `config.id` | string | Yes | Unique identifier for this question |
|
|
323
|
+
| `config.question` | string | Yes | The question text |
|
|
324
|
+
| `config.answer` | FAQAnswer | Yes | Answer content (string, rich HTML, or markdown) |
|
|
325
|
+
| `config.category` | string | No | Category for grouping items |
|
|
326
|
+
| `config.priority` | number | No | Priority weight for ordering |
|
|
327
|
+
| `config.answerStrategy` | AnswerStrategy | No | AI-generated answer configuration |
|
|
328
|
+
| `showWhen` | DecisionStrategy \| null | No | Conditional visibility strategy |
|
|
329
|
+
|
|
140
330
|
```json
|
|
141
331
|
{
|
|
142
|
-
"kind": "
|
|
143
|
-
"
|
|
332
|
+
"kind": "mount_faq",
|
|
333
|
+
"slot": "drawer_right",
|
|
334
|
+
"config": {
|
|
335
|
+
"title": "Frequently Asked Questions",
|
|
336
|
+
"expandBehavior": "single",
|
|
337
|
+
"searchable": true,
|
|
338
|
+
"theme": "auto",
|
|
339
|
+
"feedback": {
|
|
340
|
+
"style": "thumbs",
|
|
341
|
+
"prompt": "Was this helpful?"
|
|
342
|
+
},
|
|
343
|
+
"ordering": "priority",
|
|
344
|
+
"items": [
|
|
345
|
+
{
|
|
346
|
+
"kind": "faq:question",
|
|
347
|
+
"config": {
|
|
348
|
+
"id": "getting-started",
|
|
349
|
+
"question": "How do I get started?",
|
|
350
|
+
"answer": "Sign up for a free account and follow our quickstart guide.",
|
|
351
|
+
"category": "General",
|
|
352
|
+
"priority": 10
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
"kind": "faq:question",
|
|
357
|
+
"config": {
|
|
358
|
+
"id": "payment-methods",
|
|
359
|
+
"question": "What payment methods do you accept?",
|
|
360
|
+
"answer": "We accept all major credit cards and PayPal.",
|
|
361
|
+
"category": "Billing",
|
|
362
|
+
"priority": 5
|
|
363
|
+
},
|
|
364
|
+
"showWhen": {
|
|
365
|
+
"type": "rules",
|
|
366
|
+
"rules": [
|
|
367
|
+
{
|
|
368
|
+
"conditions": [{ "type": "page_url", "pattern": "/pricing*" }],
|
|
369
|
+
"value": true
|
|
370
|
+
}
|
|
371
|
+
],
|
|
372
|
+
"default": false
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
]
|
|
376
|
+
}
|
|
144
377
|
}
|
|
145
378
|
```
|
|
146
|
-
- **Requirement**: Can only remove prefixed classes
|
|
147
|
-
- **Common Use**: Revealing hidden elements, removing states
|
|
148
|
-
- **Note**: Safe if class doesn't exist
|
|
149
379
|
|
|
150
|
-
###
|
|
151
|
-
|
|
380
|
+
### scroll_to_faq
|
|
381
|
+
|
|
382
|
+
Scrolls the viewport to a specific FAQ item and optionally expands it.
|
|
383
|
+
|
|
384
|
+
| Property | Type | Required | Default | Description |
|
|
385
|
+
| -------------- | ----------------- | -------- | ---------- | ------------------------------------------ |
|
|
386
|
+
| `kind` | `"scroll_to_faq"` | Yes | | Action type |
|
|
387
|
+
| `itemId` | string | No\* | | Target item ID |
|
|
388
|
+
| `itemQuestion` | string | No\* | | Target item question text (fuzzy match) |
|
|
389
|
+
| `expand` | boolean | No | `true` | Whether to expand the item after scrolling |
|
|
390
|
+
| `behavior` | string | No | `"smooth"` | `"smooth"`, `"instant"`, `"auto"` |
|
|
391
|
+
|
|
392
|
+
\* Either `itemId` or `itemQuestion` is required.
|
|
393
|
+
|
|
152
394
|
```json
|
|
153
395
|
{
|
|
154
|
-
"kind": "
|
|
155
|
-
"
|
|
156
|
-
"
|
|
396
|
+
"kind": "scroll_to_faq",
|
|
397
|
+
"itemId": "payment-methods",
|
|
398
|
+
"expand": true,
|
|
399
|
+
"behavior": "smooth"
|
|
157
400
|
}
|
|
158
401
|
```
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
402
|
+
|
|
403
|
+
### toggle_faq_item
|
|
404
|
+
|
|
405
|
+
Opens, closes, or toggles a FAQ item's expanded state.
|
|
406
|
+
|
|
407
|
+
| Property | Type | Required | Default | Description |
|
|
408
|
+
| -------------- | ------------------- | -------- | ---------- | --------------------------------------- |
|
|
409
|
+
| `kind` | `"toggle_faq_item"` | Yes | | Action type |
|
|
410
|
+
| `itemId` | string | No\* | | Target item ID |
|
|
411
|
+
| `itemQuestion` | string | No\* | | Target item question text (fuzzy match) |
|
|
412
|
+
| `state` | string | No | `"toggle"` | `"open"`, `"closed"`, `"toggle"` |
|
|
413
|
+
|
|
414
|
+
\* Either `itemId` or `itemQuestion` is required.
|
|
415
|
+
|
|
170
416
|
```json
|
|
171
417
|
{
|
|
172
|
-
"kind": "
|
|
173
|
-
"
|
|
418
|
+
"kind": "toggle_faq_item",
|
|
419
|
+
"itemId": "getting-started",
|
|
420
|
+
"state": "open"
|
|
174
421
|
}
|
|
175
422
|
```
|
|
176
|
-
- **Effect**: Inserts after existing children
|
|
177
|
-
- **Common Use**: Adding badges, icons, supplementary content
|
|
178
|
-
- **Note**: HTML is sanitized for safety
|
|
179
423
|
|
|
180
|
-
###
|
|
181
|
-
|
|
424
|
+
### update_faq
|
|
425
|
+
|
|
426
|
+
Dynamically adds, removes, reorders, or replaces FAQ items at runtime.
|
|
427
|
+
|
|
428
|
+
| Property | Type | Required | Description |
|
|
429
|
+
| ----------- | ------------------- | -------- | ------------------------------------------------------- |
|
|
430
|
+
| `kind` | `"update_faq"` | Yes | Action type |
|
|
431
|
+
| `operation` | string | Yes | `"add"`, `"remove"`, `"reorder"`, `"replace"` |
|
|
432
|
+
| `items` | FAQQuestionAction[] | No | Items to add or replace with (required for add/replace) |
|
|
433
|
+
| `itemId` | string | No | Item to remove (required for remove) |
|
|
434
|
+
| `order` | string[] | No | Ordered list of item IDs (required for reorder) |
|
|
435
|
+
| `position` | string | No | `"prepend"`, `"append"`, `"before"`, `"after"` |
|
|
436
|
+
| `anchorId` | string | No | Reference item for before/after positioning |
|
|
437
|
+
|
|
438
|
+
**Add items:**
|
|
439
|
+
|
|
182
440
|
```json
|
|
183
441
|
{
|
|
184
|
-
"kind": "
|
|
185
|
-
"
|
|
442
|
+
"kind": "update_faq",
|
|
443
|
+
"operation": "add",
|
|
444
|
+
"position": "append",
|
|
445
|
+
"items": [
|
|
446
|
+
{
|
|
447
|
+
"kind": "faq:question",
|
|
448
|
+
"config": {
|
|
449
|
+
"id": "new-feature",
|
|
450
|
+
"question": "What is the new feature?",
|
|
451
|
+
"answer": "Our latest release includes AI-powered recommendations."
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
]
|
|
186
455
|
}
|
|
187
456
|
```
|
|
188
|
-
- **Effect**: Inserts before existing children
|
|
189
|
-
- **Common Use**: Adding icons, prefixes, notifications
|
|
190
|
-
- **Note**: HTML is sanitized for safety
|
|
191
457
|
|
|
192
|
-
|
|
193
|
-
|
|
458
|
+
**Remove an item:**
|
|
459
|
+
|
|
194
460
|
```json
|
|
195
461
|
{
|
|
196
|
-
"kind": "
|
|
197
|
-
"
|
|
198
|
-
"
|
|
462
|
+
"kind": "update_faq",
|
|
463
|
+
"operation": "remove",
|
|
464
|
+
"itemId": "outdated-question"
|
|
199
465
|
}
|
|
200
466
|
```
|
|
201
|
-
- **Positions**:
|
|
202
|
-
- `beforebegin`: Before the element itself
|
|
203
|
-
- `afterbegin`: Inside element, before first child
|
|
204
|
-
- `beforeend`: Inside element, after last child
|
|
205
|
-
- `afterend`: After the element itself
|
|
206
|
-
- **Common Use**: Complex insertions, wrapping elements
|
|
207
|
-
- **Note**: More flexible than append/prepend
|
|
208
467
|
|
|
209
|
-
|
|
468
|
+
**Reorder items:**
|
|
210
469
|
|
|
211
|
-
|
|
470
|
+
```json
|
|
471
|
+
{
|
|
472
|
+
"kind": "update_faq",
|
|
473
|
+
"operation": "reorder",
|
|
474
|
+
"order": ["getting-started", "new-feature", "payment-methods"]
|
|
475
|
+
}
|
|
476
|
+
```
|
|
212
477
|
|
|
213
|
-
|
|
214
|
-
Floating information bubbles that appear near target elements.
|
|
478
|
+
**Replace all items:**
|
|
215
479
|
|
|
216
480
|
```json
|
|
217
481
|
{
|
|
218
|
-
"kind": "
|
|
219
|
-
"
|
|
220
|
-
"
|
|
221
|
-
"by": "css",
|
|
222
|
-
"value": ".pricing-card"
|
|
223
|
-
},
|
|
224
|
-
"content": {
|
|
225
|
-
"title": "Best Value",
|
|
226
|
-
"body": "This plan includes all features at the lowest per-user cost"
|
|
227
|
-
},
|
|
228
|
-
"placement": "top",
|
|
229
|
-
"trigger": "hover",
|
|
230
|
-
"ctaButtons": [
|
|
482
|
+
"kind": "update_faq",
|
|
483
|
+
"operation": "replace",
|
|
484
|
+
"items": [
|
|
231
485
|
{
|
|
232
|
-
"
|
|
233
|
-
"
|
|
486
|
+
"kind": "faq:question",
|
|
487
|
+
"config": {
|
|
488
|
+
"id": "only-question",
|
|
489
|
+
"question": "Is this the only question?",
|
|
490
|
+
"answer": "Yes, after replacing all items."
|
|
491
|
+
}
|
|
234
492
|
}
|
|
235
|
-
]
|
|
236
|
-
"dismiss": {
|
|
237
|
-
"onEsc": true,
|
|
238
|
-
"closeButton": true
|
|
239
|
-
}
|
|
493
|
+
]
|
|
240
494
|
}
|
|
241
495
|
```
|
|
242
496
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
- `
|
|
248
|
-
- `
|
|
249
|
-
-
|
|
250
|
-
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
497
|
+
## Compositional Pattern
|
|
498
|
+
|
|
499
|
+
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:
|
|
500
|
+
|
|
501
|
+
- **Per-item conditional visibility** via `showWhen` strategies -- items can appear or hide based on page URL, user segment, viewport, or any DecisionStrategy condition
|
|
502
|
+
- **Category grouping** -- items with a `category` field are grouped under collapsible section headers
|
|
503
|
+
- **Dynamic injection** -- `injections` rules can add items when trigger conditions are met, supporting contextual FAQ content
|
|
504
|
+
- **Ordering control** -- the `ordering` strategy determines how items are sorted within categories
|
|
505
|
+
|
|
506
|
+
Items without `showWhen` are always visible. Items without `category` appear in an ungrouped section.
|
|
507
|
+
|
|
508
|
+
## Rich Answer Content
|
|
509
|
+
|
|
510
|
+
FAQ answers support three content formats via the `FAQAnswer` union type:
|
|
511
|
+
|
|
512
|
+
- **Plain string** -- simple text, supports basic markdown
|
|
513
|
+
- **Rich HTML** (`{ "type": "rich", "html": "<p>...</p>" }`) -- pre-rendered HTML content
|
|
514
|
+
- **Enhanced markdown** (`{ "type": "markdown", "content": "...", "assets": [...] }`) -- markdown with embedded media assets (images, videos)
|
|
260
515
|
|
|
261
516
|
```json
|
|
262
517
|
{
|
|
263
|
-
"
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
"
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
"onClickOutside": true,
|
|
281
|
-
"onEsc": true
|
|
518
|
+
"config": {
|
|
519
|
+
"id": "rich-example",
|
|
520
|
+
"question": "How does the visual editor work?",
|
|
521
|
+
"answer": {
|
|
522
|
+
"type": "markdown",
|
|
523
|
+
"content": "The visual editor lets you create experiments with a point-and-click interface.\n\n",
|
|
524
|
+
"assets": [
|
|
525
|
+
{
|
|
526
|
+
"id": "editor-screenshot",
|
|
527
|
+
"type": "image",
|
|
528
|
+
"src": "https://cdn.example.com/editor.png",
|
|
529
|
+
"alt": "Visual editor interface",
|
|
530
|
+
"width": 800,
|
|
531
|
+
"height": 450
|
|
532
|
+
}
|
|
533
|
+
]
|
|
534
|
+
}
|
|
282
535
|
}
|
|
283
536
|
}
|
|
284
537
|
```
|
|
285
538
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
-
|
|
289
|
-
- `anchor`: Element selector (same as patches)
|
|
290
|
-
- `copy`: Text to display near the highlight (optional)
|
|
291
|
-
- `ring.paddingPx`: Space between element and ring
|
|
292
|
-
- `ring.radiusPx`: Corner radius of ring
|
|
293
|
-
- `ringColor`: Color of the highlight ring
|
|
294
|
-
- `scrim.opacity`: Opacity of background dimming (0-1)
|
|
295
|
-
- `blocking`: If true, blocks interaction outside highlight
|
|
296
|
-
- `dismiss.onClickOutside`: Close when clicking outside
|
|
297
|
-
- `dismiss.onEsc`: Close on Escape key
|
|
298
|
-
- `dismiss.timeoutMs`: Auto-dismiss after milliseconds
|
|
539
|
+
## Feedback
|
|
540
|
+
|
|
541
|
+
Per-item feedback allows users to rate answer helpfulness. Enable with a boolean or detailed config:
|
|
299
542
|
|
|
300
|
-
|
|
543
|
+
- `"feedback": true` -- enables thumbs up/down with default prompt
|
|
544
|
+
- `"feedback": { "style": "thumbs", "prompt": "Was this helpful?" }` -- thumbs with custom prompt
|
|
545
|
+
- `"feedback": { "style": "rating" }` -- numeric rating scale
|
|
301
546
|
|
|
302
|
-
|
|
547
|
+
Feedback events are published via `context.publishEvent` for analytics integration.
|
|
548
|
+
|
|
549
|
+
## Personalization
|
|
550
|
+
|
|
551
|
+
### Ordering Strategies
|
|
552
|
+
|
|
553
|
+
The `ordering` field controls how FAQ items are sorted:
|
|
554
|
+
|
|
555
|
+
- **`"static"`** (default) -- items appear in the order defined in the config
|
|
556
|
+
- **`"priority"`** -- items are sorted by their `priority` field (higher values first)
|
|
557
|
+
- **Segment-based** -- items are ordered differently per user segment:
|
|
303
558
|
|
|
304
559
|
```json
|
|
305
560
|
{
|
|
306
|
-
"
|
|
307
|
-
"
|
|
308
|
-
"
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
{ "kind": "highlight", ... }
|
|
313
|
-
]
|
|
561
|
+
"ordering": {
|
|
562
|
+
"type": "segment",
|
|
563
|
+
"segmentWeights": {
|
|
564
|
+
"new_user": ["getting-started", "pricing", "support"],
|
|
565
|
+
"power_user": ["api-docs", "advanced-config", "integrations"]
|
|
566
|
+
}
|
|
314
567
|
}
|
|
315
568
|
}
|
|
316
569
|
```
|
|
317
570
|
|
|
318
|
-
|
|
319
|
-
- `version`: Version number for tracking changes
|
|
320
|
-
- `routes`: URL paths where this recipe applies (optional)
|
|
321
|
-
- `steps`: Array of tooltip and highlight steps
|
|
571
|
+
### Dynamic Injection
|
|
322
572
|
|
|
323
|
-
|
|
573
|
+
Injection rules add contextual FAQ items when conditions are met:
|
|
324
574
|
|
|
325
|
-
|
|
575
|
+
```json
|
|
576
|
+
{
|
|
577
|
+
"injections": [
|
|
578
|
+
{
|
|
579
|
+
"trigger": {
|
|
580
|
+
"type": "rules",
|
|
581
|
+
"rules": [
|
|
582
|
+
{
|
|
583
|
+
"conditions": [{ "type": "page_url", "pattern": "/checkout*" }],
|
|
584
|
+
"value": true
|
|
585
|
+
}
|
|
586
|
+
],
|
|
587
|
+
"default": false
|
|
588
|
+
},
|
|
589
|
+
"items": [
|
|
590
|
+
{
|
|
591
|
+
"kind": "faq:question",
|
|
592
|
+
"config": {
|
|
593
|
+
"id": "checkout-help",
|
|
594
|
+
"question": "Having trouble with checkout?",
|
|
595
|
+
"answer": "Contact support at help@example.com for immediate assistance."
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
],
|
|
599
|
+
"position": "prepend",
|
|
600
|
+
"once": true
|
|
601
|
+
}
|
|
602
|
+
]
|
|
603
|
+
}
|
|
604
|
+
```
|
|
326
605
|
|
|
327
|
-
|
|
606
|
+
- `trigger` -- a DecisionStrategy that evaluates to `true` when injection should occur
|
|
607
|
+
- `items` -- FAQ items to inject
|
|
608
|
+
- `position` -- `"prepend"` or `"append"` relative to existing items
|
|
609
|
+
- `once` -- if `true`, items are injected only on the first trigger match
|
|
328
610
|
|
|
329
|
-
Every adaptive receives a `runtime` object:
|
|
330
611
|
|
|
331
|
-
|
|
332
|
-
type SmartCanvasRuntime = {
|
|
333
|
-
telemetry: TelemetryClient; // Emit events to PostHog
|
|
334
|
-
context: ContextManager; // Subscribe to page/session state
|
|
335
|
-
events: EventBus; // Subscribe to normalized events
|
|
336
|
-
state: StateStore; // Persist dismissals, cooldowns
|
|
337
|
-
version: string;
|
|
338
|
-
mode: "production" | "development";
|
|
339
|
-
};
|
|
340
|
-
```
|
|
612
|
+
---
|
|
341
613
|
|
|
342
|
-
|
|
614
|
+
# @syntrologie/adapt-gamification
|
|
343
615
|
|
|
344
|
-
|
|
345
|
-
const { canvas, runtime } = await Syntro.init({
|
|
346
|
-
token: "syn_..."
|
|
347
|
-
});
|
|
616
|
+
Gamification capabilities including badges, points, and leaderboards.
|
|
348
617
|
|
|
349
|
-
|
|
350
|
-
runtime.telemetry?.trackAction(...);
|
|
351
|
-
runtime.context.subscribe((ctx, prev) => { ... });
|
|
352
|
-
runtime.events.subscribe({ names: ['ui.click'] }, (event) => { ... });
|
|
353
|
-
runtime.state.dismissals.mark('my-tile');
|
|
354
|
-
```
|
|
618
|
+
## Actions
|
|
355
619
|
|
|
356
|
-
###
|
|
620
|
+
### mount_gamification
|
|
357
621
|
|
|
358
|
-
|
|
622
|
+
Mounts gamification UI elements.
|
|
359
623
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
});
|
|
624
|
+
| Property | Type | Required | Description |
|
|
625
|
+
| -------- | ---------------------- | -------- | -------------------------- |
|
|
626
|
+
| `kind` | `"mount_gamification"` | Yes | Action type |
|
|
627
|
+
| `slot` | string | Yes | Target slot |
|
|
628
|
+
| `config` | object | Yes | Gamification configuration |
|
|
366
629
|
|
|
367
|
-
|
|
368
|
-
console.log(ctx.page.url, ctx.session.sessionId);
|
|
369
|
-
```
|
|
630
|
+
### Configuration Schema
|
|
370
631
|
|
|
371
|
-
|
|
372
|
-
|
|
|
373
|
-
|
|
374
|
-
| `
|
|
375
|
-
| `
|
|
376
|
-
| `
|
|
377
|
-
| `
|
|
378
|
-
| `session.startTs` | number | Session start timestamp |
|
|
379
|
-
| `viewport.width` | number | Viewport width in pixels |
|
|
380
|
-
| `viewport.height` | number | Viewport height in pixels |
|
|
381
|
-
| `anchors` | AnchorState[]? | Tracked anchor visibility |
|
|
632
|
+
| Property | Type | Required | Default | Description |
|
|
633
|
+
| ----------------------------- | ------- | -------- | ------- | ---------------------- |
|
|
634
|
+
| `badges` | array | No | `[]` | Badge definitions |
|
|
635
|
+
| `points.enabled` | boolean | No | `false` | Enable points system |
|
|
636
|
+
| `points.multiplier` | number | No | `1` | Points multiplier |
|
|
637
|
+
| `leaderboard.enabled` | boolean | No | `false` | Show leaderboard |
|
|
638
|
+
| `leaderboard.refreshInterval` | number | No | `60000` | Refresh interval in ms |
|
|
382
639
|
|
|
383
|
-
###
|
|
640
|
+
### Badge Schema
|
|
384
641
|
|
|
385
|
-
|
|
642
|
+
| Property | Type | Required | Description |
|
|
643
|
+
| -------------------- | ------ | -------- | ----------------------------- |
|
|
644
|
+
| `id` | string | Yes | Unique badge identifier |
|
|
645
|
+
| `name` | string | Yes | Display name |
|
|
646
|
+
| `icon` | string | Yes | Icon identifier |
|
|
647
|
+
| `description` | string | No | Badge description |
|
|
648
|
+
| `trigger.event` | string | Yes | Event that triggers the badge |
|
|
649
|
+
| `trigger.conditions` | array | No | Additional conditions |
|
|
386
650
|
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
651
|
+
```json
|
|
652
|
+
{
|
|
653
|
+
"kind": "mount_gamification",
|
|
654
|
+
"slot": "overlay_corner_br",
|
|
655
|
+
"config": {
|
|
656
|
+
"badges": [
|
|
657
|
+
{
|
|
658
|
+
"id": "first-purchase",
|
|
659
|
+
"name": "First Purchase",
|
|
660
|
+
"icon": "shopping-cart",
|
|
661
|
+
"description": "Made your first purchase!",
|
|
662
|
+
"trigger": {
|
|
663
|
+
"event": "purchase_completed"
|
|
664
|
+
}
|
|
665
|
+
},
|
|
666
|
+
{
|
|
667
|
+
"id": "explorer",
|
|
668
|
+
"name": "Explorer",
|
|
669
|
+
"icon": "compass",
|
|
670
|
+
"description": "Visited 10 different pages",
|
|
671
|
+
"trigger": {
|
|
672
|
+
"event": "page_view",
|
|
673
|
+
"conditions": [
|
|
674
|
+
{ "type": "session_metric", "key": "unique_pages", "operator": ">=", "threshold": 10 }
|
|
675
|
+
]
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
],
|
|
679
|
+
"points": {
|
|
680
|
+
"enabled": true,
|
|
681
|
+
"multiplier": 2
|
|
682
|
+
},
|
|
683
|
+
"leaderboard": {
|
|
684
|
+
"enabled": true,
|
|
685
|
+
"refreshInterval": 30000
|
|
686
|
+
}
|
|
392
687
|
}
|
|
393
|
-
|
|
688
|
+
}
|
|
394
689
|
```
|
|
395
690
|
|
|
396
|
-
|
|
397
|
-
```typescript
|
|
398
|
-
type NormalizedEvent = {
|
|
399
|
-
ts: number; // Timestamp
|
|
400
|
-
name: string; // e.g., "ui.click", "canvas.opened"
|
|
401
|
-
source: "posthog" | "canvas" | "derived";
|
|
402
|
-
props?: Record<string, any>;
|
|
403
|
-
schemaVersion: string;
|
|
404
|
-
};
|
|
405
|
-
```
|
|
691
|
+
## Use Cases
|
|
406
692
|
|
|
407
|
-
**
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
| `ui.scroll` | posthog | User scrolled |
|
|
412
|
-
| `nav.page_view` | posthog | Page navigation |
|
|
413
|
-
| `canvas.opened` | canvas | Smart Canvas opened |
|
|
414
|
-
| `canvas.closed` | canvas | Smart Canvas closed |
|
|
415
|
-
| `tile.viewed` | canvas | Tile became visible |
|
|
416
|
-
| `tile.action` | canvas | User clicked tile action |
|
|
693
|
+
- **Engagement rewards**: Award badges for completing onboarding steps
|
|
694
|
+
- **Loyalty programs**: Track points for purchases or interactions
|
|
695
|
+
- **Social proof**: Display leaderboards to encourage participation
|
|
696
|
+
- **Progress tracking**: Show achievement progress to motivate users
|
|
417
697
|
|
|
418
|
-
### State Store
|
|
419
698
|
|
|
420
|
-
|
|
699
|
+
---
|
|
421
700
|
|
|
422
|
-
|
|
423
|
-
// Session storage (cleared on close)
|
|
424
|
-
runtime.state.session.set("lastTileViewed", "tile-123");
|
|
425
|
-
const last = runtime.state.session.get<string>("lastTileViewed");
|
|
701
|
+
# @syntrologie/adapt-nav
|
|
426
702
|
|
|
427
|
-
|
|
428
|
-
runtime.state.user.set("onboardingComplete", true);
|
|
703
|
+
Navigation link list widget with conditional item visibility.
|
|
429
704
|
|
|
430
|
-
|
|
431
|
-
const ns = runtime.state.ns("my-adaptive");
|
|
432
|
-
ns.session.set("step", 3);
|
|
433
|
-
```
|
|
705
|
+
## Actions
|
|
434
706
|
|
|
435
|
-
|
|
436
|
-
```typescript
|
|
437
|
-
// Track dismissals
|
|
438
|
-
runtime.state.dismissals.mark("tooltip-1");
|
|
439
|
-
if (runtime.state.dismissals.isDismissed("tooltip-1")) { ... }
|
|
707
|
+
### mount_nav
|
|
440
708
|
|
|
441
|
-
|
|
442
|
-
runtime.state.cooldowns.set("promo-banner", 86400000); // 24h
|
|
443
|
-
if (runtime.state.cooldowns.isActive("promo-banner")) { ... }
|
|
709
|
+
Mounts a navigation link list widget to a surface slot.
|
|
444
710
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
711
|
+
| Property | Type | Required | Description |
|
|
712
|
+
| -------------- | ------------- | -------- | ---------------------------------------------------------- |
|
|
713
|
+
| `kind` | `"mount_nav"` | Yes | Action type |
|
|
714
|
+
| `slot` | string | Yes | Target slot (e.g., `"drawer_left"`, `"inline:{anchorId}"`) |
|
|
715
|
+
| `config.title` | string | No | Widget title |
|
|
716
|
+
| `config.items` | array | Yes | Navigation items (see below) |
|
|
717
|
+
|
|
718
|
+
### Nav Item Schema
|
|
449
719
|
|
|
450
|
-
|
|
720
|
+
Each item in the `items` array:
|
|
451
721
|
|
|
452
|
-
|
|
722
|
+
| Property | Type | Required | Description |
|
|
723
|
+
| ---------- | ---------------- | -------- | ------------------------------- |
|
|
724
|
+
| `label` | string | Yes | Link text |
|
|
725
|
+
| `href` | string | Yes | Link destination |
|
|
726
|
+
| `icon` | string | No | Icon identifier |
|
|
727
|
+
| `showWhen` | DecisionStrategy | No | Conditional visibility strategy |
|
|
453
728
|
|
|
454
729
|
```json
|
|
455
730
|
{
|
|
456
|
-
"
|
|
457
|
-
"
|
|
458
|
-
"
|
|
459
|
-
"
|
|
460
|
-
"
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
731
|
+
"kind": "mount_nav",
|
|
732
|
+
"slot": "drawer_left",
|
|
733
|
+
"config": {
|
|
734
|
+
"title": "Quick Links",
|
|
735
|
+
"items": [
|
|
736
|
+
{
|
|
737
|
+
"label": "Dashboard",
|
|
738
|
+
"href": "/dashboard",
|
|
739
|
+
"icon": "home"
|
|
740
|
+
},
|
|
741
|
+
{
|
|
742
|
+
"label": "Admin Settings",
|
|
743
|
+
"href": "/admin",
|
|
744
|
+
"icon": "settings",
|
|
745
|
+
"showWhen": {
|
|
746
|
+
"type": "rules",
|
|
747
|
+
"rules": [
|
|
748
|
+
{
|
|
749
|
+
"conditions": [{ "type": "state_equals", "key": "user.role", "value": "admin" }],
|
|
750
|
+
"value": true
|
|
751
|
+
}
|
|
467
752
|
],
|
|
468
|
-
"
|
|
753
|
+
"default": false
|
|
469
754
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
755
|
+
},
|
|
756
|
+
{
|
|
757
|
+
"label": "Upgrade",
|
|
758
|
+
"href": "/pricing",
|
|
759
|
+
"icon": "sparkles",
|
|
760
|
+
"showWhen": {
|
|
761
|
+
"type": "rules",
|
|
762
|
+
"rules": [
|
|
763
|
+
{
|
|
764
|
+
"conditions": [{ "type": "state_equals", "key": "user.plan", "value": "free" }],
|
|
765
|
+
"value": true
|
|
766
|
+
}
|
|
767
|
+
],
|
|
768
|
+
"default": false
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
]
|
|
473
772
|
}
|
|
474
773
|
}
|
|
475
774
|
```
|
|
476
775
|
|
|
477
|
-
|
|
478
|
-
| Type | Description |
|
|
479
|
-
|------|-------------|
|
|
480
|
-
| `rules` | Condition-based matching (if/then) |
|
|
481
|
-
| `score` | Threshold on augmented field |
|
|
482
|
-
| `model` | ML model prediction (future) |
|
|
483
|
-
| `external` | Remote decision endpoint (future) |
|
|
776
|
+
## Compositional Pattern
|
|
484
777
|
|
|
485
|
-
**
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
778
|
+
The nav widget supports **per-item conditional visibility** using `showWhen` strategies. This allows different navigation items to appear based on:
|
|
779
|
+
|
|
780
|
+
- User role or permissions
|
|
781
|
+
- Subscription tier
|
|
782
|
+
- Feature flags
|
|
783
|
+
- Any other DecisionStrategy condition
|
|
784
|
+
|
|
785
|
+
Items without `showWhen` are always visible.
|
|
786
|
+
|
|
787
|
+
## Navigation Actions
|
|
788
|
+
|
|
789
|
+
### scroll_to
|
|
790
|
+
|
|
791
|
+
Scrolls the viewport to bring an element into view.
|
|
792
|
+
|
|
793
|
+
| Property | Type | Required | Default | Description |
|
|
794
|
+
| ---------- | ------------- | -------- | ----------- | ------------------------------------------- |
|
|
795
|
+
| `kind` | `"scroll_to"` | Yes | | Action type |
|
|
796
|
+
| `anchorId` | string | Yes | | Element selector |
|
|
797
|
+
| `behavior` | string | No | `"smooth"` | `"smooth"`, `"instant"`, `"auto"` |
|
|
798
|
+
| `block` | string | No | `"center"` | `"start"`, `"center"`, `"end"`, `"nearest"` |
|
|
799
|
+
| `inline` | string | No | `"nearest"` | `"start"`, `"center"`, `"end"`, `"nearest"` |
|
|
800
|
+
|
|
801
|
+
```json
|
|
802
|
+
{
|
|
803
|
+
"kind": "scroll_to",
|
|
804
|
+
"anchorId": "#pricing-section",
|
|
805
|
+
"behavior": "smooth",
|
|
806
|
+
"block": "start"
|
|
807
|
+
}
|
|
808
|
+
```
|
|
498
809
|
|
|
499
|
-
###
|
|
810
|
+
### navigate
|
|
500
811
|
|
|
501
|
-
|
|
812
|
+
Navigates to a URL.
|
|
813
|
+
|
|
814
|
+
| Property | Type | Required | Default | Description |
|
|
815
|
+
| -------- | ------------ | -------- | --------- | ----------------------- |
|
|
816
|
+
| `kind` | `"navigate"` | Yes | | Action type |
|
|
817
|
+
| `url` | string | Yes | | Destination URL |
|
|
818
|
+
| `target` | string | No | `"_self"` | `"_self"` or `"_blank"` |
|
|
502
819
|
|
|
503
|
-
**Before (v1):**
|
|
504
820
|
```json
|
|
505
821
|
{
|
|
506
|
-
"
|
|
507
|
-
|
|
508
|
-
|
|
822
|
+
"kind": "navigate",
|
|
823
|
+
"url": "/signup?ref=banner",
|
|
824
|
+
"target": "_self"
|
|
825
|
+
}
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
**Note:** `javascript:` URLs are blocked for security.
|
|
829
|
+
|
|
830
|
+
|
|
831
|
+
---
|
|
832
|
+
|
|
833
|
+
# @syntrologie/adapt-overlays
|
|
834
|
+
|
|
835
|
+
Visual overlay capabilities including highlights, tooltips, badges, and pulse animations.
|
|
836
|
+
|
|
837
|
+
## Actions
|
|
838
|
+
|
|
839
|
+
### highlight
|
|
840
|
+
|
|
841
|
+
Creates a spotlight effect around an element with a scrim overlay.
|
|
842
|
+
|
|
843
|
+
| Property | Type | Required | Default | Description |
|
|
844
|
+
| -------------------- | ------------- | -------- | ----------- | --------------------------------------- |
|
|
845
|
+
| `kind` | `"highlight"` | Yes | | Action type |
|
|
846
|
+
| `anchorId` | string | Yes | | Element selector |
|
|
847
|
+
| `style.color` | string | No | `"#5b8cff"` | Ring color |
|
|
848
|
+
| `style.scrimOpacity` | number | No | `0.55` | Backdrop opacity 0-1 (set to 0 to hide) |
|
|
849
|
+
| `style.paddingPx` | number | No | `12` | Space around element |
|
|
850
|
+
| `style.radiusPx` | number | No | `12` | Ring corner radius |
|
|
851
|
+
|
|
852
|
+
```json
|
|
853
|
+
{
|
|
854
|
+
"kind": "highlight",
|
|
855
|
+
"anchorId": "#signup-button",
|
|
856
|
+
"style": {
|
|
857
|
+
"color": "#22c55e",
|
|
858
|
+
"scrimOpacity": 0.4
|
|
509
859
|
}
|
|
510
860
|
}
|
|
511
861
|
```
|
|
512
862
|
|
|
513
|
-
|
|
863
|
+
### tooltip
|
|
864
|
+
|
|
865
|
+
Shows a tooltip near an element with optional title, body, and CTA.
|
|
866
|
+
|
|
867
|
+
| Property | Type | Required | Default | Description |
|
|
868
|
+
| -------------------- | ----------- | -------- | ------------- | ----------------------------------- |
|
|
869
|
+
| `kind` | `"tooltip"` | Yes | | Action type |
|
|
870
|
+
| `anchorId` | string | Yes | | Element selector |
|
|
871
|
+
| `content.title` | string | No | | Tooltip heading |
|
|
872
|
+
| `content.body` | string | Yes | | Tooltip text |
|
|
873
|
+
| `content.cta.label` | string | No | | CTA button text |
|
|
874
|
+
| `content.cta.action` | Action | No | | Action to execute on CTA click |
|
|
875
|
+
| `trigger` | string | No | `"immediate"` | `"immediate"`, `"hover"`, `"click"` |
|
|
876
|
+
| `placement` | string | No | `"top"` | See placement options below |
|
|
877
|
+
|
|
878
|
+
**Placement options:** `top`, `top-start`, `top-end`, `bottom`, `bottom-start`, `bottom-end`, `left`, `left-start`, `left-end`, `right`, `right-start`, `right-end`
|
|
879
|
+
|
|
514
880
|
```json
|
|
515
881
|
{
|
|
516
|
-
"
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
"value": true
|
|
525
|
-
}
|
|
526
|
-
],
|
|
527
|
-
"default": false
|
|
882
|
+
"kind": "tooltip",
|
|
883
|
+
"anchorId": "#pricing-toggle",
|
|
884
|
+
"content": {
|
|
885
|
+
"title": "Save 20%",
|
|
886
|
+
"body": "Switch to annual billing to save on your subscription.",
|
|
887
|
+
"cta": {
|
|
888
|
+
"label": "Switch Now",
|
|
889
|
+
"action": { "kind": "navigate", "url": "/billing?annual=true" }
|
|
528
890
|
}
|
|
529
|
-
}
|
|
891
|
+
},
|
|
892
|
+
"placement": "bottom",
|
|
893
|
+
"trigger": "immediate"
|
|
530
894
|
}
|
|
531
895
|
```
|
|
532
896
|
|
|
533
|
-
|
|
897
|
+
### badge
|
|
534
898
|
|
|
535
|
-
|
|
536
|
-
- Use the most specific selector that won't break
|
|
537
|
-
- Prefer data attributes over classes for stability
|
|
538
|
-
- Test selectors across different page states
|
|
899
|
+
Adds a small badge indicator near an element.
|
|
539
900
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
901
|
+
| Property | Type | Required | Default | Description |
|
|
902
|
+
| ---------- | --------- | -------- | ------------- | -------------------------------------------------------------- |
|
|
903
|
+
| `kind` | `"badge"` | Yes | | Action type |
|
|
904
|
+
| `anchorId` | string | Yes | | Element selector |
|
|
905
|
+
| `text` | string | Yes | | Badge text (e.g., "NEW", "3") |
|
|
906
|
+
| `position` | string | No | `"top-right"` | `"top-left"`, `"top-right"`, `"bottom-left"`, `"bottom-right"` |
|
|
544
907
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
-
|
|
908
|
+
```json
|
|
909
|
+
{
|
|
910
|
+
"kind": "badge",
|
|
911
|
+
"anchorId": "#inbox-icon",
|
|
912
|
+
"text": "5",
|
|
913
|
+
"position": "top-right"
|
|
914
|
+
}
|
|
915
|
+
```
|
|
549
916
|
|
|
550
|
-
###
|
|
551
|
-
- Batch related patches together
|
|
552
|
-
- Avoid selecting too many elements
|
|
553
|
-
- Use CSS classes instead of inline styles when possible
|
|
917
|
+
### pulse
|
|
554
918
|
|
|
555
|
-
|
|
556
|
-
- All HTML is sanitized to prevent XSS
|
|
557
|
-
- Classes must be prefixed to avoid conflicts
|
|
558
|
-
- Test across different browsers and devices
|
|
919
|
+
Adds a pulsing animation to draw attention.
|
|
559
920
|
|
|
560
|
-
|
|
921
|
+
| Property | Type | Required | Default | Description |
|
|
922
|
+
| ---------- | --------- | -------- | ------- | ------------------------ |
|
|
923
|
+
| `kind` | `"pulse"` | Yes | | Action type |
|
|
924
|
+
| `anchorId` | string | Yes | | Element selector |
|
|
925
|
+
| `duration` | number | No | `2000` | Animation duration in ms |
|
|
561
926
|
|
|
562
927
|
```json
|
|
563
928
|
{
|
|
564
|
-
"
|
|
565
|
-
"
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
929
|
+
"kind": "pulse",
|
|
930
|
+
"anchorId": ".notification-bell",
|
|
931
|
+
"duration": 3000
|
|
932
|
+
}
|
|
933
|
+
```
|
|
934
|
+
|
|
935
|
+
### modal
|
|
936
|
+
|
|
937
|
+
Shows a centered modal dialog with optional CTA buttons.
|
|
938
|
+
|
|
939
|
+
| Property | Type | Required | Default | Description |
|
|
940
|
+
| --------------------- | ------------------ | -------- | ------- | ------------------------------------------------------------- |
|
|
941
|
+
| `kind` | `"overlays:modal"` | Yes | | Action type |
|
|
942
|
+
| `content.title` | string | No | | Modal heading |
|
|
943
|
+
| `content.body` | string | Yes | | Modal text |
|
|
944
|
+
| `size` | string | No | `"md"` | `"sm"`, `"md"`, `"lg"` |
|
|
945
|
+
| `blocking` | boolean | No | `false` | Block page interaction |
|
|
946
|
+
| `scrim.opacity` | number | No | `0.6` | Backdrop opacity 0-1 |
|
|
947
|
+
| `dismiss.onEsc` | boolean | No | `true` | Close on Escape key |
|
|
948
|
+
| `dismiss.closeButton` | boolean | No | `true` | Show close button |
|
|
949
|
+
| `dismiss.timeoutMs` | number | No | | Auto-close after timeout |
|
|
950
|
+
| `ctaButtons` | array | No | | Array of CTA buttons |
|
|
951
|
+
| `waitFor` | string | No | | When to complete: `"dismissed"`, `"cta-click"`, `"timeout:N"` |
|
|
952
|
+
|
|
953
|
+
**CTA Button properties:**
|
|
954
|
+
|
|
955
|
+
- `label`: Button text
|
|
956
|
+
- `actionId`: Identifier for the action
|
|
957
|
+
- `primary`: Whether this is a primary button (default: false)
|
|
958
|
+
|
|
959
|
+
```json
|
|
960
|
+
{
|
|
961
|
+
"kind": "overlays:modal",
|
|
962
|
+
"content": {
|
|
963
|
+
"title": "Welcome!",
|
|
964
|
+
"body": "Thanks for signing up. Let us show you around."
|
|
965
|
+
},
|
|
966
|
+
"size": "md",
|
|
967
|
+
"ctaButtons": [
|
|
968
|
+
{ "label": "Skip", "actionId": "skip" },
|
|
969
|
+
{ "label": "Start Tour", "actionId": "start", "primary": true }
|
|
970
|
+
],
|
|
971
|
+
"waitFor": "cta-click"
|
|
972
|
+
}
|
|
973
|
+
```
|
|
974
|
+
|
|
975
|
+
|
|
976
|
+
---
|
|
977
|
+
|
|
978
|
+
|
|
979
|
+
---
|
|
980
|
+
|
|
981
|
+
## Surfaces
|
|
982
|
+
|
|
983
|
+
Surfaces are named slots where widgets can be mounted. Use the `mount_widget` action to render content into a surface slot.
|
|
984
|
+
|
|
985
|
+
### Static Slots
|
|
986
|
+
|
|
987
|
+
Fixed-position slots for common UI patterns:
|
|
988
|
+
|
|
989
|
+
| Slot | Position | Use Case |
|
|
990
|
+
|------|----------|----------|
|
|
991
|
+
| `drawer_right` | Right edge, full height | Settings panels, details |
|
|
992
|
+
| `drawer_left` | Left edge, full height | Navigation, filters |
|
|
993
|
+
| `drawer_bottom` | Bottom edge, full width | Action sheets, keyboards |
|
|
994
|
+
| `overlay_center` | Centered modal | Dialogs, confirmations |
|
|
995
|
+
| `overlay_corner_br` | Bottom-right corner | Chat widgets, help |
|
|
996
|
+
| `overlay_corner_bl` | Bottom-left corner | Notifications |
|
|
997
|
+
| `toast_top` | Top center | Success/error messages |
|
|
998
|
+
| `toast_bottom` | Bottom center | Snackbar notifications |
|
|
999
|
+
|
|
1000
|
+
### Dynamic Slots
|
|
1001
|
+
|
|
1002
|
+
Slots that position content relative to anchors:
|
|
1003
|
+
|
|
1004
|
+
- **Inline Slots**: Render content inside an anchor element. Format: `inline:{anchorId}`
|
|
1005
|
+
- **Adjacent Slots**: Render content positioned near an anchor element. Format: `adjacent:{anchorId}`
|
|
1006
|
+
|
|
1007
|
+
---
|
|
1008
|
+
|
|
1009
|
+
## Anchor Resolution
|
|
1010
|
+
|
|
1011
|
+
The `anchorId` field identifies which DOM element to target. Multiple formats are supported:
|
|
1012
|
+
|
|
1013
|
+
### CSS Selectors
|
|
1014
|
+
|
|
1015
|
+
| Format | Example | Matches |
|
|
1016
|
+
|--------|---------|---------|
|
|
1017
|
+
| Class | `".hero-title"` | `<h1 class="hero-title">` |
|
|
1018
|
+
| ID | `"#signup-button"` | `<button id="signup-button">` |
|
|
1019
|
+
| Attribute | `"[data-testid='cta']"` | `<button data-testid="cta">` |
|
|
1020
|
+
| Tag + class | `"h1.page-title"` | `<h1 class="page-title">` |
|
|
1021
|
+
| Nested | `".card .title"` | `<div class="title">` inside `.card` |
|
|
1022
|
+
|
|
1023
|
+
### Data Attributes (Recommended)
|
|
1024
|
+
|
|
1025
|
+
For stability, add `data-syntro-anchor` attributes to target elements:
|
|
1026
|
+
|
|
1027
|
+
```html
|
|
1028
|
+
<h1 data-syntro-anchor="hero-title">Welcome</h1>
|
|
1029
|
+
```
|
|
1030
|
+
|
|
1031
|
+
Then reference by the anchor name:
|
|
1032
|
+
|
|
1033
|
+
```json
|
|
1034
|
+
{ "anchorId": "hero-title" }
|
|
1035
|
+
```
|
|
1036
|
+
|
|
1037
|
+
---
|
|
1038
|
+
|
|
1039
|
+
## Decision Strategies
|
|
1040
|
+
|
|
1041
|
+
Control when adaptives activate using `DecisionStrategy`:
|
|
1042
|
+
|
|
1043
|
+
### Rules Strategy
|
|
1044
|
+
|
|
1045
|
+
```json
|
|
1046
|
+
{
|
|
1047
|
+
"type": "rules",
|
|
1048
|
+
"rules": [
|
|
584
1049
|
{
|
|
585
|
-
"
|
|
586
|
-
|
|
587
|
-
"
|
|
588
|
-
"key": "
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
"tier": "additive",
|
|
592
|
-
"operations": [
|
|
593
|
-
{
|
|
594
|
-
"kind": "setStyle",
|
|
595
|
-
"prop": "boxShadow",
|
|
596
|
-
"value": "0 4px 6px rgba(0,0,0,0.1)"
|
|
597
|
-
},
|
|
598
|
-
{
|
|
599
|
-
"kind": "append",
|
|
600
|
-
"html": "<span class='syntro-arrow'>→</span>"
|
|
601
|
-
}
|
|
602
|
-
]
|
|
1050
|
+
"conditions": [
|
|
1051
|
+
{ "type": "page_url", "pattern": "/pricing*" },
|
|
1052
|
+
{ "type": "viewport", "minWidth": 768 },
|
|
1053
|
+
{ "type": "dismissed", "key": "promo", "inverted": true }
|
|
1054
|
+
],
|
|
1055
|
+
"value": true
|
|
603
1056
|
}
|
|
604
|
-
]
|
|
1057
|
+
],
|
|
1058
|
+
"default": false
|
|
605
1059
|
}
|
|
606
1060
|
```
|
|
607
1061
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
1062
|
+
### Condition Types
|
|
1063
|
+
|
|
1064
|
+
| Type | Parameters | Description |
|
|
1065
|
+
|------|------------|-------------|
|
|
1066
|
+
| `page_url` | `pattern` | URL matches glob pattern |
|
|
1067
|
+
| `route` | `routeId` | Route ID matches |
|
|
1068
|
+
| `anchor_visible` | `anchorId`, `state` | Anchor visibility |
|
|
1069
|
+
| `event_occurred` | `eventName`, `withinMs?` | Recent event |
|
|
1070
|
+
| `state_equals` | `key`, `value` | State matches |
|
|
1071
|
+
| `viewport` | `minWidth?`, `maxWidth?`, `minHeight?`, `maxHeight?` | Viewport size |
|
|
1072
|
+
| `session_metric` | `key`, `operator`, `threshold` | Metric comparison |
|
|
1073
|
+
| `dismissed` | `key`, `inverted?` | Dismissal check |
|
|
1074
|
+
| `cooldown_active` | `key`, `inverted?` | Cooldown check |
|
|
1075
|
+
| `frequency_limit` | `key`, `limit`, `inverted?` | Frequency cap |
|
|
1076
|
+
|
|
1077
|
+
---
|
|
1078
|
+
|
|
1079
|
+
## Best Practices
|
|
1080
|
+
|
|
1081
|
+
### 1. Choose the Right Action Type
|
|
1082
|
+
|
|
1083
|
+
| Goal | Action |
|
|
1084
|
+
|------|--------|
|
|
1085
|
+
| Change text | `set_text` |
|
|
1086
|
+
| Add visual indicator | `badge`, `pulse` |
|
|
1087
|
+
| Show help text | `tooltip` |
|
|
1088
|
+
| Draw attention | `highlight` |
|
|
1089
|
+
| Add content | `insert_html` |
|
|
1090
|
+
| Navigate user | `scroll_to`, `navigate` |
|
|
1091
|
+
| Show UI panel | `mount_widget` + Surfaces |
|
|
1092
|
+
|
|
1093
|
+
### 2. Anchor Selection
|
|
1094
|
+
|
|
1095
|
+
- **Prefer stable selectors:** `[data-testid]`, IDs over classes
|
|
1096
|
+
- **Test across states:** Element may not exist on all pages
|
|
1097
|
+
- **Be specific:** Avoid selectors matching multiple elements
|
|
1098
|
+
|
|
1099
|
+
### 3. Reversibility
|
|
1100
|
+
|
|
1101
|
+
- Always store handles if you need to revert
|
|
1102
|
+
- Use `applyBatch` for related changes (atomic rollback)
|
|
1103
|
+
- Clean up on route changes
|
|
1104
|
+
|
|
1105
|
+
### 4. Performance
|
|
1106
|
+
|
|
1107
|
+
- Batch related actions together
|
|
1108
|
+
- Use `validate()` to check actions before applying
|
|
1109
|
+
- Avoid applying many actions simultaneously
|
|
1110
|
+
|
|
1111
|
+
### 5. Events
|
|
1112
|
+
|
|
1113
|
+
- Listen for `action.failed` to handle errors
|
|
1114
|
+
- Track `action.applied` for analytics
|
|
1115
|
+
- Use EventBus for cross-adaptive coordination
|