@cognior/iap-sdk 0.1.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.

Potentially problematic release.


This version of @cognior/iap-sdk might be problematic. Click here for more details.

Files changed (60) hide show
  1. package/.github/copilot-instructions.md +95 -0
  2. package/README.md +79 -0
  3. package/TRACKING.md +105 -0
  4. package/USER_CONTEXT_README.md +284 -0
  5. package/package.json +154 -0
  6. package/src/config.ts +25 -0
  7. package/src/core/flowEngine.ts +1833 -0
  8. package/src/core/triggerManager.ts +1011 -0
  9. package/src/experiences/banner.ts +366 -0
  10. package/src/experiences/beacon.ts +668 -0
  11. package/src/experiences/hotspotTour.ts +654 -0
  12. package/src/experiences/hotspots.ts +566 -0
  13. package/src/experiences/modal.ts +1337 -0
  14. package/src/experiences/modalSequence.ts +1247 -0
  15. package/src/experiences/popover.ts +652 -0
  16. package/src/experiences/registry.ts +21 -0
  17. package/src/experiences/survey.ts +1639 -0
  18. package/src/experiences/taskList.ts +625 -0
  19. package/src/experiences/tooltip.ts +740 -0
  20. package/src/experiences/types.ts +395 -0
  21. package/src/experiences/walkthrough.ts +670 -0
  22. package/src/flow-sequence.ts +177 -0
  23. package/src/flows.ts +512 -0
  24. package/src/http.ts +61 -0
  25. package/src/index.ts +355 -0
  26. package/src/services/flowManager.ts +905 -0
  27. package/src/services/flowNormalizer.ts +74 -0
  28. package/src/services/locationContextService.ts +189 -0
  29. package/src/services/pageContextService.ts +221 -0
  30. package/src/services/userContextService.ts +286 -0
  31. package/src/state/appState.ts +0 -0
  32. package/src/state/hooks.ts +0 -0
  33. package/src/state/index.ts +0 -0
  34. package/src/state/migration.ts +0 -0
  35. package/src/state/store.ts +0 -0
  36. package/src/styles/banner.css.ts +0 -0
  37. package/src/styles/hotspot.css.ts +0 -0
  38. package/src/styles/hotspotTour.css.ts +0 -0
  39. package/src/styles/modal.css.ts +564 -0
  40. package/src/styles/survey.css.ts +1013 -0
  41. package/src/styles/taskList.css.ts +0 -0
  42. package/src/styles/tooltip.css.ts +149 -0
  43. package/src/styles/walkthrough.css.ts +0 -0
  44. package/src/tourUtils.ts +0 -0
  45. package/src/tracking.ts +223 -0
  46. package/src/utils/debounce.ts +66 -0
  47. package/src/utils/eventSequenceValidator.ts +124 -0
  48. package/src/utils/flowTrackingSystem.ts +524 -0
  49. package/src/utils/idGenerator.ts +155 -0
  50. package/src/utils/immediateValidationPrevention.ts +184 -0
  51. package/src/utils/normalize.ts +50 -0
  52. package/src/utils/privacyManager.ts +166 -0
  53. package/src/utils/ruleEvaluator.ts +199 -0
  54. package/src/utils/sanitize.ts +79 -0
  55. package/src/utils/selectors.ts +107 -0
  56. package/src/utils/stepExecutor.ts +345 -0
  57. package/src/utils/triggerNormalizer.ts +149 -0
  58. package/src/utils/validationInterceptor.ts +650 -0
  59. package/tsconfig.json +13 -0
  60. package/tsup.config.ts +13 -0
@@ -0,0 +1,670 @@
1
+ // src/experiences/walkthrough.ts
2
+ // Guided walkthrough experience renderer with step-by-step navigation
3
+
4
+ import { sanitizeHtml } from "../utils/sanitize";
5
+ import { waitForElement, resolveSelector } from "../utils/selectors";
6
+ import { register } from "./registry";
7
+ import type { WalkthroughPayload, WalkthroughStep } from "./types";
8
+
9
+ type WalkthroughFlow = { id: string; type: "walkthrough"; payload: WalkthroughPayload };
10
+
11
+ const walkthroughCssText = `
12
+ :root {
13
+ --dap-z-walkthrough: 2147483630;
14
+ --dap-walkthrough-overlay: rgba(15, 23, 42, 0.75);
15
+ --dap-walkthrough-bg: #ffffff;
16
+ --dap-walkthrough-border: #e2e8f0;
17
+ --dap-walkthrough-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
18
+ --dap-walkthrough-text: #1e293b;
19
+ --dap-walkthrough-text-muted: #64748b;
20
+ --dap-walkthrough-primary: #3b82f6;
21
+ --dap-walkthrough-success: #10b981;
22
+ --dap-walkthrough-highlight: #fef3c7;
23
+ }
24
+
25
+ .dap-walkthrough-overlay {
26
+ position: fixed;
27
+ inset: 0;
28
+ background: var(--dap-walkthrough-overlay);
29
+ z-index: var(--dap-z-walkthrough);
30
+ pointer-events: none;
31
+ opacity: 0;
32
+ transition: opacity 0.3s ease;
33
+ }
34
+
35
+ .dap-walkthrough-overlay.active {
36
+ opacity: 1;
37
+ pointer-events: auto;
38
+ }
39
+
40
+ .dap-walkthrough-spotlight {
41
+ position: absolute;
42
+ border: 2px solid var(--dap-walkthrough-primary);
43
+ border-radius: 8px;
44
+ box-shadow: 0 0 0 9999px var(--dap-walkthrough-overlay);
45
+ pointer-events: none;
46
+ transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
47
+ z-index: calc(var(--dap-z-walkthrough) + 1);
48
+ }
49
+
50
+ .dap-walkthrough-tooltip {
51
+ position: absolute;
52
+ background: var(--dap-walkthrough-bg);
53
+ border: 1px solid var(--dap-walkthrough-border);
54
+ border-radius: 12px;
55
+ box-shadow: var(--dap-walkthrough-shadow);
56
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
57
+ width: 320px;
58
+ max-width: 90vw;
59
+ z-index: calc(var(--dap-z-walkthrough) + 2);
60
+ opacity: 0;
61
+ transform: scale(0.95);
62
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
63
+ pointer-events: auto;
64
+ }
65
+
66
+ .dap-walkthrough-tooltip.visible {
67
+ opacity: 1;
68
+ transform: scale(1);
69
+ }
70
+
71
+ .dap-walkthrough-tooltip::before {
72
+ content: '';
73
+ position: absolute;
74
+ width: 12px;
75
+ height: 12px;
76
+ background: var(--dap-walkthrough-bg);
77
+ border: 1px solid var(--dap-walkthrough-border);
78
+ border-bottom: none;
79
+ border-right: none;
80
+ transform: rotate(45deg);
81
+ }
82
+
83
+ .dap-walkthrough-tooltip.position-top {
84
+ transform-origin: bottom center;
85
+ }
86
+
87
+ .dap-walkthrough-tooltip.position-top::before {
88
+ bottom: -7px;
89
+ left: 50%;
90
+ margin-left: -6px;
91
+ border-top: none;
92
+ border-left: none;
93
+ border-bottom: 1px solid var(--dap-walkthrough-border);
94
+ border-right: 1px solid var(--dap-walkthrough-border);
95
+ transform: rotate(-135deg);
96
+ }
97
+
98
+ .dap-walkthrough-tooltip.position-bottom {
99
+ transform-origin: top center;
100
+ }
101
+
102
+ .dap-walkthrough-tooltip.position-bottom::before {
103
+ top: -7px;
104
+ left: 50%;
105
+ margin-left: -6px;
106
+ }
107
+
108
+ .dap-walkthrough-tooltip.position-left {
109
+ transform-origin: right center;
110
+ }
111
+
112
+ .dap-walkthrough-tooltip.position-left::before {
113
+ right: -7px;
114
+ top: 50%;
115
+ margin-top: -6px;
116
+ border-top: none;
117
+ border-left: none;
118
+ border-bottom: 1px solid var(--dap-walkthrough-border);
119
+ border-right: 1px solid var(--dap-walkthrough-border);
120
+ transform: rotate(-45deg);
121
+ }
122
+
123
+ .dap-walkthrough-tooltip.position-right {
124
+ transform-origin: left center;
125
+ }
126
+
127
+ .dap-walkthrough-tooltip.position-right::before {
128
+ left: -7px;
129
+ top: 50%;
130
+ margin-top: -6px;
131
+ border-bottom: none;
132
+ border-right: none;
133
+ border-top: 1px solid var(--dap-walkthrough-border);
134
+ border-left: 1px solid var(--dap-walkthrough-border);
135
+ transform: rotate(135deg);
136
+ }
137
+
138
+ .dap-walkthrough-header {
139
+ padding: 20px 20px 16px;
140
+ border-bottom: 1px solid var(--dap-walkthrough-border);
141
+ position: relative;
142
+ }
143
+
144
+ .dap-walkthrough-step-indicator {
145
+ font-size: 12px;
146
+ font-weight: 600;
147
+ color: var(--dap-walkthrough-primary);
148
+ text-transform: uppercase;
149
+ letter-spacing: 0.5px;
150
+ margin-bottom: 8px;
151
+ }
152
+
153
+ .dap-walkthrough-title {
154
+ font-size: 18px;
155
+ font-weight: 600;
156
+ color: var(--dap-walkthrough-text);
157
+ margin: 0;
158
+ line-height: 1.3;
159
+ }
160
+
161
+ .dap-walkthrough-close {
162
+ position: absolute;
163
+ top: 16px;
164
+ right: 16px;
165
+ background: none;
166
+ border: none;
167
+ font-size: 18px;
168
+ color: var(--dap-walkthrough-text-muted);
169
+ cursor: pointer;
170
+ width: 24px;
171
+ height: 24px;
172
+ display: flex;
173
+ align-items: center;
174
+ justify-content: center;
175
+ border-radius: 4px;
176
+ transition: background-color 0.15s ease;
177
+ }
178
+
179
+ .dap-walkthrough-close:hover {
180
+ background: var(--dap-walkthrough-border);
181
+ }
182
+
183
+ .dap-walkthrough-body {
184
+ padding: 16px 20px;
185
+ }
186
+
187
+ .dap-walkthrough-content {
188
+ font-size: 14px;
189
+ color: var(--dap-walkthrough-text);
190
+ line-height: 1.6;
191
+ margin: 0;
192
+ }
193
+
194
+ .dap-walkthrough-footer {
195
+ padding: 16px 20px;
196
+ border-top: 1px solid var(--dap-walkthrough-border);
197
+ display: flex;
198
+ justify-content: space-between;
199
+ align-items: center;
200
+ }
201
+
202
+ .dap-walkthrough-progress {
203
+ display: flex;
204
+ gap: 6px;
205
+ align-items: center;
206
+ }
207
+
208
+ .dap-walkthrough-progress-dot {
209
+ width: 8px;
210
+ height: 8px;
211
+ border-radius: 50%;
212
+ background: var(--dap-walkthrough-border);
213
+ transition: all 0.2s ease;
214
+ }
215
+
216
+ .dap-walkthrough-progress-dot.active {
217
+ background: var(--dap-walkthrough-primary);
218
+ transform: scale(1.25);
219
+ }
220
+
221
+ .dap-walkthrough-progress-dot.completed {
222
+ background: var(--dap-walkthrough-success);
223
+ }
224
+
225
+ .dap-walkthrough-nav {
226
+ display: flex;
227
+ gap: 8px;
228
+ align-items: center;
229
+ }
230
+
231
+ .dap-walkthrough-btn {
232
+ padding: 8px 16px;
233
+ border: 1px solid var(--dap-walkthrough-border);
234
+ border-radius: 6px;
235
+ background: transparent;
236
+ color: var(--dap-walkthrough-text);
237
+ font-size: 14px;
238
+ font-weight: 500;
239
+ cursor: pointer;
240
+ transition: all 0.15s ease;
241
+ }
242
+
243
+ .dap-walkthrough-btn:hover {
244
+ background: var(--dap-walkthrough-border);
245
+ }
246
+
247
+ .dap-walkthrough-btn:disabled {
248
+ opacity: 0.5;
249
+ cursor: not-allowed;
250
+ }
251
+
252
+ .dap-walkthrough-btn.primary {
253
+ background: var(--dap-walkthrough-primary);
254
+ border-color: var(--dap-walkthrough-primary);
255
+ color: white;
256
+ }
257
+
258
+ .dap-walkthrough-btn.primary:hover:not(:disabled) {
259
+ background: #2563eb;
260
+ border-color: #2563eb;
261
+ }
262
+
263
+ .dap-walkthrough-btn.success {
264
+ background: var(--dap-walkthrough-success);
265
+ border-color: var(--dap-walkthrough-success);
266
+ color: white;
267
+ }
268
+
269
+ .dap-walkthrough-btn.success:hover:not(:disabled) {
270
+ background: #059669;
271
+ border-color: #059669;
272
+ }
273
+
274
+ .dap-walkthrough-step-count {
275
+ font-size: 12px;
276
+ color: var(--dap-walkthrough-text-muted);
277
+ margin-right: 12px;
278
+ }
279
+
280
+ .dap-walkthrough-highlight {
281
+ position: relative;
282
+ z-index: calc(var(--dap-z-walkthrough) + 1);
283
+ }
284
+
285
+ .dap-walkthrough-highlight::after {
286
+ content: '';
287
+ position: absolute;
288
+ inset: -4px;
289
+ background: var(--dap-walkthrough-highlight);
290
+ border-radius: 6px;
291
+ opacity: 0.6;
292
+ z-index: -1;
293
+ animation: walkthroughPulse 2s infinite;
294
+ }
295
+
296
+ @keyframes walkthroughPulse {
297
+ 0%, 100% { opacity: 0.6; transform: scale(1); }
298
+ 50% { opacity: 0.8; transform: scale(1.02); }
299
+ }
300
+
301
+ @media (max-width: 640px) {
302
+ .dap-walkthrough-tooltip {
303
+ width: 280px;
304
+ max-width: calc(100vw - 32px);
305
+ }
306
+
307
+ .dap-walkthrough-header {
308
+ padding: 16px;
309
+ }
310
+
311
+ .dap-walkthrough-body {
312
+ padding: 12px 16px;
313
+ }
314
+
315
+ .dap-walkthrough-footer {
316
+ padding: 12px 16px;
317
+ flex-direction: column;
318
+ gap: 12px;
319
+ align-items: stretch;
320
+ }
321
+
322
+ .dap-walkthrough-nav {
323
+ justify-content: space-between;
324
+ width: 100%;
325
+ }
326
+
327
+ .dap-walkthrough-step-count {
328
+ margin-right: 0;
329
+ text-align: center;
330
+ }
331
+ }
332
+ `;
333
+
334
+ export function registerWalkthrough() {
335
+ register("walkthrough", renderWalkthrough);
336
+ }
337
+
338
+ export async function renderWalkthrough(flow: WalkthroughFlow): Promise<void> {
339
+ const { payload, id } = flow;
340
+ const completionTracker = payload._completionTracker;
341
+
342
+ // Ensure CSS is injected
343
+ ensureStyles();
344
+
345
+ let currentStepIndex = 0;
346
+ let isActive = false;
347
+
348
+ // Create overlay and spotlight
349
+ const overlay = createWalkthroughOverlay();
350
+ const spotlight = createSpotlight();
351
+ const tooltip = createTooltip();
352
+
353
+ document.documentElement.appendChild(overlay);
354
+ document.documentElement.appendChild(spotlight);
355
+ document.documentElement.appendChild(tooltip);
356
+
357
+ // Start walkthrough
358
+ await showStep(currentStepIndex);
359
+
360
+ async function showStep(stepIndex: number): Promise<void> {
361
+ if (stepIndex < 0 || stepIndex >= payload.steps.length) return;
362
+
363
+ const step = payload.steps[stepIndex];
364
+ currentStepIndex = stepIndex;
365
+
366
+ // Highlight target element
367
+ let targetElement: Element | null = null;
368
+
369
+ if (step.target) {
370
+ try {
371
+ targetElement = await waitForElement(step.target, { timeout: step.waitTimeout || 5000 });
372
+ } catch (error) {
373
+ console.warn(`[DAP] Walkthrough target not found: ${step.target}`, error);
374
+ }
375
+ }
376
+
377
+ // Position spotlight
378
+ if (targetElement) {
379
+ positionSpotlight(targetElement);
380
+
381
+ // Add highlight class
382
+ targetElement.classList.add('dap-walkthrough-highlight');
383
+
384
+ // Scroll into view
385
+ if (step.autoScroll !== false) {
386
+ targetElement.scrollIntoView({
387
+ behavior: 'smooth',
388
+ block: 'center',
389
+ inline: 'center'
390
+ });
391
+ }
392
+ } else {
393
+ hideSpotlight();
394
+ }
395
+
396
+ // Update tooltip content
397
+ updateTooltipContent(step, stepIndex);
398
+
399
+ // Position tooltip
400
+ if (targetElement) {
401
+ positionTooltip(tooltip, targetElement, step.position || 'bottom');
402
+ } else {
403
+ positionTooltipCenter(tooltip);
404
+ }
405
+
406
+ // Show elements
407
+ overlay.classList.add('active');
408
+ spotlight.style.opacity = targetElement ? '1' : '0';
409
+ tooltip.classList.add('visible');
410
+ isActive = true;
411
+
412
+ // Auto-advance
413
+ if (step.autoAdvance && step.autoAdvanceDelay) {
414
+ setTimeout(() => {
415
+ if (isActive && currentStepIndex === stepIndex) {
416
+ nextStep();
417
+ }
418
+ }, step.autoAdvanceDelay);
419
+ }
420
+
421
+ // Clean up previous highlights
422
+ document.querySelectorAll('.dap-walkthrough-highlight').forEach(el => {
423
+ if (el !== targetElement) {
424
+ el.classList.remove('dap-walkthrough-highlight');
425
+ }
426
+ });
427
+ }
428
+
429
+ function positionSpotlight(element: Element) {
430
+ const rect = element.getBoundingClientRect();
431
+ const padding = 8;
432
+
433
+ spotlight.style.left = `${rect.left - padding}px`;
434
+ spotlight.style.top = `${rect.top - padding}px`;
435
+ spotlight.style.width = `${rect.width + padding * 2}px`;
436
+ spotlight.style.height = `${rect.height + padding * 2}px`;
437
+ }
438
+
439
+ function hideSpotlight() {
440
+ spotlight.style.opacity = '0';
441
+ }
442
+
443
+ function updateTooltipContent(step: WalkthroughStep, stepIndex: number) {
444
+ // Header
445
+ const stepIndicator = tooltip.querySelector('.dap-walkthrough-step-indicator') as HTMLElement;
446
+ const title = tooltip.querySelector('.dap-walkthrough-title') as HTMLElement;
447
+
448
+ stepIndicator.textContent = `Step ${stepIndex + 1} of ${payload.steps.length}`;
449
+ title.textContent = step.title;
450
+
451
+ // Body
452
+ const content = tooltip.querySelector('.dap-walkthrough-content') as HTMLElement;
453
+ content.innerHTML = sanitizeHtml(step.content);
454
+
455
+ // Progress dots
456
+ const progressContainer = tooltip.querySelector('.dap-walkthrough-progress') as HTMLElement;
457
+ progressContainer.innerHTML = '';
458
+
459
+ payload.steps.forEach((_: WalkthroughStep, index: number) => {
460
+ const dot = document.createElement('div');
461
+ dot.className = 'dap-walkthrough-progress-dot';
462
+
463
+ if (index < stepIndex) {
464
+ dot.classList.add('completed');
465
+ } else if (index === stepIndex) {
466
+ dot.classList.add('active');
467
+ }
468
+
469
+ progressContainer.appendChild(dot);
470
+ });
471
+
472
+ // Navigation buttons
473
+ const prevBtn = tooltip.querySelector('.dap-walkthrough-prev') as HTMLButtonElement;
474
+ const nextBtn = tooltip.querySelector('.dap-walkthrough-next') as HTMLButtonElement;
475
+ const stepCount = tooltip.querySelector('.dap-walkthrough-step-count') as HTMLElement;
476
+
477
+ prevBtn.disabled = stepIndex === 0;
478
+
479
+ if (stepIndex === payload.steps.length - 1) {
480
+ nextBtn.textContent = 'Complete';
481
+ nextBtn.className = 'dap-walkthrough-btn success dap-walkthrough-next';
482
+ } else {
483
+ nextBtn.textContent = 'Next';
484
+ nextBtn.className = 'dap-walkthrough-btn primary dap-walkthrough-next';
485
+ }
486
+
487
+ stepCount.textContent = `${stepIndex + 1} / ${payload.steps.length}`;
488
+ }
489
+
490
+ function positionTooltip(tooltip: HTMLElement, target: Element, position: string = 'bottom') {
491
+ const targetRect = target.getBoundingClientRect();
492
+ const tooltipRect = tooltip.getBoundingClientRect();
493
+ const viewportWidth = window.innerWidth;
494
+ const viewportHeight = window.innerHeight;
495
+ const gap = 16;
496
+
497
+ // Reset classes
498
+ tooltip.className = 'dap-walkthrough-tooltip visible';
499
+
500
+ let left = 0;
501
+ let top = 0;
502
+
503
+ switch (position) {
504
+ case 'top':
505
+ left = targetRect.left + (targetRect.width - tooltipRect.width) / 2;
506
+ top = targetRect.top - tooltipRect.height - gap;
507
+ tooltip.classList.add('position-top');
508
+ break;
509
+
510
+ case 'bottom':
511
+ left = targetRect.left + (targetRect.width - tooltipRect.width) / 2;
512
+ top = targetRect.bottom + gap;
513
+ tooltip.classList.add('position-bottom');
514
+ break;
515
+
516
+ case 'left':
517
+ left = targetRect.left - tooltipRect.width - gap;
518
+ top = targetRect.top + (targetRect.height - tooltipRect.height) / 2;
519
+ tooltip.classList.add('position-left');
520
+ break;
521
+
522
+ case 'right':
523
+ left = targetRect.right + gap;
524
+ top = targetRect.top + (targetRect.height - tooltipRect.height) / 2;
525
+ tooltip.classList.add('position-right');
526
+ break;
527
+ }
528
+
529
+ // Keep tooltip in viewport
530
+ left = Math.max(16, Math.min(left, viewportWidth - tooltipRect.width - 16));
531
+ top = Math.max(16, Math.min(top, viewportHeight - tooltipRect.height - 16));
532
+
533
+ tooltip.style.left = `${left}px`;
534
+ tooltip.style.top = `${top}px`;
535
+ }
536
+
537
+ function positionTooltipCenter(tooltip: HTMLElement) {
538
+ tooltip.className = 'dap-walkthrough-tooltip visible';
539
+ tooltip.style.left = '50%';
540
+ tooltip.style.top = '50%';
541
+ tooltip.style.transform = 'translate(-50%, -50%)';
542
+ }
543
+
544
+ function nextStep() {
545
+ if (currentStepIndex < payload.steps.length - 1) {
546
+ showStep(currentStepIndex + 1);
547
+ } else {
548
+ completeWalkthrough();
549
+ }
550
+ }
551
+
552
+ function prevStep() {
553
+ if (currentStepIndex > 0) {
554
+ showStep(currentStepIndex - 1);
555
+ }
556
+ }
557
+
558
+ function completeWalkthrough() {
559
+ isActive = false;
560
+
561
+ // Clean up highlights
562
+ document.querySelectorAll('.dap-walkthrough-highlight').forEach(el => {
563
+ el.classList.remove('dap-walkthrough-highlight');
564
+ });
565
+
566
+ // Fade out
567
+ overlay.style.opacity = '0';
568
+ tooltip.style.opacity = '0';
569
+ spotlight.style.opacity = '0';
570
+
571
+ setTimeout(() => {
572
+ overlay.remove();
573
+ tooltip.remove();
574
+ spotlight.remove();
575
+
576
+ // Signal completion
577
+ if (completionTracker?.onComplete) {
578
+ console.debug(`[DAP] Completing walkthrough flow: ${id}`);
579
+ completionTracker.onComplete();
580
+ }
581
+ }, 300);
582
+
583
+ document.removeEventListener('keydown', handleKeyboard);
584
+ }
585
+
586
+ // Event handlers
587
+ const closeBtn = tooltip.querySelector('.dap-walkthrough-close') as HTMLButtonElement;
588
+ const prevBtn = tooltip.querySelector('.dap-walkthrough-prev') as HTMLButtonElement;
589
+ const nextBtn = tooltip.querySelector('.dap-walkthrough-next') as HTMLButtonElement;
590
+
591
+ closeBtn.addEventListener('click', completeWalkthrough);
592
+ prevBtn.addEventListener('click', prevStep);
593
+ nextBtn.addEventListener('click', nextStep);
594
+
595
+ // Keyboard navigation
596
+ document.addEventListener('keydown', handleKeyboard);
597
+
598
+ function handleKeyboard(e: KeyboardEvent) {
599
+ if (!isActive) return;
600
+
601
+ switch (e.key) {
602
+ case 'Escape':
603
+ completeWalkthrough();
604
+ break;
605
+ case 'ArrowRight':
606
+ case ' ':
607
+ e.preventDefault();
608
+ nextStep();
609
+ break;
610
+ case 'ArrowLeft':
611
+ e.preventDefault();
612
+ prevStep();
613
+ break;
614
+ }
615
+ }
616
+
617
+ // Prevent clicks on overlay from closing
618
+ overlay.addEventListener('click', (e) => {
619
+ e.stopPropagation();
620
+ });
621
+ }
622
+
623
+ function createWalkthroughOverlay(): HTMLElement {
624
+ const overlay = document.createElement('div');
625
+ overlay.className = 'dap-walkthrough-overlay';
626
+ return overlay;
627
+ }
628
+
629
+ function createSpotlight(): HTMLElement {
630
+ const spotlight = document.createElement('div');
631
+ spotlight.className = 'dap-walkthrough-spotlight';
632
+ return spotlight;
633
+ }
634
+
635
+ function createTooltip(): HTMLElement {
636
+ const tooltip = document.createElement('div');
637
+ tooltip.className = 'dap-walkthrough-tooltip';
638
+ tooltip.setAttribute('role', 'dialog');
639
+ tooltip.setAttribute('aria-modal', 'true');
640
+
641
+ tooltip.innerHTML = `
642
+ <div class="dap-walkthrough-header">
643
+ <div class="dap-walkthrough-step-indicator">Step 1 of 1</div>
644
+ <h3 class="dap-walkthrough-title">Step Title</h3>
645
+ <button class="dap-walkthrough-close" aria-label="Close walkthrough">×</button>
646
+ </div>
647
+ <div class="dap-walkthrough-body">
648
+ <div class="dap-walkthrough-content">Step content goes here</div>
649
+ </div>
650
+ <div class="dap-walkthrough-footer">
651
+ <div class="dap-walkthrough-progress"></div>
652
+ <div class="dap-walkthrough-nav">
653
+ <span class="dap-walkthrough-step-count">1 / 1</span>
654
+ <button class="dap-walkthrough-btn dap-walkthrough-prev">Previous</button>
655
+ <button class="dap-walkthrough-btn primary dap-walkthrough-next">Next</button>
656
+ </div>
657
+ </div>
658
+ `;
659
+
660
+ return tooltip;
661
+ }
662
+
663
+ function ensureStyles() {
664
+ if (!document.getElementById('dap-walkthrough-style')) {
665
+ const style = document.createElement('style');
666
+ style.id = 'dap-walkthrough-style';
667
+ style.textContent = walkthroughCssText;
668
+ document.head.appendChild(style);
669
+ }
670
+ }