veo-sdk 0.3.16 → 0.4.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-ZDNVF6KG.mjs +5 -0
- package/dist/{builder-EQYOMQFY.mjs.map → builder-ZDNVF6KG.mjs.map} +1 -1
- package/dist/builder.cjs +1008 -215
- package/dist/builder.cjs.map +1 -1
- package/dist/builder.d.cts +38 -2
- package/dist/builder.d.ts +38 -2
- package/dist/builder.mjs +4 -4
- package/dist/{chunk-QCIT7CGE.mjs → chunk-I55Z7UGJ.mjs} +269 -5
- package/dist/chunk-I55Z7UGJ.mjs.map +1 -0
- package/dist/{chunk-YHUPUTWH.mjs → chunk-ICKEQ7VC.mjs} +713 -184
- package/dist/chunk-ICKEQ7VC.mjs.map +1 -0
- package/dist/{chunk-IEHGG5NL.mjs → chunk-MA2BXLZ7.mjs} +67 -11
- package/dist/chunk-MA2BXLZ7.mjs.map +1 -0
- package/dist/{guide-preview-DewCTa-B.d.cts → guide-preview-D_xhRVLe.d.cts} +20 -2
- package/dist/{guide-preview-DewCTa-B.d.ts → guide-preview-D_xhRVLe.d.ts} +20 -2
- package/dist/index.cjs +1097 -216
- 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 +7 -6
- package/dist/index.mjs.map +1 -1
- package/dist/veo-builder.js +125 -9
- package/dist/veo-builder.js.map +1 -1
- package/dist/veo-guides.js +86 -4
- package/dist/veo-guides.js.map +1 -1
- package/dist/veo.js +1 -1
- package/dist/veo.js.map +1 -1
- package/package.json +1 -1
- package/dist/builder-EQYOMQFY.mjs +0 -5
- package/dist/chunk-IEHGG5NL.mjs.map +0 -1
- package/dist/chunk-QCIT7CGE.mjs.map +0 -1
- package/dist/chunk-YHUPUTWH.mjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -374,6 +374,118 @@ var init_constants2 = __esm({
|
|
|
374
374
|
}
|
|
375
375
|
});
|
|
376
376
|
|
|
377
|
+
// src/plugins/guides/rich-text.ts
|
|
378
|
+
function renderRichText(html, doc) {
|
|
379
|
+
const fragment = doc.createDocumentFragment();
|
|
380
|
+
if (typeof html !== "string" || html.length === 0) return fragment;
|
|
381
|
+
let parsed;
|
|
382
|
+
try {
|
|
383
|
+
parsed = new DOMParser().parseFromString(
|
|
384
|
+
html.length > MAX_INPUT_LEN ? html.slice(0, MAX_INPUT_LEN) : html,
|
|
385
|
+
"text/html"
|
|
386
|
+
);
|
|
387
|
+
} catch {
|
|
388
|
+
fragment.appendChild(doc.createTextNode(html));
|
|
389
|
+
return fragment;
|
|
390
|
+
}
|
|
391
|
+
const budget = { nodes: 0, exceeded: false };
|
|
392
|
+
for (const child of Array.from(parsed.body.childNodes)) {
|
|
393
|
+
const rebuilt = rebuildNode(child, doc, 0, budget);
|
|
394
|
+
if (rebuilt) fragment.appendChild(rebuilt);
|
|
395
|
+
}
|
|
396
|
+
if (budget.exceeded) {
|
|
397
|
+
const plain = doc.createDocumentFragment();
|
|
398
|
+
plain.appendChild(doc.createTextNode(parsed.body.textContent ?? ""));
|
|
399
|
+
return plain;
|
|
400
|
+
}
|
|
401
|
+
return fragment;
|
|
402
|
+
}
|
|
403
|
+
function rebuildNode(node, doc, depth, budget) {
|
|
404
|
+
if (budget.exceeded) return null;
|
|
405
|
+
if (++budget.nodes > MAX_NODES || depth > MAX_DEPTH) {
|
|
406
|
+
budget.exceeded = true;
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
410
|
+
return doc.createTextNode(node.textContent ?? "");
|
|
411
|
+
}
|
|
412
|
+
if (node.nodeType !== Node.ELEMENT_NODE) return null;
|
|
413
|
+
const el = node;
|
|
414
|
+
if (DROP_TAGS.has(el.tagName)) return null;
|
|
415
|
+
const mapped = ALLOWED_TAGS[el.tagName];
|
|
416
|
+
if (!mapped) {
|
|
417
|
+
const frag = doc.createDocumentFragment();
|
|
418
|
+
for (const child of Array.from(el.childNodes)) {
|
|
419
|
+
const rebuilt2 = rebuildNode(child, doc, depth + 1, budget);
|
|
420
|
+
if (rebuilt2) frag.appendChild(rebuilt2);
|
|
421
|
+
}
|
|
422
|
+
return frag;
|
|
423
|
+
}
|
|
424
|
+
if (mapped === "a") {
|
|
425
|
+
const href = el.getAttribute("href") ?? "";
|
|
426
|
+
if (!isSafeUrl(href)) {
|
|
427
|
+
const frag = doc.createDocumentFragment();
|
|
428
|
+
for (const child of Array.from(el.childNodes)) {
|
|
429
|
+
const rebuilt2 = rebuildNode(child, doc, depth + 1, budget);
|
|
430
|
+
if (rebuilt2) frag.appendChild(rebuilt2);
|
|
431
|
+
}
|
|
432
|
+
return frag;
|
|
433
|
+
}
|
|
434
|
+
const a = doc.createElement("a");
|
|
435
|
+
a.setAttribute("href", href);
|
|
436
|
+
a.setAttribute("target", "_blank");
|
|
437
|
+
a.setAttribute("rel", "noopener noreferrer nofollow");
|
|
438
|
+
for (const child of Array.from(el.childNodes)) {
|
|
439
|
+
const rebuilt2 = rebuildNode(child, doc, depth + 1, budget);
|
|
440
|
+
if (rebuilt2) a.appendChild(rebuilt2);
|
|
441
|
+
}
|
|
442
|
+
return a;
|
|
443
|
+
}
|
|
444
|
+
const rebuilt = doc.createElement(mapped);
|
|
445
|
+
for (const child of Array.from(el.childNodes)) {
|
|
446
|
+
const childNode = rebuildNode(child, doc, depth + 1, budget);
|
|
447
|
+
if (childNode) rebuilt.appendChild(childNode);
|
|
448
|
+
}
|
|
449
|
+
return rebuilt;
|
|
450
|
+
}
|
|
451
|
+
var ALLOWED_TAGS, DROP_TAGS, MAX_INPUT_LEN, MAX_DEPTH, MAX_NODES;
|
|
452
|
+
var init_rich_text = __esm({
|
|
453
|
+
"src/plugins/guides/rich-text.ts"() {
|
|
454
|
+
init_block_builder();
|
|
455
|
+
ALLOWED_TAGS = {
|
|
456
|
+
P: "p",
|
|
457
|
+
BR: "br",
|
|
458
|
+
STRONG: "strong",
|
|
459
|
+
B: "strong",
|
|
460
|
+
EM: "em",
|
|
461
|
+
I: "em",
|
|
462
|
+
U: "u",
|
|
463
|
+
S: "s",
|
|
464
|
+
UL: "ul",
|
|
465
|
+
OL: "ol",
|
|
466
|
+
LI: "li",
|
|
467
|
+
A: "a"
|
|
468
|
+
};
|
|
469
|
+
DROP_TAGS = /* @__PURE__ */ new Set([
|
|
470
|
+
"SCRIPT",
|
|
471
|
+
"STYLE",
|
|
472
|
+
"TEMPLATE",
|
|
473
|
+
"IFRAME",
|
|
474
|
+
"OBJECT",
|
|
475
|
+
"EMBED",
|
|
476
|
+
"NOSCRIPT",
|
|
477
|
+
"TITLE",
|
|
478
|
+
"TEXTAREA",
|
|
479
|
+
"SELECT",
|
|
480
|
+
"SVG",
|
|
481
|
+
"MATH"
|
|
482
|
+
]);
|
|
483
|
+
MAX_INPUT_LEN = 16 * 1024;
|
|
484
|
+
MAX_DEPTH = 20;
|
|
485
|
+
MAX_NODES = 1e3;
|
|
486
|
+
}
|
|
487
|
+
});
|
|
488
|
+
|
|
377
489
|
// src/plugins/guides/block-builder.ts
|
|
378
490
|
function buildStepContent(step, doc, callbacks) {
|
|
379
491
|
const container = doc.createElement("div");
|
|
@@ -468,9 +580,13 @@ function buildContentBlock(block, doc) {
|
|
|
468
580
|
return el;
|
|
469
581
|
}
|
|
470
582
|
case "text": {
|
|
471
|
-
const el = doc.createElement("
|
|
583
|
+
const el = doc.createElement("div");
|
|
472
584
|
el.className = "veo-guide-text";
|
|
473
|
-
|
|
585
|
+
if (typeof block.html === "string" && block.html) {
|
|
586
|
+
el.appendChild(renderRichText(block.html, doc));
|
|
587
|
+
} else {
|
|
588
|
+
el.textContent = typeof block.text === "string" ? block.text : "";
|
|
589
|
+
}
|
|
474
590
|
applyBlockStyle(el, block.style, "text");
|
|
475
591
|
return el;
|
|
476
592
|
}
|
|
@@ -496,7 +612,11 @@ function buildButtonBlock(block, doc, callbacks) {
|
|
|
496
612
|
btn.addEventListener("click", () => {
|
|
497
613
|
const action = block.action ?? "dismiss";
|
|
498
614
|
const url = action === "url" && typeof block.url === "string" && isSafeUrl(block.url) ? block.url : void 0;
|
|
499
|
-
|
|
615
|
+
const meta = {
|
|
616
|
+
...typeof block.id === "string" && block.id ? { buttonId: block.id } : {},
|
|
617
|
+
...typeof block.text === "string" && block.text ? { buttonText: block.text } : {}
|
|
618
|
+
};
|
|
619
|
+
callbacks.onCtaClick(action, url, Object.keys(meta).length ? meta : void 0);
|
|
500
620
|
});
|
|
501
621
|
return btn;
|
|
502
622
|
}
|
|
@@ -506,6 +626,10 @@ function clampNum(n, min, max) {
|
|
|
506
626
|
function applyBlockStyle(el, style, kind) {
|
|
507
627
|
if (!style || typeof style !== "object") return;
|
|
508
628
|
const s = style;
|
|
629
|
+
if (kind === "image" && s.bleed === true) {
|
|
630
|
+
el.classList.add("veo-guide-image--bleed");
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
509
633
|
const align = typeof s.align === "string" ? s.align : null;
|
|
510
634
|
if (align && (align === "left" || align === "center" || align === "right")) {
|
|
511
635
|
if (kind === "text") el.style.textAlign = align;
|
|
@@ -556,6 +680,7 @@ var BLOCK_ALIGN_SELF;
|
|
|
556
680
|
var init_block_builder = __esm({
|
|
557
681
|
"src/plugins/guides/block-builder.ts"() {
|
|
558
682
|
init_constants2();
|
|
683
|
+
init_rich_text();
|
|
559
684
|
BLOCK_ALIGN_SELF = {
|
|
560
685
|
left: "flex-start",
|
|
561
686
|
center: "center",
|
|
@@ -564,83 +689,6 @@ var init_block_builder = __esm({
|
|
|
564
689
|
}
|
|
565
690
|
});
|
|
566
691
|
|
|
567
|
-
// src/plugins/guides/walkthrough-block-builder.ts
|
|
568
|
-
function buildWalkthroughStepContent(step, stepIndex, totalSteps, doc, callbacks) {
|
|
569
|
-
const container = doc.createElement("div");
|
|
570
|
-
container.className = "veo-guide-content";
|
|
571
|
-
const counter = doc.createElement("div");
|
|
572
|
-
counter.className = "veo-walkthrough-counter";
|
|
573
|
-
counter.textContent = `Paso ${stepIndex + 1} de ${totalSteps}`;
|
|
574
|
-
container.appendChild(counter);
|
|
575
|
-
const progress = doc.createElement("div");
|
|
576
|
-
progress.className = "veo-walkthrough-progress";
|
|
577
|
-
for (let i = 0; i < totalSteps; i++) {
|
|
578
|
-
const dot = doc.createElement("span");
|
|
579
|
-
dot.className = "veo-walkthrough-progress-dot";
|
|
580
|
-
if (i < stepIndex) dot.classList.add("completed");
|
|
581
|
-
if (i === stepIndex) dot.classList.add("active");
|
|
582
|
-
progress.appendChild(dot);
|
|
583
|
-
}
|
|
584
|
-
container.appendChild(progress);
|
|
585
|
-
if (typeof step.imageUrl === "string" && step.imageUrl && isSafeUrl(step.imageUrl)) {
|
|
586
|
-
const img = doc.createElement("img");
|
|
587
|
-
img.className = "veo-guide-image";
|
|
588
|
-
img.src = step.imageUrl;
|
|
589
|
-
img.alt = typeof step.title === "string" ? step.title : "";
|
|
590
|
-
container.appendChild(img);
|
|
591
|
-
}
|
|
592
|
-
if (typeof step.title === "string" && step.title) {
|
|
593
|
-
const heading = doc.createElement("h2");
|
|
594
|
-
heading.className = "veo-guide-title";
|
|
595
|
-
heading.textContent = step.title;
|
|
596
|
-
container.appendChild(heading);
|
|
597
|
-
}
|
|
598
|
-
if (typeof step.content === "string" && step.content) {
|
|
599
|
-
const paragraph = doc.createElement("p");
|
|
600
|
-
paragraph.className = "veo-guide-text";
|
|
601
|
-
paragraph.textContent = step.content;
|
|
602
|
-
container.appendChild(paragraph);
|
|
603
|
-
}
|
|
604
|
-
const actions = doc.createElement("div");
|
|
605
|
-
actions.className = "veo-walkthrough-actions";
|
|
606
|
-
const skipBtn = doc.createElement("button");
|
|
607
|
-
skipBtn.type = "button";
|
|
608
|
-
skipBtn.className = "veo-walkthrough-skip";
|
|
609
|
-
skipBtn.textContent = "Omitir";
|
|
610
|
-
skipBtn.addEventListener("click", () => callbacks.onSkip());
|
|
611
|
-
actions.appendChild(skipBtn);
|
|
612
|
-
const rightGroup = doc.createElement("div");
|
|
613
|
-
rightGroup.className = "veo-walkthrough-actions-right";
|
|
614
|
-
if (stepIndex > 0) {
|
|
615
|
-
const backBtn = doc.createElement("button");
|
|
616
|
-
backBtn.type = "button";
|
|
617
|
-
backBtn.className = "veo-walkthrough-btn-secondary";
|
|
618
|
-
backBtn.textContent = "Atr\xE1s";
|
|
619
|
-
backBtn.addEventListener("click", () => callbacks.onBack());
|
|
620
|
-
rightGroup.appendChild(backBtn);
|
|
621
|
-
}
|
|
622
|
-
const isLastStep = stepIndex === totalSteps - 1;
|
|
623
|
-
const primaryBtn = doc.createElement("button");
|
|
624
|
-
primaryBtn.type = "button";
|
|
625
|
-
primaryBtn.className = "veo-guide-cta";
|
|
626
|
-
const defaultLabel = isLastStep ? "Finalizar" : "Siguiente";
|
|
627
|
-
primaryBtn.textContent = typeof step.ctaText === "string" && step.ctaText ? step.ctaText : defaultLabel;
|
|
628
|
-
primaryBtn.addEventListener("click", () => {
|
|
629
|
-
if (isLastStep) callbacks.onComplete();
|
|
630
|
-
else callbacks.onNext();
|
|
631
|
-
});
|
|
632
|
-
rightGroup.appendChild(primaryBtn);
|
|
633
|
-
actions.appendChild(rightGroup);
|
|
634
|
-
container.appendChild(actions);
|
|
635
|
-
container.appendChild(createCloseButton(doc, () => callbacks.onSkip()));
|
|
636
|
-
return container;
|
|
637
|
-
}
|
|
638
|
-
var init_walkthrough_block_builder = __esm({
|
|
639
|
-
"src/plugins/guides/walkthrough-block-builder.ts"() {
|
|
640
|
-
init_block_builder();
|
|
641
|
-
}
|
|
642
|
-
});
|
|
643
|
-
|
|
644
692
|
// src/plugins/guides/guide-design.ts
|
|
645
693
|
function clamp(n, min, max) {
|
|
646
694
|
return Math.min(max, Math.max(min, n));
|
|
@@ -674,6 +722,9 @@ function applyDesignVars(host, style) {
|
|
|
674
722
|
if (typeof s.width === "number" && Number.isFinite(s.width)) {
|
|
675
723
|
host.style.setProperty("--veo-width", `${clamp(s.width, 220, 720)}px`);
|
|
676
724
|
}
|
|
725
|
+
if (typeof s.height === "number" && Number.isFinite(s.height)) {
|
|
726
|
+
host.style.setProperty("--veo-min-h", `${clamp(s.height, 120, 900)}px`);
|
|
727
|
+
}
|
|
677
728
|
if (s.align === "left" || s.align === "center" || s.align === "right") {
|
|
678
729
|
host.style.setProperty("--veo-actions-justify", ALIGN_JUSTIFY[s.align]);
|
|
679
730
|
}
|
|
@@ -803,6 +854,77 @@ var init_guide_design = __esm({
|
|
|
803
854
|
}
|
|
804
855
|
});
|
|
805
856
|
|
|
857
|
+
// src/plugins/guides/inline-host.ts
|
|
858
|
+
function readInlinePosition(style) {
|
|
859
|
+
const p = style?.inlinePosition;
|
|
860
|
+
return p === "before" || p === "prepend" || p === "append" ? p : "after";
|
|
861
|
+
}
|
|
862
|
+
function insertHost(anchor, host, position) {
|
|
863
|
+
switch (position) {
|
|
864
|
+
case "before":
|
|
865
|
+
anchor.before(host);
|
|
866
|
+
break;
|
|
867
|
+
case "after":
|
|
868
|
+
anchor.after(host);
|
|
869
|
+
break;
|
|
870
|
+
case "prepend":
|
|
871
|
+
anchor.prepend(host);
|
|
872
|
+
break;
|
|
873
|
+
case "append":
|
|
874
|
+
anchor.append(host);
|
|
875
|
+
break;
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
function keepHostAttached(host, selector, position) {
|
|
879
|
+
const id = window.setInterval(() => {
|
|
880
|
+
if (host.isConnected) return;
|
|
881
|
+
const anchor = document.querySelector(selector);
|
|
882
|
+
if (anchor) insertHost(anchor, host, position);
|
|
883
|
+
}, 1e3);
|
|
884
|
+
return () => window.clearInterval(id);
|
|
885
|
+
}
|
|
886
|
+
var init_inline_host = __esm({
|
|
887
|
+
"src/plugins/guides/inline-host.ts"() {
|
|
888
|
+
}
|
|
889
|
+
});
|
|
890
|
+
|
|
891
|
+
// src/plugins/guides/wait-for-element.ts
|
|
892
|
+
function waitForElement(selector, timeoutMs = DEFAULT_ANCHOR_WAIT_MS) {
|
|
893
|
+
return new Promise((resolve) => {
|
|
894
|
+
const safeQuery = () => {
|
|
895
|
+
try {
|
|
896
|
+
return document.querySelector(selector);
|
|
897
|
+
} catch {
|
|
898
|
+
return null;
|
|
899
|
+
}
|
|
900
|
+
};
|
|
901
|
+
const existing = safeQuery();
|
|
902
|
+
if (existing) {
|
|
903
|
+
resolve(existing);
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
let resolved = false;
|
|
907
|
+
const finish = (el) => {
|
|
908
|
+
if (resolved) return;
|
|
909
|
+
resolved = true;
|
|
910
|
+
observer.disconnect();
|
|
911
|
+
clearTimeout(timer);
|
|
912
|
+
resolve(el);
|
|
913
|
+
};
|
|
914
|
+
const observer = new MutationObserver(() => {
|
|
915
|
+
const el = safeQuery();
|
|
916
|
+
if (el) finish(el);
|
|
917
|
+
});
|
|
918
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
919
|
+
const timer = setTimeout(() => finish(null), timeoutMs);
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
var init_wait_for_element = __esm({
|
|
923
|
+
"src/plugins/guides/wait-for-element.ts"() {
|
|
924
|
+
init_constants2();
|
|
925
|
+
}
|
|
926
|
+
});
|
|
927
|
+
|
|
806
928
|
// src/plugins/guides/styles.ts
|
|
807
929
|
var GUIDE_STYLES;
|
|
808
930
|
var init_styles = __esm({
|
|
@@ -828,6 +950,13 @@ var init_styles = __esm({
|
|
|
828
950
|
z-index: ${GUIDE_Z_INDEX};
|
|
829
951
|
animation: veo-fade-in 180ms ease-out;
|
|
830
952
|
}
|
|
953
|
+
/* Sin backdrop (style.backdrop === false): la app queda usable detr\xE1s; solo la
|
|
954
|
+
tarjeta captura el mouse. El click-en-backdrop deja de cerrar (no hay backdrop). */
|
|
955
|
+
.veo-modal-overlay--none {
|
|
956
|
+
background: transparent;
|
|
957
|
+
pointer-events: none;
|
|
958
|
+
}
|
|
959
|
+
.veo-modal-overlay--none .veo-modal-card { pointer-events: auto; }
|
|
831
960
|
/*
|
|
832
961
|
* Posici\xF3n libre/preset: --veo-pos-x/y son porcentajes (default 50% = centro).
|
|
833
962
|
* El truco translate(-pos) alinea la MISMA fracci\xF3n de la tarjeta con esa
|
|
@@ -844,6 +973,7 @@ var init_styles = __esm({
|
|
|
844
973
|
border: var(--veo-border-width, 0) solid var(--veo-border-color, transparent);
|
|
845
974
|
padding: var(--veo-pad, 24px);
|
|
846
975
|
max-width: var(--veo-width); width: 90%;
|
|
976
|
+
min-height: var(--veo-min-h, auto);
|
|
847
977
|
box-shadow: var(--veo-shadow);
|
|
848
978
|
animation: veo-fade-in 180ms ease-out;
|
|
849
979
|
}
|
|
@@ -859,6 +989,14 @@ var init_styles = __esm({
|
|
|
859
989
|
}
|
|
860
990
|
.veo-banner-top { top: 0; }
|
|
861
991
|
.veo-banner-bottom { bottom: 0; }
|
|
992
|
+
/* Banner EMBEBIDO en un contenedor (step.selector): fluye dentro del contenedor
|
|
993
|
+
y empuja su contenido, en vez de flotar fijo sobre la pantalla. */
|
|
994
|
+
.veo-banner-embedded {
|
|
995
|
+
position: static;
|
|
996
|
+
left: auto; right: auto;
|
|
997
|
+
width: 100%;
|
|
998
|
+
border-radius: var(--veo-radius, 0);
|
|
999
|
+
}
|
|
862
1000
|
|
|
863
1001
|
.veo-tooltip {
|
|
864
1002
|
position: absolute;
|
|
@@ -912,6 +1050,21 @@ var init_styles = __esm({
|
|
|
912
1050
|
border-radius: var(--veo-image-radius, 8px);
|
|
913
1051
|
margin: var(--veo-image-mt, 0) 0 var(--veo-image-mb, 12px);
|
|
914
1052
|
}
|
|
1053
|
+
/* Imagen A SANGRE: rompe el padding de la tarjeta y ocupa el ancho completo
|
|
1054
|
+
(hero estilo anuncio). Como primer bloque, hereda el redondeo superior. */
|
|
1055
|
+
.veo-guide-image--bleed {
|
|
1056
|
+
width: calc(100% + var(--veo-pad, 24px) * 2);
|
|
1057
|
+
max-width: none;
|
|
1058
|
+
max-height: 280px;
|
|
1059
|
+
align-self: auto;
|
|
1060
|
+
border-radius: 0;
|
|
1061
|
+
margin: 0 calc(var(--veo-pad, 24px) * -1) 12px;
|
|
1062
|
+
}
|
|
1063
|
+
.veo-guide-content > .veo-guide-image--bleed:first-child {
|
|
1064
|
+
margin-top: calc(var(--veo-pad, 24px) * -1);
|
|
1065
|
+
border-radius: calc(var(--veo-radius, 12px) - var(--veo-border-width, 0px))
|
|
1066
|
+
calc(var(--veo-radius, 12px) - var(--veo-border-width, 0px)) 0 0;
|
|
1067
|
+
}
|
|
915
1068
|
.veo-guide-title {
|
|
916
1069
|
font-size: var(--veo-title-size, 18px); font-weight: 600; line-height: 1.3;
|
|
917
1070
|
text-align: var(--veo-title-align, left);
|
|
@@ -924,6 +1077,18 @@ var init_styles = __esm({
|
|
|
924
1077
|
margin: var(--veo-text-mt, 0) 0 var(--veo-text-mb, 16px);
|
|
925
1078
|
color: var(--veo-text-color, var(--veo-text-secondary));
|
|
926
1079
|
}
|
|
1080
|
+
/* Rich text dentro de un bloque de texto (p/listas/links/\xE9nfasis). */
|
|
1081
|
+
.veo-guide-text p { margin: 0 0 8px; }
|
|
1082
|
+
.veo-guide-text p:last-child { margin-bottom: 0; }
|
|
1083
|
+
.veo-guide-text ul, .veo-guide-text ol { margin: 0 0 8px; padding-left: 20px; }
|
|
1084
|
+
.veo-guide-text ul { list-style: disc; }
|
|
1085
|
+
.veo-guide-text ol { list-style: decimal; }
|
|
1086
|
+
.veo-guide-text li { margin: 2px 0; display: list-item; }
|
|
1087
|
+
.veo-guide-text a { color: var(--veo-primary); text-decoration: underline; cursor: pointer; }
|
|
1088
|
+
.veo-guide-text strong { font-weight: 600; }
|
|
1089
|
+
.veo-guide-text em { font-style: italic; }
|
|
1090
|
+
.veo-guide-text u { text-decoration: underline; }
|
|
1091
|
+
.veo-guide-text s { text-decoration: line-through; }
|
|
927
1092
|
.veo-guide-actions {
|
|
928
1093
|
display: flex; gap: 8px; justify-content: var(--veo-actions-justify);
|
|
929
1094
|
}
|
|
@@ -1062,6 +1227,44 @@ var init_styles = __esm({
|
|
|
1062
1227
|
}
|
|
1063
1228
|
.veo-walkthrough-skip:hover { color: var(--veo-text); }
|
|
1064
1229
|
|
|
1230
|
+
/* \u2500\u2500 Badge (elemento inyectado junto al ancla que abre un tooltip) \u2500\u2500 */
|
|
1231
|
+
.veo-badge {
|
|
1232
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
1233
|
+
border: none; padding: 0; margin: 0 4px;
|
|
1234
|
+
background: none; cursor: pointer;
|
|
1235
|
+
line-height: 1; vertical-align: middle;
|
|
1236
|
+
font-family: inherit;
|
|
1237
|
+
}
|
|
1238
|
+
.veo-badge:focus-visible { outline: 2px solid var(--veo-primary); outline-offset: 2px; }
|
|
1239
|
+
.veo-badge--icon {
|
|
1240
|
+
border-radius: 50%;
|
|
1241
|
+
background: color-mix(in srgb, var(--veo-primary) 14%, transparent);
|
|
1242
|
+
color: var(--veo-primary);
|
|
1243
|
+
font-weight: 600;
|
|
1244
|
+
}
|
|
1245
|
+
.veo-badge--dot {
|
|
1246
|
+
border-radius: 50%;
|
|
1247
|
+
background: var(--veo-primary);
|
|
1248
|
+
animation: veo-badge-pulse 2s ease-out infinite;
|
|
1249
|
+
}
|
|
1250
|
+
.veo-badge--pill {
|
|
1251
|
+
border-radius: 999px;
|
|
1252
|
+
background: var(--veo-primary);
|
|
1253
|
+
color: #fff;
|
|
1254
|
+
font-weight: 600;
|
|
1255
|
+
padding: 3px 9px;
|
|
1256
|
+
white-space: nowrap;
|
|
1257
|
+
}
|
|
1258
|
+
.veo-badge--image img { display: block; border-radius: 4px; object-fit: cover; }
|
|
1259
|
+
/* El tooltip del badge usa strategy fixed (el host vive dentro del flujo del
|
|
1260
|
+
cliente; un absoluto se recortar\xEDa con overflow de ancestros). */
|
|
1261
|
+
.veo-badge-tooltip { position: fixed; }
|
|
1262
|
+
@keyframes veo-badge-pulse {
|
|
1263
|
+
0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--veo-primary) 45%, transparent); }
|
|
1264
|
+
70% { box-shadow: 0 0 0 7px transparent; }
|
|
1265
|
+
100% { box-shadow: 0 0 0 0 transparent; }
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1065
1268
|
.veo-custom-floating {
|
|
1066
1269
|
position: fixed;
|
|
1067
1270
|
z-index: ${GUIDE_Z_INDEX};
|
|
@@ -1180,6 +1383,14 @@ var init_base_renderer = __esm({
|
|
|
1180
1383
|
registerCleanup(fn) {
|
|
1181
1384
|
this.cleanups.push(fn);
|
|
1182
1385
|
}
|
|
1386
|
+
/**
|
|
1387
|
+
* Host de la guía montada (o `null` si aún no se montó / ya se destruyó).
|
|
1388
|
+
* Lo usa el modo builder para adjuntar manipulación directa (drag/resize)
|
|
1389
|
+
* sobre el shadow root abierto sin tocar los renderers.
|
|
1390
|
+
*/
|
|
1391
|
+
hostElement() {
|
|
1392
|
+
return this.host;
|
|
1393
|
+
}
|
|
1183
1394
|
/** Remueve el host del DOM y corre todas las funciones de cleanup. */
|
|
1184
1395
|
destroy() {
|
|
1185
1396
|
for (const fn of this.cleanups) {
|
|
@@ -1199,26 +1410,402 @@ var init_base_renderer = __esm({
|
|
|
1199
1410
|
}
|
|
1200
1411
|
});
|
|
1201
1412
|
|
|
1413
|
+
// src/plugins/guides/renderers/floating-arrow.ts
|
|
1414
|
+
function positionArrow(arrowEl, placement, data) {
|
|
1415
|
+
const side = STATIC_SIDE[placement.split("-")[0] ?? "bottom"] ?? "top";
|
|
1416
|
+
for (const prop of ["top", "bottom", "left", "right"]) {
|
|
1417
|
+
arrowEl.style.setProperty(prop, "");
|
|
1418
|
+
}
|
|
1419
|
+
if (data?.x != null) arrowEl.style.setProperty("left", `${data.x}px`);
|
|
1420
|
+
if (data?.y != null) arrowEl.style.setProperty("top", `${data.y}px`);
|
|
1421
|
+
arrowEl.style.setProperty(side, "-6px");
|
|
1422
|
+
}
|
|
1423
|
+
var STATIC_SIDE;
|
|
1424
|
+
var init_floating_arrow = __esm({
|
|
1425
|
+
"src/plugins/guides/renderers/floating-arrow.ts"() {
|
|
1426
|
+
STATIC_SIDE = {
|
|
1427
|
+
top: "bottom",
|
|
1428
|
+
bottom: "top",
|
|
1429
|
+
left: "right",
|
|
1430
|
+
right: "left"
|
|
1431
|
+
};
|
|
1432
|
+
}
|
|
1433
|
+
});
|
|
1434
|
+
function readBadgeConfig(step) {
|
|
1435
|
+
const raw = step.style?.badge;
|
|
1436
|
+
if (!raw || typeof raw !== "object") return { kind: "icon" };
|
|
1437
|
+
const b = raw;
|
|
1438
|
+
const kind = b.kind === "dot" || b.kind === "pill" || b.kind === "image" || b.kind === "icon" ? b.kind : "icon";
|
|
1439
|
+
return {
|
|
1440
|
+
kind,
|
|
1441
|
+
icon: typeof b.icon === "string" ? b.icon : null,
|
|
1442
|
+
text: typeof b.text === "string" ? b.text : null,
|
|
1443
|
+
imageUrl: typeof b.imageUrl === "string" ? b.imageUrl : null,
|
|
1444
|
+
color: typeof b.color === "string" && COLOR_RE.test(b.color.trim()) ? b.color.trim() : null,
|
|
1445
|
+
size: typeof b.size === "number" && Number.isFinite(b.size) ? b.size : null
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
1448
|
+
function clamp2(n, min, max) {
|
|
1449
|
+
return Math.min(max, Math.max(min, n));
|
|
1450
|
+
}
|
|
1451
|
+
function buildBadgeElement(config, doc) {
|
|
1452
|
+
const btn = doc.createElement("button");
|
|
1453
|
+
btn.type = "button";
|
|
1454
|
+
btn.className = `veo-badge veo-badge--${config.kind}`;
|
|
1455
|
+
btn.setAttribute("aria-label", "M\xE1s informaci\xF3n");
|
|
1456
|
+
const size = clamp2(config.size ?? 18, 8, 48);
|
|
1457
|
+
switch (config.kind) {
|
|
1458
|
+
case "dot":
|
|
1459
|
+
btn.style.width = `${size}px`;
|
|
1460
|
+
btn.style.height = `${size}px`;
|
|
1461
|
+
if (config.color) btn.style.background = config.color;
|
|
1462
|
+
break;
|
|
1463
|
+
case "pill": {
|
|
1464
|
+
btn.textContent = config.text?.slice(0, 24) || "Nuevo";
|
|
1465
|
+
btn.style.fontSize = `${clamp2(Math.round(size * 0.62), 8, 24)}px`;
|
|
1466
|
+
if (config.color) btn.style.background = config.color;
|
|
1467
|
+
break;
|
|
1468
|
+
}
|
|
1469
|
+
case "image": {
|
|
1470
|
+
if (config.imageUrl && isSafeUrl(config.imageUrl)) {
|
|
1471
|
+
const img = doc.createElement("img");
|
|
1472
|
+
img.src = config.imageUrl;
|
|
1473
|
+
img.alt = "";
|
|
1474
|
+
img.style.width = `${size}px`;
|
|
1475
|
+
img.style.height = `${size}px`;
|
|
1476
|
+
btn.appendChild(img);
|
|
1477
|
+
} else {
|
|
1478
|
+
btn.className = "veo-badge veo-badge--icon";
|
|
1479
|
+
btn.textContent = "?";
|
|
1480
|
+
btn.style.width = `${size}px`;
|
|
1481
|
+
btn.style.height = `${size}px`;
|
|
1482
|
+
btn.style.fontSize = `${clamp2(Math.round(size * 0.62), 8, 32)}px`;
|
|
1483
|
+
}
|
|
1484
|
+
break;
|
|
1485
|
+
}
|
|
1486
|
+
default: {
|
|
1487
|
+
btn.textContent = (config.icon || "\u2139\uFE0F").slice(0, 4);
|
|
1488
|
+
btn.style.width = `${size}px`;
|
|
1489
|
+
btn.style.height = `${size}px`;
|
|
1490
|
+
btn.style.fontSize = `${clamp2(Math.round(size * 0.62), 8, 32)}px`;
|
|
1491
|
+
if (config.color) btn.style.color = config.color;
|
|
1492
|
+
break;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
return btn;
|
|
1496
|
+
}
|
|
1497
|
+
var HOVER_CLOSE_DELAY_MS, BadgeRenderer;
|
|
1498
|
+
var init_badge_renderer = __esm({
|
|
1499
|
+
"src/plugins/guides/renderers/badge-renderer.ts"() {
|
|
1500
|
+
init_block_builder();
|
|
1501
|
+
init_guide_design();
|
|
1502
|
+
init_inline_host();
|
|
1503
|
+
init_wait_for_element();
|
|
1504
|
+
init_base_renderer();
|
|
1505
|
+
init_floating_arrow();
|
|
1506
|
+
HOVER_CLOSE_DELAY_MS = 150;
|
|
1507
|
+
BadgeRenderer = class extends BaseRenderer {
|
|
1508
|
+
constructor() {
|
|
1509
|
+
super(...arguments);
|
|
1510
|
+
this.tooltip = null;
|
|
1511
|
+
this.arrowEl = null;
|
|
1512
|
+
this.badgeBtn = null;
|
|
1513
|
+
this.open = false;
|
|
1514
|
+
this.shownEmitted = false;
|
|
1515
|
+
this.trigger = "hover";
|
|
1516
|
+
this.side = "top";
|
|
1517
|
+
this.closeTimer = null;
|
|
1518
|
+
this.stopFloat = null;
|
|
1519
|
+
}
|
|
1520
|
+
async render(context) {
|
|
1521
|
+
const step = context.guide.guideSteps[0];
|
|
1522
|
+
if (!step) return;
|
|
1523
|
+
const selector = step.selector ?? context.guide.activationRules.selector;
|
|
1524
|
+
if (typeof selector !== "string" || selector.length === 0) return;
|
|
1525
|
+
const anchor = await waitForElement(selector);
|
|
1526
|
+
if (!anchor) return;
|
|
1527
|
+
const { host, root } = this.createHost();
|
|
1528
|
+
const ownerDocument = root.ownerDocument ?? document;
|
|
1529
|
+
this.applyDesign(step.style);
|
|
1530
|
+
this.trigger = step.style?.badgeTrigger === "click" ? "click" : "hover";
|
|
1531
|
+
const rawSide = step.style?.tooltipPlacement;
|
|
1532
|
+
this.side = rawSide === "bottom" || rawSide === "left" || rawSide === "right" ? rawSide : "top";
|
|
1533
|
+
const position = readInlinePosition(step.style);
|
|
1534
|
+
host.style.display = "inline-flex";
|
|
1535
|
+
host.style.verticalAlign = "middle";
|
|
1536
|
+
const badge = buildBadgeElement(readBadgeConfig(step), ownerDocument);
|
|
1537
|
+
root.appendChild(badge);
|
|
1538
|
+
this.badgeBtn = badge;
|
|
1539
|
+
insertHost(anchor, host, position);
|
|
1540
|
+
this.registerCleanup(keepHostAttached(host, selector, position));
|
|
1541
|
+
const tooltip = ownerDocument.createElement("div");
|
|
1542
|
+
tooltip.className = "veo-tooltip veo-badge-tooltip";
|
|
1543
|
+
tooltip.style.display = "none";
|
|
1544
|
+
const emit = (action, meta) => {
|
|
1545
|
+
context.onInteraction({
|
|
1546
|
+
guideId: context.guide.guideId,
|
|
1547
|
+
stepIndex: 0,
|
|
1548
|
+
action,
|
|
1549
|
+
...meta ? { metadata: meta } : {}
|
|
1550
|
+
});
|
|
1551
|
+
};
|
|
1552
|
+
const buildTooltipContent = (s) => buildStepContent(s, ownerDocument, {
|
|
1553
|
+
onCtaClick: (action, url, meta) => {
|
|
1554
|
+
emit("cta_clicked", meta);
|
|
1555
|
+
if (action === "url" && url) window.open(url, "_blank", "noopener,noreferrer");
|
|
1556
|
+
if (action === "dismiss") {
|
|
1557
|
+
emit("dismissed");
|
|
1558
|
+
context.onClose();
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1561
|
+
this.closeTooltip();
|
|
1562
|
+
},
|
|
1563
|
+
onDismiss: () => {
|
|
1564
|
+
emit("dismissed");
|
|
1565
|
+
context.onClose();
|
|
1566
|
+
}
|
|
1567
|
+
});
|
|
1568
|
+
const content = buildTooltipContent(step);
|
|
1569
|
+
tooltip.appendChild(content);
|
|
1570
|
+
const arrowEl = ownerDocument.createElement("div");
|
|
1571
|
+
arrowEl.className = "veo-tooltip-arrow";
|
|
1572
|
+
tooltip.appendChild(arrowEl);
|
|
1573
|
+
root.appendChild(tooltip);
|
|
1574
|
+
this.tooltip = tooltip;
|
|
1575
|
+
this.arrowEl = arrowEl;
|
|
1576
|
+
this.liveContainer = tooltip;
|
|
1577
|
+
this.liveContent = content;
|
|
1578
|
+
this.liveKey = this.liveKeyOf(step);
|
|
1579
|
+
this.liveBuild = buildTooltipContent;
|
|
1580
|
+
const openNow = () => {
|
|
1581
|
+
this.cancelClose();
|
|
1582
|
+
if (!this.open) {
|
|
1583
|
+
this.openTooltip(badge);
|
|
1584
|
+
if (!this.shownEmitted) {
|
|
1585
|
+
this.shownEmitted = true;
|
|
1586
|
+
emit("shown");
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
};
|
|
1590
|
+
if (this.trigger === "hover") {
|
|
1591
|
+
const scheduleClose = () => {
|
|
1592
|
+
this.cancelClose();
|
|
1593
|
+
this.closeTimer = window.setTimeout(() => this.closeTooltip(), HOVER_CLOSE_DELAY_MS);
|
|
1594
|
+
};
|
|
1595
|
+
badge.addEventListener("mouseenter", openNow);
|
|
1596
|
+
badge.addEventListener("focus", openNow);
|
|
1597
|
+
badge.addEventListener("mouseleave", scheduleClose);
|
|
1598
|
+
badge.addEventListener("blur", scheduleClose);
|
|
1599
|
+
tooltip.addEventListener("mouseenter", () => this.cancelClose());
|
|
1600
|
+
tooltip.addEventListener("mouseleave", scheduleClose);
|
|
1601
|
+
} else {
|
|
1602
|
+
badge.addEventListener("click", () => {
|
|
1603
|
+
if (this.open) this.closeTooltip();
|
|
1604
|
+
else openNow();
|
|
1605
|
+
});
|
|
1606
|
+
const onDocClick = (e) => {
|
|
1607
|
+
if (!this.open) return;
|
|
1608
|
+
if (e.composedPath().includes(host)) return;
|
|
1609
|
+
this.closeTooltip();
|
|
1610
|
+
};
|
|
1611
|
+
document.addEventListener("click", onDocClick, true);
|
|
1612
|
+
this.registerCleanup(() => document.removeEventListener("click", onDocClick, true));
|
|
1613
|
+
}
|
|
1614
|
+
const onKey = (e) => {
|
|
1615
|
+
if (e.key === "Escape" && this.open) this.closeTooltip();
|
|
1616
|
+
};
|
|
1617
|
+
document.addEventListener("keydown", onKey);
|
|
1618
|
+
this.registerCleanup(() => document.removeEventListener("keydown", onKey));
|
|
1619
|
+
if (context.isPreview) openNow();
|
|
1620
|
+
}
|
|
1621
|
+
/** Cambiar de ancla/posición/trigger requiere re-montar y re-cablear. */
|
|
1622
|
+
liveKeyOf(step) {
|
|
1623
|
+
const selector = step.selector ?? "";
|
|
1624
|
+
const position = readInlinePosition(step.style);
|
|
1625
|
+
const trigger = step.style?.badgeTrigger === "click" ? "click" : "hover";
|
|
1626
|
+
return `${selector}|${position}|${trigger}`;
|
|
1627
|
+
}
|
|
1628
|
+
onLiveUpdate(step) {
|
|
1629
|
+
if (this.badgeBtn) {
|
|
1630
|
+
const doc = this.badgeBtn.ownerDocument;
|
|
1631
|
+
const next = buildBadgeElement(readBadgeConfig(step), doc);
|
|
1632
|
+
this.badgeBtn.className = next.className;
|
|
1633
|
+
this.badgeBtn.setAttribute("style", next.getAttribute("style") ?? "");
|
|
1634
|
+
this.badgeBtn.replaceChildren(...Array.from(next.childNodes));
|
|
1635
|
+
}
|
|
1636
|
+
const rawSide = step.style?.tooltipPlacement;
|
|
1637
|
+
this.side = rawSide === "bottom" || rawSide === "left" || rawSide === "right" ? rawSide : "top";
|
|
1638
|
+
if (this.open && this.badgeBtn) this.position(this.badgeBtn);
|
|
1639
|
+
}
|
|
1640
|
+
destroy() {
|
|
1641
|
+
this.cancelClose();
|
|
1642
|
+
this.stopFloat?.();
|
|
1643
|
+
this.stopFloat = null;
|
|
1644
|
+
this.tooltip = null;
|
|
1645
|
+
this.arrowEl = null;
|
|
1646
|
+
this.badgeBtn = null;
|
|
1647
|
+
this.open = false;
|
|
1648
|
+
super.destroy();
|
|
1649
|
+
}
|
|
1650
|
+
openTooltip(badge) {
|
|
1651
|
+
if (!this.tooltip) return;
|
|
1652
|
+
this.tooltip.style.display = "block";
|
|
1653
|
+
this.open = true;
|
|
1654
|
+
void this.position(badge);
|
|
1655
|
+
const reposition = () => {
|
|
1656
|
+
void this.position(badge);
|
|
1657
|
+
};
|
|
1658
|
+
window.addEventListener("scroll", reposition, true);
|
|
1659
|
+
window.addEventListener("resize", reposition);
|
|
1660
|
+
this.stopFloat = () => {
|
|
1661
|
+
window.removeEventListener("scroll", reposition, true);
|
|
1662
|
+
window.removeEventListener("resize", reposition);
|
|
1663
|
+
};
|
|
1664
|
+
}
|
|
1665
|
+
closeTooltip() {
|
|
1666
|
+
this.cancelClose();
|
|
1667
|
+
if (!this.tooltip || !this.open) return;
|
|
1668
|
+
this.tooltip.style.display = "none";
|
|
1669
|
+
this.open = false;
|
|
1670
|
+
this.stopFloat?.();
|
|
1671
|
+
this.stopFloat = null;
|
|
1672
|
+
}
|
|
1673
|
+
cancelClose() {
|
|
1674
|
+
if (this.closeTimer !== null) {
|
|
1675
|
+
window.clearTimeout(this.closeTimer);
|
|
1676
|
+
this.closeTimer = null;
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
async position(badge) {
|
|
1680
|
+
const tooltip = this.tooltip;
|
|
1681
|
+
const arrowEl = this.arrowEl;
|
|
1682
|
+
if (!tooltip || !arrowEl) return;
|
|
1683
|
+
const { x, y, placement, middlewareData } = await dom.computePosition(badge, tooltip, {
|
|
1684
|
+
strategy: "fixed",
|
|
1685
|
+
placement: this.side,
|
|
1686
|
+
middleware: [dom.offset(8), dom.flip(), dom.shift({ padding: 8 }), dom.arrow({ element: arrowEl })]
|
|
1687
|
+
});
|
|
1688
|
+
tooltip.style.left = `${x}px`;
|
|
1689
|
+
tooltip.style.top = `${y}px`;
|
|
1690
|
+
positionArrow(arrowEl, placement, middlewareData.arrow);
|
|
1691
|
+
}
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
});
|
|
1695
|
+
|
|
1696
|
+
// src/plugins/guides/walkthrough-block-builder.ts
|
|
1697
|
+
function buildWalkthroughStepContent(step, stepIndex, totalSteps, doc, callbacks) {
|
|
1698
|
+
const container = doc.createElement("div");
|
|
1699
|
+
container.className = "veo-guide-content";
|
|
1700
|
+
const counter = doc.createElement("div");
|
|
1701
|
+
counter.className = "veo-walkthrough-counter";
|
|
1702
|
+
counter.textContent = `Paso ${stepIndex + 1} de ${totalSteps}`;
|
|
1703
|
+
container.appendChild(counter);
|
|
1704
|
+
const progress = doc.createElement("div");
|
|
1705
|
+
progress.className = "veo-walkthrough-progress";
|
|
1706
|
+
for (let i = 0; i < totalSteps; i++) {
|
|
1707
|
+
const dot = doc.createElement("span");
|
|
1708
|
+
dot.className = "veo-walkthrough-progress-dot";
|
|
1709
|
+
if (i < stepIndex) dot.classList.add("completed");
|
|
1710
|
+
if (i === stepIndex) dot.classList.add("active");
|
|
1711
|
+
progress.appendChild(dot);
|
|
1712
|
+
}
|
|
1713
|
+
container.appendChild(progress);
|
|
1714
|
+
if (typeof step.imageUrl === "string" && step.imageUrl && isSafeUrl(step.imageUrl)) {
|
|
1715
|
+
const img = doc.createElement("img");
|
|
1716
|
+
img.className = "veo-guide-image";
|
|
1717
|
+
img.src = step.imageUrl;
|
|
1718
|
+
img.alt = typeof step.title === "string" ? step.title : "";
|
|
1719
|
+
container.appendChild(img);
|
|
1720
|
+
}
|
|
1721
|
+
if (typeof step.title === "string" && step.title) {
|
|
1722
|
+
const heading = doc.createElement("h2");
|
|
1723
|
+
heading.className = "veo-guide-title";
|
|
1724
|
+
heading.textContent = step.title;
|
|
1725
|
+
container.appendChild(heading);
|
|
1726
|
+
}
|
|
1727
|
+
if (typeof step.content === "string" && step.content) {
|
|
1728
|
+
const paragraph = doc.createElement("p");
|
|
1729
|
+
paragraph.className = "veo-guide-text";
|
|
1730
|
+
paragraph.textContent = step.content;
|
|
1731
|
+
container.appendChild(paragraph);
|
|
1732
|
+
}
|
|
1733
|
+
const actions = doc.createElement("div");
|
|
1734
|
+
actions.className = "veo-walkthrough-actions";
|
|
1735
|
+
const skipBtn = doc.createElement("button");
|
|
1736
|
+
skipBtn.type = "button";
|
|
1737
|
+
skipBtn.className = "veo-walkthrough-skip";
|
|
1738
|
+
skipBtn.textContent = "Omitir";
|
|
1739
|
+
skipBtn.addEventListener("click", () => callbacks.onSkip());
|
|
1740
|
+
actions.appendChild(skipBtn);
|
|
1741
|
+
const rightGroup = doc.createElement("div");
|
|
1742
|
+
rightGroup.className = "veo-walkthrough-actions-right";
|
|
1743
|
+
if (stepIndex > 0) {
|
|
1744
|
+
const backBtn = doc.createElement("button");
|
|
1745
|
+
backBtn.type = "button";
|
|
1746
|
+
backBtn.className = "veo-walkthrough-btn-secondary";
|
|
1747
|
+
backBtn.textContent = "Atr\xE1s";
|
|
1748
|
+
backBtn.addEventListener("click", () => callbacks.onBack());
|
|
1749
|
+
rightGroup.appendChild(backBtn);
|
|
1750
|
+
}
|
|
1751
|
+
const isLastStep = stepIndex === totalSteps - 1;
|
|
1752
|
+
const primaryBtn = doc.createElement("button");
|
|
1753
|
+
primaryBtn.type = "button";
|
|
1754
|
+
primaryBtn.className = "veo-guide-cta";
|
|
1755
|
+
const defaultLabel = isLastStep ? "Finalizar" : "Siguiente";
|
|
1756
|
+
primaryBtn.textContent = typeof step.ctaText === "string" && step.ctaText ? step.ctaText : defaultLabel;
|
|
1757
|
+
primaryBtn.addEventListener("click", () => {
|
|
1758
|
+
if (isLastStep) callbacks.onComplete();
|
|
1759
|
+
else callbacks.onNext();
|
|
1760
|
+
});
|
|
1761
|
+
rightGroup.appendChild(primaryBtn);
|
|
1762
|
+
actions.appendChild(rightGroup);
|
|
1763
|
+
container.appendChild(actions);
|
|
1764
|
+
container.appendChild(createCloseButton(doc, () => callbacks.onSkip()));
|
|
1765
|
+
return container;
|
|
1766
|
+
}
|
|
1767
|
+
var init_walkthrough_block_builder = __esm({
|
|
1768
|
+
"src/plugins/guides/walkthrough-block-builder.ts"() {
|
|
1769
|
+
init_block_builder();
|
|
1770
|
+
}
|
|
1771
|
+
});
|
|
1772
|
+
|
|
1202
1773
|
// src/plugins/guides/renderers/banner-renderer.ts
|
|
1774
|
+
function bannerClassOf(position, embedded) {
|
|
1775
|
+
return embedded ? "veo-banner veo-banner-embedded" : `veo-banner veo-banner-${position}`;
|
|
1776
|
+
}
|
|
1203
1777
|
var BannerRenderer;
|
|
1204
1778
|
var init_banner_renderer = __esm({
|
|
1205
1779
|
"src/plugins/guides/renderers/banner-renderer.ts"() {
|
|
1206
1780
|
init_block_builder();
|
|
1781
|
+
init_inline_host();
|
|
1782
|
+
init_wait_for_element();
|
|
1207
1783
|
init_walkthrough_block_builder();
|
|
1208
1784
|
init_base_renderer();
|
|
1209
1785
|
BannerRenderer = class extends BaseRenderer {
|
|
1210
|
-
render(context) {
|
|
1786
|
+
async render(context) {
|
|
1211
1787
|
const idx = context.nav?.stepIndex ?? 0;
|
|
1212
1788
|
const step = context.guide.guideSteps[idx];
|
|
1213
1789
|
if (!step) return;
|
|
1214
|
-
const
|
|
1790
|
+
const position = step.style?.position === "bottom" ? "bottom" : "top";
|
|
1791
|
+
const selector = context.nav ? null : step.selector?.trim() ?? null;
|
|
1792
|
+
let anchor = null;
|
|
1793
|
+
if (selector) {
|
|
1794
|
+
anchor = await waitForElement(selector);
|
|
1795
|
+
if (!anchor) return;
|
|
1796
|
+
}
|
|
1797
|
+
const { host, root } = this.createHost();
|
|
1215
1798
|
this.applyDesign(step.style);
|
|
1216
1799
|
const ownerDocument = root.ownerDocument ?? document;
|
|
1217
|
-
const position = step.style?.position === "bottom" ? "bottom" : "top";
|
|
1218
1800
|
const banner = ownerDocument.createElement("div");
|
|
1219
|
-
banner.className =
|
|
1220
|
-
const dismiss = (action, ctaUrl) => {
|
|
1221
|
-
context.onInteraction({
|
|
1801
|
+
banner.className = bannerClassOf(position, Boolean(anchor));
|
|
1802
|
+
const dismiss = (action, ctaUrl, meta) => {
|
|
1803
|
+
context.onInteraction({
|
|
1804
|
+
guideId: context.guide.guideId,
|
|
1805
|
+
stepIndex: idx,
|
|
1806
|
+
action,
|
|
1807
|
+
...meta ? { metadata: meta } : {}
|
|
1808
|
+
});
|
|
1222
1809
|
if (ctaUrl) window.open(ctaUrl, "_blank", "noopener,noreferrer");
|
|
1223
1810
|
context.onClose();
|
|
1224
1811
|
};
|
|
@@ -1229,68 +1816,46 @@ var init_banner_renderer = __esm({
|
|
|
1229
1816
|
ownerDocument,
|
|
1230
1817
|
context.nav.callbacks
|
|
1231
1818
|
) : buildStepContent(step, ownerDocument, {
|
|
1232
|
-
onCtaClick: (action, url) => dismiss("cta_clicked", action === "url" ? url : void 0),
|
|
1819
|
+
onCtaClick: (action, url, meta) => dismiss("cta_clicked", action === "url" ? url : void 0, meta),
|
|
1233
1820
|
onDismiss: () => dismiss("dismissed")
|
|
1234
1821
|
});
|
|
1235
1822
|
banner.appendChild(content);
|
|
1236
1823
|
root.appendChild(banner);
|
|
1824
|
+
if (selector && anchor) {
|
|
1825
|
+
host.style.display = "block";
|
|
1826
|
+
const insertAt = position === "top" ? "prepend" : "append";
|
|
1827
|
+
insertHost(anchor, host, insertAt);
|
|
1828
|
+
this.registerCleanup(keepHostAttached(host, selector, insertAt));
|
|
1829
|
+
}
|
|
1237
1830
|
if (!context.nav) {
|
|
1238
1831
|
this.liveContainer = banner;
|
|
1239
1832
|
this.liveContent = content;
|
|
1240
1833
|
this.liveBuild = (s) => buildStepContent(s, ownerDocument, {
|
|
1241
|
-
onCtaClick: (action, url) => dismiss("cta_clicked", action === "url" ? url : void 0),
|
|
1834
|
+
onCtaClick: (action, url, meta) => dismiss("cta_clicked", action === "url" ? url : void 0, meta),
|
|
1242
1835
|
onDismiss: () => dismiss("dismissed")
|
|
1243
1836
|
});
|
|
1837
|
+
this.liveKey = this.liveKeyOf(step);
|
|
1244
1838
|
context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
|
|
1245
1839
|
}
|
|
1246
1840
|
}
|
|
1841
|
+
/** Cambiar de contenedor o de top/bottom embebido requiere re-insertar → remontar. */
|
|
1842
|
+
liveKeyOf(step) {
|
|
1843
|
+
const selector = step.selector?.trim() ?? "";
|
|
1844
|
+
if (!selector) return "";
|
|
1845
|
+
const position = step.style?.position === "bottom" ? "bottom" : "top";
|
|
1846
|
+
return `${selector}|${position}`;
|
|
1847
|
+
}
|
|
1247
1848
|
onLiveUpdate(step) {
|
|
1248
1849
|
if (this.liveContainer) {
|
|
1249
1850
|
const position = step.style?.position === "bottom" ? "bottom" : "top";
|
|
1250
|
-
|
|
1851
|
+
const embedded = Boolean(step.selector?.trim());
|
|
1852
|
+
this.liveContainer.className = bannerClassOf(position, embedded);
|
|
1251
1853
|
}
|
|
1252
1854
|
}
|
|
1253
1855
|
};
|
|
1254
1856
|
}
|
|
1255
1857
|
});
|
|
1256
1858
|
|
|
1257
|
-
// src/plugins/guides/wait-for-element.ts
|
|
1258
|
-
function waitForElement(selector, timeoutMs = DEFAULT_ANCHOR_WAIT_MS) {
|
|
1259
|
-
return new Promise((resolve) => {
|
|
1260
|
-
const safeQuery = () => {
|
|
1261
|
-
try {
|
|
1262
|
-
return document.querySelector(selector);
|
|
1263
|
-
} catch {
|
|
1264
|
-
return null;
|
|
1265
|
-
}
|
|
1266
|
-
};
|
|
1267
|
-
const existing = safeQuery();
|
|
1268
|
-
if (existing) {
|
|
1269
|
-
resolve(existing);
|
|
1270
|
-
return;
|
|
1271
|
-
}
|
|
1272
|
-
let resolved = false;
|
|
1273
|
-
const finish = (el) => {
|
|
1274
|
-
if (resolved) return;
|
|
1275
|
-
resolved = true;
|
|
1276
|
-
observer.disconnect();
|
|
1277
|
-
clearTimeout(timer);
|
|
1278
|
-
resolve(el);
|
|
1279
|
-
};
|
|
1280
|
-
const observer = new MutationObserver(() => {
|
|
1281
|
-
const el = safeQuery();
|
|
1282
|
-
if (el) finish(el);
|
|
1283
|
-
});
|
|
1284
|
-
observer.observe(document.body, { childList: true, subtree: true });
|
|
1285
|
-
const timer = setTimeout(() => finish(null), timeoutMs);
|
|
1286
|
-
});
|
|
1287
|
-
}
|
|
1288
|
-
var init_wait_for_element = __esm({
|
|
1289
|
-
"src/plugins/guides/wait-for-element.ts"() {
|
|
1290
|
-
init_constants2();
|
|
1291
|
-
}
|
|
1292
|
-
});
|
|
1293
|
-
|
|
1294
1859
|
// src/plugins/guides/renderers/custom-frame.ts
|
|
1295
1860
|
function mountCustomFrame(doc, step, context, opts) {
|
|
1296
1861
|
const iframe = doc.createElement("iframe");
|
|
@@ -1824,40 +2389,6 @@ var init_form_renderer = __esm({
|
|
|
1824
2389
|
}
|
|
1825
2390
|
});
|
|
1826
2391
|
|
|
1827
|
-
// src/plugins/guides/inline-host.ts
|
|
1828
|
-
function readInlinePosition(style) {
|
|
1829
|
-
const p = style?.inlinePosition;
|
|
1830
|
-
return p === "before" || p === "prepend" || p === "append" ? p : "after";
|
|
1831
|
-
}
|
|
1832
|
-
function insertHost(anchor, host, position) {
|
|
1833
|
-
switch (position) {
|
|
1834
|
-
case "before":
|
|
1835
|
-
anchor.before(host);
|
|
1836
|
-
break;
|
|
1837
|
-
case "after":
|
|
1838
|
-
anchor.after(host);
|
|
1839
|
-
break;
|
|
1840
|
-
case "prepend":
|
|
1841
|
-
anchor.prepend(host);
|
|
1842
|
-
break;
|
|
1843
|
-
case "append":
|
|
1844
|
-
anchor.append(host);
|
|
1845
|
-
break;
|
|
1846
|
-
}
|
|
1847
|
-
}
|
|
1848
|
-
function keepHostAttached(host, selector, position) {
|
|
1849
|
-
const id = window.setInterval(() => {
|
|
1850
|
-
if (host.isConnected) return;
|
|
1851
|
-
const anchor = document.querySelector(selector);
|
|
1852
|
-
if (anchor) insertHost(anchor, host, position);
|
|
1853
|
-
}, 1e3);
|
|
1854
|
-
return () => window.clearInterval(id);
|
|
1855
|
-
}
|
|
1856
|
-
var init_inline_host = __esm({
|
|
1857
|
-
"src/plugins/guides/inline-host.ts"() {
|
|
1858
|
-
}
|
|
1859
|
-
});
|
|
1860
|
-
|
|
1861
2392
|
// src/plugins/guides/renderers/inline-custom-renderer.ts
|
|
1862
2393
|
var InlineCustomRenderer;
|
|
1863
2394
|
var init_inline_custom_renderer = __esm({
|
|
@@ -1950,13 +2481,18 @@ var init_inline_renderer = __esm({
|
|
|
1950
2481
|
const ownerDocument = root.ownerDocument ?? document;
|
|
1951
2482
|
const card = ownerDocument.createElement("div");
|
|
1952
2483
|
card.className = "veo-inline";
|
|
1953
|
-
const dismiss = (action, ctaUrl) => {
|
|
1954
|
-
context.onInteraction({
|
|
2484
|
+
const dismiss = (action, ctaUrl, meta) => {
|
|
2485
|
+
context.onInteraction({
|
|
2486
|
+
guideId: context.guide.guideId,
|
|
2487
|
+
stepIndex: 0,
|
|
2488
|
+
action,
|
|
2489
|
+
...meta ? { metadata: meta } : {}
|
|
2490
|
+
});
|
|
1955
2491
|
if (ctaUrl) window.open(ctaUrl, "_blank", "noopener,noreferrer");
|
|
1956
2492
|
context.onClose();
|
|
1957
2493
|
};
|
|
1958
2494
|
const content = buildStepContent(step, ownerDocument, {
|
|
1959
|
-
onCtaClick: (action, url) => dismiss("cta_clicked", action === "url" ? url : void 0),
|
|
2495
|
+
onCtaClick: (action, url, meta) => dismiss("cta_clicked", action === "url" ? url : void 0, meta),
|
|
1960
2496
|
onDismiss: () => dismiss("dismissed")
|
|
1961
2497
|
});
|
|
1962
2498
|
card.appendChild(content);
|
|
@@ -1965,7 +2501,7 @@ var init_inline_renderer = __esm({
|
|
|
1965
2501
|
this.liveContent = content;
|
|
1966
2502
|
this.liveKey = `${selector}|${position}`;
|
|
1967
2503
|
this.liveBuild = (s) => buildStepContent(s, ownerDocument, {
|
|
1968
|
-
onCtaClick: (action, url) => dismiss("cta_clicked", action === "url" ? url : void 0),
|
|
2504
|
+
onCtaClick: (action, url, meta) => dismiss("cta_clicked", action === "url" ? url : void 0, meta),
|
|
1969
2505
|
onDismiss: () => dismiss("dismissed")
|
|
1970
2506
|
});
|
|
1971
2507
|
context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
|
|
@@ -1978,6 +2514,9 @@ var init_inline_renderer = __esm({
|
|
|
1978
2514
|
});
|
|
1979
2515
|
|
|
1980
2516
|
// src/plugins/guides/renderers/modal-renderer.ts
|
|
2517
|
+
function overlayClassOf(step) {
|
|
2518
|
+
return step.style?.backdrop === false ? "veo-modal-overlay veo-modal-overlay--none" : "veo-modal-overlay";
|
|
2519
|
+
}
|
|
1981
2520
|
var ModalRenderer;
|
|
1982
2521
|
var init_modal_renderer = __esm({
|
|
1983
2522
|
"src/plugins/guides/renderers/modal-renderer.ts"() {
|
|
@@ -1985,6 +2524,10 @@ var init_modal_renderer = __esm({
|
|
|
1985
2524
|
init_walkthrough_block_builder();
|
|
1986
2525
|
init_base_renderer();
|
|
1987
2526
|
ModalRenderer = class extends BaseRenderer {
|
|
2527
|
+
constructor() {
|
|
2528
|
+
super(...arguments);
|
|
2529
|
+
this.overlay = null;
|
|
2530
|
+
}
|
|
1988
2531
|
render(context) {
|
|
1989
2532
|
const idx = context.nav?.stepIndex ?? 0;
|
|
1990
2533
|
const step = context.guide.guideSteps[idx];
|
|
@@ -1993,11 +2536,17 @@ var init_modal_renderer = __esm({
|
|
|
1993
2536
|
this.applyDesign(step.style);
|
|
1994
2537
|
const ownerDocument = root.ownerDocument ?? document;
|
|
1995
2538
|
const overlay = ownerDocument.createElement("div");
|
|
1996
|
-
overlay.className =
|
|
2539
|
+
overlay.className = overlayClassOf(step);
|
|
2540
|
+
this.overlay = overlay;
|
|
1997
2541
|
const card = ownerDocument.createElement("div");
|
|
1998
2542
|
card.className = "veo-modal-card";
|
|
1999
|
-
const dismiss = (action, ctaUrl) => {
|
|
2000
|
-
context.onInteraction({
|
|
2543
|
+
const dismiss = (action, ctaUrl, meta) => {
|
|
2544
|
+
context.onInteraction({
|
|
2545
|
+
guideId: context.guide.guideId,
|
|
2546
|
+
stepIndex: idx,
|
|
2547
|
+
action,
|
|
2548
|
+
...meta ? { metadata: meta } : {}
|
|
2549
|
+
});
|
|
2001
2550
|
if (ctaUrl) window.open(ctaUrl, "_blank", "noopener,noreferrer");
|
|
2002
2551
|
context.onClose();
|
|
2003
2552
|
};
|
|
@@ -2012,7 +2561,7 @@ var init_modal_renderer = __esm({
|
|
|
2012
2561
|
ownerDocument,
|
|
2013
2562
|
context.nav.callbacks
|
|
2014
2563
|
) : buildStepContent(step, ownerDocument, {
|
|
2015
|
-
onCtaClick: (action, url) => dismiss("cta_clicked", action === "url" ? url : void 0),
|
|
2564
|
+
onCtaClick: (action, url, meta) => dismiss("cta_clicked", action === "url" ? url : void 0, meta),
|
|
2016
2565
|
onDismiss: () => dismiss("dismissed")
|
|
2017
2566
|
});
|
|
2018
2567
|
card.appendChild(content);
|
|
@@ -2022,7 +2571,7 @@ var init_modal_renderer = __esm({
|
|
|
2022
2571
|
this.liveContainer = card;
|
|
2023
2572
|
this.liveContent = content;
|
|
2024
2573
|
this.liveBuild = (s) => buildStepContent(s, ownerDocument, {
|
|
2025
|
-
onCtaClick: (action, url) => dismiss("cta_clicked", action === "url" ? url : void 0),
|
|
2574
|
+
onCtaClick: (action, url, meta) => dismiss("cta_clicked", action === "url" ? url : void 0, meta),
|
|
2026
2575
|
onDismiss: () => dismiss("dismissed")
|
|
2027
2576
|
});
|
|
2028
2577
|
}
|
|
@@ -2038,28 +2587,13 @@ var init_modal_renderer = __esm({
|
|
|
2038
2587
|
context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
|
|
2039
2588
|
}
|
|
2040
2589
|
}
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
}
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
for (const prop of ["top", "bottom", "left", "right"]) {
|
|
2049
|
-
arrowEl.style.setProperty(prop, "");
|
|
2050
|
-
}
|
|
2051
|
-
if (data?.x != null) arrowEl.style.setProperty("left", `${data.x}px`);
|
|
2052
|
-
if (data?.y != null) arrowEl.style.setProperty("top", `${data.y}px`);
|
|
2053
|
-
arrowEl.style.setProperty(side, "-6px");
|
|
2054
|
-
}
|
|
2055
|
-
var STATIC_SIDE;
|
|
2056
|
-
var init_floating_arrow = __esm({
|
|
2057
|
-
"src/plugins/guides/renderers/floating-arrow.ts"() {
|
|
2058
|
-
STATIC_SIDE = {
|
|
2059
|
-
top: "bottom",
|
|
2060
|
-
bottom: "top",
|
|
2061
|
-
left: "right",
|
|
2062
|
-
right: "left"
|
|
2590
|
+
onLiveUpdate(step) {
|
|
2591
|
+
if (this.overlay) this.overlay.className = overlayClassOf(step);
|
|
2592
|
+
}
|
|
2593
|
+
destroy() {
|
|
2594
|
+
this.overlay = null;
|
|
2595
|
+
super.destroy();
|
|
2596
|
+
}
|
|
2063
2597
|
};
|
|
2064
2598
|
}
|
|
2065
2599
|
});
|
|
@@ -2091,18 +2625,19 @@ var init_tooltip_renderer = __esm({
|
|
|
2091
2625
|
const ownerDocument = root.ownerDocument ?? document;
|
|
2092
2626
|
const tooltip = ownerDocument.createElement("div");
|
|
2093
2627
|
tooltip.className = "veo-tooltip";
|
|
2094
|
-
const dismiss = (action, ctaUrl) => {
|
|
2628
|
+
const dismiss = (action, ctaUrl, meta) => {
|
|
2095
2629
|
context.onInteraction({
|
|
2096
2630
|
guideId: context.guide.guideId,
|
|
2097
2631
|
stepIndex: 0,
|
|
2098
|
-
action
|
|
2632
|
+
action,
|
|
2633
|
+
...meta ? { metadata: meta } : {}
|
|
2099
2634
|
});
|
|
2100
2635
|
if (ctaUrl) window.open(ctaUrl, "_blank", "noopener,noreferrer");
|
|
2101
2636
|
context.onClose();
|
|
2102
2637
|
};
|
|
2103
2638
|
const content = buildStepContent(step, ownerDocument, {
|
|
2104
|
-
onCtaClick: (action, url) => {
|
|
2105
|
-
dismiss("cta_clicked", action === "url" ? url : void 0);
|
|
2639
|
+
onCtaClick: (action, url, meta) => {
|
|
2640
|
+
dismiss("cta_clicked", action === "url" ? url : void 0, meta);
|
|
2106
2641
|
},
|
|
2107
2642
|
onDismiss: () => dismiss("dismissed")
|
|
2108
2643
|
});
|
|
@@ -2135,7 +2670,7 @@ var init_tooltip_renderer = __esm({
|
|
|
2135
2670
|
this.liveContent = content;
|
|
2136
2671
|
this.liveKey = selector;
|
|
2137
2672
|
this.liveBuild = (s) => buildStepContent(s, ownerDocument, {
|
|
2138
|
-
onCtaClick: (action, url) => dismiss("cta_clicked", action === "url" ? url : void 0),
|
|
2673
|
+
onCtaClick: (action, url, meta) => dismiss("cta_clicked", action === "url" ? url : void 0, meta),
|
|
2139
2674
|
onDismiss: () => dismiss("dismissed")
|
|
2140
2675
|
});
|
|
2141
2676
|
context.onInteraction({
|
|
@@ -2219,7 +2754,7 @@ var init_walkthrough_renderer = __esm({
|
|
|
2219
2754
|
function previewGuide(input) {
|
|
2220
2755
|
if (!hasDocument()) {
|
|
2221
2756
|
return { close: () => {
|
|
2222
|
-
}, ready: Promise.resolve({ rendered: false }) };
|
|
2757
|
+
}, ready: Promise.resolve({ rendered: false }), host: () => null };
|
|
2223
2758
|
}
|
|
2224
2759
|
if (!activeController) activeController = new GuidePreviewController();
|
|
2225
2760
|
return activeController.preview(input);
|
|
@@ -2262,6 +2797,8 @@ function createSingleStepRenderer(type) {
|
|
|
2262
2797
|
return new FormRenderer();
|
|
2263
2798
|
case "inline-form":
|
|
2264
2799
|
return new InlineFormRenderer();
|
|
2800
|
+
case "badge":
|
|
2801
|
+
return new BadgeRenderer();
|
|
2265
2802
|
case "walkthrough":
|
|
2266
2803
|
return null;
|
|
2267
2804
|
}
|
|
@@ -2276,7 +2813,9 @@ var PREVIEW_GUIDE_ID, GuidePreviewController, activeController;
|
|
|
2276
2813
|
var init_guide_preview = __esm({
|
|
2277
2814
|
"src/plugins/guides/guide-preview.ts"() {
|
|
2278
2815
|
init_safe_env();
|
|
2816
|
+
init_badge_renderer();
|
|
2279
2817
|
init_banner_renderer();
|
|
2818
|
+
init_base_renderer();
|
|
2280
2819
|
init_custom_renderer();
|
|
2281
2820
|
init_form_renderer();
|
|
2282
2821
|
init_inline_custom_renderer();
|
|
@@ -2300,14 +2839,26 @@ var init_guide_preview = __esm({
|
|
|
2300
2839
|
const nextStep = input.guideSteps[0];
|
|
2301
2840
|
if (this.singleStep && this.input && input.guideType === this.input.guideType && input.guideType !== "walkthrough" && nextStep && this.singleStep.updateStep(nextStep)) {
|
|
2302
2841
|
this.input = input;
|
|
2303
|
-
return {
|
|
2842
|
+
return {
|
|
2843
|
+
close: () => this.close(),
|
|
2844
|
+
ready: Promise.resolve({ rendered: true }),
|
|
2845
|
+
host: () => this.activeHost()
|
|
2846
|
+
};
|
|
2304
2847
|
}
|
|
2305
2848
|
this.close();
|
|
2306
2849
|
this.input = input;
|
|
2307
2850
|
const guide = normalize(input);
|
|
2308
2851
|
const start = typeof input.startStepIndex === "number" ? input.startStepIndex : 0;
|
|
2309
2852
|
const ready = guide.guideType === "walkthrough" ? this.startWalkthrough(guide, start) : this.renderSingleStep(guide);
|
|
2310
|
-
return { close: () => this.close(), ready };
|
|
2853
|
+
return { close: () => this.close(), ready, host: () => this.activeHost() };
|
|
2854
|
+
}
|
|
2855
|
+
/** Host de la guía montada (single-step o paso de walkthrough activo). */
|
|
2856
|
+
activeHost() {
|
|
2857
|
+
if (this.singleStep) return this.singleStep.hostElement();
|
|
2858
|
+
if (this.walkthrough instanceof BaseRenderer) {
|
|
2859
|
+
return this.walkthrough.hostElement();
|
|
2860
|
+
}
|
|
2861
|
+
return null;
|
|
2311
2862
|
}
|
|
2312
2863
|
close() {
|
|
2313
2864
|
if (this.singleStep) {
|
|
@@ -2522,8 +3073,8 @@ function injectBuilderPanel(opts) {
|
|
|
2522
3073
|
const ot = top;
|
|
2523
3074
|
iframe.style.pointerEvents = "none";
|
|
2524
3075
|
const move = (ev) => {
|
|
2525
|
-
left =
|
|
2526
|
-
top =
|
|
3076
|
+
left = clamp3(ol + (ev.clientX - sx), 0, Math.max(0, window.innerWidth - card.offsetWidth));
|
|
3077
|
+
top = clamp3(ot + (ev.clientY - sy), 0, Math.max(0, window.innerHeight - 40));
|
|
2527
3078
|
card.style.left = `${left}px`;
|
|
2528
3079
|
card.style.top = `${top}px`;
|
|
2529
3080
|
};
|
|
@@ -2544,8 +3095,8 @@ function injectBuilderPanel(opts) {
|
|
|
2544
3095
|
const sh = card.offsetHeight;
|
|
2545
3096
|
iframe.style.pointerEvents = "none";
|
|
2546
3097
|
const move = (ev) => {
|
|
2547
|
-
card.style.width = `${
|
|
2548
|
-
card.style.height = `${
|
|
3098
|
+
card.style.width = `${clamp3(sw + (ev.clientX - sx), MIN_W, window.innerWidth)}px`;
|
|
3099
|
+
card.style.height = `${clamp3(sh + (ev.clientY - sy), MIN_H, window.innerHeight)}px`;
|
|
2549
3100
|
};
|
|
2550
3101
|
const up = () => {
|
|
2551
3102
|
window.removeEventListener("mousemove", move);
|
|
@@ -2564,7 +3115,7 @@ function injectBuilderPanel(opts) {
|
|
|
2564
3115
|
}
|
|
2565
3116
|
};
|
|
2566
3117
|
}
|
|
2567
|
-
function
|
|
3118
|
+
function clamp3(v, min, max) {
|
|
2568
3119
|
return Math.min(Math.max(v, min), max);
|
|
2569
3120
|
}
|
|
2570
3121
|
function buildPanelUrl(opts) {
|
|
@@ -2628,6 +3179,242 @@ var init_builder_panel = __esm({
|
|
|
2628
3179
|
}
|
|
2629
3180
|
});
|
|
2630
3181
|
|
|
3182
|
+
// src/plugins/builder/design-manipulator.ts
|
|
3183
|
+
function clamp4(v, min, max) {
|
|
3184
|
+
return Math.min(Math.max(v, min), max);
|
|
3185
|
+
}
|
|
3186
|
+
function round3(v) {
|
|
3187
|
+
return Math.round(v * 1e3) / 1e3;
|
|
3188
|
+
}
|
|
3189
|
+
function isInteractive(target) {
|
|
3190
|
+
return Boolean(
|
|
3191
|
+
target instanceof Element && target.closest("button, a, input, textarea, select, label")
|
|
3192
|
+
);
|
|
3193
|
+
}
|
|
3194
|
+
function attachDesignManipulator(opts) {
|
|
3195
|
+
const shadow = opts.host.shadowRoot;
|
|
3196
|
+
if (!shadow) return null;
|
|
3197
|
+
const card = shadow.querySelector(".veo-modal-card");
|
|
3198
|
+
if (card) return attachModal(opts, shadow, card);
|
|
3199
|
+
const banner = shadow.querySelector(".veo-banner");
|
|
3200
|
+
if (banner && !banner.classList.contains("veo-banner-embedded")) {
|
|
3201
|
+
return attachBanner(opts, shadow, banner);
|
|
3202
|
+
}
|
|
3203
|
+
return null;
|
|
3204
|
+
}
|
|
3205
|
+
function attachModal(opts, shadow, card) {
|
|
3206
|
+
if (card.dataset.veoManipulated === "1") return null;
|
|
3207
|
+
card.dataset.veoManipulated = "1";
|
|
3208
|
+
card.classList.add("veo-mnp-card");
|
|
3209
|
+
const doc = card.ownerDocument;
|
|
3210
|
+
const style = doc.createElement("style");
|
|
3211
|
+
style.textContent = MANIPULATOR_CSS;
|
|
3212
|
+
shadow.appendChild(style);
|
|
3213
|
+
const handles = [];
|
|
3214
|
+
for (const dir of RESIZE_DIRS) {
|
|
3215
|
+
const h = doc.createElement("div");
|
|
3216
|
+
h.className = `veo-mnp-handle veo-mnp-${dir}`;
|
|
3217
|
+
h.dataset.veoDir = dir;
|
|
3218
|
+
card.appendChild(h);
|
|
3219
|
+
handles.push(h);
|
|
3220
|
+
}
|
|
3221
|
+
let dragging = false;
|
|
3222
|
+
let detached = false;
|
|
3223
|
+
let patch = {};
|
|
3224
|
+
const setVars = (v) => {
|
|
3225
|
+
const host = opts.host;
|
|
3226
|
+
if (v.px !== void 0) host.style.setProperty("--veo-pos-x", `${v.px * 100}%`);
|
|
3227
|
+
if (v.py !== void 0) host.style.setProperty("--veo-pos-y", `${v.py * 100}%`);
|
|
3228
|
+
if (v.w !== void 0) host.style.setProperty("--veo-width", `${v.w}px`);
|
|
3229
|
+
if (v.h !== void 0) host.style.setProperty("--veo-min-h", `${v.h}px`);
|
|
3230
|
+
};
|
|
3231
|
+
const startGesture = (e, apply) => {
|
|
3232
|
+
e.preventDefault();
|
|
3233
|
+
e.stopPropagation();
|
|
3234
|
+
dragging = true;
|
|
3235
|
+
patch = {};
|
|
3236
|
+
const start = card.getBoundingClientRect();
|
|
3237
|
+
const sx = e.clientX;
|
|
3238
|
+
const sy = e.clientY;
|
|
3239
|
+
let raf = 0;
|
|
3240
|
+
let lastEv = null;
|
|
3241
|
+
const flush = () => {
|
|
3242
|
+
raf = 0;
|
|
3243
|
+
if (!lastEv) return;
|
|
3244
|
+
const next = apply(lastEv.clientX - sx, lastEv.clientY - sy, start);
|
|
3245
|
+
patch = { ...patch, ...next };
|
|
3246
|
+
setVars({
|
|
3247
|
+
...next.posX !== void 0 ? { px: next.posX } : {},
|
|
3248
|
+
...next.posY !== void 0 ? { py: next.posY } : {},
|
|
3249
|
+
...next.width !== void 0 ? { w: next.width } : {},
|
|
3250
|
+
...next.height !== void 0 ? { h: next.height } : {}
|
|
3251
|
+
});
|
|
3252
|
+
};
|
|
3253
|
+
const move = (ev) => {
|
|
3254
|
+
lastEv = ev;
|
|
3255
|
+
if (typeof window.requestAnimationFrame !== "function") flush();
|
|
3256
|
+
else if (!raf) raf = window.requestAnimationFrame(flush);
|
|
3257
|
+
};
|
|
3258
|
+
const up = () => {
|
|
3259
|
+
window.removeEventListener("mousemove", move);
|
|
3260
|
+
window.removeEventListener("mouseup", up);
|
|
3261
|
+
if (raf) window.cancelAnimationFrame(raf);
|
|
3262
|
+
flush();
|
|
3263
|
+
dragging = false;
|
|
3264
|
+
if (Object.keys(patch).length > 0 && !detached) {
|
|
3265
|
+
opts.onCommit({ stepIndex: opts.stepIndex, style: patch });
|
|
3266
|
+
}
|
|
3267
|
+
opts.onGestureEnd?.();
|
|
3268
|
+
};
|
|
3269
|
+
window.addEventListener("mousemove", move);
|
|
3270
|
+
window.addEventListener("mouseup", up);
|
|
3271
|
+
};
|
|
3272
|
+
const posXFor = (left, w) => {
|
|
3273
|
+
const span = window.innerWidth - w;
|
|
3274
|
+
return span <= 0 ? 0.5 : round3(clamp4(left / span, 0, 1));
|
|
3275
|
+
};
|
|
3276
|
+
const posYFor = (top, h) => {
|
|
3277
|
+
const span = window.innerHeight - h;
|
|
3278
|
+
return span <= 0 ? 0.5 : round3(clamp4(top / span, 0, 1));
|
|
3279
|
+
};
|
|
3280
|
+
const onCardDown = (e) => {
|
|
3281
|
+
if (isInteractive(e.target)) return;
|
|
3282
|
+
if (e.target instanceof Element && e.target.closest(".veo-mnp-handle")) return;
|
|
3283
|
+
startGesture(e, (dx, dy, start) => ({
|
|
3284
|
+
posX: posXFor(start.left + dx, start.width),
|
|
3285
|
+
posY: posYFor(start.top + dy, start.height)
|
|
3286
|
+
}));
|
|
3287
|
+
};
|
|
3288
|
+
const onHandleDown = (e) => {
|
|
3289
|
+
const dir = e.currentTarget.dataset.veoDir;
|
|
3290
|
+
startGesture(e, (dx, dy, start) => {
|
|
3291
|
+
const out = {};
|
|
3292
|
+
if (dir.includes("e")) out.width = Math.round(clamp4(start.width + dx, MIN_W2, MAX_W));
|
|
3293
|
+
if (dir.includes("w")) {
|
|
3294
|
+
out.width = Math.round(clamp4(start.width - dx, MIN_W2, MAX_W));
|
|
3295
|
+
out.posX = posXFor(start.right - out.width, out.width);
|
|
3296
|
+
}
|
|
3297
|
+
if (dir.includes("s")) out.height = Math.round(clamp4(start.height + dy, MIN_H2, MAX_H));
|
|
3298
|
+
if (dir.includes("n")) {
|
|
3299
|
+
out.height = Math.round(clamp4(start.height - dy, MIN_H2, MAX_H));
|
|
3300
|
+
out.posY = posYFor(start.bottom - out.height, out.height);
|
|
3301
|
+
}
|
|
3302
|
+
return out;
|
|
3303
|
+
});
|
|
3304
|
+
};
|
|
3305
|
+
card.addEventListener("mousedown", onCardDown);
|
|
3306
|
+
for (const h of handles) h.addEventListener("mousedown", onHandleDown);
|
|
3307
|
+
return {
|
|
3308
|
+
isDragging: () => dragging,
|
|
3309
|
+
detach: () => {
|
|
3310
|
+
if (detached) return;
|
|
3311
|
+
detached = true;
|
|
3312
|
+
card.removeEventListener("mousedown", onCardDown);
|
|
3313
|
+
for (const h of handles) h.remove();
|
|
3314
|
+
style.remove();
|
|
3315
|
+
card.classList.remove("veo-mnp-card");
|
|
3316
|
+
delete card.dataset.veoManipulated;
|
|
3317
|
+
}
|
|
3318
|
+
};
|
|
3319
|
+
}
|
|
3320
|
+
function attachBanner(opts, shadow, banner) {
|
|
3321
|
+
if (banner.dataset.veoManipulated === "1") return null;
|
|
3322
|
+
banner.dataset.veoManipulated = "1";
|
|
3323
|
+
banner.classList.add("veo-mnp-banner");
|
|
3324
|
+
const doc = banner.ownerDocument;
|
|
3325
|
+
const style = doc.createElement("style");
|
|
3326
|
+
style.textContent = MANIPULATOR_CSS;
|
|
3327
|
+
shadow.appendChild(style);
|
|
3328
|
+
let dragging = false;
|
|
3329
|
+
let detached = false;
|
|
3330
|
+
const positionOf = () => banner.classList.contains("veo-banner-bottom") ? "bottom" : "top";
|
|
3331
|
+
const onDown = (e) => {
|
|
3332
|
+
if (isInteractive(e.target)) return;
|
|
3333
|
+
e.preventDefault();
|
|
3334
|
+
dragging = true;
|
|
3335
|
+
const startPos = positionOf();
|
|
3336
|
+
let current = startPos;
|
|
3337
|
+
const move = (ev) => {
|
|
3338
|
+
const next = ev.clientY < window.innerHeight / 2 ? "top" : "bottom";
|
|
3339
|
+
if (next !== current) {
|
|
3340
|
+
current = next;
|
|
3341
|
+
banner.classList.remove("veo-banner-top", "veo-banner-bottom");
|
|
3342
|
+
banner.classList.add(`veo-banner-${next}`);
|
|
3343
|
+
}
|
|
3344
|
+
};
|
|
3345
|
+
const up = () => {
|
|
3346
|
+
window.removeEventListener("mousemove", move);
|
|
3347
|
+
window.removeEventListener("mouseup", up);
|
|
3348
|
+
dragging = false;
|
|
3349
|
+
if (current !== startPos && !detached) {
|
|
3350
|
+
opts.onCommit({ stepIndex: opts.stepIndex, style: { position: current } });
|
|
3351
|
+
}
|
|
3352
|
+
opts.onGestureEnd?.();
|
|
3353
|
+
};
|
|
3354
|
+
window.addEventListener("mousemove", move);
|
|
3355
|
+
window.addEventListener("mouseup", up);
|
|
3356
|
+
};
|
|
3357
|
+
banner.addEventListener("mousedown", onDown);
|
|
3358
|
+
return {
|
|
3359
|
+
isDragging: () => dragging,
|
|
3360
|
+
detach: () => {
|
|
3361
|
+
if (detached) return;
|
|
3362
|
+
detached = true;
|
|
3363
|
+
banner.removeEventListener("mousedown", onDown);
|
|
3364
|
+
style.remove();
|
|
3365
|
+
banner.classList.remove("veo-mnp-banner");
|
|
3366
|
+
delete banner.dataset.veoManipulated;
|
|
3367
|
+
}
|
|
3368
|
+
};
|
|
3369
|
+
}
|
|
3370
|
+
var MIN_W2, MAX_W, MIN_H2, MAX_H, EDGE, RESIZE_DIRS, MANIPULATOR_CSS;
|
|
3371
|
+
var init_design_manipulator = __esm({
|
|
3372
|
+
"src/plugins/builder/design-manipulator.ts"() {
|
|
3373
|
+
MIN_W2 = 220;
|
|
3374
|
+
MAX_W = 720;
|
|
3375
|
+
MIN_H2 = 120;
|
|
3376
|
+
MAX_H = 900;
|
|
3377
|
+
EDGE = 8;
|
|
3378
|
+
RESIZE_DIRS = ["n", "s", "e", "w", "ne", "nw", "se", "sw"];
|
|
3379
|
+
MANIPULATOR_CSS = `
|
|
3380
|
+
.veo-mnp-card {
|
|
3381
|
+
outline: 1.5px dashed rgba(255, 91, 53, 0.75);
|
|
3382
|
+
outline-offset: 2px;
|
|
3383
|
+
}
|
|
3384
|
+
.veo-mnp-card:hover { cursor: move; }
|
|
3385
|
+
.veo-mnp-handle {
|
|
3386
|
+
position: absolute;
|
|
3387
|
+
z-index: 10;
|
|
3388
|
+
background: transparent;
|
|
3389
|
+
}
|
|
3390
|
+
.veo-mnp-handle::after {
|
|
3391
|
+
content: '';
|
|
3392
|
+
position: absolute;
|
|
3393
|
+
width: 8px; height: 8px;
|
|
3394
|
+
background: #fff;
|
|
3395
|
+
border: 1.5px solid rgba(255, 91, 53, 0.9);
|
|
3396
|
+
border-radius: 2px;
|
|
3397
|
+
top: 50%; left: 50%;
|
|
3398
|
+
transform: translate(-50%, -50%);
|
|
3399
|
+
opacity: 0;
|
|
3400
|
+
transition: opacity 100ms ease;
|
|
3401
|
+
}
|
|
3402
|
+
.veo-mnp-card:hover .veo-mnp-handle::after,
|
|
3403
|
+
.veo-mnp-handle:hover::after { opacity: 1; }
|
|
3404
|
+
.veo-mnp-n { top: -${EDGE / 2}px; left: ${EDGE}px; right: ${EDGE}px; height: ${EDGE}px; cursor: ns-resize; }
|
|
3405
|
+
.veo-mnp-s { bottom: -${EDGE / 2}px; left: ${EDGE}px; right: ${EDGE}px; height: ${EDGE}px; cursor: ns-resize; }
|
|
3406
|
+
.veo-mnp-e { right: -${EDGE / 2}px; top: ${EDGE}px; bottom: ${EDGE}px; width: ${EDGE}px; cursor: ew-resize; }
|
|
3407
|
+
.veo-mnp-w { left: -${EDGE / 2}px; top: ${EDGE}px; bottom: ${EDGE}px; width: ${EDGE}px; cursor: ew-resize; }
|
|
3408
|
+
.veo-mnp-ne { top: -${EDGE / 2}px; right: -${EDGE / 2}px; width: ${EDGE * 1.5}px; height: ${EDGE * 1.5}px; cursor: nesw-resize; }
|
|
3409
|
+
.veo-mnp-nw { top: -${EDGE / 2}px; left: -${EDGE / 2}px; width: ${EDGE * 1.5}px; height: ${EDGE * 1.5}px; cursor: nwse-resize; }
|
|
3410
|
+
.veo-mnp-se { bottom: -${EDGE / 2}px; right: -${EDGE / 2}px; width: ${EDGE * 1.5}px; height: ${EDGE * 1.5}px; cursor: nwse-resize; }
|
|
3411
|
+
.veo-mnp-sw { bottom: -${EDGE / 2}px; left: -${EDGE / 2}px; width: ${EDGE * 1.5}px; height: ${EDGE * 1.5}px; cursor: nesw-resize; }
|
|
3412
|
+
.veo-mnp-banner { outline: 1.5px dashed rgba(255, 91, 53, 0.75); outline-offset: -2px; cursor: grab; }
|
|
3413
|
+
.veo-mnp-banner:active { cursor: grabbing; }
|
|
3414
|
+
`;
|
|
3415
|
+
}
|
|
3416
|
+
});
|
|
3417
|
+
|
|
2631
3418
|
// src/plugins/builder/element-picker.ts
|
|
2632
3419
|
function startElementPicker(options) {
|
|
2633
3420
|
if (!hasDocument()) {
|
|
@@ -2841,10 +3628,35 @@ function initBuilderMode() {
|
|
|
2841
3628
|
let picker = null;
|
|
2842
3629
|
let panel = null;
|
|
2843
3630
|
let staticTarget = null;
|
|
3631
|
+
let manipulator = null;
|
|
3632
|
+
let pendingPreview = null;
|
|
2844
3633
|
const getTarget = () => panel ? panel.target() : staticTarget;
|
|
2845
3634
|
const post = (event) => {
|
|
2846
3635
|
getTarget()?.postMessage({ source: VEO_BUILDER_SOURCE, token, ...event }, dashboardOrigin);
|
|
2847
3636
|
};
|
|
3637
|
+
const applyPreview = (cmd) => {
|
|
3638
|
+
manipulator?.detach();
|
|
3639
|
+
manipulator = null;
|
|
3640
|
+
const handle = previewGuide(cmd.guide);
|
|
3641
|
+
if (!cmd.editable) return;
|
|
3642
|
+
void handle.ready.then((result) => {
|
|
3643
|
+
if (!result.rendered) return;
|
|
3644
|
+
const host = handle.host();
|
|
3645
|
+
if (!host) return;
|
|
3646
|
+
manipulator = attachDesignManipulator({
|
|
3647
|
+
host,
|
|
3648
|
+
guideType: cmd.guide.guideType,
|
|
3649
|
+
stepIndex: cmd.guide.startStepIndex ?? 0,
|
|
3650
|
+
onCommit: (payload) => post({ type: "design-updated", payload }),
|
|
3651
|
+
onGestureEnd: () => {
|
|
3652
|
+
if (!pendingPreview) return;
|
|
3653
|
+
const queued = pendingPreview;
|
|
3654
|
+
pendingPreview = null;
|
|
3655
|
+
applyPreview(queued);
|
|
3656
|
+
}
|
|
3657
|
+
});
|
|
3658
|
+
});
|
|
3659
|
+
};
|
|
2848
3660
|
const handleCommand = (cmd) => {
|
|
2849
3661
|
switch (cmd.type) {
|
|
2850
3662
|
case "panel-ready":
|
|
@@ -2870,9 +3682,14 @@ function initBuilderMode() {
|
|
|
2870
3682
|
picker = null;
|
|
2871
3683
|
break;
|
|
2872
3684
|
case "preview":
|
|
2873
|
-
if (cmd.guide)
|
|
3685
|
+
if (!cmd.guide) break;
|
|
3686
|
+
if (manipulator?.isDragging()) pendingPreview = cmd;
|
|
3687
|
+
else applyPreview(cmd);
|
|
2874
3688
|
break;
|
|
2875
3689
|
case "close-preview":
|
|
3690
|
+
manipulator?.detach();
|
|
3691
|
+
manipulator = null;
|
|
3692
|
+
pendingPreview = null;
|
|
2876
3693
|
closeGuidePreview();
|
|
2877
3694
|
break;
|
|
2878
3695
|
case "teardown":
|
|
@@ -2892,6 +3709,9 @@ function initBuilderMode() {
|
|
|
2892
3709
|
window.removeEventListener("pagehide", onPageHide);
|
|
2893
3710
|
picker?.stop();
|
|
2894
3711
|
picker = null;
|
|
3712
|
+
manipulator?.detach();
|
|
3713
|
+
manipulator = null;
|
|
3714
|
+
pendingPreview = null;
|
|
2895
3715
|
closeGuidePreview();
|
|
2896
3716
|
post({ type: "closed" });
|
|
2897
3717
|
panel?.teardown();
|
|
@@ -2971,6 +3791,7 @@ var init_builder_mode = __esm({
|
|
|
2971
3791
|
init_guide_preview();
|
|
2972
3792
|
init_bridge_protocol();
|
|
2973
3793
|
init_builder_panel();
|
|
3794
|
+
init_design_manipulator();
|
|
2974
3795
|
init_element_picker();
|
|
2975
3796
|
}
|
|
2976
3797
|
});
|
|
@@ -3104,6 +3925,7 @@ var init_builder_session = __esm({
|
|
|
3104
3925
|
// src/plugins/builder/index.ts
|
|
3105
3926
|
var builder_exports = {};
|
|
3106
3927
|
__export(builder_exports, {
|
|
3928
|
+
attachDesignManipulator: () => attachDesignManipulator,
|
|
3107
3929
|
createBuilderSession: () => createBuilderSession,
|
|
3108
3930
|
initBuilderMode: () => initBuilderMode,
|
|
3109
3931
|
startElementPicker: () => startElementPicker,
|
|
@@ -3113,6 +3935,7 @@ var init_builder = __esm({
|
|
|
3113
3935
|
"src/plugins/builder/index.ts"() {
|
|
3114
3936
|
init_builder_mode();
|
|
3115
3937
|
init_builder_session();
|
|
3938
|
+
init_design_manipulator();
|
|
3116
3939
|
init_element_picker();
|
|
3117
3940
|
}
|
|
3118
3941
|
});
|
|
@@ -3379,8 +4202,8 @@ var DomListener = class {
|
|
|
3379
4202
|
let depth = 0;
|
|
3380
4203
|
while (current && current !== document.documentElement && depth < 5) {
|
|
3381
4204
|
const tag = current.tagName.toLowerCase();
|
|
3382
|
-
const
|
|
3383
|
-
if (
|
|
4205
|
+
const isInteractive2 = tag === "button" || tag === "a" || tag === "input" || tag === "select" || tag === "textarea" || current.hasAttribute("data-veo-tag") || current.hasAttribute("role") || current.hasAttribute("onclick") || current instanceof HTMLElement && current.style.cursor === "pointer";
|
|
4206
|
+
if (isInteractive2) return current;
|
|
3384
4207
|
current = current.parentElement;
|
|
3385
4208
|
depth++;
|
|
3386
4209
|
}
|
|
@@ -3502,7 +4325,8 @@ var Queue = class {
|
|
|
3502
4325
|
return;
|
|
3503
4326
|
}
|
|
3504
4327
|
this.retryCount++;
|
|
3505
|
-
const
|
|
4328
|
+
const base = Math.min(1e3 * 2 ** (this.retryCount - 1), RETRY_MAX_DELAY_MS);
|
|
4329
|
+
const delayMs = base / 2 + Math.random() * (base / 2);
|
|
3506
4330
|
if (this.retryTimer) clearTimeout(this.retryTimer);
|
|
3507
4331
|
this.retryTimer = setTimeout(() => {
|
|
3508
4332
|
void this.flush();
|
|
@@ -4181,6 +5005,7 @@ function toWire(payload) {
|
|
|
4181
5005
|
if (typeof payload.stepIndex === "number") wire.stepPosition = payload.stepIndex;
|
|
4182
5006
|
if (payload.pageUrl) wire.pageUrl = payload.pageUrl;
|
|
4183
5007
|
if (payload.pagePath) wire.pagePath = payload.pagePath;
|
|
5008
|
+
if (payload.metadata) wire.metadata = payload.metadata;
|
|
4184
5009
|
return wire;
|
|
4185
5010
|
}
|
|
4186
5011
|
var GuideTrackerClient = class {
|
|
@@ -4228,6 +5053,8 @@ var GuideTrackerClient = class {
|
|
|
4228
5053
|
};
|
|
4229
5054
|
|
|
4230
5055
|
// src/plugins/guides/guide-tracker-queue.ts
|
|
5056
|
+
var RETRY_BASE_MS = 3e3;
|
|
5057
|
+
var RETRY_MAX_DELAY_MS2 = 3e4;
|
|
4231
5058
|
var GuideTrackerQueue = class {
|
|
4232
5059
|
constructor(config) {
|
|
4233
5060
|
this.config = config;
|
|
@@ -4264,20 +5091,23 @@ var GuideTrackerQueue = class {
|
|
|
4264
5091
|
*/
|
|
4265
5092
|
async flush() {
|
|
4266
5093
|
if (this.flushing || this.buffer.length === 0) return;
|
|
5094
|
+
const now = Date.now();
|
|
5095
|
+
const due = this.buffer.filter((item) => (item.nextRetryAt ?? 0) <= now);
|
|
5096
|
+
if (due.length === 0) return;
|
|
4267
5097
|
this.flushing = true;
|
|
4268
|
-
const
|
|
5098
|
+
const dueSet = new Set(due);
|
|
5099
|
+
this.buffer = this.buffer.filter((item) => !dueSet.has(item));
|
|
4269
5100
|
const results = await Promise.allSettled(
|
|
4270
|
-
|
|
4271
|
-
(item) => this.config.client.send({ guideId: item.guideId, payload: item.payload })
|
|
4272
|
-
)
|
|
5101
|
+
due.map((item) => this.config.client.send({ guideId: item.guideId, payload: item.payload }))
|
|
4273
5102
|
);
|
|
4274
5103
|
for (let i = 0; i < results.length; i++) {
|
|
4275
5104
|
const result = results[i];
|
|
4276
|
-
const item =
|
|
5105
|
+
const item = due[i];
|
|
4277
5106
|
if (!result || !item) continue;
|
|
4278
5107
|
if (result.status === "fulfilled") continue;
|
|
4279
5108
|
item.attempts += 1;
|
|
4280
5109
|
if (item.attempts < this.config.maxRetries) {
|
|
5110
|
+
item.nextRetryAt = Date.now() + this.retryDelayMs(item.attempts);
|
|
4281
5111
|
this.buffer.push(item);
|
|
4282
5112
|
} else {
|
|
4283
5113
|
this.safeOnError(result.reason, item);
|
|
@@ -4285,6 +5115,15 @@ var GuideTrackerQueue = class {
|
|
|
4285
5115
|
}
|
|
4286
5116
|
this.flushing = false;
|
|
4287
5117
|
}
|
|
5118
|
+
/**
|
|
5119
|
+
* Backoff exponencial (base = intervalo de flush) con equal-jitter: mitad
|
|
5120
|
+
* fija + mitad aleatoria. Desincroniza los reintentos entre clientes ante
|
|
5121
|
+
* una caída del backend.
|
|
5122
|
+
*/
|
|
5123
|
+
retryDelayMs(attempts) {
|
|
5124
|
+
const base = Math.min(RETRY_BASE_MS * 2 ** (attempts - 1), RETRY_MAX_DELAY_MS2);
|
|
5125
|
+
return base / 2 + Math.random() * (base / 2);
|
|
5126
|
+
}
|
|
4288
5127
|
/**
|
|
4289
5128
|
* Drena el buffer vía `sendBeacon`. Síncrono, sin retry. Para pagehide.
|
|
4290
5129
|
*/
|
|
@@ -4331,6 +5170,7 @@ var GuideTrackerQueue = class {
|
|
|
4331
5170
|
};
|
|
4332
5171
|
|
|
4333
5172
|
// src/plugins/guides/guides-controller.ts
|
|
5173
|
+
init_badge_renderer();
|
|
4334
5174
|
init_banner_renderer();
|
|
4335
5175
|
init_custom_renderer();
|
|
4336
5176
|
init_form_renderer();
|
|
@@ -4458,6 +5298,7 @@ var WalkthroughStateManager = class {
|
|
|
4458
5298
|
};
|
|
4459
5299
|
|
|
4460
5300
|
// src/plugins/guides/guides-controller.ts
|
|
5301
|
+
var RESOLVE_CACHE_TTL_MS = 3e4;
|
|
4461
5302
|
var STATUS_BY_ACTION = {
|
|
4462
5303
|
shown: "shown",
|
|
4463
5304
|
dismissed: "dismissed",
|
|
@@ -4484,6 +5325,10 @@ var GuidesController = class {
|
|
|
4484
5325
|
*/
|
|
4485
5326
|
this.activeWalkthroughRenderer = null;
|
|
4486
5327
|
this.activeWalkthroughGuide = null;
|
|
5328
|
+
/** Última resolución cacheada (mismo `(endUserId, url)` → sin red). */
|
|
5329
|
+
this.resolveCache = null;
|
|
5330
|
+
/** Resolución idéntica en vuelo, para coalescer llamadas concurrentes. */
|
|
5331
|
+
this.inflightResolve = null;
|
|
4487
5332
|
this.debug = config.debug === true;
|
|
4488
5333
|
this.apiUrl = config.apiUrl;
|
|
4489
5334
|
this.apiKey = config.apiKey;
|
|
@@ -4535,7 +5380,7 @@ var GuidesController = class {
|
|
|
4535
5380
|
}
|
|
4536
5381
|
let guides;
|
|
4537
5382
|
try {
|
|
4538
|
-
guides = await this.
|
|
5383
|
+
guides = await this.resolveGuides(endUserId, pageUrl);
|
|
4539
5384
|
} catch (err) {
|
|
4540
5385
|
if (this.debug) console.error("[veo] guides resolver threw:", err);
|
|
4541
5386
|
return;
|
|
@@ -4562,6 +5407,36 @@ var GuidesController = class {
|
|
|
4562
5407
|
this.runSingleStepRender(guide, sessionId, pageUrl);
|
|
4563
5408
|
}
|
|
4564
5409
|
}
|
|
5410
|
+
/**
|
|
5411
|
+
* `resolve` con cache de corta duración + coalescing:
|
|
5412
|
+
* - Mismo `(endUserId, url)` resuelto hace < {@link RESOLVE_CACHE_TTL_MS} →
|
|
5413
|
+
* se reusa sin red. Colapsa el doble disparo identify+pageview y los
|
|
5414
|
+
* pageviews repetidos de una SPA hacia la misma URL.
|
|
5415
|
+
* - Si ya hay un resolve idéntico en vuelo, se espera ESE (no se abre otro).
|
|
5416
|
+
* El re-render sigue siendo idempotente (dedupe por `activeByGuideId` +
|
|
5417
|
+
* `frequencyCache`), así que reusar el resultado no duplica guías.
|
|
5418
|
+
*/
|
|
5419
|
+
async resolveGuides(endUserId, pageUrl) {
|
|
5420
|
+
const key = `${endUserId}
|
|
5421
|
+
${pageUrl}`;
|
|
5422
|
+
const now = Date.now();
|
|
5423
|
+
if (this.resolveCache?.key === key && now - this.resolveCache.at < RESOLVE_CACHE_TTL_MS) {
|
|
5424
|
+
if (this.debug) console.log("[veo] guides: resolve cache hit");
|
|
5425
|
+
return this.resolveCache.guides;
|
|
5426
|
+
}
|
|
5427
|
+
if (this.inflightResolve?.key === key) {
|
|
5428
|
+
return this.inflightResolve.promise;
|
|
5429
|
+
}
|
|
5430
|
+
const promise = this.resolver.resolve(endUserId, pageUrl);
|
|
5431
|
+
this.inflightResolve = { key, promise };
|
|
5432
|
+
try {
|
|
5433
|
+
const guides = await promise;
|
|
5434
|
+
this.resolveCache = { key, at: Date.now(), guides };
|
|
5435
|
+
return guides;
|
|
5436
|
+
} finally {
|
|
5437
|
+
if (this.inflightResolve?.key === key) this.inflightResolve = null;
|
|
5438
|
+
}
|
|
5439
|
+
}
|
|
4565
5440
|
/** Cierra todas las guías activas, limpia cache de dispatch y para timers. */
|
|
4566
5441
|
destroy() {
|
|
4567
5442
|
for (const renderer of this.activeRenderers) {
|
|
@@ -4582,6 +5457,8 @@ var GuidesController = class {
|
|
|
4582
5457
|
this.activeWalkthroughGuide = null;
|
|
4583
5458
|
}
|
|
4584
5459
|
this.dispatched.clear();
|
|
5460
|
+
this.resolveCache = null;
|
|
5461
|
+
this.inflightResolve = null;
|
|
4585
5462
|
this.trackerQueue.destroy();
|
|
4586
5463
|
}
|
|
4587
5464
|
runSingleStepRender(guide, sessionId, pageUrl) {
|
|
@@ -4806,7 +5683,8 @@ var GuidesController = class {
|
|
|
4806
5683
|
* por si el usuario se identificó después de que la guía apareció.
|
|
4807
5684
|
*/
|
|
4808
5685
|
handleInteraction(guide, sessionId, pageUrl, event) {
|
|
4809
|
-
const
|
|
5686
|
+
const buttonKey = event.metadata?.buttonId ? `:${event.metadata.buttonId}` : "";
|
|
5687
|
+
const dedupKey = `${guide.guideId}:${event.action}${buttonKey}`;
|
|
4810
5688
|
if (this.dispatched.has(dedupKey)) {
|
|
4811
5689
|
if (this.debug) console.log(`[veo] guides: dedup hit ${dedupKey}`);
|
|
4812
5690
|
return;
|
|
@@ -4828,7 +5706,8 @@ var GuidesController = class {
|
|
|
4828
5706
|
action: event.action,
|
|
4829
5707
|
stepIndex: event.stepIndex,
|
|
4830
5708
|
pageUrl,
|
|
4831
|
-
pagePath: extractPath(pageUrl)
|
|
5709
|
+
pagePath: extractPath(pageUrl),
|
|
5710
|
+
...event.metadata ? { metadata: event.metadata } : {}
|
|
4832
5711
|
}
|
|
4833
5712
|
});
|
|
4834
5713
|
}
|
|
@@ -4879,6 +5758,8 @@ var GuidesController = class {
|
|
|
4879
5758
|
return new FormRenderer();
|
|
4880
5759
|
case "inline-form":
|
|
4881
5760
|
return new InlineFormRenderer();
|
|
5761
|
+
case "badge":
|
|
5762
|
+
return new BadgeRenderer();
|
|
4882
5763
|
case "walkthrough":
|
|
4883
5764
|
return null;
|
|
4884
5765
|
}
|