veo-sdk 0.5.5 → 0.5.10
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-2TBF3NZF.mjs +5 -0
- package/dist/{builder-O2QHV2DV.mjs.map → builder-2TBF3NZF.mjs.map} +1 -1
- package/dist/builder.cjs +544 -75
- package/dist/builder.cjs.map +1 -1
- package/dist/builder.d.cts +2 -2
- package/dist/builder.d.ts +2 -2
- package/dist/builder.mjs +4 -4
- package/dist/{chunk-JPA46JBS.mjs → chunk-END325ZM.mjs} +547 -78
- package/dist/chunk-END325ZM.mjs.map +1 -0
- package/dist/{chunk-GKJW5TOZ.mjs → chunk-RO5IHBTJ.mjs} +123 -3
- package/dist/chunk-RO5IHBTJ.mjs.map +1 -0
- package/dist/{chunk-XPB4DL2Y.mjs → chunk-S2CLUAAC.mjs} +3 -3
- package/dist/{chunk-XPB4DL2Y.mjs.map → chunk-S2CLUAAC.mjs.map} +1 -1
- package/dist/{guide-preview-B912Y6UY.d.cts → guide-preview-XUnTmzUO.d.cts} +22 -4
- package/dist/{guide-preview-B912Y6UY.d.ts → guide-preview-XUnTmzUO.d.ts} +22 -4
- package/dist/index.cjs +691 -83
- 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 +39 -10
- package/dist/veo-builder.js.map +1 -1
- package/dist/veo-guides.js +34 -5
- package/dist/veo-guides.js.map +1 -1
- package/package.json +1 -1
- package/dist/builder-O2QHV2DV.mjs +0 -5
- package/dist/chunk-GKJW5TOZ.mjs.map +0 -1
- package/dist/chunk-JPA46JBS.mjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -654,7 +654,8 @@ function buildButtonBlock(block, doc, callbacks) {
|
|
|
654
654
|
btn.textContent = typeof block.text === "string" && block.text ? block.text : "OK";
|
|
655
655
|
applyBlockStyle(btn, block.style, "button");
|
|
656
656
|
btn.addEventListener("click", () => {
|
|
657
|
-
const
|
|
657
|
+
const raw = block.action ?? "dismiss";
|
|
658
|
+
const action = raw === "next" || raw === "url" || raw === "guide" ? raw : "dismiss";
|
|
658
659
|
const target = action === "url" && typeof block.url === "string" && isSafeUrl(block.url) ? block.url : action === "guide" && typeof block.targetGuideId === "string" && block.targetGuideId ? block.targetGuideId : void 0;
|
|
659
660
|
const meta = {
|
|
660
661
|
...typeof block.id === "string" && block.id ? { buttonId: block.id } : {},
|
|
@@ -695,6 +696,42 @@ function applyBlockStyle(el, style, kind) {
|
|
|
695
696
|
if (typeof s.marginBottom === "number" && Number.isFinite(s.marginBottom)) {
|
|
696
697
|
el.style.marginBottom = `${clampNum(s.marginBottom, 0, 64)}px`;
|
|
697
698
|
}
|
|
699
|
+
if (kind !== "image") {
|
|
700
|
+
if (typeof s.fontSize === "number" && Number.isFinite(s.fontSize)) {
|
|
701
|
+
el.style.fontSize = `${clampNum(s.fontSize, 8, 96)}px`;
|
|
702
|
+
}
|
|
703
|
+
if (typeof s.fontFamily === "string" && s.fontFamily && s.fontFamily !== "inherit") {
|
|
704
|
+
el.style.fontFamily = s.fontFamily;
|
|
705
|
+
}
|
|
706
|
+
if (typeof s.fontWeight === "string" && s.fontWeight) el.style.fontWeight = s.fontWeight;
|
|
707
|
+
if (typeof s.textTransform === "string" && s.textTransform && s.textTransform !== "none") {
|
|
708
|
+
el.style.textTransform = s.textTransform;
|
|
709
|
+
}
|
|
710
|
+
if (typeof s.lineHeight === "number" && Number.isFinite(s.lineHeight)) {
|
|
711
|
+
el.style.lineHeight = `${clampNum(s.lineHeight, 50, 300)}%`;
|
|
712
|
+
}
|
|
713
|
+
if (typeof s.letterSpacing === "number" && Number.isFinite(s.letterSpacing)) {
|
|
714
|
+
el.style.letterSpacing = `${clampNum(s.letterSpacing, -5, 20)}px`;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
if (typeof s.background === "string" && s.background) el.style.background = s.background;
|
|
718
|
+
if (typeof s.borderWidth === "number" && s.borderWidth > 0) {
|
|
719
|
+
const bc = typeof s.borderColor === "string" && s.borderColor ? s.borderColor : "#bbbbbb";
|
|
720
|
+
el.style.border = `${clampNum(s.borderWidth, 0, 20)}px solid ${bc}`;
|
|
721
|
+
}
|
|
722
|
+
if (kind !== "image" && typeof s.radius === "number" && Number.isFinite(s.radius)) {
|
|
723
|
+
el.style.borderRadius = `${clampNum(s.radius, 0, 64)}px`;
|
|
724
|
+
}
|
|
725
|
+
for (const side of ["Top", "Right", "Bottom", "Left"]) {
|
|
726
|
+
const p = s[`padding${side}`];
|
|
727
|
+
if (typeof p === "number" && Number.isFinite(p)) {
|
|
728
|
+
el.style.setProperty(`padding-${side.toLowerCase()}`, `${clampNum(p, 0, 128)}px`);
|
|
729
|
+
}
|
|
730
|
+
const m = s[`margin${side}`];
|
|
731
|
+
if (typeof m === "number" && Number.isFinite(m)) {
|
|
732
|
+
el.style.setProperty(`margin-${side.toLowerCase()}`, `${clampNum(m, 0, 128)}px`);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
698
735
|
}
|
|
699
736
|
function createCloseButton(doc, onClose, className = "veo-guide-close") {
|
|
700
737
|
const closeBtn = doc.createElement("button");
|
|
@@ -1075,6 +1112,35 @@ var init_styles = __esm({
|
|
|
1075
1112
|
animation: veo-fade-in 160ms ease-out;
|
|
1076
1113
|
}
|
|
1077
1114
|
|
|
1115
|
+
/* \u2500\u2500 Modelo unificado (composite) \u2500\u2500 */
|
|
1116
|
+
.veo-composite-overlay {
|
|
1117
|
+
position: fixed; inset: 0;
|
|
1118
|
+
z-index: ${GUIDE_Z_INDEX};
|
|
1119
|
+
pointer-events: none;
|
|
1120
|
+
}
|
|
1121
|
+
/* Backdrop atenuado (solo centro con backdrop activo). */
|
|
1122
|
+
.veo-composite-overlay--dim {
|
|
1123
|
+
background: rgba(0, 0, 0, 0.5);
|
|
1124
|
+
pointer-events: auto;
|
|
1125
|
+
}
|
|
1126
|
+
.veo-composite-anchored {
|
|
1127
|
+
position: fixed;
|
|
1128
|
+
z-index: ${GUIDE_Z_INDEX};
|
|
1129
|
+
}
|
|
1130
|
+
/* Card gen\xE9rica del modelo unificado. El dise\xF1o (bg/borde/padding/\u2026) lo aplica
|
|
1131
|
+
el renderer como estilos inline desde step.design; ac\xE1 van los defaults. */
|
|
1132
|
+
.veo-card {
|
|
1133
|
+
pointer-events: auto;
|
|
1134
|
+
background: var(--veo-bg, #ffffff);
|
|
1135
|
+
color: var(--veo-text, #1f2937);
|
|
1136
|
+
border-radius: var(--veo-radius, 12px);
|
|
1137
|
+
padding: 20px;
|
|
1138
|
+
max-width: 90vw;
|
|
1139
|
+
box-shadow: var(--veo-shadow, 0 12px 40px rgba(0, 0, 0, 0.18));
|
|
1140
|
+
animation: veo-fade-in 160ms ease-out;
|
|
1141
|
+
}
|
|
1142
|
+
.veo-composite-anchored .veo-card, .veo-composite-overlay .veo-card { position: relative; }
|
|
1143
|
+
|
|
1078
1144
|
.veo-guide-content {
|
|
1079
1145
|
position: relative;
|
|
1080
1146
|
display: flex; flex-direction: column;
|
|
@@ -2058,91 +2124,58 @@ var init_custom_frame = __esm({
|
|
|
2058
2124
|
MAX_HOST_CSS = 800 * 1024;
|
|
2059
2125
|
}
|
|
2060
2126
|
});
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
return;
|
|
2071
|
-
}
|
|
2072
|
-
const
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
}
|
|
2080
|
-
|
|
2081
|
-
|
|
2127
|
+
|
|
2128
|
+
// src/plugins/guides/code-block.ts
|
|
2129
|
+
function mountCodeBlock(block, doc, context) {
|
|
2130
|
+
if (block.sandboxed) {
|
|
2131
|
+
const step = { html: block.html ?? "", css: block.css, js: block.js };
|
|
2132
|
+
const { iframe, cleanup } = mountCustomFrame(doc, step, context, { width: "100%" });
|
|
2133
|
+
const wrapper = doc.createElement("div");
|
|
2134
|
+
wrapper.className = "veo-code-sandbox";
|
|
2135
|
+
wrapper.appendChild(iframe);
|
|
2136
|
+
return { node: wrapper, cleanup };
|
|
2137
|
+
}
|
|
2138
|
+
const hostDoc = doc;
|
|
2139
|
+
const injected = [];
|
|
2140
|
+
if (typeof block.css === "string" && block.css.trim()) {
|
|
2141
|
+
const style = hostDoc.createElement("style");
|
|
2142
|
+
style.textContent = block.css;
|
|
2143
|
+
hostDoc.head.appendChild(style);
|
|
2144
|
+
injected.push(style);
|
|
2145
|
+
}
|
|
2146
|
+
let node = null;
|
|
2147
|
+
if (typeof block.html === "string" && block.html.trim()) {
|
|
2148
|
+
node = doc.createElement("div");
|
|
2149
|
+
node.className = "veo-code-html";
|
|
2150
|
+
node.innerHTML = block.html;
|
|
2151
|
+
for (const old of Array.from(node.querySelectorAll("script"))) {
|
|
2152
|
+
injected.push(runScript(old, hostDoc));
|
|
2153
|
+
old.remove();
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
if (typeof block.js === "string" && block.js.trim()) {
|
|
2157
|
+
const s = hostDoc.createElement("script");
|
|
2158
|
+
s.textContent = block.js;
|
|
2159
|
+
hostDoc.body.appendChild(s);
|
|
2160
|
+
injected.push(s);
|
|
2082
2161
|
}
|
|
2162
|
+
return {
|
|
2163
|
+
node,
|
|
2164
|
+
cleanup: () => {
|
|
2165
|
+
for (const n of injected) n.parentNode?.removeChild(n);
|
|
2166
|
+
}
|
|
2167
|
+
};
|
|
2083
2168
|
}
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2169
|
+
function runScript(old, hostDoc) {
|
|
2170
|
+
const s = hostDoc.createElement("script");
|
|
2171
|
+
for (const attr of Array.from(old.attributes)) s.setAttribute(attr.name, attr.value);
|
|
2172
|
+
if (!old.src) s.textContent = old.textContent;
|
|
2173
|
+
hostDoc.body.appendChild(s);
|
|
2174
|
+
return s;
|
|
2175
|
+
}
|
|
2176
|
+
var init_code_block = __esm({
|
|
2177
|
+
"src/plugins/guides/code-block.ts"() {
|
|
2089
2178
|
init_custom_frame();
|
|
2090
|
-
DEFAULT_PLACEMENT = { mode: "floating", position: "bottom-right" };
|
|
2091
|
-
DEFAULT_OFFSET = 16;
|
|
2092
|
-
DEFAULT_WIDTH = 360;
|
|
2093
|
-
CustomRenderer = class extends BaseRenderer {
|
|
2094
|
-
async render(context) {
|
|
2095
|
-
const step = context.guide.guideSteps[0];
|
|
2096
|
-
if (!step || typeof step.html !== "string" || step.html.length === 0) return;
|
|
2097
|
-
const placement = step.placement ?? DEFAULT_PLACEMENT;
|
|
2098
|
-
let anchor = null;
|
|
2099
|
-
if (placement.mode === "anchored") {
|
|
2100
|
-
const selector = placement.selector ?? step.selector ?? context.guide.activationRules.selector;
|
|
2101
|
-
if (typeof selector !== "string" || selector.length === 0) return;
|
|
2102
|
-
anchor = await waitForElement(selector);
|
|
2103
|
-
if (!anchor) return;
|
|
2104
|
-
}
|
|
2105
|
-
const { root } = this.createHost();
|
|
2106
|
-
const doc = root.ownerDocument ?? document;
|
|
2107
|
-
const wrapper = doc.createElement("div");
|
|
2108
|
-
wrapper.className = placement.mode === "anchored" ? "veo-custom-anchored" : "veo-custom-floating";
|
|
2109
|
-
const { iframe, cleanup } = mountCustomFrame(doc, step, context, {
|
|
2110
|
-
width: toCssSize(placement.width ?? DEFAULT_WIDTH),
|
|
2111
|
-
fixedHeight: placement.height ?? null
|
|
2112
|
-
});
|
|
2113
|
-
this.registerCleanup(cleanup);
|
|
2114
|
-
wrapper.appendChild(iframe);
|
|
2115
|
-
root.appendChild(wrapper);
|
|
2116
|
-
if (placement.mode === "floating") {
|
|
2117
|
-
applyFloatingPosition(wrapper, placement);
|
|
2118
|
-
} else if (anchor) {
|
|
2119
|
-
this.registerCleanup(await this.setupAnchoredPosition(anchor, wrapper, placement));
|
|
2120
|
-
}
|
|
2121
|
-
context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
|
|
2122
|
-
}
|
|
2123
|
-
async setupAnchoredPosition(anchor, wrapper, placement) {
|
|
2124
|
-
const side = placement.side ?? "bottom";
|
|
2125
|
-
const update = async () => {
|
|
2126
|
-
const { x, y } = await dom.computePosition(anchor, wrapper, {
|
|
2127
|
-
placement: side,
|
|
2128
|
-
strategy: "fixed",
|
|
2129
|
-
middleware: [dom.offset(placement.offsetY ?? 8), dom.flip(), dom.shift({ padding: 8 })]
|
|
2130
|
-
});
|
|
2131
|
-
wrapper.style.left = `${x}px`;
|
|
2132
|
-
wrapper.style.top = `${y}px`;
|
|
2133
|
-
};
|
|
2134
|
-
await update();
|
|
2135
|
-
const reposition = () => {
|
|
2136
|
-
void update();
|
|
2137
|
-
};
|
|
2138
|
-
window.addEventListener("scroll", reposition, true);
|
|
2139
|
-
window.addEventListener("resize", reposition);
|
|
2140
|
-
return () => {
|
|
2141
|
-
window.removeEventListener("scroll", reposition, true);
|
|
2142
|
-
window.removeEventListener("resize", reposition);
|
|
2143
|
-
};
|
|
2144
|
-
}
|
|
2145
|
-
};
|
|
2146
2179
|
}
|
|
2147
2180
|
});
|
|
2148
2181
|
|
|
@@ -2415,6 +2448,448 @@ var init_form_content = __esm({
|
|
|
2415
2448
|
init_block_builder();
|
|
2416
2449
|
}
|
|
2417
2450
|
});
|
|
2451
|
+
function inlinePosOf(p) {
|
|
2452
|
+
return p === "before" || p === "prepend" || p === "append" ? p : "after";
|
|
2453
|
+
}
|
|
2454
|
+
function applyViewportPosition(card, position, design) {
|
|
2455
|
+
const s = card.style;
|
|
2456
|
+
s.position = "fixed";
|
|
2457
|
+
const M = "16px";
|
|
2458
|
+
const hAlign = design.hAlign === "left" ? "left" : design.hAlign === "right" ? "right" : null;
|
|
2459
|
+
const v = position.includes("top") ? "top" : position.includes("bottom") ? "bottom" : "middle";
|
|
2460
|
+
const h = hAlign ?? (position.includes("left") ? "left" : position.includes("right") ? "right" : "middle");
|
|
2461
|
+
if (v === "top") s.top = M;
|
|
2462
|
+
else if (v === "bottom") s.bottom = M;
|
|
2463
|
+
else s.top = "50%";
|
|
2464
|
+
if (h === "left") s.left = M;
|
|
2465
|
+
else if (h === "right") s.right = M;
|
|
2466
|
+
else s.left = "50%";
|
|
2467
|
+
const tx = h === "middle" ? "-50%" : "0";
|
|
2468
|
+
const ty = v === "middle" ? "-50%" : "0";
|
|
2469
|
+
s.transform = tx === "0" && ty === "0" ? "" : `translate(${tx}, ${ty})`;
|
|
2470
|
+
}
|
|
2471
|
+
function makeCaret(doc, design) {
|
|
2472
|
+
const caret = doc.createElement("div");
|
|
2473
|
+
caret.className = "veo-composite-caret";
|
|
2474
|
+
const w = typeof design.caretWidth === "number" ? design.caretWidth : 12;
|
|
2475
|
+
const h = typeof design.caretHeight === "number" ? design.caretHeight : 12;
|
|
2476
|
+
const s = caret.style;
|
|
2477
|
+
s.position = "absolute";
|
|
2478
|
+
s.width = `${w}px`;
|
|
2479
|
+
s.height = `${h}px`;
|
|
2480
|
+
s.transform = "rotate(45deg)";
|
|
2481
|
+
s.background = typeof design.background === "string" ? design.background : "#ffffff";
|
|
2482
|
+
if (typeof design.borderWidth === "number" && design.borderWidth > 0) {
|
|
2483
|
+
const bc = typeof design.borderColor === "string" ? design.borderColor : "#bbbbbb";
|
|
2484
|
+
s.border = `${design.borderWidth}px solid ${bc}`;
|
|
2485
|
+
}
|
|
2486
|
+
return caret;
|
|
2487
|
+
}
|
|
2488
|
+
function liftTarget(anchor, zIndex) {
|
|
2489
|
+
const el = anchor;
|
|
2490
|
+
const prevPosition = el.style.position;
|
|
2491
|
+
const prevZ = el.style.zIndex;
|
|
2492
|
+
if (getComputedStyle(el).position === "static") el.style.position = "relative";
|
|
2493
|
+
el.style.zIndex = String(zIndex);
|
|
2494
|
+
return () => {
|
|
2495
|
+
el.style.position = prevPosition;
|
|
2496
|
+
el.style.zIndex = prevZ;
|
|
2497
|
+
};
|
|
2498
|
+
}
|
|
2499
|
+
async function anchorPosition(anchor, wrapper, caret) {
|
|
2500
|
+
const update = async () => {
|
|
2501
|
+
const { x, y, placement, middlewareData } = await dom.computePosition(anchor, wrapper, {
|
|
2502
|
+
placement: "bottom",
|
|
2503
|
+
strategy: "fixed",
|
|
2504
|
+
middleware: [
|
|
2505
|
+
dom.offset(caret ? 10 : 8),
|
|
2506
|
+
dom.flip(),
|
|
2507
|
+
dom.shift({ padding: 8 }),
|
|
2508
|
+
...caret ? [dom.arrow({ element: caret })] : []
|
|
2509
|
+
]
|
|
2510
|
+
});
|
|
2511
|
+
wrapper.style.left = `${x}px`;
|
|
2512
|
+
wrapper.style.top = `${y}px`;
|
|
2513
|
+
if (caret) positionArrow(caret, placement, middlewareData.arrow);
|
|
2514
|
+
};
|
|
2515
|
+
await update();
|
|
2516
|
+
const reposition = () => void update();
|
|
2517
|
+
window.addEventListener("scroll", reposition, true);
|
|
2518
|
+
window.addEventListener("resize", reposition);
|
|
2519
|
+
return () => {
|
|
2520
|
+
window.removeEventListener("scroll", reposition, true);
|
|
2521
|
+
window.removeEventListener("resize", reposition);
|
|
2522
|
+
};
|
|
2523
|
+
}
|
|
2524
|
+
function applyCompositeDesign(card, design) {
|
|
2525
|
+
const s = card.style;
|
|
2526
|
+
const px = (v) => typeof v === "number" && Number.isFinite(v) ? `${v}px` : null;
|
|
2527
|
+
const strv = (v) => typeof v === "string" && v ? v : null;
|
|
2528
|
+
if (strv(design.background)) s.background = design.background;
|
|
2529
|
+
const textColor = strv(design.color) ?? strv(design.textColor);
|
|
2530
|
+
if (textColor) s.color = textColor;
|
|
2531
|
+
if (px(design.fontSize)) s.fontSize = px(design.fontSize);
|
|
2532
|
+
if (strv(design.fontWeight)) s.fontWeight = design.fontWeight;
|
|
2533
|
+
if (strv(design.fontFamily) && design.fontFamily !== "inherit") {
|
|
2534
|
+
s.fontFamily = design.fontFamily;
|
|
2535
|
+
}
|
|
2536
|
+
if (strv(design.textTransform) && design.textTransform !== "none") {
|
|
2537
|
+
s.textTransform = design.textTransform;
|
|
2538
|
+
}
|
|
2539
|
+
if (typeof design.letterSpacing === "number") s.letterSpacing = `${design.letterSpacing}px`;
|
|
2540
|
+
if (strv(design.align)) s.textAlign = design.align;
|
|
2541
|
+
if (typeof design.lineHeight === "number") s.lineHeight = `${design.lineHeight}%`;
|
|
2542
|
+
if (px(design.radius)) s.borderRadius = px(design.radius);
|
|
2543
|
+
const bw = px(design.borderWidth);
|
|
2544
|
+
if (bw) s.border = `${bw} solid ${strv(design.borderColor) ?? "#e5e5e5"}`;
|
|
2545
|
+
if (strv(design.shadowColor) || typeof design.shadowBlur === "number") {
|
|
2546
|
+
const n = (k) => typeof design[k] === "number" ? design[k] : 0;
|
|
2547
|
+
s.boxShadow = `${n("shadowX")}px ${n("shadowY")}px ${n("shadowBlur")}px ${n("shadowSpread")}px ${strv(design.shadowColor) ?? "rgba(0,0,0,0.2)"}`;
|
|
2548
|
+
}
|
|
2549
|
+
if (design.fullWidth === true) {
|
|
2550
|
+
s.width = "100%";
|
|
2551
|
+
} else if (design.widthType === "fluid") {
|
|
2552
|
+
s.width = "auto";
|
|
2553
|
+
if (px(design.width)) s.maxWidth = px(design.width);
|
|
2554
|
+
} else if (px(design.width)) {
|
|
2555
|
+
s.width = px(design.width);
|
|
2556
|
+
}
|
|
2557
|
+
for (const side of ["Top", "Right", "Bottom", "Left"]) {
|
|
2558
|
+
const p = px(design[`padding${side}`]);
|
|
2559
|
+
if (p) s.setProperty(`padding-${side.toLowerCase()}`, p);
|
|
2560
|
+
const m = px(design[`margin${side}`]);
|
|
2561
|
+
if (m) s.setProperty(`margin-${side.toLowerCase()}`, m);
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
var CompositeRenderer;
|
|
2565
|
+
var init_composite_renderer = __esm({
|
|
2566
|
+
"src/plugins/guides/renderers/composite-renderer.ts"() {
|
|
2567
|
+
init_block_builder();
|
|
2568
|
+
init_code_block();
|
|
2569
|
+
init_form_content();
|
|
2570
|
+
init_inline_host();
|
|
2571
|
+
init_wait_for_element();
|
|
2572
|
+
init_base_renderer();
|
|
2573
|
+
init_floating_arrow();
|
|
2574
|
+
CompositeRenderer = class extends BaseRenderer {
|
|
2575
|
+
async render(context) {
|
|
2576
|
+
const idx = context.nav?.stepIndex ?? 0;
|
|
2577
|
+
const step = context.guide.guideSteps[idx];
|
|
2578
|
+
if (!step) return;
|
|
2579
|
+
const anchorSelector = step.position === "relative-to-element" || step.presentation === "inline" ? step.anchorSelector?.trim() || void 0 : void 0;
|
|
2580
|
+
let anchor = null;
|
|
2581
|
+
if (anchorSelector) {
|
|
2582
|
+
anchor = await waitForElement(anchorSelector);
|
|
2583
|
+
if (!anchor) return;
|
|
2584
|
+
}
|
|
2585
|
+
const { host, root } = this.createHost();
|
|
2586
|
+
const doc = root.ownerDocument ?? document;
|
|
2587
|
+
const design = step.design ?? {};
|
|
2588
|
+
if (typeof design.zIndex === "number") host.style.zIndex = String(design.zIndex);
|
|
2589
|
+
const card = doc.createElement("div");
|
|
2590
|
+
card.className = "veo-card";
|
|
2591
|
+
applyCompositeDesign(card, design);
|
|
2592
|
+
const dismiss = (action, meta) => {
|
|
2593
|
+
context.onInteraction({
|
|
2594
|
+
guideId: context.guide.guideId,
|
|
2595
|
+
stepIndex: idx,
|
|
2596
|
+
action,
|
|
2597
|
+
...{}
|
|
2598
|
+
});
|
|
2599
|
+
};
|
|
2600
|
+
const readers = [];
|
|
2601
|
+
this.buildContent(card, step.blocks ?? [], doc, context, idx, readers);
|
|
2602
|
+
if (design.closeButton !== false) {
|
|
2603
|
+
const closeBtn = createCloseButton(doc, () => {
|
|
2604
|
+
dismiss("dismissed");
|
|
2605
|
+
this.closeOrSkip(context);
|
|
2606
|
+
});
|
|
2607
|
+
const closeColor = typeof design.closeColor === "string" ? design.closeColor : null;
|
|
2608
|
+
const hoverColor = typeof design.closeHoverColor === "string" ? design.closeHoverColor : null;
|
|
2609
|
+
if (closeColor) closeBtn.style.color = closeColor;
|
|
2610
|
+
if (hoverColor) {
|
|
2611
|
+
closeBtn.addEventListener("mouseenter", () => {
|
|
2612
|
+
closeBtn.style.color = hoverColor;
|
|
2613
|
+
});
|
|
2614
|
+
closeBtn.addEventListener("mouseleave", () => {
|
|
2615
|
+
closeBtn.style.color = closeColor ?? "";
|
|
2616
|
+
});
|
|
2617
|
+
}
|
|
2618
|
+
card.appendChild(closeBtn);
|
|
2619
|
+
}
|
|
2620
|
+
if (step.presentation === "inline" && anchor) {
|
|
2621
|
+
host.style.display = "block";
|
|
2622
|
+
const pos = inlinePosOf(step.inlinePosition);
|
|
2623
|
+
root.appendChild(card);
|
|
2624
|
+
insertHost(anchor, host, pos);
|
|
2625
|
+
this.registerCleanup(keepHostAttached(host, anchorSelector, pos));
|
|
2626
|
+
} else if (step.position === "relative-to-element" && anchor) {
|
|
2627
|
+
if (design.backdrop === true) {
|
|
2628
|
+
const bg = doc.createElement("div");
|
|
2629
|
+
bg.className = "veo-composite-overlay veo-composite-overlay--dim";
|
|
2630
|
+
if (typeof design.backdropColor === "string") bg.style.background = design.backdropColor;
|
|
2631
|
+
const bz = typeof design.backdropZIndex === "number" ? design.backdropZIndex : 2e5;
|
|
2632
|
+
bg.style.zIndex = String(bz);
|
|
2633
|
+
root.appendChild(bg);
|
|
2634
|
+
if (design.targetAbove === true) this.registerCleanup(liftTarget(anchor, bz + 1));
|
|
2635
|
+
}
|
|
2636
|
+
const wrapper = doc.createElement("div");
|
|
2637
|
+
wrapper.className = "veo-composite-anchored";
|
|
2638
|
+
const caretEl = design.caret === false ? null : makeCaret(doc, design);
|
|
2639
|
+
if (caretEl) wrapper.appendChild(caretEl);
|
|
2640
|
+
wrapper.appendChild(card);
|
|
2641
|
+
root.appendChild(wrapper);
|
|
2642
|
+
this.registerCleanup(await anchorPosition(anchor, wrapper, caretEl));
|
|
2643
|
+
} else {
|
|
2644
|
+
const overlay = doc.createElement("div");
|
|
2645
|
+
overlay.className = "veo-composite-overlay";
|
|
2646
|
+
if (step.position === "center" && design.backdrop !== false) {
|
|
2647
|
+
overlay.classList.add("veo-composite-overlay--dim");
|
|
2648
|
+
if (typeof design.backdropColor === "string") {
|
|
2649
|
+
overlay.style.background = design.backdropColor;
|
|
2650
|
+
}
|
|
2651
|
+
if (typeof design.backdropZIndex === "number") {
|
|
2652
|
+
overlay.style.zIndex = String(design.backdropZIndex);
|
|
2653
|
+
}
|
|
2654
|
+
overlay.addEventListener("click", (e) => {
|
|
2655
|
+
if (e.target === overlay) {
|
|
2656
|
+
dismiss("dismissed");
|
|
2657
|
+
this.closeOrSkip(context);
|
|
2658
|
+
}
|
|
2659
|
+
});
|
|
2660
|
+
}
|
|
2661
|
+
applyViewportPosition(card, step.position, design);
|
|
2662
|
+
overlay.appendChild(card);
|
|
2663
|
+
root.appendChild(overlay);
|
|
2664
|
+
}
|
|
2665
|
+
context.onInteraction({ guideId: context.guide.guideId, stepIndex: idx, action: "shown" });
|
|
2666
|
+
}
|
|
2667
|
+
/** Recorre los bloques en orden y los pinta en la card. */
|
|
2668
|
+
buildContent(card, blocks, doc, context, idx, readers) {
|
|
2669
|
+
const emit = (block) => {
|
|
2670
|
+
context.onInteraction({
|
|
2671
|
+
guideId: context.guide.guideId,
|
|
2672
|
+
stepIndex: idx,
|
|
2673
|
+
action: "cta_clicked",
|
|
2674
|
+
...block.id ? { metadata: { buttonId: block.id } } : {}
|
|
2675
|
+
});
|
|
2676
|
+
};
|
|
2677
|
+
let i = 0;
|
|
2678
|
+
while (i < blocks.length) {
|
|
2679
|
+
const b = blocks[i];
|
|
2680
|
+
if (!b) {
|
|
2681
|
+
i++;
|
|
2682
|
+
continue;
|
|
2683
|
+
}
|
|
2684
|
+
if (b.type === "button") {
|
|
2685
|
+
const row = doc.createElement("div");
|
|
2686
|
+
row.className = "veo-guide-actions";
|
|
2687
|
+
while (i < blocks.length && blocks[i]?.type === "button") {
|
|
2688
|
+
row.appendChild(
|
|
2689
|
+
this.buildButton(blocks[i], doc, context, idx, readers, emit)
|
|
2690
|
+
);
|
|
2691
|
+
i++;
|
|
2692
|
+
}
|
|
2693
|
+
card.appendChild(row);
|
|
2694
|
+
continue;
|
|
2695
|
+
}
|
|
2696
|
+
if (b.type === "code") {
|
|
2697
|
+
const { node: node2, cleanup } = mountCodeBlock(b, doc, context);
|
|
2698
|
+
if (node2) card.appendChild(node2);
|
|
2699
|
+
this.registerCleanup(cleanup);
|
|
2700
|
+
i++;
|
|
2701
|
+
continue;
|
|
2702
|
+
}
|
|
2703
|
+
if (b.type === "form") {
|
|
2704
|
+
const form = doc.createElement("div");
|
|
2705
|
+
form.className = "veo-form";
|
|
2706
|
+
for (const field of b.fields ?? []) {
|
|
2707
|
+
const { wrapper, read } = buildField(field, doc);
|
|
2708
|
+
readers.push({ field, read });
|
|
2709
|
+
form.appendChild(wrapper);
|
|
2710
|
+
}
|
|
2711
|
+
card.appendChild(form);
|
|
2712
|
+
i++;
|
|
2713
|
+
continue;
|
|
2714
|
+
}
|
|
2715
|
+
const node = buildContentBlock(b, doc);
|
|
2716
|
+
if (node) card.appendChild(node);
|
|
2717
|
+
i++;
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
/** Botón con enrutado de acción (navegación / cierre / url / guía / envío de forms). */
|
|
2721
|
+
buildButton(block, doc, context, idx, readers, emit) {
|
|
2722
|
+
const btn = doc.createElement("button");
|
|
2723
|
+
btn.type = "button";
|
|
2724
|
+
btn.className = "veo-guide-cta";
|
|
2725
|
+
btn.textContent = block.text?.trim() || "OK";
|
|
2726
|
+
const nav = context.nav?.callbacks;
|
|
2727
|
+
btn.addEventListener("click", () => {
|
|
2728
|
+
switch (block.action) {
|
|
2729
|
+
case "next":
|
|
2730
|
+
emit(block);
|
|
2731
|
+
if (nav) nav.onNext();
|
|
2732
|
+
else context.onClose();
|
|
2733
|
+
break;
|
|
2734
|
+
case "prev":
|
|
2735
|
+
emit(block);
|
|
2736
|
+
nav?.onBack();
|
|
2737
|
+
break;
|
|
2738
|
+
case "url":
|
|
2739
|
+
emit(block);
|
|
2740
|
+
if (typeof block.url === "string" && isSafeUrl(block.url)) {
|
|
2741
|
+
window.open(block.url, "_blank", "noopener,noreferrer");
|
|
2742
|
+
}
|
|
2743
|
+
break;
|
|
2744
|
+
case "guide":
|
|
2745
|
+
emit(block);
|
|
2746
|
+
if (block.targetGuideId) context.onOpenGuide?.(block.targetGuideId);
|
|
2747
|
+
this.closeOrSkip(context);
|
|
2748
|
+
break;
|
|
2749
|
+
case "submit-forms":
|
|
2750
|
+
case "submit-forms-advance":
|
|
2751
|
+
case "submit-forms-goto":
|
|
2752
|
+
this.submitForms(readers, context, idx, btn, () => {
|
|
2753
|
+
emit(block);
|
|
2754
|
+
if (block.action === "submit-forms" || !nav) {
|
|
2755
|
+
context.onClose();
|
|
2756
|
+
} else if (block.action === "submit-forms-goto" && typeof block.targetStep === "number" && nav.onGoto) {
|
|
2757
|
+
nav.onGoto(block.targetStep);
|
|
2758
|
+
} else {
|
|
2759
|
+
nav.onNext();
|
|
2760
|
+
}
|
|
2761
|
+
});
|
|
2762
|
+
break;
|
|
2763
|
+
default:
|
|
2764
|
+
emit(block);
|
|
2765
|
+
this.closeOrSkip(context);
|
|
2766
|
+
}
|
|
2767
|
+
});
|
|
2768
|
+
return btn;
|
|
2769
|
+
}
|
|
2770
|
+
/** Recoge respuestas de todos los form-blocks del paso, valida y envía. */
|
|
2771
|
+
submitForms(readers, context, idx, btn, onDone) {
|
|
2772
|
+
const answers = {};
|
|
2773
|
+
for (const { field, read } of readers) {
|
|
2774
|
+
const value = read();
|
|
2775
|
+
const empty = value === void 0 || value === null || value === "";
|
|
2776
|
+
if (empty) {
|
|
2777
|
+
if (field.required) {
|
|
2778
|
+
btn.textContent = `Completa "${field.label}"`;
|
|
2779
|
+
return;
|
|
2780
|
+
}
|
|
2781
|
+
continue;
|
|
2782
|
+
}
|
|
2783
|
+
answers[field.key] = value;
|
|
2784
|
+
}
|
|
2785
|
+
if (readers.length > 0 && Object.keys(answers).length === 0) return;
|
|
2786
|
+
btn.disabled = true;
|
|
2787
|
+
const submitFn = context.onFormSubmit ?? (() => Promise.resolve(true));
|
|
2788
|
+
void submitFn(answers).then((ok) => {
|
|
2789
|
+
btn.disabled = false;
|
|
2790
|
+
if (!ok) return;
|
|
2791
|
+
context.onInteraction({
|
|
2792
|
+
guideId: context.guide.guideId,
|
|
2793
|
+
stepIndex: idx,
|
|
2794
|
+
action: "completed"
|
|
2795
|
+
});
|
|
2796
|
+
onDone();
|
|
2797
|
+
});
|
|
2798
|
+
}
|
|
2799
|
+
closeOrSkip(context) {
|
|
2800
|
+
if (context.nav?.callbacks) context.nav.callbacks.onSkip();
|
|
2801
|
+
else context.onClose();
|
|
2802
|
+
}
|
|
2803
|
+
};
|
|
2804
|
+
}
|
|
2805
|
+
});
|
|
2806
|
+
function applyFloatingPosition(wrapper, placement) {
|
|
2807
|
+
const position = placement.position ?? "bottom-right";
|
|
2808
|
+
const ox = `${placement.offsetX ?? DEFAULT_OFFSET}px`;
|
|
2809
|
+
const oy = `${placement.offsetY ?? DEFAULT_OFFSET}px`;
|
|
2810
|
+
const s = wrapper.style;
|
|
2811
|
+
if (position === "center") {
|
|
2812
|
+
s.top = "50%";
|
|
2813
|
+
s.left = "50%";
|
|
2814
|
+
s.transform = "translate(-50%, -50%)";
|
|
2815
|
+
return;
|
|
2816
|
+
}
|
|
2817
|
+
const [vertical, horizontal] = position.split("-");
|
|
2818
|
+
if (vertical === "top") s.top = oy;
|
|
2819
|
+
else s.bottom = oy;
|
|
2820
|
+
if (horizontal === "left") {
|
|
2821
|
+
s.left = ox;
|
|
2822
|
+
} else if (horizontal === "right") {
|
|
2823
|
+
s.right = ox;
|
|
2824
|
+
} else {
|
|
2825
|
+
s.left = "50%";
|
|
2826
|
+
s.transform = "translateX(-50%)";
|
|
2827
|
+
}
|
|
2828
|
+
}
|
|
2829
|
+
var DEFAULT_PLACEMENT, DEFAULT_OFFSET, DEFAULT_WIDTH, CustomRenderer;
|
|
2830
|
+
var init_custom_renderer = __esm({
|
|
2831
|
+
"src/plugins/guides/renderers/custom-renderer.ts"() {
|
|
2832
|
+
init_wait_for_element();
|
|
2833
|
+
init_base_renderer();
|
|
2834
|
+
init_custom_frame();
|
|
2835
|
+
DEFAULT_PLACEMENT = { mode: "floating", position: "bottom-right" };
|
|
2836
|
+
DEFAULT_OFFSET = 16;
|
|
2837
|
+
DEFAULT_WIDTH = 360;
|
|
2838
|
+
CustomRenderer = class extends BaseRenderer {
|
|
2839
|
+
async render(context) {
|
|
2840
|
+
const step = context.guide.guideSteps[0];
|
|
2841
|
+
if (!step || typeof step.html !== "string" || step.html.length === 0) return;
|
|
2842
|
+
const placement = step.placement ?? DEFAULT_PLACEMENT;
|
|
2843
|
+
let anchor = null;
|
|
2844
|
+
if (placement.mode === "anchored") {
|
|
2845
|
+
const selector = placement.selector ?? step.selector ?? context.guide.activationRules.selector;
|
|
2846
|
+
if (typeof selector !== "string" || selector.length === 0) return;
|
|
2847
|
+
anchor = await waitForElement(selector);
|
|
2848
|
+
if (!anchor) return;
|
|
2849
|
+
}
|
|
2850
|
+
const { root } = this.createHost();
|
|
2851
|
+
const doc = root.ownerDocument ?? document;
|
|
2852
|
+
const wrapper = doc.createElement("div");
|
|
2853
|
+
wrapper.className = placement.mode === "anchored" ? "veo-custom-anchored" : "veo-custom-floating";
|
|
2854
|
+
const { iframe, cleanup } = mountCustomFrame(doc, step, context, {
|
|
2855
|
+
width: toCssSize(placement.width ?? DEFAULT_WIDTH),
|
|
2856
|
+
fixedHeight: placement.height ?? null
|
|
2857
|
+
});
|
|
2858
|
+
this.registerCleanup(cleanup);
|
|
2859
|
+
wrapper.appendChild(iframe);
|
|
2860
|
+
root.appendChild(wrapper);
|
|
2861
|
+
if (placement.mode === "floating") {
|
|
2862
|
+
applyFloatingPosition(wrapper, placement);
|
|
2863
|
+
} else if (anchor) {
|
|
2864
|
+
this.registerCleanup(await this.setupAnchoredPosition(anchor, wrapper, placement));
|
|
2865
|
+
}
|
|
2866
|
+
context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
|
|
2867
|
+
}
|
|
2868
|
+
async setupAnchoredPosition(anchor, wrapper, placement) {
|
|
2869
|
+
const side = placement.side ?? "bottom";
|
|
2870
|
+
const update = async () => {
|
|
2871
|
+
const { x, y } = await dom.computePosition(anchor, wrapper, {
|
|
2872
|
+
placement: side,
|
|
2873
|
+
strategy: "fixed",
|
|
2874
|
+
middleware: [dom.offset(placement.offsetY ?? 8), dom.flip(), dom.shift({ padding: 8 })]
|
|
2875
|
+
});
|
|
2876
|
+
wrapper.style.left = `${x}px`;
|
|
2877
|
+
wrapper.style.top = `${y}px`;
|
|
2878
|
+
};
|
|
2879
|
+
await update();
|
|
2880
|
+
const reposition = () => {
|
|
2881
|
+
void update();
|
|
2882
|
+
};
|
|
2883
|
+
window.addEventListener("scroll", reposition, true);
|
|
2884
|
+
window.addEventListener("resize", reposition);
|
|
2885
|
+
return () => {
|
|
2886
|
+
window.removeEventListener("scroll", reposition, true);
|
|
2887
|
+
window.removeEventListener("resize", reposition);
|
|
2888
|
+
};
|
|
2889
|
+
}
|
|
2890
|
+
};
|
|
2891
|
+
}
|
|
2892
|
+
});
|
|
2418
2893
|
|
|
2419
2894
|
// src/plugins/guides/renderers/form-renderer.ts
|
|
2420
2895
|
var FormRenderer;
|
|
@@ -2880,8 +3355,12 @@ function createSingleStepRenderer(type) {
|
|
|
2880
3355
|
return new InlineFormRenderer();
|
|
2881
3356
|
case "badge":
|
|
2882
3357
|
return new BadgeRenderer();
|
|
3358
|
+
case "composite":
|
|
3359
|
+
return new CompositeRenderer();
|
|
2883
3360
|
case "walkthrough":
|
|
2884
3361
|
return null;
|
|
3362
|
+
default:
|
|
3363
|
+
return null;
|
|
2885
3364
|
}
|
|
2886
3365
|
}
|
|
2887
3366
|
function safeDestroy(renderer) {
|
|
@@ -2897,6 +3376,7 @@ var init_guide_preview = __esm({
|
|
|
2897
3376
|
init_badge_renderer();
|
|
2898
3377
|
init_banner_renderer();
|
|
2899
3378
|
init_base_renderer();
|
|
3379
|
+
init_composite_renderer();
|
|
2900
3380
|
init_custom_renderer();
|
|
2901
3381
|
init_form_renderer();
|
|
2902
3382
|
init_inline_custom_renderer();
|
|
@@ -2930,6 +3410,13 @@ var init_guide_preview = __esm({
|
|
|
2930
3410
|
this.input = input;
|
|
2931
3411
|
const guide = normalize(input);
|
|
2932
3412
|
const start = typeof input.startStepIndex === "number" ? input.startStepIndex : 0;
|
|
3413
|
+
if (guide.guideType === "composite") {
|
|
3414
|
+
const s = Math.min(Math.max(0, start), guide.guideSteps.length - 1);
|
|
3415
|
+
const active2 = guide.guideSteps[s];
|
|
3416
|
+
if (active2) guide.guideSteps = [active2];
|
|
3417
|
+
const ready2 = this.renderSingleStep(guide);
|
|
3418
|
+
return { close: () => this.close(), ready: ready2, host: () => this.activeHost() };
|
|
3419
|
+
}
|
|
2933
3420
|
const ready = guide.guideType === "walkthrough" ? this.startWalkthrough(guide, start) : this.renderSingleStep(guide);
|
|
2934
3421
|
return { close: () => this.close(), ready, host: () => this.activeHost() };
|
|
2935
3422
|
}
|
|
@@ -5434,6 +5921,7 @@ var GuideTrackerQueue = class {
|
|
|
5434
5921
|
// src/plugins/guides/guides-controller.ts
|
|
5435
5922
|
init_badge_renderer();
|
|
5436
5923
|
init_banner_renderer();
|
|
5924
|
+
init_composite_renderer();
|
|
5437
5925
|
init_custom_renderer();
|
|
5438
5926
|
init_form_renderer();
|
|
5439
5927
|
init_inline_custom_renderer();
|
|
@@ -5443,6 +5931,26 @@ init_modal_renderer();
|
|
|
5443
5931
|
init_tooltip_renderer();
|
|
5444
5932
|
init_walkthrough_renderer();
|
|
5445
5933
|
|
|
5934
|
+
// src/plugins/guides/url-match.ts
|
|
5935
|
+
function matchesUrl(matcher, pageUrl) {
|
|
5936
|
+
switch (matcher.type) {
|
|
5937
|
+
case "exact":
|
|
5938
|
+
return matcher.pattern === pageUrl;
|
|
5939
|
+
case "prefix":
|
|
5940
|
+
return pageUrl.startsWith(matcher.pattern);
|
|
5941
|
+
case "regex":
|
|
5942
|
+
try {
|
|
5943
|
+
return new RegExp(matcher.pattern).test(pageUrl);
|
|
5944
|
+
} catch {
|
|
5945
|
+
return false;
|
|
5946
|
+
}
|
|
5947
|
+
}
|
|
5948
|
+
}
|
|
5949
|
+
function stepMatchesPage(loc, pageUrl) {
|
|
5950
|
+
if (!loc || loc.scope === "sitewide" || !loc.pattern) return true;
|
|
5951
|
+
return matchesUrl({ type: loc.matchType ?? "prefix", pattern: loc.pattern }, pageUrl);
|
|
5952
|
+
}
|
|
5953
|
+
|
|
5446
5954
|
// src/plugins/guides/walkthrough-state.ts
|
|
5447
5955
|
init_constants2();
|
|
5448
5956
|
var WalkthroughStateManager = class {
|
|
@@ -5597,6 +6105,12 @@ var GuidesController = class {
|
|
|
5597
6105
|
* resolve fresco manda) y se limpian en `destroy()`.
|
|
5598
6106
|
*/
|
|
5599
6107
|
this.armedTriggers = /* @__PURE__ */ new Map();
|
|
6108
|
+
/**
|
|
6109
|
+
* Tour composite ESPERANDO navegación: el usuario avanzó a un paso cuya
|
|
6110
|
+
* `pageLocation` apunta a otra página. Se resume en `checkGuides` cuando llega
|
|
6111
|
+
* a esa página (tours multi-página estilo Pendo).
|
|
6112
|
+
*/
|
|
6113
|
+
this.pendingComposite = null;
|
|
5600
6114
|
this.debug = config.debug === true;
|
|
5601
6115
|
this.apiUrl = config.apiUrl;
|
|
5602
6116
|
this.apiKey = config.apiKey;
|
|
@@ -5646,6 +6160,7 @@ var GuidesController = class {
|
|
|
5646
6160
|
if (await this.tryResumeWalkthrough(endUserId, sessionId, pageUrl)) {
|
|
5647
6161
|
return;
|
|
5648
6162
|
}
|
|
6163
|
+
this.tryResumeComposite(sessionId, pageUrl);
|
|
5649
6164
|
let guides;
|
|
5650
6165
|
try {
|
|
5651
6166
|
guides = await this.resolveGuides(endUserId, pageUrl);
|
|
@@ -5687,6 +6202,10 @@ var GuidesController = class {
|
|
|
5687
6202
|
await this.startWalkthrough(guide, endUserId, sessionId, pageUrl);
|
|
5688
6203
|
return;
|
|
5689
6204
|
}
|
|
6205
|
+
if (guide.guideType === "composite") {
|
|
6206
|
+
this.startComposite(guide, sessionId, pageUrl);
|
|
6207
|
+
return;
|
|
6208
|
+
}
|
|
5690
6209
|
this.runSingleStepRender(guide, sessionId, pageUrl);
|
|
5691
6210
|
}
|
|
5692
6211
|
/**
|
|
@@ -5794,6 +6313,91 @@ ${pageUrl}`;
|
|
|
5794
6313
|
delayMs
|
|
5795
6314
|
);
|
|
5796
6315
|
}
|
|
6316
|
+
/**
|
|
6317
|
+
* Camino del modelo unificado (`composite`). Elige el PRIMER paso cuya
|
|
6318
|
+
* `pageLocation` matchea la URL actual (targeting por-paso) y lo pinta con el
|
|
6319
|
+
* `CompositeRenderer`. Si ningún paso aplica a esta página, no muestra nada.
|
|
6320
|
+
*/
|
|
6321
|
+
startComposite(guide, sessionId, pageUrl) {
|
|
6322
|
+
if (this.pendingComposite?.guide.guideId === guide.guideId) return;
|
|
6323
|
+
const steps = guide.guideSteps;
|
|
6324
|
+
const start = steps.findIndex((s) => stepMatchesPage(s.pageLocation, pageUrl));
|
|
6325
|
+
if (start === -1) return;
|
|
6326
|
+
this.renderCompositeStep(guide, start, sessionId, pageUrl);
|
|
6327
|
+
}
|
|
6328
|
+
/**
|
|
6329
|
+
* Resume un tour composite que quedó esperando navegación: si el paso
|
|
6330
|
+
* pendiente aplica a la página actual, lo pinta. Devuelve `true` si resumió.
|
|
6331
|
+
*/
|
|
6332
|
+
tryResumeComposite(sessionId, pageUrl) {
|
|
6333
|
+
const pending = this.pendingComposite;
|
|
6334
|
+
if (!pending) return false;
|
|
6335
|
+
const steps = pending.guide.guideSteps;
|
|
6336
|
+
const step = steps[pending.stepIndex];
|
|
6337
|
+
if (!step) {
|
|
6338
|
+
this.pendingComposite = null;
|
|
6339
|
+
return false;
|
|
6340
|
+
}
|
|
6341
|
+
if (!stepMatchesPage(step.pageLocation, pageUrl)) return false;
|
|
6342
|
+
this.pendingComposite = null;
|
|
6343
|
+
this.renderCompositeStep(pending.guide, pending.stepIndex, sessionId, pageUrl);
|
|
6344
|
+
return true;
|
|
6345
|
+
}
|
|
6346
|
+
/**
|
|
6347
|
+
* Pinta UN paso composite. En guías multi-paso pasa `nav` al renderer para
|
|
6348
|
+
* que los botones next/prev avancen (misma página); en single-step los
|
|
6349
|
+
* botones cierran. Cada avance destruye el paso anterior y monta el siguiente.
|
|
6350
|
+
*/
|
|
6351
|
+
renderCompositeStep(guide, idx, sessionId, pageUrl) {
|
|
6352
|
+
const steps = guide.guideSteps;
|
|
6353
|
+
const renderer = new CompositeRenderer();
|
|
6354
|
+
this.activeRenderers.add(renderer);
|
|
6355
|
+
this.activeByGuideId.set(guide.guideId, renderer);
|
|
6356
|
+
const cleanup = () => {
|
|
6357
|
+
renderer.destroy();
|
|
6358
|
+
this.activeRenderers.delete(renderer);
|
|
6359
|
+
if (this.activeByGuideId.get(guide.guideId) === renderer) {
|
|
6360
|
+
this.activeByGuideId.delete(guide.guideId);
|
|
6361
|
+
}
|
|
6362
|
+
};
|
|
6363
|
+
const goToStep = (target) => {
|
|
6364
|
+
cleanup();
|
|
6365
|
+
if (target < 0 || target >= steps.length) return;
|
|
6366
|
+
const step = steps[target];
|
|
6367
|
+
if (step && stepMatchesPage(step.pageLocation, pageUrl)) {
|
|
6368
|
+
this.renderCompositeStep(guide, target, sessionId, pageUrl);
|
|
6369
|
+
} else {
|
|
6370
|
+
this.pendingComposite = { guide, stepIndex: target };
|
|
6371
|
+
}
|
|
6372
|
+
};
|
|
6373
|
+
const nav = steps.length > 1 ? {
|
|
6374
|
+
stepIndex: idx,
|
|
6375
|
+
totalSteps: steps.length,
|
|
6376
|
+
callbacks: {
|
|
6377
|
+
onNext: () => goToStep(idx + 1),
|
|
6378
|
+
onBack: () => goToStep(idx - 1),
|
|
6379
|
+
onGoto: (target) => goToStep(target),
|
|
6380
|
+
onSkip: cleanup,
|
|
6381
|
+
onComplete: cleanup
|
|
6382
|
+
}
|
|
6383
|
+
} : void 0;
|
|
6384
|
+
try {
|
|
6385
|
+
void renderer.render({
|
|
6386
|
+
guide,
|
|
6387
|
+
onInteraction: (e) => this.handleInteraction(guide, sessionId, pageUrl, e),
|
|
6388
|
+
onClose: cleanup,
|
|
6389
|
+
onTrack: (name, props) => this.client.track(name, props),
|
|
6390
|
+
onFormSubmit: (answers) => this.submitFormResponse(guide.guideId, answers),
|
|
6391
|
+
onOpenGuide: (targetGuideId) => {
|
|
6392
|
+
void this.openGuideById(targetGuideId, sessionId, pageUrl);
|
|
6393
|
+
},
|
|
6394
|
+
...nav ? { nav } : {}
|
|
6395
|
+
});
|
|
6396
|
+
} catch (err) {
|
|
6397
|
+
if (this.debug) console.error("[veo] composite renderer threw:", err);
|
|
6398
|
+
cleanup();
|
|
6399
|
+
}
|
|
6400
|
+
}
|
|
5797
6401
|
mountSingleStepRenderer(guide, renderer, sessionId, pageUrl) {
|
|
5798
6402
|
this.activeRenderers.add(renderer);
|
|
5799
6403
|
this.activeByGuideId.set(guide.guideId, renderer);
|
|
@@ -6108,8 +6712,12 @@ ${pageUrl}`;
|
|
|
6108
6712
|
return new InlineFormRenderer();
|
|
6109
6713
|
case "badge":
|
|
6110
6714
|
return new BadgeRenderer();
|
|
6715
|
+
case "composite":
|
|
6716
|
+
return new CompositeRenderer();
|
|
6111
6717
|
case "walkthrough":
|
|
6112
6718
|
return null;
|
|
6719
|
+
default:
|
|
6720
|
+
return null;
|
|
6113
6721
|
}
|
|
6114
6722
|
}
|
|
6115
6723
|
/**
|