veo-sdk 0.3.5 → 0.3.7

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/dist/index.cjs CHANGED
@@ -58,6 +58,59 @@ function session() {
58
58
  return null;
59
59
  }
60
60
  }
61
+ function local() {
62
+ try {
63
+ return window.localStorage;
64
+ } catch {
65
+ return null;
66
+ }
67
+ }
68
+ function lsKey(guide) {
69
+ return LS_PREFIX + (guide || "default");
70
+ }
71
+ function persist(ctx) {
72
+ try {
73
+ session()?.setItem(CTX_KEY, JSON.stringify(ctx));
74
+ } catch {
75
+ }
76
+ try {
77
+ const stored = { ...ctx, savedAt: Date.now() };
78
+ local()?.setItem(lsKey(ctx.guide), JSON.stringify(stored));
79
+ } catch {
80
+ }
81
+ }
82
+ function readFreshFromLocal() {
83
+ const store = local();
84
+ if (!store) return null;
85
+ let best = null;
86
+ try {
87
+ for (let i = 0; i < store.length; i++) {
88
+ const key = store.key(i);
89
+ if (!key?.startsWith(LS_PREFIX)) continue;
90
+ const raw = store.getItem(key);
91
+ if (!raw) continue;
92
+ let parsed = null;
93
+ try {
94
+ parsed = JSON.parse(raw);
95
+ } catch {
96
+ continue;
97
+ }
98
+ if (typeof parsed?.savedAt !== "number" || Date.now() - parsed.savedAt > PERSIST_TTL_MS) {
99
+ try {
100
+ store.removeItem(key);
101
+ } catch {
102
+ }
103
+ continue;
104
+ }
105
+ if (!best || parsed.savedAt > best.savedAt) best = parsed;
106
+ }
107
+ } catch {
108
+ return null;
109
+ }
110
+ if (!best) return null;
111
+ const { savedAt: _savedAt, ...ctx } = best;
112
+ return ctx;
113
+ }
61
114
  function urlParams() {
62
115
  try {
63
116
  return new URLSearchParams(window.location.search);
@@ -77,18 +130,22 @@ function resolveBuilderContext() {
77
130
  guide: params?.get(BUILDER_GUIDE_PARAM) ?? null,
78
131
  panelPath: params?.get(BUILDER_PANEL_PATH_PARAM) ?? null
79
132
  };
80
- try {
81
- session()?.setItem(CTX_KEY, JSON.stringify(ctx));
82
- } catch {
83
- }
133
+ persist(ctx);
84
134
  return ctx;
85
135
  }
86
136
  try {
87
137
  const raw = session()?.getItem(CTX_KEY);
88
- return raw ? JSON.parse(raw) : null;
138
+ if (raw) return JSON.parse(raw);
89
139
  } catch {
90
- return null;
91
140
  }
141
+ const fromLocal = readFreshFromLocal();
142
+ if (fromLocal) {
143
+ try {
144
+ session()?.setItem(CTX_KEY, JSON.stringify(fromLocal));
145
+ } catch {
146
+ }
147
+ }
148
+ return fromLocal;
92
149
  }
93
150
  function resolveBuilderToken() {
94
151
  return resolveBuilderContext()?.token ?? null;
@@ -98,18 +155,30 @@ function isBuilderMode() {
98
155
  }
99
156
  function clearBuilderToken() {
100
157
  if (!hasWindow()) return;
158
+ let guide = null;
159
+ try {
160
+ const raw = session()?.getItem(CTX_KEY);
161
+ if (raw) guide = JSON.parse(raw).guide ?? null;
162
+ } catch {
163
+ }
101
164
  try {
102
165
  session()?.removeItem(CTX_KEY);
103
166
  } catch {
104
167
  }
168
+ try {
169
+ local()?.removeItem(lsKey(guide));
170
+ } catch {
171
+ }
105
172
  }
106
- var CTX_KEY;
173
+ var CTX_KEY, LS_PREFIX, PERSIST_TTL_MS;
107
174
  var init_builder_token = __esm({
108
175
  "src/core/builder-token.ts"() {
109
176
  init_safe_env();
110
177
  init_builder_constants();
111
178
  init_builder_constants();
112
179
  CTX_KEY = "veo:builder_ctx";
180
+ LS_PREFIX = "veo:builder_ctx:";
181
+ PERSIST_TTL_MS = 12 * 60 * 60 * 1e3;
113
182
  }
114
183
  });
115
184
 
@@ -309,6 +378,16 @@ var init_constants2 = __esm({
309
378
  function buildStepContent(step, doc, callbacks) {
310
379
  const container = doc.createElement("div");
311
380
  container.className = "veo-guide-content";
381
+ const blocks = normalizeBlocks(step);
382
+ if (blocks) {
383
+ renderBlocks(container, blocks, doc, callbacks);
384
+ } else {
385
+ renderLegacyStep(container, step, doc, callbacks);
386
+ }
387
+ container.appendChild(createCloseButton(doc, () => callbacks.onDismiss()));
388
+ return container;
389
+ }
390
+ function renderLegacyStep(container, step, doc, callbacks) {
312
391
  if (typeof step.imageUrl === "string" && step.imageUrl && isSafeUrl(step.imageUrl)) {
313
392
  const img = doc.createElement("img");
314
393
  img.className = "veo-guide-image";
@@ -343,14 +422,114 @@ function buildStepContent(step, doc, callbacks) {
343
422
  actions.appendChild(cta);
344
423
  }
345
424
  container.appendChild(actions);
425
+ }
426
+ function normalizeBlocks(step) {
427
+ if (!Array.isArray(step.blocks) || step.blocks.length === 0) return null;
428
+ const valid = step.blocks.filter((b) => {
429
+ if (!b || typeof b !== "object") return false;
430
+ if (b.type === "image") return typeof b.url === "string" && isSafeUrl(b.url);
431
+ return b.type === "title" || b.type === "text" || b.type === "button";
432
+ });
433
+ return valid.length > 0 ? valid : null;
434
+ }
435
+ function renderBlocks(container, blocks, doc, callbacks) {
436
+ let i = 0;
437
+ while (i < blocks.length) {
438
+ const block = blocks[i];
439
+ if (block?.type === "button") {
440
+ const actions = doc.createElement("div");
441
+ actions.className = "veo-guide-actions";
442
+ while (i < blocks.length && blocks[i]?.type === "button") {
443
+ const btnBlock = blocks[i];
444
+ actions.appendChild(buildButtonBlock(btnBlock, doc, callbacks));
445
+ i++;
446
+ }
447
+ container.appendChild(actions);
448
+ continue;
449
+ }
450
+ const node = buildContentBlock(block, doc);
451
+ if (node) container.appendChild(node);
452
+ i++;
453
+ }
454
+ }
455
+ function buildContentBlock(block, doc) {
456
+ switch (block.type) {
457
+ case "title": {
458
+ const el = doc.createElement("h2");
459
+ el.className = "veo-guide-title";
460
+ el.textContent = typeof block.text === "string" ? block.text : "";
461
+ applyBlockStyle(el, block.style, "text");
462
+ return el;
463
+ }
464
+ case "text": {
465
+ const el = doc.createElement("p");
466
+ el.className = "veo-guide-text";
467
+ el.textContent = typeof block.text === "string" ? block.text : "";
468
+ applyBlockStyle(el, block.style, "text");
469
+ return el;
470
+ }
471
+ case "image": {
472
+ if (typeof block.url !== "string" || !isSafeUrl(block.url)) return null;
473
+ const el = doc.createElement("img");
474
+ el.className = "veo-guide-image";
475
+ el.src = block.url;
476
+ el.alt = "";
477
+ applyBlockStyle(el, block.style, "image");
478
+ return el;
479
+ }
480
+ default:
481
+ return null;
482
+ }
483
+ }
484
+ function buildButtonBlock(block, doc, callbacks) {
485
+ const btn = doc.createElement("button");
486
+ btn.type = "button";
487
+ btn.className = "veo-guide-cta";
488
+ btn.textContent = typeof block.text === "string" && block.text ? block.text : "OK";
489
+ applyBlockStyle(btn, block.style, "button");
490
+ btn.addEventListener("click", () => {
491
+ const action = block.action ?? "dismiss";
492
+ const url = action === "url" && typeof block.url === "string" && isSafeUrl(block.url) ? block.url : void 0;
493
+ callbacks.onCtaClick(action, url);
494
+ });
495
+ return btn;
496
+ }
497
+ function clampNum(n, min, max) {
498
+ return Math.min(max, Math.max(min, n));
499
+ }
500
+ function applyBlockStyle(el, style, kind) {
501
+ if (!style || typeof style !== "object") return;
502
+ const s = style;
503
+ const align = typeof s.align === "string" ? s.align : null;
504
+ if (align && (align === "left" || align === "center" || align === "right")) {
505
+ if (kind === "text") el.style.textAlign = align;
506
+ else el.style.alignSelf = BLOCK_ALIGN_SELF[align];
507
+ }
508
+ if (kind !== "image" && typeof s.color === "string") el.style.color = s.color;
509
+ if (kind === "text" && typeof s.fontSize === "number" && Number.isFinite(s.fontSize)) {
510
+ el.style.fontSize = `${clampNum(s.fontSize, 8, 72)}px`;
511
+ }
512
+ if (kind === "image" && typeof s.width === "number" && Number.isFinite(s.width)) {
513
+ el.style.width = `${clampNum(s.width, 10, 100)}%`;
514
+ }
515
+ if (kind === "image" && typeof s.radius === "number" && Number.isFinite(s.radius)) {
516
+ el.style.borderRadius = `${clampNum(s.radius, 0, 48)}px`;
517
+ }
518
+ if (typeof s.marginTop === "number" && Number.isFinite(s.marginTop)) {
519
+ el.style.marginTop = `${clampNum(s.marginTop, 0, 64)}px`;
520
+ }
521
+ if (typeof s.marginBottom === "number" && Number.isFinite(s.marginBottom)) {
522
+ el.style.marginBottom = `${clampNum(s.marginBottom, 0, 64)}px`;
523
+ }
524
+ }
525
+ function createCloseButton(doc, onClose, className = "veo-guide-close") {
346
526
  const closeBtn = doc.createElement("button");
347
527
  closeBtn.type = "button";
348
- closeBtn.className = "veo-guide-close";
528
+ closeBtn.className = className;
349
529
  closeBtn.setAttribute("aria-label", "Cerrar");
350
530
  closeBtn.textContent = "\xD7";
351
- closeBtn.addEventListener("click", () => callbacks.onDismiss());
352
- container.appendChild(closeBtn);
353
- return container;
531
+ closeBtn.addEventListener("click", onClose);
532
+ return closeBtn;
354
533
  }
355
534
  function isSafeUrl(url) {
356
535
  if (typeof url !== "string" || url.length === 0) return false;
@@ -367,9 +546,92 @@ function readCtaUrl(step) {
367
546
  if (typeof candidate !== "string") return void 0;
368
547
  return isSafeUrl(candidate) ? candidate : void 0;
369
548
  }
549
+ var BLOCK_ALIGN_SELF;
370
550
  var init_block_builder = __esm({
371
551
  "src/plugins/guides/block-builder.ts"() {
372
552
  init_constants2();
553
+ BLOCK_ALIGN_SELF = {
554
+ left: "flex-start",
555
+ center: "center",
556
+ right: "flex-end"
557
+ };
558
+ }
559
+ });
560
+
561
+ // src/plugins/guides/walkthrough-block-builder.ts
562
+ function buildWalkthroughStepContent(step, stepIndex, totalSteps, doc, callbacks) {
563
+ const container = doc.createElement("div");
564
+ container.className = "veo-guide-content";
565
+ const counter = doc.createElement("div");
566
+ counter.className = "veo-walkthrough-counter";
567
+ counter.textContent = `Paso ${stepIndex + 1} de ${totalSteps}`;
568
+ container.appendChild(counter);
569
+ const progress = doc.createElement("div");
570
+ progress.className = "veo-walkthrough-progress";
571
+ for (let i = 0; i < totalSteps; i++) {
572
+ const dot = doc.createElement("span");
573
+ dot.className = "veo-walkthrough-progress-dot";
574
+ if (i < stepIndex) dot.classList.add("completed");
575
+ if (i === stepIndex) dot.classList.add("active");
576
+ progress.appendChild(dot);
577
+ }
578
+ container.appendChild(progress);
579
+ if (typeof step.imageUrl === "string" && step.imageUrl && isSafeUrl(step.imageUrl)) {
580
+ const img = doc.createElement("img");
581
+ img.className = "veo-guide-image";
582
+ img.src = step.imageUrl;
583
+ img.alt = typeof step.title === "string" ? step.title : "";
584
+ container.appendChild(img);
585
+ }
586
+ if (typeof step.title === "string" && step.title) {
587
+ const heading = doc.createElement("h2");
588
+ heading.className = "veo-guide-title";
589
+ heading.textContent = step.title;
590
+ container.appendChild(heading);
591
+ }
592
+ if (typeof step.content === "string" && step.content) {
593
+ const paragraph = doc.createElement("p");
594
+ paragraph.className = "veo-guide-text";
595
+ paragraph.textContent = step.content;
596
+ container.appendChild(paragraph);
597
+ }
598
+ const actions = doc.createElement("div");
599
+ actions.className = "veo-walkthrough-actions";
600
+ const skipBtn = doc.createElement("button");
601
+ skipBtn.type = "button";
602
+ skipBtn.className = "veo-walkthrough-skip";
603
+ skipBtn.textContent = "Omitir";
604
+ skipBtn.addEventListener("click", () => callbacks.onSkip());
605
+ actions.appendChild(skipBtn);
606
+ const rightGroup = doc.createElement("div");
607
+ rightGroup.className = "veo-walkthrough-actions-right";
608
+ if (stepIndex > 0) {
609
+ const backBtn = doc.createElement("button");
610
+ backBtn.type = "button";
611
+ backBtn.className = "veo-walkthrough-btn-secondary";
612
+ backBtn.textContent = "Atr\xE1s";
613
+ backBtn.addEventListener("click", () => callbacks.onBack());
614
+ rightGroup.appendChild(backBtn);
615
+ }
616
+ const isLastStep = stepIndex === totalSteps - 1;
617
+ const primaryBtn = doc.createElement("button");
618
+ primaryBtn.type = "button";
619
+ primaryBtn.className = "veo-guide-cta";
620
+ const defaultLabel = isLastStep ? "Finalizar" : "Siguiente";
621
+ primaryBtn.textContent = typeof step.ctaText === "string" && step.ctaText ? step.ctaText : defaultLabel;
622
+ primaryBtn.addEventListener("click", () => {
623
+ if (isLastStep) callbacks.onComplete();
624
+ else callbacks.onNext();
625
+ });
626
+ rightGroup.appendChild(primaryBtn);
627
+ actions.appendChild(rightGroup);
628
+ container.appendChild(actions);
629
+ container.appendChild(createCloseButton(doc, () => callbacks.onSkip()));
630
+ return container;
631
+ }
632
+ var init_walkthrough_block_builder = __esm({
633
+ "src/plugins/guides/walkthrough-block-builder.ts"() {
634
+ init_block_builder();
373
635
  }
374
636
  });
375
637
 
@@ -383,6 +645,9 @@ function applyDesignVars(host, style) {
383
645
  if (typeof s.accentColor === "string" && COLOR_RE.test(s.accentColor.trim())) {
384
646
  host.style.setProperty("--veo-primary", s.accentColor.trim());
385
647
  }
648
+ if (typeof s.fontFamily === "string" && s.fontFamily.length <= 200 && !/[<>{}]/.test(s.fontFamily)) {
649
+ host.style.setProperty("--veo-font", s.fontFamily.trim());
650
+ }
386
651
  if (s.theme === "dark") {
387
652
  for (const [key, value] of Object.entries(DARK_THEME)) {
388
653
  host.style.setProperty(key, value);
@@ -424,8 +689,45 @@ function applyDesignVars(host, style) {
424
689
  host.style.setProperty("--veo-pos-x", `${px * 100}%`);
425
690
  host.style.setProperty("--veo-pos-y", `${py * 100}%`);
426
691
  }
692
+ if (s.elements && typeof s.elements === "object") {
693
+ const el = s.elements;
694
+ applyElementVars(host, "title", el.title);
695
+ applyElementVars(host, "text", el.text);
696
+ applyElementVars(host, "image", el.image);
697
+ const cta = el.cta;
698
+ if (cta && typeof cta.align === "string" && ALIGN_JUSTIFY[cta.align]) {
699
+ host.style.setProperty("--veo-actions-justify", ALIGN_JUSTIFY[cta.align]);
700
+ }
701
+ }
702
+ }
703
+ function applyElementVars(host, prefix, raw) {
704
+ if (!raw || typeof raw !== "object") return;
705
+ const el = raw;
706
+ if (typeof el.align === "string" && (el.align === "left" || el.align === "center" || el.align === "right")) {
707
+ const mode = ELEMENT_ALIGN_MODE[prefix] ?? "text";
708
+ const value = mode === "self" ? ALIGN_SELF[el.align] : el.align;
709
+ host.style.setProperty(`--veo-${prefix}-align`, value);
710
+ }
711
+ if (typeof el.color === "string" && COLOR_RE.test(el.color.trim())) {
712
+ host.style.setProperty(`--veo-${prefix}-color`, el.color.trim());
713
+ }
714
+ if (typeof el.fontSize === "number" && Number.isFinite(el.fontSize)) {
715
+ host.style.setProperty(`--veo-${prefix}-size`, `${clamp(el.fontSize, 8, 72)}px`);
716
+ }
717
+ if (typeof el.width === "number" && Number.isFinite(el.width)) {
718
+ host.style.setProperty(`--veo-${prefix}-width`, `${clamp(el.width, 10, 100)}%`);
719
+ }
720
+ if (typeof el.radius === "number" && Number.isFinite(el.radius)) {
721
+ host.style.setProperty(`--veo-${prefix}-radius`, `${clamp(el.radius, 0, 48)}px`);
722
+ }
723
+ if (typeof el.marginTop === "number" && Number.isFinite(el.marginTop)) {
724
+ host.style.setProperty(`--veo-${prefix}-mt`, `${clamp(el.marginTop, 0, 64)}px`);
725
+ }
726
+ if (typeof el.marginBottom === "number" && Number.isFinite(el.marginBottom)) {
727
+ host.style.setProperty(`--veo-${prefix}-mb`, `${clamp(el.marginBottom, 0, 64)}px`);
728
+ }
427
729
  }
428
- var DARK_THEME, ALIGN_JUSTIFY, SHADOW, PRESET_POS, COLOR_RE;
730
+ var DARK_THEME, ALIGN_JUSTIFY, ALIGN_SELF, ELEMENT_ALIGN_MODE, SHADOW, PRESET_POS, COLOR_RE;
429
731
  var init_guide_design = __esm({
430
732
  "src/plugins/guides/guide-design.ts"() {
431
733
  DARK_THEME = {
@@ -439,6 +741,16 @@ var init_guide_design = __esm({
439
741
  center: "center",
440
742
  right: "flex-end"
441
743
  };
744
+ ALIGN_SELF = {
745
+ left: "flex-start",
746
+ center: "center",
747
+ right: "flex-end"
748
+ };
749
+ ELEMENT_ALIGN_MODE = {
750
+ title: "text",
751
+ text: "text",
752
+ image: "self"
753
+ };
442
754
  SHADOW = {
443
755
  none: "none",
444
756
  soft: "0 4px 14px rgba(0, 0, 0, 0.10)",
@@ -477,7 +789,7 @@ var init_styles = __esm({
477
789
  --veo-actions-justify: flex-end;
478
790
  all: initial;
479
791
  }
480
- * { box-sizing: border-box; font-family: -apple-system, system-ui, "Segoe UI", Roboto, sans-serif; }
792
+ * { box-sizing: border-box; font-family: var(--veo-font, -apple-system, system-ui, "Segoe UI", Roboto, sans-serif); }
481
793
 
482
794
  .veo-modal-overlay {
483
795
  position: fixed; inset: 0;
@@ -528,6 +840,15 @@ var init_styles = __esm({
528
840
  box-shadow: var(--veo-shadow, 0 8px 30px rgba(0, 0, 0, 0.18));
529
841
  animation: veo-fade-in 150ms ease-out;
530
842
  }
843
+ .veo-tooltip-arrow {
844
+ position: absolute;
845
+ width: 12px;
846
+ height: 12px;
847
+ background: var(--veo-bg);
848
+ border: var(--veo-border-width, 0) solid var(--veo-border-color, transparent);
849
+ transform: rotate(45deg);
850
+ pointer-events: none;
851
+ }
531
852
 
532
853
  .veo-inline {
533
854
  background: var(--veo-bg);
@@ -546,17 +867,25 @@ var init_styles = __esm({
546
867
  display: flex; flex-direction: column;
547
868
  }
548
869
  .veo-guide-image {
549
- display: block; width: 100%; max-height: 180px;
550
- object-fit: cover; border-radius: 8px;
551
- margin-bottom: 12px;
870
+ display: block;
871
+ width: var(--veo-image-width, 100%);
872
+ max-height: 180px;
873
+ object-fit: cover;
874
+ align-self: var(--veo-image-align, stretch);
875
+ border-radius: var(--veo-image-radius, 8px);
876
+ margin: var(--veo-image-mt, 0) 0 var(--veo-image-mb, 12px);
552
877
  }
553
878
  .veo-guide-title {
554
- font-size: 18px; font-weight: 600; line-height: 1.3;
555
- margin: 0 0 6px; color: var(--veo-text);
879
+ font-size: var(--veo-title-size, 18px); font-weight: 600; line-height: 1.3;
880
+ text-align: var(--veo-title-align, left);
881
+ margin: var(--veo-title-mt, 0) 0 var(--veo-title-mb, 6px);
882
+ color: var(--veo-title-color, var(--veo-text));
556
883
  }
557
884
  .veo-guide-text {
558
- font-size: 14px; line-height: 1.5;
559
- margin: 0 0 16px; color: var(--veo-text-secondary);
885
+ font-size: var(--veo-text-size, 14px); line-height: 1.5;
886
+ text-align: var(--veo-text-align, left);
887
+ margin: var(--veo-text-mt, 0) 0 var(--veo-text-mb, 16px);
888
+ color: var(--veo-text-color, var(--veo-text-secondary));
560
889
  }
561
890
  .veo-guide-actions {
562
891
  display: flex; gap: 8px; justify-content: var(--veo-actions-justify);
@@ -693,6 +1022,7 @@ var init_styles = __esm({
693
1022
  }
694
1023
  .veo-custom-inline {
695
1024
  display: block;
1025
+ position: relative;
696
1026
  margin: 12px 0;
697
1027
  }
698
1028
  .veo-custom-frame {
@@ -702,6 +1032,21 @@ var init_styles = __esm({
702
1032
  background: transparent;
703
1033
  color-scheme: normal;
704
1034
  }
1035
+ /*
1036
+ * X de cerrar de las gu\xEDas custom: el HTML del cliente vive en un iframe
1037
+ * sandbox, as\xED que el bot\xF3n lo pone el host POR ENCIMA del iframe. Con fondo
1038
+ * propio para ser visible sobre cualquier contenido.
1039
+ */
1040
+ .veo-custom-close {
1041
+ position: absolute; top: 6px; right: 6px; z-index: 1;
1042
+ width: 22px; height: 22px; padding: 0;
1043
+ display: flex; align-items: center; justify-content: center;
1044
+ font-size: 16px; line-height: 1; color: #6b7280;
1045
+ background: rgba(255, 255, 255, 0.92);
1046
+ border: 1px solid rgba(0, 0, 0, 0.08); border-radius: 50%;
1047
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.14); cursor: pointer;
1048
+ }
1049
+ .veo-custom-close:hover { color: #111827; }
705
1050
 
706
1051
  @keyframes veo-fade-in { from { opacity: 0; } to { opacity: 1; } }
707
1052
  @keyframes veo-slide-up {
@@ -787,10 +1132,12 @@ var BannerRenderer;
787
1132
  var init_banner_renderer = __esm({
788
1133
  "src/plugins/guides/renderers/banner-renderer.ts"() {
789
1134
  init_block_builder();
1135
+ init_walkthrough_block_builder();
790
1136
  init_base_renderer();
791
1137
  BannerRenderer = class extends BaseRenderer {
792
1138
  render(context) {
793
- const step = context.guide.guideSteps[0];
1139
+ const idx = context.nav?.stepIndex ?? 0;
1140
+ const step = context.guide.guideSteps[idx];
794
1141
  if (!step) return;
795
1142
  const { root } = this.createHost();
796
1143
  this.applyDesign(step.style);
@@ -799,27 +1146,25 @@ var init_banner_renderer = __esm({
799
1146
  const banner = ownerDocument.createElement("div");
800
1147
  banner.className = `veo-banner veo-banner-${position}`;
801
1148
  const dismiss = (action, ctaUrl) => {
802
- context.onInteraction({
803
- guideId: context.guide.guideId,
804
- stepIndex: 0,
805
- action
806
- });
1149
+ context.onInteraction({ guideId: context.guide.guideId, stepIndex: idx, action });
807
1150
  if (ctaUrl) window.open(ctaUrl, "_blank", "noopener,noreferrer");
808
1151
  context.onClose();
809
1152
  };
810
- const content = buildStepContent(step, ownerDocument, {
811
- onCtaClick: (action, url) => {
812
- dismiss("cta_clicked", action === "url" ? url : void 0);
813
- },
1153
+ const content = context.nav ? buildWalkthroughStepContent(
1154
+ step,
1155
+ context.nav.stepIndex,
1156
+ context.nav.totalSteps,
1157
+ ownerDocument,
1158
+ context.nav.callbacks
1159
+ ) : buildStepContent(step, ownerDocument, {
1160
+ onCtaClick: (action, url) => dismiss("cta_clicked", action === "url" ? url : void 0),
814
1161
  onDismiss: () => dismiss("dismissed")
815
1162
  });
816
1163
  banner.appendChild(content);
817
1164
  root.appendChild(banner);
818
- context.onInteraction({
819
- guideId: context.guide.guideId,
820
- stepIndex: 0,
821
- action: "shown"
822
- });
1165
+ if (!context.nav) {
1166
+ context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
1167
+ }
823
1168
  }
824
1169
  };
825
1170
  }
@@ -872,7 +1217,11 @@ function mountCustomFrame(doc, step, context, opts) {
872
1217
  const hasFixedHeight = opts.fixedHeight !== void 0 && opts.fixedHeight !== null;
873
1218
  if (hasFixedHeight) iframe.style.height = toCssSize(opts.fixedHeight);
874
1219
  const token = uuidv7();
875
- iframe.srcdoc = buildSrcdoc(step.html ?? "", step.css ?? "", step.js ?? "", token);
1220
+ const wantsTailwind = step.style?.tailwind === true;
1221
+ iframe.srcdoc = buildSrcdoc(step.html ?? "", step.css ?? "", step.js ?? "", token, {
1222
+ tokens: collectHostTokens(),
1223
+ tailwind: wantsTailwind
1224
+ });
876
1225
  const close = () => context.onClose();
877
1226
  const onMessage = (event) => {
878
1227
  if (event.source !== iframe.contentWindow) return;
@@ -921,12 +1270,37 @@ function mountCustomFrame(doc, step, context, opts) {
921
1270
  function toCssSize(value) {
922
1271
  return typeof value === "number" ? `${value}px` : value;
923
1272
  }
1273
+ function collectHostTokens() {
1274
+ if (typeof window === "undefined" || typeof document === "undefined") return "";
1275
+ try {
1276
+ const rootStyle = getComputedStyle(document.documentElement);
1277
+ const decls = [];
1278
+ const seen = /* @__PURE__ */ new Set();
1279
+ for (let i = 0; i < rootStyle.length && decls.length < 400; i++) {
1280
+ const name = rootStyle.item(i);
1281
+ if (!name.startsWith("--") || seen.has(name)) continue;
1282
+ seen.add(name);
1283
+ const value = rootStyle.getPropertyValue(name).trim();
1284
+ if (!value || value.length > 200 || /[<>{}]/.test(value)) continue;
1285
+ decls.push(`${name}:${value}`);
1286
+ }
1287
+ const bodyFont = getComputedStyle(document.body).fontFamily;
1288
+ if (bodyFont && bodyFont.length <= 200 && !/[<>{}]/.test(bodyFont)) {
1289
+ decls.push(`--veo-host-font:${bodyFont}`);
1290
+ }
1291
+ return decls.length ? `:root{${decls.join(";")}}` : "";
1292
+ } catch {
1293
+ return "";
1294
+ }
1295
+ }
924
1296
  function isRecord(value) {
925
1297
  return typeof value === "object" && value !== null && !Array.isArray(value);
926
1298
  }
927
- function buildSrcdoc(html, css, js, token) {
1299
+ function buildSrcdoc(html, css, js, token, opts) {
928
1300
  const bridge = `(function(){var T=${JSON.stringify(token)};function P(m){m.__veo=T;parent.postMessage(m,'*');}window.veo={dismiss:function(){P({type:'dismiss'});},cta:function(u){P({type:'cta',url:u});},track:function(n,p){P({type:'track',name:n,props:p||{}});},navigate:function(u){P({type:'navigate',url:u});}};document.addEventListener('click',function(e){var a=e.target&&e.target.closest?e.target.closest('a[href]'):null;if(!a)return;var h=a.getAttribute('href');if(!h||h.charAt(0)==='#'||/^(javascript|mailto|tel):/i.test(h))return;e.preventDefault();if(a.target==='_blank'){P({type:'cta',url:a.href,close:false});}else{P({type:'navigate',url:h});}},true);function H(){P({type:'resize',height:Math.ceil(document.documentElement.scrollHeight)});}window.addEventListener('load',H);try{if(typeof ResizeObserver!=='undefined'){new ResizeObserver(H).observe(document.documentElement);}}catch(e){}})();`;
929
- return `<!DOCTYPE html><html><head><meta charset="utf-8"><style>html,body{margin:0;padding:0;background:transparent;}</style><style>${escapeClosing(css, "style")}</style></head><body>${html}<script>${bridge}</script>` + (js ? `<script>${escapeClosing(js, "script")}</script>` : "") + "</body></html>";
1301
+ const tokensStyle = opts.tokens ? `<style>${escapeClosing(opts.tokens, "style")}</style>` : "";
1302
+ const tailwind = opts.tailwind ? '<script src="https://cdn.tailwindcss.com"></script>' : "";
1303
+ return '<!DOCTYPE html><html><head><meta charset="utf-8"><style>html,body{margin:0;padding:0;background:transparent;font-family:var(--veo-host-font,system-ui,sans-serif);}</style>' + tokensStyle + tailwind + `<style>${escapeClosing(css, "style")}</style></head><body>${html}<script>${bridge}</script>` + (js ? `<script>${escapeClosing(js, "script")}</script>` : "") + "</body></html>";
930
1304
  }
931
1305
  function escapeClosing(code, tag) {
932
1306
  const rx = tag === "script" ? /<\/script/gi : /<\/style/gi;
@@ -964,6 +1338,7 @@ function applyFloatingPosition(wrapper, placement) {
964
1338
  var DEFAULT_PLACEMENT, DEFAULT_OFFSET, DEFAULT_WIDTH, CustomRenderer;
965
1339
  var init_custom_renderer = __esm({
966
1340
  "src/plugins/guides/renderers/custom-renderer.ts"() {
1341
+ init_block_builder();
967
1342
  init_wait_for_element();
968
1343
  init_base_renderer();
969
1344
  init_custom_frame();
@@ -992,6 +1367,20 @@ var init_custom_renderer = __esm({
992
1367
  });
993
1368
  this.registerCleanup(cleanup);
994
1369
  wrapper.appendChild(iframe);
1370
+ wrapper.appendChild(
1371
+ createCloseButton(
1372
+ doc,
1373
+ () => {
1374
+ context.onInteraction({
1375
+ guideId: context.guide.guideId,
1376
+ stepIndex: 0,
1377
+ action: "dismissed"
1378
+ });
1379
+ context.onClose();
1380
+ },
1381
+ "veo-custom-close"
1382
+ )
1383
+ );
995
1384
  root.appendChild(wrapper);
996
1385
  if (placement.mode === "floating") {
997
1386
  applyFloatingPosition(wrapper, placement);
@@ -1108,13 +1497,7 @@ function mountFormContent(card, context, doc) {
1108
1497
  });
1109
1498
  });
1110
1499
  content.appendChild(form);
1111
- const closeBtn = doc.createElement("button");
1112
- closeBtn.type = "button";
1113
- closeBtn.className = "veo-guide-close";
1114
- closeBtn.setAttribute("aria-label", "Cerrar");
1115
- closeBtn.textContent = "\xD7";
1116
- closeBtn.addEventListener("click", dismiss);
1117
- content.appendChild(closeBtn);
1500
+ content.appendChild(createCloseButton(doc, dismiss));
1118
1501
  card.appendChild(content);
1119
1502
  }
1120
1503
  function showThanks(card, doc, note) {
@@ -1298,6 +1681,7 @@ function buildScale(wrapper, doc, min, max, btnClass, symbol) {
1298
1681
  }
1299
1682
  var init_form_content = __esm({
1300
1683
  "src/plugins/guides/form-content.ts"() {
1684
+ init_block_builder();
1301
1685
  }
1302
1686
  });
1303
1687
 
@@ -1377,6 +1761,7 @@ var init_inline_host = __esm({
1377
1761
  var InlineCustomRenderer;
1378
1762
  var init_inline_custom_renderer = __esm({
1379
1763
  "src/plugins/guides/renderers/inline-custom-renderer.ts"() {
1764
+ init_block_builder();
1380
1765
  init_inline_host();
1381
1766
  init_wait_for_element();
1382
1767
  init_base_renderer();
@@ -1400,6 +1785,20 @@ var init_inline_custom_renderer = __esm({
1400
1785
  const { iframe, cleanup } = mountCustomFrame(doc, step, context, { width: "100%" });
1401
1786
  this.registerCleanup(cleanup);
1402
1787
  wrapper.appendChild(iframe);
1788
+ wrapper.appendChild(
1789
+ createCloseButton(
1790
+ doc,
1791
+ () => {
1792
+ context.onInteraction({
1793
+ guideId: context.guide.guideId,
1794
+ stepIndex: 0,
1795
+ action: "dismissed"
1796
+ });
1797
+ context.onClose();
1798
+ },
1799
+ "veo-custom-close"
1800
+ )
1801
+ );
1403
1802
  root.appendChild(wrapper);
1404
1803
  context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
1405
1804
  }
@@ -1487,10 +1886,12 @@ var ModalRenderer;
1487
1886
  var init_modal_renderer = __esm({
1488
1887
  "src/plugins/guides/renderers/modal-renderer.ts"() {
1489
1888
  init_block_builder();
1889
+ init_walkthrough_block_builder();
1490
1890
  init_base_renderer();
1491
1891
  ModalRenderer = class extends BaseRenderer {
1492
1892
  render(context) {
1493
- const step = context.guide.guideSteps[0];
1893
+ const idx = context.nav?.stepIndex ?? 0;
1894
+ const step = context.guide.guideSteps[idx];
1494
1895
  if (!step) return;
1495
1896
  const { root } = this.createHost();
1496
1897
  this.applyDesign(step.style);
@@ -1500,46 +1901,71 @@ var init_modal_renderer = __esm({
1500
1901
  const card = ownerDocument.createElement("div");
1501
1902
  card.className = "veo-modal-card";
1502
1903
  const dismiss = (action, ctaUrl) => {
1503
- context.onInteraction({
1504
- guideId: context.guide.guideId,
1505
- stepIndex: 0,
1506
- action
1507
- });
1904
+ context.onInteraction({ guideId: context.guide.guideId, stepIndex: idx, action });
1508
1905
  if (ctaUrl) window.open(ctaUrl, "_blank", "noopener,noreferrer");
1509
1906
  context.onClose();
1510
1907
  };
1511
- const content = buildStepContent(step, ownerDocument, {
1512
- onCtaClick: (action, url) => {
1513
- dismiss("cta_clicked", action === "url" ? url : void 0);
1514
- },
1908
+ const onBackdropOrEsc = () => {
1909
+ if (context.nav) context.nav.callbacks.onSkip();
1910
+ else dismiss("dismissed");
1911
+ };
1912
+ const content = context.nav ? buildWalkthroughStepContent(
1913
+ step,
1914
+ context.nav.stepIndex,
1915
+ context.nav.totalSteps,
1916
+ ownerDocument,
1917
+ context.nav.callbacks
1918
+ ) : buildStepContent(step, ownerDocument, {
1919
+ onCtaClick: (action, url) => dismiss("cta_clicked", action === "url" ? url : void 0),
1515
1920
  onDismiss: () => dismiss("dismissed")
1516
1921
  });
1517
1922
  card.appendChild(content);
1518
1923
  overlay.appendChild(card);
1519
1924
  root.appendChild(overlay);
1520
1925
  overlay.addEventListener("click", (e) => {
1521
- if (e.target === overlay) dismiss("dismissed");
1926
+ if (e.target === overlay) onBackdropOrEsc();
1522
1927
  });
1523
1928
  const onKey = (e) => {
1524
- if (e.key === "Escape") dismiss("dismissed");
1929
+ if (e.key === "Escape") onBackdropOrEsc();
1525
1930
  };
1526
1931
  document.addEventListener("keydown", onKey);
1527
1932
  this.registerCleanup(() => document.removeEventListener("keydown", onKey));
1528
- context.onInteraction({
1529
- guideId: context.guide.guideId,
1530
- stepIndex: 0,
1531
- action: "shown"
1532
- });
1933
+ if (!context.nav) {
1934
+ context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
1935
+ }
1533
1936
  }
1534
1937
  };
1535
1938
  }
1536
1939
  });
1940
+
1941
+ // src/plugins/guides/renderers/floating-arrow.ts
1942
+ function positionArrow(arrowEl, placement, data) {
1943
+ const side = STATIC_SIDE[placement.split("-")[0] ?? "bottom"] ?? "top";
1944
+ for (const prop of ["top", "bottom", "left", "right"]) {
1945
+ arrowEl.style.setProperty(prop, "");
1946
+ }
1947
+ if (data?.x != null) arrowEl.style.setProperty("left", `${data.x}px`);
1948
+ if (data?.y != null) arrowEl.style.setProperty("top", `${data.y}px`);
1949
+ arrowEl.style.setProperty(side, "-6px");
1950
+ }
1951
+ var STATIC_SIDE;
1952
+ var init_floating_arrow = __esm({
1953
+ "src/plugins/guides/renderers/floating-arrow.ts"() {
1954
+ STATIC_SIDE = {
1955
+ top: "bottom",
1956
+ bottom: "top",
1957
+ left: "right",
1958
+ right: "left"
1959
+ };
1960
+ }
1961
+ });
1537
1962
  var TooltipRenderer;
1538
1963
  var init_tooltip_renderer = __esm({
1539
1964
  "src/plugins/guides/renderers/tooltip-renderer.ts"() {
1540
1965
  init_block_builder();
1541
1966
  init_wait_for_element();
1542
1967
  init_base_renderer();
1968
+ init_floating_arrow();
1543
1969
  TooltipRenderer = class extends BaseRenderer {
1544
1970
  async render(context) {
1545
1971
  const step = context.guide.guideSteps[0];
@@ -1569,14 +1995,18 @@ var init_tooltip_renderer = __esm({
1569
1995
  onDismiss: () => dismiss("dismissed")
1570
1996
  });
1571
1997
  tooltip.appendChild(content);
1998
+ const arrowEl = ownerDocument.createElement("div");
1999
+ arrowEl.className = "veo-tooltip-arrow";
2000
+ tooltip.appendChild(arrowEl);
1572
2001
  root.appendChild(tooltip);
1573
2002
  const updatePosition = async () => {
1574
- const { x, y } = await dom.computePosition(anchor, tooltip, {
2003
+ const { x, y, placement, middlewareData } = await dom.computePosition(anchor, tooltip, {
1575
2004
  placement: "bottom",
1576
- middleware: [dom.offset(8), dom.flip(), dom.shift({ padding: 8 })]
2005
+ middleware: [dom.offset(10), dom.flip(), dom.shift({ padding: 8 }), dom.arrow({ element: arrowEl })]
1577
2006
  });
1578
2007
  tooltip.style.left = `${x}px`;
1579
2008
  tooltip.style.top = `${y}px`;
2009
+ positionArrow(arrowEl, placement, middlewareData.arrow);
1580
2010
  };
1581
2011
  await updatePosition();
1582
2012
  const reposition = () => {
@@ -1597,98 +2027,14 @@ var init_tooltip_renderer = __esm({
1597
2027
  };
1598
2028
  }
1599
2029
  });
1600
-
1601
- // src/plugins/guides/walkthrough-block-builder.ts
1602
- function buildWalkthroughStepContent(step, stepIndex, totalSteps, doc, callbacks) {
1603
- const container = doc.createElement("div");
1604
- container.className = "veo-guide-content";
1605
- const counter = doc.createElement("div");
1606
- counter.className = "veo-walkthrough-counter";
1607
- counter.textContent = `Paso ${stepIndex + 1} de ${totalSteps}`;
1608
- container.appendChild(counter);
1609
- const progress = doc.createElement("div");
1610
- progress.className = "veo-walkthrough-progress";
1611
- for (let i = 0; i < totalSteps; i++) {
1612
- const dot = doc.createElement("span");
1613
- dot.className = "veo-walkthrough-progress-dot";
1614
- if (i < stepIndex) dot.classList.add("completed");
1615
- if (i === stepIndex) dot.classList.add("active");
1616
- progress.appendChild(dot);
1617
- }
1618
- container.appendChild(progress);
1619
- if (typeof step.imageUrl === "string" && step.imageUrl && isSafeUrl(step.imageUrl)) {
1620
- const img = doc.createElement("img");
1621
- img.className = "veo-guide-image";
1622
- img.src = step.imageUrl;
1623
- img.alt = typeof step.title === "string" ? step.title : "";
1624
- container.appendChild(img);
1625
- }
1626
- if (typeof step.title === "string" && step.title) {
1627
- const heading = doc.createElement("h2");
1628
- heading.className = "veo-guide-title";
1629
- heading.textContent = step.title;
1630
- container.appendChild(heading);
1631
- }
1632
- if (typeof step.content === "string" && step.content) {
1633
- const paragraph = doc.createElement("p");
1634
- paragraph.className = "veo-guide-text";
1635
- paragraph.textContent = step.content;
1636
- container.appendChild(paragraph);
1637
- }
1638
- const actions = doc.createElement("div");
1639
- actions.className = "veo-walkthrough-actions";
1640
- const skipBtn = doc.createElement("button");
1641
- skipBtn.type = "button";
1642
- skipBtn.className = "veo-walkthrough-skip";
1643
- skipBtn.textContent = "Omitir";
1644
- skipBtn.addEventListener("click", () => callbacks.onSkip());
1645
- actions.appendChild(skipBtn);
1646
- const rightGroup = doc.createElement("div");
1647
- rightGroup.className = "veo-walkthrough-actions-right";
1648
- if (stepIndex > 0) {
1649
- const backBtn = doc.createElement("button");
1650
- backBtn.type = "button";
1651
- backBtn.className = "veo-walkthrough-btn-secondary";
1652
- backBtn.textContent = "Atr\xE1s";
1653
- backBtn.addEventListener("click", () => callbacks.onBack());
1654
- rightGroup.appendChild(backBtn);
1655
- }
1656
- const isLastStep = stepIndex === totalSteps - 1;
1657
- const primaryBtn = doc.createElement("button");
1658
- primaryBtn.type = "button";
1659
- primaryBtn.className = "veo-guide-cta";
1660
- const defaultLabel = isLastStep ? "Finalizar" : "Siguiente";
1661
- primaryBtn.textContent = typeof step.ctaText === "string" && step.ctaText ? step.ctaText : defaultLabel;
1662
- primaryBtn.addEventListener("click", () => {
1663
- if (isLastStep) callbacks.onComplete();
1664
- else callbacks.onNext();
1665
- });
1666
- rightGroup.appendChild(primaryBtn);
1667
- actions.appendChild(rightGroup);
1668
- container.appendChild(actions);
1669
- return container;
1670
- }
1671
- var init_walkthrough_block_builder = __esm({
1672
- "src/plugins/guides/walkthrough-block-builder.ts"() {
1673
- init_block_builder();
1674
- }
1675
- });
1676
- function positionArrow(arrowEl, placement, data) {
1677
- const side = STATIC_SIDE[placement.split("-")[0] ?? "bottom"] ?? "top";
1678
- for (const prop of ["top", "bottom", "left", "right"]) {
1679
- arrowEl.style.setProperty(prop, "");
1680
- }
1681
- if (data?.x != null) arrowEl.style.setProperty("left", `${data.x}px`);
1682
- if (data?.y != null) arrowEl.style.setProperty("top", `${data.y}px`);
1683
- arrowEl.style.setProperty(side, "-6px");
1684
- }
1685
- var WalkthroughRenderer, STATIC_SIDE;
2030
+ var WalkthroughRenderer;
1686
2031
  var init_walkthrough_renderer = __esm({
1687
2032
  "src/plugins/guides/renderers/walkthrough-renderer.ts"() {
1688
2033
  init_constants2();
1689
2034
  init_wait_for_element();
1690
2035
  init_walkthrough_block_builder();
1691
2036
  init_base_renderer();
2037
+ init_floating_arrow();
1692
2038
  WalkthroughRenderer = class extends BaseRenderer {
1693
2039
  async render(context) {
1694
2040
  const step = context.guide.guideSteps[context.currentStepIndex];
@@ -1736,12 +2082,6 @@ var init_walkthrough_renderer = __esm({
1736
2082
  return true;
1737
2083
  }
1738
2084
  };
1739
- STATIC_SIDE = {
1740
- top: "bottom",
1741
- bottom: "top",
1742
- left: "right",
1743
- right: "left"
1744
- };
1745
2085
  }
1746
2086
  });
1747
2087
 
@@ -1819,6 +2159,7 @@ var init_guide_preview = __esm({
1819
2159
  GuidePreviewController = class {
1820
2160
  constructor() {
1821
2161
  this.singleStep = null;
2162
+ /** Renderer del paso de walkthrough activo (walkthrough tooltip o modal/banner). */
1822
2163
  this.walkthrough = null;
1823
2164
  this.walkthroughGuide = null;
1824
2165
  this.walkthroughIndex = 0;
@@ -1891,7 +2232,6 @@ var init_guide_preview = __esm({
1891
2232
  safeDestroy(this.walkthrough);
1892
2233
  this.walkthrough = null;
1893
2234
  }
1894
- const renderer = new WalkthroughRenderer();
1895
2235
  const callbacks = {
1896
2236
  onNext: () => {
1897
2237
  const next = this.walkthroughIndex + 1;
@@ -1911,6 +2251,30 @@ var init_guide_preview = __esm({
1911
2251
  onSkip: () => this.close(),
1912
2252
  onComplete: () => this.close()
1913
2253
  };
2254
+ const step = guide.guideSteps[index];
2255
+ const render = step?.style?.render;
2256
+ if (render === "modal" || render === "banner") {
2257
+ const renderer2 = render === "modal" ? new ModalRenderer() : new BannerRenderer();
2258
+ try {
2259
+ renderer2.render({
2260
+ guide,
2261
+ onInteraction: () => {
2262
+ },
2263
+ onClose: () => this.close(),
2264
+ nav: { stepIndex: index, totalSteps: guide.guideSteps.length, callbacks }
2265
+ });
2266
+ } catch {
2267
+ safeDestroy(renderer2);
2268
+ return false;
2269
+ }
2270
+ if (this.walkthroughGuide !== guide) {
2271
+ safeDestroy(renderer2);
2272
+ return false;
2273
+ }
2274
+ this.walkthrough = renderer2;
2275
+ return true;
2276
+ }
2277
+ const renderer = new WalkthroughRenderer();
1914
2278
  let success = false;
1915
2279
  try {
1916
2280
  success = await renderer.render({ guide, currentStepIndex: index, callbacks });
@@ -3978,6 +4342,11 @@ var GuidesController = class {
3978
4342
  */
3979
4343
  this.activeByGuideId = /* @__PURE__ */ new Map();
3980
4344
  this.dispatched = /* @__PURE__ */ new Set();
4345
+ /**
4346
+ * Renderer del paso de walkthrough activo. Puede ser el `WalkthroughRenderer`
4347
+ * (tooltip anclado) o un Modal/Banner con navegación (walkthrough heterogéneo);
4348
+ * tipamos por `destroy()`, lo único que el controller necesita.
4349
+ */
3981
4350
  this.activeWalkthroughRenderer = null;
3982
4351
  this.activeWalkthroughGuide = null;
3983
4352
  this.debug = config.debug === true;
@@ -4158,7 +4527,6 @@ var GuidesController = class {
4158
4527
  this.activeWalkthroughRenderer.destroy();
4159
4528
  this.activeWalkthroughRenderer = null;
4160
4529
  }
4161
- const renderer = new WalkthroughRenderer();
4162
4530
  this.activeWalkthroughGuide = guide;
4163
4531
  const callbacks = {
4164
4532
  onNext: () => this.handleWalkthroughNext(endUserId, sessionId, pageUrl),
@@ -4166,6 +4534,28 @@ var GuidesController = class {
4166
4534
  onSkip: () => this.handleWalkthroughSkip(endUserId, sessionId, pageUrl),
4167
4535
  onComplete: () => this.handleWalkthroughComplete(endUserId, sessionId, pageUrl)
4168
4536
  };
4537
+ const step = guide.guideSteps[stepIndex];
4538
+ const render = readStepRender(step);
4539
+ if (render === "modal" || render === "banner") {
4540
+ const renderer2 = render === "modal" ? new ModalRenderer() : new BannerRenderer();
4541
+ try {
4542
+ renderer2.render({
4543
+ guide,
4544
+ onInteraction: () => {
4545
+ },
4546
+ onClose: () => {
4547
+ },
4548
+ nav: { stepIndex, totalSteps: guide.guideSteps.length, callbacks }
4549
+ });
4550
+ } catch (err) {
4551
+ if (this.debug) console.error("[veo] sequence renderer threw:", err);
4552
+ this.tearDownWalkthrough();
4553
+ return false;
4554
+ }
4555
+ this.activeWalkthroughRenderer = renderer2;
4556
+ return true;
4557
+ }
4558
+ const renderer = new WalkthroughRenderer();
4169
4559
  let success = false;
4170
4560
  try {
4171
4561
  success = await renderer.render({ guide, currentStepIndex: stepIndex, callbacks });
@@ -4388,6 +4778,10 @@ function extractPath(url) {
4388
4778
  return "/";
4389
4779
  }
4390
4780
  }
4781
+ function readStepRender(step) {
4782
+ const r = step?.style?.render;
4783
+ return typeof r === "string" ? r : "walkthrough";
4784
+ }
4391
4785
 
4392
4786
  // src/plugins/guides/index.ts
4393
4787
  init_guide_preview();