veo-sdk 0.5.33 → 0.6.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.
- package/dist/builder-3QPHB2VJ.mjs +5 -0
- package/dist/{builder-MUTQMYAH.mjs.map → builder-3QPHB2VJ.mjs.map} +1 -1
- package/dist/builder.cjs +2470 -577
- package/dist/builder.cjs.map +1 -1
- package/dist/builder.d.cts +92 -4
- package/dist/builder.d.ts +92 -4
- package/dist/builder.mjs +4 -4
- package/dist/{chunk-FZ72JUAR.mjs → chunk-GMXQMZ4X.mjs} +123 -27
- package/dist/chunk-GMXQMZ4X.mjs.map +1 -0
- package/dist/{chunk-3ZOKCQ54.mjs → chunk-N4VFMZFA.mjs} +917 -44
- package/dist/chunk-N4VFMZFA.mjs.map +1 -0
- package/dist/{chunk-M6PB5UMW.mjs → chunk-O6USA4TU.mjs} +1133 -99
- package/dist/chunk-O6USA4TU.mjs.map +1 -0
- package/dist/{guide-preview-C73rKO0u.d.cts → guide-preview-DylsEhrg.d.cts} +109 -6
- package/dist/{guide-preview-C73rKO0u.d.ts → guide-preview-DylsEhrg.d.ts} +109 -6
- package/dist/index.cjs +2213 -164
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +5 -5
- package/dist/veo-builder.js +261 -28
- package/dist/veo-builder.js.map +1 -1
- package/dist/veo-guides.js +228 -16
- package/dist/veo-guides.js.map +1 -1
- package/package.json +1 -1
- package/dist/builder-MUTQMYAH.mjs +0 -5
- package/dist/chunk-3ZOKCQ54.mjs.map +0 -1
- package/dist/chunk-FZ72JUAR.mjs.map +0 -1
- package/dist/chunk-M6PB5UMW.mjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -422,6 +422,23 @@ var init_activation_devices = __esm({
|
|
|
422
422
|
}
|
|
423
423
|
});
|
|
424
424
|
|
|
425
|
+
// src/plugins/guides/activation-rules.ts
|
|
426
|
+
function isAutomaticActivation(rules) {
|
|
427
|
+
const trigger = rules?.trigger ?? "immediate";
|
|
428
|
+
if (trigger === "immediate" || trigger === "delayed") return true;
|
|
429
|
+
if (trigger === "on_click" || trigger === "on_hover") return rules?.autoShow === true;
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
function activationDelayMs(rules) {
|
|
433
|
+
if (!isAutomaticActivation(rules)) return 0;
|
|
434
|
+
const ms = rules?.delayMs ?? 0;
|
|
435
|
+
return Number.isFinite(ms) && ms > 0 ? ms : 0;
|
|
436
|
+
}
|
|
437
|
+
var init_activation_rules = __esm({
|
|
438
|
+
"src/plugins/guides/activation-rules.ts"() {
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
|
|
425
442
|
// src/plugins/guides/constants.ts
|
|
426
443
|
var GUIDE_Z_INDEX, DEFAULT_ANCHOR_WAIT_MS, SAFE_URL_PROTOCOLS, GUIDE_HOST_ATTR, FREQUENCY_CACHE_KEY_PREFIX, DEFAULT_TRACKER_FLUSH_INTERVAL_MS, DEFAULT_TRACKER_BATCH_SIZE, DEFAULT_TRACKER_MAX_RETRIES, FREQUENCY_CACHE_MAX_ENTRIES, FREQUENCY_CACHE_TTL_MS, WALKTHROUGH_STATE_KEY_PREFIX, WALKTHROUGH_ABANDONMENT_TIMEOUT_MS, WALKTHROUGH_STEP_SELECTOR_TIMEOUT_MS;
|
|
427
444
|
var init_constants2 = __esm({
|
|
@@ -554,6 +571,129 @@ var init_rich_text = __esm({
|
|
|
554
571
|
}
|
|
555
572
|
});
|
|
556
573
|
|
|
574
|
+
// src/plugins/guides/video-block.ts
|
|
575
|
+
function videoEmbedUrl(provider, url) {
|
|
576
|
+
let u;
|
|
577
|
+
try {
|
|
578
|
+
u = new URL(url);
|
|
579
|
+
} catch {
|
|
580
|
+
return null;
|
|
581
|
+
}
|
|
582
|
+
if (u.protocol !== "https:" && u.protocol !== "http:") return null;
|
|
583
|
+
const host = u.hostname.replace(/^www\./, "").replace(/^m\./, "");
|
|
584
|
+
const id = (re, from) => {
|
|
585
|
+
const m = from.match(re);
|
|
586
|
+
return m?.[1] ?? null;
|
|
587
|
+
};
|
|
588
|
+
switch (provider) {
|
|
589
|
+
case "youtube": {
|
|
590
|
+
let v = null;
|
|
591
|
+
if (host === "youtube.com" || host === "youtube-nocookie.com") {
|
|
592
|
+
v = u.searchParams.get("v") ?? id(/^\/(?:embed|shorts|live)\/([A-Za-z0-9_-]{6,})/, u.pathname);
|
|
593
|
+
} else if (host === "youtu.be") {
|
|
594
|
+
v = id(/^\/([A-Za-z0-9_-]{6,})/, u.pathname);
|
|
595
|
+
}
|
|
596
|
+
if (!v || !/^[A-Za-z0-9_-]{6,}$/.test(v)) return null;
|
|
597
|
+
return `https://www.youtube-nocookie.com/embed/${v}`;
|
|
598
|
+
}
|
|
599
|
+
case "vimeo": {
|
|
600
|
+
if (host !== "vimeo.com" && host !== "player.vimeo.com") return null;
|
|
601
|
+
const v = id(/(\d{6,})/, u.pathname);
|
|
602
|
+
return v ? `https://player.vimeo.com/video/${v}` : null;
|
|
603
|
+
}
|
|
604
|
+
case "loom": {
|
|
605
|
+
if (host !== "loom.com") return null;
|
|
606
|
+
const v = id(/^\/(?:share|embed)\/([A-Za-z0-9]{16,})/, u.pathname);
|
|
607
|
+
return v ? `https://www.loom.com/embed/${v}` : null;
|
|
608
|
+
}
|
|
609
|
+
case "wistia": {
|
|
610
|
+
if (!under(host, "wistia.com") && !under(host, "wistia.net")) return null;
|
|
611
|
+
const v = id(/\/(?:medias|embed\/iframe)\/([A-Za-z0-9]{6,})/, u.pathname);
|
|
612
|
+
return v ? `https://fast.wistia.net/embed/iframe/${v}` : null;
|
|
613
|
+
}
|
|
614
|
+
case "msstream": {
|
|
615
|
+
if (!under(host, "microsoftstream.com")) return null;
|
|
616
|
+
const v = id(/\/(?:embed\/)?video\/([0-9a-f-]{36})/i, u.pathname);
|
|
617
|
+
return v ? `https://web.microsoftstream.com/embed/video/${v}?autoplay=false&showinfo=true` : null;
|
|
618
|
+
}
|
|
619
|
+
case "vidyard": {
|
|
620
|
+
if (!under(host, "vidyard.com")) return null;
|
|
621
|
+
const v = id(/\/watch\/([A-Za-z0-9_-]{8,})/, u.pathname) ?? (host === "play.vidyard.com" ? id(/^\/([A-Za-z0-9_-]{8,})/, u.pathname) : null);
|
|
622
|
+
return v ? `https://play.vidyard.com/${v}` : null;
|
|
623
|
+
}
|
|
624
|
+
case "sharepoint":
|
|
625
|
+
return u.protocol === "https:" && under(host, "sharepoint.com") ? u.href : null;
|
|
626
|
+
case "brightcove":
|
|
627
|
+
return u.protocol === "https:" && host === "players.brightcove.net" && u.searchParams.get("videoId") ? u.href : null;
|
|
628
|
+
case "kaltura":
|
|
629
|
+
return u.protocol === "https:" && under(host, "kaltura.com") && u.pathname.startsWith("/p/") ? u.href : null;
|
|
630
|
+
default:
|
|
631
|
+
return null;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
function under(host, domain) {
|
|
635
|
+
return host === domain || host.endsWith(`.${domain}`);
|
|
636
|
+
}
|
|
637
|
+
function fileVideoUrl(url) {
|
|
638
|
+
try {
|
|
639
|
+
const u = new URL(url);
|
|
640
|
+
if (u.protocol !== "https:" && u.protocol !== "http:") return null;
|
|
641
|
+
return /\.(mp4|webm|mov)$/i.test(u.pathname) ? u.href : null;
|
|
642
|
+
} catch {
|
|
643
|
+
return null;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
function buildVideoBlock(block, doc, opts = {}) {
|
|
647
|
+
const isFile = block.provider === "file";
|
|
648
|
+
const src = typeof block.url === "string" ? isFile ? fileVideoUrl(block.url) : videoEmbedUrl(block.provider, block.url) : null;
|
|
649
|
+
if (!src) return null;
|
|
650
|
+
const s = block.style ?? {};
|
|
651
|
+
const wrap = doc.createElement("div");
|
|
652
|
+
wrap.className = "veo-guide-video";
|
|
653
|
+
const ratio = s.aspect === "4:3" ? "4 / 3" : "16 / 9";
|
|
654
|
+
wrap.style.aspectRatio = ratio;
|
|
655
|
+
if (s.widthMode === "fill") {
|
|
656
|
+
wrap.style.width = "100%";
|
|
657
|
+
} else {
|
|
658
|
+
const w = typeof s.width === "number" && Number.isFinite(s.width) ? s.width : 400;
|
|
659
|
+
wrap.style.width = `${Math.max(120, Math.min(1600, w))}px`;
|
|
660
|
+
wrap.style.maxWidth = "100%";
|
|
661
|
+
}
|
|
662
|
+
const align = s.align === "center" ? "center" : s.align === "right" ? "flex-end" : "flex-start";
|
|
663
|
+
wrap.style.alignSelf = align;
|
|
664
|
+
if (isFile) {
|
|
665
|
+
const video = doc.createElement("video");
|
|
666
|
+
video.src = src;
|
|
667
|
+
video.controls = true;
|
|
668
|
+
video.setAttribute("playsinline", "");
|
|
669
|
+
video.preload = "metadata";
|
|
670
|
+
video.setAttribute(
|
|
671
|
+
"aria-label",
|
|
672
|
+
typeof block.alt === "string" && block.alt.trim() ? block.alt.trim() : "Video"
|
|
673
|
+
);
|
|
674
|
+
if (opts.editing) video.style.pointerEvents = "none";
|
|
675
|
+
wrap.appendChild(video);
|
|
676
|
+
return wrap;
|
|
677
|
+
}
|
|
678
|
+
const frame = doc.createElement("iframe");
|
|
679
|
+
frame.src = src;
|
|
680
|
+
frame.title = typeof block.alt === "string" && block.alt.trim() ? block.alt.trim() : "Video";
|
|
681
|
+
frame.setAttribute("loading", "lazy");
|
|
682
|
+
frame.setAttribute("referrerpolicy", "strict-origin-when-cross-origin");
|
|
683
|
+
frame.setAttribute(
|
|
684
|
+
"allow",
|
|
685
|
+
`accelerometer; autoplay; clipboard-write; encrypted-media; picture-in-picture${opts.editing ? "" : "; fullscreen"}`
|
|
686
|
+
);
|
|
687
|
+
if (!opts.editing) frame.setAttribute("allowfullscreen", "");
|
|
688
|
+
if (opts.editing) frame.style.pointerEvents = "none";
|
|
689
|
+
wrap.appendChild(frame);
|
|
690
|
+
return wrap;
|
|
691
|
+
}
|
|
692
|
+
var init_video_block = __esm({
|
|
693
|
+
"src/plugins/guides/video-block.ts"() {
|
|
694
|
+
}
|
|
695
|
+
});
|
|
696
|
+
|
|
557
697
|
// src/plugins/guides/block-builder.ts
|
|
558
698
|
function buildStepContent(step, doc, callbacks) {
|
|
559
699
|
const container = doc.createElement("div");
|
|
@@ -678,8 +818,12 @@ function renderBlocks(container, blocks, doc, callbacks) {
|
|
|
678
818
|
i++;
|
|
679
819
|
}
|
|
680
820
|
}
|
|
681
|
-
function buildContentBlock(block, doc) {
|
|
821
|
+
function buildContentBlock(block, doc, opts = {}) {
|
|
682
822
|
switch (block.type) {
|
|
823
|
+
case "video":
|
|
824
|
+
return buildVideoBlock(block, doc, opts);
|
|
825
|
+
case "divider":
|
|
826
|
+
return buildDividerBlock(block, doc);
|
|
683
827
|
case "title": {
|
|
684
828
|
const el2 = doc.createElement("h2");
|
|
685
829
|
el2.className = "veo-guide-title";
|
|
@@ -703,7 +847,7 @@ function buildContentBlock(block, doc) {
|
|
|
703
847
|
const el2 = doc.createElement("img");
|
|
704
848
|
el2.className = "veo-guide-image";
|
|
705
849
|
el2.src = block.url;
|
|
706
|
-
el2.alt = "";
|
|
850
|
+
el2.alt = typeof block.alt === "string" ? block.alt : "";
|
|
707
851
|
applyBlockStyle(el2, block.style, "image");
|
|
708
852
|
return el2;
|
|
709
853
|
}
|
|
@@ -711,6 +855,31 @@ function buildContentBlock(block, doc) {
|
|
|
711
855
|
return null;
|
|
712
856
|
}
|
|
713
857
|
}
|
|
858
|
+
function buildDividerBlock(block, doc) {
|
|
859
|
+
const s = block.style ?? {};
|
|
860
|
+
const wrap = doc.createElement("div");
|
|
861
|
+
wrap.className = "veo-guide-divider";
|
|
862
|
+
wrap.setAttribute("role", "separator");
|
|
863
|
+
const hr = doc.createElement("hr");
|
|
864
|
+
const custom = s.preset === "custom";
|
|
865
|
+
const color = custom && typeof s.color === "string" && s.color ? s.color : s.colorMode === "dark" ? "rgba(255, 255, 255, 0.35)" : "#e5e7eb";
|
|
866
|
+
const thickness = custom && typeof s.thickness === "number" && Number.isFinite(s.thickness) ? clampNum(Math.round(s.thickness), 1, 12) : 1;
|
|
867
|
+
const lineStyle = typeof s.lineStyle === "string" && DIVIDER_LINE_STYLES.has(s.lineStyle) ? s.lineStyle : "solid";
|
|
868
|
+
hr.style.borderTop = `${thickness}px ${lineStyle} ${color}`;
|
|
869
|
+
wrap.appendChild(hr);
|
|
870
|
+
for (const [k, prop] of [
|
|
871
|
+
["marginTop", "marginTop"],
|
|
872
|
+
["marginRight", "marginRight"],
|
|
873
|
+
["marginBottom", "marginBottom"],
|
|
874
|
+
["marginLeft", "marginLeft"]
|
|
875
|
+
]) {
|
|
876
|
+
const v = s[k];
|
|
877
|
+
if (typeof v === "number" && Number.isFinite(v)) {
|
|
878
|
+
wrap.style[prop] = `${clampNum(v, 0, 64)}px`;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
return wrap;
|
|
882
|
+
}
|
|
714
883
|
function buildButtonBlock(block, doc, callbacks) {
|
|
715
884
|
const btn = doc.createElement("button");
|
|
716
885
|
btn.type = "button";
|
|
@@ -741,14 +910,34 @@ function applyBlockStyle(el2, style, kind) {
|
|
|
741
910
|
}
|
|
742
911
|
const align = typeof s.align === "string" ? s.align : null;
|
|
743
912
|
if (align && (align === "left" || align === "center" || align === "right")) {
|
|
744
|
-
if (kind === "text")
|
|
745
|
-
|
|
913
|
+
if (kind === "text") {
|
|
914
|
+
el2.style.textAlign = align;
|
|
915
|
+
el2.style.alignSelf = BLOCK_ALIGN_SELF[align];
|
|
916
|
+
} else if (kind === "image") el2.style.alignSelf = BLOCK_ALIGN_SELF[align];
|
|
917
|
+
}
|
|
918
|
+
if (kind === "text" && typeof s.preset === "string" && s.preset in TEXT_PRESETS) {
|
|
919
|
+
const preset = TEXT_PRESETS[s.preset];
|
|
920
|
+
if (preset) {
|
|
921
|
+
el2.style.fontSize = `${preset.size}px`;
|
|
922
|
+
el2.style.fontWeight = String(preset.weight);
|
|
923
|
+
el2.style.lineHeight = String(preset.lineHeight);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
if (kind !== "image" && s.colorMode === "dark") {
|
|
927
|
+
el2.style.background = "#1f1d1b";
|
|
928
|
+
el2.style.color = "#f5f0eb";
|
|
929
|
+
el2.style.borderRadius = el2.style.borderRadius || "8px";
|
|
746
930
|
}
|
|
747
931
|
if (kind !== "image" && typeof s.color === "string") el2.style.color = s.color;
|
|
748
932
|
if (kind === "text" && typeof s.fontSize === "number" && Number.isFinite(s.fontSize)) {
|
|
749
933
|
el2.style.fontSize = `${clampNum(s.fontSize, 8, 72)}px`;
|
|
750
934
|
}
|
|
751
|
-
if (kind === "image" &&
|
|
935
|
+
if (kind === "image" && s.naturalSize === true) {
|
|
936
|
+
el2.style.width = "auto";
|
|
937
|
+
el2.style.maxWidth = "100%";
|
|
938
|
+
el2.style.maxHeight = "none";
|
|
939
|
+
el2.style.objectFit = "contain";
|
|
940
|
+
} else if (kind === "image" && typeof s.width === "number" && Number.isFinite(s.width)) {
|
|
752
941
|
el2.style.width = `${clampNum(s.width, 10, 100)}%`;
|
|
753
942
|
}
|
|
754
943
|
if (kind === "image" && typeof s.radius === "number" && Number.isFinite(s.radius)) {
|
|
@@ -827,16 +1016,24 @@ function readCtaUrl(step) {
|
|
|
827
1016
|
if (typeof candidate !== "string") return void 0;
|
|
828
1017
|
return isSafeUrl(candidate) ? candidate : void 0;
|
|
829
1018
|
}
|
|
830
|
-
var BLOCK_ALIGN_SELF;
|
|
1019
|
+
var DIVIDER_LINE_STYLES, BLOCK_ALIGN_SELF, TEXT_PRESETS;
|
|
831
1020
|
var init_block_builder = __esm({
|
|
832
1021
|
"src/plugins/guides/block-builder.ts"() {
|
|
833
1022
|
init_constants2();
|
|
834
1023
|
init_rich_text();
|
|
1024
|
+
init_video_block();
|
|
1025
|
+
DIVIDER_LINE_STYLES = /* @__PURE__ */ new Set(["solid", "dotted", "dashed"]);
|
|
835
1026
|
BLOCK_ALIGN_SELF = {
|
|
836
1027
|
left: "flex-start",
|
|
837
1028
|
center: "center",
|
|
838
1029
|
right: "flex-end"
|
|
839
1030
|
};
|
|
1031
|
+
TEXT_PRESETS = {
|
|
1032
|
+
title: { size: 18, weight: 600, lineHeight: 1.3 },
|
|
1033
|
+
subtitle: { size: 16, weight: 600, lineHeight: 1.35 },
|
|
1034
|
+
paragraph: { size: 14, weight: 400, lineHeight: 1.5 },
|
|
1035
|
+
small: { size: 12, weight: 400, lineHeight: 1.5 }
|
|
1036
|
+
};
|
|
840
1037
|
}
|
|
841
1038
|
});
|
|
842
1039
|
|
|
@@ -1199,7 +1396,161 @@ var init_styles = __esm({
|
|
|
1199
1396
|
}
|
|
1200
1397
|
/* Card gen\xE9rica del modelo unificado. El dise\xF1o (bg/borde/padding/\u2026) lo aplica
|
|
1201
1398
|
el renderer como estilos inline desde step.design; ac\xE1 van los defaults. */
|
|
1399
|
+
/* Edici\xF3n (builder): sin animaci\xF3n de entrada al repintar la preview. */
|
|
1400
|
+
:host(.veo-no-anim) * { animation: none !important; }
|
|
1401
|
+
|
|
1402
|
+
/* Bloque video: caja con relaci\xF3n de aspecto; el iframe la llena. */
|
|
1403
|
+
.veo-guide-video { position: relative; overflow: hidden; border-radius: 8px; background: #000; }
|
|
1404
|
+
.veo-guide-video iframe, .veo-guide-video video {
|
|
1405
|
+
position: absolute; inset: 0; width: 100%; height: 100%; border: 0; display: block;
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
/* \u2500\u2500 Layout por filas/columnas (composite) \u2500\u2500
|
|
1409
|
+
El espacio tiene UNA fuente por capa: el aire con el borde lo da el padding
|
|
1410
|
+
de la card; la separaci\xF3n entre filas/bloques la da el margen inferior
|
|
1411
|
+
(12px, ah\xED vive la l\xEDnea \xAB+\xBB del editor); los bloques NO llevan padding
|
|
1412
|
+
propio (solo el que el autor les ponga inline). El \xFAltimo elemento no deja
|
|
1413
|
+
margen colgando. */
|
|
1414
|
+
.veo-card > [data-veo-block], .veo-card > .veo-row, .veo-card > .veo-guide-actions,
|
|
1415
|
+
.veo-col > [data-veo-block], .veo-col > .veo-guide-actions { margin-bottom: 12px; }
|
|
1416
|
+
/* La card y cada columna son columnas flex: cada bloque mide SU contenido (como
|
|
1417
|
+
el bot\xF3n) y se ubica con su alineaci\xF3n (align-self); filas, columnas, formularios
|
|
1418
|
+
y filas de botones ocupan todo el ancho. */
|
|
1419
|
+
.veo-card:not(.veo-card--empty), .veo-col {
|
|
1420
|
+
display: flex; flex-direction: column; align-items: flex-start;
|
|
1421
|
+
}
|
|
1422
|
+
.veo-card > [data-veo-block], .veo-col > [data-veo-block] { max-width: 100%; }
|
|
1423
|
+
/* L\xEDnea horizontal: ocupa todo el ancho; el padding del bloque es solo vertical
|
|
1424
|
+
(zona clickeable en el editor) y la l\xEDnea es el borde superior del hr. */
|
|
1425
|
+
.veo-card > .veo-guide-divider, .veo-col > .veo-guide-divider { padding: 6px 0; align-self: stretch; }
|
|
1426
|
+
.veo-guide-divider { box-sizing: border-box; }
|
|
1427
|
+
/* Bloque de c\xF3digo (modo host): ocupa todo el ancho, como un formulario. */
|
|
1428
|
+
.veo-code-host { display: block; min-width: 0; }
|
|
1429
|
+
.veo-card > .veo-code-host, .veo-col > .veo-code-host { align-self: stretch; }
|
|
1430
|
+
.veo-guide-divider hr { margin: 0; border: 0; height: 0; }
|
|
1431
|
+
.veo-card > .veo-row, .veo-card > .veo-guide-actions, .veo-card > .veo-form,
|
|
1432
|
+
.veo-col > .veo-guide-actions, .veo-col > .veo-form { align-self: stretch; }
|
|
1433
|
+
.veo-row { display: flex; gap: 12px; align-items: flex-start; box-sizing: border-box; }
|
|
1434
|
+
.veo-col { flex: 1 1 0; min-width: 0; }
|
|
1435
|
+
.veo-col > :last-child { margin-bottom: 0; }
|
|
1436
|
+
/* Fila con alineaci\xF3n horizontal (editor de fila): las columnas miden su contenido. */
|
|
1437
|
+
.veo-row--aligned > .veo-col { flex: 0 1 auto; }
|
|
1438
|
+
|
|
1439
|
+
/* \u2500\u2500 Editor in-app: bloque seleccionado + barra de acciones + placeholders \u2500\u2500 */
|
|
1440
|
+
.veo-block-placeholder {
|
|
1441
|
+
border: 1px dashed #b0a9a5; border-radius: 6px; padding: 12px; margin-bottom: 12px;
|
|
1442
|
+
color: #8a8582; font-size: 13px; text-align: center;
|
|
1443
|
+
}
|
|
1444
|
+
/* El bloque seleccionado no lleva contorno propio: lo rodea .veo-block-frame
|
|
1445
|
+
(marco aparte, con aire y alto m\xEDnimo, que no altera el layout). */
|
|
1446
|
+
.veo-block--selected { position: relative; z-index: 1; }
|
|
1447
|
+
.veo-block-frame {
|
|
1448
|
+
position: absolute; z-index: 4; pointer-events: none; box-sizing: border-box;
|
|
1449
|
+
border: 2px solid #3b82f6; border-radius: 10px; background: rgba(59, 130, 246, 0.04);
|
|
1450
|
+
}
|
|
1451
|
+
/* Barra del bloque (mock): p\xEDldora blanca con sombra, solo \xEDconos; la papelera
|
|
1452
|
+
en rojo, separada por una l\xEDnea. */
|
|
1453
|
+
.veo-block-toolbar {
|
|
1454
|
+
position: absolute; z-index: 5; display: inline-flex; align-items: center; gap: 2px; padding: 4px 6px;
|
|
1455
|
+
background: #fff; border: 1px solid rgba(0,0,0,0.06); border-radius: 12px;
|
|
1456
|
+
box-shadow: 0 6px 20px rgba(0,0,0,0.16);
|
|
1457
|
+
}
|
|
1458
|
+
.veo-block-toolbar button {
|
|
1459
|
+
border: 0; background: transparent; color: #4b4642; cursor: pointer; padding: 6px; border-radius: 8px;
|
|
1460
|
+
display: inline-flex; align-items: center; justify-content: center; line-height: 1;
|
|
1461
|
+
}
|
|
1462
|
+
.veo-block-toolbar button:hover { background: #f5efeb; }
|
|
1463
|
+
.veo-block-toolbar button svg { width: 20px; height: 20px; }
|
|
1464
|
+
.veo-block-toolbar .veo-block-toolbar-remove { color: #e5484d; }
|
|
1465
|
+
/* Separadores: antes de la papelera y antes de las flechas de mover. */
|
|
1466
|
+
.veo-block-toolbar .veo-block-toolbar-remove, .veo-block-toolbar .veo-block-toolbar-move-left {
|
|
1467
|
+
margin-left: 6px; position: relative;
|
|
1468
|
+
}
|
|
1469
|
+
.veo-block-toolbar .veo-block-toolbar-remove::before, .veo-block-toolbar .veo-block-toolbar-move-left::before {
|
|
1470
|
+
content: ""; position: absolute; left: -4px; top: 6px; bottom: 6px; width: 1px; background: #e7e2de;
|
|
1471
|
+
}
|
|
1472
|
+
.veo-block-toolbar button:disabled { opacity: 0.3; cursor: default; background: transparent; }
|
|
1473
|
+
/* Contenedor (hover): contorno azul de la tarjeta y dos pesta\xF1as centradas
|
|
1474
|
+
sobre su borde superior: \xABEditar contenedor\xBB (outline) y \xABEditar filas\xBB. */
|
|
1475
|
+
.veo-card-frame {
|
|
1476
|
+
position: absolute; z-index: 3; inset: 0; pointer-events: none; box-sizing: border-box;
|
|
1477
|
+
border: 2px solid #3b82f6; border-radius: inherit;
|
|
1478
|
+
}
|
|
1479
|
+
.veo-card-toolbar {
|
|
1480
|
+
position: absolute; z-index: 6; left: 50%; transform: translateX(-50%);
|
|
1481
|
+
display: inline-flex; align-items: flex-end; gap: 8px; white-space: nowrap;
|
|
1482
|
+
}
|
|
1483
|
+
.veo-card-toolbar button {
|
|
1484
|
+
cursor: pointer; padding: 8px 14px; border-radius: 8px; border: 2px solid #3b82f6;
|
|
1485
|
+
font: 600 14px/1 system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
1486
|
+
background: #fff; color: #3b82f6;
|
|
1487
|
+
}
|
|
1488
|
+
.veo-card-toolbar button:hover { background: #eff6ff; }
|
|
1489
|
+
/* Con fondo = la vista que el panel muestra ahora (contenedor o estructura). */
|
|
1490
|
+
.veo-card-toolbar button.is-active { background: #3b82f6; color: #fff; }
|
|
1491
|
+
.veo-card-toolbar button.is-active:hover { background: #2563eb; border-color: #2563eb; }
|
|
1492
|
+
/* Editor de fila: marco al pasar el mouse por una fila (o la fila del bloque
|
|
1493
|
+
seleccionado) y barra \xABEditar fila \xB7 duplicar\xBB bajo su esquina derecha. */
|
|
1494
|
+
.veo-row-frame {
|
|
1495
|
+
position: absolute; z-index: 3; pointer-events: none; box-sizing: border-box;
|
|
1496
|
+
border: 2px solid #3b82f6; border-radius: 10px;
|
|
1497
|
+
}
|
|
1498
|
+
.veo-row-toolbar {
|
|
1499
|
+
position: absolute; z-index: 5; display: inline-flex; align-items: center; gap: 2px; padding: 4px 6px;
|
|
1500
|
+
background: #fff; border: 1px solid rgba(0,0,0,0.06); border-radius: 12px;
|
|
1501
|
+
box-shadow: 0 6px 20px rgba(0,0,0,0.16); white-space: nowrap;
|
|
1502
|
+
}
|
|
1503
|
+
.veo-row-toolbar button {
|
|
1504
|
+
border: 0; background: transparent; color: #4b4642; cursor: pointer; padding: 6px 8px; border-radius: 8px;
|
|
1505
|
+
display: inline-flex; align-items: center; gap: 8px; line-height: 1;
|
|
1506
|
+
font: 500 14px/1 system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
1507
|
+
}
|
|
1508
|
+
.veo-row-toolbar button:hover { background: #f5efeb; }
|
|
1509
|
+
.veo-row-toolbar button svg { width: 20px; height: 20px; }
|
|
1510
|
+
.veo-row-toolbar .veo-row-toolbar-duplicate { margin-left: 6px; position: relative; }
|
|
1511
|
+
.veo-row-toolbar .veo-row-toolbar-duplicate::before {
|
|
1512
|
+
content: ""; position: absolute; left: -4px; top: 6px; bottom: 6px; width: 1px; background: #e7e2de;
|
|
1513
|
+
}
|
|
1514
|
+
.veo-row-toolbar .veo-row-toolbar-remove { color: #e5484d; }
|
|
1515
|
+
/* Huecos \xAB+\xBB (mock): zona invisible entre elementos; al pasar el mouse aparece
|
|
1516
|
+
la l\xEDnea azul (horizontal = fila / bloque apilado, vertical = columna) con
|
|
1517
|
+
un \xAB+\xBB circular al centro. */
|
|
1518
|
+
.veo-gap { position: absolute; z-index: 4; }
|
|
1519
|
+
.veo-gap i { position: absolute; background: #3b82f6; opacity: 0; transition: opacity 100ms; pointer-events: none; }
|
|
1520
|
+
.veo-gap--h i { left: 0; right: 0; top: 50%; height: 2px; margin-top: -1px; }
|
|
1521
|
+
.veo-gap--v i { top: 0; bottom: 0; left: 50%; width: 2px; margin-left: -1px; }
|
|
1522
|
+
.veo-gap button {
|
|
1523
|
+
position: absolute; left: 50%; top: 50%; margin: -11px 0 0 -11px; box-sizing: border-box;
|
|
1524
|
+
opacity: 0; transition: opacity 100ms; width: 22px; height: 22px; border-radius: 50%; padding: 0;
|
|
1525
|
+
border: 2px solid #fff; background: #3b82f6; color: #fff; cursor: pointer;
|
|
1526
|
+
font: 600 15px/18px system-ui, -apple-system, "Segoe UI", sans-serif; text-align: center;
|
|
1527
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.25);
|
|
1528
|
+
}
|
|
1529
|
+
.veo-gap:hover i, .veo-gap:hover button, .veo-gap:focus-within button { opacity: 1; }
|
|
1530
|
+
.veo-gap button:hover { background: #2563eb; }
|
|
1531
|
+
|
|
1532
|
+
/* Tarjeta sin bloques (paso reci\xE9n creado). La tarjeta NO impone tama\xF1o ni
|
|
1533
|
+
padding propios (as\xED el ancho/padding del dise\xF1o se ven tal cual); el tama\xF1o
|
|
1534
|
+
m\xEDnimo lo pone la zona punteada azul de la preview. */
|
|
1535
|
+
.veo-card--empty { display: flex; align-items: stretch; }
|
|
1536
|
+
.veo-card-empty-zone {
|
|
1537
|
+
flex: 1; min-width: 0; min-height: 160px; margin-top: 14px;
|
|
1538
|
+
display: flex; align-items: center; justify-content: center; gap: 6px; text-align: center;
|
|
1539
|
+
border: 2px dashed #3b82f6; border-radius: 8px; background: #eff6ff;
|
|
1540
|
+
color: #2563eb; font-size: 16px; font-weight: 500; user-select: none; pointer-events: none;
|
|
1541
|
+
padding: 0 12px; box-sizing: border-box; overflow: hidden;
|
|
1542
|
+
}
|
|
1543
|
+
.veo-card-empty-zone b { font-weight: 600; font-size: 22px; line-height: 1; }
|
|
1544
|
+
/* Sin ancho configurado, la zona da un tama\xF1o c\xF3modo por defecto. */
|
|
1545
|
+
.veo-card--empty:not([style*="width"]) .veo-card-empty-zone { min-width: 360px; }
|
|
1546
|
+
/* Tarjeta angosta (la mide el renderer): solo el \xAB+\xBB, sin texto, y m\xE1s baja. */
|
|
1547
|
+
.veo-card--narrow .veo-card-empty-zone span { display: none; }
|
|
1548
|
+
.veo-card--narrow .veo-card-empty-zone { min-height: 120px; }
|
|
1202
1549
|
.veo-card {
|
|
1550
|
+
/* Contenedor posicionado SIEMPRE: el \u2715 (absolute) se ubica respecto a la
|
|
1551
|
+
tarjeta tambi\xE9n en pasos embebidos (inline), no respecto a la app. Los
|
|
1552
|
+
flotantes lo pisan con position:fixed inline. */
|
|
1553
|
+
position: relative;
|
|
1203
1554
|
pointer-events: auto;
|
|
1204
1555
|
background: var(--veo-bg, #ffffff);
|
|
1205
1556
|
color: var(--veo-text, #1f2937);
|
|
@@ -1292,6 +1643,13 @@ var init_styles = __esm({
|
|
|
1292
1643
|
transition: opacity 120ms ease;
|
|
1293
1644
|
}
|
|
1294
1645
|
.veo-guide-cta:hover { opacity: 0.9; }
|
|
1646
|
+
/* Presets del boton (editor): secundario = contorno; enlace = solo texto. */
|
|
1647
|
+
.veo-guide-cta--secondary {
|
|
1648
|
+
background: transparent; color: var(--veo-primary);
|
|
1649
|
+
border: 1px solid var(--veo-primary);
|
|
1650
|
+
}
|
|
1651
|
+
.veo-guide-cta--link { background: transparent; color: var(--veo-primary); padding: 0; }
|
|
1652
|
+
.veo-guide-cta--link:hover { opacity: 1; text-decoration: underline; }
|
|
1295
1653
|
.veo-guide-cta:active { transform: translateY(1px); }
|
|
1296
1654
|
.veo-guide-close {
|
|
1297
1655
|
position: absolute; top: -6px; right: -6px;
|
|
@@ -1322,6 +1680,10 @@ var init_styles = __esm({
|
|
|
1322
1680
|
|
|
1323
1681
|
.veo-form { display: flex; flex-direction: column; gap: 14px; margin-bottom: 16px; }
|
|
1324
1682
|
.veo-form-field { display: flex; flex-direction: column; gap: 6px; }
|
|
1683
|
+
/* Pregunta de la encuesta (rich text del bloque form). */
|
|
1684
|
+
.veo-form-question { font-size: 16px; font-weight: 600; line-height: 1.35; color: var(--veo-text); }
|
|
1685
|
+
.veo-form-question p { margin: 0; }
|
|
1686
|
+
.veo-form > .veo-guide-actions { margin: 0; }
|
|
1325
1687
|
.veo-form-label { font-size: 13px; font-weight: 500; color: var(--veo-text); }
|
|
1326
1688
|
.veo-form-input {
|
|
1327
1689
|
font-size: 14px; color: var(--veo-text);
|
|
@@ -1330,26 +1692,71 @@ var init_styles = __esm({
|
|
|
1330
1692
|
padding: 8px 10px; width: 100%;
|
|
1331
1693
|
}
|
|
1332
1694
|
.veo-form-input:focus { outline: 2px solid var(--veo-primary); outline-offset: -1px; border-color: transparent; }
|
|
1333
|
-
|
|
1695
|
+
/* Lista de tareas (mock): \xEDtems con anillo de progreso, texto + subt\xEDtulo,
|
|
1696
|
+
y al pie \xABX de N completadas\xBB + porcentaje + barra. */
|
|
1697
|
+
.veo-checklist {
|
|
1698
|
+
display: flex; flex-direction: column; gap: 22px; align-self: stretch; width: 100%;
|
|
1699
|
+
min-width: 360px; box-sizing: border-box;
|
|
1700
|
+
}
|
|
1701
|
+
.veo-checklist-title { font-size: 24px; font-weight: 700; line-height: 1.25; color: var(--veo-cl-title); }
|
|
1702
|
+
.veo-checklist-title p { margin: 0; }
|
|
1703
|
+
.veo-checklist-items { display: flex; flex-direction: column; gap: 22px; }
|
|
1704
|
+
.veo-checklist-item {
|
|
1705
|
+
display: flex; align-items: center; gap: 18px; width: 100%; text-align: left;
|
|
1706
|
+
background: none; border: 0; padding: 0; cursor: pointer; font: inherit; color: inherit;
|
|
1707
|
+
}
|
|
1708
|
+
.veo-checklist-item:hover .veo-checklist-label { text-decoration: underline; }
|
|
1709
|
+
.veo-checklist-ring { width: 44px; height: 44px; flex: 0 0 auto; }
|
|
1710
|
+
.veo-checklist-texts { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
|
|
1711
|
+
.veo-checklist-label {
|
|
1712
|
+
font-size: calc(var(--veo-cl-font-size, 14px) + 4px); font-weight: var(--veo-cl-font-weight, 600);
|
|
1713
|
+
line-height: 1.3; color: var(--veo-cl-title);
|
|
1714
|
+
}
|
|
1715
|
+
.veo-checklist-sub { font-size: calc(var(--veo-cl-font-size, 14px) + 1px); color: var(--veo-cl-subtitle); }
|
|
1716
|
+
.veo-checklist-footer { display: flex; flex-direction: column; gap: 10px; margin-top: 6px; }
|
|
1717
|
+
.veo-checklist-summary {
|
|
1718
|
+
display: flex; justify-content: space-between; align-items: baseline; gap: 16px;
|
|
1719
|
+
font-size: calc(var(--veo-cl-font-size, 14px) + 1px);
|
|
1720
|
+
}
|
|
1721
|
+
.veo-checklist-count { color: var(--veo-cl-subtitle); }
|
|
1722
|
+
.veo-checklist-percent { font-weight: 700; font-size: calc(var(--veo-cl-font-size, 14px) + 3px); color: var(--veo-cl-bar); }
|
|
1723
|
+
.veo-checklist-bar { height: var(--veo-cl-bar-h, 8px); background: #e7e2de; border-radius: 999px; overflow: hidden; }
|
|
1724
|
+
.veo-checklist--dark .veo-checklist-bar { background: #3c3734; }
|
|
1725
|
+
.veo-checklist-bar-fill { height: 100%; background: var(--veo-cl-bar); border-radius: 999px; transition: width 300ms ease; }
|
|
1726
|
+
.veo-checklist-empty { margin: 0; font-size: 13px; color: var(--veo-cl-subtitle); }
|
|
1727
|
+
/* Opci\xF3n m\xFAltiple (mock): c\xEDrculos/casillas grandes con su texto al lado. */
|
|
1728
|
+
.veo-form-options { display: flex; flex-direction: column; gap: 14px; }
|
|
1334
1729
|
.veo-form-option {
|
|
1335
|
-
display: flex; align-items: center; gap:
|
|
1336
|
-
font-size:
|
|
1337
|
-
}
|
|
1338
|
-
.veo-form-option input {
|
|
1339
|
-
|
|
1730
|
+
display: flex; align-items: center; gap: 14px;
|
|
1731
|
+
font-size: 16px; color: var(--veo-text); cursor: pointer;
|
|
1732
|
+
}
|
|
1733
|
+
.veo-form-option input {
|
|
1734
|
+
accent-color: var(--veo-primary); margin: 0; cursor: pointer;
|
|
1735
|
+
width: 26px; height: 26px; flex: 0 0 auto;
|
|
1736
|
+
}
|
|
1737
|
+
/* Escala num\xE9rica (mock): casillas cuadradas; con etiquetas, cada casilla
|
|
1738
|
+
lleva la suya debajo (la primera cuelga a la izquierda, la \xFAltima a la derecha). */
|
|
1739
|
+
.veo-form-scale { display: flex; gap: 8px; flex-wrap: wrap; align-items: flex-start; }
|
|
1740
|
+
.veo-scale-item { display: inline-flex; flex-direction: column; align-items: center; gap: 8px; }
|
|
1741
|
+
.veo-scale-item--first { align-items: flex-start; }
|
|
1742
|
+
.veo-scale-item--last { align-items: flex-end; }
|
|
1743
|
+
.veo-scale-label { font-size: 12px; line-height: 1.2; color: #8a8582; white-space: nowrap; }
|
|
1340
1744
|
.veo-nps-btn {
|
|
1341
|
-
min-width:
|
|
1342
|
-
font-size:
|
|
1343
|
-
background: var(--veo-bg); border: 1px solid #
|
|
1745
|
+
min-width: var(--veo-scale-size, 44px); height: var(--veo-scale-size, 44px); padding: 0 6px;
|
|
1746
|
+
font-size: 16px; font-weight: 600; color: var(--veo-text);
|
|
1747
|
+
background: var(--veo-bg); border: 1px solid #d5cec9; border-radius: 8px;
|
|
1344
1748
|
cursor: pointer; transition: all 100ms ease;
|
|
1345
1749
|
}
|
|
1346
|
-
.veo-nps-btn:hover { border-color: var(--veo-primary); }
|
|
1750
|
+
.veo-nps-btn:hover { border-color: var(--veo-scale-color, var(--veo-primary)); }
|
|
1347
1751
|
.veo-rating-btn {
|
|
1348
1752
|
background: none; border: none; padding: 0 2px;
|
|
1349
1753
|
font-size: 26px; line-height: 1; color: #d1d5db;
|
|
1350
1754
|
cursor: pointer; transition: color 100ms ease;
|
|
1351
1755
|
}
|
|
1352
|
-
.veo-scale-active.veo-nps-btn {
|
|
1756
|
+
.veo-scale-active.veo-nps-btn {
|
|
1757
|
+
background: var(--veo-scale-color, var(--veo-primary)); color: #fff;
|
|
1758
|
+
border-color: var(--veo-scale-color, var(--veo-primary));
|
|
1759
|
+
}
|
|
1353
1760
|
.veo-scale-active.veo-rating-btn { color: #f59e0b; }
|
|
1354
1761
|
.veo-form-error { font-size: 13px; color: #dc2626; margin: 0; }
|
|
1355
1762
|
.veo-form-thanks {
|
|
@@ -2057,6 +2464,217 @@ var init_banner_renderer = __esm({
|
|
|
2057
2464
|
}
|
|
2058
2465
|
});
|
|
2059
2466
|
|
|
2467
|
+
// src/plugins/guides/checklist-block.ts
|
|
2468
|
+
function checklistStyle(style) {
|
|
2469
|
+
const s = style ?? {};
|
|
2470
|
+
const str = (k, fb) => typeof s[k] === "string" && s[k].trim() ? s[k] : fb;
|
|
2471
|
+
const num2 = (k, fb, min, max) => typeof s[k] === "number" && Number.isFinite(s[k]) ? Math.min(max, Math.max(min, s[k])) : fb;
|
|
2472
|
+
const weightRaw = s.fontWeight;
|
|
2473
|
+
const fontWeight = typeof weightRaw === "number" ? Math.min(900, Math.max(100, weightRaw)) : typeof weightRaw === "string" && WEIGHTS[weightRaw.toLowerCase()] ? WEIGHTS[weightRaw.toLowerCase()] : CHECKLIST_DEFAULTS.fontWeight;
|
|
2474
|
+
const dark = s.colorMode === "dark";
|
|
2475
|
+
return {
|
|
2476
|
+
colorMode: dark ? "dark" : "light",
|
|
2477
|
+
iconEmptyColor: str("iconEmptyColor", dark ? "#3c3734" : CHECKLIST_DEFAULTS.iconEmptyColor),
|
|
2478
|
+
iconFillColor: str("iconFillColor", CHECKLIST_DEFAULTS.iconFillColor),
|
|
2479
|
+
barColor: str("barColor", CHECKLIST_DEFAULTS.barColor),
|
|
2480
|
+
barHeight: num2("barHeight", CHECKLIST_DEFAULTS.barHeight, 2, 40),
|
|
2481
|
+
fontSize: num2("fontSize", CHECKLIST_DEFAULTS.fontSize, 10, 40),
|
|
2482
|
+
fontWeight,
|
|
2483
|
+
titleColor: str("titleColor", dark ? "#f5efeb" : CHECKLIST_DEFAULTS.titleColor),
|
|
2484
|
+
subtitleColor: str("subtitleColor", dark ? "#b0a9a5" : CHECKLIST_DEFAULTS.subtitleColor),
|
|
2485
|
+
subtitleTemplate: str("subtitleTemplate", CHECKLIST_DEFAULTS.subtitleTemplate)
|
|
2486
|
+
};
|
|
2487
|
+
}
|
|
2488
|
+
function renderSubtitle(template, p) {
|
|
2489
|
+
return template.replace(/<%=\s*currentStep\s*%>/g, String(p.currentStep)).replace(/<%=\s*totalSteps\s*%>/g, String(p.totalSteps));
|
|
2490
|
+
}
|
|
2491
|
+
function buildRing(doc, fraction, completed, st) {
|
|
2492
|
+
const ns = "http://www.w3.org/2000/svg";
|
|
2493
|
+
const svg2 = doc.createElementNS(ns, "svg");
|
|
2494
|
+
svg2.setAttribute("viewBox", "0 0 28 28");
|
|
2495
|
+
svg2.setAttribute("class", "veo-checklist-ring");
|
|
2496
|
+
svg2.setAttribute("aria-hidden", "true");
|
|
2497
|
+
const track = doc.createElementNS(ns, "circle");
|
|
2498
|
+
track.setAttribute("cx", "14");
|
|
2499
|
+
track.setAttribute("cy", "14");
|
|
2500
|
+
track.setAttribute("r", String(RING_R));
|
|
2501
|
+
track.setAttribute("fill", completed ? st.iconFillColor : "none");
|
|
2502
|
+
track.setAttribute("stroke", st.iconEmptyColor);
|
|
2503
|
+
track.setAttribute("stroke-width", "4.5");
|
|
2504
|
+
svg2.appendChild(track);
|
|
2505
|
+
if (completed) {
|
|
2506
|
+
const check = doc.createElementNS(ns, "path");
|
|
2507
|
+
check.setAttribute("d", "M9 14.5l3.2 3.2L19 11");
|
|
2508
|
+
check.setAttribute("fill", "none");
|
|
2509
|
+
check.setAttribute("stroke", "#fff");
|
|
2510
|
+
check.setAttribute("stroke-width", "2.4");
|
|
2511
|
+
check.setAttribute("stroke-linecap", "round");
|
|
2512
|
+
check.setAttribute("stroke-linejoin", "round");
|
|
2513
|
+
svg2.appendChild(check);
|
|
2514
|
+
return svg2;
|
|
2515
|
+
}
|
|
2516
|
+
const f = Math.max(0, Math.min(1, fraction));
|
|
2517
|
+
if (f > 0) {
|
|
2518
|
+
const arc = doc.createElementNS(ns, "circle");
|
|
2519
|
+
arc.setAttribute("cx", "14");
|
|
2520
|
+
arc.setAttribute("cy", "14");
|
|
2521
|
+
arc.setAttribute("r", String(RING_R));
|
|
2522
|
+
arc.setAttribute("fill", "none");
|
|
2523
|
+
arc.setAttribute("stroke", st.iconFillColor);
|
|
2524
|
+
arc.setAttribute("stroke-width", "4.5");
|
|
2525
|
+
arc.setAttribute("stroke-linecap", "butt");
|
|
2526
|
+
arc.setAttribute("stroke-dasharray", `${(RING_C * f).toFixed(2)} ${RING_C.toFixed(2)}`);
|
|
2527
|
+
arc.setAttribute("transform", "rotate(-90 14 14)");
|
|
2528
|
+
svg2.appendChild(arc);
|
|
2529
|
+
}
|
|
2530
|
+
return svg2;
|
|
2531
|
+
}
|
|
2532
|
+
function buildChecklistBlock(block, doc, context) {
|
|
2533
|
+
const st = checklistStyle(block.style);
|
|
2534
|
+
const items = (block.items ?? []).filter((i) => i && typeof i.guideId === "string" && i.guideId);
|
|
2535
|
+
const root = doc.createElement("div");
|
|
2536
|
+
root.className = `veo-checklist${st.colorMode === "dark" ? " veo-checklist--dark" : ""}`;
|
|
2537
|
+
root.style.setProperty("--veo-cl-font-size", `${st.fontSize}px`);
|
|
2538
|
+
root.style.setProperty("--veo-cl-font-weight", String(st.fontWeight));
|
|
2539
|
+
root.style.setProperty("--veo-cl-title", st.titleColor);
|
|
2540
|
+
root.style.setProperty("--veo-cl-subtitle", st.subtitleColor);
|
|
2541
|
+
root.style.setProperty("--veo-cl-bar", st.barColor);
|
|
2542
|
+
root.style.setProperty("--veo-cl-bar-h", `${st.barHeight}px`);
|
|
2543
|
+
if (typeof block.html === "string" && block.html || typeof block.text === "string" && block.text) {
|
|
2544
|
+
const title = doc.createElement("div");
|
|
2545
|
+
title.className = "veo-checklist-title";
|
|
2546
|
+
if (typeof block.html === "string" && block.html)
|
|
2547
|
+
title.appendChild(renderRichText(block.html, doc));
|
|
2548
|
+
else title.textContent = block.text;
|
|
2549
|
+
root.appendChild(title);
|
|
2550
|
+
}
|
|
2551
|
+
const list = doc.createElement("div");
|
|
2552
|
+
list.className = "veo-checklist-items";
|
|
2553
|
+
list.setAttribute("role", "list");
|
|
2554
|
+
root.appendChild(list);
|
|
2555
|
+
const footer = doc.createElement("div");
|
|
2556
|
+
footer.className = "veo-checklist-footer";
|
|
2557
|
+
const summary = doc.createElement("div");
|
|
2558
|
+
summary.className = "veo-checklist-summary";
|
|
2559
|
+
const count = doc.createElement("span");
|
|
2560
|
+
count.className = "veo-checklist-count";
|
|
2561
|
+
const percent = doc.createElement("span");
|
|
2562
|
+
percent.className = "veo-checklist-percent";
|
|
2563
|
+
summary.append(count, percent);
|
|
2564
|
+
const bar = doc.createElement("div");
|
|
2565
|
+
bar.className = "veo-checklist-bar";
|
|
2566
|
+
const fill = doc.createElement("div");
|
|
2567
|
+
fill.className = "veo-checklist-bar-fill";
|
|
2568
|
+
bar.appendChild(fill);
|
|
2569
|
+
footer.append(summary, bar);
|
|
2570
|
+
root.appendChild(footer);
|
|
2571
|
+
const paint = (progress) => {
|
|
2572
|
+
list.textContent = "";
|
|
2573
|
+
const visible = progress ? items.filter((i) => progress.has(i.guideId)) : items;
|
|
2574
|
+
let done = 0;
|
|
2575
|
+
for (const item of visible) {
|
|
2576
|
+
const p = progress?.get(item.guideId) ?? null;
|
|
2577
|
+
if (p?.completed) done++;
|
|
2578
|
+
const row = doc.createElement("button");
|
|
2579
|
+
row.type = "button";
|
|
2580
|
+
row.className = `veo-checklist-item${p?.completed ? " veo-checklist-item--done" : ""}`;
|
|
2581
|
+
row.setAttribute("role", "listitem");
|
|
2582
|
+
const fraction = p && p.totalSteps > 0 ? p.currentStep / p.totalSteps : 0;
|
|
2583
|
+
row.appendChild(buildRing(doc, fraction, p?.completed === true, st));
|
|
2584
|
+
const texts = doc.createElement("span");
|
|
2585
|
+
texts.className = "veo-checklist-texts";
|
|
2586
|
+
const label = doc.createElement("span");
|
|
2587
|
+
label.className = "veo-checklist-label";
|
|
2588
|
+
label.textContent = (item.text ?? "").trim() || p?.guideName || "Gu\xEDa";
|
|
2589
|
+
if (p && p.published === false) row.title = "Gu\xEDa no publicada: los usuarios no la ver\xE1n";
|
|
2590
|
+
texts.appendChild(label);
|
|
2591
|
+
if (p) {
|
|
2592
|
+
const sub = doc.createElement("span");
|
|
2593
|
+
sub.className = "veo-checklist-sub";
|
|
2594
|
+
sub.textContent = renderSubtitle(st.subtitleTemplate, p);
|
|
2595
|
+
texts.appendChild(sub);
|
|
2596
|
+
}
|
|
2597
|
+
row.appendChild(texts);
|
|
2598
|
+
row.addEventListener("click", (e) => {
|
|
2599
|
+
e.preventDefault();
|
|
2600
|
+
e.stopPropagation();
|
|
2601
|
+
if (context.isPreview) return;
|
|
2602
|
+
const start = p && !p.completed ? Math.min(p.currentStep, p.totalSteps - 1) : 0;
|
|
2603
|
+
if (context.onOpenGuideAt) context.onOpenGuideAt(item.guideId, start);
|
|
2604
|
+
else context.onOpenGuide?.(item.guideId);
|
|
2605
|
+
});
|
|
2606
|
+
list.appendChild(row);
|
|
2607
|
+
}
|
|
2608
|
+
const total = visible.length;
|
|
2609
|
+
const pct = total > 0 ? Math.round(done / total * 100) : 0;
|
|
2610
|
+
count.textContent = `${done} de ${total} ${total === 1 ? "completada" : "completadas"}`;
|
|
2611
|
+
percent.textContent = `${pct}%`;
|
|
2612
|
+
fill.style.width = `${pct}%`;
|
|
2613
|
+
footer.hidden = total === 0;
|
|
2614
|
+
};
|
|
2615
|
+
if (context.isPreview && items.length === 0) {
|
|
2616
|
+
const sample = /* @__PURE__ */ new Map([
|
|
2617
|
+
[
|
|
2618
|
+
"__sample_1",
|
|
2619
|
+
{
|
|
2620
|
+
guideId: "__sample_1",
|
|
2621
|
+
guideName: "Configura tu primer env\xEDo",
|
|
2622
|
+
totalSteps: 2,
|
|
2623
|
+
currentStep: 1,
|
|
2624
|
+
completed: false
|
|
2625
|
+
}
|
|
2626
|
+
],
|
|
2627
|
+
[
|
|
2628
|
+
"__sample_2",
|
|
2629
|
+
{
|
|
2630
|
+
guideId: "__sample_2",
|
|
2631
|
+
guideName: "Conecta tu tienda",
|
|
2632
|
+
totalSteps: 2,
|
|
2633
|
+
currentStep: 2,
|
|
2634
|
+
completed: true
|
|
2635
|
+
}
|
|
2636
|
+
]
|
|
2637
|
+
]);
|
|
2638
|
+
items.push(
|
|
2639
|
+
{ guideId: "__sample_1", text: "Configura tu primer env\xEDo" },
|
|
2640
|
+
{ guideId: "__sample_2", text: "Conecta tu tienda" }
|
|
2641
|
+
);
|
|
2642
|
+
paint(sample);
|
|
2643
|
+
const hint = doc.createElement("p");
|
|
2644
|
+
hint.className = "veo-checklist-empty";
|
|
2645
|
+
hint.textContent = "Ejemplo: agrega gu\xEDas a la lista desde Opciones.";
|
|
2646
|
+
root.appendChild(hint);
|
|
2647
|
+
return root;
|
|
2648
|
+
}
|
|
2649
|
+
paint(null);
|
|
2650
|
+
if (context.fetchProgress && items.length > 0) {
|
|
2651
|
+
void context.fetchProgress(items.map((i) => i.guideId)).then((rows) => paint(new Map(rows.map((r) => [r.guideId, r])))).catch(() => {
|
|
2652
|
+
});
|
|
2653
|
+
}
|
|
2654
|
+
return root;
|
|
2655
|
+
}
|
|
2656
|
+
var CHECKLIST_DEFAULTS, WEIGHTS, RING_R, RING_C;
|
|
2657
|
+
var init_checklist_block = __esm({
|
|
2658
|
+
"src/plugins/guides/checklist-block.ts"() {
|
|
2659
|
+
init_rich_text();
|
|
2660
|
+
CHECKLIST_DEFAULTS = {
|
|
2661
|
+
colorMode: "light",
|
|
2662
|
+
iconEmptyColor: "#e7e2de",
|
|
2663
|
+
iconFillColor: "#406a31",
|
|
2664
|
+
barColor: "#406a31",
|
|
2665
|
+
barHeight: 8,
|
|
2666
|
+
fontSize: 14,
|
|
2667
|
+
fontWeight: 600,
|
|
2668
|
+
titleColor: "#2c2825",
|
|
2669
|
+
subtitleColor: "#8a8582",
|
|
2670
|
+
subtitleTemplate: "Completado <%= currentStep %> de <%= totalSteps %> pasos"
|
|
2671
|
+
};
|
|
2672
|
+
WEIGHTS = { regular: 400, medium: 500, semibold: 600, bold: 700 };
|
|
2673
|
+
RING_R = 10;
|
|
2674
|
+
RING_C = 2 * Math.PI * RING_R;
|
|
2675
|
+
}
|
|
2676
|
+
});
|
|
2677
|
+
|
|
2060
2678
|
// src/plugins/guides/renderers/custom-frame.ts
|
|
2061
2679
|
function mountCustomFrame(doc, step, context, opts) {
|
|
2062
2680
|
const iframe = doc.createElement("iframe");
|
|
@@ -2241,6 +2859,11 @@ function mountCodeBlock(block, doc, context, host, rawInline) {
|
|
|
2241
2859
|
}
|
|
2242
2860
|
if (rawInline) {
|
|
2243
2861
|
const rawNodes = Array.from(holder.childNodes);
|
|
2862
|
+
if (rawInline.tagBlockId) {
|
|
2863
|
+
for (const n of rawNodes) {
|
|
2864
|
+
if (n instanceof Element) n.setAttribute(RAW_BLOCK_ATTR, rawInline.tagBlockId);
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2244
2867
|
insertRawNodes(rawInline.anchor, rawNodes, rawInline.pos);
|
|
2245
2868
|
injected.push(...rawNodes);
|
|
2246
2869
|
} else if (host) {
|
|
@@ -2250,24 +2873,40 @@ function mountCodeBlock(block, doc, context, host, rawInline) {
|
|
|
2250
2873
|
injected.push(holder);
|
|
2251
2874
|
const slot = doc.createElement("slot");
|
|
2252
2875
|
slot.setAttribute("name", slotName);
|
|
2253
|
-
|
|
2876
|
+
const wrapper = doc.createElement("div");
|
|
2877
|
+
wrapper.className = "veo-code-host";
|
|
2878
|
+
wrapper.appendChild(slot);
|
|
2879
|
+
node = wrapper;
|
|
2254
2880
|
} else {
|
|
2255
2881
|
node = holder;
|
|
2256
2882
|
}
|
|
2257
2883
|
}
|
|
2884
|
+
let cancelled = false;
|
|
2258
2885
|
if (typeof block.js === "string" && block.js.trim()) {
|
|
2259
|
-
const
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2886
|
+
const js = block.js;
|
|
2887
|
+
const run = () => {
|
|
2888
|
+
if (cancelled) return;
|
|
2889
|
+
const s = hostDoc.createElement("script");
|
|
2890
|
+
s.textContent = wrapUserScript(js);
|
|
2891
|
+
hostDoc.body.appendChild(s);
|
|
2892
|
+
injected.push(s);
|
|
2893
|
+
};
|
|
2894
|
+
if (host && !host.isConnected) queueMicrotask(run);
|
|
2895
|
+
else run();
|
|
2263
2896
|
}
|
|
2264
2897
|
return {
|
|
2265
2898
|
node,
|
|
2266
2899
|
cleanup: () => {
|
|
2900
|
+
cancelled = true;
|
|
2267
2901
|
for (const n of injected) n.parentNode?.removeChild(n);
|
|
2268
2902
|
}
|
|
2269
2903
|
};
|
|
2270
2904
|
}
|
|
2905
|
+
function wrapUserScript(js) {
|
|
2906
|
+
return `(function(){try{
|
|
2907
|
+
${js}
|
|
2908
|
+
}catch(e){console.error("[OnUser] Error en el bloque de c\xF3digo:",e)}})();`;
|
|
2909
|
+
}
|
|
2271
2910
|
function runScript(old, hostDoc) {
|
|
2272
2911
|
const s = hostDoc.createElement("script");
|
|
2273
2912
|
for (const attr of Array.from(old.attributes)) s.setAttribute(attr.name, attr.value);
|
|
@@ -2275,10 +2914,11 @@ function runScript(old, hostDoc) {
|
|
|
2275
2914
|
hostDoc.body.appendChild(s);
|
|
2276
2915
|
return s;
|
|
2277
2916
|
}
|
|
2278
|
-
var codeSlotSeq;
|
|
2917
|
+
var RAW_BLOCK_ATTR, codeSlotSeq;
|
|
2279
2918
|
var init_code_block = __esm({
|
|
2280
2919
|
"src/plugins/guides/code-block.ts"() {
|
|
2281
2920
|
init_custom_frame();
|
|
2921
|
+
RAW_BLOCK_ATTR = "data-veo-raw-block";
|
|
2282
2922
|
codeSlotSeq = 0;
|
|
2283
2923
|
}
|
|
2284
2924
|
});
|
|
@@ -2509,8 +3149,14 @@ function buildField(field, doc) {
|
|
|
2509
3149
|
wrapper.appendChild(group);
|
|
2510
3150
|
return { wrapper, read: () => inputs.find((i) => i.input.checked)?.value };
|
|
2511
3151
|
}
|
|
2512
|
-
case "nps":
|
|
2513
|
-
|
|
3152
|
+
case "nps": {
|
|
3153
|
+
const min = typeof field.min === "number" ? field.min : 0;
|
|
3154
|
+
const max = typeof field.max === "number" && field.max > min ? field.max : 10;
|
|
3155
|
+
return buildScale(wrapper, doc, min, max, "veo-nps-btn", void 0, {
|
|
3156
|
+
labels: field.scaleLabels ?? [],
|
|
3157
|
+
showLabels: field.showLabels !== false
|
|
3158
|
+
});
|
|
3159
|
+
}
|
|
2514
3160
|
case "rating":
|
|
2515
3161
|
return buildScale(wrapper, doc, 1, 5, "veo-rating-btn", "\u2605");
|
|
2516
3162
|
default: {
|
|
@@ -2523,17 +3169,21 @@ function buildField(field, doc) {
|
|
|
2523
3169
|
}
|
|
2524
3170
|
}
|
|
2525
3171
|
}
|
|
2526
|
-
function buildScale(wrapper, doc, min, max, btnClass, symbol) {
|
|
3172
|
+
function buildScale(wrapper, doc, min, max, btnClass, symbol, opts = { labels: [], showLabels: false }) {
|
|
2527
3173
|
const row = doc.createElement("div");
|
|
2528
3174
|
row.className = "veo-form-scale";
|
|
2529
3175
|
let selected;
|
|
2530
3176
|
const buttons = [];
|
|
3177
|
+
const labelOf = (n) => opts.labels[n - min]?.trim() ?? "";
|
|
3178
|
+
const anyLabel = opts.showLabels && Array.from({ length: max - min + 1 }, (_, i) => min + i).some((n) => labelOf(n));
|
|
2531
3179
|
for (let n = min; n <= max; n++) {
|
|
2532
3180
|
const btn = doc.createElement("button");
|
|
2533
3181
|
btn.type = "button";
|
|
2534
3182
|
btn.className = btnClass;
|
|
2535
3183
|
btn.textContent = symbol ?? String(n);
|
|
2536
|
-
|
|
3184
|
+
const lbl = labelOf(n);
|
|
3185
|
+
btn.setAttribute("aria-label", lbl ? `${n} \xB7 ${lbl}` : String(n));
|
|
3186
|
+
if (lbl) btn.title = lbl;
|
|
2537
3187
|
btn.addEventListener("click", () => {
|
|
2538
3188
|
selected = n;
|
|
2539
3189
|
buttons.forEach((b, i) => {
|
|
@@ -2542,7 +3192,17 @@ function buildScale(wrapper, doc, min, max, btnClass, symbol) {
|
|
|
2542
3192
|
});
|
|
2543
3193
|
});
|
|
2544
3194
|
buttons.push(btn);
|
|
2545
|
-
|
|
3195
|
+
if (!anyLabel) {
|
|
3196
|
+
row.appendChild(btn);
|
|
3197
|
+
continue;
|
|
3198
|
+
}
|
|
3199
|
+
const item = doc.createElement("div");
|
|
3200
|
+
item.className = `veo-scale-item${n === min ? " veo-scale-item--first" : n === max ? " veo-scale-item--last" : ""}`;
|
|
3201
|
+
const text = doc.createElement("span");
|
|
3202
|
+
text.className = "veo-scale-label";
|
|
3203
|
+
text.textContent = lbl;
|
|
3204
|
+
item.append(btn, text);
|
|
3205
|
+
row.appendChild(item);
|
|
2546
3206
|
}
|
|
2547
3207
|
wrapper.appendChild(row);
|
|
2548
3208
|
return { wrapper, read: () => selected };
|
|
@@ -2552,6 +3212,126 @@ var init_form_content = __esm({
|
|
|
2552
3212
|
init_block_builder();
|
|
2553
3213
|
}
|
|
2554
3214
|
});
|
|
3215
|
+
|
|
3216
|
+
// src/plugins/guides/layout.ts
|
|
3217
|
+
function groupLayout(blocks) {
|
|
3218
|
+
const rows = /* @__PURE__ */ new Map();
|
|
3219
|
+
const order = [];
|
|
3220
|
+
let lastRow = -1;
|
|
3221
|
+
blocks.forEach((block, index) => {
|
|
3222
|
+
if (!block) return;
|
|
3223
|
+
let row;
|
|
3224
|
+
if (typeof block.row === "number" && Number.isFinite(block.row)) {
|
|
3225
|
+
row = block.row;
|
|
3226
|
+
lastRow = row;
|
|
3227
|
+
} else {
|
|
3228
|
+
row = lastRow + 0.5;
|
|
3229
|
+
lastRow = row;
|
|
3230
|
+
}
|
|
3231
|
+
const col = typeof block.col === "number" && Number.isFinite(block.col) ? block.col : 0;
|
|
3232
|
+
let r = rows.get(row);
|
|
3233
|
+
if (!r) {
|
|
3234
|
+
r = { row, columns: [] };
|
|
3235
|
+
rows.set(row, r);
|
|
3236
|
+
order.push(row);
|
|
3237
|
+
}
|
|
3238
|
+
let c = r.columns.find((x) => x.col === col);
|
|
3239
|
+
if (!c) {
|
|
3240
|
+
c = { col, blocks: [] };
|
|
3241
|
+
r.columns.push(c);
|
|
3242
|
+
}
|
|
3243
|
+
c.blocks.push({ block, index });
|
|
3244
|
+
});
|
|
3245
|
+
return order.sort((a, b) => a - b).map((k) => {
|
|
3246
|
+
const r = rows.get(k);
|
|
3247
|
+
r.columns.sort((a, b) => a.col - b.col);
|
|
3248
|
+
return r;
|
|
3249
|
+
});
|
|
3250
|
+
}
|
|
3251
|
+
var init_layout = __esm({
|
|
3252
|
+
"src/plugins/guides/layout.ts"() {
|
|
3253
|
+
}
|
|
3254
|
+
});
|
|
3255
|
+
|
|
3256
|
+
// src/plugins/guides/scroll-lock.ts
|
|
3257
|
+
function lockScroll(doc = document) {
|
|
3258
|
+
const html = doc.documentElement;
|
|
3259
|
+
const body = doc.body;
|
|
3260
|
+
if (!html || !body) return () => {
|
|
3261
|
+
};
|
|
3262
|
+
let state = states.get(doc);
|
|
3263
|
+
if (!state) {
|
|
3264
|
+
state = {
|
|
3265
|
+
count: 0,
|
|
3266
|
+
html: { overflow: html.style.overflow, paddingRight: html.style.paddingRight },
|
|
3267
|
+
body: { overflow: body.style.overflow, paddingRight: body.style.paddingRight }
|
|
3268
|
+
};
|
|
3269
|
+
states.set(doc, state);
|
|
3270
|
+
const win = doc.defaultView;
|
|
3271
|
+
const gap = win ? Math.max(0, win.innerWidth - html.clientWidth) : 0;
|
|
3272
|
+
if (gap > 0) body.style.paddingRight = `${gap}px`;
|
|
3273
|
+
html.style.overflow = "hidden";
|
|
3274
|
+
body.style.overflow = "hidden";
|
|
3275
|
+
}
|
|
3276
|
+
state.count += 1;
|
|
3277
|
+
let released = false;
|
|
3278
|
+
return () => {
|
|
3279
|
+
if (released) return;
|
|
3280
|
+
released = true;
|
|
3281
|
+
const s = states.get(doc);
|
|
3282
|
+
if (!s) return;
|
|
3283
|
+
s.count -= 1;
|
|
3284
|
+
if (s.count > 0) return;
|
|
3285
|
+
states.delete(doc);
|
|
3286
|
+
html.style.overflow = s.html.overflow;
|
|
3287
|
+
html.style.paddingRight = s.html.paddingRight;
|
|
3288
|
+
body.style.overflow = s.body.overflow;
|
|
3289
|
+
body.style.paddingRight = s.body.paddingRight;
|
|
3290
|
+
};
|
|
3291
|
+
}
|
|
3292
|
+
var states;
|
|
3293
|
+
var init_scroll_lock = __esm({
|
|
3294
|
+
"src/plugins/guides/scroll-lock.ts"() {
|
|
3295
|
+
states = /* @__PURE__ */ new WeakMap();
|
|
3296
|
+
}
|
|
3297
|
+
});
|
|
3298
|
+
|
|
3299
|
+
// src/plugins/guides/snooze.ts
|
|
3300
|
+
function snoozeDurationMs(every, unit) {
|
|
3301
|
+
const n = Number.isFinite(every) && every > 0 ? Math.floor(every) : 1;
|
|
3302
|
+
return n * (UNIT_MS[unit] ?? UNIT_MS.day);
|
|
3303
|
+
}
|
|
3304
|
+
function snoozeGuide(guideId, every, unit, now = Date.now()) {
|
|
3305
|
+
try {
|
|
3306
|
+
localStorage.setItem(KEY(guideId), String(now + snoozeDurationMs(every, unit)));
|
|
3307
|
+
} catch {
|
|
3308
|
+
}
|
|
3309
|
+
}
|
|
3310
|
+
function isSnoozed(guideId, now = Date.now()) {
|
|
3311
|
+
try {
|
|
3312
|
+
const raw = localStorage.getItem(KEY(guideId));
|
|
3313
|
+
if (raw === null) return false;
|
|
3314
|
+
const until = Number(raw);
|
|
3315
|
+
if (!Number.isFinite(until) || until <= now) {
|
|
3316
|
+
localStorage.removeItem(KEY(guideId));
|
|
3317
|
+
return false;
|
|
3318
|
+
}
|
|
3319
|
+
return true;
|
|
3320
|
+
} catch {
|
|
3321
|
+
return false;
|
|
3322
|
+
}
|
|
3323
|
+
}
|
|
3324
|
+
var KEY, UNIT_MS;
|
|
3325
|
+
var init_snooze = __esm({
|
|
3326
|
+
"src/plugins/guides/snooze.ts"() {
|
|
3327
|
+
KEY = (guideId) => `veo:snooze:${guideId}`;
|
|
3328
|
+
UNIT_MS = {
|
|
3329
|
+
hour: 36e5,
|
|
3330
|
+
day: 864e5,
|
|
3331
|
+
week: 6048e5
|
|
3332
|
+
};
|
|
3333
|
+
}
|
|
3334
|
+
});
|
|
2555
3335
|
function num(v, fallback) {
|
|
2556
3336
|
return typeof v === "number" && Number.isFinite(v) ? v : fallback;
|
|
2557
3337
|
}
|
|
@@ -2739,26 +3519,61 @@ var init_badge_marker = __esm({
|
|
|
2739
3519
|
SIDE_GAP = 8;
|
|
2740
3520
|
}
|
|
2741
3521
|
});
|
|
3522
|
+
function applyRowStyle(rowEl, style) {
|
|
3523
|
+
const s = rowEl.style;
|
|
3524
|
+
if (typeof style.background === "string" && style.background) s.background = style.background;
|
|
3525
|
+
for (const side of ["Top", "Right", "Bottom", "Left"]) {
|
|
3526
|
+
const p = style[`padding${side}`];
|
|
3527
|
+
if (typeof p === "number" && Number.isFinite(p))
|
|
3528
|
+
s.setProperty(`padding-${side.toLowerCase()}`, `${p}px`);
|
|
3529
|
+
const m = style[`margin${side}`];
|
|
3530
|
+
if (typeof m === "number" && Number.isFinite(m))
|
|
3531
|
+
s.setProperty(`margin-${side.toLowerCase()}`, `${m}px`);
|
|
3532
|
+
}
|
|
3533
|
+
if (typeof style.minHeight === "number" && Number.isFinite(style.minHeight) && style.minHeight > 0) {
|
|
3534
|
+
s.minHeight = `${style.minHeight}px`;
|
|
3535
|
+
}
|
|
3536
|
+
if (style.align && ROW_JUSTIFY[style.align]) {
|
|
3537
|
+
s.justifyContent = ROW_JUSTIFY[style.align];
|
|
3538
|
+
rowEl.classList.add("veo-row--aligned");
|
|
3539
|
+
}
|
|
3540
|
+
if (style.valign && ROW_VALIGN[style.valign]) s.alignItems = ROW_VALIGN[style.valign];
|
|
3541
|
+
}
|
|
2742
3542
|
function inlinePosOf(p) {
|
|
2743
3543
|
return p === "before" || p === "prepend" || p === "append" ? p : "after";
|
|
2744
3544
|
}
|
|
2745
3545
|
function applyViewportPosition(card, position, design) {
|
|
2746
3546
|
const s = card.style;
|
|
2747
3547
|
s.position = "fixed";
|
|
2748
|
-
const
|
|
3548
|
+
const BASE = 16;
|
|
3549
|
+
const m = (k) => typeof design[k] === "number" && Number.isFinite(design[k]) ? design[k] : 0;
|
|
3550
|
+
s.margin = "0";
|
|
2749
3551
|
const hAlign = design.hAlign === "left" ? "left" : design.hAlign === "right" ? "right" : null;
|
|
2750
3552
|
const v = position.includes("top") ? "top" : position.includes("bottom") ? "bottom" : "middle";
|
|
2751
3553
|
const h = hAlign ?? (position.includes("left") ? "left" : position.includes("right") ? "right" : "middle");
|
|
2752
|
-
if (v === "top") s.top =
|
|
2753
|
-
else if (v === "bottom") s.bottom =
|
|
3554
|
+
if (v === "top") s.top = `${BASE + m("marginTop")}px`;
|
|
3555
|
+
else if (v === "bottom") s.bottom = `${BASE + m("marginBottom")}px`;
|
|
2754
3556
|
else s.top = "50%";
|
|
2755
|
-
if (h === "left") s.left =
|
|
2756
|
-
else if (h === "right") s.right =
|
|
3557
|
+
if (h === "left") s.left = `${BASE + m("marginLeft")}px`;
|
|
3558
|
+
else if (h === "right") s.right = `${BASE + m("marginRight")}px`;
|
|
2757
3559
|
else s.left = "50%";
|
|
2758
|
-
const
|
|
2759
|
-
const
|
|
3560
|
+
const dx = m("marginLeft") - m("marginRight");
|
|
3561
|
+
const dy = m("marginTop") - m("marginBottom");
|
|
3562
|
+
const tx = h === "middle" ? `calc(-50% + ${dx}px)` : "0";
|
|
3563
|
+
const ty = v === "middle" ? `calc(-50% + ${dy}px)` : "0";
|
|
2760
3564
|
s.transform = tx === "0" && ty === "0" ? "" : `translate(${tx}, ${ty})`;
|
|
2761
3565
|
}
|
|
3566
|
+
function reserveCloseSpace(card, blocks, position, design) {
|
|
3567
|
+
if (blocks.length === 0) return;
|
|
3568
|
+
const side = position.includes("bottom") ? "Bottom" : "Top";
|
|
3569
|
+
const edgeBlock = side === "Top" ? blocks[0] : blocks[blocks.length - 1];
|
|
3570
|
+
if (edgeBlock?.type === "image") return;
|
|
3571
|
+
const margin = design[`closeMargin${side}`];
|
|
3572
|
+
const need = CLOSE_RESERVE_PX + (typeof margin === "number" ? margin : 0);
|
|
3573
|
+
const current = design[`padding${side}`];
|
|
3574
|
+
const have = typeof current === "number" ? current : CARD_DEFAULT_PAD_PX;
|
|
3575
|
+
if (have < need) card.style.setProperty(`padding-${side.toLowerCase()}`, `${need}px`);
|
|
3576
|
+
}
|
|
2762
3577
|
function applyClosePosition(btn, position) {
|
|
2763
3578
|
const s = btn.style;
|
|
2764
3579
|
s.top = "auto";
|
|
@@ -2827,6 +3642,11 @@ function applyCompositeDesign(card, design) {
|
|
|
2827
3642
|
const s = card.style;
|
|
2828
3643
|
const px = (v) => typeof v === "number" && Number.isFinite(v) ? `${v}px` : null;
|
|
2829
3644
|
const strv = (v) => typeof v === "string" && v ? v : null;
|
|
3645
|
+
if (design.colorMode === "dark") {
|
|
3646
|
+
s.background = "#1f1d1b";
|
|
3647
|
+
s.color = "#f5f0eb";
|
|
3648
|
+
card.classList.add("veo-card--dark");
|
|
3649
|
+
}
|
|
2830
3650
|
if (strv(design.background)) s.background = design.background;
|
|
2831
3651
|
const textColor = strv(design.color) ?? strv(design.textColor);
|
|
2832
3652
|
if (textColor) s.color = textColor;
|
|
@@ -2848,13 +3668,14 @@ function applyCompositeDesign(card, design) {
|
|
|
2848
3668
|
const n = (k) => typeof design[k] === "number" ? design[k] : 0;
|
|
2849
3669
|
s.boxShadow = `${n("shadowX")}px ${n("shadowY")}px ${n("shadowBlur")}px ${n("shadowSpread")}px ${strv(design.shadowColor) ?? "rgba(0,0,0,0.2)"}`;
|
|
2850
3670
|
}
|
|
3671
|
+
const width = typeof design.width === "number" && Number.isFinite(design.width) ? `${Math.max(MIN_CARD_WIDTH, design.width)}px` : null;
|
|
2851
3672
|
if (design.fullWidth === true) {
|
|
2852
3673
|
s.width = "100%";
|
|
2853
3674
|
} else if (design.widthType === "fluid") {
|
|
2854
|
-
s.width = "
|
|
2855
|
-
if (
|
|
2856
|
-
} else if (
|
|
2857
|
-
s.width =
|
|
3675
|
+
s.width = "100%";
|
|
3676
|
+
if (width) s.maxWidth = width;
|
|
3677
|
+
} else if (width) {
|
|
3678
|
+
s.width = width;
|
|
2858
3679
|
}
|
|
2859
3680
|
for (const side of ["Top", "Right", "Bottom", "Left"]) {
|
|
2860
3681
|
const p = px(design[`padding${side}`]);
|
|
@@ -2863,13 +3684,18 @@ function applyCompositeDesign(card, design) {
|
|
|
2863
3684
|
if (m) s.setProperty(`margin-${side.toLowerCase()}`, m);
|
|
2864
3685
|
}
|
|
2865
3686
|
}
|
|
2866
|
-
var ROW_ALIGN, CompositeRenderer, CLOSE_INSET;
|
|
3687
|
+
var ROW_ALIGN, ROW_JUSTIFY, ROW_VALIGN, CompositeRenderer, CLOSE_INSET, CLOSE_RESERVE_PX, CARD_DEFAULT_PAD_PX, MIN_CARD_WIDTH, NARROW_CARD_WIDTH;
|
|
2867
3688
|
var init_composite_renderer = __esm({
|
|
2868
3689
|
"src/plugins/guides/renderers/composite-renderer.ts"() {
|
|
2869
3690
|
init_block_builder();
|
|
3691
|
+
init_checklist_block();
|
|
2870
3692
|
init_code_block();
|
|
2871
3693
|
init_form_content();
|
|
2872
3694
|
init_inline_host();
|
|
3695
|
+
init_layout();
|
|
3696
|
+
init_rich_text();
|
|
3697
|
+
init_scroll_lock();
|
|
3698
|
+
init_snooze();
|
|
2873
3699
|
init_wait_for_element();
|
|
2874
3700
|
init_badge_marker();
|
|
2875
3701
|
init_base_renderer();
|
|
@@ -2879,6 +3705,12 @@ var init_composite_renderer = __esm({
|
|
|
2879
3705
|
center: "center",
|
|
2880
3706
|
right: "flex-end"
|
|
2881
3707
|
};
|
|
3708
|
+
ROW_JUSTIFY = { ...ROW_ALIGN, distribute: "space-between" };
|
|
3709
|
+
ROW_VALIGN = {
|
|
3710
|
+
top: "flex-start",
|
|
3711
|
+
center: "center",
|
|
3712
|
+
bottom: "flex-end"
|
|
3713
|
+
};
|
|
2882
3714
|
CompositeRenderer = class extends BaseRenderer {
|
|
2883
3715
|
async render(context) {
|
|
2884
3716
|
const idx = context.nav?.stepIndex ?? 0;
|
|
@@ -2897,7 +3729,8 @@ var init_composite_renderer = __esm({
|
|
|
2897
3729
|
const rawDoc = anchor.ownerDocument ?? document;
|
|
2898
3730
|
const { cleanup } = mountCodeBlock(soleCode, rawDoc, context, void 0, {
|
|
2899
3731
|
anchor,
|
|
2900
|
-
pos: inlinePosOf(step.inlinePosition)
|
|
3732
|
+
pos: inlinePosOf(step.inlinePosition),
|
|
3733
|
+
...context.isPreview && soleCode.editorId ? { tagBlockId: soleCode.editorId } : {}
|
|
2901
3734
|
});
|
|
2902
3735
|
this.registerCleanup(cleanup);
|
|
2903
3736
|
context.onInteraction({ guideId: context.guide.guideId, stepIndex: idx, action: "shown" });
|
|
@@ -2910,6 +3743,10 @@ var init_composite_renderer = __esm({
|
|
|
2910
3743
|
const card = doc.createElement("div");
|
|
2911
3744
|
card.className = "veo-card";
|
|
2912
3745
|
applyCompositeDesign(card, design);
|
|
3746
|
+
if (typeof design.ariaLabel === "string" && design.ariaLabel.trim()) {
|
|
3747
|
+
card.setAttribute("role", "dialog");
|
|
3748
|
+
card.setAttribute("aria-label", design.ariaLabel.trim());
|
|
3749
|
+
}
|
|
2913
3750
|
const dismiss = (action, meta) => {
|
|
2914
3751
|
context.onInteraction({
|
|
2915
3752
|
guideId: context.guide.guideId,
|
|
@@ -2920,6 +3757,28 @@ var init_composite_renderer = __esm({
|
|
|
2920
3757
|
};
|
|
2921
3758
|
const readers = [];
|
|
2922
3759
|
this.buildContent(card, step.blocks ?? [], doc, context, idx, readers, host);
|
|
3760
|
+
if (blocks.length === 0) {
|
|
3761
|
+
card.classList.add("veo-card--empty");
|
|
3762
|
+
if (context.isPreview) {
|
|
3763
|
+
const zone = doc.createElement("div");
|
|
3764
|
+
zone.className = "veo-card-empty-zone";
|
|
3765
|
+
const plus = doc.createElement("b");
|
|
3766
|
+
plus.textContent = "+";
|
|
3767
|
+
const text = doc.createElement("span");
|
|
3768
|
+
text.textContent = "Clic para agregar contenido";
|
|
3769
|
+
zone.append(plus, text);
|
|
3770
|
+
card.appendChild(zone);
|
|
3771
|
+
const applyNarrow = () => {
|
|
3772
|
+
card.classList.toggle("veo-card--narrow", card.clientWidth < NARROW_CARD_WIDTH);
|
|
3773
|
+
};
|
|
3774
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
3775
|
+
const ro = new ResizeObserver(applyNarrow);
|
|
3776
|
+
ro.observe(card);
|
|
3777
|
+
this.registerCleanup(() => ro.disconnect());
|
|
3778
|
+
}
|
|
3779
|
+
applyNarrow();
|
|
3780
|
+
}
|
|
3781
|
+
}
|
|
2923
3782
|
const lastBlock = card.lastElementChild;
|
|
2924
3783
|
if (lastBlock && !lastBlock.style.marginBottom) lastBlock.style.marginBottom = "0";
|
|
2925
3784
|
let closeTooltip = null;
|
|
@@ -2937,6 +3796,14 @@ var init_composite_renderer = __esm({
|
|
|
2937
3796
|
if (closeColor) closeBtn.style.color = closeColor;
|
|
2938
3797
|
const closePos = typeof design.closePosition === "string" ? design.closePosition : "top-right";
|
|
2939
3798
|
applyClosePosition(closeBtn, closePos);
|
|
3799
|
+
for (const side of ["Top", "Right", "Bottom", "Left"]) {
|
|
3800
|
+
const p = design[`closePadding${side}`];
|
|
3801
|
+
if (typeof p === "number")
|
|
3802
|
+
closeBtn.style.setProperty(`padding-${side.toLowerCase()}`, `${p}px`);
|
|
3803
|
+
const m = design[`closeMargin${side}`];
|
|
3804
|
+
if (typeof m === "number")
|
|
3805
|
+
closeBtn.style.setProperty(`margin-${side.toLowerCase()}`, `${m}px`);
|
|
3806
|
+
}
|
|
2940
3807
|
if (hoverColor) {
|
|
2941
3808
|
closeBtn.addEventListener("mouseenter", () => {
|
|
2942
3809
|
closeBtn.style.color = hoverColor;
|
|
@@ -2946,6 +3813,7 @@ var init_composite_renderer = __esm({
|
|
|
2946
3813
|
});
|
|
2947
3814
|
}
|
|
2948
3815
|
card.appendChild(closeBtn);
|
|
3816
|
+
reserveCloseSpace(card, blocks, closePos, design);
|
|
2949
3817
|
}
|
|
2950
3818
|
if (badgeMode && anchor) {
|
|
2951
3819
|
closeTooltip = this.renderBadgeMode(
|
|
@@ -2976,6 +3844,7 @@ var init_composite_renderer = __esm({
|
|
|
2976
3844
|
const bz = typeof design.backdropZIndex === "number" ? design.backdropZIndex : 2e5;
|
|
2977
3845
|
bg.style.zIndex = String(bz);
|
|
2978
3846
|
root.appendChild(bg);
|
|
3847
|
+
this.registerCleanup(lockScroll(doc));
|
|
2979
3848
|
if (design.targetAbove === true) this.registerCleanup(liftTarget(anchor, bz + 1));
|
|
2980
3849
|
}
|
|
2981
3850
|
const wrapper = doc.createElement("div");
|
|
@@ -3002,6 +3871,7 @@ var init_composite_renderer = __esm({
|
|
|
3002
3871
|
this.closeOrSkip(context);
|
|
3003
3872
|
}
|
|
3004
3873
|
});
|
|
3874
|
+
this.registerCleanup(lockScroll(doc));
|
|
3005
3875
|
}
|
|
3006
3876
|
applyViewportPosition(card, step.position, design);
|
|
3007
3877
|
overlay.appendChild(card);
|
|
@@ -3019,83 +3889,217 @@ var init_composite_renderer = __esm({
|
|
|
3019
3889
|
...block.id ? { metadata: { buttonId: block.id } } : {}
|
|
3020
3890
|
});
|
|
3021
3891
|
};
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3892
|
+
const tag = (node, block, index) => {
|
|
3893
|
+
node.dataset.veoBlock = String(index);
|
|
3894
|
+
if (block.editorId) node.dataset.veoBlockId = block.editorId;
|
|
3895
|
+
return node;
|
|
3896
|
+
};
|
|
3897
|
+
const placeholder = (block, label) => {
|
|
3898
|
+
if (!context.isPreview || !block.editorId) return null;
|
|
3899
|
+
const el2 = doc.createElement("div");
|
|
3900
|
+
el2.className = "veo-block-placeholder";
|
|
3901
|
+
el2.textContent = label;
|
|
3902
|
+
return el2;
|
|
3903
|
+
};
|
|
3904
|
+
const buildSequence = (container, cell) => {
|
|
3905
|
+
let i = 0;
|
|
3906
|
+
while (i < cell.length) {
|
|
3907
|
+
const { block: b, index } = cell[i];
|
|
3908
|
+
if (b.type === "button") {
|
|
3909
|
+
const row = doc.createElement("div");
|
|
3910
|
+
row.className = "veo-guide-actions";
|
|
3911
|
+
let rowAlign;
|
|
3912
|
+
while (i < cell.length && cell[i]?.block.type === "button") {
|
|
3913
|
+
const { block: bb, index: bi } = cell[i];
|
|
3914
|
+
if (!rowAlign) {
|
|
3915
|
+
const a = bb.style?.align;
|
|
3916
|
+
if (a === "left" || a === "center" || a === "right") rowAlign = a;
|
|
3917
|
+
}
|
|
3918
|
+
row.appendChild(tag(this.buildButton(bb, doc, context, idx, readers, emit), bb, bi));
|
|
3919
|
+
i++;
|
|
3038
3920
|
}
|
|
3039
|
-
row.
|
|
3921
|
+
if (rowAlign) row.style.justifyContent = ROW_ALIGN[rowAlign] ?? "flex-end";
|
|
3922
|
+
container.appendChild(row);
|
|
3923
|
+
continue;
|
|
3924
|
+
}
|
|
3925
|
+
if (b.type === "code") {
|
|
3926
|
+
const { node: node2, cleanup } = mountCodeBlock(b, doc, context, host);
|
|
3927
|
+
const el2 = node2 ?? placeholder(b, "Bloque de c\xF3digo");
|
|
3928
|
+
if (el2) container.appendChild(tag(el2, b, index));
|
|
3929
|
+
this.registerCleanup(cleanup);
|
|
3040
3930
|
i++;
|
|
3931
|
+
continue;
|
|
3041
3932
|
}
|
|
3042
|
-
if (
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
form.className = "veo-form";
|
|
3056
|
-
for (const field of b.fields ?? []) {
|
|
3057
|
-
const { wrapper, read } = buildField(field, doc);
|
|
3058
|
-
readers.push({ field, read });
|
|
3059
|
-
form.appendChild(wrapper);
|
|
3933
|
+
if (b.type === "form") {
|
|
3934
|
+
container.appendChild(tag(this.buildForm(b, doc, context, idx, readers, emit), b, index));
|
|
3935
|
+
i++;
|
|
3936
|
+
continue;
|
|
3937
|
+
}
|
|
3938
|
+
if (b.type === "checklist") {
|
|
3939
|
+
const el2 = buildChecklistBlock(b, doc, context);
|
|
3940
|
+
applyBlockStyle(el2, b.style, "text");
|
|
3941
|
+
el2.style.alignSelf = "stretch";
|
|
3942
|
+
el2.style.width = "100%";
|
|
3943
|
+
container.appendChild(tag(el2, b, index));
|
|
3944
|
+
i++;
|
|
3945
|
+
continue;
|
|
3060
3946
|
}
|
|
3061
|
-
|
|
3947
|
+
const node = buildContentBlock(b, doc, { editing: context.isPreview === true }) ?? placeholder(
|
|
3948
|
+
b,
|
|
3949
|
+
b.type === "image" ? "Imagen (sin URL)" : b.type === "video" ? "Video (elige proveedor y pega la URL)" : "Bloque vac\xEDo"
|
|
3950
|
+
);
|
|
3951
|
+
if (node) container.appendChild(tag(node, b, index));
|
|
3062
3952
|
i++;
|
|
3953
|
+
}
|
|
3954
|
+
};
|
|
3955
|
+
const rowStyles = context.guide.guideSteps[idx]?.rows;
|
|
3956
|
+
for (const row of groupLayout(blocks)) {
|
|
3957
|
+
const rowStyle = Number.isInteger(row.row) ? rowStyles?.[String(row.row)] : void 0;
|
|
3958
|
+
if (row.columns.length === 1 && !rowStyle) {
|
|
3959
|
+
buildSequence(card, row.columns[0].blocks);
|
|
3063
3960
|
continue;
|
|
3064
3961
|
}
|
|
3065
|
-
const
|
|
3066
|
-
|
|
3067
|
-
|
|
3962
|
+
const rowEl = doc.createElement("div");
|
|
3963
|
+
rowEl.className = "veo-row";
|
|
3964
|
+
if (Number.isInteger(row.row)) rowEl.dataset.veoRow = String(row.row);
|
|
3965
|
+
for (const column of row.columns) {
|
|
3966
|
+
const colEl = doc.createElement("div");
|
|
3967
|
+
colEl.className = "veo-col";
|
|
3968
|
+
buildSequence(colEl, column.blocks);
|
|
3969
|
+
rowEl.appendChild(colEl);
|
|
3970
|
+
}
|
|
3971
|
+
if (rowStyle) applyRowStyle(rowEl, rowStyle);
|
|
3972
|
+
card.appendChild(rowEl);
|
|
3068
3973
|
}
|
|
3069
3974
|
}
|
|
3070
3975
|
/** Botón con enrutado de acción (navegación / cierre / url / guía / envío de forms). */
|
|
3071
|
-
|
|
3976
|
+
/**
|
|
3977
|
+
* Bloque `form` (encuesta): pregunta (rich text del bloque) + campos + botón
|
|
3978
|
+
* de envío propio (`submit`). Con pregunta, el label del campo se omite (sería
|
|
3979
|
+
* la misma pregunta dos veces) y pasa a `aria-label` del input. El estilo del
|
|
3980
|
+
* bloque viste la pregunta (preset/alineación/color); los márgenes van al
|
|
3981
|
+
* contenedor del bloque entero.
|
|
3982
|
+
*/
|
|
3983
|
+
buildForm(b, doc, context, idx, readers, emit) {
|
|
3984
|
+
const form = doc.createElement("div");
|
|
3985
|
+
form.className = "veo-form";
|
|
3986
|
+
const style = b.style ?? {};
|
|
3987
|
+
const { marginTop, marginBottom, ...questionStyle } = style;
|
|
3988
|
+
for (const [side, v] of [
|
|
3989
|
+
["marginTop", marginTop],
|
|
3990
|
+
["marginBottom", marginBottom]
|
|
3991
|
+
]) {
|
|
3992
|
+
if (typeof v === "number" && Number.isFinite(v)) form.style[side] = `${v}px`;
|
|
3993
|
+
}
|
|
3994
|
+
let question = null;
|
|
3995
|
+
if (typeof b.html === "string" && b.html || typeof b.text === "string" && b.text) {
|
|
3996
|
+
question = doc.createElement("div");
|
|
3997
|
+
question.className = "veo-form-question";
|
|
3998
|
+
if (typeof b.html === "string" && b.html) question.appendChild(renderRichText(b.html, doc));
|
|
3999
|
+
else question.textContent = b.text;
|
|
4000
|
+
applyBlockStyle(question, questionStyle, "text");
|
|
4001
|
+
form.appendChild(question);
|
|
4002
|
+
}
|
|
4003
|
+
const questionText = question?.textContent?.trim() ?? "";
|
|
4004
|
+
const own = [];
|
|
4005
|
+
for (const field of b.fields ?? []) {
|
|
4006
|
+
const { wrapper, read } = buildField(field, doc);
|
|
4007
|
+
const st = b.style ?? {};
|
|
4008
|
+
if (typeof st.scaleColor === "string" && st.scaleColor) {
|
|
4009
|
+
wrapper.style.setProperty("--veo-scale-color", st.scaleColor);
|
|
4010
|
+
}
|
|
4011
|
+
if (typeof st.scaleSize === "number" && st.scaleSize >= 24 && st.scaleSize <= 96) {
|
|
4012
|
+
wrapper.style.setProperty("--veo-scale-size", `${st.scaleSize}px`);
|
|
4013
|
+
}
|
|
4014
|
+
if (question) {
|
|
4015
|
+
wrapper.querySelector(".veo-form-label")?.remove();
|
|
4016
|
+
if (questionText) {
|
|
4017
|
+
for (const inp of wrapper.querySelectorAll("input, textarea, select")) {
|
|
4018
|
+
inp.setAttribute("aria-label", questionText);
|
|
4019
|
+
}
|
|
4020
|
+
}
|
|
4021
|
+
}
|
|
4022
|
+
const reader = { field, read, blockId: b.id ?? null };
|
|
4023
|
+
readers.push(reader);
|
|
4024
|
+
own.push(reader);
|
|
4025
|
+
form.appendChild(wrapper);
|
|
4026
|
+
}
|
|
4027
|
+
if (b.submit) {
|
|
4028
|
+
const actions = doc.createElement("div");
|
|
4029
|
+
actions.className = "veo-guide-actions";
|
|
4030
|
+
const submit = { ...b.submit, type: "button" };
|
|
4031
|
+
const sa = submit.style?.align;
|
|
4032
|
+
if (sa === "left" || sa === "center" || sa === "right") {
|
|
4033
|
+
actions.style.justifyContent = ROW_ALIGN[sa] ?? "flex-end";
|
|
4034
|
+
}
|
|
4035
|
+
actions.appendChild(this.buildButton(submit, doc, context, idx, readers, emit, own));
|
|
4036
|
+
form.appendChild(actions);
|
|
4037
|
+
}
|
|
4038
|
+
return form;
|
|
4039
|
+
}
|
|
4040
|
+
buildButton(block, doc, context, idx, readers, emit, submitFirst) {
|
|
3072
4041
|
const btn = doc.createElement("button");
|
|
3073
4042
|
btn.type = "button";
|
|
3074
4043
|
btn.className = "veo-guide-cta";
|
|
3075
4044
|
btn.textContent = block.text?.trim() || "OK";
|
|
4045
|
+
const st = block.style ?? {};
|
|
4046
|
+
if (st.preset === "secondary" || st.preset === "link") {
|
|
4047
|
+
btn.classList.add(`veo-guide-cta--${st.preset}`);
|
|
4048
|
+
}
|
|
4049
|
+
if (typeof st.ariaLabel === "string" && st.ariaLabel.trim()) {
|
|
4050
|
+
btn.setAttribute("aria-label", st.ariaLabel.trim());
|
|
4051
|
+
}
|
|
3076
4052
|
applyBlockStyle(btn, block.style, "button");
|
|
3077
4053
|
const nav = context.nav?.callbacks;
|
|
3078
|
-
|
|
3079
|
-
switch (
|
|
4054
|
+
const runSimple = (spec) => {
|
|
4055
|
+
switch (spec.action) {
|
|
3080
4056
|
case "next":
|
|
3081
|
-
emit(block);
|
|
3082
4057
|
if (nav) nav.onNext();
|
|
3083
4058
|
else context.onClose();
|
|
3084
4059
|
break;
|
|
3085
4060
|
case "prev":
|
|
3086
|
-
emit(block);
|
|
3087
4061
|
nav?.onBack();
|
|
3088
4062
|
break;
|
|
4063
|
+
case "goto":
|
|
4064
|
+
if (nav?.onGoto && typeof spec.targetStep === "number") nav.onGoto(spec.targetStep);
|
|
4065
|
+
else context.onClose();
|
|
4066
|
+
break;
|
|
4067
|
+
case "snooze":
|
|
4068
|
+
snoozeGuide(
|
|
4069
|
+
context.guide.guideId,
|
|
4070
|
+
typeof spec.snoozeEvery === "number" ? spec.snoozeEvery : 1,
|
|
4071
|
+
spec.snoozeUnit ?? "day"
|
|
4072
|
+
);
|
|
4073
|
+
context.onClose();
|
|
4074
|
+
break;
|
|
3089
4075
|
case "url":
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
window.open(
|
|
4076
|
+
if (typeof spec.url === "string" && isSafeUrl(spec.url)) {
|
|
4077
|
+
if (spec.newTab === false) window.location.assign(spec.url);
|
|
4078
|
+
else window.open(spec.url, "_blank", "noopener,noreferrer");
|
|
3093
4079
|
}
|
|
3094
4080
|
break;
|
|
3095
4081
|
case "guide":
|
|
3096
|
-
|
|
3097
|
-
|
|
4082
|
+
if (spec.targetGuideId) context.onOpenGuide?.(spec.targetGuideId);
|
|
4083
|
+
this.closeOrSkip(context);
|
|
4084
|
+
break;
|
|
4085
|
+
default:
|
|
3098
4086
|
this.closeOrSkip(context);
|
|
4087
|
+
}
|
|
4088
|
+
};
|
|
4089
|
+
const runSecond = () => {
|
|
4090
|
+
if (block.secondAction?.action) runSimple(block.secondAction);
|
|
4091
|
+
};
|
|
4092
|
+
const runActions = () => {
|
|
4093
|
+
switch (block.action) {
|
|
4094
|
+
case "next":
|
|
4095
|
+
case "prev":
|
|
4096
|
+
case "goto":
|
|
4097
|
+
case "snooze":
|
|
4098
|
+
case "url":
|
|
4099
|
+
case "guide":
|
|
4100
|
+
emit(block);
|
|
4101
|
+
runSimple(block);
|
|
4102
|
+
runSecond();
|
|
3099
4103
|
break;
|
|
3100
4104
|
case "submit-forms":
|
|
3101
4105
|
case "submit-forms-advance":
|
|
@@ -3114,7 +4118,12 @@ var init_composite_renderer = __esm({
|
|
|
3114
4118
|
default:
|
|
3115
4119
|
emit(block);
|
|
3116
4120
|
this.closeOrSkip(context);
|
|
4121
|
+
runSecond();
|
|
3117
4122
|
}
|
|
4123
|
+
};
|
|
4124
|
+
btn.addEventListener("click", () => {
|
|
4125
|
+
if (submitFirst) this.submitForms(submitFirst, context, idx, btn, runActions);
|
|
4126
|
+
else runActions();
|
|
3118
4127
|
});
|
|
3119
4128
|
return btn;
|
|
3120
4129
|
}
|
|
@@ -3133,17 +4142,27 @@ var init_composite_renderer = __esm({
|
|
|
3133
4142
|
}
|
|
3134
4143
|
answers[field.key] = value;
|
|
3135
4144
|
}
|
|
3136
|
-
if (
|
|
4145
|
+
if (Object.keys(answers).length === 0) {
|
|
4146
|
+
onDone();
|
|
4147
|
+
return;
|
|
4148
|
+
}
|
|
3137
4149
|
btn.disabled = true;
|
|
3138
4150
|
const submitFn = context.onFormSubmit ?? (() => Promise.resolve(true));
|
|
3139
|
-
|
|
4151
|
+
const blockIds = new Set(
|
|
4152
|
+
readers.filter((r) => answers[r.field.key] !== void 0).map((r) => r.blockId ?? "")
|
|
4153
|
+
);
|
|
4154
|
+
const pollId = blockIds.size === 1 ? [...blockIds][0] || void 0 : void 0;
|
|
4155
|
+
void submitFn(answers, { ...pollId ? { pollId } : {}, stepIndex: idx }).then((ok) => {
|
|
3140
4156
|
btn.disabled = false;
|
|
3141
4157
|
if (!ok) return;
|
|
3142
|
-
context.
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
4158
|
+
const lastStep = !context.nav || idx >= context.nav.totalSteps - 1;
|
|
4159
|
+
if (lastStep) {
|
|
4160
|
+
context.onInteraction({
|
|
4161
|
+
guideId: context.guide.guideId,
|
|
4162
|
+
stepIndex: idx,
|
|
4163
|
+
action: "completed"
|
|
4164
|
+
});
|
|
4165
|
+
}
|
|
3147
4166
|
onDone();
|
|
3148
4167
|
});
|
|
3149
4168
|
}
|
|
@@ -3252,6 +4271,10 @@ var init_composite_renderer = __esm({
|
|
|
3252
4271
|
}
|
|
3253
4272
|
};
|
|
3254
4273
|
CLOSE_INSET = "10px";
|
|
4274
|
+
CLOSE_RESERVE_PX = 42;
|
|
4275
|
+
CARD_DEFAULT_PAD_PX = 20;
|
|
4276
|
+
MIN_CARD_WIDTH = 120;
|
|
4277
|
+
NARROW_CARD_WIDTH = 260;
|
|
3255
4278
|
}
|
|
3256
4279
|
});
|
|
3257
4280
|
function applyFloatingPosition(wrapper, placement) {
|
|
@@ -3347,6 +4370,7 @@ var FormRenderer;
|
|
|
3347
4370
|
var init_form_renderer = __esm({
|
|
3348
4371
|
"src/plugins/guides/renderers/form-renderer.ts"() {
|
|
3349
4372
|
init_form_content();
|
|
4373
|
+
init_scroll_lock();
|
|
3350
4374
|
init_base_renderer();
|
|
3351
4375
|
FormRenderer = class extends BaseRenderer {
|
|
3352
4376
|
render(context) {
|
|
@@ -3362,6 +4386,7 @@ var init_form_renderer = __esm({
|
|
|
3362
4386
|
mountFormContent(card, context, doc);
|
|
3363
4387
|
overlay.appendChild(card);
|
|
3364
4388
|
root.appendChild(overlay);
|
|
4389
|
+
this.registerCleanup(lockScroll(doc));
|
|
3365
4390
|
const dismiss = () => {
|
|
3366
4391
|
context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "dismissed" });
|
|
3367
4392
|
context.onClose();
|
|
@@ -3518,12 +4543,25 @@ var ModalRenderer;
|
|
|
3518
4543
|
var init_modal_renderer = __esm({
|
|
3519
4544
|
"src/plugins/guides/renderers/modal-renderer.ts"() {
|
|
3520
4545
|
init_block_builder();
|
|
4546
|
+
init_scroll_lock();
|
|
3521
4547
|
init_walkthrough_block_builder();
|
|
3522
4548
|
init_base_renderer();
|
|
3523
4549
|
ModalRenderer = class extends BaseRenderer {
|
|
3524
4550
|
constructor() {
|
|
3525
4551
|
super(...arguments);
|
|
3526
4552
|
this.overlay = null;
|
|
4553
|
+
/** Suelta el bloqueo de scroll del backdrop (null = sin backdrop). */
|
|
4554
|
+
this.releaseScroll = null;
|
|
4555
|
+
}
|
|
4556
|
+
/** Con backdrop la app de atrás queda congelada; sin él, scrollea normal. */
|
|
4557
|
+
syncScrollLock(step) {
|
|
4558
|
+
const wantLock = step.style?.backdrop !== false;
|
|
4559
|
+
if (wantLock && !this.releaseScroll) {
|
|
4560
|
+
this.releaseScroll = lockScroll(this.host?.ownerDocument ?? document);
|
|
4561
|
+
} else if (!wantLock && this.releaseScroll) {
|
|
4562
|
+
this.releaseScroll();
|
|
4563
|
+
this.releaseScroll = null;
|
|
4564
|
+
}
|
|
3527
4565
|
}
|
|
3528
4566
|
render(context) {
|
|
3529
4567
|
const idx = context.nav?.stepIndex ?? 0;
|
|
@@ -3535,6 +4573,7 @@ var init_modal_renderer = __esm({
|
|
|
3535
4573
|
const overlay = ownerDocument.createElement("div");
|
|
3536
4574
|
overlay.className = overlayClassOf(step);
|
|
3537
4575
|
this.overlay = overlay;
|
|
4576
|
+
this.syncScrollLock(step);
|
|
3538
4577
|
const card = ownerDocument.createElement("div");
|
|
3539
4578
|
card.className = "veo-modal-card";
|
|
3540
4579
|
const dismiss = (action, ctaUrl, meta) => {
|
|
@@ -3592,9 +4631,12 @@ var init_modal_renderer = __esm({
|
|
|
3592
4631
|
}
|
|
3593
4632
|
onLiveUpdate(step) {
|
|
3594
4633
|
if (this.overlay) this.overlay.className = overlayClassOf(step);
|
|
4634
|
+
this.syncScrollLock(step);
|
|
3595
4635
|
}
|
|
3596
4636
|
destroy() {
|
|
3597
4637
|
this.overlay = null;
|
|
4638
|
+
this.releaseScroll?.();
|
|
4639
|
+
this.releaseScroll = null;
|
|
3598
4640
|
super.destroy();
|
|
3599
4641
|
}
|
|
3600
4642
|
};
|
|
@@ -3785,9 +4827,35 @@ function matchesUrl(matcher, pageUrl) {
|
|
|
3785
4827
|
}
|
|
3786
4828
|
}
|
|
3787
4829
|
function stepMatchesPage(loc, pageUrl) {
|
|
3788
|
-
if (!loc || loc.scope === "sitewide"
|
|
4830
|
+
if (!loc || loc.scope === "sitewide") return true;
|
|
4831
|
+
if (loc.scope === "pages") {
|
|
4832
|
+
const rules = loc.rules ?? [];
|
|
4833
|
+
return rules.some((r) => pageRuleMatches(r, pageUrl));
|
|
4834
|
+
}
|
|
4835
|
+
if (!loc.pattern) return true;
|
|
3789
4836
|
return matchesUrl({ type: loc.matchType ?? "prefix", pattern: loc.pattern }, pageUrl);
|
|
3790
4837
|
}
|
|
4838
|
+
function pageRuleMatches(rule, pageUrl) {
|
|
4839
|
+
const pattern = rule.pattern.toLowerCase();
|
|
4840
|
+
const candidates = urlCandidates(pageUrl).map((c) => c.toLowerCase());
|
|
4841
|
+
switch (rule.type) {
|
|
4842
|
+
case "exact":
|
|
4843
|
+
return candidates.some((c) => c === pattern);
|
|
4844
|
+
case "prefix":
|
|
4845
|
+
return candidates.some((c) => c.startsWith(pattern));
|
|
4846
|
+
case "contains":
|
|
4847
|
+
return candidates.some((c) => c.includes(pattern));
|
|
4848
|
+
case "regex":
|
|
4849
|
+
try {
|
|
4850
|
+
const re = new RegExp(rule.pattern, "i");
|
|
4851
|
+
return candidates.some((c) => re.test(c));
|
|
4852
|
+
} catch {
|
|
4853
|
+
return false;
|
|
4854
|
+
}
|
|
4855
|
+
default:
|
|
4856
|
+
return false;
|
|
4857
|
+
}
|
|
4858
|
+
}
|
|
3791
4859
|
var init_url_match = __esm({
|
|
3792
4860
|
"src/plugins/guides/url-match.ts"() {
|
|
3793
4861
|
}
|
|
@@ -3860,6 +4928,7 @@ var PREVIEW_GUIDE_ID, GuidePreviewController, activeController;
|
|
|
3860
4928
|
var init_guide_preview = __esm({
|
|
3861
4929
|
"src/plugins/guides/guide-preview.ts"() {
|
|
3862
4930
|
init_safe_env();
|
|
4931
|
+
init_activation_rules();
|
|
3863
4932
|
init_badge_renderer();
|
|
3864
4933
|
init_banner_renderer();
|
|
3865
4934
|
init_base_renderer();
|
|
@@ -3943,7 +5012,9 @@ var init_guide_preview = __esm({
|
|
|
3943
5012
|
if (event.action === "shown") shown = true;
|
|
3944
5013
|
},
|
|
3945
5014
|
onClose: () => this.close(true),
|
|
3946
|
-
|
|
5015
|
+
// Sin indicación explícita, el badge se abre solo si la guía arranca
|
|
5016
|
+
// sola (como en el runtime); `manual` lo deja cerrado.
|
|
5017
|
+
badgeOpen: this.input?.badgeOpen ?? isAutomaticActivation(this.input?.activationRules),
|
|
3947
5018
|
onTrack: () => {
|
|
3948
5019
|
},
|
|
3949
5020
|
// Encadenado en preview: no-op — el editor no puede montar otra guía
|
|
@@ -3953,6 +5024,7 @@ var init_guide_preview = __esm({
|
|
|
3953
5024
|
// Preview de forms: por defecto simula el envío SIN escribir props;
|
|
3954
5025
|
// el caller puede pasar `formSubmit` real (link de preview).
|
|
3955
5026
|
onFormSubmit: this.input?.formSubmit ?? (() => Promise.resolve(true)),
|
|
5027
|
+
...this.input?.fetchProgress ? { fetchProgress: this.input.fetchProgress } : {},
|
|
3956
5028
|
isPreview: true,
|
|
3957
5029
|
...this.input?.formSubmit && this.input.formSubmitNote !== void 0 ? { previewNote: this.input.formSubmitNote } : {}
|
|
3958
5030
|
});
|
|
@@ -4345,8 +5417,14 @@ function injectBuilderChrome(opts) {
|
|
|
4345
5417
|
stepsList.append(activation);
|
|
4346
5418
|
s.steps.forEach((st, i) => {
|
|
4347
5419
|
const thumb = st.presentation === "inline" ? '<div class="veo-thumb veo-thumb-inline"><i></i><i></i><i></i></div>' : '<div class="veo-thumb"><i></i><i></i><b></b></div>';
|
|
4348
|
-
const
|
|
4349
|
-
card
|
|
5420
|
+
const isStepView = s.section === "step" || s.section === "block" || s.section === "add-block" || s.section === "row" || s.section === "structure";
|
|
5421
|
+
const card = stepCard(thumb, st.label, isStepView && s.activeStep === i, {
|
|
5422
|
+
// Se pueden borrar TODOS los pasos (el panel vuelve a Activación).
|
|
5423
|
+
canRemove: true,
|
|
5424
|
+
onEdit: () => act({ action: "select-step", index: i }),
|
|
5425
|
+
onDuplicate: () => act({ action: "duplicate-step", index: i }),
|
|
5426
|
+
onRemove: () => act({ action: "remove-step", index: i })
|
|
5427
|
+
});
|
|
4350
5428
|
stepsList.append(card);
|
|
4351
5429
|
});
|
|
4352
5430
|
const add = el("button", "veo-step-add", `${svg("plus", 18)}<span>Agregar paso</span>`);
|
|
@@ -4375,16 +5453,40 @@ function injectBuilderChrome(opts) {
|
|
|
4375
5453
|
}
|
|
4376
5454
|
};
|
|
4377
5455
|
}
|
|
4378
|
-
function stepCard(inner, label, active2) {
|
|
4379
|
-
const
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
5456
|
+
function stepCard(inner, label, active2, actions) {
|
|
5457
|
+
const cls = `veo-step${active2 ? " is-active" : ""}`;
|
|
5458
|
+
if (!actions) {
|
|
5459
|
+
const b = el(
|
|
5460
|
+
"button",
|
|
5461
|
+
cls,
|
|
5462
|
+
`<div class="veo-step-box">${inner}</div><span class="veo-step-label"></span>`
|
|
5463
|
+
);
|
|
5464
|
+
b.type = "button";
|
|
5465
|
+
b.querySelector(".veo-step-label").textContent = label;
|
|
5466
|
+
b.setAttribute("aria-pressed", active2 ? "true" : "false");
|
|
5467
|
+
return b;
|
|
5468
|
+
}
|
|
5469
|
+
const card = el(
|
|
5470
|
+
"div",
|
|
5471
|
+
cls,
|
|
5472
|
+
`<div class="veo-step-box">${inner}<div class="veo-step-hover"><div class="veo-step-hover-top"><button type="button" class="veo-step-icon veo-step-remove" title="Eliminar paso" aria-label="Eliminar paso">${svg("trash", 14)}</button><button type="button" class="veo-step-icon veo-step-duplicate" title="Duplicar paso" aria-label="Duplicar paso">${svg("copy", 14)}</button></div><div class="veo-step-hover-main"><button type="button" class="veo-step-edit">${svg("pencil", 14)}<span>Editar</span></button></div></div></div><span class="veo-step-label"></span>`
|
|
4383
5473
|
);
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
5474
|
+
card.querySelector(".veo-step-label").textContent = label;
|
|
5475
|
+
card.setAttribute("aria-current", active2 ? "step" : "false");
|
|
5476
|
+
const remove = card.querySelector(".veo-step-remove");
|
|
5477
|
+
remove.disabled = !actions.canRemove;
|
|
5478
|
+
if (!actions.canRemove) remove.title = "La gu\xEDa necesita al menos un paso";
|
|
5479
|
+
const on = (sel, fn) => {
|
|
5480
|
+
card.querySelector(sel)?.addEventListener("click", (e) => {
|
|
5481
|
+
e.stopPropagation();
|
|
5482
|
+
fn();
|
|
5483
|
+
});
|
|
5484
|
+
};
|
|
5485
|
+
on(".veo-step-edit", actions.onEdit);
|
|
5486
|
+
on(".veo-step-duplicate", actions.onDuplicate);
|
|
5487
|
+
on(".veo-step-remove", actions.onRemove);
|
|
5488
|
+
card.addEventListener("click", actions.onEdit);
|
|
5489
|
+
return card;
|
|
4388
5490
|
}
|
|
4389
5491
|
var CHROME_TOPBAR_H, CHROME_STEPS_H, ICONS, CHROME_CSS;
|
|
4390
5492
|
var init_builder_chrome = __esm({
|
|
@@ -4401,6 +5503,9 @@ var init_builder_chrome = __esm({
|
|
|
4401
5503
|
chevronDown: '<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"/>',
|
|
4402
5504
|
chevronUp: '<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 15.75 7.5-7.5 7.5 7.5"/>',
|
|
4403
5505
|
plus: '<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15"/>',
|
|
5506
|
+
pencil: '<path stroke-linecap="round" stroke-linejoin="round" d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10"/>',
|
|
5507
|
+
copy: '<path stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 0 0-2.25-2.25H6A2.25 2.25 0 0 0 3.75 6v8.25A2.25 2.25 0 0 0 6 16.5h2.25m8.25-8.25H18a2.25 2.25 0 0 1 2.25 2.25V18A2.25 2.25 0 0 1 18 20.25h-7.5A2.25 2.25 0 0 1 8.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 0 0-2.25 2.25v6"/>',
|
|
5508
|
+
trash: '<path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"/>',
|
|
4404
5509
|
cursor: '<path stroke-linecap="round" stroke-linejoin="round" d="M15.042 21.672 13.684 16.6m0 0-2.51 2.225.569-9.47 5.227 7.917-3.286-.672ZM12 2.25V4.5m5.834.166-1.591 1.591M20.25 10.5H18M7.757 14.743l-1.59 1.59M6 10.5H3.75m4.007-4.243-1.59-1.59"/>',
|
|
4405
5510
|
spinner: '<path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182"/>'
|
|
4406
5511
|
};
|
|
@@ -4487,10 +5592,31 @@ var init_builder_chrome = __esm({
|
|
|
4487
5592
|
.veo-steps-list { display: flex; align-items: flex-start; gap: 20px; }
|
|
4488
5593
|
.veo-step { border: 0; background: transparent; padding: 0; cursor: pointer; font: inherit; display: flex; flex-direction: column; align-items: center; gap: 8px; flex: 0 0 auto; }
|
|
4489
5594
|
.veo-step-box {
|
|
5595
|
+
position: relative; overflow: hidden;
|
|
4490
5596
|
width: 116px; height: 64px; border-radius: 10px; background: #fff; border: 2px solid transparent;
|
|
4491
5597
|
display: flex; align-items: center; justify-content: center; color: #2b2623;
|
|
4492
5598
|
box-shadow: 0 1px 2px rgba(0,0,0,0.06);
|
|
4493
5599
|
}
|
|
5600
|
+
/* Capa de acciones del paso: aparece al pasar el mouse o al enfocar un bot\xF3n. */
|
|
5601
|
+
.veo-step-hover {
|
|
5602
|
+
position: absolute; inset: 0; background: #2b2623; border-radius: 8px;
|
|
5603
|
+
display: flex; flex-direction: column; padding: 3px 4px 6px; box-sizing: border-box;
|
|
5604
|
+
opacity: 0; transition: opacity 120ms ease-out;
|
|
5605
|
+
}
|
|
5606
|
+
.veo-step:hover .veo-step-hover, .veo-step:focus-within .veo-step-hover { opacity: 1; }
|
|
5607
|
+
.veo-step-hover-top { display: flex; justify-content: space-between; align-items: center; }
|
|
5608
|
+
.veo-step-hover-main { flex: 1; display: flex; align-items: center; justify-content: center; }
|
|
5609
|
+
.veo-step-icon {
|
|
5610
|
+
border: 0; background: transparent; color: #fff; cursor: pointer; padding: 0;
|
|
5611
|
+
width: 20px; height: 20px; border-radius: 5px; display: inline-flex; align-items: center; justify-content: center;
|
|
5612
|
+
}
|
|
5613
|
+
.veo-step-icon:hover { background: rgba(255,255,255,0.14); }
|
|
5614
|
+
.veo-step-icon:disabled { opacity: 0.35; cursor: default; background: transparent; }
|
|
5615
|
+
.veo-step-edit {
|
|
5616
|
+
width: 100%; margin: 0 6px; border: 0; background: #dbeefc; color: #1d6fb8; font: inherit; font-size: 13px; font-weight: 600;
|
|
5617
|
+
border-radius: 6px; padding: 5px 10px; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; gap: 6px;
|
|
5618
|
+
}
|
|
5619
|
+
.veo-step-edit:hover { background: #cbe4f9; }
|
|
4494
5620
|
.veo-step.is-active .veo-step-box { border-color: #b74d00; }
|
|
4495
5621
|
.veo-step-label { font-size: 13px; color: #6b635d; }
|
|
4496
5622
|
.veo-step.is-active .veo-step-label { color: #2b2623; font-weight: 600; }
|
|
@@ -4528,6 +5654,8 @@ function injectBuilderPanel(opts) {
|
|
|
4528
5654
|
},
|
|
4529
5655
|
isVisible: () => false,
|
|
4530
5656
|
setTopOffset: () => {
|
|
5657
|
+
},
|
|
5658
|
+
setSize: () => {
|
|
4531
5659
|
}
|
|
4532
5660
|
};
|
|
4533
5661
|
}
|
|
@@ -4580,6 +5708,21 @@ function injectBuilderPanel(opts) {
|
|
|
4580
5708
|
card.style.display = next && !(popped && !popped.closed) ? "" : "none";
|
|
4581
5709
|
};
|
|
4582
5710
|
minimize.addEventListener("click", () => setVisible(false));
|
|
5711
|
+
let compactRestore = null;
|
|
5712
|
+
const setSize = (size) => {
|
|
5713
|
+
if (maximized || popped && !popped.closed) return;
|
|
5714
|
+
if (size) {
|
|
5715
|
+
if (!compactRestore) compactRestore = { w: card.offsetWidth, h: card.offsetHeight };
|
|
5716
|
+
card.style.minHeight = "0";
|
|
5717
|
+
card.style.width = `${clamp3(Math.round(size.width), MIN_W, window.innerWidth - MARGIN * 2)}px`;
|
|
5718
|
+
card.style.height = `${clamp3(Math.round(size.height) + HEADER_H, HEADER_H + 80, window.innerHeight - top - MARGIN)}px`;
|
|
5719
|
+
} else if (compactRestore) {
|
|
5720
|
+
card.style.minHeight = "";
|
|
5721
|
+
card.style.width = `${compactRestore.w}px`;
|
|
5722
|
+
card.style.height = `${compactRestore.h}px`;
|
|
5723
|
+
compactRestore = null;
|
|
5724
|
+
}
|
|
5725
|
+
};
|
|
4583
5726
|
maximize.addEventListener("click", () => {
|
|
4584
5727
|
maximized = !maximized;
|
|
4585
5728
|
if (maximized) {
|
|
@@ -4679,6 +5822,7 @@ function injectBuilderPanel(opts) {
|
|
|
4679
5822
|
title.textContent = t;
|
|
4680
5823
|
},
|
|
4681
5824
|
setVisible,
|
|
5825
|
+
setSize,
|
|
4682
5826
|
isVisible: () => visible,
|
|
4683
5827
|
setTopOffset: (px) => {
|
|
4684
5828
|
topOffset = px;
|
|
@@ -5264,6 +6408,9 @@ function initBuilderMode() {
|
|
|
5264
6408
|
let staticTarget = null;
|
|
5265
6409
|
let manipulator = null;
|
|
5266
6410
|
let dropTarget = null;
|
|
6411
|
+
let cardClick = null;
|
|
6412
|
+
let rawBlock = null;
|
|
6413
|
+
let selectedBlockId = null;
|
|
5267
6414
|
let dragGhost = null;
|
|
5268
6415
|
let previewStepIndex = 0;
|
|
5269
6416
|
let pendingPreview = null;
|
|
@@ -5272,6 +6419,7 @@ function initBuilderMode() {
|
|
|
5272
6419
|
let previewTrigger = null;
|
|
5273
6420
|
let previewFired = false;
|
|
5274
6421
|
let previewDelay = null;
|
|
6422
|
+
let previewDismissed = false;
|
|
5275
6423
|
let panelWasVisible = true;
|
|
5276
6424
|
const getTarget = () => panel ? panel.target() : staticTarget;
|
|
5277
6425
|
const post = (event) => {
|
|
@@ -5283,15 +6431,751 @@ function initBuilderMode() {
|
|
|
5283
6431
|
};
|
|
5284
6432
|
const previewMatchesPage = (cmd) => stepMatchesPage(previewStep(cmd)?.pageLocation, window.location.href);
|
|
5285
6433
|
const postPreviewStatus = (payload) => post({ type: "preview-status", payload });
|
|
5286
|
-
const
|
|
6434
|
+
const detachPreviewTools = () => {
|
|
5287
6435
|
manipulator?.detach();
|
|
5288
6436
|
manipulator = null;
|
|
5289
6437
|
dropTarget?.detach();
|
|
5290
6438
|
dropTarget = null;
|
|
6439
|
+
cardClick?.();
|
|
6440
|
+
cardClick = null;
|
|
6441
|
+
rowHover?.();
|
|
6442
|
+
rowHover = null;
|
|
6443
|
+
hoverRowId = null;
|
|
6444
|
+
hoverCard = false;
|
|
6445
|
+
rawBlock?.detach();
|
|
6446
|
+
rawBlock = null;
|
|
6447
|
+
shownPreview = null;
|
|
6448
|
+
};
|
|
6449
|
+
const withholdPreview = () => {
|
|
6450
|
+
detachPreviewTools();
|
|
6451
|
+
shownPreview = null;
|
|
5291
6452
|
closeGuidePreview();
|
|
5292
6453
|
};
|
|
6454
|
+
const FRAME_PAD = 4;
|
|
6455
|
+
const FRAME_MIN_H = 0;
|
|
6456
|
+
const TOOLBAR_H = 30;
|
|
6457
|
+
const ICON = {
|
|
6458
|
+
pencil: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 0 1 1.13-1.897L16.863 4.487Zm0 0L19.5 7.125"/></svg>',
|
|
6459
|
+
copy: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M16.5 8.25V6a2.25 2.25 0 0 0-2.25-2.25H6A2.25 2.25 0 0 0 3.75 6v8.25A2.25 2.25 0 0 0 6 16.5h2.25m8.25-8.25H18a2.25 2.25 0 0 1 2.25 2.25V18A2.25 2.25 0 0 1 18 20.25h-7.5A2.25 2.25 0 0 1 8.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 0 0-2.25 2.25v6"/></svg>',
|
|
6460
|
+
trash: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"/></svg>'
|
|
6461
|
+
};
|
|
6462
|
+
const ICON_MOVE = {
|
|
6463
|
+
left: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M19.5 12h-15m0 0 6.75 6.75M4.5 12l6.75-6.75"/></svg>',
|
|
6464
|
+
right: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4.5 12h15m0 0-6.75-6.75M19.5 12l-6.75 6.75"/></svg>'
|
|
6465
|
+
};
|
|
6466
|
+
const moveAbility = (blocks, blockId) => {
|
|
6467
|
+
for (const row of groupLayout(blocks)) {
|
|
6468
|
+
for (let c = 0; c < row.columns.length; c++) {
|
|
6469
|
+
const col = row.columns[c];
|
|
6470
|
+
if (!col.blocks.some((b) => b.block.editorId === blockId)) continue;
|
|
6471
|
+
return { left: c > 0, right: c < row.columns.length - 1 };
|
|
6472
|
+
}
|
|
6473
|
+
}
|
|
6474
|
+
return { left: false, right: false };
|
|
6475
|
+
};
|
|
6476
|
+
let previewBlocks = [];
|
|
6477
|
+
let hoverRowId = null;
|
|
6478
|
+
let selectedRowId = null;
|
|
6479
|
+
let rowHover = null;
|
|
6480
|
+
let hoverCard = false;
|
|
6481
|
+
let panelSection = null;
|
|
6482
|
+
let shownPreview = null;
|
|
6483
|
+
const structureOf = (step) => {
|
|
6484
|
+
if (!step) return "";
|
|
6485
|
+
const { blocks, ...rest } = step;
|
|
6486
|
+
return JSON.stringify({
|
|
6487
|
+
rest,
|
|
6488
|
+
blocks: (blocks ?? []).map((b) => {
|
|
6489
|
+
const { text: _t, html: _h, style: _s, ...keep } = b;
|
|
6490
|
+
return keep;
|
|
6491
|
+
})
|
|
6492
|
+
});
|
|
6493
|
+
};
|
|
6494
|
+
const tryPatchPreview = (cmd) => {
|
|
6495
|
+
const prev = shownPreview;
|
|
6496
|
+
if (!prev || cmd.guide.guideType !== "composite" || prev.guide.guideType !== "composite") {
|
|
6497
|
+
return false;
|
|
6498
|
+
}
|
|
6499
|
+
const idx = cmd.guide.startStepIndex ?? 0;
|
|
6500
|
+
if ((prev.guide.startStepIndex ?? 0) !== idx) return false;
|
|
6501
|
+
const guideSig = (g) => JSON.stringify({ ...g, guideSteps: void 0 });
|
|
6502
|
+
if (guideSig(prev.guide) !== guideSig(cmd.guide) || prev.editable !== cmd.editable)
|
|
6503
|
+
return false;
|
|
6504
|
+
const host = document.querySelector("[data-veo-guide]");
|
|
6505
|
+
const card = host ? previewCard(host) : null;
|
|
6506
|
+
if (!host || !card) return false;
|
|
6507
|
+
const prevStep = prev.guide.guideSteps[idx];
|
|
6508
|
+
const nextStep = cmd.guide.guideSteps[idx];
|
|
6509
|
+
if (!prevStep || !nextStep) return false;
|
|
6510
|
+
if (structureOf(prevStep) !== structureOf(nextStep)) return false;
|
|
6511
|
+
const doc = card.ownerDocument;
|
|
6512
|
+
const prevBlocks = prevStep.blocks ?? [];
|
|
6513
|
+
const nextBlocks = nextStep.blocks ?? [];
|
|
6514
|
+
for (let i = 0; i < nextBlocks.length; i++) {
|
|
6515
|
+
const a = prevBlocks[i];
|
|
6516
|
+
const b = nextBlocks[i];
|
|
6517
|
+
if (a.text === b.text && a.html === b.html && JSON.stringify(a.style) === JSON.stringify(b.style)) {
|
|
6518
|
+
continue;
|
|
6519
|
+
}
|
|
6520
|
+
if (!b.editorId) return false;
|
|
6521
|
+
const node = card.querySelector(`[data-veo-block-id="${b.editorId}"]`);
|
|
6522
|
+
if (!node) return false;
|
|
6523
|
+
if (b.type === "button") {
|
|
6524
|
+
node.textContent = b.text?.trim() || "OK";
|
|
6525
|
+
node.removeAttribute("style");
|
|
6526
|
+
node.classList.remove("veo-guide-cta--secondary", "veo-guide-cta--link");
|
|
6527
|
+
const st = b.style ?? {};
|
|
6528
|
+
if (st.preset === "secondary" || st.preset === "link") {
|
|
6529
|
+
node.classList.add(`veo-guide-cta--${st.preset}`);
|
|
6530
|
+
}
|
|
6531
|
+
if (typeof st.ariaLabel === "string" && st.ariaLabel.trim()) {
|
|
6532
|
+
node.setAttribute("aria-label", st.ariaLabel.trim());
|
|
6533
|
+
} else node.removeAttribute("aria-label");
|
|
6534
|
+
applyBlockStyle(node, b.style, "button");
|
|
6535
|
+
continue;
|
|
6536
|
+
}
|
|
6537
|
+
if (b.type === "code" || b.type === "form") return false;
|
|
6538
|
+
const fresh = buildContentBlock(b, doc, { editing: true }) ?? (() => {
|
|
6539
|
+
const el2 = doc.createElement("div");
|
|
6540
|
+
el2.className = "veo-block-placeholder";
|
|
6541
|
+
el2.textContent = b.type === "image" ? "Imagen (sin URL)" : b.type === "video" ? "Video (elige proveedor y pega la URL)" : "Bloque vac\xEDo";
|
|
6542
|
+
return el2;
|
|
6543
|
+
})();
|
|
6544
|
+
fresh.dataset.veoBlock = node.dataset.veoBlock ?? String(i);
|
|
6545
|
+
fresh.dataset.veoBlockId = b.editorId;
|
|
6546
|
+
node.replaceWith(fresh);
|
|
6547
|
+
}
|
|
6548
|
+
shownPreview = cmd;
|
|
6549
|
+
previewBlocks = nextBlocks;
|
|
6550
|
+
applyBlockHighlight(host, idx);
|
|
6551
|
+
renderGapZones(host, idx, nextBlocks);
|
|
6552
|
+
return true;
|
|
6553
|
+
};
|
|
6554
|
+
const previewCard = (host) => host.shadowRoot?.querySelector(".veo-card") ?? null;
|
|
6555
|
+
const clearBlockHighlight = (host) => {
|
|
6556
|
+
const card = previewCard(host);
|
|
6557
|
+
if (!card) return;
|
|
6558
|
+
for (const el2 of card.querySelectorAll(".veo-block--selected")) {
|
|
6559
|
+
el2.classList.remove("veo-block--selected");
|
|
6560
|
+
}
|
|
6561
|
+
card.querySelector(".veo-block-toolbar")?.remove();
|
|
6562
|
+
card.querySelector(".veo-block-frame")?.remove();
|
|
6563
|
+
};
|
|
6564
|
+
const applyBlockHighlight = (host, stepIndex) => {
|
|
6565
|
+
clearBlockHighlight(host);
|
|
6566
|
+
const card = previewCard(host);
|
|
6567
|
+
if (!card) return;
|
|
6568
|
+
const el2 = selectedBlockId ? card.querySelector(`[data-veo-block-id="${selectedBlockId}"]`) : null;
|
|
6569
|
+
if (!el2 || !selectedBlockId) {
|
|
6570
|
+
renderRowChrome(host, stepIndex);
|
|
6571
|
+
return;
|
|
6572
|
+
}
|
|
6573
|
+
el2.classList.add("veo-block--selected");
|
|
6574
|
+
const doc = card.ownerDocument;
|
|
6575
|
+
const cs = getComputedStyle(card);
|
|
6576
|
+
const padL = Number.parseFloat(cs.paddingLeft) || 0;
|
|
6577
|
+
const padT = Number.parseFloat(cs.paddingTop) || 0;
|
|
6578
|
+
const padR = Number.parseFloat(cs.paddingRight) || 0;
|
|
6579
|
+
const padB = Number.parseFloat(cs.paddingBottom) || 0;
|
|
6580
|
+
const parent = el2.parentElement;
|
|
6581
|
+
const inCol = parent?.classList.contains("veo-col") ? parent : null;
|
|
6582
|
+
const boxL = inCol ? inCol.offsetLeft : padL;
|
|
6583
|
+
const boxR = inCol ? inCol.offsetLeft + inCol.offsetWidth : card.clientWidth - padR;
|
|
6584
|
+
const boxT = padT;
|
|
6585
|
+
const boxB = card.clientHeight - padB;
|
|
6586
|
+
const fx = Math.max(boxL, el2.offsetLeft - FRAME_PAD);
|
|
6587
|
+
const fr = Math.min(boxR, el2.offsetLeft + el2.offsetWidth + FRAME_PAD);
|
|
6588
|
+
const fw = Math.max(0, fr - fx);
|
|
6589
|
+
const fy = Math.max(boxT, el2.offsetTop - FRAME_PAD);
|
|
6590
|
+
const fb = Math.min(boxB, el2.offsetTop + el2.offsetHeight + FRAME_PAD);
|
|
6591
|
+
const fh = Math.max(FRAME_MIN_H, fb - fy);
|
|
6592
|
+
const frame = doc.createElement("div");
|
|
6593
|
+
frame.className = "veo-block-frame";
|
|
6594
|
+
frame.setAttribute("data-veo-builder-chrome", "");
|
|
6595
|
+
frame.style.left = `${fx}px`;
|
|
6596
|
+
frame.style.top = `${fy}px`;
|
|
6597
|
+
frame.style.width = `${fw}px`;
|
|
6598
|
+
frame.style.height = `${fh}px`;
|
|
6599
|
+
card.appendChild(frame);
|
|
6600
|
+
const bar = doc.createElement("div");
|
|
6601
|
+
bar.className = "veo-block-toolbar";
|
|
6602
|
+
bar.setAttribute("data-veo-builder-chrome", "");
|
|
6603
|
+
const blockId = selectedBlockId;
|
|
6604
|
+
const mk = (action, label, icon, enabled = true) => {
|
|
6605
|
+
const b = doc.createElement("button");
|
|
6606
|
+
b.type = "button";
|
|
6607
|
+
b.title = label;
|
|
6608
|
+
b.setAttribute("aria-label", label);
|
|
6609
|
+
b.className = `veo-block-toolbar-${action}`;
|
|
6610
|
+
b.innerHTML = icon;
|
|
6611
|
+
b.disabled = !enabled;
|
|
6612
|
+
b.addEventListener("click", (e) => {
|
|
6613
|
+
e.preventDefault();
|
|
6614
|
+
e.stopPropagation();
|
|
6615
|
+
post({ type: "block-action", payload: { stepIndex, blockId, action } });
|
|
6616
|
+
});
|
|
6617
|
+
bar.appendChild(b);
|
|
6618
|
+
};
|
|
6619
|
+
const can = moveAbility(previewBlocks, blockId);
|
|
6620
|
+
mk("edit", "Editar", ICON.pencil);
|
|
6621
|
+
mk("duplicate", "Duplicar", ICON.copy);
|
|
6622
|
+
mk("remove", "Eliminar", ICON.trash);
|
|
6623
|
+
mk("move-left", "Mover a la izquierda", ICON_MOVE.left, can.left);
|
|
6624
|
+
mk("move-right", "Mover a la derecha", ICON_MOVE.right, can.right);
|
|
6625
|
+
bar.style.right = `${Math.max(0, card.clientWidth - fr)}px`;
|
|
6626
|
+
card.appendChild(bar);
|
|
6627
|
+
const barH = bar.offsetHeight || TOOLBAR_H;
|
|
6628
|
+
bar.dataset.veoTopAbove = String(fy - barH - 6);
|
|
6629
|
+
bar.dataset.veoTopBelow = String(fb + 6);
|
|
6630
|
+
bar.style.top = bar.dataset.veoTopAbove;
|
|
6631
|
+
renderRowChrome(host, stepIndex);
|
|
6632
|
+
};
|
|
6633
|
+
const chromeRect = (el2) => ({
|
|
6634
|
+
left: el2.offsetLeft,
|
|
6635
|
+
top: el2.offsetTop,
|
|
6636
|
+
right: el2.offsetLeft + el2.offsetWidth,
|
|
6637
|
+
bottom: el2.offsetTop + el2.offsetHeight
|
|
6638
|
+
});
|
|
6639
|
+
const intersects = (a, b) => a.left < b.right && b.left < a.right && a.top < b.bottom && b.top < a.bottom;
|
|
6640
|
+
const resolveToolbarOverlaps = (card) => {
|
|
6641
|
+
const cardBar = card.querySelector(".veo-card-toolbar");
|
|
6642
|
+
const blockBar = card.querySelector(".veo-block-toolbar");
|
|
6643
|
+
const rowBar = card.querySelector(".veo-row-toolbar");
|
|
6644
|
+
if (blockBar?.dataset.veoTopAbove) {
|
|
6645
|
+
blockBar.style.top = `${blockBar.dataset.veoTopAbove}px`;
|
|
6646
|
+
if (cardBar && intersects(chromeRect(blockBar), chromeRect(cardBar))) {
|
|
6647
|
+
blockBar.style.top = `${blockBar.dataset.veoTopBelow}px`;
|
|
6648
|
+
}
|
|
6649
|
+
}
|
|
6650
|
+
if (rowBar?.dataset.veoRight) {
|
|
6651
|
+
rowBar.style.left = "";
|
|
6652
|
+
rowBar.style.right = `${rowBar.dataset.veoRight}px`;
|
|
6653
|
+
rowBar.style.top = `${rowBar.dataset.veoTopBelow}px`;
|
|
6654
|
+
const others = [cardBar, blockBar].filter((b) => !!b);
|
|
6655
|
+
const clashes = () => others.some((o) => intersects(chromeRect(rowBar), chromeRect(o)));
|
|
6656
|
+
if (clashes()) {
|
|
6657
|
+
rowBar.style.right = "";
|
|
6658
|
+
rowBar.style.left = `${rowBar.dataset.veoLeft}px`;
|
|
6659
|
+
}
|
|
6660
|
+
if (clashes() && blockBar) {
|
|
6661
|
+
rowBar.style.left = "";
|
|
6662
|
+
rowBar.style.right = `${rowBar.dataset.veoRight}px`;
|
|
6663
|
+
rowBar.style.top = `${chromeRect(blockBar).bottom + 6}px`;
|
|
6664
|
+
}
|
|
6665
|
+
}
|
|
6666
|
+
};
|
|
6667
|
+
const rectOf = (el2) => ({
|
|
6668
|
+
left: el2.offsetLeft,
|
|
6669
|
+
top: el2.offsetTop,
|
|
6670
|
+
right: el2.offsetLeft + el2.offsetWidth,
|
|
6671
|
+
bottom: el2.offsetTop + el2.offsetHeight
|
|
6672
|
+
});
|
|
6673
|
+
const union = (rs) => rs.length === 0 ? null : rs.reduce((acc, r) => ({
|
|
6674
|
+
left: Math.min(acc.left, r.left),
|
|
6675
|
+
top: Math.min(acc.top, r.top),
|
|
6676
|
+
right: Math.max(acc.right, r.right),
|
|
6677
|
+
bottom: Math.max(acc.bottom, r.bottom)
|
|
6678
|
+
}));
|
|
6679
|
+
const layoutRows = (card, blocks) => {
|
|
6680
|
+
const elOf = (b) => b.editorId ? card.querySelector(`[data-veo-block-id="${b.editorId}"]`) : null;
|
|
6681
|
+
const cs = getComputedStyle(card);
|
|
6682
|
+
const padL = Number.parseFloat(cs.paddingLeft) || 0;
|
|
6683
|
+
const padR = Number.parseFloat(cs.paddingRight) || 0;
|
|
6684
|
+
const contentLeft = padL;
|
|
6685
|
+
const contentRight = card.clientWidth - padR;
|
|
6686
|
+
const rows = groupLayout(blocks).map((row) => {
|
|
6687
|
+
const columns = row.columns.map((col) => {
|
|
6688
|
+
const items = col.blocks.map(({ block }) => ({ block, el: elOf(block) })).filter(
|
|
6689
|
+
(x) => !!x.el && !!x.block.editorId
|
|
6690
|
+
);
|
|
6691
|
+
const blocksRect2 = union(items.map((x) => rectOf(x.el)));
|
|
6692
|
+
if (!blocksRect2) return null;
|
|
6693
|
+
const colEl = items[0]?.el.parentElement;
|
|
6694
|
+
const rect2 = colEl?.classList.contains("veo-col") ? rectOf(colEl) : blocksRect2;
|
|
6695
|
+
return { items, rect: rect2 };
|
|
6696
|
+
}).filter((c) => c !== null);
|
|
6697
|
+
const blocksRect = union(columns.map((c) => c.rect));
|
|
6698
|
+
if (!blocksRect) return null;
|
|
6699
|
+
const rowEl = columns[0]?.items[0]?.el.parentElement?.parentElement;
|
|
6700
|
+
const rect = rowEl?.classList.contains("veo-row") ? rectOf(rowEl) : { ...blocksRect, left: contentLeft, right: contentRight };
|
|
6701
|
+
const id = columns[0]?.items[0]?.block.editorId;
|
|
6702
|
+
return { id, row: row.row, columns, rect };
|
|
6703
|
+
}).filter((r) => r !== null);
|
|
6704
|
+
return { rows, content: { left: contentLeft, right: contentRight } };
|
|
6705
|
+
};
|
|
6706
|
+
const clearRowChrome = (card) => {
|
|
6707
|
+
card.querySelector(".veo-row-frame")?.remove();
|
|
6708
|
+
card.querySelector(".veo-row-toolbar")?.remove();
|
|
6709
|
+
};
|
|
6710
|
+
const renderRowChrome = (host, stepIndex) => {
|
|
6711
|
+
const card = previewCard(host);
|
|
6712
|
+
if (!card) return;
|
|
6713
|
+
clearRowChrome(card);
|
|
6714
|
+
resolveToolbarOverlaps(card);
|
|
6715
|
+
const { rows } = layoutRows(card, previewBlocks);
|
|
6716
|
+
const rowOfSelected = selectedBlockId ? rows.find(
|
|
6717
|
+
(r) => r.columns.some((c) => c.items.some((i) => i.block.editorId === selectedBlockId))
|
|
6718
|
+
) : void 0;
|
|
6719
|
+
const target = (selectedRowId ? rows.find((r) => r.id === selectedRowId) : void 0) ?? rowOfSelected ?? (hoverRowId ? rows.find((r) => r.id === hoverRowId) : void 0);
|
|
6720
|
+
if (!target) return;
|
|
6721
|
+
const doc = card.ownerDocument;
|
|
6722
|
+
const cs = getComputedStyle(card);
|
|
6723
|
+
const padT = Number.parseFloat(cs.paddingTop) || 0;
|
|
6724
|
+
const padB = Number.parseFloat(cs.paddingBottom) || 0;
|
|
6725
|
+
const PAD = 6;
|
|
6726
|
+
const fx = Math.max(0, target.rect.left - PAD);
|
|
6727
|
+
const fr = Math.min(card.clientWidth, target.rect.right + PAD);
|
|
6728
|
+
const fy = Math.max(Math.min(padT, target.rect.top), target.rect.top - PAD);
|
|
6729
|
+
const fb = Math.min(
|
|
6730
|
+
Math.max(card.clientHeight - padB, target.rect.bottom),
|
|
6731
|
+
target.rect.bottom + PAD
|
|
6732
|
+
);
|
|
6733
|
+
const frame = doc.createElement("div");
|
|
6734
|
+
frame.className = "veo-row-frame";
|
|
6735
|
+
frame.setAttribute("data-veo-builder-chrome", "");
|
|
6736
|
+
frame.style.left = `${fx}px`;
|
|
6737
|
+
frame.style.top = `${fy}px`;
|
|
6738
|
+
frame.style.width = `${Math.max(0, fr - fx)}px`;
|
|
6739
|
+
frame.style.height = `${Math.max(0, fb - fy)}px`;
|
|
6740
|
+
card.appendChild(frame);
|
|
6741
|
+
const bar = doc.createElement("div");
|
|
6742
|
+
bar.className = "veo-row-toolbar";
|
|
6743
|
+
bar.setAttribute("data-veo-builder-chrome", "");
|
|
6744
|
+
const blockId = target.id;
|
|
6745
|
+
const mk = (action, label, icon, text) => {
|
|
6746
|
+
const b = doc.createElement("button");
|
|
6747
|
+
b.type = "button";
|
|
6748
|
+
b.title = label;
|
|
6749
|
+
b.setAttribute("aria-label", label);
|
|
6750
|
+
b.className = `veo-row-toolbar-${action}`;
|
|
6751
|
+
b.innerHTML = icon;
|
|
6752
|
+
if (text) b.append(text);
|
|
6753
|
+
b.addEventListener("click", (e) => {
|
|
6754
|
+
e.preventDefault();
|
|
6755
|
+
e.stopPropagation();
|
|
6756
|
+
post({ type: "row-action", payload: { stepIndex, blockId, action } });
|
|
6757
|
+
});
|
|
6758
|
+
bar.appendChild(b);
|
|
6759
|
+
};
|
|
6760
|
+
mk("edit", "Editar fila", ICON.pencil, "Editar fila");
|
|
6761
|
+
mk("duplicate", "Duplicar fila", ICON.copy);
|
|
6762
|
+
mk("remove", "Eliminar fila", ICON.trash);
|
|
6763
|
+
card.appendChild(bar);
|
|
6764
|
+
bar.dataset.veoRight = String(Math.max(0, card.clientWidth - fr));
|
|
6765
|
+
bar.dataset.veoLeft = String(fx);
|
|
6766
|
+
bar.dataset.veoTopBelow = String(fb + 6);
|
|
6767
|
+
resolveToolbarOverlaps(card);
|
|
6768
|
+
};
|
|
6769
|
+
const renderCardChrome = (host, stepIndex) => {
|
|
6770
|
+
const card = previewCard(host);
|
|
6771
|
+
if (!card) return;
|
|
6772
|
+
card.querySelector(".veo-card-frame")?.remove();
|
|
6773
|
+
card.querySelector(".veo-card-toolbar")?.remove();
|
|
6774
|
+
if (!hoverCard) {
|
|
6775
|
+
resolveToolbarOverlaps(card);
|
|
6776
|
+
return;
|
|
6777
|
+
}
|
|
6778
|
+
const doc = card.ownerDocument;
|
|
6779
|
+
const frame = doc.createElement("div");
|
|
6780
|
+
frame.className = "veo-card-frame";
|
|
6781
|
+
frame.setAttribute("data-veo-builder-chrome", "");
|
|
6782
|
+
card.appendChild(frame);
|
|
6783
|
+
const bar = doc.createElement("div");
|
|
6784
|
+
bar.className = "veo-card-toolbar";
|
|
6785
|
+
bar.setAttribute("data-veo-builder-chrome", "");
|
|
6786
|
+
const mk = (action, label, active2) => {
|
|
6787
|
+
const b = doc.createElement("button");
|
|
6788
|
+
b.type = "button";
|
|
6789
|
+
b.className = `veo-card-toolbar-${action}${active2 ? " is-active" : ""}`;
|
|
6790
|
+
b.setAttribute("aria-pressed", active2 ? "true" : "false");
|
|
6791
|
+
b.textContent = label;
|
|
6792
|
+
b.addEventListener("click", (e) => {
|
|
6793
|
+
e.preventDefault();
|
|
6794
|
+
e.stopPropagation();
|
|
6795
|
+
post({ type: "card-action", payload: { stepIndex, action } });
|
|
6796
|
+
});
|
|
6797
|
+
bar.appendChild(b);
|
|
6798
|
+
};
|
|
6799
|
+
mk("edit-container", "Editar contenedor", panelSection === "step");
|
|
6800
|
+
mk("edit-rows", "Editar filas", panelSection === "structure");
|
|
6801
|
+
card.appendChild(bar);
|
|
6802
|
+
bar.style.top = `${-Math.round((bar.offsetHeight || 34) / 2)}px`;
|
|
6803
|
+
resolveToolbarOverlaps(card);
|
|
6804
|
+
};
|
|
6805
|
+
const attachRowHover = (host, stepIndex) => {
|
|
6806
|
+
const onMove = (e) => {
|
|
6807
|
+
const card = previewCard(host);
|
|
6808
|
+
if (!card) return;
|
|
6809
|
+
const overCard = e.composedPath().some(
|
|
6810
|
+
(n) => n instanceof Element && (n.classList.contains("veo-card") || n.classList.contains("veo-card-toolbar"))
|
|
6811
|
+
);
|
|
6812
|
+
if (overCard !== hoverCard) {
|
|
6813
|
+
hoverCard = overCard;
|
|
6814
|
+
renderCardChrome(host, stepIndex);
|
|
6815
|
+
}
|
|
6816
|
+
if (e.composedPath().some((n) => n instanceof Element && n.classList.contains("veo-row-toolbar"))) {
|
|
6817
|
+
return;
|
|
6818
|
+
}
|
|
6819
|
+
const rect = card.getBoundingClientRect();
|
|
6820
|
+
const x = e.clientX - rect.left - card.clientLeft;
|
|
6821
|
+
const y = e.clientY - rect.top - card.clientTop;
|
|
6822
|
+
const bar = card.querySelector(".veo-row-toolbar");
|
|
6823
|
+
if (bar) {
|
|
6824
|
+
const AIR = 10;
|
|
6825
|
+
if (x >= bar.offsetLeft - AIR && x <= bar.offsetLeft + bar.offsetWidth + AIR && y >= bar.offsetTop - 14 && y <= bar.offsetTop + bar.offsetHeight + AIR) {
|
|
6826
|
+
return;
|
|
6827
|
+
}
|
|
6828
|
+
}
|
|
6829
|
+
const TOL = 6;
|
|
6830
|
+
const hit = layoutRows(card, previewBlocks).rows.find(
|
|
6831
|
+
(r) => x >= r.rect.left - TOL && x <= r.rect.right + TOL && y >= r.rect.top - TOL && y <= r.rect.bottom + TOL
|
|
6832
|
+
);
|
|
6833
|
+
const next = hit?.id ?? null;
|
|
6834
|
+
if (next === hoverRowId) return;
|
|
6835
|
+
hoverRowId = next;
|
|
6836
|
+
renderRowChrome(host, stepIndex);
|
|
6837
|
+
};
|
|
6838
|
+
const onLeave = () => {
|
|
6839
|
+
if (hoverCard) {
|
|
6840
|
+
hoverCard = false;
|
|
6841
|
+
renderCardChrome(host, stepIndex);
|
|
6842
|
+
}
|
|
6843
|
+
if (hoverRowId === null) return;
|
|
6844
|
+
hoverRowId = null;
|
|
6845
|
+
renderRowChrome(host, stepIndex);
|
|
6846
|
+
};
|
|
6847
|
+
host.addEventListener("mousemove", onMove);
|
|
6848
|
+
host.addEventListener("mouseleave", onLeave);
|
|
6849
|
+
return () => {
|
|
6850
|
+
host.removeEventListener("mousemove", onMove);
|
|
6851
|
+
host.removeEventListener("mouseleave", onLeave);
|
|
6852
|
+
};
|
|
6853
|
+
};
|
|
6854
|
+
const renderGapZones = (host, stepIndex, blocks) => {
|
|
6855
|
+
const card = previewCard(host);
|
|
6856
|
+
if (!card) return;
|
|
6857
|
+
for (const z of card.querySelectorAll(".veo-gap")) z.remove();
|
|
6858
|
+
const doc = card.ownerDocument;
|
|
6859
|
+
const GAP = 18;
|
|
6860
|
+
const EDGE2 = 4;
|
|
6861
|
+
const zone = (kind, x, y, w, h, label, blockId, action) => {
|
|
6862
|
+
const z = doc.createElement("div");
|
|
6863
|
+
z.className = `veo-gap veo-gap--${kind}`;
|
|
6864
|
+
z.setAttribute("data-veo-builder-chrome", "");
|
|
6865
|
+
z.dataset.veoGapAction = action;
|
|
6866
|
+
z.style.left = `${x}px`;
|
|
6867
|
+
z.style.top = `${y}px`;
|
|
6868
|
+
z.style.width = `${Math.max(w, GAP)}px`;
|
|
6869
|
+
z.style.height = `${Math.max(h, GAP)}px`;
|
|
6870
|
+
const line = doc.createElement("i");
|
|
6871
|
+
const btn = doc.createElement("button");
|
|
6872
|
+
btn.type = "button";
|
|
6873
|
+
btn.title = label;
|
|
6874
|
+
btn.setAttribute("aria-label", label);
|
|
6875
|
+
btn.textContent = "+";
|
|
6876
|
+
btn.addEventListener("click", (e) => {
|
|
6877
|
+
e.preventDefault();
|
|
6878
|
+
e.stopPropagation();
|
|
6879
|
+
post({ type: "block-layout", payload: { stepIndex, blockId, action } });
|
|
6880
|
+
});
|
|
6881
|
+
z.append(line, btn);
|
|
6882
|
+
card.appendChild(z);
|
|
6883
|
+
};
|
|
6884
|
+
const { rows, content } = layoutRows(card, blocks);
|
|
6885
|
+
if (rows.length === 0) return;
|
|
6886
|
+
const cw = content.right - content.left;
|
|
6887
|
+
rows.forEach((row, k) => {
|
|
6888
|
+
const first = row.columns[0]?.items[0]?.block.editorId;
|
|
6889
|
+
if (k === 0) {
|
|
6890
|
+
zone(
|
|
6891
|
+
"h",
|
|
6892
|
+
content.left,
|
|
6893
|
+
row.rect.top - EDGE2 - GAP,
|
|
6894
|
+
cw,
|
|
6895
|
+
GAP,
|
|
6896
|
+
"Agregar fila",
|
|
6897
|
+
first,
|
|
6898
|
+
"row-before"
|
|
6899
|
+
);
|
|
6900
|
+
} else {
|
|
6901
|
+
const prev = rows[k - 1];
|
|
6902
|
+
const y = (prev.rect.bottom + row.rect.top) / 2 - GAP / 2;
|
|
6903
|
+
zone("h", content.left, y, cw, GAP, "Agregar fila", first, "row-before");
|
|
6904
|
+
}
|
|
6905
|
+
if (k === rows.length - 1) {
|
|
6906
|
+
zone(
|
|
6907
|
+
"h",
|
|
6908
|
+
content.left,
|
|
6909
|
+
row.rect.bottom + EDGE2,
|
|
6910
|
+
cw,
|
|
6911
|
+
GAP,
|
|
6912
|
+
"Agregar fila",
|
|
6913
|
+
first,
|
|
6914
|
+
"row-after"
|
|
6915
|
+
);
|
|
6916
|
+
}
|
|
6917
|
+
});
|
|
6918
|
+
for (const row of rows) {
|
|
6919
|
+
const rh = row.rect.bottom - row.rect.top;
|
|
6920
|
+
row.columns.forEach((col, j) => {
|
|
6921
|
+
const id = col.items[0]?.block.editorId;
|
|
6922
|
+
if (j === 0) {
|
|
6923
|
+
zone(
|
|
6924
|
+
"v",
|
|
6925
|
+
row.rect.left - EDGE2 - GAP,
|
|
6926
|
+
row.rect.top,
|
|
6927
|
+
GAP,
|
|
6928
|
+
rh,
|
|
6929
|
+
"Agregar columna",
|
|
6930
|
+
id,
|
|
6931
|
+
"col-before"
|
|
6932
|
+
);
|
|
6933
|
+
} else {
|
|
6934
|
+
const prev = row.columns[j - 1];
|
|
6935
|
+
const x = (prev.rect.right + col.rect.left) / 2 - GAP / 2;
|
|
6936
|
+
zone("v", x, row.rect.top, GAP, rh, "Agregar columna", id, "col-before");
|
|
6937
|
+
}
|
|
6938
|
+
if (j === row.columns.length - 1) {
|
|
6939
|
+
zone(
|
|
6940
|
+
"v",
|
|
6941
|
+
row.rect.right + EDGE2,
|
|
6942
|
+
row.rect.top,
|
|
6943
|
+
GAP,
|
|
6944
|
+
rh,
|
|
6945
|
+
"Agregar columna",
|
|
6946
|
+
id,
|
|
6947
|
+
"col-after"
|
|
6948
|
+
);
|
|
6949
|
+
}
|
|
6950
|
+
});
|
|
6951
|
+
}
|
|
6952
|
+
for (const row of rows) {
|
|
6953
|
+
for (const col of row.columns) {
|
|
6954
|
+
const w = col.rect.right - col.rect.left;
|
|
6955
|
+
col.items.forEach((it, i) => {
|
|
6956
|
+
const next = col.items[i + 1];
|
|
6957
|
+
if (next) {
|
|
6958
|
+
const a = rectOf(it.el);
|
|
6959
|
+
const b = rectOf(next.el);
|
|
6960
|
+
const y = (a.bottom + b.top) / 2 - GAP / 2;
|
|
6961
|
+
zone(
|
|
6962
|
+
"h",
|
|
6963
|
+
col.rect.left,
|
|
6964
|
+
y,
|
|
6965
|
+
w,
|
|
6966
|
+
GAP,
|
|
6967
|
+
"Agregar bloque",
|
|
6968
|
+
it.block.editorId,
|
|
6969
|
+
"stack-after"
|
|
6970
|
+
);
|
|
6971
|
+
} else if (row.columns.length > 1 && col.rect.bottom < row.rect.bottom - GAP) {
|
|
6972
|
+
zone(
|
|
6973
|
+
"h",
|
|
6974
|
+
col.rect.left,
|
|
6975
|
+
col.rect.bottom + EDGE2,
|
|
6976
|
+
w,
|
|
6977
|
+
GAP,
|
|
6978
|
+
"Agregar bloque",
|
|
6979
|
+
it.block.editorId,
|
|
6980
|
+
"stack-after"
|
|
6981
|
+
);
|
|
6982
|
+
}
|
|
6983
|
+
});
|
|
6984
|
+
}
|
|
6985
|
+
}
|
|
6986
|
+
};
|
|
6987
|
+
const attachCardClick = (host, stepIndex) => {
|
|
6988
|
+
let downAt = null;
|
|
6989
|
+
const onDown = (e) => {
|
|
6990
|
+
downAt = { x: e.clientX, y: e.clientY };
|
|
6991
|
+
};
|
|
6992
|
+
const onClick = (e) => {
|
|
6993
|
+
const path = e.composedPath();
|
|
6994
|
+
if (path.some(
|
|
6995
|
+
(n) => n instanceof Element && (n.classList.contains("veo-block-toolbar") || n.classList.contains("veo-row-toolbar") || n.classList.contains("veo-card-toolbar") || n.classList.contains("veo-gap"))
|
|
6996
|
+
)) {
|
|
6997
|
+
return;
|
|
6998
|
+
}
|
|
6999
|
+
const inCard = path.some((n) => n instanceof Element && n.classList.contains("veo-card"));
|
|
7000
|
+
if (!inCard) return;
|
|
7001
|
+
if (downAt && Math.hypot(e.clientX - downAt.x, e.clientY - downAt.y) > 4) return;
|
|
7002
|
+
const blockEl = path.find(
|
|
7003
|
+
(n) => n instanceof HTMLElement && !!n.dataset.veoBlockId
|
|
7004
|
+
);
|
|
7005
|
+
if (blockEl) {
|
|
7006
|
+
if (path.some((n) => n instanceof Element && n.matches("button, a"))) {
|
|
7007
|
+
e.preventDefault();
|
|
7008
|
+
e.stopPropagation();
|
|
7009
|
+
}
|
|
7010
|
+
selectedBlockId = blockEl.dataset.veoBlockId ?? null;
|
|
7011
|
+
applyBlockHighlight(host, stepIndex);
|
|
7012
|
+
if (selectedBlockId) {
|
|
7013
|
+
post({ type: "block-clicked", payload: { stepIndex, blockId: selectedBlockId } });
|
|
7014
|
+
}
|
|
7015
|
+
return;
|
|
7016
|
+
}
|
|
7017
|
+
const interactive = path.some(
|
|
7018
|
+
(n) => n instanceof Element && n.matches("button, a, input, textarea, select, label, [contenteditable]")
|
|
7019
|
+
);
|
|
7020
|
+
if (interactive) return;
|
|
7021
|
+
selectedBlockId = null;
|
|
7022
|
+
clearBlockHighlight(host);
|
|
7023
|
+
renderRowChrome(host, stepIndex);
|
|
7024
|
+
post({ type: "card-clicked", payload: { stepIndex } });
|
|
7025
|
+
};
|
|
7026
|
+
host.addEventListener("mousedown", onDown, true);
|
|
7027
|
+
host.addEventListener("click", onClick, true);
|
|
7028
|
+
return () => {
|
|
7029
|
+
host.removeEventListener("mousedown", onDown, true);
|
|
7030
|
+
host.removeEventListener("click", onClick, true);
|
|
7031
|
+
};
|
|
7032
|
+
};
|
|
7033
|
+
const attachRawBlock = (stepIndex, blockId) => {
|
|
7034
|
+
const nodes = Array.from(
|
|
7035
|
+
document.querySelectorAll(`[${RAW_BLOCK_ATTR}="${blockId}"]`)
|
|
7036
|
+
);
|
|
7037
|
+
if (nodes.length === 0) return null;
|
|
7038
|
+
const doc = document;
|
|
7039
|
+
const box = doc.createElement("div");
|
|
7040
|
+
box.setAttribute("data-veo-builder-chrome", "");
|
|
7041
|
+
box.style.cssText = "position:fixed;left:0;top:0;width:0;height:0;z-index:2147483646;";
|
|
7042
|
+
box.style.display = "none";
|
|
7043
|
+
const shadow = box.attachShadow({ mode: "open" });
|
|
7044
|
+
const style = doc.createElement("style");
|
|
7045
|
+
style.textContent = GUIDE_STYLES;
|
|
7046
|
+
shadow.appendChild(style);
|
|
7047
|
+
const frame = doc.createElement("div");
|
|
7048
|
+
frame.className = "veo-block-frame";
|
|
7049
|
+
frame.style.position = "fixed";
|
|
7050
|
+
const bar = doc.createElement("div");
|
|
7051
|
+
bar.className = "veo-block-toolbar";
|
|
7052
|
+
bar.style.position = "fixed";
|
|
7053
|
+
const mk = (action, label, icon, enabled = true) => {
|
|
7054
|
+
const b = doc.createElement("button");
|
|
7055
|
+
b.type = "button";
|
|
7056
|
+
b.title = label;
|
|
7057
|
+
b.setAttribute("aria-label", label);
|
|
7058
|
+
b.className = `veo-block-toolbar-${action}`;
|
|
7059
|
+
b.innerHTML = icon;
|
|
7060
|
+
b.disabled = !enabled;
|
|
7061
|
+
b.addEventListener("click", (e) => {
|
|
7062
|
+
e.preventDefault();
|
|
7063
|
+
e.stopPropagation();
|
|
7064
|
+
post({ type: "block-action", payload: { stepIndex, blockId, action } });
|
|
7065
|
+
});
|
|
7066
|
+
bar.appendChild(b);
|
|
7067
|
+
};
|
|
7068
|
+
mk("edit", "Editar", ICON.pencil);
|
|
7069
|
+
mk("duplicate", "Duplicar", ICON.copy, false);
|
|
7070
|
+
mk("remove", "Eliminar", ICON.trash);
|
|
7071
|
+
mk("move-left", "Mover a la izquierda", ICON_MOVE.left, false);
|
|
7072
|
+
mk("move-right", "Mover a la derecha", ICON_MOVE.right, false);
|
|
7073
|
+
frame.style.display = "none";
|
|
7074
|
+
bar.style.display = "none";
|
|
7075
|
+
const GAP = 18;
|
|
7076
|
+
const EDGE2 = 4;
|
|
7077
|
+
const gap = (kind, label, action) => {
|
|
7078
|
+
const z = doc.createElement("div");
|
|
7079
|
+
z.className = `veo-gap veo-gap--${kind}`;
|
|
7080
|
+
z.style.position = "fixed";
|
|
7081
|
+
z.dataset.veoGapAction = action;
|
|
7082
|
+
const line = doc.createElement("i");
|
|
7083
|
+
const btn = doc.createElement("button");
|
|
7084
|
+
btn.type = "button";
|
|
7085
|
+
btn.title = label;
|
|
7086
|
+
btn.setAttribute("aria-label", label);
|
|
7087
|
+
btn.textContent = "+";
|
|
7088
|
+
btn.addEventListener("click", (e) => {
|
|
7089
|
+
e.preventDefault();
|
|
7090
|
+
e.stopPropagation();
|
|
7091
|
+
post({ type: "block-layout", payload: { stepIndex, blockId, action } });
|
|
7092
|
+
});
|
|
7093
|
+
z.append(line, btn);
|
|
7094
|
+
return z;
|
|
7095
|
+
};
|
|
7096
|
+
const gaps = {
|
|
7097
|
+
top: gap("h", "Agregar fila", "row-before"),
|
|
7098
|
+
bottom: gap("h", "Agregar fila", "row-after"),
|
|
7099
|
+
left: gap("v", "Agregar columna", "col-before"),
|
|
7100
|
+
right: gap("v", "Agregar columna", "col-after")
|
|
7101
|
+
};
|
|
7102
|
+
shadow.append(frame, bar, gaps.top, gaps.bottom, gaps.left, gaps.right);
|
|
7103
|
+
doc.body.appendChild(box);
|
|
7104
|
+
box.style.display = "";
|
|
7105
|
+
let visible = false;
|
|
7106
|
+
const place = (el2, x, y, w, h) => {
|
|
7107
|
+
el2.style.left = `${x}px`;
|
|
7108
|
+
el2.style.top = `${y}px`;
|
|
7109
|
+
el2.style.width = `${w}px`;
|
|
7110
|
+
el2.style.height = `${h}px`;
|
|
7111
|
+
};
|
|
7112
|
+
const reposition = () => {
|
|
7113
|
+
const live = nodes.filter((n) => n.isConnected);
|
|
7114
|
+
if (live.length === 0) return;
|
|
7115
|
+
let l = Number.POSITIVE_INFINITY;
|
|
7116
|
+
let t = Number.POSITIVE_INFINITY;
|
|
7117
|
+
let r = Number.NEGATIVE_INFINITY;
|
|
7118
|
+
let b = Number.NEGATIVE_INFINITY;
|
|
7119
|
+
for (const n of live) {
|
|
7120
|
+
const rc = n.getBoundingClientRect();
|
|
7121
|
+
if (rc.width === 0 && rc.height === 0) continue;
|
|
7122
|
+
l = Math.min(l, rc.left);
|
|
7123
|
+
t = Math.min(t, rc.top);
|
|
7124
|
+
r = Math.max(r, rc.right);
|
|
7125
|
+
b = Math.max(b, rc.bottom);
|
|
7126
|
+
}
|
|
7127
|
+
if (!Number.isFinite(l)) return;
|
|
7128
|
+
place(gaps.top, l, t - EDGE2 - GAP, r - l, GAP);
|
|
7129
|
+
place(gaps.bottom, l, b + EDGE2, r - l, GAP);
|
|
7130
|
+
place(gaps.left, l - EDGE2 - GAP, t, GAP, b - t);
|
|
7131
|
+
place(gaps.right, r + EDGE2, t, GAP, b - t);
|
|
7132
|
+
if (!visible) return;
|
|
7133
|
+
place(frame, l, t, r - l, b - t);
|
|
7134
|
+
const bh = bar.offsetHeight || TOOLBAR_H;
|
|
7135
|
+
const bw = bar.offsetWidth;
|
|
7136
|
+
bar.style.left = `${Math.max(0, r - bw)}px`;
|
|
7137
|
+
bar.style.top = `${Math.max(0, t - bh - 6)}px`;
|
|
7138
|
+
};
|
|
7139
|
+
const select = (id) => {
|
|
7140
|
+
visible = id === blockId;
|
|
7141
|
+
frame.style.display = visible ? "" : "none";
|
|
7142
|
+
bar.style.display = visible ? "" : "none";
|
|
7143
|
+
reposition();
|
|
7144
|
+
};
|
|
7145
|
+
reposition();
|
|
7146
|
+
const onClick = (e) => {
|
|
7147
|
+
const path = e.composedPath();
|
|
7148
|
+
if (!nodes.some((n) => path.includes(n))) return;
|
|
7149
|
+
if (path.some((n) => n instanceof Element && n.matches("button, a"))) {
|
|
7150
|
+
e.preventDefault();
|
|
7151
|
+
e.stopPropagation();
|
|
7152
|
+
}
|
|
7153
|
+
selectedBlockId = blockId;
|
|
7154
|
+
select(blockId);
|
|
7155
|
+
post({ type: "block-clicked", payload: { stepIndex, blockId } });
|
|
7156
|
+
};
|
|
7157
|
+
doc.addEventListener("click", onClick, true);
|
|
7158
|
+
window.addEventListener("scroll", reposition, true);
|
|
7159
|
+
window.addEventListener("resize", reposition);
|
|
7160
|
+
return {
|
|
7161
|
+
select,
|
|
7162
|
+
detach: () => {
|
|
7163
|
+
doc.removeEventListener("click", onClick, true);
|
|
7164
|
+
window.removeEventListener("scroll", reposition, true);
|
|
7165
|
+
window.removeEventListener("resize", reposition);
|
|
7166
|
+
box.remove();
|
|
7167
|
+
}
|
|
7168
|
+
};
|
|
7169
|
+
};
|
|
5293
7170
|
const applyPreviewGated = (cmd) => {
|
|
5294
|
-
|
|
7171
|
+
const showNow = !cmd.honorTrigger;
|
|
7172
|
+
if (showNow) {
|
|
7173
|
+
disarmPreviewTrigger();
|
|
7174
|
+
cancelPreviewDelay();
|
|
7175
|
+
previewFired = true;
|
|
7176
|
+
previewDismissed = false;
|
|
7177
|
+
}
|
|
7178
|
+
lastPreview = showNow ? { ...cmd, honorTrigger: true } : cmd;
|
|
5295
7179
|
if (!previewMatchesPage(cmd)) {
|
|
5296
7180
|
cancelPreviewDelay();
|
|
5297
7181
|
withholdPreview();
|
|
@@ -5302,13 +7186,13 @@ function initBuilderMode() {
|
|
|
5302
7186
|
return;
|
|
5303
7187
|
}
|
|
5304
7188
|
const devices = cmd.guide.activationRules?.devices;
|
|
5305
|
-
if (
|
|
7189
|
+
if (!showNow && previewMode && devices && !isDeviceAllowed(devices)) {
|
|
5306
7190
|
cancelPreviewDelay();
|
|
5307
7191
|
withholdPreview();
|
|
5308
7192
|
postPreviewStatus({ state: "device-mismatch", devices });
|
|
5309
7193
|
return;
|
|
5310
7194
|
}
|
|
5311
|
-
applyPreview(
|
|
7195
|
+
applyPreview(lastPreview);
|
|
5312
7196
|
};
|
|
5313
7197
|
const disarmPreviewTrigger = () => {
|
|
5314
7198
|
previewTrigger?.();
|
|
@@ -5333,6 +7217,7 @@ function initBuilderMode() {
|
|
|
5333
7217
|
}
|
|
5334
7218
|
disarmPreviewTrigger();
|
|
5335
7219
|
previewFired = true;
|
|
7220
|
+
previewDismissed = false;
|
|
5336
7221
|
if (lastPreview) applyPreviewGated(lastPreview);
|
|
5337
7222
|
};
|
|
5338
7223
|
document.addEventListener("click", onFire, true);
|
|
@@ -5353,22 +7238,24 @@ function initBuilderMode() {
|
|
|
5353
7238
|
postPreviewStatus({ state: "waiting-delay", delayMs });
|
|
5354
7239
|
};
|
|
5355
7240
|
const applyPreview = (cmd) => {
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
7241
|
+
if (cmd.editable && !previewMode && tryPatchPreview(cmd)) {
|
|
7242
|
+
previewStepIndex = cmd.guide.startStepIndex ?? 0;
|
|
7243
|
+
return;
|
|
7244
|
+
}
|
|
7245
|
+
detachPreviewTools();
|
|
5360
7246
|
previewStepIndex = cmd.guide.startStepIndex ?? 0;
|
|
5361
7247
|
const rules = cmd.guide.activationRules;
|
|
5362
7248
|
const trig = rules?.trigger;
|
|
5363
7249
|
const sel = rules?.selector?.trim();
|
|
5364
|
-
const gate = cmd.honorTrigger && !previewFired &&
|
|
5365
|
-
|
|
7250
|
+
const gate = cmd.honorTrigger && !previewFired && previewMode;
|
|
7251
|
+
const waitsForTarget = (trig === "on_click" || trig === "on_hover") && (!isAutomaticActivation(rules) || previewDismissed);
|
|
7252
|
+
if (gate && waitsForTarget && sel) {
|
|
5366
7253
|
cancelPreviewDelay();
|
|
5367
7254
|
closeGuidePreview();
|
|
5368
7255
|
armPreviewTrigger(sel, trig);
|
|
5369
7256
|
return;
|
|
5370
7257
|
}
|
|
5371
|
-
const delayMs =
|
|
7258
|
+
const delayMs = activationDelayMs(rules);
|
|
5372
7259
|
if (gate && delayMs > 0) {
|
|
5373
7260
|
disarmPreviewTrigger();
|
|
5374
7261
|
closeGuidePreview();
|
|
@@ -5377,15 +7264,21 @@ function initBuilderMode() {
|
|
|
5377
7264
|
}
|
|
5378
7265
|
disarmPreviewTrigger();
|
|
5379
7266
|
cancelPreviewDelay();
|
|
7267
|
+
const fetchProgress = previewProgressFetcher();
|
|
5380
7268
|
const handle = previewGuide({
|
|
5381
7269
|
...cmd.guide,
|
|
7270
|
+
...fetchProgress ? { fetchProgress } : {},
|
|
5382
7271
|
onClosed: () => {
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
dropTarget?.detach();
|
|
5386
|
-
dropTarget = null;
|
|
7272
|
+
detachPreviewTools();
|
|
7273
|
+
shownPreview = null;
|
|
5387
7274
|
previewFired = false;
|
|
5388
|
-
|
|
7275
|
+
previewDismissed = true;
|
|
7276
|
+
if (previewMode) {
|
|
7277
|
+
const r = lastPreview?.guide.activationRules;
|
|
7278
|
+
const t = r?.trigger;
|
|
7279
|
+
const selector = r?.selector?.trim();
|
|
7280
|
+
if ((t === "on_click" || t === "on_hover") && selector) armPreviewTrigger(selector, t);
|
|
7281
|
+
}
|
|
5389
7282
|
}
|
|
5390
7283
|
});
|
|
5391
7284
|
if (cmd.honorTrigger) postPreviewStatus({ state: "shown" });
|
|
@@ -5393,7 +7286,23 @@ function initBuilderMode() {
|
|
|
5393
7286
|
void handle.ready.then((result) => {
|
|
5394
7287
|
if (!result.rendered) return;
|
|
5395
7288
|
const host = handle.host();
|
|
5396
|
-
if (!host)
|
|
7289
|
+
if (!host) {
|
|
7290
|
+
const idx = cmd.guide.startStepIndex ?? 0;
|
|
7291
|
+
const only = cmd.guide.guideSteps[idx]?.blocks?.[0];
|
|
7292
|
+
if (only?.type === "code" && only.editorId) {
|
|
7293
|
+
rawBlock = attachRawBlock(idx, only.editorId);
|
|
7294
|
+
rawBlock?.select(selectedBlockId);
|
|
7295
|
+
}
|
|
7296
|
+
return;
|
|
7297
|
+
}
|
|
7298
|
+
host.classList.add("veo-no-anim");
|
|
7299
|
+
shownPreview = cmd;
|
|
7300
|
+
cardClick = attachCardClick(host, cmd.guide.startStepIndex ?? 0);
|
|
7301
|
+
const stepBlocks = cmd.guide.guideSteps[cmd.guide.startStepIndex ?? 0]?.blocks ?? [];
|
|
7302
|
+
previewBlocks = stepBlocks;
|
|
7303
|
+
applyBlockHighlight(host, cmd.guide.startStepIndex ?? 0);
|
|
7304
|
+
renderGapZones(host, cmd.guide.startStepIndex ?? 0, stepBlocks);
|
|
7305
|
+
rowHover = attachRowHover(host, cmd.guide.startStepIndex ?? 0);
|
|
5397
7306
|
manipulator = attachDesignManipulator({
|
|
5398
7307
|
host,
|
|
5399
7308
|
guideType: cmd.guide.guideType,
|
|
@@ -5461,13 +7370,21 @@ function initBuilderMode() {
|
|
|
5461
7370
|
break;
|
|
5462
7371
|
case "editor-state":
|
|
5463
7372
|
chrome?.setState(cmd.state);
|
|
7373
|
+
if (panelSection !== cmd.state.section) {
|
|
7374
|
+
panelSection = cmd.state.section;
|
|
7375
|
+
const h = document.querySelector("[data-veo-guide]");
|
|
7376
|
+
if (h && hoverCard && !previewMode) renderCardChrome(h, previewStepIndex);
|
|
7377
|
+
}
|
|
5464
7378
|
panel?.setTitle(
|
|
5465
|
-
cmd.state.section === "activation" ? "Ajustes de activaci\xF3n" : cmd.state.section === "badge" ? "Ajustes de badge" : cmd.state.section === "target" ? "Ajustes del elemento objetivo" : cmd.state.steps[cmd.state.activeStep]?.label ?? "Paso"
|
|
7379
|
+
cmd.state.section === "activation" ? "Ajustes de activaci\xF3n" : cmd.state.section === "badge" ? "Ajustes de badge" : cmd.state.section === "target" ? "Ajustes del elemento objetivo" : cmd.state.section === "row" ? "Editar fila" : cmd.state.blockLabel ? `Editor \xB7 ${cmd.state.blockLabel}` : cmd.state.steps[cmd.state.activeStep]?.label ?? "Paso"
|
|
5466
7380
|
);
|
|
5467
7381
|
break;
|
|
5468
7382
|
case "panel-visibility":
|
|
5469
7383
|
panel?.setVisible(cmd.visible);
|
|
5470
7384
|
break;
|
|
7385
|
+
case "panel-size":
|
|
7386
|
+
panel?.setSize(cmd.size);
|
|
7387
|
+
break;
|
|
5471
7388
|
case "start-picker": {
|
|
5472
7389
|
picker?.stop();
|
|
5473
7390
|
const panelWasShown = panel?.isVisible() ?? false;
|
|
@@ -5475,10 +7392,7 @@ function initBuilderMode() {
|
|
|
5475
7392
|
chrome?.setHidden(true);
|
|
5476
7393
|
const previewToRestore = lastPreview;
|
|
5477
7394
|
if (previewToRestore) {
|
|
5478
|
-
|
|
5479
|
-
manipulator = null;
|
|
5480
|
-
dropTarget?.detach();
|
|
5481
|
-
dropTarget = null;
|
|
7395
|
+
detachPreviewTools();
|
|
5482
7396
|
closeGuidePreview();
|
|
5483
7397
|
}
|
|
5484
7398
|
const restorePanel = () => {
|
|
@@ -5517,17 +7431,28 @@ function initBuilderMode() {
|
|
|
5517
7431
|
case "block-drag":
|
|
5518
7432
|
handleBlockDrag(cmd);
|
|
5519
7433
|
break;
|
|
7434
|
+
case "select-block": {
|
|
7435
|
+
selectedBlockId = cmd.blockId;
|
|
7436
|
+
const h = document.querySelector("[data-veo-guide]");
|
|
7437
|
+
if (h && !previewMode) applyBlockHighlight(h, previewStepIndex);
|
|
7438
|
+
if (!previewMode) rawBlock?.select(cmd.blockId);
|
|
7439
|
+
break;
|
|
7440
|
+
}
|
|
7441
|
+
case "select-row": {
|
|
7442
|
+
selectedRowId = cmd.blockId;
|
|
7443
|
+
const h = document.querySelector("[data-veo-guide]");
|
|
7444
|
+
if (h && !previewMode) renderRowChrome(h, previewStepIndex);
|
|
7445
|
+
break;
|
|
7446
|
+
}
|
|
5520
7447
|
case "close-preview":
|
|
5521
|
-
|
|
5522
|
-
manipulator = null;
|
|
5523
|
-
dropTarget?.detach();
|
|
5524
|
-
dropTarget = null;
|
|
7448
|
+
detachPreviewTools();
|
|
5525
7449
|
hideDragGhost();
|
|
5526
7450
|
pendingPreview = null;
|
|
5527
7451
|
lastPreview = null;
|
|
5528
7452
|
disarmPreviewTrigger();
|
|
5529
7453
|
cancelPreviewDelay();
|
|
5530
7454
|
previewFired = false;
|
|
7455
|
+
previewDismissed = false;
|
|
5531
7456
|
closeGuidePreview();
|
|
5532
7457
|
break;
|
|
5533
7458
|
case "teardown":
|
|
@@ -5573,10 +7498,7 @@ function initBuilderMode() {
|
|
|
5573
7498
|
removeNavWatcher();
|
|
5574
7499
|
picker?.stop();
|
|
5575
7500
|
picker = null;
|
|
5576
|
-
|
|
5577
|
-
manipulator = null;
|
|
5578
|
-
dropTarget?.detach();
|
|
5579
|
-
dropTarget = null;
|
|
7501
|
+
detachPreviewTools();
|
|
5580
7502
|
hideDragGhost();
|
|
5581
7503
|
pendingPreview = null;
|
|
5582
7504
|
disarmPreviewTrigger();
|
|
@@ -5604,6 +7526,8 @@ function initBuilderMode() {
|
|
|
5604
7526
|
case "preview":
|
|
5605
7527
|
if (previewMode) return;
|
|
5606
7528
|
previewMode = true;
|
|
7529
|
+
previewFired = false;
|
|
7530
|
+
previewDismissed = false;
|
|
5607
7531
|
panelWasVisible = panel?.isVisible() ?? true;
|
|
5608
7532
|
panel?.setVisible(false);
|
|
5609
7533
|
chrome?.setPreview(true);
|
|
@@ -5612,6 +7536,8 @@ function initBuilderMode() {
|
|
|
5612
7536
|
case "exit-preview":
|
|
5613
7537
|
if (!previewMode) return;
|
|
5614
7538
|
previewMode = false;
|
|
7539
|
+
disarmPreviewTrigger();
|
|
7540
|
+
cancelPreviewDelay();
|
|
5615
7541
|
chrome?.setPreview(false);
|
|
5616
7542
|
panel?.setVisible(panelWasVisible);
|
|
5617
7543
|
if (lastPreview) applyPreviewGated(lastPreview);
|
|
@@ -5693,12 +7619,36 @@ function resolveDashboardOrigin(originHint) {
|
|
|
5693
7619
|
}
|
|
5694
7620
|
return null;
|
|
5695
7621
|
}
|
|
7622
|
+
function previewProgressFetcher() {
|
|
7623
|
+
const cfg = window.__veoBuilder;
|
|
7624
|
+
if (!cfg?.apiKey || !cfg?.apiUrl) return void 0;
|
|
7625
|
+
const apiUrl = cfg.apiUrl.replace(/\/+$/, "");
|
|
7626
|
+
const apiKey = cfg.apiKey;
|
|
7627
|
+
return async (guideIds) => {
|
|
7628
|
+
const params = new URLSearchParams({
|
|
7629
|
+
endUserId: "veo_preview",
|
|
7630
|
+
guideIds: guideIds.join(","),
|
|
7631
|
+
includeUnpublished: "1"
|
|
7632
|
+
});
|
|
7633
|
+
const res = await fetch(`${apiUrl}/v1/guides/progress?${params}`, {
|
|
7634
|
+
headers: { "X-Api-Key": apiKey }
|
|
7635
|
+
});
|
|
7636
|
+
if (!res.ok) return [];
|
|
7637
|
+
const data = await res.json();
|
|
7638
|
+
return data.items ?? [];
|
|
7639
|
+
};
|
|
7640
|
+
}
|
|
5696
7641
|
var init_builder_mode = __esm({
|
|
5697
7642
|
"src/plugins/builder/builder-mode.ts"() {
|
|
5698
7643
|
init_builder_token();
|
|
5699
7644
|
init_safe_env();
|
|
5700
7645
|
init_activation_devices();
|
|
7646
|
+
init_activation_rules();
|
|
7647
|
+
init_block_builder();
|
|
7648
|
+
init_code_block();
|
|
5701
7649
|
init_guide_preview();
|
|
7650
|
+
init_layout();
|
|
7651
|
+
init_styles();
|
|
5702
7652
|
init_url_match();
|
|
5703
7653
|
init_block_drop_target();
|
|
5704
7654
|
init_bridge_protocol();
|
|
@@ -6699,6 +8649,7 @@ init_safe_env();
|
|
|
6699
8649
|
|
|
6700
8650
|
// src/plugins/guides/guides-controller.ts
|
|
6701
8651
|
init_activation_devices();
|
|
8652
|
+
init_activation_rules();
|
|
6702
8653
|
|
|
6703
8654
|
// src/plugins/guides/campaign-weekdays.ts
|
|
6704
8655
|
function isWeekdayAllowed(campaignWeekdays, now = /* @__PURE__ */ new Date()) {
|
|
@@ -7096,6 +9047,7 @@ init_inline_renderer();
|
|
|
7096
9047
|
init_modal_renderer();
|
|
7097
9048
|
init_tooltip_renderer();
|
|
7098
9049
|
init_walkthrough_renderer();
|
|
9050
|
+
init_snooze();
|
|
7099
9051
|
init_url_match();
|
|
7100
9052
|
|
|
7101
9053
|
// src/plugins/guides/walkthrough-state.ts
|
|
@@ -7288,6 +9240,12 @@ var GuidesController = class {
|
|
|
7288
9240
|
* en cada `checkGuides` con {@link pruneComposite}.
|
|
7289
9241
|
*/
|
|
7290
9242
|
this.activeComposite = /* @__PURE__ */ new Map();
|
|
9243
|
+
/**
|
|
9244
|
+
* Progreso de esta sesión, por guía: pasos vistos y completado, según las
|
|
9245
|
+
* interacciones que ESTE SDK emitió (aunque el lote aún no haya viajado al
|
|
9246
|
+
* backend). Se mezcla con la respuesta del backend en `fetchProgress`.
|
|
9247
|
+
*/
|
|
9248
|
+
this.sessionProgress = /* @__PURE__ */ new Map();
|
|
7291
9249
|
this.debug = config.debug === true;
|
|
7292
9250
|
this.apiUrl = config.apiUrl;
|
|
7293
9251
|
this.apiKey = config.apiKey;
|
|
@@ -7352,12 +9310,15 @@ var GuidesController = class {
|
|
|
7352
9310
|
if (this.debug) console.log(`[veo] guides: ${guides.length} eligible at ${pageUrl}`);
|
|
7353
9311
|
this.disarmTriggers();
|
|
7354
9312
|
for (const guide of guides) {
|
|
7355
|
-
const
|
|
7356
|
-
|
|
9313
|
+
const rules = guide.activationRules;
|
|
9314
|
+
const trigger = rules.trigger;
|
|
9315
|
+
if ((trigger === "on_click" || trigger === "on_hover") && rules.selector) {
|
|
7357
9316
|
this.armInteractionTrigger(guide, endUserId, sessionId, pageUrl);
|
|
7358
|
-
continue;
|
|
9317
|
+
if (!isAutomaticActivation(rules)) continue;
|
|
7359
9318
|
}
|
|
7360
|
-
await this.showGuide(guide, endUserId, sessionId, pageUrl
|
|
9319
|
+
await this.showGuide(guide, endUserId, sessionId, pageUrl, {
|
|
9320
|
+
badgeOpen: trigger !== "manual"
|
|
9321
|
+
});
|
|
7361
9322
|
}
|
|
7362
9323
|
}
|
|
7363
9324
|
/**
|
|
@@ -7365,12 +9326,17 @@ var GuidesController = class {
|
|
|
7365
9326
|
* tipo). Cuerpo original del loop de `checkGuides`, extraído para que los
|
|
7366
9327
|
* disparadores por interacción pasen por LOS MISMOS chequeos al disparar.
|
|
7367
9328
|
*/
|
|
7368
|
-
async showGuide(guide, endUserId, sessionId, pageUrl) {
|
|
9329
|
+
async showGuide(guide, endUserId, sessionId, pageUrl, opts = {}) {
|
|
9330
|
+
const badgeOpen = opts.badgeOpen !== false;
|
|
7369
9331
|
if (this.frequencyCache.shouldFilter(guide.guideId, guide.displayFrequency)) {
|
|
7370
9332
|
if (this.debug) console.log(`[veo] guides: filtered locally guideId=${guide.guideId}`);
|
|
7371
9333
|
return;
|
|
7372
9334
|
}
|
|
7373
|
-
if (
|
|
9335
|
+
if (!opts.byUser && isSnoozed(guide.guideId)) {
|
|
9336
|
+
if (this.debug) console.log(`[veo] guides: snoozed guideId=${guide.guideId}`);
|
|
9337
|
+
return;
|
|
9338
|
+
}
|
|
9339
|
+
if (!opts.byUser && this.isHeldByRepeat(guide)) {
|
|
7374
9340
|
if (this.debug) console.log(`[veo] guides: held by repeat guideId=${guide.guideId}`);
|
|
7375
9341
|
return;
|
|
7376
9342
|
}
|
|
@@ -7388,7 +9354,7 @@ var GuidesController = class {
|
|
|
7388
9354
|
return;
|
|
7389
9355
|
}
|
|
7390
9356
|
if (guide.guideType === "composite") {
|
|
7391
|
-
this.startComposite(guide, sessionId, pageUrl);
|
|
9357
|
+
this.startComposite(guide, sessionId, pageUrl, badgeOpen);
|
|
7392
9358
|
return;
|
|
7393
9359
|
}
|
|
7394
9360
|
this.runSingleStepRender(guide, sessionId, pageUrl);
|
|
@@ -7417,7 +9383,7 @@ var GuidesController = class {
|
|
|
7417
9383
|
return;
|
|
7418
9384
|
}
|
|
7419
9385
|
disarm();
|
|
7420
|
-
void this.showGuide(guide, endUserId, sessionId, pageUrl);
|
|
9386
|
+
void this.showGuide(guide, endUserId, sessionId, pageUrl, { byUser: true });
|
|
7421
9387
|
};
|
|
7422
9388
|
const disarm = () => {
|
|
7423
9389
|
document.removeEventListener(eventName, onFire, true);
|
|
@@ -7497,7 +9463,7 @@ ${pageUrl}`;
|
|
|
7497
9463
|
const renderer = this.createSingleStepRenderer(guide.guideType);
|
|
7498
9464
|
if (!renderer) return;
|
|
7499
9465
|
this.activeByGuideId.set(guide.guideId, renderer);
|
|
7500
|
-
const delayMs = guide.activationRules
|
|
9466
|
+
const delayMs = activationDelayMs(guide.activationRules);
|
|
7501
9467
|
window.setTimeout(
|
|
7502
9468
|
() => this.mountSingleStepRenderer(guide, renderer, sessionId, pageUrl),
|
|
7503
9469
|
delayMs
|
|
@@ -7508,23 +9474,24 @@ ${pageUrl}`;
|
|
|
7508
9474
|
* `pageLocation` matchea la URL actual (targeting por-paso) y lo pinta con el
|
|
7509
9475
|
* `CompositeRenderer`. Si ningún paso aplica a esta página, no muestra nada.
|
|
7510
9476
|
*/
|
|
7511
|
-
startComposite(guide, sessionId, pageUrl) {
|
|
9477
|
+
startComposite(guide, sessionId, pageUrl, badgeOpen = true) {
|
|
7512
9478
|
if (this.pendingComposite?.guide.guideId === guide.guideId) return;
|
|
7513
9479
|
const steps = guide.guideSteps;
|
|
7514
9480
|
const remembered = guide.activationRules.repeat?.onLoad === "resume" ? this.recallStep(guide.guideId) : null;
|
|
7515
9481
|
const start = remembered !== null && stepMatchesPage(steps[remembered]?.pageLocation, pageUrl) ? remembered : steps.findIndex((s) => stepMatchesPage(s.pageLocation, pageUrl));
|
|
7516
9482
|
if (start === -1) return;
|
|
9483
|
+
if (guide.activationRules.trigger === "manual" && !steps[start]?.badge?.enabled) return;
|
|
7517
9484
|
if (this.compositeDelays.has(guide.guideId)) return;
|
|
7518
9485
|
this.resetDispatched(guide.guideId);
|
|
7519
|
-
const delayMs = guide.activationRules
|
|
9486
|
+
const delayMs = activationDelayMs(guide.activationRules);
|
|
7520
9487
|
if (delayMs <= 0) {
|
|
7521
|
-
this.renderCompositeStep(guide, start, sessionId, pageUrl);
|
|
9488
|
+
this.renderCompositeStep(guide, start, sessionId, pageUrl, badgeOpen);
|
|
7522
9489
|
return;
|
|
7523
9490
|
}
|
|
7524
9491
|
const timer = setTimeout(() => {
|
|
7525
9492
|
this.compositeDelays.delete(guide.guideId);
|
|
7526
9493
|
if (this.activeByGuideId.has(guide.guideId)) return;
|
|
7527
|
-
this.renderCompositeStep(guide, start, sessionId, pageUrl);
|
|
9494
|
+
this.renderCompositeStep(guide, start, sessionId, pageUrl, badgeOpen);
|
|
7528
9495
|
}, delayMs);
|
|
7529
9496
|
this.compositeDelays.set(guide.guideId, { guide, stepIndex: start, timer });
|
|
7530
9497
|
}
|
|
@@ -7626,7 +9593,7 @@ ${pageUrl}`;
|
|
|
7626
9593
|
* que los botones next/prev avancen (misma página); en single-step los
|
|
7627
9594
|
* botones cierran. Cada avance destruye el paso anterior y monta el siguiente.
|
|
7628
9595
|
*/
|
|
7629
|
-
renderCompositeStep(guide, idx, sessionId, pageUrl) {
|
|
9596
|
+
renderCompositeStep(guide, idx, sessionId, pageUrl, badgeOpen = true) {
|
|
7630
9597
|
const steps = guide.guideSteps;
|
|
7631
9598
|
this.rememberStep(guide.guideId, idx);
|
|
7632
9599
|
const renderer = new CompositeRenderer();
|
|
@@ -7644,9 +9611,20 @@ ${pageUrl}`;
|
|
|
7644
9611
|
const goToStep = (target) => {
|
|
7645
9612
|
cleanup();
|
|
7646
9613
|
if (target < 0 || target >= steps.length) return;
|
|
9614
|
+
const endUserId = this.client.getEndUserId();
|
|
9615
|
+
if (endUserId) {
|
|
9616
|
+
this.enqueueInteraction(
|
|
9617
|
+
guide.guideId,
|
|
9618
|
+
endUserId,
|
|
9619
|
+
sessionId,
|
|
9620
|
+
pageUrl,
|
|
9621
|
+
target > idx ? "step_advanced" : "step_back",
|
|
9622
|
+
target
|
|
9623
|
+
);
|
|
9624
|
+
}
|
|
7647
9625
|
const step = steps[target];
|
|
7648
9626
|
if (step && stepMatchesPage(step.pageLocation, pageUrl)) {
|
|
7649
|
-
this.renderCompositeStep(guide, target, sessionId, pageUrl);
|
|
9627
|
+
this.renderCompositeStep(guide, target, sessionId, pageUrl, badgeOpen);
|
|
7650
9628
|
} else {
|
|
7651
9629
|
this.pendingComposite = { guide, stepIndex: target };
|
|
7652
9630
|
}
|
|
@@ -7668,10 +9646,19 @@ ${pageUrl}`;
|
|
|
7668
9646
|
onInteraction: (e) => this.handleInteraction(guide, sessionId, pageUrl, e),
|
|
7669
9647
|
onClose: cleanup,
|
|
7670
9648
|
onTrack: (name, props) => this.client.track(name, props),
|
|
7671
|
-
onFormSubmit: (answers) => this.submitFormResponse(guide.guideId, answers),
|
|
9649
|
+
onFormSubmit: (answers, meta) => this.submitFormResponse(guide.guideId, answers, meta),
|
|
7672
9650
|
onOpenGuide: (targetGuideId) => {
|
|
7673
9651
|
void this.openGuideById(targetGuideId, sessionId, pageUrl);
|
|
7674
9652
|
},
|
|
9653
|
+
// Lista de tareas: progreso del usuario y abrir una guía en su paso.
|
|
9654
|
+
fetchProgress: (ids) => this.fetchProgress(ids),
|
|
9655
|
+
onOpenGuideAt: (targetGuideId, stepIndex) => {
|
|
9656
|
+
void this.openGuideById(targetGuideId, sessionId, pageUrl, stepIndex);
|
|
9657
|
+
},
|
|
9658
|
+
// Badge: cuando el MOTOR muestra la guía (automático, elemento objetivo
|
|
9659
|
+
// o encadenada), el contenido se abre solo; el marcador queda para
|
|
9660
|
+
// volver a verla. Solo `manual` deja el tooltip cerrado.
|
|
9661
|
+
badgeOpen,
|
|
7675
9662
|
...nav ? { nav } : {}
|
|
7676
9663
|
});
|
|
7677
9664
|
} catch (err) {
|
|
@@ -7695,7 +9682,7 @@ ${pageUrl}`;
|
|
|
7695
9682
|
const onTrack = (eventName, properties) => {
|
|
7696
9683
|
this.client.track(eventName, properties);
|
|
7697
9684
|
};
|
|
7698
|
-
const onFormSubmit = (answers) => this.submitFormResponse(guide.guideId, answers);
|
|
9685
|
+
const onFormSubmit = (answers, meta) => this.submitFormResponse(guide.guideId, answers, meta);
|
|
7699
9686
|
const onOpenGuide = (targetGuideId) => {
|
|
7700
9687
|
void this.openGuideById(targetGuideId, sessionId, pageUrl);
|
|
7701
9688
|
};
|
|
@@ -7715,7 +9702,7 @@ ${pageUrl}`;
|
|
|
7715
9702
|
* un botón); respeta el dedupe de guías activas y el flujo normal de
|
|
7716
9703
|
* tracking. 404/red → no-op (la guía destino pudo borrarse).
|
|
7717
9704
|
*/
|
|
7718
|
-
async openGuideById(guideId, sessionId, pageUrl) {
|
|
9705
|
+
async openGuideById(guideId, sessionId, pageUrl, startStep) {
|
|
7719
9706
|
if (this.activeByGuideId.has(guideId)) return;
|
|
7720
9707
|
const guide = await this.resolver.fetchById(guideId);
|
|
7721
9708
|
if (!guide) {
|
|
@@ -7723,6 +9710,15 @@ ${pageUrl}`;
|
|
|
7723
9710
|
return;
|
|
7724
9711
|
}
|
|
7725
9712
|
const endUserId = this.client.getEndUserId() ?? this.client.getAnonymousId();
|
|
9713
|
+
if (guide.guideType === "composite" && typeof startStep === "number") {
|
|
9714
|
+
const total = guide.guideSteps.length;
|
|
9715
|
+
const idx = Math.max(0, Math.min(total - 1, Math.floor(startStep)));
|
|
9716
|
+
this.enqueueInteractionOnce(guide.guideId, endUserId, sessionId, pageUrl, "shown", 0);
|
|
9717
|
+
if (idx > 0)
|
|
9718
|
+
this.enqueueInteraction(guide.guideId, endUserId, sessionId, pageUrl, "step_advanced", idx);
|
|
9719
|
+
this.renderCompositeStep(guide, idx, sessionId, pageUrl);
|
|
9720
|
+
return;
|
|
9721
|
+
}
|
|
7726
9722
|
if (guide.guideType === "walkthrough" && guide.guideSteps.length > 1) {
|
|
7727
9723
|
if (!this.walkthroughState.hasActive()) {
|
|
7728
9724
|
await this.startWalkthrough(guide, endUserId, sessionId, pageUrl);
|
|
@@ -7933,6 +9929,7 @@ ${pageUrl}`;
|
|
|
7933
9929
|
console.warn("[veo] guides: interaction without endUserId \u2014 skipping enqueue");
|
|
7934
9930
|
return;
|
|
7935
9931
|
}
|
|
9932
|
+
this.noteProgress(guide.guideId, event.action, event.stepIndex);
|
|
7936
9933
|
this.trackerQueue.enqueue({
|
|
7937
9934
|
guideId: guide.guideId,
|
|
7938
9935
|
payload: {
|
|
@@ -7952,6 +9949,7 @@ ${pageUrl}`;
|
|
|
7952
9949
|
* una navegación distinta) y deduplicarlas perdería datos.
|
|
7953
9950
|
*/
|
|
7954
9951
|
enqueueInteraction(guideId, endUserId, sessionId, pageUrl, action, stepIndex) {
|
|
9952
|
+
this.noteProgress(guideId, action, stepIndex);
|
|
7955
9953
|
this.trackerQueue.enqueue({
|
|
7956
9954
|
guideId,
|
|
7957
9955
|
payload: {
|
|
@@ -7964,6 +9962,45 @@ ${pageUrl}`;
|
|
|
7964
9962
|
}
|
|
7965
9963
|
});
|
|
7966
9964
|
}
|
|
9965
|
+
noteProgress(guideId, action, stepIndex) {
|
|
9966
|
+
if (action !== "shown" && action !== "step_advanced" && action !== "step_back" && action !== "completed") {
|
|
9967
|
+
return;
|
|
9968
|
+
}
|
|
9969
|
+
const cur = this.sessionProgress.get(guideId) ?? { maxStep: -1, completed: false };
|
|
9970
|
+
this.sessionProgress.set(guideId, {
|
|
9971
|
+
maxStep: Math.max(cur.maxStep, stepIndex),
|
|
9972
|
+
completed: cur.completed || action === "completed"
|
|
9973
|
+
});
|
|
9974
|
+
}
|
|
9975
|
+
/**
|
|
9976
|
+
* Progreso del usuario en varias guías (bloque «Lista de tareas»): lo pide
|
|
9977
|
+
* al backend y lo eleva con lo visto en esta sesión (nunca lo baja).
|
|
9978
|
+
*/
|
|
9979
|
+
async fetchProgress(guideIds) {
|
|
9980
|
+
const endUserId = this.client.getEndUserId() ?? this.client.getAnonymousId();
|
|
9981
|
+
let rows = [];
|
|
9982
|
+
try {
|
|
9983
|
+
const params = new URLSearchParams({ endUserId, guideIds: guideIds.join(",") });
|
|
9984
|
+
const res = await fetch(`${this.apiUrl}/v1/guides/progress?${params}`, {
|
|
9985
|
+
headers: { "X-Api-Key": this.apiKey }
|
|
9986
|
+
});
|
|
9987
|
+
if (res.ok) rows = (await res.json()).items ?? [];
|
|
9988
|
+
else if (this.debug) console.warn("[veo] guides/progress respondi\xF3", res.status);
|
|
9989
|
+
} catch (err) {
|
|
9990
|
+
if (this.debug) console.warn("[veo] guides/progress fall\xF3:", err);
|
|
9991
|
+
}
|
|
9992
|
+
return rows.map((r) => {
|
|
9993
|
+
const local2 = this.sessionProgress.get(r.guideId);
|
|
9994
|
+
if (!local2) return r;
|
|
9995
|
+
const seen = Math.min(r.totalSteps, local2.maxStep + 1);
|
|
9996
|
+
const completed = r.completed || local2.completed || seen >= r.totalSteps;
|
|
9997
|
+
return {
|
|
9998
|
+
...r,
|
|
9999
|
+
currentStep: completed ? r.totalSteps : Math.max(r.currentStep, seen),
|
|
10000
|
+
completed
|
|
10001
|
+
};
|
|
10002
|
+
});
|
|
10003
|
+
}
|
|
7967
10004
|
/**
|
|
7968
10005
|
* Encola con dedup por sesión del SDK. Para `shown` del primer step:
|
|
7969
10006
|
* no queremos re-emitirlo si el usuario navega entre dos páginas que
|
|
@@ -8008,13 +10045,19 @@ ${pageUrl}`;
|
|
|
8008
10045
|
* propiedades del usuario. Usa el endUserId actual (o el anónimo si aún no
|
|
8009
10046
|
* hubo identify — el backend pre-crea la fila igual que identify).
|
|
8010
10047
|
*/
|
|
8011
|
-
async submitFormResponse(guideId, answers) {
|
|
10048
|
+
async submitFormResponse(guideId, answers, meta) {
|
|
8012
10049
|
const endUserId = this.client.getEndUserId() ?? this.client.getAnonymousId();
|
|
8013
10050
|
try {
|
|
8014
10051
|
const res = await fetch(`${this.apiUrl}/v1/guides/${guideId}/form-response`, {
|
|
8015
10052
|
method: "POST",
|
|
8016
10053
|
headers: { "Content-Type": "application/json", "X-Api-Key": this.apiKey },
|
|
8017
|
-
body: JSON.stringify({
|
|
10054
|
+
body: JSON.stringify({
|
|
10055
|
+
endUserId,
|
|
10056
|
+
answers,
|
|
10057
|
+
sessionId: this.client.getSessionId(),
|
|
10058
|
+
...meta ? { stepPosition: meta.stepIndex } : {},
|
|
10059
|
+
...meta?.pollId ? { pollId: meta.pollId } : {}
|
|
10060
|
+
})
|
|
8018
10061
|
});
|
|
8019
10062
|
if (!res.ok && this.debug) {
|
|
8020
10063
|
console.warn("[veo] form-response respondi\xF3", res.status);
|
|
@@ -8044,6 +10087,7 @@ init_guide_preview();
|
|
|
8044
10087
|
// src/plugins/guides/preview-mode.ts
|
|
8045
10088
|
init_preview_loader();
|
|
8046
10089
|
init_safe_env();
|
|
10090
|
+
init_activation_rules();
|
|
8047
10091
|
init_guide_preview();
|
|
8048
10092
|
init_url_match();
|
|
8049
10093
|
var active = null;
|
|
@@ -8066,7 +10110,7 @@ async function runPreviewMode(opts) {
|
|
|
8066
10110
|
} catch (err) {
|
|
8067
10111
|
if (opts.debug) console.warn("[veo] preview-resolve fall\xF3:", err);
|
|
8068
10112
|
}
|
|
8069
|
-
const submitToBackend = async (answers) => {
|
|
10113
|
+
const submitToBackend = async (answers, meta) => {
|
|
8070
10114
|
if (!guide) return false;
|
|
8071
10115
|
const veo = window.veo;
|
|
8072
10116
|
const endUserId = veo?.getEndUserId?.() ?? veo?.getAnonymousId?.() ?? null;
|
|
@@ -8075,7 +10119,12 @@ async function runPreviewMode(opts) {
|
|
|
8075
10119
|
const res = await fetch(`${opts.apiUrl}/v1/guides/${guide.guideId}/form-response`, {
|
|
8076
10120
|
method: "POST",
|
|
8077
10121
|
headers: { "Content-Type": "application/json", "X-Api-Key": opts.apiKey },
|
|
8078
|
-
body: JSON.stringify({
|
|
10122
|
+
body: JSON.stringify({
|
|
10123
|
+
endUserId,
|
|
10124
|
+
answers,
|
|
10125
|
+
...meta ? { stepPosition: meta.stepIndex } : {},
|
|
10126
|
+
...meta?.pollId ? { pollId: meta.pollId } : {}
|
|
10127
|
+
})
|
|
8079
10128
|
});
|
|
8080
10129
|
return res.ok;
|
|
8081
10130
|
} catch {
|
|
@@ -8121,7 +10170,7 @@ async function runPreviewMode(opts) {
|
|
|
8121
10170
|
clearWaits();
|
|
8122
10171
|
const rules = guide.activationRules;
|
|
8123
10172
|
const selector = rules.selector?.trim();
|
|
8124
|
-
if ((rules.trigger === "on_click" || rules.trigger === "on_hover") && selector) {
|
|
10173
|
+
if ((rules.trigger === "on_click" || rules.trigger === "on_hover") && selector && !isAutomaticActivation(rules)) {
|
|
8125
10174
|
const verb = rules.trigger === "on_hover" ? "pasa el mouse por" : "haz clic en";
|
|
8126
10175
|
chip.setLabel(`${baseLabel} \xB7 ${verb} el elemento objetivo`);
|
|
8127
10176
|
const onFire = (e) => {
|
|
@@ -8144,7 +10193,7 @@ async function runPreviewMode(opts) {
|
|
|
8144
10193
|
};
|
|
8145
10194
|
return;
|
|
8146
10195
|
}
|
|
8147
|
-
const delayMs = rules
|
|
10196
|
+
const delayMs = activationDelayMs(rules);
|
|
8148
10197
|
if (delayMs > 0) {
|
|
8149
10198
|
chip.setLabel(`${baseLabel} \xB7 aparece en ${Math.round(delayMs / 1e3)} s`);
|
|
8150
10199
|
delayTimer = setTimeout(() => {
|