@syntrologie/adapt-overlays 2.16.0 → 2.18.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.
Files changed (226) hide show
  1. package/dist/chunk-VHAA22YE.js +14 -0
  2. package/dist/chunk-VHAA22YE.js.map +7 -0
  3. package/dist/runtime.d.ts +2 -1
  4. package/dist/runtime.d.ts.map +1 -1
  5. package/dist/runtime.js +2596 -354
  6. package/dist/runtime.js.map +7 -0
  7. package/dist/schema.d.ts +90 -21
  8. package/dist/schema.d.ts.map +1 -1
  9. package/dist/schema.js +440 -151
  10. package/dist/schema.js.map +7 -0
  11. package/dist/types.d.ts +1 -1
  12. package/dist/types.d.ts.map +1 -1
  13. package/package.json +5 -22
  14. package/dist/WorkflowTracker.d.ts +0 -10
  15. package/dist/WorkflowTracker.d.ts.map +0 -1
  16. package/dist/WorkflowTracker.js +0 -19
  17. package/dist/WorkflowWidget.d.ts +0 -70
  18. package/dist/WorkflowWidget.d.ts.map +0 -1
  19. package/dist/WorkflowWidget.js +0 -330
  20. package/dist/WorkflowWidgetLit.js +0 -617
  21. package/dist/cdn.d.ts +0 -35
  22. package/dist/cdn.d.ts.map +0 -1
  23. package/dist/cdn.js +0 -39
  24. package/dist/celebrations/__tests__/engine.test.js +0 -130
  25. package/dist/celebrations/__tests__/executor.test.js +0 -102
  26. package/dist/celebrations/__tests__/reduced-motion.test.js +0 -97
  27. package/dist/celebrations/effects/__tests__/confetti.test.js +0 -89
  28. package/dist/celebrations/effects/__tests__/emoji-rain.test.js +0 -88
  29. package/dist/celebrations/effects/__tests__/fireworks.test.js +0 -87
  30. package/dist/celebrations/effects/__tests__/sparkles.test.js +0 -79
  31. package/dist/celebrations/effects/confetti.js +0 -80
  32. package/dist/celebrations/effects/emoji-rain.js +0 -73
  33. package/dist/celebrations/effects/fireworks.js +0 -69
  34. package/dist/celebrations/effects/sparkles.js +0 -83
  35. package/dist/celebrations/engine.js +0 -93
  36. package/dist/celebrations/index.js +0 -73
  37. package/dist/celebrations/types.js +0 -1
  38. package/dist/editor.d.ts +0 -27
  39. package/dist/editor.d.ts.map +0 -1
  40. package/dist/editor.js +0 -22
  41. package/dist/executors/tour.js +0 -335
  42. package/dist/highlight.js +0 -180
  43. package/dist/modal.js +0 -218
  44. package/dist/overlay-editor-state.d.ts +0 -41
  45. package/dist/overlay-editor-state.d.ts.map +0 -1
  46. package/dist/overlay-editor-state.js +0 -131
  47. package/dist/overlay-editor-ui.d.ts +0 -9
  48. package/dist/overlay-editor-ui.d.ts.map +0 -1
  49. package/dist/overlay-editor-ui.js +0 -306
  50. package/dist/runtime-lit.d.ts +0 -94
  51. package/dist/runtime-lit.d.ts.map +0 -1
  52. package/dist/runtime-lit.js +0 -402
  53. package/dist/sanitizer.js +0 -84
  54. package/dist/summarize.js +0 -86
  55. package/dist/tooltip.js +0 -279
  56. package/dist/tour-types.js +0 -7
  57. package/dist/types.js +0 -7
  58. package/dist/workflow-types.js +0 -1
  59. package/node_modules/@syntro/design-system/README.md +0 -335
  60. package/node_modules/@syntro/design-system/dist/assets/syntrologie-logo.svg +0 -21
  61. package/node_modules/@syntro/design-system/dist/assets/syntrologie-logomark.svg +0 -10
  62. package/node_modules/@syntro/design-system/dist/index.d.ts +0 -8
  63. package/node_modules/@syntro/design-system/dist/index.d.ts.map +0 -1
  64. package/node_modules/@syntro/design-system/dist/index.js +0 -7
  65. package/node_modules/@syntro/design-system/dist/tailwind-preset.d.ts +0 -19
  66. package/node_modules/@syntro/design-system/dist/tailwind-preset.d.ts.map +0 -1
  67. package/node_modules/@syntro/design-system/dist/tailwind-preset.js +0 -455
  68. package/node_modules/@syntro/design-system/dist/tokens/colors.css +0 -464
  69. package/node_modules/@syntro/design-system/dist/tokens/colors.d.ts +0 -874
  70. package/node_modules/@syntro/design-system/dist/tokens/colors.d.ts.map +0 -1
  71. package/node_modules/@syntro/design-system/dist/tokens/colors.js +0 -564
  72. package/node_modules/@syntro/design-system/dist/tokens/effects.css +0 -43
  73. package/node_modules/@syntro/design-system/dist/tokens/effects.d.ts +0 -139
  74. package/node_modules/@syntro/design-system/dist/tokens/effects.d.ts.map +0 -1
  75. package/node_modules/@syntro/design-system/dist/tokens/effects.js +0 -121
  76. package/node_modules/@syntro/design-system/dist/tokens/index.d.ts +0 -12
  77. package/node_modules/@syntro/design-system/dist/tokens/index.d.ts.map +0 -1
  78. package/node_modules/@syntro/design-system/dist/tokens/index.js +0 -11
  79. package/node_modules/@syntro/design-system/dist/tokens/panel-shell.d.ts +0 -93
  80. package/node_modules/@syntro/design-system/dist/tokens/panel-shell.d.ts.map +0 -1
  81. package/node_modules/@syntro/design-system/dist/tokens/panel-shell.js +0 -72
  82. package/node_modules/@syntro/design-system/package.json +0 -55
  83. package/node_modules/@syntro/design-system/src/assets/syntrologie-logo.svg +0 -21
  84. package/node_modules/@syntro/design-system/src/assets/syntrologie-logomark.svg +0 -10
  85. package/node_modules/@syntrologie/shared-editor-ui/dist/cn.d.ts +0 -2
  86. package/node_modules/@syntrologie/shared-editor-ui/dist/cn.d.ts.map +0 -1
  87. package/node_modules/@syntrologie/shared-editor-ui/dist/cn.js +0 -3
  88. package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPicker.d.ts +0 -34
  89. package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPicker.d.ts.map +0 -1
  90. package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPicker.js +0 -161
  91. package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPickerLit.d.ts +0 -84
  92. package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPickerLit.d.ts.map +0 -1
  93. package/node_modules/@syntrologie/shared-editor-ui/dist/components/AnchorPickerLit.js +0 -323
  94. package/node_modules/@syntrologie/shared-editor-ui/dist/components/BeforeAfterToggle.d.ts +0 -7
  95. package/node_modules/@syntrologie/shared-editor-ui/dist/components/BeforeAfterToggle.d.ts.map +0 -1
  96. package/node_modules/@syntrologie/shared-editor-ui/dist/components/BeforeAfterToggle.js +0 -9
  97. package/node_modules/@syntrologie/shared-editor-ui/dist/components/BeforeAfterToggleLit.d.ts +0 -25
  98. package/node_modules/@syntrologie/shared-editor-ui/dist/components/BeforeAfterToggleLit.d.ts.map +0 -1
  99. package/node_modules/@syntrologie/shared-editor-ui/dist/components/BeforeAfterToggleLit.js +0 -55
  100. package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLine.d.ts +0 -23
  101. package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLine.d.ts.map +0 -1
  102. package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLine.js +0 -40
  103. package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLineLit.d.ts +0 -33
  104. package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLineLit.d.ts.map +0 -1
  105. package/node_modules/@syntrologie/shared-editor-ui/dist/components/ConditionStatusLineLit.js +0 -118
  106. package/node_modules/@syntrologie/shared-editor-ui/dist/components/DetectionBadge.d.ts +0 -7
  107. package/node_modules/@syntrologie/shared-editor-ui/dist/components/DetectionBadge.d.ts.map +0 -1
  108. package/node_modules/@syntrologie/shared-editor-ui/dist/components/DetectionBadge.js +0 -22
  109. package/node_modules/@syntrologie/shared-editor-ui/dist/components/DetectionBadgeLit.d.ts +0 -32
  110. package/node_modules/@syntrologie/shared-editor-ui/dist/components/DetectionBadgeLit.d.ts.map +0 -1
  111. package/node_modules/@syntrologie/shared-editor-ui/dist/components/DetectionBadgeLit.js +0 -68
  112. package/node_modules/@syntrologie/shared-editor-ui/dist/components/DismissedSection.d.ts +0 -8
  113. package/node_modules/@syntrologie/shared-editor-ui/dist/components/DismissedSection.d.ts.map +0 -1
  114. package/node_modules/@syntrologie/shared-editor-ui/dist/components/DismissedSection.js +0 -9
  115. package/node_modules/@syntrologie/shared-editor-ui/dist/components/DismissedSectionLit.d.ts +0 -34
  116. package/node_modules/@syntrologie/shared-editor-ui/dist/components/DismissedSectionLit.d.ts.map +0 -1
  117. package/node_modules/@syntrologie/shared-editor-ui/dist/components/DismissedSectionLit.js +0 -57
  118. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditBackButton.d.ts +0 -7
  119. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditBackButton.d.ts.map +0 -1
  120. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditBackButton.js +0 -4
  121. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditBackButtonLit.d.ts +0 -13
  122. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditBackButtonLit.d.ts.map +0 -1
  123. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditBackButtonLit.js +0 -31
  124. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorBody.d.ts +0 -7
  125. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorBody.d.ts.map +0 -1
  126. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorBody.js +0 -4
  127. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorBodyLit.d.ts +0 -7
  128. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorBodyLit.d.ts.map +0 -1
  129. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorBodyLit.js +0 -15
  130. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorCard.d.ts +0 -13
  131. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorCard.d.ts.map +0 -1
  132. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorCard.js +0 -15
  133. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorCardLit.d.ts +0 -36
  134. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorCardLit.d.ts.map +0 -1
  135. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorCardLit.js +0 -102
  136. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorFooter.d.ts +0 -7
  137. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorFooter.d.ts.map +0 -1
  138. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorFooter.js +0 -4
  139. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorFooterLit.d.ts +0 -20
  140. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorFooterLit.d.ts.map +0 -1
  141. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorFooterLit.js +0 -48
  142. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorHeader.d.ts +0 -9
  143. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorHeader.d.ts.map +0 -1
  144. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorHeader.js +0 -4
  145. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorHeaderLit.d.ts +0 -16
  146. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorHeaderLit.d.ts.map +0 -1
  147. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorHeaderLit.js +0 -25
  148. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorInput.d.ts +0 -8
  149. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorInput.d.ts.map +0 -1
  150. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorInput.js +0 -8
  151. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorInputLit.d.ts +0 -66
  152. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorInputLit.d.ts.map +0 -1
  153. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorInputLit.js +0 -87
  154. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorLayout.d.ts +0 -7
  155. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorLayout.d.ts.map +0 -1
  156. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorLayout.js +0 -4
  157. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorLayoutLit.d.ts +0 -7
  158. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorLayoutLit.d.ts.map +0 -1
  159. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorLayoutLit.js +0 -15
  160. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShell.d.ts +0 -25
  161. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShell.d.ts.map +0 -1
  162. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShell.js +0 -390
  163. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShellLit.d.ts +0 -66
  164. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShellLit.d.ts.map +0 -1
  165. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorPanelShellLit.js +0 -528
  166. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorSelect.d.ts +0 -8
  167. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorSelect.d.ts.map +0 -1
  168. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorSelect.js +0 -8
  169. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorSelectLit.d.ts +0 -41
  170. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorSelectLit.d.ts.map +0 -1
  171. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorSelectLit.js +0 -63
  172. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorTextarea.d.ts +0 -8
  173. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorTextarea.d.ts.map +0 -1
  174. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorTextarea.js +0 -17
  175. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorTextareaLit.d.ts +0 -55
  176. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorTextareaLit.d.ts.map +0 -1
  177. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EditorTextareaLit.js +0 -92
  178. package/node_modules/@syntrologie/shared-editor-ui/dist/components/ElementHighlight.d.ts +0 -32
  179. package/node_modules/@syntrologie/shared-editor-ui/dist/components/ElementHighlight.d.ts.map +0 -1
  180. package/node_modules/@syntrologie/shared-editor-ui/dist/components/ElementHighlight.js +0 -85
  181. package/node_modules/@syntrologie/shared-editor-ui/dist/components/ElementHighlightLit.d.ts +0 -90
  182. package/node_modules/@syntrologie/shared-editor-ui/dist/components/ElementHighlightLit.d.ts.map +0 -1
  183. package/node_modules/@syntrologie/shared-editor-ui/dist/components/ElementHighlightLit.js +0 -242
  184. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EmptyState.d.ts +0 -6
  185. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EmptyState.d.ts.map +0 -1
  186. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EmptyState.js +0 -4
  187. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EmptyStateLit.d.ts +0 -12
  188. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EmptyStateLit.d.ts.map +0 -1
  189. package/node_modules/@syntrologie/shared-editor-ui/dist/components/EmptyStateLit.js +0 -21
  190. package/node_modules/@syntrologie/shared-editor-ui/dist/components/GroupHeader.d.ts +0 -8
  191. package/node_modules/@syntrologie/shared-editor-ui/dist/components/GroupHeader.d.ts.map +0 -1
  192. package/node_modules/@syntrologie/shared-editor-ui/dist/components/GroupHeader.js +0 -5
  193. package/node_modules/@syntrologie/shared-editor-ui/dist/components/GroupHeaderLit.d.ts +0 -21
  194. package/node_modules/@syntrologie/shared-editor-ui/dist/components/GroupHeaderLit.d.ts.map +0 -1
  195. package/node_modules/@syntrologie/shared-editor-ui/dist/components/GroupHeaderLit.js +0 -33
  196. package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourney.d.ts +0 -12
  197. package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourney.d.ts.map +0 -1
  198. package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourney.js +0 -40
  199. package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourneyLit.d.ts +0 -28
  200. package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourneyLit.d.ts.map +0 -1
  201. package/node_modules/@syntrologie/shared-editor-ui/dist/components/TriggerJourneyLit.js +0 -121
  202. package/node_modules/@syntrologie/shared-editor-ui/dist/controllers/PanelShellController.d.ts +0 -110
  203. package/node_modules/@syntrologie/shared-editor-ui/dist/controllers/PanelShellController.d.ts.map +0 -1
  204. package/node_modules/@syntrologie/shared-editor-ui/dist/controllers/PanelShellController.js +0 -481
  205. package/node_modules/@syntrologie/shared-editor-ui/dist/formatConditionLabel.d.ts +0 -26
  206. package/node_modules/@syntrologie/shared-editor-ui/dist/formatConditionLabel.d.ts.map +0 -1
  207. package/node_modules/@syntrologie/shared-editor-ui/dist/formatConditionLabel.js +0 -202
  208. package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useElementRect.d.ts +0 -8
  209. package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useElementRect.d.ts.map +0 -1
  210. package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useElementRect.js +0 -46
  211. package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useTriggerWhenStatus.d.ts +0 -24
  212. package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useTriggerWhenStatus.d.ts.map +0 -1
  213. package/node_modules/@syntrologie/shared-editor-ui/dist/hooks/useTriggerWhenStatus.js +0 -86
  214. package/node_modules/@syntrologie/shared-editor-ui/dist/index.d.ts +0 -36
  215. package/node_modules/@syntrologie/shared-editor-ui/dist/index.d.ts.map +0 -1
  216. package/node_modules/@syntrologie/shared-editor-ui/dist/index.js +0 -26
  217. package/node_modules/@syntrologie/shared-editor-ui/dist/lit-elements.d.ts +0 -15
  218. package/node_modules/@syntrologie/shared-editor-ui/dist/lit-elements.d.ts.map +0 -1
  219. package/node_modules/@syntrologie/shared-editor-ui/dist/lit-elements.js +0 -14
  220. package/node_modules/@syntrologie/shared-editor-ui/dist/utils/elementChainRecommender.d.ts +0 -33
  221. package/node_modules/@syntrologie/shared-editor-ui/dist/utils/elementChainRecommender.d.ts.map +0 -1
  222. package/node_modules/@syntrologie/shared-editor-ui/dist/utils/elementChainRecommender.js +0 -68
  223. package/node_modules/@syntrologie/shared-editor-ui/dist/utils/selectorGenerator.d.ts +0 -22
  224. package/node_modules/@syntrologie/shared-editor-ui/dist/utils/selectorGenerator.d.ts.map +0 -1
  225. package/node_modules/@syntrologie/shared-editor-ui/dist/utils/selectorGenerator.js +0 -143
  226. package/node_modules/@syntrologie/shared-editor-ui/package.json +0 -55
package/dist/runtime.js CHANGED
@@ -1,135 +1,2413 @@
1
- /**
2
- * Adaptive Overlays - Runtime Module
3
- *
4
- * Visual overlay actions: highlight, pulse, badge, tooltip, celebrations.
5
- */
6
- import { executeCelebrate } from './celebrations';
7
- import { executeTour } from './executors/tour';
8
- import { showHighlight } from './highlight';
9
- import { executeModal } from './modal';
10
- import { sanitizeHtml } from './sanitizer';
11
- import { showTooltip } from './tooltip';
12
- import { WorkflowMountableWidget } from './WorkflowWidget';
13
- import { WorkflowWidgetLitMountable } from './WorkflowWidgetLit';
14
- // Re-export executeModal and executeTour for use by other packages
15
- export { executeModal, executeTour };
16
- // ============================================================================
17
- // Executors
18
- // ============================================================================
19
- /**
20
- * Execute a highlight action
21
- */
22
- export const executeHighlight = async (action, context) => {
23
- let anchorEl = context.resolveAnchor(action.anchorId);
24
- if (!anchorEl && context.waitForAnchor) {
25
- anchorEl = await context.waitForAnchor(action.anchorId, 3000);
26
- }
27
- if (!anchorEl) {
28
- console.warn(`[adaptive-overlays] Anchor not found after waiting: ${action.anchorId.selector}`);
29
- return { cleanup: () => { } };
30
- }
31
- // Skip re-application if the user previously dismissed this highlight.
32
- // The dismissed attribute is set by onDismiss (user click-outside / Esc)
33
- // and survives config-driven revert+reapply cycles.
34
- if (anchorEl.getAttribute('data-syntro-highlight-dismissed')) {
35
- return { cleanup: () => { } };
36
- }
37
- // Self-guard: destroy any existing highlight on this anchor to prevent duplicates
38
- // (batch re-apply on navigation can call this executor multiple times for the same anchor)
39
- const existing = anchorEl.getAttribute('data-syntro-highlight');
40
- if (existing) {
41
- const prev = context.overlayRoot.querySelectorAll('.syntro-spotlight-scrim, .syntro-spotlight-ring');
42
- prev.forEach((el) => el.remove());
43
- }
44
- anchorEl.setAttribute('data-syntro-highlight', 'true');
45
- // Resolve ring color: action override → theme primary → host page var → fallback
46
- let ringColor = action.style?.color;
47
- if (!ringColor) {
48
- try {
49
- const primary = getComputedStyle(context.overlayRoot)
50
- .getPropertyValue('--sc-color-primary')
51
- ?.trim();
52
- if (primary)
53
- ringColor = primary;
1
+ import {
2
+ __privateAdd,
3
+ __privateGet,
4
+ __privateSet
5
+ } from "./chunk-VHAA22YE.js";
6
+
7
+ // src/celebrations/effects/confetti.ts
8
+ var INTENSITY_COUNTS = { light: 50, medium: 100, heavy: 200 };
9
+ var DEFAULT_COLORS = [
10
+ "#ff0000",
11
+ "#00ff00",
12
+ "#0000ff",
13
+ "#ffff00",
14
+ "#ff00ff",
15
+ "#00ffff",
16
+ "#ff8800",
17
+ "#8800ff"
18
+ ];
19
+ var confettiEffect = {
20
+ init(width, height, config) {
21
+ const count = INTENSITY_COUNTS[config.intensity];
22
+ const colors = config.colors.length > 0 ? config.colors : DEFAULT_COLORS;
23
+ const particles = [];
24
+ for (let i = 0; i < count; i++) {
25
+ particles.push({
26
+ x: Math.random() * width,
27
+ y: Math.random() * -height * 0.3,
28
+ vx: (Math.random() - 0.5) * 4,
29
+ vy: Math.random() * 2 + 1,
30
+ rotation: Math.random() * Math.PI * 2,
31
+ rotationSpeed: (Math.random() - 0.5) * 0.2,
32
+ size: Math.random() * 6 + 4,
33
+ color: colors[Math.floor(Math.random() * colors.length)],
34
+ opacity: 1,
35
+ shape: Math.random() > 0.5 ? "rect" : "circle"
36
+ });
37
+ }
38
+ return particles;
39
+ },
40
+ update(particles, _dt, _elapsed) {
41
+ let anyVisible = false;
42
+ for (const p of particles) {
43
+ p.vy += 0.15;
44
+ p.vx *= 0.99;
45
+ p.x += p.vx;
46
+ p.y += p.vy;
47
+ p.rotation += p.rotationSpeed;
48
+ if (p.y > 0 && p.opacity > 0) {
49
+ if (p.y > 500) {
50
+ p.opacity -= 0.02;
51
+ if (p.opacity < 0) p.opacity = 0;
52
+ }
53
+ }
54
+ if (p.opacity > 0.01) {
55
+ anyVisible = true;
56
+ }
57
+ }
58
+ return anyVisible;
59
+ },
60
+ render(ctx, particles) {
61
+ for (const p of particles) {
62
+ if (p.opacity < 0.01) continue;
63
+ ctx.save();
64
+ ctx.globalAlpha = p.opacity;
65
+ ctx.fillStyle = p.color;
66
+ ctx.translate(p.x, p.y);
67
+ ctx.rotate(p.rotation);
68
+ if (p.shape === "circle") {
69
+ ctx.beginPath();
70
+ ctx.arc(0, 0, p.size / 2, 0, Math.PI * 2);
71
+ ctx.fill();
72
+ } else {
73
+ ctx.fillRect(-p.size / 2, -p.size / 2, p.size, p.size * 0.6);
74
+ }
75
+ ctx.restore();
76
+ }
77
+ }
78
+ };
79
+
80
+ // src/celebrations/effects/emoji-rain.ts
81
+ var INTENSITY_COUNTS2 = { light: 20, medium: 40, heavy: 80 };
82
+ var DEFAULT_EMOJI = "\u{1F389}";
83
+ var emojiRainEffect = {
84
+ init(width, _height, config) {
85
+ const count = INTENSITY_COUNTS2[config.intensity];
86
+ const emoji = typeof config.props?.emoji === "string" ? config.props.emoji : DEFAULT_EMOJI;
87
+ const particles = [];
88
+ for (let i = 0; i < count; i++) {
89
+ particles.push({
90
+ x: Math.random() * width,
91
+ y: Math.random() * -200,
92
+ vx: 0,
93
+ vy: Math.random() * 1.5 + 1,
94
+ rotation: 0,
95
+ rotationSpeed: 0,
96
+ size: Math.random() * 12 + 16,
97
+ color: "#000000",
98
+ opacity: 1,
99
+ shape: "emoji",
100
+ emoji,
101
+ data: {
102
+ /** Phase offset for horizontal wobble */
103
+ wobblePhase: Math.random() * Math.PI * 2,
104
+ /** Amplitude of horizontal wobble */
105
+ wobbleAmp: Math.random() * 1.5 + 0.5,
106
+ /** Original x for wobble base */
107
+ originX: 0
54
108
  }
55
- catch {
56
- /* fallback to showHighlight default */
109
+ });
110
+ }
111
+ for (const p of particles) {
112
+ if (p.data) p.data.originX = p.x;
113
+ }
114
+ return particles;
115
+ },
116
+ update(particles, _dt, elapsed) {
117
+ let anyVisible = false;
118
+ for (const p of particles) {
119
+ p.y += p.vy;
120
+ const phase = p.data?.wobblePhase ?? 0;
121
+ const amp = p.data?.wobbleAmp ?? 1;
122
+ const originX = p.data?.originX ?? p.x;
123
+ p.x = originX + Math.sin(elapsed * 3e-3 + phase) * amp * 20;
124
+ if (p.y > 600) {
125
+ p.opacity -= 0.02;
126
+ if (p.opacity < 0) p.opacity = 0;
127
+ }
128
+ if (p.opacity > 0.01) {
129
+ anyVisible = true;
130
+ }
131
+ }
132
+ return anyVisible;
133
+ },
134
+ render(ctx, particles) {
135
+ for (const p of particles) {
136
+ if (p.opacity < 0.01 || !p.emoji) continue;
137
+ ctx.save();
138
+ ctx.globalAlpha = p.opacity;
139
+ ctx.font = `${p.size}px serif`;
140
+ ctx.textAlign = "center";
141
+ ctx.textBaseline = "middle";
142
+ ctx.fillText(p.emoji, p.x, p.y);
143
+ ctx.restore();
144
+ }
145
+ }
146
+ };
147
+
148
+ // src/celebrations/effects/fireworks.ts
149
+ var PARTICLES_PER_BURST = { light: 20, medium: 40, heavy: 80 };
150
+ var BURST_COUNTS = { light: 3, medium: 4, heavy: 5 };
151
+ var fireworksEffect = {
152
+ init(width, height, config) {
153
+ const perBurst = PARTICLES_PER_BURST[config.intensity];
154
+ const burstCount = BURST_COUNTS[config.intensity];
155
+ const colors = config.colors.length > 0 ? config.colors : ["#ff4444", "#44ff44", "#4444ff"];
156
+ const particles = [];
157
+ for (let b = 0; b < burstCount; b++) {
158
+ const cx = Math.random() * width * 0.8 + width * 0.1;
159
+ const cy = Math.random() * height * 0.6;
160
+ const burstColor = colors[Math.floor(Math.random() * colors.length)];
161
+ for (let i = 0; i < perBurst; i++) {
162
+ const angle = Math.PI * 2 * i / perBurst + (Math.random() - 0.5) * 0.3;
163
+ const speed = Math.random() * 3 + 2;
164
+ particles.push({
165
+ x: cx,
166
+ y: cy,
167
+ vx: Math.cos(angle) * speed,
168
+ vy: Math.sin(angle) * speed,
169
+ rotation: 0,
170
+ rotationSpeed: 0,
171
+ size: Math.random() * 3 + 2,
172
+ color: burstColor,
173
+ opacity: 1,
174
+ shape: "circle",
175
+ data: { centerX: cx, centerY: cy }
176
+ });
177
+ }
178
+ }
179
+ return particles;
180
+ },
181
+ update(particles, _dt, _elapsed) {
182
+ let anyVisible = false;
183
+ for (const p of particles) {
184
+ p.vx *= 0.97;
185
+ p.vy *= 0.97;
186
+ p.vy += 0.03;
187
+ p.x += p.vx;
188
+ p.y += p.vy;
189
+ p.opacity -= 8e-3;
190
+ if (p.opacity < 0) p.opacity = 0;
191
+ if (p.opacity > 0.01) {
192
+ anyVisible = true;
193
+ }
194
+ }
195
+ return anyVisible;
196
+ },
197
+ render(ctx, particles) {
198
+ for (const p of particles) {
199
+ if (p.opacity < 0.01) continue;
200
+ ctx.save();
201
+ ctx.globalAlpha = p.opacity;
202
+ ctx.fillStyle = p.color;
203
+ ctx.shadowBlur = 12;
204
+ ctx.shadowColor = p.color;
205
+ ctx.beginPath();
206
+ ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
207
+ ctx.fill();
208
+ ctx.restore();
209
+ }
210
+ }
211
+ };
212
+
213
+ // src/celebrations/effects/sparkles.ts
214
+ var INTENSITY_COUNTS3 = { light: 30, medium: 60, heavy: 120 };
215
+ var sparklesEffect = {
216
+ init(width, height, config) {
217
+ const count = INTENSITY_COUNTS3[config.intensity];
218
+ const colors = config.colors.length > 0 ? config.colors : ["#ffd700", "#ffffff", "#fffacd"];
219
+ const particles = [];
220
+ for (let i = 0; i < count; i++) {
221
+ particles.push({
222
+ x: Math.random() * width,
223
+ y: Math.random() * height,
224
+ vx: (Math.random() - 0.5) * 0.3,
225
+ vy: -(Math.random() * 0.5 + 0.2),
226
+ rotation: Math.random() * Math.PI * 2,
227
+ rotationSpeed: (Math.random() - 0.5) * 0.1,
228
+ size: Math.random() * 4 + 2,
229
+ color: colors[Math.floor(Math.random() * colors.length)],
230
+ opacity: Math.random() * 0.5 + 0.5,
231
+ shape: "circle",
232
+ data: {
233
+ /** Phase offset for sine-wave twinkle */
234
+ phase: Math.random() * Math.PI * 2,
235
+ /** Base opacity before twinkle modulation */
236
+ baseOpacity: Math.random() * 0.5 + 0.5
57
237
  }
238
+ });
239
+ }
240
+ return particles;
241
+ },
242
+ update(particles, _dt, elapsed) {
243
+ let anyVisible = false;
244
+ for (const p of particles) {
245
+ p.x += p.vx;
246
+ p.y += p.vy;
247
+ p.rotation += p.rotationSpeed;
248
+ if (p.data) {
249
+ p.data.baseOpacity = (p.data.baseOpacity ?? 1) - 1e-3;
250
+ if (p.data.baseOpacity < 0) p.data.baseOpacity = 0;
251
+ }
252
+ const phase = p.data?.phase ?? 0;
253
+ const baseOpacity = p.data?.baseOpacity ?? 1;
254
+ if (baseOpacity <= 0.01) {
255
+ p.opacity = 0;
256
+ } else {
257
+ const twinkle = Math.sin(elapsed * 5e-3 + phase) * 0.4 + 0.6;
258
+ p.opacity = baseOpacity * twinkle;
259
+ }
260
+ if (p.opacity > 0.01) {
261
+ anyVisible = true;
262
+ }
263
+ }
264
+ return anyVisible;
265
+ },
266
+ render(ctx, particles) {
267
+ for (const p of particles) {
268
+ if (p.opacity < 0.01) continue;
269
+ ctx.save();
270
+ ctx.globalAlpha = p.opacity;
271
+ ctx.fillStyle = p.color;
272
+ ctx.translate(p.x, p.y);
273
+ ctx.rotate(p.rotation);
274
+ const s = p.size;
275
+ ctx.beginPath();
276
+ ctx.moveTo(0, -s);
277
+ ctx.lineTo(s * 0.3, -s * 0.3);
278
+ ctx.lineTo(s, 0);
279
+ ctx.lineTo(s * 0.3, s * 0.3);
280
+ ctx.lineTo(0, s);
281
+ ctx.lineTo(-s * 0.3, s * 0.3);
282
+ ctx.lineTo(-s, 0);
283
+ ctx.lineTo(-s * 0.3, -s * 0.3);
284
+ ctx.closePath();
285
+ ctx.fill();
286
+ ctx.restore();
58
287
  }
59
- const handle = showHighlight(anchorEl, context.overlayRoot, {
60
- paddingPx: action.style?.paddingPx ?? 12,
61
- radiusPx: action.style?.radiusPx ?? 12,
62
- scrimOpacity: action.style?.scrimOpacity ?? 0.55,
63
- ringColor,
64
- blocking: action.blocking ?? false,
65
- onClickOutside: action.onClickOutside ?? true,
66
- onEsc: action.onEsc ?? true,
67
- onDismiss: () => {
68
- anchorEl.setAttribute('data-syntro-highlight-dismissed', 'true');
69
- },
288
+ }
289
+ };
290
+
291
+ // src/celebrations/engine.ts
292
+ var CelebrationEngine = class {
293
+ constructor() {
294
+ this.canvas = null;
295
+ this.ctx = null;
296
+ this.rafId = null;
297
+ this.particles = [];
298
+ this.startTime = 0;
299
+ this.lastFrame = 0;
300
+ this.duration = 0;
301
+ this.effect = null;
302
+ this.container = null;
303
+ }
304
+ start(container, effect, config) {
305
+ const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
306
+ if (prefersReducedMotion) {
307
+ return;
308
+ }
309
+ this.container = container;
310
+ this.effect = effect;
311
+ this.duration = config.duration;
312
+ const canvas = document.createElement("canvas");
313
+ canvas.setAttribute("data-syntro-celebrate", "");
314
+ Object.assign(canvas.style, {
315
+ position: "fixed",
316
+ inset: "0",
317
+ pointerEvents: "none",
318
+ zIndex: "2147483646"
70
319
  });
71
- context.publishEvent('action.applied', {
72
- id: context.generateId(),
73
- kind: 'overlays:highlight',
74
- anchorId: action.anchorId,
320
+ const dpr = window.devicePixelRatio || 1;
321
+ const width = window.innerWidth;
322
+ const height = window.innerHeight;
323
+ canvas.width = width * dpr;
324
+ canvas.height = height * dpr;
325
+ canvas.style.width = `${width}px`;
326
+ canvas.style.height = `${height}px`;
327
+ const ctx = canvas.getContext("2d");
328
+ if (!ctx) {
329
+ return;
330
+ }
331
+ ctx.scale(dpr, dpr);
332
+ this.canvas = canvas;
333
+ this.ctx = ctx;
334
+ container.appendChild(canvas);
335
+ this.particles = effect.init(width, height, config);
336
+ this.startTime = performance.now();
337
+ this.lastFrame = this.startTime;
338
+ this.tick = this.tick.bind(this);
339
+ this.rafId = requestAnimationFrame(this.tick);
340
+ }
341
+ stop() {
342
+ if (this.rafId !== null) {
343
+ cancelAnimationFrame(this.rafId);
344
+ this.rafId = null;
345
+ }
346
+ if (this.canvas && this.container) {
347
+ this.canvas.remove();
348
+ this.canvas = null;
349
+ }
350
+ this.ctx = null;
351
+ this.effect = null;
352
+ this.container = null;
353
+ this.particles = [];
354
+ }
355
+ tick(now) {
356
+ if (!this.ctx || !this.canvas || !this.effect) return;
357
+ const elapsed = now - this.startTime;
358
+ const dt = now - this.lastFrame;
359
+ this.lastFrame = now;
360
+ if (elapsed >= this.duration) {
361
+ this.stop();
362
+ return;
363
+ }
364
+ const width = this.canvas.width / (window.devicePixelRatio || 1);
365
+ const height = this.canvas.height / (window.devicePixelRatio || 1);
366
+ this.ctx.clearRect(0, 0, width, height);
367
+ const alive = this.effect.update(this.particles, dt, elapsed);
368
+ if (!alive) {
369
+ this.stop();
370
+ return;
371
+ }
372
+ this.effect.render(this.ctx, this.particles);
373
+ this.rafId = requestAnimationFrame(this.tick);
374
+ }
375
+ };
376
+
377
+ // src/celebrations/index.ts
378
+ var FALLBACK_COLORS = [
379
+ "#ff0000",
380
+ "#00ff00",
381
+ "#0000ff",
382
+ "#ffff00",
383
+ "#ff00ff",
384
+ "#00ffff",
385
+ "#ff8800",
386
+ "#8800ff"
387
+ ];
388
+ function buildThemePalette(primary, hover) {
389
+ return [primary, hover, `${primary}cc`, `${hover}cc`, "#ffffff", `${primary}80`];
390
+ }
391
+ function readThemeColors(overlayRoot) {
392
+ try {
393
+ const styles = getComputedStyle(overlayRoot);
394
+ const primary = styles.getPropertyValue("--sc-color-primary")?.trim();
395
+ const hover = styles.getPropertyValue("--sc-color-primary-hover")?.trim();
396
+ if (primary?.startsWith("#") && primary.length >= 7) {
397
+ return buildThemePalette(primary, hover || primary);
398
+ }
399
+ } catch {
400
+ }
401
+ return null;
402
+ }
403
+ var effectRegistry = /* @__PURE__ */ new Map([
404
+ ["confetti", confettiEffect],
405
+ ["fireworks", fireworksEffect],
406
+ ["sparkles", sparklesEffect],
407
+ ["emoji-rain", emojiRainEffect]
408
+ ]);
409
+ var executeCelebrate = async (action, context) => {
410
+ const effect = effectRegistry.get(action.effect);
411
+ if (!effect) {
412
+ console.warn(
413
+ `[adaptive-overlays] Unknown celebration effect: "${action.effect}". Available: ${[...effectRegistry.keys()].join(", ")}`
414
+ );
415
+ return { cleanup: () => {
416
+ } };
417
+ }
418
+ const colors = action.colors ?? readThemeColors(context.overlayRoot) ?? FALLBACK_COLORS;
419
+ const config = {
420
+ duration: action.duration ?? 3e3,
421
+ intensity: action.intensity ?? "medium",
422
+ colors,
423
+ props: action.props
424
+ };
425
+ const engine = new CelebrationEngine();
426
+ engine.start(context.overlayRoot, effect, config);
427
+ context.publishEvent("action.applied", {
428
+ id: context.generateId(),
429
+ kind: "overlays:celebrate",
430
+ effect: action.effect
431
+ });
432
+ return {
433
+ cleanup: () => {
434
+ engine.stop();
435
+ }
436
+ };
437
+ };
438
+
439
+ // src/executors/tour.ts
440
+ var ACTIVE_TOUR_KEY = "syntro_active_tour";
441
+ var activeTours = /* @__PURE__ */ new Map();
442
+ function getTourState(tourId) {
443
+ try {
444
+ const data = localStorage.getItem(ACTIVE_TOUR_KEY);
445
+ if (!data) return null;
446
+ const state = JSON.parse(data);
447
+ if (state.tourId !== tourId) return null;
448
+ return state;
449
+ } catch {
450
+ return null;
451
+ }
452
+ }
453
+ function saveTourState(state) {
454
+ try {
455
+ localStorage.setItem(ACTIVE_TOUR_KEY, JSON.stringify(state));
456
+ } catch {
457
+ }
458
+ }
459
+ function clearTourState() {
460
+ try {
461
+ localStorage.removeItem(ACTIVE_TOUR_KEY);
462
+ } catch {
463
+ }
464
+ }
465
+ function getCurrentRoute() {
466
+ return window.location.pathname;
467
+ }
468
+ function stepMatchesRoute(step) {
469
+ if (!step.route) return true;
470
+ const currentRoute = getCurrentRoute();
471
+ if (step.route.includes("*")) {
472
+ const pattern = new RegExp(`^${step.route.replace(/\*/g, ".*")}$`);
473
+ return pattern.test(currentRoute);
474
+ }
475
+ return currentRoute === step.route;
476
+ }
477
+ var executeTour = async (action, context) => {
478
+ const { tourId, steps, startStep, autoStart = true } = action;
479
+ if (steps.length === 0) {
480
+ throw new Error(`Tour "${tourId}" has no steps`);
481
+ }
482
+ if (activeTours.has(tourId)) {
483
+ return {
484
+ cleanup: async () => {
485
+ const existing = activeTours.get(tourId);
486
+ if (existing) {
487
+ await existing.cleanup();
488
+ }
489
+ }
490
+ };
491
+ }
492
+ if (!context.applyAction) {
493
+ throw new Error("Tour executor requires applyAction in context");
494
+ }
495
+ let state = getTourState(tourId);
496
+ const isResumingTour = !!state;
497
+ if (!isResumingTour && !autoStart) {
498
+ return {
499
+ cleanup: () => {
500
+ }
501
+ };
502
+ }
503
+ if (!state) {
504
+ state = {
505
+ tourId,
506
+ currentStepId: startStep || steps[0].id,
507
+ startedAt: Date.now()
508
+ };
509
+ saveTourState(state);
510
+ }
511
+ let currentStepIndex = steps.findIndex((s) => s.id === state.currentStepId);
512
+ if (currentStepIndex === -1) {
513
+ const initialStepId = startStep || steps[0].id;
514
+ currentStepIndex = steps.findIndex((s) => s.id === initialStepId);
515
+ if (currentStepIndex === -1) currentStepIndex = 0;
516
+ state.currentStepId = steps[currentStepIndex].id;
517
+ saveTourState(state);
518
+ }
519
+ const currentStep = steps[currentStepIndex];
520
+ if (!stepMatchesRoute(currentStep)) {
521
+ context.publishEvent("tour.waiting_for_route", {
522
+ tourId,
523
+ stepId: currentStep.id,
524
+ expectedRoute: currentStep.route,
525
+ currentRoute: getCurrentRoute()
75
526
  });
76
527
  return {
77
- cleanup: () => {
78
- handle.destroy();
79
- anchorEl.removeAttribute('data-syntro-highlight');
80
- anchorEl.removeAttribute('data-syntro-highlight-dismissed');
81
- },
528
+ cleanup: () => {
529
+ }
530
+ };
531
+ }
532
+ let isDestroyed = false;
533
+ let currentActionHandle = null;
534
+ let eventUnsubscribe = null;
535
+ let routeWatcher = null;
536
+ const cleanupCurrentStep = async () => {
537
+ if (eventUnsubscribe) {
538
+ eventUnsubscribe();
539
+ eventUnsubscribe = null;
540
+ }
541
+ if (currentActionHandle?.isApplied()) {
542
+ await currentActionHandle.revert();
543
+ currentActionHandle = null;
544
+ }
545
+ };
546
+ const advanceToStep = async (nextStepId) => {
547
+ if (isDestroyed) return;
548
+ await cleanupCurrentStep();
549
+ if (nextStepId === "end") {
550
+ clearTourState();
551
+ context.publishEvent("tour.completed", {
552
+ tourId,
553
+ totalSteps: steps.length
554
+ });
555
+ isDestroyed = true;
556
+ return;
557
+ }
558
+ const nextStep = steps.find((s) => s.id === nextStepId);
559
+ if (!nextStep) {
560
+ console.error(`[Tour] Step "${nextStepId}" not found`);
561
+ return;
562
+ }
563
+ state.currentStepId = nextStepId;
564
+ saveTourState(state);
565
+ context.publishEvent("tour.step_changed", {
566
+ tourId,
567
+ previousStepId: currentStep.id,
568
+ nextStepId
569
+ });
570
+ if (nextStep.route && nextStep.route !== getCurrentRoute()) {
571
+ context.publishEvent("tour.awaiting_navigation", {
572
+ tourId,
573
+ stepId: nextStepId,
574
+ targetRoute: nextStep.route
575
+ });
576
+ return;
577
+ }
578
+ await executeStep(nextStep);
579
+ };
580
+ const executeStep = async (step) => {
581
+ if (isDestroyed) return;
582
+ context.publishEvent("tour.step_started", {
583
+ tourId,
584
+ stepId: step.id,
585
+ stepIndex: steps.findIndex((s) => s.id === step.id),
586
+ totalSteps: steps.length
587
+ });
588
+ try {
589
+ currentActionHandle = await context.applyAction(step.action);
590
+ } catch (error) {
591
+ console.error(`[Tour] Failed to execute step "${step.id}":`, error);
592
+ context.publishEvent("tour.step_failed", {
593
+ tourId,
594
+ stepId: step.id,
595
+ error: String(error)
596
+ });
597
+ return;
598
+ }
599
+ if (step.onAction && context.subscribeEvent) {
600
+ eventUnsubscribe = context.subscribeEvent("action.modal_cta_clicked", (props) => {
601
+ const actionId = props?.actionId;
602
+ if (actionId && step.onAction) {
603
+ const nextStepId = step.onAction[actionId];
604
+ if (nextStepId) {
605
+ advanceToStep(nextStepId);
606
+ }
607
+ }
608
+ });
609
+ const tooltipUnsubscribe = context.subscribeEvent("action.tooltip_cta_clicked", (props) => {
610
+ const actionId = props?.actionId;
611
+ if (actionId && step.onAction) {
612
+ const nextStepId = step.onAction[actionId];
613
+ if (nextStepId) {
614
+ advanceToStep(nextStepId);
615
+ }
616
+ }
617
+ });
618
+ const originalUnsubscribe = eventUnsubscribe;
619
+ eventUnsubscribe = () => {
620
+ originalUnsubscribe();
621
+ tooltipUnsubscribe();
622
+ };
623
+ }
624
+ };
625
+ const setupRouteWatcher = () => {
626
+ let lastPath = getCurrentRoute();
627
+ const checkRoute = () => {
628
+ const currentPath = getCurrentRoute();
629
+ if (currentPath !== lastPath) {
630
+ lastPath = currentPath;
631
+ context.publishEvent("tour.route_changed", {
632
+ tourId,
633
+ newRoute: currentPath
634
+ });
635
+ }
82
636
  };
637
+ if (context.subscribeNavigation) {
638
+ return context.subscribeNavigation(() => checkRoute());
639
+ }
640
+ window.addEventListener("popstate", checkRoute);
641
+ const origPushState = history.pushState.bind(history);
642
+ const origReplaceState = history.replaceState.bind(history);
643
+ history.pushState = (...args) => {
644
+ origPushState(...args);
645
+ queueMicrotask(checkRoute);
646
+ };
647
+ history.replaceState = (...args) => {
648
+ origReplaceState(...args);
649
+ queueMicrotask(checkRoute);
650
+ };
651
+ return () => {
652
+ window.removeEventListener("popstate", checkRoute);
653
+ history.pushState = origPushState;
654
+ history.replaceState = origReplaceState;
655
+ };
656
+ };
657
+ routeWatcher = setupRouteWatcher();
658
+ if (!isResumingTour) {
659
+ context.publishEvent("tour.started", {
660
+ tourId,
661
+ totalSteps: steps.length,
662
+ startStepId: state.currentStepId
663
+ });
664
+ } else {
665
+ context.publishEvent("tour.resumed", {
666
+ tourId,
667
+ stepId: state.currentStepId
668
+ });
669
+ }
670
+ await executeStep(currentStep);
671
+ const cleanup = async () => {
672
+ isDestroyed = true;
673
+ activeTours.delete(tourId);
674
+ await cleanupCurrentStep();
675
+ if (routeWatcher) {
676
+ routeWatcher();
677
+ }
678
+ context.publishEvent("tour.paused", {
679
+ tourId,
680
+ stepId: state.currentStepId
681
+ });
682
+ };
683
+ activeTours.set(tourId, { cleanup });
684
+ return { cleanup };
685
+ };
686
+
687
+ // ../../design-system/dist/tokens/colors.js
688
+ var base = {
689
+ white: "#ffffff",
690
+ black: "#000000"
691
+ };
692
+ var brand = {
693
+ 0: "#2c0b0a",
694
+ 1: "#5b1715",
695
+ 2: "#89221f",
696
+ 3: "#b72e2a",
697
+ 4: "#d44844",
698
+ 5: "#dd6d69",
699
+ 6: "#e5918f",
700
+ 7: "#eeb6b4",
701
+ 8: "#f6dada",
702
+ 9: "#faebea"
703
+ };
704
+ var slateGrey = {
705
+ 0: "#07080a",
706
+ 1: "#0f1318",
707
+ 2: "#0e1114",
708
+ 3: "#1c222a",
709
+ 4: "#2b333f",
710
+ 5: "#394454",
711
+ 6: "#475569",
712
+ 7: "#677384",
713
+ 8: "#87919f",
714
+ 9: "#a8afba",
715
+ 10: "#cbd0d7",
716
+ 11: "#e8eaee",
717
+ 12: "#f6f7f9"
718
+ };
719
+ var green = {
720
+ 0: "#07230a",
721
+ 1: "#0e4514",
722
+ 2: "#16681e",
723
+ 3: "#1d8a28",
724
+ 4: "#24ad32",
725
+ 5: "#4fbd5a",
726
+ 6: "#7acd82",
727
+ 7: "#a5deab",
728
+ 8: "#d0eed3",
729
+ 9: "#e5f6e7"
730
+ };
731
+ var yellow = {
732
+ 0: "#301f09",
733
+ 1: "#5f3e12",
734
+ 2: "#8f5e1b",
735
+ 3: "#be7d24",
736
+ 4: "#ee9c2d",
737
+ 5: "#f1b057",
738
+ 6: "#f5c481",
739
+ 7: "#f8d7ab",
740
+ 8: "#fcebd5",
741
+ 9: "#fdf5ea"
742
+ };
743
+ var red = {
744
+ 0: "#330707",
745
+ 1: "#660f0e",
746
+ 2: "#991616",
747
+ 3: "#cc1e1d",
748
+ 4: "#ff2524",
749
+ 5: "#ff5150",
750
+ 6: "#ff7c7c",
751
+ 7: "#ffa8a7",
752
+ 8: "#ffd3d3",
753
+ 9: "#ffe9e9"
754
+ };
755
+ var blue = {
756
+ 0: "#051533",
757
+ 1: "#0a2a66",
758
+ 2: "#0f3f98",
759
+ 3: "#1454cb",
760
+ 4: "#1969fe",
761
+ 5: "#4787fe",
762
+ 6: "#75a5fe",
763
+ 7: "#a3c3ff",
764
+ 8: "#d1e1ff",
765
+ 9: "#e8f0ff"
766
+ };
767
+ var orange = {
768
+ 0: "#662500",
769
+ 1: "#993d00",
770
+ 2: "#cc5800",
771
+ 3: "#ff7700",
772
+ 4: "#fea85d",
773
+ 5: "#fec58f",
774
+ 6: "#ffd6ae",
775
+ 7: "#fee6cd",
776
+ 8: "#fff1e1",
777
+ 9: "#fff8f0"
778
+ };
779
+ var purple = {
780
+ 0: "#151229",
781
+ 1: "#2a2452",
782
+ 2: "#40357c",
783
+ 3: "#5547a5",
784
+ 4: "#6a59ce",
785
+ 5: "#887ad8",
786
+ 6: "#a69be2",
787
+ 7: "#c3bdeb",
788
+ 8: "#e1def5",
789
+ 9: "#f0eefa"
790
+ };
791
+ var pink = {
792
+ 0: "#37091f",
793
+ 1: "#69123c",
794
+ 2: "#9b1c58",
795
+ 3: "#cd2575",
796
+ 4: "#ff2e92",
797
+ 5: "#ff58a8",
798
+ 6: "#ff82be",
799
+ 7: "#ffabd3",
800
+ 8: "#ffd5e9",
801
+ 9: "#ffeaf4"
802
+ };
803
+ var text = {
804
+ primary: slateGrey[10],
805
+ secondary: slateGrey[9],
806
+ tertiary: slateGrey[8]
807
+ };
808
+ var background = {
809
+ primary: slateGrey[2],
810
+ secondary: slateGrey[0]
811
+ };
812
+ var border = {
813
+ primary: slateGrey[4],
814
+ secondary: slateGrey[3]
815
+ };
816
+ var button = {
817
+ primary: {
818
+ text: base.white,
819
+ icon: base.white,
820
+ border: brand[3],
821
+ backgroundDefault: brand[3],
822
+ backgroundHover: brand[2]
823
+ },
824
+ neutral: {
825
+ text: slateGrey[10],
826
+ textHover: base.white,
827
+ icon: slateGrey[10],
828
+ iconHover: base.white,
829
+ border: slateGrey[4],
830
+ background: slateGrey[2]
831
+ },
832
+ link: {
833
+ text: base.white,
834
+ icon: base.white,
835
+ hover: brand[5]
836
+ },
837
+ error: {
838
+ text: red[5],
839
+ hover: red[6]
840
+ },
841
+ success: {
842
+ text: green[5],
843
+ hover: green[6]
844
+ }
845
+ };
846
+ var badge = {
847
+ slateGrey: {
848
+ content: slateGrey[10],
849
+ pillOutline: slateGrey[10],
850
+ borderPrimary: slateGrey[5],
851
+ borderSecondary: slateGrey[5],
852
+ background: slateGrey[3]
853
+ },
854
+ brand: {
855
+ content: brand[9],
856
+ pillOutline: brand[9],
857
+ borderPrimary: brand[6],
858
+ borderSecondary: brand[6],
859
+ background: brand[0]
860
+ },
861
+ red: {
862
+ content: red[8],
863
+ pillOutline: red[4],
864
+ borderPrimary: red[2],
865
+ borderSecondary: red[2],
866
+ background: red[0]
867
+ },
868
+ yellow: {
869
+ content: yellow[8],
870
+ pillOutline: yellow[4],
871
+ borderPrimary: yellow[2],
872
+ borderSecondary: yellow[2],
873
+ background: yellow[0]
874
+ },
875
+ green: {
876
+ content: green[8],
877
+ pillOutline: green[4],
878
+ borderPrimary: green[2],
879
+ borderSecondary: green[2],
880
+ background: green[0]
881
+ },
882
+ purple: {
883
+ content: purple[8],
884
+ pillOutline: purple[4],
885
+ borderPrimary: purple[2],
886
+ borderSecondary: purple[2],
887
+ background: purple[0]
888
+ },
889
+ blue: {
890
+ content: blue[8],
891
+ pillOutline: blue[4],
892
+ borderPrimary: blue[2],
893
+ borderSecondary: blue[2],
894
+ background: blue[0]
895
+ },
896
+ orange: {
897
+ content: orange[8],
898
+ pillOutline: orange[4],
899
+ borderPrimary: orange[2],
900
+ borderSecondary: orange[2],
901
+ background: orange[0]
902
+ },
903
+ pink: {
904
+ content: pink[8],
905
+ pillOutline: pink[4],
906
+ borderPrimary: pink[2],
907
+ borderSecondary: pink[2],
908
+ background: pink[0]
909
+ }
910
+ };
911
+ var badgeBanner = {
912
+ green: {
913
+ content: green[8],
914
+ border: green[2],
915
+ background: green[0]
916
+ },
917
+ yellow: {
918
+ content: yellow[8],
919
+ border: yellow[2],
920
+ background: yellow[0]
921
+ },
922
+ red: {
923
+ content: red[8],
924
+ border: red[2],
925
+ background: red[0]
926
+ }
927
+ };
928
+ var alert = {
929
+ green: {
930
+ content: green[1],
931
+ background: green[9]
932
+ },
933
+ yellow: {
934
+ content: yellow[1],
935
+ background: yellow[9]
936
+ },
937
+ red: {
938
+ content: red[1],
939
+ background: red[9]
940
+ }
941
+ };
942
+ var tag = {
943
+ content: slateGrey[10],
944
+ border: slateGrey[4],
945
+ background: slateGrey[3]
946
+ };
947
+ var menu = {
948
+ backgroundDefault: slateGrey[2],
949
+ backgroundHover: slateGrey[1],
950
+ selected: slateGrey[3]
951
+ };
952
+ var inputDropdown = {
953
+ background: slateGrey[2],
954
+ icon: slateGrey[10],
955
+ borderDefault: slateGrey[4],
956
+ borderSelected: brand[3],
957
+ textLabel: slateGrey[9],
958
+ textPlaceholder: slateGrey[8],
959
+ textHint: slateGrey[8]
960
+ };
961
+ var inputField = {
962
+ backgroundDefault: slateGrey[2],
963
+ backgroundDisabled: slateGrey[0],
964
+ textLabel: slateGrey[9],
965
+ textPlaceholder: slateGrey[8],
966
+ textHint: slateGrey[8],
967
+ textError: red[5],
968
+ iconDefault: slateGrey[9],
969
+ iconPlaceholder: slateGrey[10],
970
+ iconError: red[5],
971
+ borderDefault: slateGrey[4],
972
+ borderSelected: brand[3],
973
+ borderError: red[5]
974
+ };
975
+ var toggle = {
976
+ handleDefault: base.white,
977
+ handleDisabled: slateGrey[10],
978
+ off: {
979
+ backgroundDefault: slateGrey[4],
980
+ backgroundHover: slateGrey[5],
981
+ backgroundDisabled: slateGrey[4]
982
+ },
983
+ on: {
984
+ backgroundDefault: green[3],
985
+ backgroundHover: green[2],
986
+ backgroundDisabled: slateGrey[4]
987
+ }
988
+ };
989
+ var checkbox = {
990
+ off: {
991
+ backgroundDefault: "#00000000",
992
+ backgroundHover: slateGrey[5],
993
+ backgroundDisabled: slateGrey[2],
994
+ border: slateGrey[6]
995
+ },
996
+ on: {
997
+ backgroundDefault: green[0],
998
+ backgroundHover: green[1],
999
+ backgroundDisabled: slateGrey[2],
1000
+ border: green[3]
1001
+ }
1002
+ };
1003
+ var avatar = {
1004
+ content: slateGrey[10],
1005
+ background: slateGrey[4]
83
1006
  };
84
- /**
85
- * Execute a pulse action
86
- */
87
- export const executePulse = async (action, context) => {
88
- let anchorEl = context.resolveAnchor(action.anchorId);
89
- if (!anchorEl && context.waitForAnchor) {
90
- anchorEl = await context.waitForAnchor(action.anchorId, 3000);
91
- }
92
- if (!anchorEl) {
93
- console.warn(`[adaptive-overlays] Anchor not found after waiting: ${action.anchorId.selector}`);
94
- return { cleanup: () => { } };
95
- }
96
- const duration = action.duration ?? 4000;
97
- // Wait one frame so ThemeProvider's useEffect has injected CSS vars
98
- await new Promise((resolve) => requestAnimationFrame(resolve));
99
- // Read primary + secondary colors from theme (shadow DOM overlay root has --sc-* vars)
100
- const parseHex = (hex) => ({
101
- r: parseInt(hex.slice(1, 3), 16),
102
- g: parseInt(hex.slice(3, 5), 16),
103
- b: parseInt(hex.slice(5, 7), 16),
1007
+ var progressBarSlider = {
1008
+ background: slateGrey[4],
1009
+ active: green[3]
1010
+ };
1011
+ var card = {
1012
+ background: slateGrey[1],
1013
+ content: slateGrey[9],
1014
+ border: slateGrey[4]
1015
+ };
1016
+ var sidebar = {
1017
+ backgroundDefault: slateGrey[1],
1018
+ backgroundHover: slateGrey[3],
1019
+ backgroundActive: slateGrey[4],
1020
+ border: slateGrey[4],
1021
+ contentPrimary: slateGrey[10],
1022
+ contentSecondary: slateGrey[9],
1023
+ contentTertiary: slateGrey[8]
1024
+ };
1025
+ var modal = {
1026
+ background: slateGrey[1],
1027
+ content: slateGrey[9],
1028
+ border: slateGrey[4]
1029
+ };
1030
+ var tab = {
1031
+ activeBackground: slateGrey[3],
1032
+ activeContent: brand[5],
1033
+ inactiveContent: slateGrey[9],
1034
+ border: slateGrey[4]
1035
+ };
1036
+ var table = {
1037
+ header: {
1038
+ textDefault: slateGrey[9],
1039
+ textHover: slateGrey[8],
1040
+ backgroundDefault: slateGrey[1]
1041
+ },
1042
+ border: slateGrey[4],
1043
+ cell: {
1044
+ textPrimary: slateGrey[10],
1045
+ textSecondary: slateGrey[9],
1046
+ backgroundDefault: slateGrey[2],
1047
+ backgroundHover: slateGrey[1]
1048
+ }
1049
+ };
1050
+ var breadcrumbs = {
1051
+ textPrimaryDefault: slateGrey[10],
1052
+ textPrimaryHover: slateGrey[10],
1053
+ textSecondaryDefault: slateGrey[8],
1054
+ textSecondaryHover: slateGrey[9],
1055
+ iconPrimary: slateGrey[10],
1056
+ iconSecondary: slateGrey[8]
1057
+ };
1058
+ var loadingIndicator = {
1059
+ background: green[1],
1060
+ active: green[5]
1061
+ };
1062
+ var datePicker = {
1063
+ textDefault: slateGrey[10],
1064
+ textSelected: base.white,
1065
+ textDisabled: slateGrey[7],
1066
+ backgroundDefault: slateGrey[2],
1067
+ backgroundMiddle: slateGrey[3],
1068
+ backgroundSelected: brand[3],
1069
+ border: slateGrey[4]
1070
+ };
1071
+ var scroll = slateGrey[9];
1072
+
1073
+ // ../../design-system/dist/tokens/panel-shell.js
1074
+ var fab = {
1075
+ /** Diameter in pixels. */
1076
+ size: 56,
1077
+ /** Inset from the panel's top-left corner in pixels. */
1078
+ inset: 12,
1079
+ /** Background color (always the brand black). */
1080
+ background: base.black,
1081
+ /** Icon / logo color. */
1082
+ color: base.white,
1083
+ /** Border — 2px brand red ring. */
1084
+ border: `2px solid ${brand[3]}`,
1085
+ /** Shadow when the panel is open (inner ring for "active" state). */
1086
+ shadowOpen: "0 4px 24px rgba(0,0,0,0.6), 0 0 0 2px rgba(255,255,255,0.08)",
1087
+ /** Shadow when the panel is closed. */
1088
+ shadowClosed: "0 4px 24px rgba(0,0,0,0.6)"
1089
+ };
1090
+
1091
+ // src/highlight.ts
1092
+ var supportsPathClip = typeof CSS !== "undefined" && CSS.supports?.("clip-path", "path('M0 0 H1 V1 Z')");
1093
+ function showHighlight(anchorEl, overlayRoot, opts) {
1094
+ const padding = opts?.paddingPx ?? 12;
1095
+ const radius = opts?.radiusPx ?? 12;
1096
+ const opacity = Math.min(Math.max(opts?.scrimOpacity ?? 0.55, 0), 1);
1097
+ const ringColor = opts?.ringColor ?? `var(--syntro-ring, ${blue[5]})`;
1098
+ const blocking = opts?.blocking ?? false;
1099
+ const onClickOutside = opts?.onClickOutside ?? true;
1100
+ const onEsc = opts?.onEsc ?? true;
1101
+ const rootStyles = getComputedStyle(document.documentElement);
1102
+ const tokenScrim = rootStyles.getPropertyValue("--syntro-spotlight-backdrop").trim();
1103
+ const tokenRing = rootStyles.getPropertyValue("--syntro-ring").trim();
1104
+ const scrim = document.createElement("div");
1105
+ scrim.className = "syntro-spotlight-scrim";
1106
+ const needsPointerEvents = blocking || onClickOutside;
1107
+ Object.assign(scrim.style, {
1108
+ position: "fixed",
1109
+ inset: "0",
1110
+ zIndex: "2147483646",
1111
+ pointerEvents: needsPointerEvents ? "auto" : "none",
1112
+ background: tokenScrim || `rgba(2, 6, 23, ${opacity})`,
1113
+ transition: "opacity 220ms ease",
1114
+ opacity: "0"
1115
+ });
1116
+ overlayRoot.appendChild(scrim);
1117
+ requestAnimationFrame(() => scrim.style.opacity = "1");
1118
+ const ring = document.createElement("div");
1119
+ ring.className = "syntro-spotlight-ring";
1120
+ Object.assign(ring.style, {
1121
+ position: "fixed",
1122
+ pointerEvents: "none",
1123
+ borderRadius: `${radius}px`,
1124
+ border: `2px solid ${ringColor || tokenRing || blue[5]}`,
1125
+ boxShadow: `0 0 0 4px rgba(255,255,255,0.35)`,
1126
+ zIndex: "2147483647",
1127
+ transition: "all 220ms cubic-bezier(0.16,1,0.3,1)"
1128
+ });
1129
+ overlayRoot.appendChild(ring);
1130
+ const fallbackSlices = [];
1131
+ if (!supportsPathClip) {
1132
+ for (let i = 0; i < 4; i++) {
1133
+ const slice = document.createElement("div");
1134
+ slice.style.position = "fixed";
1135
+ slice.style.background = "inherit";
1136
+ fallbackSlices.push(slice);
1137
+ scrim.appendChild(slice);
1138
+ }
1139
+ }
1140
+ const setClipPath = (path) => {
1141
+ scrim.style.clipPath = path;
1142
+ scrim.style.webkitClipPath = path;
1143
+ };
1144
+ const update = () => {
1145
+ if (!anchorEl.isConnected) {
1146
+ handle.destroy();
1147
+ return;
1148
+ }
1149
+ const rect = anchorEl.getBoundingClientRect();
1150
+ const x = rect.left - padding;
1151
+ const y = rect.top - padding;
1152
+ const w = rect.width + padding * 2;
1153
+ const h = rect.height + padding * 2;
1154
+ Object.assign(ring.style, {
1155
+ left: `${x}px`,
1156
+ top: `${y}px`,
1157
+ width: `${w}px`,
1158
+ height: `${h}px`
104
1159
  });
105
- const fallback = { r: 79, g: 70, b: 229 }; // indigo-600
106
- let primary = fallback;
107
- let secondary = null;
1160
+ if (supportsPathClip) {
1161
+ const vw = window.innerWidth;
1162
+ const vh = window.innerHeight;
1163
+ const r = Math.min(radius, w / 2, h / 2);
1164
+ const outer = `M 0 0 L ${vw} 0 L ${vw} ${vh} L 0 ${vh} Z`;
1165
+ const inner = `M ${x + r} ${y} A ${r} ${r} 0 0 0 ${x} ${y + r} L ${x} ${y + h - r} A ${r} ${r} 0 0 0 ${x + r} ${y + h} L ${x + w - r} ${y + h} A ${r} ${r} 0 0 0 ${x + w} ${y + h - r} L ${x + w} ${y + r} A ${r} ${r} 0 0 0 ${x + w - r} ${y} L ${x + r} ${y} Z`;
1166
+ setClipPath(`path('${outer} ${inner}')`);
1167
+ } else {
1168
+ const [top, right, bottom, left] = fallbackSlices;
1169
+ Object.assign(top.style, {
1170
+ left: "0px",
1171
+ top: "0px",
1172
+ width: "100vw",
1173
+ height: `${y}px`
1174
+ });
1175
+ Object.assign(bottom.style, {
1176
+ left: "0px",
1177
+ top: `${y + h}px`,
1178
+ width: "100vw",
1179
+ height: `${Math.max(0, window.innerHeight - (y + h))}px`
1180
+ });
1181
+ Object.assign(left.style, {
1182
+ left: "0px",
1183
+ top: `${y}px`,
1184
+ width: `${x}px`,
1185
+ height: `${h}px`
1186
+ });
1187
+ Object.assign(right.style, {
1188
+ left: `${x + w}px`,
1189
+ top: `${y}px`,
1190
+ width: `${Math.max(0, window.innerWidth - (x + w))}px`,
1191
+ height: `${h}px`
1192
+ });
1193
+ }
1194
+ };
1195
+ const ro = new ResizeObserver(() => requestAnimationFrame(update));
1196
+ ro.observe(anchorEl);
1197
+ const onScroll = () => requestAnimationFrame(update);
1198
+ const onResize = () => requestAnimationFrame(update);
1199
+ window.addEventListener("scroll", onScroll, true);
1200
+ window.addEventListener("resize", onResize);
1201
+ const onKey = (e) => {
1202
+ if (e.key === "Escape" && onEsc) {
1203
+ opts?.onDismiss?.();
1204
+ handle.destroy();
1205
+ }
1206
+ };
1207
+ if (onEsc) {
1208
+ window.addEventListener("keydown", onKey);
1209
+ }
1210
+ const onClick = (event) => {
1211
+ if (blocking) {
1212
+ event.preventDefault();
1213
+ event.stopPropagation();
1214
+ } else if (onClickOutside) {
1215
+ opts?.onDismiss?.();
1216
+ handle.destroy();
1217
+ }
1218
+ };
1219
+ scrim.addEventListener("click", onClick);
1220
+ const handle = {
1221
+ destroy() {
1222
+ ro.disconnect();
1223
+ window.removeEventListener("scroll", onScroll, true);
1224
+ window.removeEventListener("resize", onResize);
1225
+ if (onEsc) {
1226
+ window.removeEventListener("keydown", onKey);
1227
+ }
1228
+ scrim.removeEventListener("click", onClick);
1229
+ scrim.style.pointerEvents = "none";
1230
+ scrim.style.opacity = "0";
1231
+ setTimeout(() => {
1232
+ try {
1233
+ scrim.remove();
1234
+ } catch {
1235
+ }
1236
+ try {
1237
+ ring.remove();
1238
+ } catch {
1239
+ }
1240
+ }, 220);
1241
+ }
1242
+ };
1243
+ update();
1244
+ return handle;
1245
+ }
1246
+
1247
+ // src/sanitizer.ts
1248
+ var ALLOWED_TAGS = /* @__PURE__ */ new Set([
1249
+ "b",
1250
+ "strong",
1251
+ "i",
1252
+ "em",
1253
+ "u",
1254
+ "span",
1255
+ "div",
1256
+ "p",
1257
+ "br",
1258
+ "ul",
1259
+ "ol",
1260
+ "li",
1261
+ "code",
1262
+ "pre",
1263
+ "small",
1264
+ "sup",
1265
+ "sub",
1266
+ "a",
1267
+ "button",
1268
+ // SVG elements (for inline Lucide icons in config HTML)
1269
+ "svg",
1270
+ "path",
1271
+ "circle",
1272
+ "line",
1273
+ "polyline",
1274
+ "polygon",
1275
+ "rect",
1276
+ "g"
1277
+ ]);
1278
+ function sanitizeHtml(html2) {
1279
+ const hasNative = typeof window.Sanitizer === "function";
1280
+ if (hasNative) {
108
1281
  try {
109
- const styles = getComputedStyle(context.overlayRoot);
110
- const pHex = styles.getPropertyValue('--sc-color-primary')?.trim();
111
- const sHex = styles.getPropertyValue('--sc-color-primary-hover')?.trim();
112
- if (pHex?.startsWith('#') && pHex.length >= 7) {
113
- primary = parseHex(pHex);
1282
+ const s = new window.Sanitizer({});
1283
+ const frag = s.sanitizeToFragment(html2);
1284
+ const div = document.createElement("div");
1285
+ div.append(frag);
1286
+ return div.innerHTML;
1287
+ } catch {
1288
+ }
1289
+ }
1290
+ const tpl = document.createElement("template");
1291
+ tpl.innerHTML = html2;
1292
+ const root = tpl.content;
1293
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, null);
1294
+ const toRemove = [];
1295
+ while (walker.nextNode()) {
1296
+ const el = walker.currentNode;
1297
+ const tag2 = el.tagName.toLowerCase();
1298
+ if (!ALLOWED_TAGS.has(tag2)) {
1299
+ toRemove.push(el);
1300
+ continue;
1301
+ }
1302
+ for (const attr of Array.from(el.attributes)) {
1303
+ const name = attr.name.toLowerCase();
1304
+ const value = attr.value.trim().toLowerCase();
1305
+ const isEvent = name.startsWith("on");
1306
+ const isJsUrl = (name === "href" || name === "src") && value.startsWith("javascript:");
1307
+ if (isEvent || isJsUrl) {
1308
+ el.removeAttribute(attr.name);
1309
+ }
1310
+ }
1311
+ }
1312
+ for (const el of toRemove) {
1313
+ while (el.firstChild) {
1314
+ el.parentNode?.insertBefore(el.firstChild, el);
1315
+ }
1316
+ el.remove();
1317
+ }
1318
+ return tpl.innerHTML;
1319
+ }
1320
+
1321
+ // src/modal.ts
1322
+ var V = {
1323
+ bg: "var(--sc-overlay-background, #ffffff)",
1324
+ title: "var(--sc-overlay-title-color, var(--sc-overlay-text-color, #111827))",
1325
+ text: "var(--sc-overlay-text-color, #4b5563)",
1326
+ accent: "var(--sc-color-primary, #4f46e5)",
1327
+ radius: "var(--sc-border-radius, 12px)"
1328
+ };
1329
+ var executeModal = async (action, context) => {
1330
+ const { content, size = "md", blocking = false, scrim, dismiss, ctaButtons } = action;
1331
+ const scrimEl = document.createElement("div");
1332
+ scrimEl.className = "syntro-modal-scrim";
1333
+ scrimEl.style.cssText = `
1334
+ position: fixed;
1335
+ inset: 0;
1336
+ background: rgba(0, 0, 0, ${scrim?.opacity ?? 0.6});
1337
+ z-index: 2147483645;
1338
+ opacity: 0;
1339
+ transition: opacity 200ms ease-out;
1340
+ `;
1341
+ context.overlayRoot.appendChild(scrimEl);
1342
+ const modal2 = document.createElement("div");
1343
+ modal2.className = `syntro-modal syntro-modal-${size}`;
1344
+ modal2.setAttribute("role", "dialog");
1345
+ modal2.setAttribute("aria-modal", "true");
1346
+ const sizeMap = { sm: "360px", md: "480px", lg: "640px" };
1347
+ modal2.style.cssText = `
1348
+ position: fixed;
1349
+ top: 50%;
1350
+ left: 50%;
1351
+ transform: translate(-50%, -50%) scale(0.95);
1352
+ max-width: ${sizeMap[size]};
1353
+ width: 90%;
1354
+ background: ${V.bg};
1355
+ border-radius: ${V.radius};
1356
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
1357
+ z-index: 2147483646;
1358
+ opacity: 0;
1359
+ transition: opacity 200ms ease-out, transform 200ms ease-out;
1360
+ padding: 24px;
1361
+ `;
1362
+ let html2 = "";
1363
+ if (content.title) {
1364
+ html2 += `<h2 class="syntro-modal-title" style="margin: 0 0 12px 0; font-size: 18px; font-weight: 600; color: ${V.title};">${sanitizeHtml(content.title)}</h2>`;
1365
+ }
1366
+ html2 += `<div class="syntro-modal-body" style="color: ${V.text}; line-height: 1.5;">${sanitizeHtml(content.body)}</div>`;
1367
+ if (dismiss?.closeButton !== false) {
1368
+ html2 += `
1369
+ <button class="syntro-modal-close" data-syntro-action="dismiss" style="
1370
+ position: absolute;
1371
+ top: 16px;
1372
+ right: 16px;
1373
+ background: none;
1374
+ border: none;
1375
+ cursor: pointer;
1376
+ padding: 4px;
1377
+ color: ${V.text};
1378
+ opacity: 0.6;
1379
+ " aria-label="Close">
1380
+ <svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
1381
+ <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/>
1382
+ </svg>
1383
+ </button>
1384
+ `;
1385
+ }
1386
+ if (ctaButtons && ctaButtons.length > 0) {
1387
+ html2 += `<div class="syntro-modal-actions" style="display: flex; gap: 12px; margin-top: 24px; justify-content: flex-end;">`;
1388
+ for (const btn of ctaButtons) {
1389
+ const isPrimary = btn.primary ?? false;
1390
+ html2 += `
1391
+ <button
1392
+ class="syntro-modal-btn ${isPrimary ? "syntro-modal-btn-primary" : ""}"
1393
+ data-syntro-action="${sanitizeHtml(btn.actionId)}"
1394
+ style="
1395
+ padding: 10px 20px;
1396
+ border-radius: 8px;
1397
+ font-size: 14px;
1398
+ font-weight: 500;
1399
+ cursor: pointer;
1400
+ transition: background 150ms ease;
1401
+ ${isPrimary ? `background: ${V.accent}; color: white; border: none;` : `background: transparent; color: ${V.accent}; border: 1px solid currentColor; opacity: 0.7;`}
1402
+ "
1403
+ >
1404
+ ${sanitizeHtml(btn.label)}
1405
+ </button>
1406
+ `;
1407
+ }
1408
+ html2 += `</div>`;
1409
+ }
1410
+ modal2.innerHTML = html2;
1411
+ context.overlayRoot.appendChild(modal2);
1412
+ let actionClicked = null;
1413
+ const actionBtns = modal2.querySelectorAll("[data-syntro-action]");
1414
+ const actionHandler = (e) => {
1415
+ const btn = e.currentTarget;
1416
+ const actionId = btn.getAttribute("data-syntro-action");
1417
+ if (actionId) {
1418
+ actionClicked = actionId;
1419
+ context.publishEvent("action.modal_cta_clicked", {
1420
+ actionId
1421
+ });
1422
+ handle.destroy();
1423
+ }
1424
+ };
1425
+ actionBtns.forEach((btn) => btn.addEventListener("click", actionHandler));
1426
+ const onKey = (e) => {
1427
+ if (e.key === "Escape" && dismiss?.onEsc !== false) {
1428
+ handle.destroy();
1429
+ }
1430
+ };
1431
+ window.addEventListener("keydown", onKey);
1432
+ const onScrimClick = () => {
1433
+ if (!blocking) {
1434
+ handle.destroy();
1435
+ }
1436
+ };
1437
+ scrimEl.addEventListener("click", onScrimClick);
1438
+ const originalInert = [];
1439
+ if (blocking) {
1440
+ Array.from(document.body.children).forEach((el) => {
1441
+ if (el !== context.overlayRoot && el.getAttribute("inert") === null && !el.querySelector("[data-syntro-editor-panel]") && !el.hasAttribute("data-syntro-editor-panel")) {
1442
+ el.setAttribute("inert", "");
1443
+ originalInert.push(el);
1444
+ }
1445
+ });
1446
+ }
1447
+ let timeoutId;
1448
+ if (dismiss?.timeoutMs) {
1449
+ timeoutId = setTimeout(() => {
1450
+ handle.destroy();
1451
+ }, dismiss.timeoutMs);
1452
+ }
1453
+ const focusableEls = modal2.querySelectorAll(
1454
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
1455
+ );
1456
+ if (focusableEls.length > 0) {
1457
+ requestAnimationFrame(() => focusableEls[0].focus());
1458
+ }
1459
+ requestAnimationFrame(() => {
1460
+ scrimEl.style.opacity = "1";
1461
+ modal2.style.opacity = "1";
1462
+ modal2.style.transform = "translate(-50%, -50%) scale(1)";
1463
+ });
1464
+ context.publishEvent("action.applied", {
1465
+ id: context.generateId(),
1466
+ kind: "overlays:modal",
1467
+ size,
1468
+ blocking
1469
+ });
1470
+ const handle = {
1471
+ destroy() {
1472
+ if (timeoutId) {
1473
+ clearTimeout(timeoutId);
1474
+ }
1475
+ window.removeEventListener("keydown", onKey);
1476
+ scrimEl.removeEventListener("click", onScrimClick);
1477
+ actionBtns.forEach((btn) => btn.removeEventListener("click", actionHandler));
1478
+ originalInert.forEach((el) => el.removeAttribute("inert"));
1479
+ modal2.style.pointerEvents = "none";
1480
+ scrimEl.style.pointerEvents = "none";
1481
+ modal2.style.opacity = "0";
1482
+ modal2.style.transform = "translate(-50%, -50%) scale(0.95)";
1483
+ scrimEl.style.opacity = "0";
1484
+ setTimeout(() => {
1485
+ try {
1486
+ modal2.remove();
1487
+ } catch {
1488
+ }
1489
+ try {
1490
+ scrimEl.remove();
1491
+ } catch {
1492
+ }
1493
+ }, 200);
1494
+ context.publishEvent("action.modal_dismissed", {
1495
+ actionClicked
1496
+ });
1497
+ }
1498
+ };
1499
+ return {
1500
+ cleanup: () => {
1501
+ handle.destroy();
1502
+ }
1503
+ };
1504
+ };
1505
+
1506
+ // src/tooltip.ts
1507
+ import {
1508
+ arrow as arrowMiddleware,
1509
+ autoUpdate,
1510
+ computePosition,
1511
+ flip,
1512
+ hide,
1513
+ offset,
1514
+ shift
1515
+ } from "@floating-ui/dom";
1516
+ function getAnchorReference(anchorEl) {
1517
+ const rect = anchorEl.getBoundingClientRect();
1518
+ const viewportWidth = window.innerWidth;
1519
+ const viewportHeight = window.innerHeight;
1520
+ const isLargeElement = rect.width > viewportWidth * 0.8 || rect.height > viewportHeight * 0.8;
1521
+ if (!isLargeElement) {
1522
+ return anchorEl;
1523
+ }
1524
+ const visibleLeft = Math.max(rect.left, 0);
1525
+ const visibleTop = Math.max(rect.top, 0);
1526
+ const visibleRight = Math.min(rect.right, viewportWidth);
1527
+ const visibleBottom = Math.min(rect.bottom, viewportHeight);
1528
+ const centerX = (visibleLeft + visibleRight) / 2;
1529
+ const centerY = (visibleTop + visibleBottom) / 2;
1530
+ return {
1531
+ getBoundingClientRect() {
1532
+ return {
1533
+ width: 0,
1534
+ height: 0,
1535
+ x: centerX,
1536
+ y: centerY,
1537
+ top: centerY,
1538
+ left: centerX,
1539
+ right: centerX,
1540
+ bottom: centerY
1541
+ };
1542
+ }
1543
+ };
1544
+ }
1545
+ function showTooltip(anchorEl, overlayRoot, opts) {
1546
+ if (!opts.trigger || opts.trigger === "immediate") {
1547
+ const rect = anchorEl.getBoundingClientRect();
1548
+ const isLargeElement = rect.width > window.innerWidth * 0.8 || rect.height > window.innerHeight * 0.8;
1549
+ if (!isLargeElement) {
1550
+ anchorEl.scrollIntoView({ behavior: "smooth", block: "center", inline: "center" });
1551
+ }
1552
+ }
1553
+ const div = document.createElement("div");
1554
+ div.className = "syntro-tooltip";
1555
+ div.setAttribute("role", "tooltip");
1556
+ div.innerHTML = sanitizeHtml(opts.html);
1557
+ if (!opts.trigger || opts.trigger === "immediate") {
1558
+ const closeBtn = document.createElement("button");
1559
+ closeBtn.className = "syntro-tooltip-close";
1560
+ closeBtn.setAttribute("aria-label", "Close");
1561
+ closeBtn.textContent = "\xD7";
1562
+ Object.assign(closeBtn.style, {
1563
+ position: "absolute",
1564
+ top: "4px",
1565
+ right: "4px",
1566
+ background: "none",
1567
+ border: "none",
1568
+ color: "inherit",
1569
+ fontSize: "16px",
1570
+ lineHeight: "1",
1571
+ cursor: "pointer",
1572
+ opacity: "0.6",
1573
+ padding: "2px 4px"
1574
+ });
1575
+ closeBtn.addEventListener("mouseenter", () => {
1576
+ closeBtn.style.opacity = "1";
1577
+ });
1578
+ closeBtn.addEventListener("mouseleave", () => {
1579
+ closeBtn.style.opacity = "0.6";
1580
+ });
1581
+ closeBtn.addEventListener("click", () => handle.destroy());
1582
+ div.style.position = "relative";
1583
+ div.appendChild(closeBtn);
1584
+ }
1585
+ const actionBtns = div.querySelectorAll("[data-syntro-action]");
1586
+ const actionHandler = (e) => {
1587
+ const btn = e.currentTarget;
1588
+ const actionId = btn.getAttribute("data-syntro-action");
1589
+ if (actionId && opts.onAction) {
1590
+ opts.onAction(actionId);
1591
+ }
1592
+ };
1593
+ actionBtns.forEach((btn) => btn.addEventListener("click", actionHandler));
1594
+ const arrowEl = document.createElement("div");
1595
+ arrowEl.className = "syntro-tooltip-arrow";
1596
+ div.appendChild(arrowEl);
1597
+ overlayRoot.appendChild(div);
1598
+ const middleware = [
1599
+ offset(opts.offsetPx ?? 8),
1600
+ flip(),
1601
+ shift({ padding: 8 }),
1602
+ hide(),
1603
+ arrowMiddleware({ element: arrowEl })
1604
+ ];
1605
+ const placement = opts.placement && opts.placement !== "auto" ? opts.placement : "top";
1606
+ const cleanup = autoUpdate(anchorEl, div, async () => {
1607
+ if (!anchorEl.isConnected) {
1608
+ handle.destroy();
1609
+ return;
1610
+ }
1611
+ const currentAnchorRef = getAnchorReference(anchorEl);
1612
+ const result = await computePosition(currentAnchorRef, div, {
1613
+ placement,
1614
+ strategy: "fixed",
1615
+ middleware
1616
+ });
1617
+ const { x, y, strategy, middlewareData, placement: finalPlacement } = result;
1618
+ Object.assign(div.style, {
1619
+ left: `${x}px`,
1620
+ top: `${y}px`,
1621
+ position: strategy
1622
+ });
1623
+ if (middlewareData.arrow) {
1624
+ const { x: arrowX, y: arrowY } = middlewareData.arrow;
1625
+ const side = finalPlacement.split("-")[0];
1626
+ const staticSide = {
1627
+ top: "bottom",
1628
+ right: "left",
1629
+ bottom: "top",
1630
+ left: "right"
1631
+ };
1632
+ Object.assign(arrowEl.style, {
1633
+ left: arrowX != null ? `${arrowX}px` : "",
1634
+ top: arrowY != null ? `${arrowY}px` : "",
1635
+ right: "",
1636
+ bottom: "",
1637
+ [staticSide[side]]: "-4px"
1638
+ });
1639
+ const rotation = {
1640
+ top: "0deg",
1641
+ right: "90deg",
1642
+ bottom: "180deg",
1643
+ left: "270deg"
1644
+ };
1645
+ arrowEl.style.transform = `rotate(${rotation[side] || "0deg"})`;
1646
+ }
1647
+ });
1648
+ const onKey = (e) => {
1649
+ if (e.key === "Escape") handle.destroy();
1650
+ };
1651
+ window.addEventListener("keydown", onKey);
1652
+ const originalInert = [];
1653
+ if (opts.blocking) {
1654
+ Array.from(document.body.children).forEach((el) => {
1655
+ if (el !== overlayRoot && el.getAttribute("inert") === null) {
1656
+ el.setAttribute("inert", "");
1657
+ originalInert.push(el.id || el.tagName);
1658
+ }
1659
+ });
1660
+ const focusableEls = Array.from(
1661
+ div.querySelectorAll(
1662
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
1663
+ )
1664
+ );
1665
+ if (focusableEls.length > 0) {
1666
+ const firstFocusable = focusableEls[0];
1667
+ const lastFocusable = focusableEls[focusableEls.length - 1];
1668
+ const trapFocus = (e) => {
1669
+ if (e.key !== "Tab") return;
1670
+ if (e.shiftKey) {
1671
+ if (document.activeElement === firstFocusable) {
1672
+ lastFocusable.focus();
1673
+ e.preventDefault();
1674
+ }
1675
+ } else if (document.activeElement === lastFocusable) {
1676
+ firstFocusable.focus();
1677
+ e.preventDefault();
114
1678
  }
115
- if (sHex?.startsWith('#') && sHex.length >= 7) {
116
- secondary = parseHex(sHex);
1679
+ };
1680
+ div.addEventListener("keydown", trapFocus);
1681
+ requestAnimationFrame(() => firstFocusable.focus());
1682
+ }
1683
+ }
1684
+ const attachTrigger = () => {
1685
+ if (opts.trigger === "hover") {
1686
+ let hideTimeout = null;
1687
+ const show = () => {
1688
+ if (hideTimeout) {
1689
+ clearTimeout(hideTimeout);
1690
+ hideTimeout = null;
117
1691
  }
1692
+ div.style.visibility = "visible";
1693
+ div.style.opacity = "1";
1694
+ };
1695
+ const scheduleHide = () => {
1696
+ hideTimeout = setTimeout(() => {
1697
+ div.style.visibility = "hidden";
1698
+ div.style.opacity = "0";
1699
+ hideTimeout = null;
1700
+ }, 100);
1701
+ };
1702
+ div.style.visibility = "hidden";
1703
+ div.style.opacity = "0";
1704
+ div.style.transition = "opacity 200ms ease, visibility 200ms";
1705
+ anchorEl.addEventListener("mouseenter", show);
1706
+ anchorEl.addEventListener("mouseleave", scheduleHide);
1707
+ div.addEventListener("mouseenter", show);
1708
+ div.addEventListener("mouseleave", scheduleHide);
1709
+ anchorEl.addEventListener("focus", show);
1710
+ anchorEl.addEventListener("blur", scheduleHide);
1711
+ return () => {
1712
+ if (hideTimeout) clearTimeout(hideTimeout);
1713
+ anchorEl.removeEventListener("mouseenter", show);
1714
+ anchorEl.removeEventListener("mouseleave", scheduleHide);
1715
+ div.removeEventListener("mouseenter", show);
1716
+ div.removeEventListener("mouseleave", scheduleHide);
1717
+ anchorEl.removeEventListener("focus", show);
1718
+ anchorEl.removeEventListener("blur", scheduleHide);
1719
+ };
118
1720
  }
119
- catch {
120
- /* fallback to default */
121
- }
122
- // Inject/update pulse animation with theme colors
123
- // Two-tone pulse when both primary and secondary are available
124
- const existing = document.querySelector('[data-syntro-pulse-styles]');
125
- if (existing)
126
- existing.remove();
127
- const style = document.createElement('style');
128
- style.setAttribute('data-syntro-pulse-styles', '');
129
- const { r: pr, g: pg, b: pb } = primary;
130
- if (secondary) {
131
- const { r: sr, g: sg, b: sb } = secondary;
132
- style.textContent = `
1721
+ if (opts.trigger === "click") {
1722
+ const toggle2 = () => {
1723
+ const isVisible = div.style.visibility === "visible";
1724
+ if (isVisible) {
1725
+ handle.destroy();
1726
+ } else {
1727
+ div.style.visibility = "visible";
1728
+ div.style.opacity = "1";
1729
+ }
1730
+ };
1731
+ div.style.visibility = "hidden";
1732
+ div.style.opacity = "0";
1733
+ div.style.transition = "opacity 200ms ease, visibility 200ms";
1734
+ anchorEl.addEventListener("click", toggle2);
1735
+ return () => anchorEl.removeEventListener("click", toggle2);
1736
+ }
1737
+ div.style.opacity = "0";
1738
+ div.style.transition = "opacity 200ms ease";
1739
+ requestAnimationFrame(() => {
1740
+ div.style.opacity = "1";
1741
+ });
1742
+ return () => {
1743
+ };
1744
+ };
1745
+ const removeTrigger = attachTrigger();
1746
+ const handle = {
1747
+ el: div,
1748
+ destroy() {
1749
+ cleanup();
1750
+ removeTrigger();
1751
+ window.removeEventListener("keydown", onKey);
1752
+ actionBtns.forEach((btn) => btn.removeEventListener("click", actionHandler));
1753
+ if (opts.blocking) {
1754
+ Array.from(document.body.children).forEach((el) => {
1755
+ if (el !== overlayRoot) {
1756
+ el.removeAttribute("inert");
1757
+ }
1758
+ });
1759
+ }
1760
+ div.style.pointerEvents = "none";
1761
+ div.style.opacity = "0";
1762
+ setTimeout(() => {
1763
+ try {
1764
+ div.remove();
1765
+ } catch {
1766
+ }
1767
+ }, 200);
1768
+ }
1769
+ };
1770
+ return handle;
1771
+ }
1772
+
1773
+ // src/WorkflowWidgetLit.ts
1774
+ import { html, LitElement, nothing } from "lit";
1775
+ import { styleMap } from "lit/directives/style-map.js";
1776
+ var TOKEN_BLUE_4 = "#1969fe";
1777
+ var TOKEN_GREEN_4 = "#24ad32";
1778
+ var TOKEN_SLATE_2 = "#0e1114";
1779
+ var TOKEN_SLATE_7 = "#677384";
1780
+ var TOKEN_SLATE_9 = "#a8afba";
1781
+ var TOKEN_SLATE_12 = "#f6f7f9";
1782
+ var TOKEN_WHITE = "#ffffff";
1783
+ function showWorkflowToast(notification) {
1784
+ const toast = document.createElement("div");
1785
+ toast.setAttribute("data-testid", "workflow-toast");
1786
+ Object.assign(toast.style, {
1787
+ position: "fixed",
1788
+ bottom: "16px",
1789
+ right: "16px",
1790
+ zIndex: "2147483646",
1791
+ padding: "12px 16px",
1792
+ borderRadius: "8px",
1793
+ backgroundColor: `var(--se-color-bg-surface, ${TOKEN_WHITE})`,
1794
+ color: `var(--se-color-text-primary, ${TOKEN_SLATE_2})`,
1795
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)",
1796
+ maxWidth: "320px",
1797
+ fontFamily: "var(--se-font-family, system-ui, sans-serif)",
1798
+ fontSize: "14px",
1799
+ lineHeight: "1.4",
1800
+ transition: "opacity 0.3s ease"
1801
+ });
1802
+ const titleEl = document.createElement("div");
1803
+ titleEl.style.fontWeight = "600";
1804
+ titleEl.textContent = notification.title;
1805
+ toast.appendChild(titleEl);
1806
+ if (notification.body) {
1807
+ const bodyEl = document.createElement("div");
1808
+ bodyEl.style.marginTop = "4px";
1809
+ bodyEl.style.fontSize = "13px";
1810
+ bodyEl.style.color = "var(--se-color-text-secondary, #666)";
1811
+ bodyEl.textContent = notification.body;
1812
+ toast.appendChild(bodyEl);
1813
+ }
1814
+ document.body.appendChild(toast);
1815
+ let removeTimer;
1816
+ const fadeTimer = setTimeout(() => {
1817
+ toast.style.opacity = "0";
1818
+ removeTimer = setTimeout(() => {
1819
+ toast.remove();
1820
+ }, 300);
1821
+ }, 4e3);
1822
+ return () => {
1823
+ clearTimeout(fadeTimer);
1824
+ clearTimeout(removeTimer);
1825
+ toast.remove();
1826
+ };
1827
+ }
1828
+ function extractWorkflowsFromActive(activeActions) {
1829
+ const workflows = /* @__PURE__ */ new Map();
1830
+ for (const entry of activeActions) {
1831
+ const action = entry.action;
1832
+ if (action.kind === "overlays:tour" && action.workflow && action.tourId) {
1833
+ const meta = action.workflow;
1834
+ const rawSteps = action.steps || [];
1835
+ const steps = rawSteps.map((s) => ({
1836
+ id: s.id,
1837
+ title: meta.stepTitles?.[s.id] || s.id
1838
+ }));
1839
+ workflows.set(action.tourId, { meta, steps });
1840
+ }
1841
+ }
1842
+ return workflows;
1843
+ }
1844
+ var TAG_NAME = "syntro-workflow-tracker";
1845
+ var _unsubTourStarted, _unsubTourEvents, _toastCleanups, _notified, _completedMap, _persistInitialized, _tourWorkflows;
1846
+ var WorkflowTrackerLit = class extends LitElement {
1847
+ constructor() {
1848
+ super(...arguments);
1849
+ // ── Public properties ─────────────────────────────────────────────────────
1850
+ this.runtimeRef = null;
1851
+ // ── Internal reactive state ───────────────────────────────────────────────
1852
+ /** @internal */
1853
+ this._workflowEntries = [];
1854
+ /**
1855
+ * @internal
1856
+ * Bumped on tour.started / tour.resumed to trigger re-scan of active actions.
1857
+ */
1858
+ /** @internal */
1859
+ this._actionVersion = 0;
1860
+ // ── Private (non-reactive) fields ─────────────────────────────────────────
1861
+ // Subscription cleanup functions
1862
+ __privateAdd(this, _unsubTourStarted, null);
1863
+ __privateAdd(this, _unsubTourEvents, null);
1864
+ // Toast cleanup tracking
1865
+ __privateAdd(this, _toastCleanups, []);
1866
+ // Notifications already shown (mirrors notifiedRef)
1867
+ __privateAdd(this, _notified, /* @__PURE__ */ new Set());
1868
+ // Completed timestamps (mirrors completedMapRef)
1869
+ __privateAdd(this, _completedMap, {});
1870
+ // Whether persisted state has been loaded
1871
+ __privateAdd(this, _persistInitialized, false);
1872
+ // Cache of the last scanned tourWorkflows map (used by event handler)
1873
+ __privateAdd(this, _tourWorkflows, /* @__PURE__ */ new Map());
1874
+ }
1875
+ // ── Light DOM ─────────────────────────────────────────────────────────────
1876
+ /**
1877
+ * Render into the element itself (light DOM) so host-page CSS variables
1878
+ * flow through without a nested shadow boundary.
1879
+ */
1880
+ createRenderRoot() {
1881
+ return this;
1882
+ }
1883
+ // ── Helpers ───────────────────────────────────────────────────────────────
1884
+ get _stateNs() {
1885
+ return this.runtimeRef?.state?.user?.ns?.("workflows") ?? null;
1886
+ }
1887
+ /**
1888
+ * Re-scan active actions and update _tourWorkflows + entries.
1889
+ * Called initially and whenever _actionVersion bumps.
1890
+ */
1891
+ _rescanWorkflows() {
1892
+ const active = this.runtimeRef?.actions?.getActive?.() ?? [];
1893
+ const workflows = extractWorkflowsFromActive(active);
1894
+ __privateSet(this, _tourWorkflows, workflows);
1895
+ if (workflows.size === 0) return;
1896
+ const stateNs = this._stateNs;
1897
+ const dismissed = stateNs?.get("dismissed") ?? [];
1898
+ const completed = stateNs?.get("completed") ?? {};
1899
+ this._workflowEntries = (() => {
1900
+ const existingIds = new Set(this._workflowEntries.map((e) => e.tourId));
1901
+ const newEntries = [];
1902
+ for (const [tourId, { meta, steps }] of workflows) {
1903
+ if (existingIds.has(tourId)) continue;
1904
+ let status = "active";
1905
+ if (dismissed.includes(tourId)) {
1906
+ status = "dismissed";
1907
+ } else if (completed[tourId]) {
1908
+ status = "completed";
1909
+ }
1910
+ newEntries.push({
1911
+ tourId,
1912
+ meta,
1913
+ steps,
1914
+ currentStepId: null,
1915
+ completedSteps: [],
1916
+ status,
1917
+ completedAt: completed[tourId] || void 0
1918
+ });
1919
+ }
1920
+ return newEntries.length > 0 ? [...this._workflowEntries, ...newEntries] : this._workflowEntries;
1921
+ })();
1922
+ for (const [tourId, { meta }] of workflows) {
1923
+ const dismissed2 = stateNs?.get("dismissed") ?? [];
1924
+ const completed2 = stateNs?.get("completed") ?? {};
1925
+ if (!__privateGet(this, _notified).has(tourId) && meta.notification && !dismissed2.includes(tourId) && !completed2[tourId]) {
1926
+ __privateGet(this, _notified).add(tourId);
1927
+ stateNs?.set("notified", [...__privateGet(this, _notified)]);
1928
+ const cleanup = showWorkflowToast(meta.notification);
1929
+ __privateGet(this, _toastCleanups).push(cleanup);
1930
+ }
1931
+ }
1932
+ }
1933
+ // ── Lifecycle ─────────────────────────────────────────────────────────────
1934
+ connectedCallback() {
1935
+ super.connectedCallback();
1936
+ this._initSubscriptions();
1937
+ }
1938
+ disconnectedCallback() {
1939
+ super.disconnectedCallback();
1940
+ this._teardownSubscriptions();
1941
+ for (const cleanup of __privateGet(this, _toastCleanups)) {
1942
+ cleanup();
1943
+ }
1944
+ __privateSet(this, _toastCleanups, []);
1945
+ }
1946
+ updated(changed) {
1947
+ if (changed.has("runtimeRef")) {
1948
+ this._teardownSubscriptions();
1949
+ this._initSubscriptions();
1950
+ }
1951
+ if (changed.has("_actionVersion")) {
1952
+ this._rescanWorkflows();
1953
+ }
1954
+ }
1955
+ // ── Subscription management ───────────────────────────────────────────────
1956
+ _initSubscriptions() {
1957
+ if (!this.runtimeRef?.events?.subscribe) return;
1958
+ if (!__privateGet(this, _persistInitialized) && this._stateNs) {
1959
+ const notified = this._stateNs.get("notified") ?? [];
1960
+ for (const id of notified) {
1961
+ __privateGet(this, _notified).add(id);
1962
+ }
1963
+ const completed = this._stateNs.get("completed") ?? {};
1964
+ __privateSet(this, _completedMap, { ...completed });
1965
+ __privateSet(this, _persistInitialized, true);
1966
+ }
1967
+ __privateSet(this, _unsubTourStarted, this.runtimeRef.events.subscribe(
1968
+ { names: ["tour.started", "tour.resumed"] },
1969
+ () => {
1970
+ this._actionVersion += 1;
1971
+ }
1972
+ ));
1973
+ __privateSet(this, _unsubTourEvents, this.runtimeRef.events.subscribe(
1974
+ { patterns: ["^tour\\."] },
1975
+ (event) => {
1976
+ this._handleTourEvent(event);
1977
+ }
1978
+ ));
1979
+ this._rescanWorkflows();
1980
+ }
1981
+ _teardownSubscriptions() {
1982
+ var _a, _b;
1983
+ (_a = __privateGet(this, _unsubTourStarted)) == null ? void 0 : _a.call(this);
1984
+ __privateSet(this, _unsubTourStarted, null);
1985
+ (_b = __privateGet(this, _unsubTourEvents)) == null ? void 0 : _b.call(this);
1986
+ __privateSet(this, _unsubTourEvents, null);
1987
+ }
1988
+ // ── Event handler ─────────────────────────────────────────────────────────
1989
+ _handleTourEvent(event) {
1990
+ const tourId = event.props?.tourId;
1991
+ if (!tourId) return;
1992
+ if (!__privateGet(this, _tourWorkflows).has(tourId) && event.name === "tour.started") {
1993
+ this._actionVersion += 1;
1994
+ return;
1995
+ }
1996
+ if (!__privateGet(this, _tourWorkflows).has(tourId)) return;
1997
+ const stateNs = this._stateNs;
1998
+ this._workflowEntries = this._workflowEntries.map((entry) => {
1999
+ if (entry.tourId !== tourId) return entry;
2000
+ switch (event.name) {
2001
+ case "tour.started": {
2002
+ const startStepId = event.props?.startStepId || entry.steps[0]?.id || null;
2003
+ if (!__privateGet(this, _notified).has(tourId)) {
2004
+ __privateGet(this, _notified).add(tourId);
2005
+ stateNs?.set("notified", [...__privateGet(this, _notified)]);
2006
+ const workflow = __privateGet(this, _tourWorkflows).get(tourId);
2007
+ if (workflow?.meta.notification) {
2008
+ const cleanup = showWorkflowToast(workflow.meta.notification);
2009
+ __privateGet(this, _toastCleanups).push(cleanup);
2010
+ }
2011
+ }
2012
+ const activeIds = this._workflowEntries.filter((e) => e.status === "active" || e.tourId === tourId).map((e) => e.tourId);
2013
+ if (!activeIds.includes(tourId)) {
2014
+ activeIds.push(tourId);
2015
+ }
2016
+ stateNs?.set("active", [...new Set(activeIds)]);
2017
+ return {
2018
+ ...entry,
2019
+ status: "active",
2020
+ currentStepId: startStepId,
2021
+ completedSteps: entry.status === "active" ? entry.completedSteps : []
2022
+ };
2023
+ }
2024
+ case "tour.step_started": {
2025
+ const stepId = event.props?.stepId;
2026
+ return {
2027
+ ...entry,
2028
+ currentStepId: stepId || entry.currentStepId
2029
+ };
2030
+ }
2031
+ case "tour.step_changed": {
2032
+ const previousStepId = event.props?.previousStepId;
2033
+ const nextStepId = event.props?.nextStepId;
2034
+ const completedSteps = previousStepId && !entry.completedSteps.includes(previousStepId) ? [...entry.completedSteps, previousStepId] : entry.completedSteps;
2035
+ return {
2036
+ ...entry,
2037
+ currentStepId: nextStepId || entry.currentStepId,
2038
+ completedSteps
2039
+ };
2040
+ }
2041
+ case "tour.completed": {
2042
+ const completedAt = Date.now();
2043
+ __privateGet(this, _completedMap)[tourId] = completedAt;
2044
+ stateNs?.set("completed", { ...__privateGet(this, _completedMap) });
2045
+ return {
2046
+ ...entry,
2047
+ status: "completed",
2048
+ currentStepId: null,
2049
+ completedSteps: entry.steps.map((s) => s.id),
2050
+ completedAt
2051
+ };
2052
+ }
2053
+ case "tour.paused":
2054
+ return entry;
2055
+ default:
2056
+ return entry;
2057
+ }
2058
+ });
2059
+ }
2060
+ // ── User action handlers ──────────────────────────────────────────────────
2061
+ _handleStepClick(tourId, stepId) {
2062
+ this.runtimeRef?.events?.publish("workflow:jump_to_step", { tourId, stepId });
2063
+ this.dispatchEvent(
2064
+ new CustomEvent("workflow-step-click", {
2065
+ bubbles: true,
2066
+ detail: { tourId, stepId }
2067
+ })
2068
+ );
2069
+ }
2070
+ _handleDismiss(tourId) {
2071
+ this._workflowEntries = this._workflowEntries.map(
2072
+ (entry) => entry.tourId === tourId ? { ...entry, status: "dismissed" } : entry
2073
+ );
2074
+ const dismissedIds = this._workflowEntries.filter((e) => e.status === "dismissed").map((e) => e.tourId);
2075
+ this._stateNs?.set("dismissed", dismissedIds);
2076
+ this.dispatchEvent(
2077
+ new CustomEvent("workflow-dismissed", {
2078
+ bubbles: true,
2079
+ detail: { tourId }
2080
+ })
2081
+ );
2082
+ }
2083
+ // ── Render helpers ────────────────────────────────────────────────────────
2084
+ _renderProgressBar(completed, total) {
2085
+ const percent = total > 0 ? Math.round(completed / total * 100) : 0;
2086
+ const trackStyles = {
2087
+ width: "100%",
2088
+ height: "6px",
2089
+ borderRadius: "9999px",
2090
+ background: "rgba(255,255,255,0.08)",
2091
+ overflow: "hidden"
2092
+ };
2093
+ const fillStyles = {
2094
+ height: "100%",
2095
+ borderRadius: "9999px",
2096
+ background: `var(--se-color-primary, ${TOKEN_BLUE_4})`,
2097
+ transition: "width 0.3s ease",
2098
+ width: `${percent}%`
2099
+ };
2100
+ return html`
2101
+ <div style=${styleMap(trackStyles)}>
2102
+ <div
2103
+ role="progressbar"
2104
+ aria-valuenow=${percent}
2105
+ aria-valuemin="0"
2106
+ aria-valuemax="100"
2107
+ style=${styleMap(fillStyles)}
2108
+ ></div>
2109
+ </div>
2110
+ `;
2111
+ }
2112
+ _renderStepItem(step, isCompleted, isCurrent, tourId) {
2113
+ const btnStyles = {
2114
+ display: "flex",
2115
+ alignItems: "center",
2116
+ gap: "8px",
2117
+ width: "100%",
2118
+ padding: "6px 8px",
2119
+ borderRadius: "4px",
2120
+ border: "none",
2121
+ background: "transparent",
2122
+ cursor: "pointer",
2123
+ textAlign: "left",
2124
+ fontSize: "12px",
2125
+ lineHeight: "1.4",
2126
+ color: isCurrent ? `var(--se-color-text-primary, ${TOKEN_SLATE_12})` : `var(--se-color-text-secondary, ${TOKEN_SLATE_9})`,
2127
+ fontWeight: isCurrent ? "600" : "400"
2128
+ };
2129
+ const indicatorWrapStyles = {
2130
+ flexShrink: "0",
2131
+ width: "16px",
2132
+ textAlign: "center"
2133
+ };
2134
+ const dotStyles = {
2135
+ display: "inline-block",
2136
+ width: "6px",
2137
+ height: "6px",
2138
+ borderRadius: "50%",
2139
+ background: isCurrent ? `var(--se-color-primary, ${TOKEN_BLUE_4})` : "rgba(255,255,255,0.12)"
2140
+ };
2141
+ const checkStyles = {
2142
+ color: `var(--se-color-success, ${TOKEN_GREEN_4})`
2143
+ };
2144
+ const labelStyles = {
2145
+ flex: "1",
2146
+ overflow: "hidden",
2147
+ textOverflow: "ellipsis",
2148
+ whiteSpace: "nowrap"
2149
+ };
2150
+ const indicator = isCompleted ? html`<span role="img" aria-label="completed" style=${styleMap(checkStyles)}>&#10003;</span>` : isCurrent ? html`<span style=${styleMap(dotStyles)}></span>` : html`<span style=${styleMap(dotStyles)}></span>`;
2151
+ return html`
2152
+ <button
2153
+ type="button"
2154
+ data-testid=${`step-${step.id}`}
2155
+ data-current=${isCurrent ? "true" : nothing}
2156
+ data-completed=${isCompleted ? "true" : nothing}
2157
+ aria-current=${isCurrent ? "step" : nothing}
2158
+ style=${styleMap(btnStyles)}
2159
+ @click=${() => this._handleStepClick(tourId, step.id)}
2160
+ >
2161
+ <span style=${styleMap(indicatorWrapStyles)}>${indicator}</span>
2162
+ <span style=${styleMap(labelStyles)}>${step.title}</span>
2163
+ </button>
2164
+ `;
2165
+ }
2166
+ _renderWorkflowCard(workflow) {
2167
+ const completedCount = workflow.completedSteps.length;
2168
+ const totalSteps = workflow.steps.length;
2169
+ const cardStyles = {
2170
+ padding: "12px",
2171
+ borderRadius: "8px",
2172
+ border: "1px solid rgba(255,255,255,0.08)",
2173
+ background: "rgba(255,255,255,0.02)"
2174
+ };
2175
+ const headerStyles = {
2176
+ display: "flex",
2177
+ alignItems: "center",
2178
+ gap: "8px",
2179
+ marginBottom: "8px"
2180
+ };
2181
+ const titleStyles = {
2182
+ flex: "1",
2183
+ fontSize: "13px",
2184
+ fontWeight: "600",
2185
+ color: `var(--se-color-text-primary, ${TOKEN_SLATE_12})`,
2186
+ overflow: "hidden",
2187
+ textOverflow: "ellipsis",
2188
+ whiteSpace: "nowrap"
2189
+ };
2190
+ const dismissBtnStyles = {
2191
+ flexShrink: "0",
2192
+ padding: "2px 6px",
2193
+ border: "none",
2194
+ borderRadius: "4px",
2195
+ background: "transparent",
2196
+ color: `var(--se-color-text-tertiary, ${TOKEN_SLATE_7})`,
2197
+ fontSize: "12px",
2198
+ cursor: "pointer",
2199
+ lineHeight: "1"
2200
+ };
2201
+ const progressWrapStyles = {
2202
+ marginBottom: "8px"
2203
+ };
2204
+ const progressLabelStyles = {
2205
+ fontSize: "10px",
2206
+ color: `var(--se-color-text-tertiary, ${TOKEN_SLATE_7})`,
2207
+ marginTop: "4px"
2208
+ };
2209
+ const stepsColStyles = {
2210
+ display: "flex",
2211
+ flexDirection: "column"
2212
+ };
2213
+ return html`
2214
+ <div style=${styleMap(cardStyles)}>
2215
+ <!-- Header: icon + title + dismiss -->
2216
+ <div style=${styleMap(headerStyles)}>
2217
+ ${workflow.meta.icon ? html`<span data-testid="workflow-icon" style="flex-shrink:0;font-size:14px">${workflow.meta.icon}</span>` : nothing}
2218
+ <span style=${styleMap(titleStyles)}>${workflow.meta.title}</span>
2219
+ <button
2220
+ type="button"
2221
+ data-testid=${`dismiss-${workflow.tourId}`}
2222
+ style=${styleMap(dismissBtnStyles)}
2223
+ aria-label=${`Dismiss ${workflow.meta.title}`}
2224
+ @click=${() => this._handleDismiss(workflow.tourId)}
2225
+ >&#10005;</button>
2226
+ </div>
2227
+
2228
+ <!-- Progress bar + label -->
2229
+ <div style=${styleMap(progressWrapStyles)}>
2230
+ ${this._renderProgressBar(completedCount, totalSteps)}
2231
+ <div style=${styleMap(progressLabelStyles)}>
2232
+ ${completedCount} of ${totalSteps} steps
2233
+ </div>
2234
+ </div>
2235
+
2236
+ <!-- Step list -->
2237
+ <div style=${styleMap(stepsColStyles)}>
2238
+ ${workflow.steps.map(
2239
+ (step) => this._renderStepItem(
2240
+ step,
2241
+ workflow.completedSteps.includes(step.id),
2242
+ workflow.currentStepId === step.id,
2243
+ workflow.tourId
2244
+ )
2245
+ )}
2246
+ </div>
2247
+ </div>
2248
+ `;
2249
+ }
2250
+ // ── Render ────────────────────────────────────────────────────────────────
2251
+ render() {
2252
+ const activeWorkflows = this._workflowEntries.filter((w) => w.status === "active");
2253
+ if (activeWorkflows.length === 0) {
2254
+ const emptyStyles = {
2255
+ display: "flex",
2256
+ alignItems: "center",
2257
+ justifyContent: "center",
2258
+ padding: "24px 0",
2259
+ fontSize: "12px",
2260
+ color: `var(--se-color-text-tertiary, ${TOKEN_SLATE_7})`
2261
+ };
2262
+ return html`<div style=${styleMap(emptyStyles)}>No active workflows</div>`;
2263
+ }
2264
+ const containerStyles = {
2265
+ display: "flex",
2266
+ flexDirection: "column",
2267
+ gap: "8px"
2268
+ };
2269
+ return html`
2270
+ <div style=${styleMap(containerStyles)}>
2271
+ ${activeWorkflows.map((workflow) => this._renderWorkflowCard(workflow))}
2272
+ </div>
2273
+ `;
2274
+ }
2275
+ };
2276
+ _unsubTourStarted = new WeakMap();
2277
+ _unsubTourEvents = new WeakMap();
2278
+ _toastCleanups = new WeakMap();
2279
+ _notified = new WeakMap();
2280
+ _completedMap = new WeakMap();
2281
+ _persistInitialized = new WeakMap();
2282
+ _tourWorkflows = new WeakMap();
2283
+ // ── Static properties (no decorators) ────────────────────────────────────
2284
+ WorkflowTrackerLit.properties = {
2285
+ // Public input: runtime ref injected by MountableWidget
2286
+ runtimeRef: { attribute: false },
2287
+ // Internal reactive state
2288
+ _workflowEntries: { state: true },
2289
+ _actionVersion: { state: true }
2290
+ };
2291
+ if (typeof window !== "undefined" && !customElements.get(TAG_NAME)) {
2292
+ customElements.define(TAG_NAME, WorkflowTrackerLit);
2293
+ }
2294
+ var WorkflowWidgetLitMountable = {
2295
+ mount(container, config) {
2296
+ const runtime2 = config?.runtime ?? null;
2297
+ if (typeof window !== "undefined" && !customElements.get(TAG_NAME)) {
2298
+ customElements.define(TAG_NAME, WorkflowTrackerLit);
2299
+ }
2300
+ const el = document.createElement(TAG_NAME);
2301
+ el.runtimeRef = runtime2;
2302
+ container.appendChild(el);
2303
+ return () => {
2304
+ el.remove();
2305
+ };
2306
+ },
2307
+ update(container, config) {
2308
+ const el = container.querySelector(TAG_NAME);
2309
+ if (!el) return;
2310
+ const runtime2 = config?.runtime ?? null;
2311
+ el.runtimeRef = runtime2;
2312
+ }
2313
+ };
2314
+
2315
+ // src/runtime.ts
2316
+ var executeHighlight = async (action, context) => {
2317
+ let anchorEl = context.resolveAnchor(action.anchorId);
2318
+ if (!anchorEl && context.waitForAnchor) {
2319
+ anchorEl = await context.waitForAnchor(action.anchorId, 3e3);
2320
+ }
2321
+ if (!anchorEl) {
2322
+ console.warn(`[adaptive-overlays] Anchor not found after waiting: ${action.anchorId.selector}`);
2323
+ return { cleanup: () => {
2324
+ } };
2325
+ }
2326
+ if (anchorEl.getAttribute("data-syntro-highlight-dismissed")) {
2327
+ return { cleanup: () => {
2328
+ } };
2329
+ }
2330
+ const existing = anchorEl.getAttribute("data-syntro-highlight");
2331
+ if (existing) {
2332
+ const prev = context.overlayRoot.querySelectorAll(
2333
+ ".syntro-spotlight-scrim, .syntro-spotlight-ring"
2334
+ );
2335
+ prev.forEach((el) => el.remove());
2336
+ }
2337
+ anchorEl.setAttribute("data-syntro-highlight", "true");
2338
+ let ringColor = action.style?.color;
2339
+ if (!ringColor) {
2340
+ try {
2341
+ const primary = getComputedStyle(context.overlayRoot).getPropertyValue("--sc-color-primary")?.trim();
2342
+ if (primary) ringColor = primary;
2343
+ } catch {
2344
+ }
2345
+ }
2346
+ const handle = showHighlight(anchorEl, context.overlayRoot, {
2347
+ paddingPx: action.style?.paddingPx ?? 12,
2348
+ radiusPx: action.style?.radiusPx ?? 12,
2349
+ scrimOpacity: action.style?.scrimOpacity ?? 0.55,
2350
+ ringColor,
2351
+ blocking: action.blocking ?? false,
2352
+ onClickOutside: action.onClickOutside ?? true,
2353
+ onEsc: action.onEsc ?? true,
2354
+ onDismiss: () => {
2355
+ anchorEl.setAttribute("data-syntro-highlight-dismissed", "true");
2356
+ }
2357
+ });
2358
+ context.publishEvent("action.applied", {
2359
+ id: context.generateId(),
2360
+ kind: "overlays:highlight",
2361
+ anchorId: action.anchorId
2362
+ });
2363
+ return {
2364
+ cleanup: () => {
2365
+ handle.destroy();
2366
+ anchorEl.removeAttribute("data-syntro-highlight");
2367
+ anchorEl.removeAttribute("data-syntro-highlight-dismissed");
2368
+ }
2369
+ };
2370
+ };
2371
+ var executePulse = async (action, context) => {
2372
+ let anchorEl = context.resolveAnchor(action.anchorId);
2373
+ if (!anchorEl && context.waitForAnchor) {
2374
+ anchorEl = await context.waitForAnchor(action.anchorId, 3e3);
2375
+ }
2376
+ if (!anchorEl) {
2377
+ console.warn(`[adaptive-overlays] Anchor not found after waiting: ${action.anchorId.selector}`);
2378
+ return { cleanup: () => {
2379
+ } };
2380
+ }
2381
+ const duration = action.duration ?? 4e3;
2382
+ await new Promise((resolve) => requestAnimationFrame(resolve));
2383
+ const parseHex = (hex) => ({
2384
+ r: parseInt(hex.slice(1, 3), 16),
2385
+ g: parseInt(hex.slice(3, 5), 16),
2386
+ b: parseInt(hex.slice(5, 7), 16)
2387
+ });
2388
+ const fallback = { r: 79, g: 70, b: 229 };
2389
+ let primary = fallback;
2390
+ let secondary = null;
2391
+ try {
2392
+ const styles = getComputedStyle(context.overlayRoot);
2393
+ const pHex = styles.getPropertyValue("--sc-color-primary")?.trim();
2394
+ const sHex = styles.getPropertyValue("--sc-color-primary-hover")?.trim();
2395
+ if (pHex?.startsWith("#") && pHex.length >= 7) {
2396
+ primary = parseHex(pHex);
2397
+ }
2398
+ if (sHex?.startsWith("#") && sHex.length >= 7) {
2399
+ secondary = parseHex(sHex);
2400
+ }
2401
+ } catch {
2402
+ }
2403
+ const existing = document.querySelector("[data-syntro-pulse-styles]");
2404
+ if (existing) existing.remove();
2405
+ const style = document.createElement("style");
2406
+ style.setAttribute("data-syntro-pulse-styles", "");
2407
+ const { r: pr, g: pg, b: pb } = primary;
2408
+ if (secondary) {
2409
+ const { r: sr, g: sg, b: sb } = secondary;
2410
+ style.textContent = `
133
2411
  @keyframes syntro-pulse-anim {
134
2412
  0%, 100% {
135
2413
  box-shadow: 0 0 0 0 rgba(${pr}, ${pg}, ${pb}, 0.35);
@@ -145,9 +2423,8 @@ export const executePulse = async (action, context) => {
145
2423
  }
146
2424
  }
147
2425
  `;
148
- }
149
- else {
150
- style.textContent = `
2426
+ } else {
2427
+ style.textContent = `
151
2428
  @keyframes syntro-pulse-anim {
152
2429
  0%, 100% {
153
2430
  box-shadow: 0 0 0 0 rgba(${pr}, ${pg}, ${pb}, 0.35);
@@ -157,255 +2434,220 @@ export const executePulse = async (action, context) => {
157
2434
  }
158
2435
  }
159
2436
  `;
2437
+ }
2438
+ document.head.appendChild(style);
2439
+ const originalAnimation = anchorEl.style.animation;
2440
+ anchorEl.style.animation = "syntro-pulse-anim 2.5s cubic-bezier(0.4, 0, 0.6, 1) infinite";
2441
+ anchorEl.setAttribute("data-syntro-pulse", "true");
2442
+ const timeoutId = setTimeout(() => {
2443
+ anchorEl.style.animation = originalAnimation;
2444
+ anchorEl.removeAttribute("data-syntro-pulse");
2445
+ }, duration);
2446
+ context.publishEvent("action.applied", {
2447
+ id: context.generateId(),
2448
+ kind: "overlays:pulse",
2449
+ anchorId: action.anchorId,
2450
+ duration
2451
+ });
2452
+ return {
2453
+ cleanup: () => {
2454
+ clearTimeout(timeoutId);
2455
+ if (!anchorEl.isConnected) return;
2456
+ anchorEl.style.animation = originalAnimation;
2457
+ anchorEl.removeAttribute("data-syntro-pulse");
160
2458
  }
161
- document.head.appendChild(style);
162
- // Apply pulse animation via inline style (scoped to this element)
163
- const originalAnimation = anchorEl.style.animation;
164
- anchorEl.style.animation = 'syntro-pulse-anim 2.5s cubic-bezier(0.4, 0, 0.6, 1) infinite';
165
- anchorEl.setAttribute('data-syntro-pulse', 'true');
166
- // Set up auto-remove timeout
167
- const timeoutId = setTimeout(() => {
168
- anchorEl.style.animation = originalAnimation;
169
- anchorEl.removeAttribute('data-syntro-pulse');
170
- }, duration);
171
- context.publishEvent('action.applied', {
172
- id: context.generateId(),
173
- kind: 'overlays:pulse',
174
- anchorId: action.anchorId,
175
- duration,
176
- });
177
- return {
178
- cleanup: () => {
179
- clearTimeout(timeoutId);
180
- if (!anchorEl.isConnected)
181
- return;
182
- anchorEl.style.animation = originalAnimation;
183
- anchorEl.removeAttribute('data-syntro-pulse');
184
- },
185
- };
2459
+ };
186
2460
  };
187
- /**
188
- * Execute a badge action
189
- */
190
- export const executeBadge = async (action, context) => {
191
- let anchorEl = context.resolveAnchor(action.anchorId);
192
- if (!anchorEl && context.waitForAnchor) {
193
- anchorEl = await context.waitForAnchor(action.anchorId, 3000);
2461
+ var executeBadge = async (action, context) => {
2462
+ let anchorEl = context.resolveAnchor(action.anchorId);
2463
+ if (!anchorEl && context.waitForAnchor) {
2464
+ anchorEl = await context.waitForAnchor(action.anchorId, 3e3);
2465
+ }
2466
+ if (!anchorEl) {
2467
+ console.warn(`[adaptive-overlays] Anchor not found after waiting: ${action.anchorId.selector}`);
2468
+ return { cleanup: () => {
2469
+ } };
2470
+ }
2471
+ let badgeColor = "#4f46e5";
2472
+ try {
2473
+ const primary = getComputedStyle(context.overlayRoot).getPropertyValue("--sc-color-primary")?.trim();
2474
+ if (primary?.startsWith("#") && primary.length >= 7) {
2475
+ badgeColor = primary;
194
2476
  }
195
- if (!anchorEl) {
196
- console.warn(`[adaptive-overlays] Anchor not found after waiting: ${action.anchorId.selector}`);
197
- return { cleanup: () => { } };
198
- }
199
- // Read primary color from theme (overlay root has --sc-* vars from shadow DOM)
200
- let badgeColor = '#4f46e5';
201
- try {
202
- const primary = getComputedStyle(context.overlayRoot)
203
- .getPropertyValue('--sc-color-primary')
204
- ?.trim();
205
- if (primary?.startsWith('#') && primary.length >= 7) {
206
- badgeColor = primary;
207
- }
2477
+ } catch {
2478
+ }
2479
+ const badge2 = document.createElement("div");
2480
+ badge2.textContent = action.content;
2481
+ badge2.setAttribute("data-syntro-badge", action.anchorId.selector);
2482
+ Object.assign(badge2.style, {
2483
+ position: "absolute",
2484
+ padding: "2px 6px",
2485
+ fontSize: "12px",
2486
+ fontWeight: "600",
2487
+ lineHeight: "1",
2488
+ color: "white",
2489
+ background: badgeColor,
2490
+ borderRadius: "9999px",
2491
+ pointerEvents: "none",
2492
+ zIndex: "2147483646",
2493
+ whiteSpace: "nowrap"
2494
+ });
2495
+ const position = action.position ?? "top-right";
2496
+ const originalPosition = anchorEl.style.position;
2497
+ if (getComputedStyle(anchorEl).position === "static") {
2498
+ anchorEl.style.position = "relative";
2499
+ }
2500
+ anchorEl.appendChild(badge2);
2501
+ switch (position) {
2502
+ case "top-left":
2503
+ Object.assign(badge2.style, { top: "-8px", left: "-8px" });
2504
+ break;
2505
+ case "top-right":
2506
+ Object.assign(badge2.style, { top: "-8px", right: "-8px" });
2507
+ break;
2508
+ case "bottom-left":
2509
+ Object.assign(badge2.style, { bottom: "-8px", left: "-8px" });
2510
+ break;
2511
+ case "bottom-right":
2512
+ Object.assign(badge2.style, { bottom: "-8px", right: "-8px" });
2513
+ break;
2514
+ }
2515
+ context.publishEvent("action.applied", {
2516
+ id: context.generateId(),
2517
+ kind: "overlays:badge",
2518
+ anchorId: action.anchorId,
2519
+ content: action.content,
2520
+ position
2521
+ });
2522
+ return {
2523
+ cleanup: () => {
2524
+ try {
2525
+ badge2.remove();
2526
+ } catch {
2527
+ }
2528
+ if (!anchorEl.isConnected) return;
2529
+ if (originalPosition !== void 0) {
2530
+ anchorEl.style.position = originalPosition;
2531
+ }
2532
+ },
2533
+ updateFn: (changes) => {
2534
+ if ("content" in changes && typeof changes.content === "string") {
2535
+ badge2.textContent = changes.content;
2536
+ }
208
2537
  }
209
- catch {
210
- /* fallback to default */
211
- }
212
- // Create badge element with inline styles (no global style injection)
213
- const badge = document.createElement('div');
214
- badge.textContent = action.content;
215
- badge.setAttribute('data-syntro-badge', action.anchorId.selector);
216
- // Apply styles directly to element
217
- Object.assign(badge.style, {
218
- position: 'absolute',
219
- padding: '2px 6px',
220
- fontSize: '12px',
221
- fontWeight: '600',
222
- lineHeight: '1',
223
- color: 'white',
224
- background: badgeColor,
225
- borderRadius: '9999px',
226
- pointerEvents: 'none',
227
- zIndex: '2147483646',
228
- whiteSpace: 'nowrap',
229
- });
230
- // Position badge relative to anchor
231
- const position = action.position ?? 'top-right';
232
- // Ensure anchor has relative positioning for badge
233
- const originalPosition = anchorEl.style.position;
234
- if (getComputedStyle(anchorEl).position === 'static') {
235
- anchorEl.style.position = 'relative';
236
- }
237
- // Append to anchor for relative positioning
238
- anchorEl.appendChild(badge);
239
- // Position based on config
240
- switch (position) {
241
- case 'top-left':
242
- Object.assign(badge.style, { top: '-8px', left: '-8px' });
243
- break;
244
- case 'top-right':
245
- Object.assign(badge.style, { top: '-8px', right: '-8px' });
246
- break;
247
- case 'bottom-left':
248
- Object.assign(badge.style, { bottom: '-8px', left: '-8px' });
249
- break;
250
- case 'bottom-right':
251
- Object.assign(badge.style, { bottom: '-8px', right: '-8px' });
252
- break;
253
- }
254
- context.publishEvent('action.applied', {
255
- id: context.generateId(),
256
- kind: 'overlays:badge',
257
- anchorId: action.anchorId,
258
- content: action.content,
259
- position,
260
- });
261
- return {
262
- cleanup: () => {
263
- try {
264
- badge.remove();
265
- }
266
- catch {
267
- /* already detached */
268
- }
269
- if (!anchorEl.isConnected)
270
- return;
271
- if (originalPosition !== undefined) {
272
- anchorEl.style.position = originalPosition;
273
- }
274
- },
275
- updateFn: (changes) => {
276
- if ('content' in changes && typeof changes.content === 'string') {
277
- badge.textContent = changes.content;
278
- }
279
- },
280
- };
2538
+ };
281
2539
  };
282
- /**
283
- * Execute a tooltip action
284
- */
285
- export const executeTooltip = async (action, context) => {
286
- let anchorEl = context.resolveAnchor(action.anchorId);
287
- if (!anchorEl && context.waitForAnchor) {
288
- anchorEl = await context.waitForAnchor(action.anchorId, 3000);
289
- }
290
- if (!anchorEl) {
291
- console.warn(`[adaptive-overlays] Anchor not found after waiting: ${action.anchorId.selector}`);
292
- return { cleanup: () => { } };
293
- }
294
- // Build tooltip HTML
295
- const { content } = action;
296
- let html = '';
297
- if (content.title) {
298
- html += `<div class="syntro-tt-title">${sanitizeHtml(content.title)}</div>`;
299
- }
300
- html += `<div class="syntro-tt-body">${sanitizeHtml(content.body)}</div>`;
301
- // Add CTA buttons if provided (new multi-button support)
302
- if (content.ctaButtons && content.ctaButtons.length > 0) {
303
- html += `<div class="syntro-tt-actions">`;
304
- for (const btn of content.ctaButtons) {
305
- const isPrimary = btn.primary ?? false;
306
- html += `
2540
+ var executeTooltip = async (action, context) => {
2541
+ let anchorEl = context.resolveAnchor(action.anchorId);
2542
+ if (!anchorEl && context.waitForAnchor) {
2543
+ anchorEl = await context.waitForAnchor(action.anchorId, 3e3);
2544
+ }
2545
+ if (!anchorEl) {
2546
+ console.warn(`[adaptive-overlays] Anchor not found after waiting: ${action.anchorId.selector}`);
2547
+ return { cleanup: () => {
2548
+ } };
2549
+ }
2550
+ const { content } = action;
2551
+ let html2 = "";
2552
+ if (content.title) {
2553
+ html2 += `<div class="syntro-tt-title">${sanitizeHtml(content.title)}</div>`;
2554
+ }
2555
+ html2 += `<div class="syntro-tt-body">${sanitizeHtml(content.body)}</div>`;
2556
+ if (content.ctaButtons && content.ctaButtons.length > 0) {
2557
+ html2 += `<div class="syntro-tt-actions">`;
2558
+ for (const btn of content.ctaButtons) {
2559
+ const isPrimary = btn.primary ?? false;
2560
+ html2 += `
307
2561
  <button
308
- class="syntro-tt-btn ${isPrimary ? 'syntro-tt-btn-primary' : ''}"
2562
+ class="syntro-tt-btn ${isPrimary ? "syntro-tt-btn-primary" : ""}"
309
2563
  data-syntro-action="${sanitizeHtml(btn.actionId)}"
310
2564
  >
311
2565
  ${sanitizeHtml(btn.label)}
312
2566
  </button>
313
2567
  `;
314
- }
315
- html += `</div>`;
316
2568
  }
317
- else if (content.cta) {
318
- // Legacy single CTA support
319
- html += `<div class="syntro-tt-actions">
2569
+ html2 += `</div>`;
2570
+ } else if (content.cta) {
2571
+ html2 += `<div class="syntro-tt-actions">
320
2572
  <button class="syntro-tt-btn syntro-tt-btn-primary" data-syntro-action="cta">
321
2573
  ${sanitizeHtml(content.cta.label)}
322
2574
  </button>
323
2575
  </div>`;
2576
+ }
2577
+ const handle = showTooltip(anchorEl, context.overlayRoot, {
2578
+ html: html2,
2579
+ placement: action.placement ?? "top",
2580
+ trigger: action.trigger ?? "immediate",
2581
+ onAction: (actionId) => {
2582
+ if (actionId === "dismiss") {
2583
+ handle.destroy();
2584
+ return;
2585
+ }
2586
+ if (actionId === "cta" && content.cta) {
2587
+ context.publishEvent("action.cta_clicked", {
2588
+ anchorId: action.anchorId,
2589
+ ctaLabel: content.cta.label
2590
+ });
2591
+ } else if (content.ctaButtons) {
2592
+ const clickedBtn = content.ctaButtons.find((b) => b.actionId === actionId);
2593
+ if (clickedBtn) {
2594
+ context.publishEvent("action.tooltip_cta_clicked", {
2595
+ anchorId: action.anchorId,
2596
+ actionId,
2597
+ label: clickedBtn.label
2598
+ });
2599
+ }
2600
+ }
2601
+ handle.destroy();
324
2602
  }
325
- const handle = showTooltip(anchorEl, context.overlayRoot, {
326
- html,
327
- placement: action.placement ?? 'top',
328
- trigger: action.trigger ?? 'immediate',
329
- onAction: (actionId) => {
330
- // Handle dismiss CTA — destroy the tooltip
331
- if (actionId === 'dismiss') {
332
- handle.destroy();
333
- return;
334
- }
335
- if (actionId === 'cta' && content.cta) {
336
- context.publishEvent('action.cta_clicked', {
337
- anchorId: action.anchorId,
338
- ctaLabel: content.cta.label,
339
- });
340
- }
341
- else if (content.ctaButtons) {
342
- const clickedBtn = content.ctaButtons.find((b) => b.actionId === actionId);
343
- if (clickedBtn) {
344
- context.publishEvent('action.tooltip_cta_clicked', {
345
- anchorId: action.anchorId,
346
- actionId,
347
- label: clickedBtn.label,
348
- });
349
- }
350
- }
351
- handle.destroy();
352
- },
353
- });
354
- context.publishEvent('action.applied', {
355
- id: context.generateId(),
356
- kind: 'overlays:tooltip',
357
- anchorId: action.anchorId,
358
- trigger: action.trigger ?? 'immediate',
359
- });
360
- return {
361
- cleanup: () => {
362
- handle.destroy();
363
- },
364
- };
2603
+ });
2604
+ context.publishEvent("action.applied", {
2605
+ id: context.generateId(),
2606
+ kind: "overlays:tooltip",
2607
+ anchorId: action.anchorId,
2608
+ trigger: action.trigger ?? "immediate"
2609
+ });
2610
+ return {
2611
+ cleanup: () => {
2612
+ handle.destroy();
2613
+ }
2614
+ };
365
2615
  };
366
- // ============================================================================
367
- // Executor Definitions for Registration
368
- // ============================================================================
369
- /**
370
- * All executors provided by this app.
371
- * These are registered with the runtime's ExecutorRegistry.
372
- */
373
- export const executors = [
374
- { kind: 'overlays:highlight', executor: executeHighlight },
375
- { kind: 'overlays:pulse', executor: executePulse },
376
- { kind: 'overlays:badge', executor: executeBadge },
377
- { kind: 'overlays:tooltip', executor: executeTooltip },
378
- { kind: 'overlays:modal', executor: executeModal },
379
- { kind: 'overlays:tour', executor: executeTour },
380
- { kind: 'overlays:celebrate', executor: executeCelebrate },
2616
+ var executors = [
2617
+ { kind: "overlays:highlight", executor: executeHighlight },
2618
+ { kind: "overlays:pulse", executor: executePulse },
2619
+ { kind: "overlays:badge", executor: executeBadge },
2620
+ { kind: "overlays:tooltip", executor: executeTooltip },
2621
+ { kind: "overlays:modal", executor: executeModal },
2622
+ { kind: "overlays:tour", executor: executeTour },
2623
+ { kind: "overlays:celebrate", executor: executeCelebrate }
381
2624
  ];
382
- /**
383
- * App runtime manifest.
384
- */
385
- export const runtime = {
386
- id: 'adaptive-overlays',
387
- version: '1.0.0',
388
- name: 'Overlays',
389
- description: 'Tooltips, highlights, badges, modals, celebrations, visual overlays, and workflow tracking',
390
- executors,
391
- widgets: [
392
- {
393
- id: 'adaptive-overlays:workflow-tracker',
394
- component: WorkflowMountableWidget,
395
- metadata: {
396
- name: 'Workflow Tracker',
397
- icon: '📋',
398
- description: 'Tracks multi-step workflow progress across tours',
399
- },
400
- },
401
- {
402
- id: 'adaptive-overlays:workflow-tracker-lit',
403
- component: WorkflowWidgetLitMountable,
404
- metadata: {
405
- name: 'Workflow Tracker (Lit)',
406
- icon: '📋',
407
- description: 'Lit web component equivalent of the Workflow Tracker — decorator-free, light DOM',
408
- },
409
- },
410
- ],
2625
+ var runtime = {
2626
+ id: "adaptive-overlays",
2627
+ version: "1.0.0",
2628
+ name: "Overlays",
2629
+ description: "Tooltips, highlights, badges, modals, celebrations, visual overlays, and workflow tracking",
2630
+ executors,
2631
+ widgets: [
2632
+ {
2633
+ id: "adaptive-overlays:workflow-tracker",
2634
+ component: WorkflowWidgetLitMountable,
2635
+ metadata: {
2636
+ name: "Workflow Tracker",
2637
+ icon: "\u{1F4CB}",
2638
+ description: "Tracks multi-step workflow progress across tours"
2639
+ }
2640
+ }
2641
+ ]
2642
+ };
2643
+ export {
2644
+ executeBadge,
2645
+ executeHighlight,
2646
+ executeModal,
2647
+ executePulse,
2648
+ executeTooltip,
2649
+ executeTour,
2650
+ executors,
2651
+ runtime
411
2652
  };
2653
+ //# sourceMappingURL=runtime.js.map