@syntrologie/runtime-sdk 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CAPABILITIES.md +400 -0
- package/dist/SmartCanvasApp.js +28 -10
- package/dist/SmartCanvasApp.js.map +1 -1
- package/dist/api.d.ts +23 -0
- package/dist/api.js +23 -5
- package/dist/api.js.map +1 -1
- package/dist/blocks/data/ComparisonBlock.d.ts +10 -0
- package/dist/blocks/data/ComparisonBlock.js +92 -0
- package/dist/blocks/data/ComparisonBlock.js.map +1 -0
- package/dist/blocks/data/StatsBlock.d.ts +10 -0
- package/dist/blocks/data/StatsBlock.js +103 -0
- package/dist/blocks/data/StatsBlock.js.map +1 -0
- package/dist/blocks/data/index.d.ts +2 -0
- package/dist/blocks/data/index.js +3 -0
- package/dist/blocks/data/index.js.map +1 -0
- package/dist/blocks/index.d.ts +26 -0
- package/dist/blocks/index.js +94 -0
- package/dist/blocks/index.js.map +1 -0
- package/dist/blocks/interactive/ChecklistBlock.d.ts +11 -0
- package/dist/blocks/interactive/ChecklistBlock.js +110 -0
- package/dist/blocks/interactive/ChecklistBlock.js.map +1 -0
- package/dist/blocks/interactive/RatingBlock.d.ts +11 -0
- package/dist/blocks/interactive/RatingBlock.js +131 -0
- package/dist/blocks/interactive/RatingBlock.js.map +1 -0
- package/dist/blocks/interactive/index.d.ts +2 -0
- package/dist/blocks/interactive/index.js +3 -0
- package/dist/blocks/interactive/index.js.map +1 -0
- package/dist/blocks/notification/NotificationBlock.d.ts +26 -0
- package/dist/blocks/notification/NotificationBlock.js +166 -0
- package/dist/blocks/notification/NotificationBlock.js.map +1 -0
- package/dist/blocks/notification/index.d.ts +1 -0
- package/dist/blocks/notification/index.js +2 -0
- package/dist/blocks/notification/index.js.map +1 -0
- package/dist/bootstrap.d.ts +19 -1
- package/dist/bootstrap.js +118 -17
- package/dist/bootstrap.js.map +1 -1
- package/dist/components/ShadowCanvasOverlay.d.ts +12 -3
- package/dist/components/ShadowCanvasOverlay.js +75 -24
- package/dist/components/ShadowCanvasOverlay.js.map +1 -1
- package/dist/components/{RectangleCard.d.ts → TileCard.d.ts} +4 -4
- package/dist/components/{RectangleCard.js → TileCard.js} +57 -6
- package/dist/components/TileCard.js.map +1 -0
- package/dist/components/TileWheel.d.ts +8 -0
- package/dist/components/{RectangleWheel.js → TileWheel.js} +7 -7
- package/dist/components/TileWheel.js.map +1 -0
- package/dist/configFetcher.js.map +1 -1
- package/dist/earlyPatcher.js +1 -2
- package/dist/earlyPatcher.js.map +1 -1
- package/dist/editorLoader.js +74 -17
- package/dist/editorLoader.js.map +1 -1
- package/dist/experiments/adapters/growthbook.d.ts +18 -2
- package/dist/experiments/adapters/growthbook.js +17 -3
- package/dist/experiments/adapters/growthbook.js.map +1 -1
- package/dist/experiments/registry.d.ts +18 -4
- package/dist/experiments/registry.js +1 -1
- package/dist/experiments/registry.js.map +1 -1
- package/dist/experiments/types.d.ts +8 -3
- package/dist/fetchers/cdnFetcher.js.map +1 -1
- package/dist/fetchers/experimentsFetcher.js +20 -0
- package/dist/fetchers/experimentsFetcher.js.map +1 -1
- package/dist/fetchers/types.d.ts +2 -2
- package/dist/hooks/useCanvasOverlays.d.ts +5 -1
- package/dist/hooks/useCanvasOverlays.js +33 -13
- package/dist/hooks/useCanvasOverlays.js.map +1 -1
- package/dist/hooks/useShadowCanvasConfig.d.ts +9 -2
- package/dist/hooks/useShadowCanvasConfig.js +8 -8
- package/dist/hooks/useShadowCanvasConfig.js.map +1 -1
- package/dist/hostPatcher/core/patcher.js +14 -15
- package/dist/hostPatcher/core/patcher.js.map +1 -1
- package/dist/hostPatcher/core/sanitizer.js +1 -1
- package/dist/hostPatcher/core/sanitizer.js.map +1 -1
- package/dist/hostPatcher/core/types.d.ts +8 -9
- package/dist/hostPatcher/policy/defaultPolicy.d.ts +4 -0
- package/dist/hostPatcher/policy/defaultPolicy.js +9 -37
- package/dist/hostPatcher/policy/defaultPolicy.js.map +1 -1
- package/dist/hostPatcher/utils/anchors.js +3 -3
- package/dist/hostPatcher/utils/anchors.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/overlays/recipeRegistry.d.ts +14 -0
- package/dist/overlays/recipeRegistry.js +32 -0
- package/dist/overlays/recipeRegistry.js.map +1 -0
- package/dist/overlays/runtime/index.d.ts +1 -0
- package/dist/overlays/runtime/index.js +1 -0
- package/dist/overlays/runtime/index.js.map +1 -1
- package/dist/overlays/runtime/overlay/modal.d.ts +11 -0
- package/dist/overlays/runtime/overlay/modal.js +78 -0
- package/dist/overlays/runtime/overlay/modal.js.map +1 -0
- package/dist/overlays/runtime/overlay/root.js +132 -0
- package/dist/overlays/runtime/overlay/root.js.map +1 -1
- package/dist/overlays/runtime/overlay/runner.d.ts +2 -0
- package/dist/overlays/runtime/overlay/runner.js +441 -2
- package/dist/overlays/runtime/overlay/runner.js.map +1 -1
- package/dist/overlays/runtime/overlay/tooltip.d.ts +1 -0
- package/dist/overlays/runtime/overlay/tooltip.js +61 -1
- package/dist/overlays/runtime/overlay/tooltip.js.map +1 -1
- package/dist/overlays/schema.d.ts +6 -6
- package/dist/overlays/types.d.ts +55 -1
- package/dist/react.d.ts +6 -1
- package/dist/react.js +31 -9
- package/dist/react.js.map +1 -1
- package/dist/render/RenderContext.d.ts +39 -0
- package/dist/render/RenderContext.js +67 -0
- package/dist/render/RenderContext.js.map +1 -0
- package/dist/render/index.d.ts +3 -0
- package/dist/render/index.js +3 -0
- package/dist/render/index.js.map +1 -0
- package/dist/render/types.d.ts +81 -0
- package/dist/render/types.js +2 -0
- package/dist/render/types.js.map +1 -0
- package/dist/smart-canvas.esm.js +192 -25
- package/dist/smart-canvas.esm.js.map +4 -4
- package/dist/smart-canvas.js +25962 -26846
- package/dist/smart-canvas.js.map +4 -4
- package/dist/smart-canvas.min.js +192 -25
- package/dist/smart-canvas.min.js.map +4 -4
- package/dist/telemetry/adapters/posthog.d.ts +6 -0
- package/dist/telemetry/adapters/posthog.js +48 -0
- package/dist/telemetry/adapters/posthog.js.map +1 -1
- package/dist/telemetry/types.d.ts +30 -0
- package/dist/theme/ThemeProvider.d.ts +31 -0
- package/dist/theme/ThemeProvider.js +109 -0
- package/dist/theme/ThemeProvider.js.map +1 -0
- package/dist/theme/defaultTheme.d.ts +18 -0
- package/dist/theme/defaultTheme.js +163 -0
- package/dist/theme/defaultTheme.js.map +1 -0
- package/dist/theme/extractHostTheme.d.ts +14 -0
- package/dist/theme/extractHostTheme.js +261 -0
- package/dist/theme/extractHostTheme.js.map +1 -0
- package/dist/theme/index.d.ts +5 -0
- package/dist/theme/index.js +7 -0
- package/dist/theme/index.js.map +1 -0
- package/dist/theme/types.d.ts +91 -0
- package/dist/theme/types.js +6 -0
- package/dist/theme/types.js.map +1 -0
- package/dist/token.d.ts +4 -0
- package/dist/token.js.map +1 -1
- package/dist/types.d.ts +228 -47
- package/package.json +8 -4
- package/schema/canvas-config.schema.json +24 -15
- package/dist/components/RectangleCard.js.map +0 -1
- package/dist/components/RectangleWheel.d.ts +0 -8
- package/dist/components/RectangleWheel.js.map +0 -1
|
@@ -99,6 +99,40 @@ export function injectBaseStyles() {
|
|
|
99
99
|
opacity: 1;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
/* Tooltip action buttons (for tours) */
|
|
103
|
+
.syntro-tt-actions {
|
|
104
|
+
display: flex;
|
|
105
|
+
gap: 8px;
|
|
106
|
+
margin-top: 12px;
|
|
107
|
+
justify-content: flex-end;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.syntro-tt-btn {
|
|
111
|
+
padding: 8px 16px;
|
|
112
|
+
border-radius: 6px;
|
|
113
|
+
font-size: 13px;
|
|
114
|
+
font-weight: 500;
|
|
115
|
+
cursor: pointer;
|
|
116
|
+
transition: all 150ms;
|
|
117
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
118
|
+
background: transparent;
|
|
119
|
+
color: inherit;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.syntro-tt-btn:hover {
|
|
123
|
+
background: rgba(255, 255, 255, 0.1);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.syntro-tt-btn-primary {
|
|
127
|
+
background: var(--syntro-accent, #4f46e5);
|
|
128
|
+
border-color: transparent;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.syntro-tt-btn-primary:hover {
|
|
132
|
+
background: var(--syntro-accent, #4338ca);
|
|
133
|
+
filter: brightness(1.1);
|
|
134
|
+
}
|
|
135
|
+
|
|
102
136
|
/* Buttons inside tooltips inherit font */
|
|
103
137
|
.syntro-tooltip button {
|
|
104
138
|
font-family: inherit;
|
|
@@ -147,6 +181,104 @@ export function injectBaseStyles() {
|
|
|
147
181
|
transform: scale(1) translateY(0);
|
|
148
182
|
}
|
|
149
183
|
}
|
|
184
|
+
|
|
185
|
+
/* Modal scrim */
|
|
186
|
+
.syntro-modal-scrim {
|
|
187
|
+
position: fixed;
|
|
188
|
+
inset: 0;
|
|
189
|
+
background: rgba(0, 0, 0, 0.6);
|
|
190
|
+
backdrop-filter: blur(4px);
|
|
191
|
+
opacity: 0;
|
|
192
|
+
transition: opacity 200ms ease;
|
|
193
|
+
pointer-events: auto;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* Modal container */
|
|
197
|
+
.syntro-modal {
|
|
198
|
+
position: fixed;
|
|
199
|
+
top: 50%;
|
|
200
|
+
left: 50%;
|
|
201
|
+
transform: translate(-50%, -50%) scale(0.95);
|
|
202
|
+
background: var(--syntro-surface, #0f172a);
|
|
203
|
+
color: var(--syntro-fg, #fff);
|
|
204
|
+
border-radius: 16px;
|
|
205
|
+
padding: 24px;
|
|
206
|
+
box-shadow: 0 24px 48px rgba(0, 0, 0, 0.4);
|
|
207
|
+
pointer-events: auto;
|
|
208
|
+
opacity: 0;
|
|
209
|
+
transition: opacity 200ms ease, transform 200ms ease;
|
|
210
|
+
z-index: 2147483647;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.syntro-modal-sm { max-width: 320px; width: 90vw; }
|
|
214
|
+
.syntro-modal-md { max-width: 480px; width: 90vw; }
|
|
215
|
+
.syntro-modal-lg { max-width: 640px; width: 90vw; }
|
|
216
|
+
|
|
217
|
+
/* Modal content */
|
|
218
|
+
.syntro-modal-title {
|
|
219
|
+
font-size: 20px;
|
|
220
|
+
font-weight: 600;
|
|
221
|
+
margin-bottom: 12px;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.syntro-modal-body {
|
|
225
|
+
font-size: 15px;
|
|
226
|
+
line-height: 1.6;
|
|
227
|
+
opacity: 0.9;
|
|
228
|
+
margin-bottom: 20px;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.syntro-modal-close {
|
|
232
|
+
position: absolute;
|
|
233
|
+
top: 12px;
|
|
234
|
+
right: 12px;
|
|
235
|
+
background: transparent;
|
|
236
|
+
border: none;
|
|
237
|
+
color: inherit;
|
|
238
|
+
font-size: 24px;
|
|
239
|
+
line-height: 1;
|
|
240
|
+
cursor: pointer;
|
|
241
|
+
opacity: 0.6;
|
|
242
|
+
transition: opacity 150ms;
|
|
243
|
+
padding: 4px;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.syntro-modal-close:hover {
|
|
247
|
+
opacity: 1;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/* Modal buttons */
|
|
251
|
+
.syntro-modal-actions {
|
|
252
|
+
display: flex;
|
|
253
|
+
gap: 12px;
|
|
254
|
+
justify-content: flex-end;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.syntro-modal-btn {
|
|
258
|
+
padding: 10px 20px;
|
|
259
|
+
border-radius: 8px;
|
|
260
|
+
font-size: 14px;
|
|
261
|
+
font-weight: 500;
|
|
262
|
+
cursor: pointer;
|
|
263
|
+
transition: all 150ms;
|
|
264
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
265
|
+
background: transparent;
|
|
266
|
+
color: inherit;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.syntro-modal-btn:hover {
|
|
270
|
+
background: rgba(255, 255, 255, 0.1);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.syntro-modal-btn-primary {
|
|
274
|
+
background: var(--syntro-accent, #4f46e5);
|
|
275
|
+
border-color: transparent;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.syntro-modal-btn-primary:hover {
|
|
279
|
+
background: var(--syntro-accent, #4338ca);
|
|
280
|
+
filter: brightness(1.1);
|
|
281
|
+
}
|
|
150
282
|
`;
|
|
151
283
|
const style = document.createElement('style');
|
|
152
284
|
style.setAttribute('data-syntro', 'base-overlay');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"root.js","sourceRoot":"","sources":["../../../../src/overlays/runtime/overlay/root.ts"],"names":[],"mappings":"AAAA,IAAI,aAAa,GAAuB,IAAI,CAAC;AAE7C,MAAM,UAAU,iBAAiB;IAC/B,IAAI,aAAa,IAAI,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC;QAAE,OAAO,aAAa,CAAC;IACjF,IAAI,EAAE,GAAG,QAAQ,CAAC,cAAc,CAAC,iBAAiB,CAAuB,CAAC;IAC1E,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QACnC,EAAE,CAAC,EAAE,GAAG,iBAAiB,CAAC;QAC1B,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE;YACtB,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,GAAG;YACV,MAAM,EAAE,YAAY;YACpB,aAAa,EAAE,MAAM;SACtB,CAAC,CAAC;QACH,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IAChC,CAAC;IACD,aAAa,GAAG,EAAE,CAAC;IACnB,gBAAgB,EAAE,CAAC;IACnB,OAAO,aAAa,CAAC;AACvB,CAAC;AAED,IAAI,cAAc,GAAG,KAAK,CAAC;AAC3B,MAAM,UAAU,gBAAgB;IAC9B,IAAI,cAAc;QAAE,OAAO;IAC3B,MAAM,GAAG,GAAG
|
|
1
|
+
{"version":3,"file":"root.js","sourceRoot":"","sources":["../../../../src/overlays/runtime/overlay/root.ts"],"names":[],"mappings":"AAAA,IAAI,aAAa,GAAuB,IAAI,CAAC;AAE7C,MAAM,UAAU,iBAAiB;IAC/B,IAAI,aAAa,IAAI,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC;QAAE,OAAO,aAAa,CAAC;IACjF,IAAI,EAAE,GAAG,QAAQ,CAAC,cAAc,CAAC,iBAAiB,CAAuB,CAAC;IAC1E,IAAI,CAAC,EAAE,EAAE,CAAC;QACR,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QACnC,EAAE,CAAC,EAAE,GAAG,iBAAiB,CAAC;QAC1B,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE;YACtB,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,GAAG;YACV,MAAM,EAAE,YAAY;YACpB,aAAa,EAAE,MAAM;SACtB,CAAC,CAAC;QACH,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IAChC,CAAC;IACD,aAAa,GAAG,EAAE,CAAC;IACnB,gBAAgB,EAAE,CAAC;IACnB,OAAO,aAAa,CAAC;AACvB,CAAC;AAED,IAAI,cAAc,GAAG,KAAK,CAAC;AAC3B,MAAM,UAAU,gBAAgB;IAC9B,IAAI,cAAc;QAAE,OAAO;IAC3B,MAAM,GAAG,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiQb,CAAC;IACA,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAC9C,KAAK,CAAC,YAAY,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC;IAClD,KAAK,CAAC,WAAW,GAAG,GAAG,CAAC;IACxB,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IACjC,cAAc,GAAG,IAAI,CAAC;AACxB,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,sBAAsB,CAAC,MAAmB;IACxD,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC;IAChF,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AACzD,CAAC;AACD,MAAM,UAAU,kBAAkB;IAChC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC;AAClF,CAAC"}
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
import type { CanvasRecipe, OverlayContext } from '../../types';
|
|
2
2
|
export declare function runOverlays(recipe: CanvasRecipe, ctx: OverlayContext): Promise<() => void>;
|
|
3
|
+
/** Start a tour by ID - clears completion state and begins */
|
|
4
|
+
export declare function startTour(tourId: string): void;
|
|
@@ -1,11 +1,112 @@
|
|
|
1
1
|
import { showTooltip } from './tooltip';
|
|
2
2
|
import { showHighlight } from './highlight';
|
|
3
|
+
import { showModal } from './modal';
|
|
4
|
+
// Storage keys for cross-page tour state
|
|
5
|
+
const ACTIVE_TOUR_KEY = 'syntro_active_tour';
|
|
6
|
+
// Tour state expires after 5 minutes of inactivity
|
|
7
|
+
// This prevents stale tours from resuming after hard refresh hours/days later
|
|
8
|
+
const TOUR_STATE_EXPIRY_MS = 5 * 60 * 1000;
|
|
9
|
+
/** Get active tour state from localStorage (returns null if expired) */
|
|
10
|
+
function getActiveTourState() {
|
|
11
|
+
try {
|
|
12
|
+
const state = localStorage.getItem(ACTIVE_TOUR_KEY);
|
|
13
|
+
if (!state)
|
|
14
|
+
return null;
|
|
15
|
+
const parsed = JSON.parse(state);
|
|
16
|
+
// Check if state has expired
|
|
17
|
+
if (parsed.timestamp && Date.now() - parsed.timestamp > TOUR_STATE_EXPIRY_MS) {
|
|
18
|
+
console.log(`[SmartCanvas] Tour state expired (${Math.round((Date.now() - parsed.timestamp) / 1000)}s old), clearing`);
|
|
19
|
+
localStorage.removeItem(ACTIVE_TOUR_KEY);
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
return parsed;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/** Save active tour state to localStorage with timestamp */
|
|
29
|
+
function setActiveTourState(state) {
|
|
30
|
+
try {
|
|
31
|
+
if (state) {
|
|
32
|
+
const stateWithTimestamp = {
|
|
33
|
+
...state,
|
|
34
|
+
timestamp: Date.now()
|
|
35
|
+
};
|
|
36
|
+
localStorage.setItem(ACTIVE_TOUR_KEY, JSON.stringify(stateWithTimestamp));
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
localStorage.removeItem(ACTIVE_TOUR_KEY);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch { }
|
|
43
|
+
}
|
|
44
|
+
/** Check if a step's route matches the current page */
|
|
45
|
+
function stepRouteMatches(step, path) {
|
|
46
|
+
const stepRoute = step.route;
|
|
47
|
+
if (!stepRoute)
|
|
48
|
+
return true; // No route restriction = matches any page
|
|
49
|
+
// Exact match
|
|
50
|
+
if (stepRoute === path)
|
|
51
|
+
return true;
|
|
52
|
+
// Match with trailing path segments (e.g., /dashboard matches /dashboard/settings)
|
|
53
|
+
// But /dashboard should NOT match /dashboard-other or /login
|
|
54
|
+
if (path.startsWith(stepRoute + '/'))
|
|
55
|
+
return true;
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Watch for route changes in SPAs
|
|
60
|
+
* Handles: popstate (back/forward), pushState, replaceState
|
|
61
|
+
*/
|
|
62
|
+
function watchRouteChanges(callback) {
|
|
63
|
+
let lastPath = location.pathname;
|
|
64
|
+
const checkAndNotify = () => {
|
|
65
|
+
const currentPath = location.pathname;
|
|
66
|
+
if (currentPath !== lastPath) {
|
|
67
|
+
lastPath = currentPath;
|
|
68
|
+
callback(currentPath);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
// Handle back/forward button navigation
|
|
72
|
+
const onPopState = () => {
|
|
73
|
+
checkAndNotify();
|
|
74
|
+
};
|
|
75
|
+
window.addEventListener('popstate', onPopState);
|
|
76
|
+
// Patch pushState/replaceState to detect SPA soft navigation
|
|
77
|
+
// React Router, Next.js, etc. use these for client-side routing
|
|
78
|
+
const originalPushState = history.pushState.bind(history);
|
|
79
|
+
const originalReplaceState = history.replaceState.bind(history);
|
|
80
|
+
history.pushState = function (...args) {
|
|
81
|
+
originalPushState(...args);
|
|
82
|
+
checkAndNotify();
|
|
83
|
+
};
|
|
84
|
+
history.replaceState = function (...args) {
|
|
85
|
+
originalReplaceState(...args);
|
|
86
|
+
checkAndNotify();
|
|
87
|
+
};
|
|
88
|
+
return () => {
|
|
89
|
+
window.removeEventListener('popstate', onPopState);
|
|
90
|
+
history.pushState = originalPushState;
|
|
91
|
+
history.replaceState = originalReplaceState;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
3
94
|
export async function runOverlays(recipe, ctx) {
|
|
4
95
|
var _a;
|
|
5
96
|
if (recipe.routes && !routeMatches(recipe.routes, location.pathname))
|
|
6
97
|
return () => { };
|
|
98
|
+
// Sequential (tour) mode
|
|
99
|
+
if (recipe.mode === 'sequential') {
|
|
100
|
+
return runSequentialOverlays(recipe, ctx);
|
|
101
|
+
}
|
|
102
|
+
// Parallel mode (default) - show all steps at once
|
|
7
103
|
const cleanups = [];
|
|
8
104
|
for (const step of recipe.steps) {
|
|
105
|
+
// Modal steps don't need an anchor
|
|
106
|
+
if (step.kind === 'modal') {
|
|
107
|
+
cleanups.push(runModal(step, ctx));
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
9
110
|
const el = await ctx.resolve(step.anchor);
|
|
10
111
|
if (!el) {
|
|
11
112
|
(_a = ctx.onEvent) === null || _a === void 0 ? void 0 : _a.call(ctx, 'syntro_anchor_missing', { stepId: step.id, anchor: step.anchor });
|
|
@@ -27,10 +128,260 @@ export async function runOverlays(recipe, ctx) {
|
|
|
27
128
|
});
|
|
28
129
|
};
|
|
29
130
|
}
|
|
131
|
+
/** Run steps one at a time, advancing based on onAction mappings */
|
|
132
|
+
async function runSequentialOverlays(recipe, ctx) {
|
|
133
|
+
var _a;
|
|
134
|
+
const storageKey = `syntro_tour_${recipe.id}`;
|
|
135
|
+
// Check if user already completed/dismissed this tour
|
|
136
|
+
try {
|
|
137
|
+
if (localStorage.getItem(storageKey) === 'done') {
|
|
138
|
+
return () => { }; // Tour already completed, don't show again
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch { }
|
|
142
|
+
// Check for active cross-page tour state
|
|
143
|
+
const activeState = getActiveTourState();
|
|
144
|
+
const isResumingTour = (activeState === null || activeState === void 0 ? void 0 : activeState.tourId) === recipe.id;
|
|
145
|
+
// If autoStart is false and we're not resuming an active tour, don't auto-start
|
|
146
|
+
// Tours with autoStart: false should only start via explicit user action (e.g., clicking "Start Tour")
|
|
147
|
+
if (!isResumingTour && recipe.autoStart === false) {
|
|
148
|
+
return () => { };
|
|
149
|
+
}
|
|
150
|
+
const stepMap = new Map();
|
|
151
|
+
for (const step of recipe.steps) {
|
|
152
|
+
stepMap.set(step.id, step);
|
|
153
|
+
}
|
|
154
|
+
let currentCleanup = null;
|
|
155
|
+
let isDestroyed = false;
|
|
156
|
+
const runStep = async (stepId) => {
|
|
157
|
+
var _a, _b, _c, _d;
|
|
158
|
+
if (isDestroyed)
|
|
159
|
+
return;
|
|
160
|
+
const step = stepMap.get(stepId);
|
|
161
|
+
if (!step) {
|
|
162
|
+
(_a = ctx.onEvent) === null || _a === void 0 ? void 0 : _a.call(ctx, 'syntro_tour_end', { recipeId: recipe.id, reason: 'step_not_found', stepId });
|
|
163
|
+
setActiveTourState(null);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
// Check if this step should show on current page
|
|
167
|
+
const currentPath = location.pathname;
|
|
168
|
+
const stepRoute = step.route;
|
|
169
|
+
console.log(`[SmartCanvas] runStep: stepId="${stepId}", kind="${step.kind}", stepRoute="${stepRoute}", currentPath="${currentPath}"`);
|
|
170
|
+
if (!stepRouteMatches(step, currentPath)) {
|
|
171
|
+
// Step is for a different page - save state but DON'T auto-navigate
|
|
172
|
+
// Auto-navigation can cause redirect wars with multiple tours
|
|
173
|
+
// Instead, just pause and wait for user to return to the correct page
|
|
174
|
+
console.log(`[SmartCanvas] Route mismatch - step "${stepId}" is for "${stepRoute}", currently on "${currentPath}". Pausing tour.`);
|
|
175
|
+
setActiveTourState({ tourId: recipe.id, stepId });
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
console.log(`[SmartCanvas] Route matches, showing step "${stepId}"`);
|
|
179
|
+
// Save current step state for cross-page resume
|
|
180
|
+
setActiveTourState({ tourId: recipe.id, stepId });
|
|
181
|
+
// Clean up previous step
|
|
182
|
+
if (currentCleanup) {
|
|
183
|
+
currentCleanup();
|
|
184
|
+
currentCleanup = null;
|
|
185
|
+
}
|
|
186
|
+
const onStepAction = (actionId) => {
|
|
187
|
+
var _a, _b, _c, _d, _e;
|
|
188
|
+
const nextStepId = (_a = step.onAction) === null || _a === void 0 ? void 0 : _a[actionId];
|
|
189
|
+
(_b = ctx.onEvent) === null || _b === void 0 ? void 0 : _b.call(ctx, 'syntro_tour_action', { recipeId: recipe.id, stepId: step.id, actionId, nextStepId });
|
|
190
|
+
if (!nextStepId || nextStepId === 'end') {
|
|
191
|
+
// Tour ends - mark as done
|
|
192
|
+
try {
|
|
193
|
+
localStorage.setItem(storageKey, 'done');
|
|
194
|
+
}
|
|
195
|
+
catch { }
|
|
196
|
+
(_d = (_c = ctx.telemetry) === null || _c === void 0 ? void 0 : _c.setPersonPropertiesOnce) === null || _d === void 0 ? void 0 : _d.call(_c, { [storageKey]: 'done' });
|
|
197
|
+
setActiveTourState(null);
|
|
198
|
+
if (currentCleanup) {
|
|
199
|
+
currentCleanup();
|
|
200
|
+
currentCleanup = null;
|
|
201
|
+
}
|
|
202
|
+
(_e = ctx.onEvent) === null || _e === void 0 ? void 0 : _e.call(ctx, 'syntro_tour_end', { recipeId: recipe.id, reason: 'completed' });
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
// Advance to next step
|
|
206
|
+
const nextStep = stepMap.get(nextStepId);
|
|
207
|
+
const nextStepRoute = nextStep === null || nextStep === void 0 ? void 0 : nextStep.route;
|
|
208
|
+
const nowPath = location.pathname; // Get current path at action time
|
|
209
|
+
// Update tracked step ID
|
|
210
|
+
currentStepId = nextStepId;
|
|
211
|
+
// If next step is on a different page, navigate there (user-initiated)
|
|
212
|
+
if (nextStep && nextStepRoute && !stepRouteMatches(nextStep, nowPath)) {
|
|
213
|
+
console.log(`[SmartCanvas] User advancing to step "${nextStepId}" on route "${nextStepRoute}" - navigating...`);
|
|
214
|
+
setActiveTourState({ tourId: recipe.id, stepId: nextStepId });
|
|
215
|
+
if (currentCleanup) {
|
|
216
|
+
currentCleanup();
|
|
217
|
+
currentCleanup = null;
|
|
218
|
+
}
|
|
219
|
+
window.location.href = nextStepRoute;
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
// Same page - just run the step
|
|
223
|
+
runStep(nextStepId);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
if (step.kind === 'modal') {
|
|
228
|
+
currentCleanup = runModalWithCallback(step, ctx, onStepAction);
|
|
229
|
+
}
|
|
230
|
+
else if (step.kind === 'tooltip') {
|
|
231
|
+
const el = await ctx.resolve(step.anchor);
|
|
232
|
+
if (!el) {
|
|
233
|
+
(_b = ctx.onEvent) === null || _b === void 0 ? void 0 : _b.call(ctx, 'syntro_anchor_missing', { stepId: step.id, anchor: step.anchor });
|
|
234
|
+
// Don't end tour - element might appear later or on different page
|
|
235
|
+
console.log(`[SmartCanvas] Anchor not found for step "${stepId}", waiting...`);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
currentCleanup = runTooltipWithCallback(step, el, ctx, onStepAction);
|
|
239
|
+
}
|
|
240
|
+
else if (step.kind === 'highlight') {
|
|
241
|
+
const el = await ctx.resolve(step.anchor);
|
|
242
|
+
if (!el) {
|
|
243
|
+
(_c = ctx.onEvent) === null || _c === void 0 ? void 0 : _c.call(ctx, 'syntro_anchor_missing', { stepId: step.id, anchor: step.anchor });
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
currentCleanup = runHighlight(step, el, ctx);
|
|
247
|
+
}
|
|
248
|
+
(_d = ctx.onEvent) === null || _d === void 0 ? void 0 : _d.call(ctx, 'syntro_tour_step', { recipeId: recipe.id, stepId: step.id });
|
|
249
|
+
};
|
|
250
|
+
// Track current step for route change handling
|
|
251
|
+
let currentStepId;
|
|
252
|
+
// Determine starting step
|
|
253
|
+
if (isResumingTour && (activeState === null || activeState === void 0 ? void 0 : activeState.stepId)) {
|
|
254
|
+
// Resume from saved step
|
|
255
|
+
currentStepId = activeState.stepId;
|
|
256
|
+
console.log(`[SmartCanvas] Resuming tour "${recipe.id}" from step "${currentStepId}"`);
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
// Start fresh
|
|
260
|
+
currentStepId = recipe.startStep || ((_a = recipe.steps[0]) === null || _a === void 0 ? void 0 : _a.id);
|
|
261
|
+
}
|
|
262
|
+
// Watch for SPA route changes (back/forward, pushState, replaceState)
|
|
263
|
+
// When route changes, re-evaluate if current step should show
|
|
264
|
+
const unwatchRoutes = watchRouteChanges((newPath) => {
|
|
265
|
+
if (isDestroyed || !currentStepId)
|
|
266
|
+
return;
|
|
267
|
+
const step = stepMap.get(currentStepId);
|
|
268
|
+
if (!step)
|
|
269
|
+
return;
|
|
270
|
+
const stepRoute = step.route;
|
|
271
|
+
const wasShowingStep = currentCleanup !== null;
|
|
272
|
+
const shouldShowStep = stepRouteMatches(step, newPath);
|
|
273
|
+
console.log(`[SmartCanvas] Route changed to "${newPath}" - step "${currentStepId}" route="${stepRoute}", wasShowing=${wasShowingStep}, shouldShow=${shouldShowStep}`);
|
|
274
|
+
if (wasShowingStep && !shouldShowStep) {
|
|
275
|
+
// User navigated away - hide the step
|
|
276
|
+
console.log(`[SmartCanvas] Hiding step "${currentStepId}" - user navigated away`);
|
|
277
|
+
if (currentCleanup) {
|
|
278
|
+
currentCleanup();
|
|
279
|
+
currentCleanup = null;
|
|
280
|
+
}
|
|
281
|
+
// Check if any next step from onAction matches the new route
|
|
282
|
+
// This handles cases where user clicks the actual app button instead of tour button
|
|
283
|
+
const currentStep = stepMap.get(currentStepId);
|
|
284
|
+
if (currentStep) {
|
|
285
|
+
const onActionMap = currentStep.onAction || {};
|
|
286
|
+
// Find a next step that matches the new route
|
|
287
|
+
for (const nextStepId of Object.values(onActionMap)) {
|
|
288
|
+
if (nextStepId === 'end' || typeof nextStepId !== 'string')
|
|
289
|
+
continue;
|
|
290
|
+
const nextStep = stepMap.get(nextStepId);
|
|
291
|
+
if (nextStep && stepRouteMatches(nextStep, newPath)) {
|
|
292
|
+
console.log(`[SmartCanvas] Auto-advancing to step "${nextStepId}" - matches new route "${newPath}"`);
|
|
293
|
+
currentStepId = nextStepId;
|
|
294
|
+
setActiveTourState({ tourId: recipe.id, stepId: nextStepId });
|
|
295
|
+
runStep(nextStepId);
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// No matching next step - just pause (save state for later resume)
|
|
300
|
+
console.log(`[SmartCanvas] No next step matches new route "${newPath}", pausing tour`);
|
|
301
|
+
setActiveTourState({ tourId: recipe.id, stepId: currentStepId });
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
else if (!wasShowingStep && shouldShowStep) {
|
|
305
|
+
// User navigated back - show the step (resume tour)
|
|
306
|
+
console.log(`[SmartCanvas] Resuming step "${currentStepId}" - user returned to correct route`);
|
|
307
|
+
runStep(currentStepId);
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
if (currentStepId) {
|
|
311
|
+
await runStep(currentStepId);
|
|
312
|
+
}
|
|
313
|
+
return () => {
|
|
314
|
+
isDestroyed = true;
|
|
315
|
+
unwatchRoutes();
|
|
316
|
+
if (currentCleanup) {
|
|
317
|
+
currentCleanup();
|
|
318
|
+
currentCleanup = null;
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
/** Start a tour by ID - clears completion state and begins */
|
|
323
|
+
export function startTour(tourId) {
|
|
324
|
+
const storageKey = `syntro_tour_${tourId}`;
|
|
325
|
+
try {
|
|
326
|
+
localStorage.removeItem(storageKey);
|
|
327
|
+
setActiveTourState({ tourId, stepId: '' }); // Will use startStep
|
|
328
|
+
window.location.reload();
|
|
329
|
+
}
|
|
330
|
+
catch { }
|
|
331
|
+
}
|
|
30
332
|
function routeMatches(routes, path) {
|
|
31
333
|
// simple contains/prefix; replace with real matcher as needed
|
|
32
334
|
return routes.some(r => r === path || path.startsWith(r));
|
|
33
335
|
}
|
|
336
|
+
function runModal(step, ctx) {
|
|
337
|
+
var _a, _b, _c, _d, _e, _f;
|
|
338
|
+
const html = modalHtml(step);
|
|
339
|
+
const handle = showModal(ctx.overlayRoot, {
|
|
340
|
+
html,
|
|
341
|
+
size: (_a = step.size) !== null && _a !== void 0 ? _a : 'md',
|
|
342
|
+
blocking: (_b = step.blocking) !== null && _b !== void 0 ? _b : true,
|
|
343
|
+
scrimOpacity: (_d = (_c = step.scrim) === null || _c === void 0 ? void 0 : _c.opacity) !== null && _d !== void 0 ? _d : 0.6,
|
|
344
|
+
onAction: (actionId) => {
|
|
345
|
+
var _a;
|
|
346
|
+
(_a = ctx.onEvent) === null || _a === void 0 ? void 0 : _a.call(ctx, 'syntro_overlay_action', { stepId: step.id, actionId });
|
|
347
|
+
handle.destroy();
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
(_e = ctx.onEvent) === null || _e === void 0 ? void 0 : _e.call(ctx, 'syntro_overlay_exposed', { kind: 'modal', stepId: step.id, recipeId: ctx.recipeId });
|
|
351
|
+
const timers = [];
|
|
352
|
+
if ((_f = step.dismiss) === null || _f === void 0 ? void 0 : _f.timeoutMs) {
|
|
353
|
+
const id = window.setTimeout(() => {
|
|
354
|
+
var _a;
|
|
355
|
+
handle.destroy();
|
|
356
|
+
(_a = ctx.onEvent) === null || _a === void 0 ? void 0 : _a.call(ctx, 'syntro_overlay_dismissed', { stepId: step.id, reason: 'timeout' });
|
|
357
|
+
}, step.dismiss.timeoutMs);
|
|
358
|
+
timers.push(id);
|
|
359
|
+
}
|
|
360
|
+
const closeBtn = handle.el.querySelector('[data-syntro-role="close"]');
|
|
361
|
+
const onClose = () => {
|
|
362
|
+
var _a;
|
|
363
|
+
handle.destroy();
|
|
364
|
+
(_a = ctx.onEvent) === null || _a === void 0 ? void 0 : _a.call(ctx, 'syntro_overlay_dismissed', { stepId: step.id, reason: 'close' });
|
|
365
|
+
};
|
|
366
|
+
closeBtn === null || closeBtn === void 0 ? void 0 : closeBtn.addEventListener('click', onClose);
|
|
367
|
+
return () => {
|
|
368
|
+
timers.forEach((id) => clearTimeout(id));
|
|
369
|
+
closeBtn === null || closeBtn === void 0 ? void 0 : closeBtn.removeEventListener('click', onClose);
|
|
370
|
+
handle.destroy();
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
function modalHtml(step) {
|
|
374
|
+
var _a, _b, _c;
|
|
375
|
+
const title = step.content.title ? `<div class="syntro-modal-title">${escapeHtml(step.content.title)}</div>` : '';
|
|
376
|
+
const body = `<div class="syntro-modal-body">${escapeHtml(step.content.body)}</div>`;
|
|
377
|
+
const close = ((_b = (_a = step.dismiss) === null || _a === void 0 ? void 0 : _a.closeButton) !== null && _b !== void 0 ? _b : true) ? `<button data-syntro-role="close" class="syntro-modal-close" aria-label="Close">×</button>` : '';
|
|
378
|
+
let actions = '';
|
|
379
|
+
if ((_c = step.ctaButtons) === null || _c === void 0 ? void 0 : _c.length) {
|
|
380
|
+
const btns = step.ctaButtons.map(btn => `<button class="syntro-modal-btn${btn.primary ? ' syntro-modal-btn-primary' : ''}" data-syntro-action="${escapeHtml(btn.actionId)}">${escapeHtml(btn.label)}</button>`).join('');
|
|
381
|
+
actions = `<div class="syntro-modal-actions">${btns}</div>`;
|
|
382
|
+
}
|
|
383
|
+
return `${close}${title}${body}${actions}`;
|
|
384
|
+
}
|
|
34
385
|
function runTooltip(step, anchorEl, ctx) {
|
|
35
386
|
var _a, _b, _c, _d;
|
|
36
387
|
const html = tooltipHtml(step);
|
|
@@ -89,13 +440,101 @@ function runHighlight(step, anchorEl, ctx) {
|
|
|
89
440
|
};
|
|
90
441
|
}
|
|
91
442
|
function tooltipHtml(step) {
|
|
92
|
-
var _a, _b;
|
|
443
|
+
var _a, _b, _c;
|
|
93
444
|
const title = step.content.title ? `<div class="syntro-tt-title">${escapeHtml(step.content.title)}</div>` : '';
|
|
94
445
|
const body = `<div class="syntro-tt-body">${escapeHtml(step.content.body)}</div>`;
|
|
95
446
|
const close = ((_b = (_a = step.dismiss) === null || _a === void 0 ? void 0 : _a.closeButton) !== null && _b !== void 0 ? _b : true) ? `<button data-syntro-role="close" class="syntro-tt-close" aria-label="Close">×</button>` : '';
|
|
96
|
-
|
|
447
|
+
let actions = '';
|
|
448
|
+
if ((_c = step.ctaButtons) === null || _c === void 0 ? void 0 : _c.length) {
|
|
449
|
+
const btns = step.ctaButtons.map(btn => `<button class="syntro-tt-btn${btn.primary ? ' syntro-tt-btn-primary' : ''}" data-syntro-action="${escapeHtml(btn.actionId)}">${escapeHtml(btn.label)}</button>`).join('');
|
|
450
|
+
actions = `<div class="syntro-tt-actions">${btns}</div>`;
|
|
451
|
+
}
|
|
452
|
+
return `<div class="syntro-tt">${close}${title}${body}${actions}</div>`;
|
|
97
453
|
}
|
|
98
454
|
function escapeHtml(s) {
|
|
99
455
|
return s.replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]));
|
|
100
456
|
}
|
|
457
|
+
/** Modal runner for sequential tours with external action callback */
|
|
458
|
+
function runModalWithCallback(step, ctx, onAction) {
|
|
459
|
+
var _a, _b, _c, _d, _e, _f;
|
|
460
|
+
const html = modalHtml(step);
|
|
461
|
+
const handle = showModal(ctx.overlayRoot, {
|
|
462
|
+
html,
|
|
463
|
+
size: (_a = step.size) !== null && _a !== void 0 ? _a : 'md',
|
|
464
|
+
blocking: (_b = step.blocking) !== null && _b !== void 0 ? _b : true,
|
|
465
|
+
scrimOpacity: (_d = (_c = step.scrim) === null || _c === void 0 ? void 0 : _c.opacity) !== null && _d !== void 0 ? _d : 0.6,
|
|
466
|
+
onAction: (actionId) => {
|
|
467
|
+
var _a;
|
|
468
|
+
(_a = ctx.onEvent) === null || _a === void 0 ? void 0 : _a.call(ctx, 'syntro_overlay_action', { stepId: step.id, actionId });
|
|
469
|
+
handle.destroy();
|
|
470
|
+
onAction(actionId);
|
|
471
|
+
}
|
|
472
|
+
});
|
|
473
|
+
(_e = ctx.onEvent) === null || _e === void 0 ? void 0 : _e.call(ctx, 'syntro_overlay_exposed', { kind: 'modal', stepId: step.id, recipeId: ctx.recipeId });
|
|
474
|
+
const timers = [];
|
|
475
|
+
if ((_f = step.dismiss) === null || _f === void 0 ? void 0 : _f.timeoutMs) {
|
|
476
|
+
const id = window.setTimeout(() => {
|
|
477
|
+
var _a;
|
|
478
|
+
handle.destroy();
|
|
479
|
+
(_a = ctx.onEvent) === null || _a === void 0 ? void 0 : _a.call(ctx, 'syntro_overlay_dismissed', { stepId: step.id, reason: 'timeout' });
|
|
480
|
+
onAction('dismiss');
|
|
481
|
+
}, step.dismiss.timeoutMs);
|
|
482
|
+
timers.push(id);
|
|
483
|
+
}
|
|
484
|
+
const closeBtn = handle.el.querySelector('[data-syntro-role="close"]');
|
|
485
|
+
const onClose = () => {
|
|
486
|
+
var _a;
|
|
487
|
+
handle.destroy();
|
|
488
|
+
(_a = ctx.onEvent) === null || _a === void 0 ? void 0 : _a.call(ctx, 'syntro_overlay_dismissed', { stepId: step.id, reason: 'close' });
|
|
489
|
+
onAction('dismiss');
|
|
490
|
+
};
|
|
491
|
+
closeBtn === null || closeBtn === void 0 ? void 0 : closeBtn.addEventListener('click', onClose);
|
|
492
|
+
return () => {
|
|
493
|
+
timers.forEach((id) => clearTimeout(id));
|
|
494
|
+
closeBtn === null || closeBtn === void 0 ? void 0 : closeBtn.removeEventListener('click', onClose);
|
|
495
|
+
handle.destroy();
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
/** Tooltip runner for sequential tours with CTA buttons and external action callback */
|
|
499
|
+
function runTooltipWithCallback(step, anchorEl, ctx, onAction) {
|
|
500
|
+
var _a, _b, _c, _d;
|
|
501
|
+
const html = tooltipHtml(step);
|
|
502
|
+
const handle = showTooltip(anchorEl, ctx.overlayRoot, {
|
|
503
|
+
html,
|
|
504
|
+
placement: (_a = step.placement) !== null && _a !== void 0 ? _a : 'top',
|
|
505
|
+
offsetPx: (_b = step.offsetPx) !== null && _b !== void 0 ? _b : 8,
|
|
506
|
+
blocking: !!step.blocking,
|
|
507
|
+
trigger: step.trigger,
|
|
508
|
+
onAction: (actionId) => {
|
|
509
|
+
var _a;
|
|
510
|
+
(_a = ctx.onEvent) === null || _a === void 0 ? void 0 : _a.call(ctx, 'syntro_overlay_action', { stepId: step.id, actionId });
|
|
511
|
+
handle.destroy();
|
|
512
|
+
onAction(actionId);
|
|
513
|
+
}
|
|
514
|
+
});
|
|
515
|
+
(_c = ctx.onEvent) === null || _c === void 0 ? void 0 : _c.call(ctx, 'syntro_overlay_exposed', { kind: 'tooltip', stepId: step.id, recipeId: ctx.recipeId });
|
|
516
|
+
const timers = [];
|
|
517
|
+
if ((_d = step.dismiss) === null || _d === void 0 ? void 0 : _d.timeoutMs) {
|
|
518
|
+
const id = window.setTimeout(() => {
|
|
519
|
+
var _a;
|
|
520
|
+
handle.destroy();
|
|
521
|
+
(_a = ctx.onEvent) === null || _a === void 0 ? void 0 : _a.call(ctx, 'syntro_overlay_dismissed', { stepId: step.id, reason: 'timeout' });
|
|
522
|
+
onAction('dismiss');
|
|
523
|
+
}, step.dismiss.timeoutMs);
|
|
524
|
+
timers.push(id);
|
|
525
|
+
}
|
|
526
|
+
const closeBtn = handle.el.querySelector('[data-syntro-role="close"]');
|
|
527
|
+
const onClose = () => {
|
|
528
|
+
var _a;
|
|
529
|
+
handle.destroy();
|
|
530
|
+
(_a = ctx.onEvent) === null || _a === void 0 ? void 0 : _a.call(ctx, 'syntro_overlay_dismissed', { stepId: step.id, reason: 'close' });
|
|
531
|
+
onAction('dismiss');
|
|
532
|
+
};
|
|
533
|
+
closeBtn === null || closeBtn === void 0 ? void 0 : closeBtn.addEventListener('click', onClose);
|
|
534
|
+
return () => {
|
|
535
|
+
timers.forEach((id) => clearTimeout(id));
|
|
536
|
+
closeBtn === null || closeBtn === void 0 ? void 0 : closeBtn.removeEventListener('click', onClose);
|
|
537
|
+
handle.destroy();
|
|
538
|
+
};
|
|
539
|
+
}
|
|
101
540
|
//# sourceMappingURL=runner.js.map
|