@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.
Files changed (332) hide show
  1. package/CAPABILITIES.md +756 -284
  2. package/README.md +310 -68
  3. package/dist/RuntimeProvider.d.ts +51 -0
  4. package/dist/RuntimeProvider.js +113 -0
  5. package/dist/RuntimeProvider.js.map +1 -0
  6. package/dist/SmartCanvasApp.d.ts +16 -10
  7. package/dist/SmartCanvasApp.js +45 -49
  8. package/dist/SmartCanvasApp.js.map +1 -1
  9. package/dist/SmartCanvasElement.d.ts +5 -5
  10. package/dist/SmartCanvasElement.js +24 -14
  11. package/dist/SmartCanvasElement.js.map +1 -1
  12. package/dist/SmartCanvasPortal.d.ts +2 -2
  13. package/dist/SmartCanvasPortal.js +2 -2
  14. package/dist/actions/ActionEngine.d.ts +11 -0
  15. package/dist/actions/ActionEngine.js +274 -0
  16. package/dist/actions/ActionEngine.js.map +1 -0
  17. package/dist/actions/executors/index.d.ts +117 -0
  18. package/dist/actions/executors/index.js +242 -0
  19. package/dist/actions/executors/index.js.map +1 -0
  20. package/dist/actions/executors/tour.d.ts +18 -0
  21. package/dist/actions/executors/tour.js +332 -0
  22. package/dist/actions/executors/tour.js.map +1 -0
  23. package/dist/actions/index.d.ts +10 -0
  24. package/dist/actions/index.js +12 -0
  25. package/dist/actions/index.js.map +1 -0
  26. package/dist/actions/types.d.ts +399 -0
  27. package/dist/actions/types.js +8 -0
  28. package/dist/actions/types.js.map +1 -0
  29. package/dist/actions/validation.d.ts +14 -0
  30. package/dist/actions/validation.js +602 -0
  31. package/dist/actions/validation.js.map +1 -0
  32. package/dist/antiFlicker.js +1 -1
  33. package/dist/api.d.ts +40 -26
  34. package/dist/api.js +90 -59
  35. package/dist/api.js.map +1 -1
  36. package/dist/apps/AppContext.d.ts +31 -0
  37. package/dist/apps/AppContext.js +93 -0
  38. package/dist/apps/AppContext.js.map +1 -0
  39. package/dist/apps/AppLoader.d.ts +84 -0
  40. package/dist/apps/AppLoader.js +256 -0
  41. package/dist/apps/AppLoader.js.map +1 -0
  42. package/dist/apps/AppRegistry.d.ts +102 -0
  43. package/dist/apps/AppRegistry.js +317 -0
  44. package/dist/apps/AppRegistry.js.map +1 -0
  45. package/dist/apps/adaptive-chatbot/index.js +7 -0
  46. package/dist/apps/adaptive-chatbot/index.js.map +7 -0
  47. package/dist/apps/examples/gamification-app.example.d.ts +305 -0
  48. package/dist/apps/examples/gamification-app.example.js +329 -0
  49. package/dist/apps/examples/gamification-app.example.js.map +1 -0
  50. package/dist/apps/faq/index.js +11 -0
  51. package/dist/apps/faq/index.js.map +7 -0
  52. package/dist/apps/gamification/index.js +2 -0
  53. package/dist/apps/gamification/index.js.map +7 -0
  54. package/dist/apps/index.d.ts +15 -0
  55. package/dist/apps/index.js +17 -0
  56. package/dist/apps/index.js.map +1 -0
  57. package/dist/apps/nav/index.js +11 -0
  58. package/dist/apps/nav/index.js.map +7 -0
  59. package/dist/apps/types.d.ts +231 -0
  60. package/dist/apps/types.js +8 -0
  61. package/dist/apps/types.js.map +1 -0
  62. package/dist/blocks/data/ComparisonBlock.d.ts +1 -1
  63. package/dist/blocks/data/ComparisonBlock.js +40 -40
  64. package/dist/blocks/data/ComparisonBlock.js.map +1 -1
  65. package/dist/blocks/data/StatsBlock.d.ts +1 -1
  66. package/dist/blocks/data/StatsBlock.js +41 -41
  67. package/dist/blocks/data/StatsBlock.js.map +1 -1
  68. package/dist/blocks/data/index.d.ts +2 -2
  69. package/dist/blocks/data/index.js +2 -2
  70. package/dist/blocks/index.d.ts +5 -5
  71. package/dist/blocks/index.js +29 -29
  72. package/dist/blocks/index.js.map +1 -1
  73. package/dist/blocks/interactive/ChecklistBlock.d.ts +1 -1
  74. package/dist/blocks/interactive/ChecklistBlock.js +60 -60
  75. package/dist/blocks/interactive/ChecklistBlock.js.map +1 -1
  76. package/dist/blocks/interactive/RatingBlock.d.ts +1 -1
  77. package/dist/blocks/interactive/RatingBlock.js +75 -65
  78. package/dist/blocks/interactive/RatingBlock.js.map +1 -1
  79. package/dist/blocks/interactive/index.d.ts +2 -2
  80. package/dist/blocks/interactive/index.js +2 -2
  81. package/dist/blocks/notification/NotificationBlock.d.ts +2 -2
  82. package/dist/blocks/notification/NotificationBlock.js +67 -63
  83. package/dist/blocks/notification/NotificationBlock.js.map +1 -1
  84. package/dist/blocks/notification/index.d.ts +1 -1
  85. package/dist/blocks/notification/index.js +1 -1
  86. package/dist/bootstrap.d.ts +47 -9
  87. package/dist/bootstrap.js +237 -69
  88. package/dist/bootstrap.js.map +1 -1
  89. package/dist/components/ShadowCanvasOverlay.d.ts +6 -6
  90. package/dist/components/ShadowCanvasOverlay.js +144 -107
  91. package/dist/components/ShadowCanvasOverlay.js.map +1 -1
  92. package/dist/components/TileCard.d.ts +5 -5
  93. package/dist/components/TileCard.js +204 -154
  94. package/dist/components/TileCard.js.map +1 -1
  95. package/dist/components/TileWheel.d.ts +3 -3
  96. package/dist/components/TileWheel.js +7 -7
  97. package/dist/components/TileWheel.js.map +1 -1
  98. package/dist/config-validator.d.ts +49 -0
  99. package/dist/config-validator.js +173 -0
  100. package/dist/config-validator.js.map +1 -0
  101. package/dist/configFetcher.d.ts +2 -2
  102. package/dist/configFetcher.js +20 -8
  103. package/dist/configFetcher.js.map +1 -1
  104. package/dist/context/ContextManager.d.ts +66 -0
  105. package/dist/context/ContextManager.js +268 -0
  106. package/dist/context/ContextManager.js.map +1 -0
  107. package/dist/context/index.d.ts +7 -0
  108. package/dist/context/index.js +7 -0
  109. package/dist/context/index.js.map +1 -0
  110. package/dist/context/schema.d.ts +360 -0
  111. package/dist/context/schema.js +50 -0
  112. package/dist/context/schema.js.map +1 -0
  113. package/dist/context/types.d.ts +101 -0
  114. package/dist/context/types.js +8 -0
  115. package/dist/context/types.js.map +1 -0
  116. package/dist/decisions/engine.d.ts +43 -0
  117. package/dist/decisions/engine.js +112 -0
  118. package/dist/decisions/engine.js.map +1 -0
  119. package/dist/decisions/index.d.ts +9 -0
  120. package/dist/decisions/index.js +10 -0
  121. package/dist/decisions/index.js.map +1 -0
  122. package/dist/decisions/schema.d.ts +2166 -0
  123. package/dist/decisions/schema.js +143 -0
  124. package/dist/decisions/schema.js.map +1 -0
  125. package/dist/decisions/strategies/rules.d.ts +24 -0
  126. package/dist/decisions/strategies/rules.js +152 -0
  127. package/dist/decisions/strategies/rules.js.map +1 -0
  128. package/dist/decisions/strategies/score.d.ts +10 -0
  129. package/dist/decisions/strategies/score.js +29 -0
  130. package/dist/decisions/strategies/score.js.map +1 -0
  131. package/dist/decisions/types.d.ts +242 -0
  132. package/dist/decisions/types.js +2 -0
  133. package/dist/decisions/types.js.map +1 -0
  134. package/dist/earlyPatcher.d.ts +8 -20
  135. package/dist/earlyPatcher.js +13 -62
  136. package/dist/earlyPatcher.js.map +1 -1
  137. package/dist/editorLoader.d.ts +12 -0
  138. package/dist/editorLoader.js +132 -48
  139. package/dist/editorLoader.js.map +1 -1
  140. package/dist/events/EventBus.d.ts +59 -0
  141. package/dist/events/EventBus.js +152 -0
  142. package/dist/events/EventBus.js.map +1 -0
  143. package/dist/events/index.d.ts +9 -0
  144. package/dist/events/index.js +10 -0
  145. package/dist/events/index.js.map +1 -0
  146. package/dist/events/normalizers/canvas.d.ts +67 -0
  147. package/dist/events/normalizers/canvas.js +116 -0
  148. package/dist/events/normalizers/canvas.js.map +1 -0
  149. package/dist/events/normalizers/posthog.d.ts +53 -0
  150. package/dist/events/normalizers/posthog.js +163 -0
  151. package/dist/events/normalizers/posthog.js.map +1 -0
  152. package/dist/events/schema.d.ts +70 -0
  153. package/dist/events/schema.js +30 -0
  154. package/dist/events/schema.js.map +1 -0
  155. package/dist/events/types.d.ts +79 -0
  156. package/dist/events/types.js +49 -0
  157. package/dist/events/types.js.map +1 -0
  158. package/dist/experiments/adapters/growthbook.d.ts +4 -4
  159. package/dist/experiments/adapters/growthbook.js +5 -5
  160. package/dist/experiments/adapters/growthbook.js.map +1 -1
  161. package/dist/experiments/index.d.ts +3 -3
  162. package/dist/experiments/index.js +1 -1
  163. package/dist/experiments/registry.d.ts +2 -2
  164. package/dist/experiments/registry.js +2 -2
  165. package/dist/experiments/types.d.ts +5 -1
  166. package/dist/fetchers/cdnFetcher.d.ts +1 -1
  167. package/dist/fetchers/cdnFetcher.js +4 -8
  168. package/dist/fetchers/cdnFetcher.js.map +1 -1
  169. package/dist/fetchers/experimentsFetcher.d.ts +2 -2
  170. package/dist/fetchers/experimentsFetcher.js +7 -7
  171. package/dist/fetchers/experimentsFetcher.js.map +1 -1
  172. package/dist/fetchers/index.d.ts +3 -3
  173. package/dist/fetchers/index.js +2 -2
  174. package/dist/fetchers/index.js.map +1 -1
  175. package/dist/fetchers/registry.d.ts +1 -1
  176. package/dist/fetchers/registry.js +4 -4
  177. package/dist/fetchers/types.d.ts +1 -1
  178. package/dist/hooks/useCanvasOverlays.d.ts +8 -5
  179. package/dist/hooks/useCanvasOverlays.js +66 -17
  180. package/dist/hooks/useCanvasOverlays.js.map +1 -1
  181. package/dist/hooks/useHostPatches.d.ts +2 -2
  182. package/dist/hooks/useHostPatches.js +8 -8
  183. package/dist/hooks/useHostPatches.js.map +1 -1
  184. package/dist/hooks/useShadowCanvasConfig.d.ts +9 -9
  185. package/dist/hooks/useShadowCanvasConfig.js +24 -8
  186. package/dist/hooks/useShadowCanvasConfig.js.map +1 -1
  187. package/dist/hostPatcher/core/patcher.d.ts +1 -1
  188. package/dist/hostPatcher/core/patcher.js +18 -9
  189. package/dist/hostPatcher/core/patcher.js.map +1 -1
  190. package/dist/hostPatcher/core/sanitizer.js +24 -3
  191. package/dist/hostPatcher/core/sanitizer.js.map +1 -1
  192. package/dist/hostPatcher/policy/defaultPolicy.js +15 -5
  193. package/dist/hostPatcher/policy/defaultPolicy.js.map +1 -1
  194. package/dist/hostPatcher/utils/anchors.js +4 -6
  195. package/dist/hostPatcher/utils/anchors.js.map +1 -1
  196. package/dist/index.d.ts +34 -21
  197. package/dist/index.js +46 -19
  198. package/dist/index.js.map +1 -1
  199. package/dist/logger.d.ts +29 -0
  200. package/dist/logger.js +81 -0
  201. package/dist/logger.js.map +1 -0
  202. package/dist/metrics/index.d.ts +1 -1
  203. package/dist/metrics/index.js +1 -1
  204. package/dist/metrics/sessionMetrics.d.ts +1 -1
  205. package/dist/metrics/sessionMetrics.js +6 -6
  206. package/dist/overlays/fetcher.d.ts +2 -2
  207. package/dist/overlays/fetcher.js +4 -4
  208. package/dist/overlays/recipeRegistry.js +2 -2
  209. package/dist/overlays/recipeRegistry.js.map +1 -1
  210. package/dist/overlays/runtime/anchor/resolve.js +1 -1
  211. package/dist/overlays/runtime/anchor/resolve.js.map +1 -1
  212. package/dist/overlays/runtime/index.d.ts +7 -7
  213. package/dist/overlays/runtime/index.js +7 -7
  214. package/dist/overlays/runtime/overlay/highlight.js +39 -39
  215. package/dist/overlays/runtime/overlay/highlight.js.map +1 -1
  216. package/dist/overlays/runtime/overlay/modal.js +5 -5
  217. package/dist/overlays/runtime/overlay/modal.js.map +1 -1
  218. package/dist/overlays/runtime/overlay/root.js +1 -1
  219. package/dist/overlays/runtime/overlay/runner.js +70 -23
  220. package/dist/overlays/runtime/overlay/runner.js.map +1 -1
  221. package/dist/overlays/runtime/overlay/tooltip.d.ts +1 -1
  222. package/dist/overlays/runtime/overlay/tooltip.js +10 -10
  223. package/dist/overlays/runtime/overlay/tooltip.js.map +1 -1
  224. package/dist/overlays/runtime/utils/dom.js +4 -1
  225. package/dist/overlays/runtime/utils/dom.js.map +1 -1
  226. package/dist/overlays/schema.d.ts +98 -98
  227. package/dist/overlays/schema.js +12 -8
  228. package/dist/overlays/schema.js.map +1 -1
  229. package/dist/react.d.ts +7 -7
  230. package/dist/react.js +4 -4
  231. package/dist/react.js.map +1 -1
  232. package/dist/render/RenderContext.d.ts +2 -2
  233. package/dist/render/RenderContext.js +5 -5
  234. package/dist/render/RenderContext.js.map +1 -1
  235. package/dist/render/index.d.ts +3 -3
  236. package/dist/render/index.js +1 -1
  237. package/dist/render/types.d.ts +4 -4
  238. package/dist/runtime.d.ts +110 -0
  239. package/dist/runtime.js +206 -0
  240. package/dist/runtime.js.map +1 -0
  241. package/dist/smart-canvas.esm.js +155 -78
  242. package/dist/smart-canvas.esm.js.map +4 -4
  243. package/dist/smart-canvas.js +44390 -37343
  244. package/dist/smart-canvas.js.map +4 -4
  245. package/dist/smart-canvas.min.js +156 -78
  246. package/dist/smart-canvas.min.js.map +4 -4
  247. package/dist/state/StateStore.d.ts +41 -0
  248. package/dist/state/StateStore.js +170 -0
  249. package/dist/state/StateStore.js.map +1 -0
  250. package/dist/state/helpers/cooldowns.d.ts +7 -0
  251. package/dist/state/helpers/cooldowns.js +31 -0
  252. package/dist/state/helpers/cooldowns.js.map +1 -0
  253. package/dist/state/helpers/dismissals.d.ts +7 -0
  254. package/dist/state/helpers/dismissals.js +34 -0
  255. package/dist/state/helpers/dismissals.js.map +1 -0
  256. package/dist/state/helpers/frequency.d.ts +8 -0
  257. package/dist/state/helpers/frequency.js +43 -0
  258. package/dist/state/helpers/frequency.js.map +1 -0
  259. package/dist/state/index.d.ts +7 -0
  260. package/dist/state/index.js +7 -0
  261. package/dist/state/index.js.map +1 -0
  262. package/dist/state/schema.d.ts +49 -0
  263. package/dist/state/schema.js +25 -0
  264. package/dist/state/schema.js.map +1 -0
  265. package/dist/state/types.d.ts +137 -0
  266. package/dist/state/types.js +9 -0
  267. package/dist/state/types.js.map +1 -0
  268. package/dist/store/example.d.ts +1 -0
  269. package/dist/store/example.js +43 -0
  270. package/dist/store/example.js.map +1 -0
  271. package/dist/store/mini-effector.d.ts +46 -0
  272. package/dist/store/mini-effector.js +88 -0
  273. package/dist/store/mini-effector.js.map +1 -0
  274. package/dist/surfaces/Surfaces.d.ts +11 -0
  275. package/dist/surfaces/Surfaces.js +361 -0
  276. package/dist/surfaces/Surfaces.js.map +1 -0
  277. package/dist/surfaces/index.d.ts +9 -0
  278. package/dist/surfaces/index.js +12 -0
  279. package/dist/surfaces/index.js.map +1 -0
  280. package/dist/surfaces/positioning.d.ts +50 -0
  281. package/dist/surfaces/positioning.js +228 -0
  282. package/dist/surfaces/positioning.js.map +1 -0
  283. package/dist/surfaces/types.d.ts +167 -0
  284. package/dist/surfaces/types.js +23 -0
  285. package/dist/surfaces/types.js.map +1 -0
  286. package/dist/telemetry/adapters/noop.d.ts +12 -0
  287. package/dist/telemetry/adapters/noop.js +42 -0
  288. package/dist/telemetry/adapters/noop.js.map +1 -0
  289. package/dist/telemetry/adapters/posthog.d.ts +9 -3
  290. package/dist/telemetry/adapters/posthog.js +36 -14
  291. package/dist/telemetry/adapters/posthog.js.map +1 -1
  292. package/dist/telemetry/index.d.ts +4 -3
  293. package/dist/telemetry/index.js +3 -2
  294. package/dist/telemetry/index.js.map +1 -1
  295. package/dist/telemetry/registry.d.ts +2 -2
  296. package/dist/telemetry/registry.js +4 -2
  297. package/dist/telemetry/registry.js.map +1 -1
  298. package/dist/telemetry/types.d.ts +1 -1
  299. package/dist/theme/ThemeProvider.d.ts +2 -2
  300. package/dist/theme/ThemeProvider.js +21 -21
  301. package/dist/theme/ThemeProvider.js.map +1 -1
  302. package/dist/theme/defaultTheme.d.ts +2 -2
  303. package/dist/theme/defaultTheme.js +111 -111
  304. package/dist/theme/defaultTheme.js.map +1 -1
  305. package/dist/theme/extractHostTheme.d.ts +1 -1
  306. package/dist/theme/extractHostTheme.js +42 -44
  307. package/dist/theme/extractHostTheme.js.map +1 -1
  308. package/dist/theme/index.d.ts +5 -5
  309. package/dist/theme/index.js +3 -3
  310. package/dist/theme/index.js.map +1 -1
  311. package/dist/theme/types.d.ts +2 -2
  312. package/dist/token.d.ts +2 -0
  313. package/dist/token.js +3 -6
  314. package/dist/token.js.map +1 -1
  315. package/dist/types-only.d.ts +32 -0
  316. package/dist/types-only.js +11 -0
  317. package/dist/types-only.js.map +1 -0
  318. package/dist/types.d.ts +95 -54
  319. package/dist/types.js +15 -2
  320. package/dist/types.js.map +1 -1
  321. package/dist/version.d.ts +13 -0
  322. package/dist/version.js +14 -0
  323. package/dist/version.js.map +1 -0
  324. package/dist/widgets/WidgetRegistry.d.ts +139 -0
  325. package/dist/widgets/WidgetRegistry.js +182 -0
  326. package/dist/widgets/WidgetRegistry.js.map +1 -0
  327. package/dist/widgets/index.d.ts +7 -0
  328. package/dist/widgets/index.js +7 -0
  329. package/dist/widgets/index.js.map +1 -0
  330. package/package.json +27 -11
  331. package/schema/canvas-config.schema.json +666 -227
  332. 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 enhancement.
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
- - [Policy Tiers](#policy-tiers)
8
- - [Anchor Selectors](#anchor-selectors)
9
- - [Operations](#operations)
10
- - [Overlays](#overlays)
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 allows you to safely modify web pages through a controlled patching system. All modifications are applied through "patches" that contain:
16
- - An anchor selector to find the target element
17
- - One or more operations to apply
18
- - A policy tier that controls what modifications are allowed
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
- ## Policy Tiers
90
+ **Tile config example:**
21
91
 
22
- The SDK uses three policy tiers to control what operations are allowed:
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
- ### 1. **additive** (default)
25
- - **Purpose**: Safest tier - only add new elements and classes
26
- - **Allowed**: Adding HTML, adding prefixed classes, safe styles
27
- - **Not Allowed**: Modifying existing text or attributes
28
- - **Use When**: Adding badges, tooltips, or decorative elements
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
- ### 3. **surgical**
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
- ## Anchor Selectors
159
+ # @syntrologie/adapt-content
43
160
 
44
- Anchors define how to find elements in the DOM. The SDK supports four types:
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
- "by": "css",
50
- "value": "h1.hero-title"
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
- ### 2. Data Attribute
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
- "by": "data",
60
- "key": "testid",
61
- "value": "hero-banner" // optional
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
- ### 3. ARIA Selector
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
- "by": "aria",
72
- "role": "button", // optional
73
- "label": "Submit" // optional
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
- ### 4. Element Reference
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
- "by": "ref",
84
- "el": elementReference
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
- ## Operations
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": "setText",
97
- "text": "New headline text"
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
- ### 2. setStyle
106
- **Purpose**: Modify CSS styles on an element
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": "setStyle",
110
- "prop": "backgroundColor",
111
- "value": "#ff0000",
112
- "important": true // optional, adds !important
284
+ "kind": "remove_class",
285
+ "anchorId": ".pricing-card",
286
+ "className": "hidden"
113
287
  }
114
288
  ```
115
- - **Allowed Properties (any tier)**:
116
- - `outline`, `outlineColor`, `outlineWidth`, `outlineStyle`
117
- - `boxShadow`, `filter`
118
- - `scrollMargin` and related properties
119
- - CSS variables (`--custom-var`)
120
- - **Additional Properties (surgical tier)**:
121
- - `backgroundColor`, `color`
122
- - **Common Use**: Highlighting elements, adding visual emphasis
123
- - **Note**: Value can be `null` to remove a style
124
-
125
- ### 3. addClass
126
- **Purpose**: Add a CSS class to an element
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": "addClass",
130
- "className": "syntro-highlight"
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
- ### 4. removeClass
138
- **Purpose**: Remove a CSS class from an element
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": "removeClass",
142
- "className": "syntro-hidden"
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
- ### 5. setAttr
150
- **Purpose**: Set or remove HTML attributes
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": "setAttr",
154
- "name": "data-tracked",
155
- "value": "true" // or null to remove
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
- - **Allowed Attributes (any tier)**:
159
- - `data-*` (any data attribute)
160
- - `aria-*` (accessibility attributes)
161
- - `role`
162
- - **Additional Attributes (moderate/surgical)**:
163
- - `title`
164
- - **Common Use**: Tracking, accessibility improvements
165
- - **Note**: Setting to `null` removes the attribute
166
-
167
- ### 6. append
168
- **Purpose**: Add HTML content at the end of an element
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": "append",
172
- "html": "<span class='syntro-badge'>NEW</span>"
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
- ### 7. prepend
180
- **Purpose**: Add HTML content at the beginning of an element
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": "prepend",
184
- "html": "<span class='syntro-icon'>★</span>"
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
- ### 8. insertAdjacent
192
- **Purpose**: Insert HTML at specific positions relative to element
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": "insertAdjacent",
196
- "where": "afterbegin",
197
- "html": "<div class='syntro-alert'>Important!</div>"
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
- ## Overlays
620
+ ### tooltip
621
+
622
+ Shows a tooltip near an element with optional title, body, and CTA.
209
623
 
210
- The SDK supports overlay elements that don't modify the DOM. These are defined in `overlayRecipe.steps`:
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
- ### Tooltips
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
- "id": "feature_tooltip_1",
219
- "anchor": {
220
- "by": "css",
221
- "value": ".pricing-card"
222
- },
640
+ "anchorId": "#pricing-toggle",
223
641
  "content": {
224
- "title": "Best Value",
225
- "body": "This plan includes all features at the lowest per-user cost"
226
- },
227
- "placement": "top",
228
- "trigger": "hover",
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
- "dismiss": {
236
- "onEsc": true,
237
- "closeButton": true
238
- }
648
+ },
649
+ "placement": "bottom",
650
+ "trigger": "immediate"
239
651
  }
240
652
  ```
241
653
 
242
- **Properties:**
243
- - `kind`: Must be `"tooltip"`
244
- - `id`: Unique identifier for tracking
245
- - `anchor`: Element selector (same as patches)
246
- - `content.title`: Optional heading text
247
- - `content.body`: Required tooltip body text
248
- - `placement`: `"top"`, `"bottom"`, `"left"`, `"right"`, or `"auto"`
249
- - `trigger`: `"immediate"`, `"hover"`, or `"click"`
250
- - `offsetPx`: Distance from anchor element (optional)
251
- - `blocking`: If true, blocks page interaction (optional)
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": "highlight",
263
- "id": "cta_highlight_1",
264
- "anchor": {
265
- "by": "css",
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
- **Properties:**
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
- ### Overlay Recipe Structure
676
+ Adds a pulsing animation to draw attention.
300
677
 
301
- Overlays are defined in `overlayRecipe`:
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
- "overlayRecipe": {
306
- "id": "onboarding_flow",
307
- "version": 1,
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
- - `id`: Recipe identifier
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
- ## Best Practices
694
+ Shows a centered modal dialog with optional CTA buttons.
323
695
 
324
- ### 1. Element Selection
325
- - Use the most specific selector that won't break
326
- - Prefer data attributes over classes for stability
327
- - Test selectors across different page states
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
- ### 2. Tier Selection
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
- ### 3. Operation Order
335
- - Operations are applied in array order
336
- - Plan sequences carefully (e.g., addClass before setStyle)
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
- ### 4. Performance
340
- - Batch related patches together
341
- - Avoid selecting too many elements
342
- - Use CSS classes instead of inline styles when possible
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
- ### 5. Compatibility
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
- ## Example: Complete A/B Test
798
+ Control when adaptives activate using `DecisionStrategy`:
799
+
800
+ ### Rules Strategy
350
801
 
351
802
  ```json
352
803
  {
353
- "configVersion": "1.0",
354
- "patches": [
804
+ "type": "rules",
805
+ "rules": [
355
806
  {
356
- "id": "hero_headline_test",
357
- "anchor": {
358
- "by": "css",
359
- "value": "h1.hero-title"
360
- },
361
- "tier": "surgical",
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
- This example:
398
- 1. Changes the hero headline text (requires surgical tier)
399
- 2. Enhances the CTA button with shadow and an arrow icon (uses additive tier)
400
- 3. Properly tracks both changes with IDs
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