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
|
@@ -195,6 +195,19 @@ function clearBuilderToken() {
|
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
// src/plugins/guides/activation-rules.ts
|
|
199
|
+
function isAutomaticActivation(rules) {
|
|
200
|
+
const trigger = rules?.trigger ?? "immediate";
|
|
201
|
+
if (trigger === "immediate" || trigger === "delayed") return true;
|
|
202
|
+
if (trigger === "on_click" || trigger === "on_hover") return rules?.autoShow === true;
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
function activationDelayMs(rules) {
|
|
206
|
+
if (!isAutomaticActivation(rules)) return 0;
|
|
207
|
+
const ms = rules?.delayMs ?? 0;
|
|
208
|
+
return Number.isFinite(ms) && ms > 0 ? ms : 0;
|
|
209
|
+
}
|
|
210
|
+
|
|
198
211
|
// src/plugins/guides/constants.ts
|
|
199
212
|
var GUIDE_Z_INDEX = 2147483640;
|
|
200
213
|
var DEFAULT_ANCHOR_WAIT_MS = 5e3;
|
|
@@ -316,6 +329,125 @@ function rebuildNode(node, doc, depth, budget) {
|
|
|
316
329
|
return rebuilt;
|
|
317
330
|
}
|
|
318
331
|
|
|
332
|
+
// src/plugins/guides/video-block.ts
|
|
333
|
+
function videoEmbedUrl(provider, url) {
|
|
334
|
+
let u;
|
|
335
|
+
try {
|
|
336
|
+
u = new URL(url);
|
|
337
|
+
} catch {
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
if (u.protocol !== "https:" && u.protocol !== "http:") return null;
|
|
341
|
+
const host = u.hostname.replace(/^www\./, "").replace(/^m\./, "");
|
|
342
|
+
const id = (re, from) => {
|
|
343
|
+
const m = from.match(re);
|
|
344
|
+
return m?.[1] ?? null;
|
|
345
|
+
};
|
|
346
|
+
switch (provider) {
|
|
347
|
+
case "youtube": {
|
|
348
|
+
let v = null;
|
|
349
|
+
if (host === "youtube.com" || host === "youtube-nocookie.com") {
|
|
350
|
+
v = u.searchParams.get("v") ?? id(/^\/(?:embed|shorts|live)\/([A-Za-z0-9_-]{6,})/, u.pathname);
|
|
351
|
+
} else if (host === "youtu.be") {
|
|
352
|
+
v = id(/^\/([A-Za-z0-9_-]{6,})/, u.pathname);
|
|
353
|
+
}
|
|
354
|
+
if (!v || !/^[A-Za-z0-9_-]{6,}$/.test(v)) return null;
|
|
355
|
+
return `https://www.youtube-nocookie.com/embed/${v}`;
|
|
356
|
+
}
|
|
357
|
+
case "vimeo": {
|
|
358
|
+
if (host !== "vimeo.com" && host !== "player.vimeo.com") return null;
|
|
359
|
+
const v = id(/(\d{6,})/, u.pathname);
|
|
360
|
+
return v ? `https://player.vimeo.com/video/${v}` : null;
|
|
361
|
+
}
|
|
362
|
+
case "loom": {
|
|
363
|
+
if (host !== "loom.com") return null;
|
|
364
|
+
const v = id(/^\/(?:share|embed)\/([A-Za-z0-9]{16,})/, u.pathname);
|
|
365
|
+
return v ? `https://www.loom.com/embed/${v}` : null;
|
|
366
|
+
}
|
|
367
|
+
case "wistia": {
|
|
368
|
+
if (!under(host, "wistia.com") && !under(host, "wistia.net")) return null;
|
|
369
|
+
const v = id(/\/(?:medias|embed\/iframe)\/([A-Za-z0-9]{6,})/, u.pathname);
|
|
370
|
+
return v ? `https://fast.wistia.net/embed/iframe/${v}` : null;
|
|
371
|
+
}
|
|
372
|
+
case "msstream": {
|
|
373
|
+
if (!under(host, "microsoftstream.com")) return null;
|
|
374
|
+
const v = id(/\/(?:embed\/)?video\/([0-9a-f-]{36})/i, u.pathname);
|
|
375
|
+
return v ? `https://web.microsoftstream.com/embed/video/${v}?autoplay=false&showinfo=true` : null;
|
|
376
|
+
}
|
|
377
|
+
case "vidyard": {
|
|
378
|
+
if (!under(host, "vidyard.com")) return null;
|
|
379
|
+
const v = id(/\/watch\/([A-Za-z0-9_-]{8,})/, u.pathname) ?? (host === "play.vidyard.com" ? id(/^\/([A-Za-z0-9_-]{8,})/, u.pathname) : null);
|
|
380
|
+
return v ? `https://play.vidyard.com/${v}` : null;
|
|
381
|
+
}
|
|
382
|
+
case "sharepoint":
|
|
383
|
+
return u.protocol === "https:" && under(host, "sharepoint.com") ? u.href : null;
|
|
384
|
+
case "brightcove":
|
|
385
|
+
return u.protocol === "https:" && host === "players.brightcove.net" && u.searchParams.get("videoId") ? u.href : null;
|
|
386
|
+
case "kaltura":
|
|
387
|
+
return u.protocol === "https:" && under(host, "kaltura.com") && u.pathname.startsWith("/p/") ? u.href : null;
|
|
388
|
+
default:
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
function under(host, domain) {
|
|
393
|
+
return host === domain || host.endsWith(`.${domain}`);
|
|
394
|
+
}
|
|
395
|
+
function fileVideoUrl(url) {
|
|
396
|
+
try {
|
|
397
|
+
const u = new URL(url);
|
|
398
|
+
if (u.protocol !== "https:" && u.protocol !== "http:") return null;
|
|
399
|
+
return /\.(mp4|webm|mov)$/i.test(u.pathname) ? u.href : null;
|
|
400
|
+
} catch {
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
function buildVideoBlock(block, doc, opts = {}) {
|
|
405
|
+
const isFile = block.provider === "file";
|
|
406
|
+
const src = typeof block.url === "string" ? isFile ? fileVideoUrl(block.url) : videoEmbedUrl(block.provider, block.url) : null;
|
|
407
|
+
if (!src) return null;
|
|
408
|
+
const s = block.style ?? {};
|
|
409
|
+
const wrap = doc.createElement("div");
|
|
410
|
+
wrap.className = "veo-guide-video";
|
|
411
|
+
const ratio = s.aspect === "4:3" ? "4 / 3" : "16 / 9";
|
|
412
|
+
wrap.style.aspectRatio = ratio;
|
|
413
|
+
if (s.widthMode === "fill") {
|
|
414
|
+
wrap.style.width = "100%";
|
|
415
|
+
} else {
|
|
416
|
+
const w = typeof s.width === "number" && Number.isFinite(s.width) ? s.width : 400;
|
|
417
|
+
wrap.style.width = `${Math.max(120, Math.min(1600, w))}px`;
|
|
418
|
+
wrap.style.maxWidth = "100%";
|
|
419
|
+
}
|
|
420
|
+
const align = s.align === "center" ? "center" : s.align === "right" ? "flex-end" : "flex-start";
|
|
421
|
+
wrap.style.alignSelf = align;
|
|
422
|
+
if (isFile) {
|
|
423
|
+
const video = doc.createElement("video");
|
|
424
|
+
video.src = src;
|
|
425
|
+
video.controls = true;
|
|
426
|
+
video.setAttribute("playsinline", "");
|
|
427
|
+
video.preload = "metadata";
|
|
428
|
+
video.setAttribute(
|
|
429
|
+
"aria-label",
|
|
430
|
+
typeof block.alt === "string" && block.alt.trim() ? block.alt.trim() : "Video"
|
|
431
|
+
);
|
|
432
|
+
if (opts.editing) video.style.pointerEvents = "none";
|
|
433
|
+
wrap.appendChild(video);
|
|
434
|
+
return wrap;
|
|
435
|
+
}
|
|
436
|
+
const frame = doc.createElement("iframe");
|
|
437
|
+
frame.src = src;
|
|
438
|
+
frame.title = typeof block.alt === "string" && block.alt.trim() ? block.alt.trim() : "Video";
|
|
439
|
+
frame.setAttribute("loading", "lazy");
|
|
440
|
+
frame.setAttribute("referrerpolicy", "strict-origin-when-cross-origin");
|
|
441
|
+
frame.setAttribute(
|
|
442
|
+
"allow",
|
|
443
|
+
`accelerometer; autoplay; clipboard-write; encrypted-media; picture-in-picture${opts.editing ? "" : "; fullscreen"}`
|
|
444
|
+
);
|
|
445
|
+
if (!opts.editing) frame.setAttribute("allowfullscreen", "");
|
|
446
|
+
if (opts.editing) frame.style.pointerEvents = "none";
|
|
447
|
+
wrap.appendChild(frame);
|
|
448
|
+
return wrap;
|
|
449
|
+
}
|
|
450
|
+
|
|
319
451
|
// src/plugins/guides/block-builder.ts
|
|
320
452
|
function buildStepContent(step, doc, callbacks) {
|
|
321
453
|
const container = doc.createElement("div");
|
|
@@ -440,8 +572,12 @@ function renderBlocks(container, blocks, doc, callbacks) {
|
|
|
440
572
|
i++;
|
|
441
573
|
}
|
|
442
574
|
}
|
|
443
|
-
function buildContentBlock(block, doc) {
|
|
575
|
+
function buildContentBlock(block, doc, opts = {}) {
|
|
444
576
|
switch (block.type) {
|
|
577
|
+
case "video":
|
|
578
|
+
return buildVideoBlock(block, doc, opts);
|
|
579
|
+
case "divider":
|
|
580
|
+
return buildDividerBlock(block, doc);
|
|
445
581
|
case "title": {
|
|
446
582
|
const el = doc.createElement("h2");
|
|
447
583
|
el.className = "veo-guide-title";
|
|
@@ -465,7 +601,7 @@ function buildContentBlock(block, doc) {
|
|
|
465
601
|
const el = doc.createElement("img");
|
|
466
602
|
el.className = "veo-guide-image";
|
|
467
603
|
el.src = block.url;
|
|
468
|
-
el.alt = "";
|
|
604
|
+
el.alt = typeof block.alt === "string" ? block.alt : "";
|
|
469
605
|
applyBlockStyle(el, block.style, "image");
|
|
470
606
|
return el;
|
|
471
607
|
}
|
|
@@ -473,6 +609,32 @@ function buildContentBlock(block, doc) {
|
|
|
473
609
|
return null;
|
|
474
610
|
}
|
|
475
611
|
}
|
|
612
|
+
var DIVIDER_LINE_STYLES = /* @__PURE__ */ new Set(["solid", "dotted", "dashed"]);
|
|
613
|
+
function buildDividerBlock(block, doc) {
|
|
614
|
+
const s = block.style ?? {};
|
|
615
|
+
const wrap = doc.createElement("div");
|
|
616
|
+
wrap.className = "veo-guide-divider";
|
|
617
|
+
wrap.setAttribute("role", "separator");
|
|
618
|
+
const hr = doc.createElement("hr");
|
|
619
|
+
const custom = s.preset === "custom";
|
|
620
|
+
const color = custom && typeof s.color === "string" && s.color ? s.color : s.colorMode === "dark" ? "rgba(255, 255, 255, 0.35)" : "#e5e7eb";
|
|
621
|
+
const thickness = custom && typeof s.thickness === "number" && Number.isFinite(s.thickness) ? clampNum(Math.round(s.thickness), 1, 12) : 1;
|
|
622
|
+
const lineStyle = typeof s.lineStyle === "string" && DIVIDER_LINE_STYLES.has(s.lineStyle) ? s.lineStyle : "solid";
|
|
623
|
+
hr.style.borderTop = `${thickness}px ${lineStyle} ${color}`;
|
|
624
|
+
wrap.appendChild(hr);
|
|
625
|
+
for (const [k, prop] of [
|
|
626
|
+
["marginTop", "marginTop"],
|
|
627
|
+
["marginRight", "marginRight"],
|
|
628
|
+
["marginBottom", "marginBottom"],
|
|
629
|
+
["marginLeft", "marginLeft"]
|
|
630
|
+
]) {
|
|
631
|
+
const v = s[k];
|
|
632
|
+
if (typeof v === "number" && Number.isFinite(v)) {
|
|
633
|
+
wrap.style[prop] = `${clampNum(v, 0, 64)}px`;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
return wrap;
|
|
637
|
+
}
|
|
476
638
|
function buildButtonBlock(block, doc, callbacks) {
|
|
477
639
|
const btn = doc.createElement("button");
|
|
478
640
|
btn.type = "button";
|
|
@@ -499,6 +661,12 @@ var BLOCK_ALIGN_SELF = {
|
|
|
499
661
|
function clampNum(n, min, max) {
|
|
500
662
|
return Math.min(max, Math.max(min, n));
|
|
501
663
|
}
|
|
664
|
+
var TEXT_PRESETS = {
|
|
665
|
+
title: { size: 18, weight: 600, lineHeight: 1.3 },
|
|
666
|
+
subtitle: { size: 16, weight: 600, lineHeight: 1.35 },
|
|
667
|
+
paragraph: { size: 14, weight: 400, lineHeight: 1.5 },
|
|
668
|
+
small: { size: 12, weight: 400, lineHeight: 1.5 }
|
|
669
|
+
};
|
|
502
670
|
function applyBlockStyle(el, style, kind) {
|
|
503
671
|
if (!style || typeof style !== "object") return;
|
|
504
672
|
const s = style;
|
|
@@ -508,14 +676,34 @@ function applyBlockStyle(el, style, kind) {
|
|
|
508
676
|
}
|
|
509
677
|
const align = typeof s.align === "string" ? s.align : null;
|
|
510
678
|
if (align && (align === "left" || align === "center" || align === "right")) {
|
|
511
|
-
if (kind === "text")
|
|
512
|
-
|
|
679
|
+
if (kind === "text") {
|
|
680
|
+
el.style.textAlign = align;
|
|
681
|
+
el.style.alignSelf = BLOCK_ALIGN_SELF[align];
|
|
682
|
+
} else if (kind === "image") el.style.alignSelf = BLOCK_ALIGN_SELF[align];
|
|
683
|
+
}
|
|
684
|
+
if (kind === "text" && typeof s.preset === "string" && s.preset in TEXT_PRESETS) {
|
|
685
|
+
const preset = TEXT_PRESETS[s.preset];
|
|
686
|
+
if (preset) {
|
|
687
|
+
el.style.fontSize = `${preset.size}px`;
|
|
688
|
+
el.style.fontWeight = String(preset.weight);
|
|
689
|
+
el.style.lineHeight = String(preset.lineHeight);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
if (kind !== "image" && s.colorMode === "dark") {
|
|
693
|
+
el.style.background = "#1f1d1b";
|
|
694
|
+
el.style.color = "#f5f0eb";
|
|
695
|
+
el.style.borderRadius = el.style.borderRadius || "8px";
|
|
513
696
|
}
|
|
514
697
|
if (kind !== "image" && typeof s.color === "string") el.style.color = s.color;
|
|
515
698
|
if (kind === "text" && typeof s.fontSize === "number" && Number.isFinite(s.fontSize)) {
|
|
516
699
|
el.style.fontSize = `${clampNum(s.fontSize, 8, 72)}px`;
|
|
517
700
|
}
|
|
518
|
-
if (kind === "image" &&
|
|
701
|
+
if (kind === "image" && s.naturalSize === true) {
|
|
702
|
+
el.style.width = "auto";
|
|
703
|
+
el.style.maxWidth = "100%";
|
|
704
|
+
el.style.maxHeight = "none";
|
|
705
|
+
el.style.objectFit = "contain";
|
|
706
|
+
} else if (kind === "image" && typeof s.width === "number" && Number.isFinite(s.width)) {
|
|
519
707
|
el.style.width = `${clampNum(s.width, 10, 100)}%`;
|
|
520
708
|
}
|
|
521
709
|
if (kind === "image" && typeof s.radius === "number" && Number.isFinite(s.radius)) {
|
|
@@ -936,7 +1124,161 @@ var GUIDE_STYLES = `
|
|
|
936
1124
|
}
|
|
937
1125
|
/* Card gen\xE9rica del modelo unificado. El dise\xF1o (bg/borde/padding/\u2026) lo aplica
|
|
938
1126
|
el renderer como estilos inline desde step.design; ac\xE1 van los defaults. */
|
|
1127
|
+
/* Edici\xF3n (builder): sin animaci\xF3n de entrada al repintar la preview. */
|
|
1128
|
+
:host(.veo-no-anim) * { animation: none !important; }
|
|
1129
|
+
|
|
1130
|
+
/* Bloque video: caja con relaci\xF3n de aspecto; el iframe la llena. */
|
|
1131
|
+
.veo-guide-video { position: relative; overflow: hidden; border-radius: 8px; background: #000; }
|
|
1132
|
+
.veo-guide-video iframe, .veo-guide-video video {
|
|
1133
|
+
position: absolute; inset: 0; width: 100%; height: 100%; border: 0; display: block;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
/* \u2500\u2500 Layout por filas/columnas (composite) \u2500\u2500
|
|
1137
|
+
El espacio tiene UNA fuente por capa: el aire con el borde lo da el padding
|
|
1138
|
+
de la card; la separaci\xF3n entre filas/bloques la da el margen inferior
|
|
1139
|
+
(12px, ah\xED vive la l\xEDnea \xAB+\xBB del editor); los bloques NO llevan padding
|
|
1140
|
+
propio (solo el que el autor les ponga inline). El \xFAltimo elemento no deja
|
|
1141
|
+
margen colgando. */
|
|
1142
|
+
.veo-card > [data-veo-block], .veo-card > .veo-row, .veo-card > .veo-guide-actions,
|
|
1143
|
+
.veo-col > [data-veo-block], .veo-col > .veo-guide-actions { margin-bottom: 12px; }
|
|
1144
|
+
/* La card y cada columna son columnas flex: cada bloque mide SU contenido (como
|
|
1145
|
+
el bot\xF3n) y se ubica con su alineaci\xF3n (align-self); filas, columnas, formularios
|
|
1146
|
+
y filas de botones ocupan todo el ancho. */
|
|
1147
|
+
.veo-card:not(.veo-card--empty), .veo-col {
|
|
1148
|
+
display: flex; flex-direction: column; align-items: flex-start;
|
|
1149
|
+
}
|
|
1150
|
+
.veo-card > [data-veo-block], .veo-col > [data-veo-block] { max-width: 100%; }
|
|
1151
|
+
/* L\xEDnea horizontal: ocupa todo el ancho; el padding del bloque es solo vertical
|
|
1152
|
+
(zona clickeable en el editor) y la l\xEDnea es el borde superior del hr. */
|
|
1153
|
+
.veo-card > .veo-guide-divider, .veo-col > .veo-guide-divider { padding: 6px 0; align-self: stretch; }
|
|
1154
|
+
.veo-guide-divider { box-sizing: border-box; }
|
|
1155
|
+
/* Bloque de c\xF3digo (modo host): ocupa todo el ancho, como un formulario. */
|
|
1156
|
+
.veo-code-host { display: block; min-width: 0; }
|
|
1157
|
+
.veo-card > .veo-code-host, .veo-col > .veo-code-host { align-self: stretch; }
|
|
1158
|
+
.veo-guide-divider hr { margin: 0; border: 0; height: 0; }
|
|
1159
|
+
.veo-card > .veo-row, .veo-card > .veo-guide-actions, .veo-card > .veo-form,
|
|
1160
|
+
.veo-col > .veo-guide-actions, .veo-col > .veo-form { align-self: stretch; }
|
|
1161
|
+
.veo-row { display: flex; gap: 12px; align-items: flex-start; box-sizing: border-box; }
|
|
1162
|
+
.veo-col { flex: 1 1 0; min-width: 0; }
|
|
1163
|
+
.veo-col > :last-child { margin-bottom: 0; }
|
|
1164
|
+
/* Fila con alineaci\xF3n horizontal (editor de fila): las columnas miden su contenido. */
|
|
1165
|
+
.veo-row--aligned > .veo-col { flex: 0 1 auto; }
|
|
1166
|
+
|
|
1167
|
+
/* \u2500\u2500 Editor in-app: bloque seleccionado + barra de acciones + placeholders \u2500\u2500 */
|
|
1168
|
+
.veo-block-placeholder {
|
|
1169
|
+
border: 1px dashed #b0a9a5; border-radius: 6px; padding: 12px; margin-bottom: 12px;
|
|
1170
|
+
color: #8a8582; font-size: 13px; text-align: center;
|
|
1171
|
+
}
|
|
1172
|
+
/* El bloque seleccionado no lleva contorno propio: lo rodea .veo-block-frame
|
|
1173
|
+
(marco aparte, con aire y alto m\xEDnimo, que no altera el layout). */
|
|
1174
|
+
.veo-block--selected { position: relative; z-index: 1; }
|
|
1175
|
+
.veo-block-frame {
|
|
1176
|
+
position: absolute; z-index: 4; pointer-events: none; box-sizing: border-box;
|
|
1177
|
+
border: 2px solid #3b82f6; border-radius: 10px; background: rgba(59, 130, 246, 0.04);
|
|
1178
|
+
}
|
|
1179
|
+
/* Barra del bloque (mock): p\xEDldora blanca con sombra, solo \xEDconos; la papelera
|
|
1180
|
+
en rojo, separada por una l\xEDnea. */
|
|
1181
|
+
.veo-block-toolbar {
|
|
1182
|
+
position: absolute; z-index: 5; display: inline-flex; align-items: center; gap: 2px; padding: 4px 6px;
|
|
1183
|
+
background: #fff; border: 1px solid rgba(0,0,0,0.06); border-radius: 12px;
|
|
1184
|
+
box-shadow: 0 6px 20px rgba(0,0,0,0.16);
|
|
1185
|
+
}
|
|
1186
|
+
.veo-block-toolbar button {
|
|
1187
|
+
border: 0; background: transparent; color: #4b4642; cursor: pointer; padding: 6px; border-radius: 8px;
|
|
1188
|
+
display: inline-flex; align-items: center; justify-content: center; line-height: 1;
|
|
1189
|
+
}
|
|
1190
|
+
.veo-block-toolbar button:hover { background: #f5efeb; }
|
|
1191
|
+
.veo-block-toolbar button svg { width: 20px; height: 20px; }
|
|
1192
|
+
.veo-block-toolbar .veo-block-toolbar-remove { color: #e5484d; }
|
|
1193
|
+
/* Separadores: antes de la papelera y antes de las flechas de mover. */
|
|
1194
|
+
.veo-block-toolbar .veo-block-toolbar-remove, .veo-block-toolbar .veo-block-toolbar-move-left {
|
|
1195
|
+
margin-left: 6px; position: relative;
|
|
1196
|
+
}
|
|
1197
|
+
.veo-block-toolbar .veo-block-toolbar-remove::before, .veo-block-toolbar .veo-block-toolbar-move-left::before {
|
|
1198
|
+
content: ""; position: absolute; left: -4px; top: 6px; bottom: 6px; width: 1px; background: #e7e2de;
|
|
1199
|
+
}
|
|
1200
|
+
.veo-block-toolbar button:disabled { opacity: 0.3; cursor: default; background: transparent; }
|
|
1201
|
+
/* Contenedor (hover): contorno azul de la tarjeta y dos pesta\xF1as centradas
|
|
1202
|
+
sobre su borde superior: \xABEditar contenedor\xBB (outline) y \xABEditar filas\xBB. */
|
|
1203
|
+
.veo-card-frame {
|
|
1204
|
+
position: absolute; z-index: 3; inset: 0; pointer-events: none; box-sizing: border-box;
|
|
1205
|
+
border: 2px solid #3b82f6; border-radius: inherit;
|
|
1206
|
+
}
|
|
1207
|
+
.veo-card-toolbar {
|
|
1208
|
+
position: absolute; z-index: 6; left: 50%; transform: translateX(-50%);
|
|
1209
|
+
display: inline-flex; align-items: flex-end; gap: 8px; white-space: nowrap;
|
|
1210
|
+
}
|
|
1211
|
+
.veo-card-toolbar button {
|
|
1212
|
+
cursor: pointer; padding: 8px 14px; border-radius: 8px; border: 2px solid #3b82f6;
|
|
1213
|
+
font: 600 14px/1 system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
1214
|
+
background: #fff; color: #3b82f6;
|
|
1215
|
+
}
|
|
1216
|
+
.veo-card-toolbar button:hover { background: #eff6ff; }
|
|
1217
|
+
/* Con fondo = la vista que el panel muestra ahora (contenedor o estructura). */
|
|
1218
|
+
.veo-card-toolbar button.is-active { background: #3b82f6; color: #fff; }
|
|
1219
|
+
.veo-card-toolbar button.is-active:hover { background: #2563eb; border-color: #2563eb; }
|
|
1220
|
+
/* Editor de fila: marco al pasar el mouse por una fila (o la fila del bloque
|
|
1221
|
+
seleccionado) y barra \xABEditar fila \xB7 duplicar\xBB bajo su esquina derecha. */
|
|
1222
|
+
.veo-row-frame {
|
|
1223
|
+
position: absolute; z-index: 3; pointer-events: none; box-sizing: border-box;
|
|
1224
|
+
border: 2px solid #3b82f6; border-radius: 10px;
|
|
1225
|
+
}
|
|
1226
|
+
.veo-row-toolbar {
|
|
1227
|
+
position: absolute; z-index: 5; display: inline-flex; align-items: center; gap: 2px; padding: 4px 6px;
|
|
1228
|
+
background: #fff; border: 1px solid rgba(0,0,0,0.06); border-radius: 12px;
|
|
1229
|
+
box-shadow: 0 6px 20px rgba(0,0,0,0.16); white-space: nowrap;
|
|
1230
|
+
}
|
|
1231
|
+
.veo-row-toolbar button {
|
|
1232
|
+
border: 0; background: transparent; color: #4b4642; cursor: pointer; padding: 6px 8px; border-radius: 8px;
|
|
1233
|
+
display: inline-flex; align-items: center; gap: 8px; line-height: 1;
|
|
1234
|
+
font: 500 14px/1 system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
1235
|
+
}
|
|
1236
|
+
.veo-row-toolbar button:hover { background: #f5efeb; }
|
|
1237
|
+
.veo-row-toolbar button svg { width: 20px; height: 20px; }
|
|
1238
|
+
.veo-row-toolbar .veo-row-toolbar-duplicate { margin-left: 6px; position: relative; }
|
|
1239
|
+
.veo-row-toolbar .veo-row-toolbar-duplicate::before {
|
|
1240
|
+
content: ""; position: absolute; left: -4px; top: 6px; bottom: 6px; width: 1px; background: #e7e2de;
|
|
1241
|
+
}
|
|
1242
|
+
.veo-row-toolbar .veo-row-toolbar-remove { color: #e5484d; }
|
|
1243
|
+
/* Huecos \xAB+\xBB (mock): zona invisible entre elementos; al pasar el mouse aparece
|
|
1244
|
+
la l\xEDnea azul (horizontal = fila / bloque apilado, vertical = columna) con
|
|
1245
|
+
un \xAB+\xBB circular al centro. */
|
|
1246
|
+
.veo-gap { position: absolute; z-index: 4; }
|
|
1247
|
+
.veo-gap i { position: absolute; background: #3b82f6; opacity: 0; transition: opacity 100ms; pointer-events: none; }
|
|
1248
|
+
.veo-gap--h i { left: 0; right: 0; top: 50%; height: 2px; margin-top: -1px; }
|
|
1249
|
+
.veo-gap--v i { top: 0; bottom: 0; left: 50%; width: 2px; margin-left: -1px; }
|
|
1250
|
+
.veo-gap button {
|
|
1251
|
+
position: absolute; left: 50%; top: 50%; margin: -11px 0 0 -11px; box-sizing: border-box;
|
|
1252
|
+
opacity: 0; transition: opacity 100ms; width: 22px; height: 22px; border-radius: 50%; padding: 0;
|
|
1253
|
+
border: 2px solid #fff; background: #3b82f6; color: #fff; cursor: pointer;
|
|
1254
|
+
font: 600 15px/18px system-ui, -apple-system, "Segoe UI", sans-serif; text-align: center;
|
|
1255
|
+
box-shadow: 0 2px 8px rgba(0,0,0,0.25);
|
|
1256
|
+
}
|
|
1257
|
+
.veo-gap:hover i, .veo-gap:hover button, .veo-gap:focus-within button { opacity: 1; }
|
|
1258
|
+
.veo-gap button:hover { background: #2563eb; }
|
|
1259
|
+
|
|
1260
|
+
/* Tarjeta sin bloques (paso reci\xE9n creado). La tarjeta NO impone tama\xF1o ni
|
|
1261
|
+
padding propios (as\xED el ancho/padding del dise\xF1o se ven tal cual); el tama\xF1o
|
|
1262
|
+
m\xEDnimo lo pone la zona punteada azul de la preview. */
|
|
1263
|
+
.veo-card--empty { display: flex; align-items: stretch; }
|
|
1264
|
+
.veo-card-empty-zone {
|
|
1265
|
+
flex: 1; min-width: 0; min-height: 160px; margin-top: 14px;
|
|
1266
|
+
display: flex; align-items: center; justify-content: center; gap: 6px; text-align: center;
|
|
1267
|
+
border: 2px dashed #3b82f6; border-radius: 8px; background: #eff6ff;
|
|
1268
|
+
color: #2563eb; font-size: 16px; font-weight: 500; user-select: none; pointer-events: none;
|
|
1269
|
+
padding: 0 12px; box-sizing: border-box; overflow: hidden;
|
|
1270
|
+
}
|
|
1271
|
+
.veo-card-empty-zone b { font-weight: 600; font-size: 22px; line-height: 1; }
|
|
1272
|
+
/* Sin ancho configurado, la zona da un tama\xF1o c\xF3modo por defecto. */
|
|
1273
|
+
.veo-card--empty:not([style*="width"]) .veo-card-empty-zone { min-width: 360px; }
|
|
1274
|
+
/* Tarjeta angosta (la mide el renderer): solo el \xAB+\xBB, sin texto, y m\xE1s baja. */
|
|
1275
|
+
.veo-card--narrow .veo-card-empty-zone span { display: none; }
|
|
1276
|
+
.veo-card--narrow .veo-card-empty-zone { min-height: 120px; }
|
|
939
1277
|
.veo-card {
|
|
1278
|
+
/* Contenedor posicionado SIEMPRE: el \u2715 (absolute) se ubica respecto a la
|
|
1279
|
+
tarjeta tambi\xE9n en pasos embebidos (inline), no respecto a la app. Los
|
|
1280
|
+
flotantes lo pisan con position:fixed inline. */
|
|
1281
|
+
position: relative;
|
|
940
1282
|
pointer-events: auto;
|
|
941
1283
|
background: var(--veo-bg, #ffffff);
|
|
942
1284
|
color: var(--veo-text, #1f2937);
|
|
@@ -1029,6 +1371,13 @@ var GUIDE_STYLES = `
|
|
|
1029
1371
|
transition: opacity 120ms ease;
|
|
1030
1372
|
}
|
|
1031
1373
|
.veo-guide-cta:hover { opacity: 0.9; }
|
|
1374
|
+
/* Presets del boton (editor): secundario = contorno; enlace = solo texto. */
|
|
1375
|
+
.veo-guide-cta--secondary {
|
|
1376
|
+
background: transparent; color: var(--veo-primary);
|
|
1377
|
+
border: 1px solid var(--veo-primary);
|
|
1378
|
+
}
|
|
1379
|
+
.veo-guide-cta--link { background: transparent; color: var(--veo-primary); padding: 0; }
|
|
1380
|
+
.veo-guide-cta--link:hover { opacity: 1; text-decoration: underline; }
|
|
1032
1381
|
.veo-guide-cta:active { transform: translateY(1px); }
|
|
1033
1382
|
.veo-guide-close {
|
|
1034
1383
|
position: absolute; top: -6px; right: -6px;
|
|
@@ -1059,6 +1408,10 @@ var GUIDE_STYLES = `
|
|
|
1059
1408
|
|
|
1060
1409
|
.veo-form { display: flex; flex-direction: column; gap: 14px; margin-bottom: 16px; }
|
|
1061
1410
|
.veo-form-field { display: flex; flex-direction: column; gap: 6px; }
|
|
1411
|
+
/* Pregunta de la encuesta (rich text del bloque form). */
|
|
1412
|
+
.veo-form-question { font-size: 16px; font-weight: 600; line-height: 1.35; color: var(--veo-text); }
|
|
1413
|
+
.veo-form-question p { margin: 0; }
|
|
1414
|
+
.veo-form > .veo-guide-actions { margin: 0; }
|
|
1062
1415
|
.veo-form-label { font-size: 13px; font-weight: 500; color: var(--veo-text); }
|
|
1063
1416
|
.veo-form-input {
|
|
1064
1417
|
font-size: 14px; color: var(--veo-text);
|
|
@@ -1067,26 +1420,71 @@ var GUIDE_STYLES = `
|
|
|
1067
1420
|
padding: 8px 10px; width: 100%;
|
|
1068
1421
|
}
|
|
1069
1422
|
.veo-form-input:focus { outline: 2px solid var(--veo-primary); outline-offset: -1px; border-color: transparent; }
|
|
1070
|
-
|
|
1423
|
+
/* Lista de tareas (mock): \xEDtems con anillo de progreso, texto + subt\xEDtulo,
|
|
1424
|
+
y al pie \xABX de N completadas\xBB + porcentaje + barra. */
|
|
1425
|
+
.veo-checklist {
|
|
1426
|
+
display: flex; flex-direction: column; gap: 22px; align-self: stretch; width: 100%;
|
|
1427
|
+
min-width: 360px; box-sizing: border-box;
|
|
1428
|
+
}
|
|
1429
|
+
.veo-checklist-title { font-size: 24px; font-weight: 700; line-height: 1.25; color: var(--veo-cl-title); }
|
|
1430
|
+
.veo-checklist-title p { margin: 0; }
|
|
1431
|
+
.veo-checklist-items { display: flex; flex-direction: column; gap: 22px; }
|
|
1432
|
+
.veo-checklist-item {
|
|
1433
|
+
display: flex; align-items: center; gap: 18px; width: 100%; text-align: left;
|
|
1434
|
+
background: none; border: 0; padding: 0; cursor: pointer; font: inherit; color: inherit;
|
|
1435
|
+
}
|
|
1436
|
+
.veo-checklist-item:hover .veo-checklist-label { text-decoration: underline; }
|
|
1437
|
+
.veo-checklist-ring { width: 44px; height: 44px; flex: 0 0 auto; }
|
|
1438
|
+
.veo-checklist-texts { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
|
|
1439
|
+
.veo-checklist-label {
|
|
1440
|
+
font-size: calc(var(--veo-cl-font-size, 14px) + 4px); font-weight: var(--veo-cl-font-weight, 600);
|
|
1441
|
+
line-height: 1.3; color: var(--veo-cl-title);
|
|
1442
|
+
}
|
|
1443
|
+
.veo-checklist-sub { font-size: calc(var(--veo-cl-font-size, 14px) + 1px); color: var(--veo-cl-subtitle); }
|
|
1444
|
+
.veo-checklist-footer { display: flex; flex-direction: column; gap: 10px; margin-top: 6px; }
|
|
1445
|
+
.veo-checklist-summary {
|
|
1446
|
+
display: flex; justify-content: space-between; align-items: baseline; gap: 16px;
|
|
1447
|
+
font-size: calc(var(--veo-cl-font-size, 14px) + 1px);
|
|
1448
|
+
}
|
|
1449
|
+
.veo-checklist-count { color: var(--veo-cl-subtitle); }
|
|
1450
|
+
.veo-checklist-percent { font-weight: 700; font-size: calc(var(--veo-cl-font-size, 14px) + 3px); color: var(--veo-cl-bar); }
|
|
1451
|
+
.veo-checklist-bar { height: var(--veo-cl-bar-h, 8px); background: #e7e2de; border-radius: 999px; overflow: hidden; }
|
|
1452
|
+
.veo-checklist--dark .veo-checklist-bar { background: #3c3734; }
|
|
1453
|
+
.veo-checklist-bar-fill { height: 100%; background: var(--veo-cl-bar); border-radius: 999px; transition: width 300ms ease; }
|
|
1454
|
+
.veo-checklist-empty { margin: 0; font-size: 13px; color: var(--veo-cl-subtitle); }
|
|
1455
|
+
/* Opci\xF3n m\xFAltiple (mock): c\xEDrculos/casillas grandes con su texto al lado. */
|
|
1456
|
+
.veo-form-options { display: flex; flex-direction: column; gap: 14px; }
|
|
1071
1457
|
.veo-form-option {
|
|
1072
|
-
display: flex; align-items: center; gap:
|
|
1073
|
-
font-size:
|
|
1074
|
-
}
|
|
1075
|
-
.veo-form-option input {
|
|
1076
|
-
|
|
1458
|
+
display: flex; align-items: center; gap: 14px;
|
|
1459
|
+
font-size: 16px; color: var(--veo-text); cursor: pointer;
|
|
1460
|
+
}
|
|
1461
|
+
.veo-form-option input {
|
|
1462
|
+
accent-color: var(--veo-primary); margin: 0; cursor: pointer;
|
|
1463
|
+
width: 26px; height: 26px; flex: 0 0 auto;
|
|
1464
|
+
}
|
|
1465
|
+
/* Escala num\xE9rica (mock): casillas cuadradas; con etiquetas, cada casilla
|
|
1466
|
+
lleva la suya debajo (la primera cuelga a la izquierda, la \xFAltima a la derecha). */
|
|
1467
|
+
.veo-form-scale { display: flex; gap: 8px; flex-wrap: wrap; align-items: flex-start; }
|
|
1468
|
+
.veo-scale-item { display: inline-flex; flex-direction: column; align-items: center; gap: 8px; }
|
|
1469
|
+
.veo-scale-item--first { align-items: flex-start; }
|
|
1470
|
+
.veo-scale-item--last { align-items: flex-end; }
|
|
1471
|
+
.veo-scale-label { font-size: 12px; line-height: 1.2; color: #8a8582; white-space: nowrap; }
|
|
1077
1472
|
.veo-nps-btn {
|
|
1078
|
-
min-width:
|
|
1079
|
-
font-size:
|
|
1080
|
-
background: var(--veo-bg); border: 1px solid #
|
|
1473
|
+
min-width: var(--veo-scale-size, 44px); height: var(--veo-scale-size, 44px); padding: 0 6px;
|
|
1474
|
+
font-size: 16px; font-weight: 600; color: var(--veo-text);
|
|
1475
|
+
background: var(--veo-bg); border: 1px solid #d5cec9; border-radius: 8px;
|
|
1081
1476
|
cursor: pointer; transition: all 100ms ease;
|
|
1082
1477
|
}
|
|
1083
|
-
.veo-nps-btn:hover { border-color: var(--veo-primary); }
|
|
1478
|
+
.veo-nps-btn:hover { border-color: var(--veo-scale-color, var(--veo-primary)); }
|
|
1084
1479
|
.veo-rating-btn {
|
|
1085
1480
|
background: none; border: none; padding: 0 2px;
|
|
1086
1481
|
font-size: 26px; line-height: 1; color: #d1d5db;
|
|
1087
1482
|
cursor: pointer; transition: color 100ms ease;
|
|
1088
1483
|
}
|
|
1089
|
-
.veo-scale-active.veo-nps-btn {
|
|
1484
|
+
.veo-scale-active.veo-nps-btn {
|
|
1485
|
+
background: var(--veo-scale-color, var(--veo-primary)); color: #fff;
|
|
1486
|
+
border-color: var(--veo-scale-color, var(--veo-primary));
|
|
1487
|
+
}
|
|
1090
1488
|
.veo-scale-active.veo-rating-btn { color: #f59e0b; }
|
|
1091
1489
|
.veo-form-error { font-size: 13px; color: #dc2626; margin: 0; }
|
|
1092
1490
|
.veo-form-thanks {
|
|
@@ -1755,6 +2153,210 @@ function bannerClassOf(position, embedded) {
|
|
|
1755
2153
|
return embedded ? "veo-banner veo-banner-embedded" : `veo-banner veo-banner-${position}`;
|
|
1756
2154
|
}
|
|
1757
2155
|
|
|
2156
|
+
// src/plugins/guides/checklist-block.ts
|
|
2157
|
+
var CHECKLIST_DEFAULTS = {
|
|
2158
|
+
iconEmptyColor: "#e7e2de",
|
|
2159
|
+
iconFillColor: "#406a31",
|
|
2160
|
+
barColor: "#406a31",
|
|
2161
|
+
barHeight: 8,
|
|
2162
|
+
fontSize: 14,
|
|
2163
|
+
fontWeight: 600,
|
|
2164
|
+
titleColor: "#2c2825",
|
|
2165
|
+
subtitleColor: "#8a8582",
|
|
2166
|
+
subtitleTemplate: "Completado <%= currentStep %> de <%= totalSteps %> pasos"
|
|
2167
|
+
};
|
|
2168
|
+
var WEIGHTS = { regular: 400, medium: 500, semibold: 600, bold: 700 };
|
|
2169
|
+
function checklistStyle(style) {
|
|
2170
|
+
const s = style ?? {};
|
|
2171
|
+
const str = (k, fb) => typeof s[k] === "string" && s[k].trim() ? s[k] : fb;
|
|
2172
|
+
const num2 = (k, fb, min, max) => typeof s[k] === "number" && Number.isFinite(s[k]) ? Math.min(max, Math.max(min, s[k])) : fb;
|
|
2173
|
+
const weightRaw = s.fontWeight;
|
|
2174
|
+
const fontWeight = typeof weightRaw === "number" ? Math.min(900, Math.max(100, weightRaw)) : typeof weightRaw === "string" && WEIGHTS[weightRaw.toLowerCase()] ? WEIGHTS[weightRaw.toLowerCase()] : CHECKLIST_DEFAULTS.fontWeight;
|
|
2175
|
+
const dark = s.colorMode === "dark";
|
|
2176
|
+
return {
|
|
2177
|
+
colorMode: dark ? "dark" : "light",
|
|
2178
|
+
iconEmptyColor: str("iconEmptyColor", dark ? "#3c3734" : CHECKLIST_DEFAULTS.iconEmptyColor),
|
|
2179
|
+
iconFillColor: str("iconFillColor", CHECKLIST_DEFAULTS.iconFillColor),
|
|
2180
|
+
barColor: str("barColor", CHECKLIST_DEFAULTS.barColor),
|
|
2181
|
+
barHeight: num2("barHeight", CHECKLIST_DEFAULTS.barHeight, 2, 40),
|
|
2182
|
+
fontSize: num2("fontSize", CHECKLIST_DEFAULTS.fontSize, 10, 40),
|
|
2183
|
+
fontWeight,
|
|
2184
|
+
titleColor: str("titleColor", dark ? "#f5efeb" : CHECKLIST_DEFAULTS.titleColor),
|
|
2185
|
+
subtitleColor: str("subtitleColor", dark ? "#b0a9a5" : CHECKLIST_DEFAULTS.subtitleColor),
|
|
2186
|
+
subtitleTemplate: str("subtitleTemplate", CHECKLIST_DEFAULTS.subtitleTemplate)
|
|
2187
|
+
};
|
|
2188
|
+
}
|
|
2189
|
+
function renderSubtitle(template, p) {
|
|
2190
|
+
return template.replace(/<%=\s*currentStep\s*%>/g, String(p.currentStep)).replace(/<%=\s*totalSteps\s*%>/g, String(p.totalSteps));
|
|
2191
|
+
}
|
|
2192
|
+
var RING_R = 10;
|
|
2193
|
+
var RING_C = 2 * Math.PI * RING_R;
|
|
2194
|
+
function buildRing(doc, fraction, completed, st) {
|
|
2195
|
+
const ns = "http://www.w3.org/2000/svg";
|
|
2196
|
+
const svg = doc.createElementNS(ns, "svg");
|
|
2197
|
+
svg.setAttribute("viewBox", "0 0 28 28");
|
|
2198
|
+
svg.setAttribute("class", "veo-checklist-ring");
|
|
2199
|
+
svg.setAttribute("aria-hidden", "true");
|
|
2200
|
+
const track = doc.createElementNS(ns, "circle");
|
|
2201
|
+
track.setAttribute("cx", "14");
|
|
2202
|
+
track.setAttribute("cy", "14");
|
|
2203
|
+
track.setAttribute("r", String(RING_R));
|
|
2204
|
+
track.setAttribute("fill", completed ? st.iconFillColor : "none");
|
|
2205
|
+
track.setAttribute("stroke", st.iconEmptyColor);
|
|
2206
|
+
track.setAttribute("stroke-width", "4.5");
|
|
2207
|
+
svg.appendChild(track);
|
|
2208
|
+
if (completed) {
|
|
2209
|
+
const check = doc.createElementNS(ns, "path");
|
|
2210
|
+
check.setAttribute("d", "M9 14.5l3.2 3.2L19 11");
|
|
2211
|
+
check.setAttribute("fill", "none");
|
|
2212
|
+
check.setAttribute("stroke", "#fff");
|
|
2213
|
+
check.setAttribute("stroke-width", "2.4");
|
|
2214
|
+
check.setAttribute("stroke-linecap", "round");
|
|
2215
|
+
check.setAttribute("stroke-linejoin", "round");
|
|
2216
|
+
svg.appendChild(check);
|
|
2217
|
+
return svg;
|
|
2218
|
+
}
|
|
2219
|
+
const f = Math.max(0, Math.min(1, fraction));
|
|
2220
|
+
if (f > 0) {
|
|
2221
|
+
const arc = doc.createElementNS(ns, "circle");
|
|
2222
|
+
arc.setAttribute("cx", "14");
|
|
2223
|
+
arc.setAttribute("cy", "14");
|
|
2224
|
+
arc.setAttribute("r", String(RING_R));
|
|
2225
|
+
arc.setAttribute("fill", "none");
|
|
2226
|
+
arc.setAttribute("stroke", st.iconFillColor);
|
|
2227
|
+
arc.setAttribute("stroke-width", "4.5");
|
|
2228
|
+
arc.setAttribute("stroke-linecap", "butt");
|
|
2229
|
+
arc.setAttribute("stroke-dasharray", `${(RING_C * f).toFixed(2)} ${RING_C.toFixed(2)}`);
|
|
2230
|
+
arc.setAttribute("transform", "rotate(-90 14 14)");
|
|
2231
|
+
svg.appendChild(arc);
|
|
2232
|
+
}
|
|
2233
|
+
return svg;
|
|
2234
|
+
}
|
|
2235
|
+
function buildChecklistBlock(block, doc, context) {
|
|
2236
|
+
const st = checklistStyle(block.style);
|
|
2237
|
+
const items = (block.items ?? []).filter((i) => i && typeof i.guideId === "string" && i.guideId);
|
|
2238
|
+
const root = doc.createElement("div");
|
|
2239
|
+
root.className = `veo-checklist${st.colorMode === "dark" ? " veo-checklist--dark" : ""}`;
|
|
2240
|
+
root.style.setProperty("--veo-cl-font-size", `${st.fontSize}px`);
|
|
2241
|
+
root.style.setProperty("--veo-cl-font-weight", String(st.fontWeight));
|
|
2242
|
+
root.style.setProperty("--veo-cl-title", st.titleColor);
|
|
2243
|
+
root.style.setProperty("--veo-cl-subtitle", st.subtitleColor);
|
|
2244
|
+
root.style.setProperty("--veo-cl-bar", st.barColor);
|
|
2245
|
+
root.style.setProperty("--veo-cl-bar-h", `${st.barHeight}px`);
|
|
2246
|
+
if (typeof block.html === "string" && block.html || typeof block.text === "string" && block.text) {
|
|
2247
|
+
const title = doc.createElement("div");
|
|
2248
|
+
title.className = "veo-checklist-title";
|
|
2249
|
+
if (typeof block.html === "string" && block.html)
|
|
2250
|
+
title.appendChild(renderRichText(block.html, doc));
|
|
2251
|
+
else title.textContent = block.text;
|
|
2252
|
+
root.appendChild(title);
|
|
2253
|
+
}
|
|
2254
|
+
const list = doc.createElement("div");
|
|
2255
|
+
list.className = "veo-checklist-items";
|
|
2256
|
+
list.setAttribute("role", "list");
|
|
2257
|
+
root.appendChild(list);
|
|
2258
|
+
const footer = doc.createElement("div");
|
|
2259
|
+
footer.className = "veo-checklist-footer";
|
|
2260
|
+
const summary = doc.createElement("div");
|
|
2261
|
+
summary.className = "veo-checklist-summary";
|
|
2262
|
+
const count = doc.createElement("span");
|
|
2263
|
+
count.className = "veo-checklist-count";
|
|
2264
|
+
const percent = doc.createElement("span");
|
|
2265
|
+
percent.className = "veo-checklist-percent";
|
|
2266
|
+
summary.append(count, percent);
|
|
2267
|
+
const bar = doc.createElement("div");
|
|
2268
|
+
bar.className = "veo-checklist-bar";
|
|
2269
|
+
const fill = doc.createElement("div");
|
|
2270
|
+
fill.className = "veo-checklist-bar-fill";
|
|
2271
|
+
bar.appendChild(fill);
|
|
2272
|
+
footer.append(summary, bar);
|
|
2273
|
+
root.appendChild(footer);
|
|
2274
|
+
const paint = (progress) => {
|
|
2275
|
+
list.textContent = "";
|
|
2276
|
+
const visible = progress ? items.filter((i) => progress.has(i.guideId)) : items;
|
|
2277
|
+
let done = 0;
|
|
2278
|
+
for (const item of visible) {
|
|
2279
|
+
const p = progress?.get(item.guideId) ?? null;
|
|
2280
|
+
if (p?.completed) done++;
|
|
2281
|
+
const row = doc.createElement("button");
|
|
2282
|
+
row.type = "button";
|
|
2283
|
+
row.className = `veo-checklist-item${p?.completed ? " veo-checklist-item--done" : ""}`;
|
|
2284
|
+
row.setAttribute("role", "listitem");
|
|
2285
|
+
const fraction = p && p.totalSteps > 0 ? p.currentStep / p.totalSteps : 0;
|
|
2286
|
+
row.appendChild(buildRing(doc, fraction, p?.completed === true, st));
|
|
2287
|
+
const texts = doc.createElement("span");
|
|
2288
|
+
texts.className = "veo-checklist-texts";
|
|
2289
|
+
const label = doc.createElement("span");
|
|
2290
|
+
label.className = "veo-checklist-label";
|
|
2291
|
+
label.textContent = (item.text ?? "").trim() || p?.guideName || "Gu\xEDa";
|
|
2292
|
+
if (p && p.published === false) row.title = "Gu\xEDa no publicada: los usuarios no la ver\xE1n";
|
|
2293
|
+
texts.appendChild(label);
|
|
2294
|
+
if (p) {
|
|
2295
|
+
const sub = doc.createElement("span");
|
|
2296
|
+
sub.className = "veo-checklist-sub";
|
|
2297
|
+
sub.textContent = renderSubtitle(st.subtitleTemplate, p);
|
|
2298
|
+
texts.appendChild(sub);
|
|
2299
|
+
}
|
|
2300
|
+
row.appendChild(texts);
|
|
2301
|
+
row.addEventListener("click", (e) => {
|
|
2302
|
+
e.preventDefault();
|
|
2303
|
+
e.stopPropagation();
|
|
2304
|
+
if (context.isPreview) return;
|
|
2305
|
+
const start = p && !p.completed ? Math.min(p.currentStep, p.totalSteps - 1) : 0;
|
|
2306
|
+
if (context.onOpenGuideAt) context.onOpenGuideAt(item.guideId, start);
|
|
2307
|
+
else context.onOpenGuide?.(item.guideId);
|
|
2308
|
+
});
|
|
2309
|
+
list.appendChild(row);
|
|
2310
|
+
}
|
|
2311
|
+
const total = visible.length;
|
|
2312
|
+
const pct = total > 0 ? Math.round(done / total * 100) : 0;
|
|
2313
|
+
count.textContent = `${done} de ${total} ${total === 1 ? "completada" : "completadas"}`;
|
|
2314
|
+
percent.textContent = `${pct}%`;
|
|
2315
|
+
fill.style.width = `${pct}%`;
|
|
2316
|
+
footer.hidden = total === 0;
|
|
2317
|
+
};
|
|
2318
|
+
if (context.isPreview && items.length === 0) {
|
|
2319
|
+
const sample = /* @__PURE__ */ new Map([
|
|
2320
|
+
[
|
|
2321
|
+
"__sample_1",
|
|
2322
|
+
{
|
|
2323
|
+
guideId: "__sample_1",
|
|
2324
|
+
guideName: "Configura tu primer env\xEDo",
|
|
2325
|
+
totalSteps: 2,
|
|
2326
|
+
currentStep: 1,
|
|
2327
|
+
completed: false
|
|
2328
|
+
}
|
|
2329
|
+
],
|
|
2330
|
+
[
|
|
2331
|
+
"__sample_2",
|
|
2332
|
+
{
|
|
2333
|
+
guideId: "__sample_2",
|
|
2334
|
+
guideName: "Conecta tu tienda",
|
|
2335
|
+
totalSteps: 2,
|
|
2336
|
+
currentStep: 2,
|
|
2337
|
+
completed: true
|
|
2338
|
+
}
|
|
2339
|
+
]
|
|
2340
|
+
]);
|
|
2341
|
+
items.push(
|
|
2342
|
+
{ guideId: "__sample_1", text: "Configura tu primer env\xEDo" },
|
|
2343
|
+
{ guideId: "__sample_2", text: "Conecta tu tienda" }
|
|
2344
|
+
);
|
|
2345
|
+
paint(sample);
|
|
2346
|
+
const hint = doc.createElement("p");
|
|
2347
|
+
hint.className = "veo-checklist-empty";
|
|
2348
|
+
hint.textContent = "Ejemplo: agrega gu\xEDas a la lista desde Opciones.";
|
|
2349
|
+
root.appendChild(hint);
|
|
2350
|
+
return root;
|
|
2351
|
+
}
|
|
2352
|
+
paint(null);
|
|
2353
|
+
if (context.fetchProgress && items.length > 0) {
|
|
2354
|
+
void context.fetchProgress(items.map((i) => i.guideId)).then((rows) => paint(new Map(rows.map((r) => [r.guideId, r])))).catch(() => {
|
|
2355
|
+
});
|
|
2356
|
+
}
|
|
2357
|
+
return root;
|
|
2358
|
+
}
|
|
2359
|
+
|
|
1758
2360
|
// src/utils/uuid.ts
|
|
1759
2361
|
function uuidv7() {
|
|
1760
2362
|
const timestamp = Date.now();
|
|
@@ -1911,6 +2513,7 @@ function escapeClosing(code, tag) {
|
|
|
1911
2513
|
}
|
|
1912
2514
|
|
|
1913
2515
|
// src/plugins/guides/code-block.ts
|
|
2516
|
+
var RAW_BLOCK_ATTR = "data-veo-raw-block";
|
|
1914
2517
|
function insertRawNodes(anchor, nodes, pos) {
|
|
1915
2518
|
const parent = anchor.parentNode;
|
|
1916
2519
|
if (pos === "prepend") {
|
|
@@ -1957,6 +2560,11 @@ function mountCodeBlock(block, doc, context, host, rawInline) {
|
|
|
1957
2560
|
}
|
|
1958
2561
|
if (rawInline) {
|
|
1959
2562
|
const rawNodes = Array.from(holder.childNodes);
|
|
2563
|
+
if (rawInline.tagBlockId) {
|
|
2564
|
+
for (const n of rawNodes) {
|
|
2565
|
+
if (n instanceof Element) n.setAttribute(RAW_BLOCK_ATTR, rawInline.tagBlockId);
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
1960
2568
|
insertRawNodes(rawInline.anchor, rawNodes, rawInline.pos);
|
|
1961
2569
|
injected.push(...rawNodes);
|
|
1962
2570
|
} else if (host) {
|
|
@@ -1966,24 +2574,40 @@ function mountCodeBlock(block, doc, context, host, rawInline) {
|
|
|
1966
2574
|
injected.push(holder);
|
|
1967
2575
|
const slot = doc.createElement("slot");
|
|
1968
2576
|
slot.setAttribute("name", slotName);
|
|
1969
|
-
|
|
2577
|
+
const wrapper = doc.createElement("div");
|
|
2578
|
+
wrapper.className = "veo-code-host";
|
|
2579
|
+
wrapper.appendChild(slot);
|
|
2580
|
+
node = wrapper;
|
|
1970
2581
|
} else {
|
|
1971
2582
|
node = holder;
|
|
1972
2583
|
}
|
|
1973
2584
|
}
|
|
2585
|
+
let cancelled = false;
|
|
1974
2586
|
if (typeof block.js === "string" && block.js.trim()) {
|
|
1975
|
-
const
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
2587
|
+
const js = block.js;
|
|
2588
|
+
const run = () => {
|
|
2589
|
+
if (cancelled) return;
|
|
2590
|
+
const s = hostDoc.createElement("script");
|
|
2591
|
+
s.textContent = wrapUserScript(js);
|
|
2592
|
+
hostDoc.body.appendChild(s);
|
|
2593
|
+
injected.push(s);
|
|
2594
|
+
};
|
|
2595
|
+
if (host && !host.isConnected) queueMicrotask(run);
|
|
2596
|
+
else run();
|
|
1979
2597
|
}
|
|
1980
2598
|
return {
|
|
1981
2599
|
node,
|
|
1982
2600
|
cleanup: () => {
|
|
2601
|
+
cancelled = true;
|
|
1983
2602
|
for (const n of injected) n.parentNode?.removeChild(n);
|
|
1984
2603
|
}
|
|
1985
2604
|
};
|
|
1986
2605
|
}
|
|
2606
|
+
function wrapUserScript(js) {
|
|
2607
|
+
return `(function(){try{
|
|
2608
|
+
${js}
|
|
2609
|
+
}catch(e){console.error("[OnUser] Error en el bloque de c\xF3digo:",e)}})();`;
|
|
2610
|
+
}
|
|
1987
2611
|
function runScript(old, hostDoc) {
|
|
1988
2612
|
const s = hostDoc.createElement("script");
|
|
1989
2613
|
for (const attr of Array.from(old.attributes)) s.setAttribute(attr.name, attr.value);
|
|
@@ -2218,8 +2842,14 @@ function buildField(field, doc) {
|
|
|
2218
2842
|
wrapper.appendChild(group);
|
|
2219
2843
|
return { wrapper, read: () => inputs.find((i) => i.input.checked)?.value };
|
|
2220
2844
|
}
|
|
2221
|
-
case "nps":
|
|
2222
|
-
|
|
2845
|
+
case "nps": {
|
|
2846
|
+
const min = typeof field.min === "number" ? field.min : 0;
|
|
2847
|
+
const max = typeof field.max === "number" && field.max > min ? field.max : 10;
|
|
2848
|
+
return buildScale(wrapper, doc, min, max, "veo-nps-btn", void 0, {
|
|
2849
|
+
labels: field.scaleLabels ?? [],
|
|
2850
|
+
showLabels: field.showLabels !== false
|
|
2851
|
+
});
|
|
2852
|
+
}
|
|
2223
2853
|
case "rating":
|
|
2224
2854
|
return buildScale(wrapper, doc, 1, 5, "veo-rating-btn", "\u2605");
|
|
2225
2855
|
default: {
|
|
@@ -2232,17 +2862,21 @@ function buildField(field, doc) {
|
|
|
2232
2862
|
}
|
|
2233
2863
|
}
|
|
2234
2864
|
}
|
|
2235
|
-
function buildScale(wrapper, doc, min, max, btnClass, symbol) {
|
|
2865
|
+
function buildScale(wrapper, doc, min, max, btnClass, symbol, opts = { labels: [], showLabels: false }) {
|
|
2236
2866
|
const row = doc.createElement("div");
|
|
2237
2867
|
row.className = "veo-form-scale";
|
|
2238
2868
|
let selected;
|
|
2239
2869
|
const buttons = [];
|
|
2870
|
+
const labelOf = (n) => opts.labels[n - min]?.trim() ?? "";
|
|
2871
|
+
const anyLabel = opts.showLabels && Array.from({ length: max - min + 1 }, (_, i) => min + i).some((n) => labelOf(n));
|
|
2240
2872
|
for (let n = min; n <= max; n++) {
|
|
2241
2873
|
const btn = doc.createElement("button");
|
|
2242
2874
|
btn.type = "button";
|
|
2243
2875
|
btn.className = btnClass;
|
|
2244
2876
|
btn.textContent = symbol ?? String(n);
|
|
2245
|
-
|
|
2877
|
+
const lbl = labelOf(n);
|
|
2878
|
+
btn.setAttribute("aria-label", lbl ? `${n} \xB7 ${lbl}` : String(n));
|
|
2879
|
+
if (lbl) btn.title = lbl;
|
|
2246
2880
|
btn.addEventListener("click", () => {
|
|
2247
2881
|
selected = n;
|
|
2248
2882
|
buttons.forEach((b, i) => {
|
|
@@ -2251,11 +2885,127 @@ function buildScale(wrapper, doc, min, max, btnClass, symbol) {
|
|
|
2251
2885
|
});
|
|
2252
2886
|
});
|
|
2253
2887
|
buttons.push(btn);
|
|
2254
|
-
|
|
2888
|
+
if (!anyLabel) {
|
|
2889
|
+
row.appendChild(btn);
|
|
2890
|
+
continue;
|
|
2891
|
+
}
|
|
2892
|
+
const item = doc.createElement("div");
|
|
2893
|
+
item.className = `veo-scale-item${n === min ? " veo-scale-item--first" : n === max ? " veo-scale-item--last" : ""}`;
|
|
2894
|
+
const text = doc.createElement("span");
|
|
2895
|
+
text.className = "veo-scale-label";
|
|
2896
|
+
text.textContent = lbl;
|
|
2897
|
+
item.append(btn, text);
|
|
2898
|
+
row.appendChild(item);
|
|
2255
2899
|
}
|
|
2256
2900
|
wrapper.appendChild(row);
|
|
2257
2901
|
return { wrapper, read: () => selected };
|
|
2258
2902
|
}
|
|
2903
|
+
|
|
2904
|
+
// src/plugins/guides/layout.ts
|
|
2905
|
+
function groupLayout(blocks) {
|
|
2906
|
+
const rows = /* @__PURE__ */ new Map();
|
|
2907
|
+
const order = [];
|
|
2908
|
+
let lastRow = -1;
|
|
2909
|
+
blocks.forEach((block, index) => {
|
|
2910
|
+
if (!block) return;
|
|
2911
|
+
let row;
|
|
2912
|
+
if (typeof block.row === "number" && Number.isFinite(block.row)) {
|
|
2913
|
+
row = block.row;
|
|
2914
|
+
lastRow = row;
|
|
2915
|
+
} else {
|
|
2916
|
+
row = lastRow + 0.5;
|
|
2917
|
+
lastRow = row;
|
|
2918
|
+
}
|
|
2919
|
+
const col = typeof block.col === "number" && Number.isFinite(block.col) ? block.col : 0;
|
|
2920
|
+
let r = rows.get(row);
|
|
2921
|
+
if (!r) {
|
|
2922
|
+
r = { row, columns: [] };
|
|
2923
|
+
rows.set(row, r);
|
|
2924
|
+
order.push(row);
|
|
2925
|
+
}
|
|
2926
|
+
let c = r.columns.find((x) => x.col === col);
|
|
2927
|
+
if (!c) {
|
|
2928
|
+
c = { col, blocks: [] };
|
|
2929
|
+
r.columns.push(c);
|
|
2930
|
+
}
|
|
2931
|
+
c.blocks.push({ block, index });
|
|
2932
|
+
});
|
|
2933
|
+
return order.sort((a, b) => a - b).map((k) => {
|
|
2934
|
+
const r = rows.get(k);
|
|
2935
|
+
r.columns.sort((a, b) => a.col - b.col);
|
|
2936
|
+
return r;
|
|
2937
|
+
});
|
|
2938
|
+
}
|
|
2939
|
+
|
|
2940
|
+
// src/plugins/guides/scroll-lock.ts
|
|
2941
|
+
var states = /* @__PURE__ */ new WeakMap();
|
|
2942
|
+
function lockScroll(doc = document) {
|
|
2943
|
+
const html = doc.documentElement;
|
|
2944
|
+
const body = doc.body;
|
|
2945
|
+
if (!html || !body) return () => {
|
|
2946
|
+
};
|
|
2947
|
+
let state = states.get(doc);
|
|
2948
|
+
if (!state) {
|
|
2949
|
+
state = {
|
|
2950
|
+
count: 0,
|
|
2951
|
+
html: { overflow: html.style.overflow, paddingRight: html.style.paddingRight },
|
|
2952
|
+
body: { overflow: body.style.overflow, paddingRight: body.style.paddingRight }
|
|
2953
|
+
};
|
|
2954
|
+
states.set(doc, state);
|
|
2955
|
+
const win = doc.defaultView;
|
|
2956
|
+
const gap = win ? Math.max(0, win.innerWidth - html.clientWidth) : 0;
|
|
2957
|
+
if (gap > 0) body.style.paddingRight = `${gap}px`;
|
|
2958
|
+
html.style.overflow = "hidden";
|
|
2959
|
+
body.style.overflow = "hidden";
|
|
2960
|
+
}
|
|
2961
|
+
state.count += 1;
|
|
2962
|
+
let released = false;
|
|
2963
|
+
return () => {
|
|
2964
|
+
if (released) return;
|
|
2965
|
+
released = true;
|
|
2966
|
+
const s = states.get(doc);
|
|
2967
|
+
if (!s) return;
|
|
2968
|
+
s.count -= 1;
|
|
2969
|
+
if (s.count > 0) return;
|
|
2970
|
+
states.delete(doc);
|
|
2971
|
+
html.style.overflow = s.html.overflow;
|
|
2972
|
+
html.style.paddingRight = s.html.paddingRight;
|
|
2973
|
+
body.style.overflow = s.body.overflow;
|
|
2974
|
+
body.style.paddingRight = s.body.paddingRight;
|
|
2975
|
+
};
|
|
2976
|
+
}
|
|
2977
|
+
|
|
2978
|
+
// src/plugins/guides/snooze.ts
|
|
2979
|
+
var KEY = (guideId) => `veo:snooze:${guideId}`;
|
|
2980
|
+
var UNIT_MS = {
|
|
2981
|
+
hour: 36e5,
|
|
2982
|
+
day: 864e5,
|
|
2983
|
+
week: 6048e5
|
|
2984
|
+
};
|
|
2985
|
+
function snoozeDurationMs(every, unit) {
|
|
2986
|
+
const n = Number.isFinite(every) && every > 0 ? Math.floor(every) : 1;
|
|
2987
|
+
return n * (UNIT_MS[unit] ?? UNIT_MS.day);
|
|
2988
|
+
}
|
|
2989
|
+
function snoozeGuide(guideId, every, unit, now = Date.now()) {
|
|
2990
|
+
try {
|
|
2991
|
+
localStorage.setItem(KEY(guideId), String(now + snoozeDurationMs(every, unit)));
|
|
2992
|
+
} catch {
|
|
2993
|
+
}
|
|
2994
|
+
}
|
|
2995
|
+
function isSnoozed(guideId, now = Date.now()) {
|
|
2996
|
+
try {
|
|
2997
|
+
const raw = localStorage.getItem(KEY(guideId));
|
|
2998
|
+
if (raw === null) return false;
|
|
2999
|
+
const until = Number(raw);
|
|
3000
|
+
if (!Number.isFinite(until) || until <= now) {
|
|
3001
|
+
localStorage.removeItem(KEY(guideId));
|
|
3002
|
+
return false;
|
|
3003
|
+
}
|
|
3004
|
+
return true;
|
|
3005
|
+
} catch {
|
|
3006
|
+
return false;
|
|
3007
|
+
}
|
|
3008
|
+
}
|
|
2259
3009
|
var INFO_GLYPH = "M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021";
|
|
2260
3010
|
var QUESTION_GLYPH = "M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75";
|
|
2261
3011
|
var STAR_GLYPH = "M11.48 3.499a.562.562 0 011.04 0l2.125 5.111a.563.563 0 00.475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 00-.182.557l1.285 5.385a.562.562 0 01-.84.61l-4.725-2.885a.563.563 0 00-.586 0L6.982 20.54a.562.562 0 01-.84-.61l1.285-5.386a.562.562 0 00-.182-.557l-4.204-3.602a.563.563 0 01.321-.988l5.518-.442a.563.563 0 00.475-.345L11.48 3.5z";
|
|
@@ -2445,6 +3195,32 @@ var ROW_ALIGN = {
|
|
|
2445
3195
|
center: "center",
|
|
2446
3196
|
right: "flex-end"
|
|
2447
3197
|
};
|
|
3198
|
+
var ROW_JUSTIFY = { ...ROW_ALIGN, distribute: "space-between" };
|
|
3199
|
+
var ROW_VALIGN = {
|
|
3200
|
+
top: "flex-start",
|
|
3201
|
+
center: "center",
|
|
3202
|
+
bottom: "flex-end"
|
|
3203
|
+
};
|
|
3204
|
+
function applyRowStyle(rowEl, style) {
|
|
3205
|
+
const s = rowEl.style;
|
|
3206
|
+
if (typeof style.background === "string" && style.background) s.background = style.background;
|
|
3207
|
+
for (const side of ["Top", "Right", "Bottom", "Left"]) {
|
|
3208
|
+
const p = style[`padding${side}`];
|
|
3209
|
+
if (typeof p === "number" && Number.isFinite(p))
|
|
3210
|
+
s.setProperty(`padding-${side.toLowerCase()}`, `${p}px`);
|
|
3211
|
+
const m = style[`margin${side}`];
|
|
3212
|
+
if (typeof m === "number" && Number.isFinite(m))
|
|
3213
|
+
s.setProperty(`margin-${side.toLowerCase()}`, `${m}px`);
|
|
3214
|
+
}
|
|
3215
|
+
if (typeof style.minHeight === "number" && Number.isFinite(style.minHeight) && style.minHeight > 0) {
|
|
3216
|
+
s.minHeight = `${style.minHeight}px`;
|
|
3217
|
+
}
|
|
3218
|
+
if (style.align && ROW_JUSTIFY[style.align]) {
|
|
3219
|
+
s.justifyContent = ROW_JUSTIFY[style.align];
|
|
3220
|
+
rowEl.classList.add("veo-row--aligned");
|
|
3221
|
+
}
|
|
3222
|
+
if (style.valign && ROW_VALIGN[style.valign]) s.alignItems = ROW_VALIGN[style.valign];
|
|
3223
|
+
}
|
|
2448
3224
|
var CompositeRenderer = class extends BaseRenderer {
|
|
2449
3225
|
async render(context) {
|
|
2450
3226
|
const idx = context.nav?.stepIndex ?? 0;
|
|
@@ -2463,7 +3239,8 @@ var CompositeRenderer = class extends BaseRenderer {
|
|
|
2463
3239
|
const rawDoc = anchor.ownerDocument ?? document;
|
|
2464
3240
|
const { cleanup } = mountCodeBlock(soleCode, rawDoc, context, void 0, {
|
|
2465
3241
|
anchor,
|
|
2466
|
-
pos: inlinePosOf(step.inlinePosition)
|
|
3242
|
+
pos: inlinePosOf(step.inlinePosition),
|
|
3243
|
+
...context.isPreview && soleCode.editorId ? { tagBlockId: soleCode.editorId } : {}
|
|
2467
3244
|
});
|
|
2468
3245
|
this.registerCleanup(cleanup);
|
|
2469
3246
|
context.onInteraction({ guideId: context.guide.guideId, stepIndex: idx, action: "shown" });
|
|
@@ -2476,6 +3253,10 @@ var CompositeRenderer = class extends BaseRenderer {
|
|
|
2476
3253
|
const card = doc.createElement("div");
|
|
2477
3254
|
card.className = "veo-card";
|
|
2478
3255
|
applyCompositeDesign(card, design);
|
|
3256
|
+
if (typeof design.ariaLabel === "string" && design.ariaLabel.trim()) {
|
|
3257
|
+
card.setAttribute("role", "dialog");
|
|
3258
|
+
card.setAttribute("aria-label", design.ariaLabel.trim());
|
|
3259
|
+
}
|
|
2479
3260
|
const dismiss = (action, meta) => {
|
|
2480
3261
|
context.onInteraction({
|
|
2481
3262
|
guideId: context.guide.guideId,
|
|
@@ -2486,6 +3267,28 @@ var CompositeRenderer = class extends BaseRenderer {
|
|
|
2486
3267
|
};
|
|
2487
3268
|
const readers = [];
|
|
2488
3269
|
this.buildContent(card, step.blocks ?? [], doc, context, idx, readers, host);
|
|
3270
|
+
if (blocks.length === 0) {
|
|
3271
|
+
card.classList.add("veo-card--empty");
|
|
3272
|
+
if (context.isPreview) {
|
|
3273
|
+
const zone = doc.createElement("div");
|
|
3274
|
+
zone.className = "veo-card-empty-zone";
|
|
3275
|
+
const plus = doc.createElement("b");
|
|
3276
|
+
plus.textContent = "+";
|
|
3277
|
+
const text = doc.createElement("span");
|
|
3278
|
+
text.textContent = "Clic para agregar contenido";
|
|
3279
|
+
zone.append(plus, text);
|
|
3280
|
+
card.appendChild(zone);
|
|
3281
|
+
const applyNarrow = () => {
|
|
3282
|
+
card.classList.toggle("veo-card--narrow", card.clientWidth < NARROW_CARD_WIDTH);
|
|
3283
|
+
};
|
|
3284
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
3285
|
+
const ro = new ResizeObserver(applyNarrow);
|
|
3286
|
+
ro.observe(card);
|
|
3287
|
+
this.registerCleanup(() => ro.disconnect());
|
|
3288
|
+
}
|
|
3289
|
+
applyNarrow();
|
|
3290
|
+
}
|
|
3291
|
+
}
|
|
2489
3292
|
const lastBlock = card.lastElementChild;
|
|
2490
3293
|
if (lastBlock && !lastBlock.style.marginBottom) lastBlock.style.marginBottom = "0";
|
|
2491
3294
|
let closeTooltip = null;
|
|
@@ -2503,6 +3306,14 @@ var CompositeRenderer = class extends BaseRenderer {
|
|
|
2503
3306
|
if (closeColor) closeBtn.style.color = closeColor;
|
|
2504
3307
|
const closePos = typeof design.closePosition === "string" ? design.closePosition : "top-right";
|
|
2505
3308
|
applyClosePosition(closeBtn, closePos);
|
|
3309
|
+
for (const side of ["Top", "Right", "Bottom", "Left"]) {
|
|
3310
|
+
const p = design[`closePadding${side}`];
|
|
3311
|
+
if (typeof p === "number")
|
|
3312
|
+
closeBtn.style.setProperty(`padding-${side.toLowerCase()}`, `${p}px`);
|
|
3313
|
+
const m = design[`closeMargin${side}`];
|
|
3314
|
+
if (typeof m === "number")
|
|
3315
|
+
closeBtn.style.setProperty(`margin-${side.toLowerCase()}`, `${m}px`);
|
|
3316
|
+
}
|
|
2506
3317
|
if (hoverColor) {
|
|
2507
3318
|
closeBtn.addEventListener("mouseenter", () => {
|
|
2508
3319
|
closeBtn.style.color = hoverColor;
|
|
@@ -2512,6 +3323,7 @@ var CompositeRenderer = class extends BaseRenderer {
|
|
|
2512
3323
|
});
|
|
2513
3324
|
}
|
|
2514
3325
|
card.appendChild(closeBtn);
|
|
3326
|
+
reserveCloseSpace(card, blocks, closePos, design);
|
|
2515
3327
|
}
|
|
2516
3328
|
if (badgeMode && anchor) {
|
|
2517
3329
|
closeTooltip = this.renderBadgeMode(
|
|
@@ -2542,6 +3354,7 @@ var CompositeRenderer = class extends BaseRenderer {
|
|
|
2542
3354
|
const bz = typeof design.backdropZIndex === "number" ? design.backdropZIndex : 2e5;
|
|
2543
3355
|
bg.style.zIndex = String(bz);
|
|
2544
3356
|
root.appendChild(bg);
|
|
3357
|
+
this.registerCleanup(lockScroll(doc));
|
|
2545
3358
|
if (design.targetAbove === true) this.registerCleanup(liftTarget(anchor, bz + 1));
|
|
2546
3359
|
}
|
|
2547
3360
|
const wrapper = doc.createElement("div");
|
|
@@ -2568,6 +3381,7 @@ var CompositeRenderer = class extends BaseRenderer {
|
|
|
2568
3381
|
this.closeOrSkip(context);
|
|
2569
3382
|
}
|
|
2570
3383
|
});
|
|
3384
|
+
this.registerCleanup(lockScroll(doc));
|
|
2571
3385
|
}
|
|
2572
3386
|
applyViewportPosition(card, step.position, design);
|
|
2573
3387
|
overlay.appendChild(card);
|
|
@@ -2585,84 +3399,218 @@ var CompositeRenderer = class extends BaseRenderer {
|
|
|
2585
3399
|
...block.id ? { metadata: { buttonId: block.id } } : {}
|
|
2586
3400
|
});
|
|
2587
3401
|
};
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
3402
|
+
const tag = (node, block, index) => {
|
|
3403
|
+
node.dataset.veoBlock = String(index);
|
|
3404
|
+
if (block.editorId) node.dataset.veoBlockId = block.editorId;
|
|
3405
|
+
return node;
|
|
3406
|
+
};
|
|
3407
|
+
const placeholder = (block, label) => {
|
|
3408
|
+
if (!context.isPreview || !block.editorId) return null;
|
|
3409
|
+
const el = doc.createElement("div");
|
|
3410
|
+
el.className = "veo-block-placeholder";
|
|
3411
|
+
el.textContent = label;
|
|
3412
|
+
return el;
|
|
3413
|
+
};
|
|
3414
|
+
const buildSequence = (container, cell) => {
|
|
3415
|
+
let i = 0;
|
|
3416
|
+
while (i < cell.length) {
|
|
3417
|
+
const { block: b, index } = cell[i];
|
|
3418
|
+
if (b.type === "button") {
|
|
3419
|
+
const row = doc.createElement("div");
|
|
3420
|
+
row.className = "veo-guide-actions";
|
|
3421
|
+
let rowAlign;
|
|
3422
|
+
while (i < cell.length && cell[i]?.block.type === "button") {
|
|
3423
|
+
const { block: bb, index: bi } = cell[i];
|
|
3424
|
+
if (!rowAlign) {
|
|
3425
|
+
const a = bb.style?.align;
|
|
3426
|
+
if (a === "left" || a === "center" || a === "right") rowAlign = a;
|
|
3427
|
+
}
|
|
3428
|
+
row.appendChild(tag(this.buildButton(bb, doc, context, idx, readers, emit), bb, bi));
|
|
3429
|
+
i++;
|
|
2604
3430
|
}
|
|
2605
|
-
row.
|
|
3431
|
+
if (rowAlign) row.style.justifyContent = ROW_ALIGN[rowAlign] ?? "flex-end";
|
|
3432
|
+
container.appendChild(row);
|
|
3433
|
+
continue;
|
|
3434
|
+
}
|
|
3435
|
+
if (b.type === "code") {
|
|
3436
|
+
const { node: node2, cleanup } = mountCodeBlock(b, doc, context, host);
|
|
3437
|
+
const el = node2 ?? placeholder(b, "Bloque de c\xF3digo");
|
|
3438
|
+
if (el) container.appendChild(tag(el, b, index));
|
|
3439
|
+
this.registerCleanup(cleanup);
|
|
2606
3440
|
i++;
|
|
3441
|
+
continue;
|
|
2607
3442
|
}
|
|
2608
|
-
if (
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
form.className = "veo-form";
|
|
2622
|
-
for (const field of b.fields ?? []) {
|
|
2623
|
-
const { wrapper, read } = buildField(field, doc);
|
|
2624
|
-
readers.push({ field, read });
|
|
2625
|
-
form.appendChild(wrapper);
|
|
3443
|
+
if (b.type === "form") {
|
|
3444
|
+
container.appendChild(tag(this.buildForm(b, doc, context, idx, readers, emit), b, index));
|
|
3445
|
+
i++;
|
|
3446
|
+
continue;
|
|
3447
|
+
}
|
|
3448
|
+
if (b.type === "checklist") {
|
|
3449
|
+
const el = buildChecklistBlock(b, doc, context);
|
|
3450
|
+
applyBlockStyle(el, b.style, "text");
|
|
3451
|
+
el.style.alignSelf = "stretch";
|
|
3452
|
+
el.style.width = "100%";
|
|
3453
|
+
container.appendChild(tag(el, b, index));
|
|
3454
|
+
i++;
|
|
3455
|
+
continue;
|
|
2626
3456
|
}
|
|
2627
|
-
|
|
3457
|
+
const node = buildContentBlock(b, doc, { editing: context.isPreview === true }) ?? placeholder(
|
|
3458
|
+
b,
|
|
3459
|
+
b.type === "image" ? "Imagen (sin URL)" : b.type === "video" ? "Video (elige proveedor y pega la URL)" : "Bloque vac\xEDo"
|
|
3460
|
+
);
|
|
3461
|
+
if (node) container.appendChild(tag(node, b, index));
|
|
2628
3462
|
i++;
|
|
3463
|
+
}
|
|
3464
|
+
};
|
|
3465
|
+
const rowStyles = context.guide.guideSteps[idx]?.rows;
|
|
3466
|
+
for (const row of groupLayout(blocks)) {
|
|
3467
|
+
const rowStyle = Number.isInteger(row.row) ? rowStyles?.[String(row.row)] : void 0;
|
|
3468
|
+
if (row.columns.length === 1 && !rowStyle) {
|
|
3469
|
+
buildSequence(card, row.columns[0].blocks);
|
|
2629
3470
|
continue;
|
|
2630
3471
|
}
|
|
2631
|
-
const
|
|
2632
|
-
|
|
2633
|
-
|
|
3472
|
+
const rowEl = doc.createElement("div");
|
|
3473
|
+
rowEl.className = "veo-row";
|
|
3474
|
+
if (Number.isInteger(row.row)) rowEl.dataset.veoRow = String(row.row);
|
|
3475
|
+
for (const column of row.columns) {
|
|
3476
|
+
const colEl = doc.createElement("div");
|
|
3477
|
+
colEl.className = "veo-col";
|
|
3478
|
+
buildSequence(colEl, column.blocks);
|
|
3479
|
+
rowEl.appendChild(colEl);
|
|
3480
|
+
}
|
|
3481
|
+
if (rowStyle) applyRowStyle(rowEl, rowStyle);
|
|
3482
|
+
card.appendChild(rowEl);
|
|
2634
3483
|
}
|
|
2635
3484
|
}
|
|
2636
3485
|
/** Botón con enrutado de acción (navegación / cierre / url / guía / envío de forms). */
|
|
2637
|
-
|
|
3486
|
+
/**
|
|
3487
|
+
* Bloque `form` (encuesta): pregunta (rich text del bloque) + campos + botón
|
|
3488
|
+
* de envío propio (`submit`). Con pregunta, el label del campo se omite (sería
|
|
3489
|
+
* la misma pregunta dos veces) y pasa a `aria-label` del input. El estilo del
|
|
3490
|
+
* bloque viste la pregunta (preset/alineación/color); los márgenes van al
|
|
3491
|
+
* contenedor del bloque entero.
|
|
3492
|
+
*/
|
|
3493
|
+
buildForm(b, doc, context, idx, readers, emit) {
|
|
3494
|
+
const form = doc.createElement("div");
|
|
3495
|
+
form.className = "veo-form";
|
|
3496
|
+
const style = b.style ?? {};
|
|
3497
|
+
const { marginTop, marginBottom, ...questionStyle } = style;
|
|
3498
|
+
for (const [side, v] of [
|
|
3499
|
+
["marginTop", marginTop],
|
|
3500
|
+
["marginBottom", marginBottom]
|
|
3501
|
+
]) {
|
|
3502
|
+
if (typeof v === "number" && Number.isFinite(v)) form.style[side] = `${v}px`;
|
|
3503
|
+
}
|
|
3504
|
+
let question = null;
|
|
3505
|
+
if (typeof b.html === "string" && b.html || typeof b.text === "string" && b.text) {
|
|
3506
|
+
question = doc.createElement("div");
|
|
3507
|
+
question.className = "veo-form-question";
|
|
3508
|
+
if (typeof b.html === "string" && b.html) question.appendChild(renderRichText(b.html, doc));
|
|
3509
|
+
else question.textContent = b.text;
|
|
3510
|
+
applyBlockStyle(question, questionStyle, "text");
|
|
3511
|
+
form.appendChild(question);
|
|
3512
|
+
}
|
|
3513
|
+
const questionText = question?.textContent?.trim() ?? "";
|
|
3514
|
+
const own = [];
|
|
3515
|
+
for (const field of b.fields ?? []) {
|
|
3516
|
+
const { wrapper, read } = buildField(field, doc);
|
|
3517
|
+
const st = b.style ?? {};
|
|
3518
|
+
if (typeof st.scaleColor === "string" && st.scaleColor) {
|
|
3519
|
+
wrapper.style.setProperty("--veo-scale-color", st.scaleColor);
|
|
3520
|
+
}
|
|
3521
|
+
if (typeof st.scaleSize === "number" && st.scaleSize >= 24 && st.scaleSize <= 96) {
|
|
3522
|
+
wrapper.style.setProperty("--veo-scale-size", `${st.scaleSize}px`);
|
|
3523
|
+
}
|
|
3524
|
+
if (question) {
|
|
3525
|
+
wrapper.querySelector(".veo-form-label")?.remove();
|
|
3526
|
+
if (questionText) {
|
|
3527
|
+
for (const inp of wrapper.querySelectorAll("input, textarea, select")) {
|
|
3528
|
+
inp.setAttribute("aria-label", questionText);
|
|
3529
|
+
}
|
|
3530
|
+
}
|
|
3531
|
+
}
|
|
3532
|
+
const reader = { field, read, blockId: b.id ?? null };
|
|
3533
|
+
readers.push(reader);
|
|
3534
|
+
own.push(reader);
|
|
3535
|
+
form.appendChild(wrapper);
|
|
3536
|
+
}
|
|
3537
|
+
if (b.submit) {
|
|
3538
|
+
const actions = doc.createElement("div");
|
|
3539
|
+
actions.className = "veo-guide-actions";
|
|
3540
|
+
const submit = { ...b.submit, type: "button" };
|
|
3541
|
+
const sa = submit.style?.align;
|
|
3542
|
+
if (sa === "left" || sa === "center" || sa === "right") {
|
|
3543
|
+
actions.style.justifyContent = ROW_ALIGN[sa] ?? "flex-end";
|
|
3544
|
+
}
|
|
3545
|
+
actions.appendChild(this.buildButton(submit, doc, context, idx, readers, emit, own));
|
|
3546
|
+
form.appendChild(actions);
|
|
3547
|
+
}
|
|
3548
|
+
return form;
|
|
3549
|
+
}
|
|
3550
|
+
buildButton(block, doc, context, idx, readers, emit, submitFirst) {
|
|
2638
3551
|
const btn = doc.createElement("button");
|
|
2639
3552
|
btn.type = "button";
|
|
2640
3553
|
btn.className = "veo-guide-cta";
|
|
2641
3554
|
btn.textContent = block.text?.trim() || "OK";
|
|
3555
|
+
const st = block.style ?? {};
|
|
3556
|
+
if (st.preset === "secondary" || st.preset === "link") {
|
|
3557
|
+
btn.classList.add(`veo-guide-cta--${st.preset}`);
|
|
3558
|
+
}
|
|
3559
|
+
if (typeof st.ariaLabel === "string" && st.ariaLabel.trim()) {
|
|
3560
|
+
btn.setAttribute("aria-label", st.ariaLabel.trim());
|
|
3561
|
+
}
|
|
2642
3562
|
applyBlockStyle(btn, block.style, "button");
|
|
2643
3563
|
const nav = context.nav?.callbacks;
|
|
2644
|
-
|
|
2645
|
-
switch (
|
|
3564
|
+
const runSimple = (spec) => {
|
|
3565
|
+
switch (spec.action) {
|
|
2646
3566
|
case "next":
|
|
2647
|
-
emit(block);
|
|
2648
3567
|
if (nav) nav.onNext();
|
|
2649
3568
|
else context.onClose();
|
|
2650
3569
|
break;
|
|
2651
3570
|
case "prev":
|
|
2652
|
-
emit(block);
|
|
2653
3571
|
nav?.onBack();
|
|
2654
3572
|
break;
|
|
3573
|
+
case "goto":
|
|
3574
|
+
if (nav?.onGoto && typeof spec.targetStep === "number") nav.onGoto(spec.targetStep);
|
|
3575
|
+
else context.onClose();
|
|
3576
|
+
break;
|
|
3577
|
+
case "snooze":
|
|
3578
|
+
snoozeGuide(
|
|
3579
|
+
context.guide.guideId,
|
|
3580
|
+
typeof spec.snoozeEvery === "number" ? spec.snoozeEvery : 1,
|
|
3581
|
+
spec.snoozeUnit ?? "day"
|
|
3582
|
+
);
|
|
3583
|
+
context.onClose();
|
|
3584
|
+
break;
|
|
2655
3585
|
case "url":
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
window.open(
|
|
3586
|
+
if (typeof spec.url === "string" && isSafeUrl(spec.url)) {
|
|
3587
|
+
if (spec.newTab === false) window.location.assign(spec.url);
|
|
3588
|
+
else window.open(spec.url, "_blank", "noopener,noreferrer");
|
|
2659
3589
|
}
|
|
2660
3590
|
break;
|
|
2661
3591
|
case "guide":
|
|
2662
|
-
|
|
2663
|
-
if (block.targetGuideId) context.onOpenGuide?.(block.targetGuideId);
|
|
3592
|
+
if (spec.targetGuideId) context.onOpenGuide?.(spec.targetGuideId);
|
|
2664
3593
|
this.closeOrSkip(context);
|
|
2665
3594
|
break;
|
|
3595
|
+
default:
|
|
3596
|
+
this.closeOrSkip(context);
|
|
3597
|
+
}
|
|
3598
|
+
};
|
|
3599
|
+
const runSecond = () => {
|
|
3600
|
+
if (block.secondAction?.action) runSimple(block.secondAction);
|
|
3601
|
+
};
|
|
3602
|
+
const runActions = () => {
|
|
3603
|
+
switch (block.action) {
|
|
3604
|
+
case "next":
|
|
3605
|
+
case "prev":
|
|
3606
|
+
case "goto":
|
|
3607
|
+
case "snooze":
|
|
3608
|
+
case "url":
|
|
3609
|
+
case "guide":
|
|
3610
|
+
emit(block);
|
|
3611
|
+
runSimple(block);
|
|
3612
|
+
runSecond();
|
|
3613
|
+
break;
|
|
2666
3614
|
case "submit-forms":
|
|
2667
3615
|
case "submit-forms-advance":
|
|
2668
3616
|
case "submit-forms-goto":
|
|
@@ -2680,7 +3628,12 @@ var CompositeRenderer = class extends BaseRenderer {
|
|
|
2680
3628
|
default:
|
|
2681
3629
|
emit(block);
|
|
2682
3630
|
this.closeOrSkip(context);
|
|
3631
|
+
runSecond();
|
|
2683
3632
|
}
|
|
3633
|
+
};
|
|
3634
|
+
btn.addEventListener("click", () => {
|
|
3635
|
+
if (submitFirst) this.submitForms(submitFirst, context, idx, btn, runActions);
|
|
3636
|
+
else runActions();
|
|
2684
3637
|
});
|
|
2685
3638
|
return btn;
|
|
2686
3639
|
}
|
|
@@ -2699,17 +3652,27 @@ var CompositeRenderer = class extends BaseRenderer {
|
|
|
2699
3652
|
}
|
|
2700
3653
|
answers[field.key] = value;
|
|
2701
3654
|
}
|
|
2702
|
-
if (
|
|
3655
|
+
if (Object.keys(answers).length === 0) {
|
|
3656
|
+
onDone();
|
|
3657
|
+
return;
|
|
3658
|
+
}
|
|
2703
3659
|
btn.disabled = true;
|
|
2704
3660
|
const submitFn = context.onFormSubmit ?? (() => Promise.resolve(true));
|
|
2705
|
-
|
|
3661
|
+
const blockIds = new Set(
|
|
3662
|
+
readers.filter((r) => answers[r.field.key] !== void 0).map((r) => r.blockId ?? "")
|
|
3663
|
+
);
|
|
3664
|
+
const pollId = blockIds.size === 1 ? [...blockIds][0] || void 0 : void 0;
|
|
3665
|
+
void submitFn(answers, { ...pollId ? { pollId } : {}, stepIndex: idx }).then((ok) => {
|
|
2706
3666
|
btn.disabled = false;
|
|
2707
3667
|
if (!ok) return;
|
|
2708
|
-
context.
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
3668
|
+
const lastStep = !context.nav || idx >= context.nav.totalSteps - 1;
|
|
3669
|
+
if (lastStep) {
|
|
3670
|
+
context.onInteraction({
|
|
3671
|
+
guideId: context.guide.guideId,
|
|
3672
|
+
stepIndex: idx,
|
|
3673
|
+
action: "completed"
|
|
3674
|
+
});
|
|
3675
|
+
}
|
|
2713
3676
|
onDone();
|
|
2714
3677
|
});
|
|
2715
3678
|
}
|
|
@@ -2823,21 +3786,38 @@ function inlinePosOf(p) {
|
|
|
2823
3786
|
function applyViewportPosition(card, position, design) {
|
|
2824
3787
|
const s = card.style;
|
|
2825
3788
|
s.position = "fixed";
|
|
2826
|
-
const
|
|
3789
|
+
const BASE = 16;
|
|
3790
|
+
const m = (k) => typeof design[k] === "number" && Number.isFinite(design[k]) ? design[k] : 0;
|
|
3791
|
+
s.margin = "0";
|
|
2827
3792
|
const hAlign = design.hAlign === "left" ? "left" : design.hAlign === "right" ? "right" : null;
|
|
2828
3793
|
const v = position.includes("top") ? "top" : position.includes("bottom") ? "bottom" : "middle";
|
|
2829
3794
|
const h = hAlign ?? (position.includes("left") ? "left" : position.includes("right") ? "right" : "middle");
|
|
2830
|
-
if (v === "top") s.top =
|
|
2831
|
-
else if (v === "bottom") s.bottom =
|
|
3795
|
+
if (v === "top") s.top = `${BASE + m("marginTop")}px`;
|
|
3796
|
+
else if (v === "bottom") s.bottom = `${BASE + m("marginBottom")}px`;
|
|
2832
3797
|
else s.top = "50%";
|
|
2833
|
-
if (h === "left") s.left =
|
|
2834
|
-
else if (h === "right") s.right =
|
|
3798
|
+
if (h === "left") s.left = `${BASE + m("marginLeft")}px`;
|
|
3799
|
+
else if (h === "right") s.right = `${BASE + m("marginRight")}px`;
|
|
2835
3800
|
else s.left = "50%";
|
|
2836
|
-
const
|
|
2837
|
-
const
|
|
3801
|
+
const dx = m("marginLeft") - m("marginRight");
|
|
3802
|
+
const dy = m("marginTop") - m("marginBottom");
|
|
3803
|
+
const tx = h === "middle" ? `calc(-50% + ${dx}px)` : "0";
|
|
3804
|
+
const ty = v === "middle" ? `calc(-50% + ${dy}px)` : "0";
|
|
2838
3805
|
s.transform = tx === "0" && ty === "0" ? "" : `translate(${tx}, ${ty})`;
|
|
2839
3806
|
}
|
|
2840
3807
|
var CLOSE_INSET = "10px";
|
|
3808
|
+
var CLOSE_RESERVE_PX = 42;
|
|
3809
|
+
var CARD_DEFAULT_PAD_PX = 20;
|
|
3810
|
+
function reserveCloseSpace(card, blocks, position, design) {
|
|
3811
|
+
if (blocks.length === 0) return;
|
|
3812
|
+
const side = position.includes("bottom") ? "Bottom" : "Top";
|
|
3813
|
+
const edgeBlock = side === "Top" ? blocks[0] : blocks[blocks.length - 1];
|
|
3814
|
+
if (edgeBlock?.type === "image") return;
|
|
3815
|
+
const margin = design[`closeMargin${side}`];
|
|
3816
|
+
const need = CLOSE_RESERVE_PX + (typeof margin === "number" ? margin : 0);
|
|
3817
|
+
const current = design[`padding${side}`];
|
|
3818
|
+
const have = typeof current === "number" ? current : CARD_DEFAULT_PAD_PX;
|
|
3819
|
+
if (have < need) card.style.setProperty(`padding-${side.toLowerCase()}`, `${need}px`);
|
|
3820
|
+
}
|
|
2841
3821
|
function applyClosePosition(btn, position) {
|
|
2842
3822
|
const s = btn.style;
|
|
2843
3823
|
s.top = "auto";
|
|
@@ -2902,10 +3882,17 @@ async function anchorPosition(anchor, wrapper, caret, preferred = "bottom") {
|
|
|
2902
3882
|
window.removeEventListener("resize", reposition);
|
|
2903
3883
|
};
|
|
2904
3884
|
}
|
|
3885
|
+
var MIN_CARD_WIDTH = 120;
|
|
3886
|
+
var NARROW_CARD_WIDTH = 260;
|
|
2905
3887
|
function applyCompositeDesign(card, design) {
|
|
2906
3888
|
const s = card.style;
|
|
2907
3889
|
const px = (v) => typeof v === "number" && Number.isFinite(v) ? `${v}px` : null;
|
|
2908
3890
|
const strv = (v) => typeof v === "string" && v ? v : null;
|
|
3891
|
+
if (design.colorMode === "dark") {
|
|
3892
|
+
s.background = "#1f1d1b";
|
|
3893
|
+
s.color = "#f5f0eb";
|
|
3894
|
+
card.classList.add("veo-card--dark");
|
|
3895
|
+
}
|
|
2909
3896
|
if (strv(design.background)) s.background = design.background;
|
|
2910
3897
|
const textColor = strv(design.color) ?? strv(design.textColor);
|
|
2911
3898
|
if (textColor) s.color = textColor;
|
|
@@ -2927,13 +3914,14 @@ function applyCompositeDesign(card, design) {
|
|
|
2927
3914
|
const n = (k) => typeof design[k] === "number" ? design[k] : 0;
|
|
2928
3915
|
s.boxShadow = `${n("shadowX")}px ${n("shadowY")}px ${n("shadowBlur")}px ${n("shadowSpread")}px ${strv(design.shadowColor) ?? "rgba(0,0,0,0.2)"}`;
|
|
2929
3916
|
}
|
|
3917
|
+
const width = typeof design.width === "number" && Number.isFinite(design.width) ? `${Math.max(MIN_CARD_WIDTH, design.width)}px` : null;
|
|
2930
3918
|
if (design.fullWidth === true) {
|
|
2931
3919
|
s.width = "100%";
|
|
2932
3920
|
} else if (design.widthType === "fluid") {
|
|
2933
|
-
s.width = "
|
|
2934
|
-
if (
|
|
2935
|
-
} else if (
|
|
2936
|
-
s.width =
|
|
3921
|
+
s.width = "100%";
|
|
3922
|
+
if (width) s.maxWidth = width;
|
|
3923
|
+
} else if (width) {
|
|
3924
|
+
s.width = width;
|
|
2937
3925
|
}
|
|
2938
3926
|
for (const side of ["Top", "Right", "Bottom", "Left"]) {
|
|
2939
3927
|
const p = px(design[`padding${side}`]);
|
|
@@ -3037,6 +4025,7 @@ var FormRenderer = class extends BaseRenderer {
|
|
|
3037
4025
|
mountFormContent(card, context, doc);
|
|
3038
4026
|
overlay.appendChild(card);
|
|
3039
4027
|
root.appendChild(overlay);
|
|
4028
|
+
this.registerCleanup(lockScroll(doc));
|
|
3040
4029
|
const dismiss = () => {
|
|
3041
4030
|
context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "dismissed" });
|
|
3042
4031
|
context.onClose();
|
|
@@ -3164,6 +4153,18 @@ var ModalRenderer = class extends BaseRenderer {
|
|
|
3164
4153
|
constructor() {
|
|
3165
4154
|
super(...arguments);
|
|
3166
4155
|
this.overlay = null;
|
|
4156
|
+
/** Suelta el bloqueo de scroll del backdrop (null = sin backdrop). */
|
|
4157
|
+
this.releaseScroll = null;
|
|
4158
|
+
}
|
|
4159
|
+
/** Con backdrop la app de atrás queda congelada; sin él, scrollea normal. */
|
|
4160
|
+
syncScrollLock(step) {
|
|
4161
|
+
const wantLock = step.style?.backdrop !== false;
|
|
4162
|
+
if (wantLock && !this.releaseScroll) {
|
|
4163
|
+
this.releaseScroll = lockScroll(this.host?.ownerDocument ?? document);
|
|
4164
|
+
} else if (!wantLock && this.releaseScroll) {
|
|
4165
|
+
this.releaseScroll();
|
|
4166
|
+
this.releaseScroll = null;
|
|
4167
|
+
}
|
|
3167
4168
|
}
|
|
3168
4169
|
render(context) {
|
|
3169
4170
|
const idx = context.nav?.stepIndex ?? 0;
|
|
@@ -3175,6 +4176,7 @@ var ModalRenderer = class extends BaseRenderer {
|
|
|
3175
4176
|
const overlay = ownerDocument.createElement("div");
|
|
3176
4177
|
overlay.className = overlayClassOf(step);
|
|
3177
4178
|
this.overlay = overlay;
|
|
4179
|
+
this.syncScrollLock(step);
|
|
3178
4180
|
const card = ownerDocument.createElement("div");
|
|
3179
4181
|
card.className = "veo-modal-card";
|
|
3180
4182
|
const dismiss = (action, ctaUrl, meta) => {
|
|
@@ -3232,9 +4234,12 @@ var ModalRenderer = class extends BaseRenderer {
|
|
|
3232
4234
|
}
|
|
3233
4235
|
onLiveUpdate(step) {
|
|
3234
4236
|
if (this.overlay) this.overlay.className = overlayClassOf(step);
|
|
4237
|
+
this.syncScrollLock(step);
|
|
3235
4238
|
}
|
|
3236
4239
|
destroy() {
|
|
3237
4240
|
this.overlay = null;
|
|
4241
|
+
this.releaseScroll?.();
|
|
4242
|
+
this.releaseScroll = null;
|
|
3238
4243
|
super.destroy();
|
|
3239
4244
|
}
|
|
3240
4245
|
};
|
|
@@ -3448,7 +4453,9 @@ var GuidePreviewController = class {
|
|
|
3448
4453
|
if (event.action === "shown") shown = true;
|
|
3449
4454
|
},
|
|
3450
4455
|
onClose: () => this.close(true),
|
|
3451
|
-
|
|
4456
|
+
// Sin indicación explícita, el badge se abre solo si la guía arranca
|
|
4457
|
+
// sola (como en el runtime); `manual` lo deja cerrado.
|
|
4458
|
+
badgeOpen: this.input?.badgeOpen ?? isAutomaticActivation(this.input?.activationRules),
|
|
3452
4459
|
onTrack: () => {
|
|
3453
4460
|
},
|
|
3454
4461
|
// Encadenado en preview: no-op — el editor no puede montar otra guía
|
|
@@ -3458,6 +4465,7 @@ var GuidePreviewController = class {
|
|
|
3458
4465
|
// Preview de forms: por defecto simula el envío SIN escribir props;
|
|
3459
4466
|
// el caller puede pasar `formSubmit` real (link de preview).
|
|
3460
4467
|
onFormSubmit: this.input?.formSubmit ?? (() => Promise.resolve(true)),
|
|
4468
|
+
...this.input?.fetchProgress ? { fetchProgress: this.input.fetchProgress } : {},
|
|
3461
4469
|
isPreview: true,
|
|
3462
4470
|
...this.input?.formSubmit && this.input.formSubmitNote !== void 0 ? { previewNote: this.input.formSubmitNote } : {}
|
|
3463
4471
|
});
|
|
@@ -3643,9 +4651,35 @@ function matchesUrl(matcher, pageUrl) {
|
|
|
3643
4651
|
}
|
|
3644
4652
|
}
|
|
3645
4653
|
function stepMatchesPage(loc, pageUrl) {
|
|
3646
|
-
if (!loc || loc.scope === "sitewide"
|
|
4654
|
+
if (!loc || loc.scope === "sitewide") return true;
|
|
4655
|
+
if (loc.scope === "pages") {
|
|
4656
|
+
const rules = loc.rules ?? [];
|
|
4657
|
+
return rules.some((r) => pageRuleMatches(r, pageUrl));
|
|
4658
|
+
}
|
|
4659
|
+
if (!loc.pattern) return true;
|
|
3647
4660
|
return matchesUrl({ type: loc.matchType ?? "prefix", pattern: loc.pattern }, pageUrl);
|
|
3648
4661
|
}
|
|
4662
|
+
function pageRuleMatches(rule, pageUrl) {
|
|
4663
|
+
const pattern = rule.pattern.toLowerCase();
|
|
4664
|
+
const candidates = urlCandidates(pageUrl).map((c) => c.toLowerCase());
|
|
4665
|
+
switch (rule.type) {
|
|
4666
|
+
case "exact":
|
|
4667
|
+
return candidates.some((c) => c === pattern);
|
|
4668
|
+
case "prefix":
|
|
4669
|
+
return candidates.some((c) => c.startsWith(pattern));
|
|
4670
|
+
case "contains":
|
|
4671
|
+
return candidates.some((c) => c.includes(pattern));
|
|
4672
|
+
case "regex":
|
|
4673
|
+
try {
|
|
4674
|
+
const re = new RegExp(rule.pattern, "i");
|
|
4675
|
+
return candidates.some((c) => re.test(c));
|
|
4676
|
+
} catch {
|
|
4677
|
+
return false;
|
|
4678
|
+
}
|
|
4679
|
+
default:
|
|
4680
|
+
return false;
|
|
4681
|
+
}
|
|
4682
|
+
}
|
|
3649
4683
|
|
|
3650
4684
|
// src/plugins/autocapture/constants.ts
|
|
3651
4685
|
var DEFAULT_AUTOCAPTURE_CONFIG = {
|
|
@@ -3780,6 +4814,6 @@ function buildSelectorPath(element, maxAncestors = 5) {
|
|
|
3780
4814
|
return parts.join(" > ");
|
|
3781
4815
|
}
|
|
3782
4816
|
|
|
3783
|
-
export { ALWAYS_BLOCK_SELECTORS, BadgeRenderer, BannerRenderer, CompositeRenderer, CustomRenderer, DEFAULT_AUTOCAPTURE_CONFIG, DEFAULT_TRACKER_BATCH_SIZE, DEFAULT_TRACKER_FLUSH_INTERVAL_MS, DEFAULT_TRACKER_MAX_RETRIES, FREQUENCY_CACHE_KEY_PREFIX, FREQUENCY_CACHE_MAX_ENTRIES, FREQUENCY_CACHE_TTL_MS, FormRenderer, InlineCustomRenderer, InlineFormRenderer, InlineRenderer, MAX_ANCESTORS, ModalRenderer, PRIORITY_ATTRIBUTES, SENSITIVE_ATTRIBUTES, TooltipRenderer, WALKTHROUGH_ABANDONMENT_TIMEOUT_MS, WALKTHROUGH_STATE_KEY_PREFIX, WalkthroughRenderer, buildSelectorPath, clearBuilderToken, clearPreviewToken, closeGuidePreview, filterHumanClasses, getPreviewToken, hasDocument, hasLocalStorage, hasNavigatorSendBeacon, hasWindow, isBuilderMode, isDeviceAllowed, previewGuide, resolveBuilderContext, resolveBuilderToken, stepMatchesPage, uuidv7 };
|
|
3784
|
-
//# sourceMappingURL=chunk-
|
|
3785
|
-
//# sourceMappingURL=chunk-
|
|
4817
|
+
export { ALWAYS_BLOCK_SELECTORS, BadgeRenderer, BannerRenderer, CompositeRenderer, CustomRenderer, DEFAULT_AUTOCAPTURE_CONFIG, DEFAULT_TRACKER_BATCH_SIZE, DEFAULT_TRACKER_FLUSH_INTERVAL_MS, DEFAULT_TRACKER_MAX_RETRIES, FREQUENCY_CACHE_KEY_PREFIX, FREQUENCY_CACHE_MAX_ENTRIES, FREQUENCY_CACHE_TTL_MS, FormRenderer, GUIDE_STYLES, InlineCustomRenderer, InlineFormRenderer, InlineRenderer, MAX_ANCESTORS, ModalRenderer, PRIORITY_ATTRIBUTES, RAW_BLOCK_ATTR, SENSITIVE_ATTRIBUTES, TooltipRenderer, WALKTHROUGH_ABANDONMENT_TIMEOUT_MS, WALKTHROUGH_STATE_KEY_PREFIX, WalkthroughRenderer, activationDelayMs, applyBlockStyle, buildContentBlock, buildSelectorPath, clearBuilderToken, clearPreviewToken, closeGuidePreview, filterHumanClasses, getPreviewToken, groupLayout, hasDocument, hasLocalStorage, hasNavigatorSendBeacon, hasWindow, isAutomaticActivation, isBuilderMode, isDeviceAllowed, isSnoozed, previewGuide, resolveBuilderContext, resolveBuilderToken, stepMatchesPage, uuidv7 };
|
|
4818
|
+
//# sourceMappingURL=chunk-O6USA4TU.mjs.map
|
|
4819
|
+
//# sourceMappingURL=chunk-O6USA4TU.mjs.map
|