veo-sdk 0.3.5 → 0.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/builder-7MBMYL26.mjs +5 -0
- package/dist/{builder-FBNBSKCL.mjs.map → builder-7MBMYL26.mjs.map} +1 -1
- package/dist/builder.cjs +502 -151
- 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-GHP7ZJCW.mjs → chunk-4VJIILXF.mjs} +504 -153
- package/dist/chunk-4VJIILXF.mjs.map +1 -0
- package/dist/{chunk-TAZXRQYY.mjs → chunk-734ORVMM.mjs} +3 -3
- package/dist/{chunk-TAZXRQYY.mjs.map → chunk-734ORVMM.mjs.map} +1 -1
- package/dist/{chunk-R4E3LIDB.mjs → chunk-RNHOBE2D.mjs} +34 -4
- package/dist/chunk-RNHOBE2D.mjs.map +1 -0
- package/dist/{guide-preview-CO5ShtNf.d.cts → guide-preview-DewCTa-B.d.cts} +26 -1
- package/dist/{guide-preview-CO5ShtNf.d.ts → guide-preview-DewCTa-B.d.ts} +26 -1
- package/dist/index.cjs +552 -158
- 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 +49 -16
- package/dist/veo-builder.js.map +1 -1
- package/dist/veo-guides.js +44 -11
- package/dist/veo-guides.js.map +1 -1
- package/dist/veo.js +2 -2
- package/dist/veo.js.map +1 -1
- package/package.json +1 -1
- package/dist/builder-FBNBSKCL.mjs +0 -5
- package/dist/chunk-GHP7ZJCW.mjs.map +0 -1
- package/dist/chunk-R4E3LIDB.mjs.map +0 -1
package/dist/builder.cjs
CHANGED
|
@@ -19,6 +19,8 @@ var BUILDER_GUIDE_PARAM = "veoGuide";
|
|
|
19
19
|
|
|
20
20
|
// src/core/builder-token.ts
|
|
21
21
|
var CTX_KEY = "veo:builder_ctx";
|
|
22
|
+
var LS_PREFIX = "veo:builder_ctx:";
|
|
23
|
+
var PERSIST_TTL_MS = 12 * 60 * 60 * 1e3;
|
|
22
24
|
function session() {
|
|
23
25
|
try {
|
|
24
26
|
return window.sessionStorage;
|
|
@@ -26,6 +28,59 @@ function session() {
|
|
|
26
28
|
return null;
|
|
27
29
|
}
|
|
28
30
|
}
|
|
31
|
+
function local() {
|
|
32
|
+
try {
|
|
33
|
+
return window.localStorage;
|
|
34
|
+
} catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function lsKey(guide) {
|
|
39
|
+
return LS_PREFIX + (guide || "default");
|
|
40
|
+
}
|
|
41
|
+
function persist(ctx) {
|
|
42
|
+
try {
|
|
43
|
+
session()?.setItem(CTX_KEY, JSON.stringify(ctx));
|
|
44
|
+
} catch {
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const stored = { ...ctx, savedAt: Date.now() };
|
|
48
|
+
local()?.setItem(lsKey(ctx.guide), JSON.stringify(stored));
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function readFreshFromLocal() {
|
|
53
|
+
const store = local();
|
|
54
|
+
if (!store) return null;
|
|
55
|
+
let best = null;
|
|
56
|
+
try {
|
|
57
|
+
for (let i = 0; i < store.length; i++) {
|
|
58
|
+
const key = store.key(i);
|
|
59
|
+
if (!key?.startsWith(LS_PREFIX)) continue;
|
|
60
|
+
const raw = store.getItem(key);
|
|
61
|
+
if (!raw) continue;
|
|
62
|
+
let parsed = null;
|
|
63
|
+
try {
|
|
64
|
+
parsed = JSON.parse(raw);
|
|
65
|
+
} catch {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (typeof parsed?.savedAt !== "number" || Date.now() - parsed.savedAt > PERSIST_TTL_MS) {
|
|
69
|
+
try {
|
|
70
|
+
store.removeItem(key);
|
|
71
|
+
} catch {
|
|
72
|
+
}
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (!best || parsed.savedAt > best.savedAt) best = parsed;
|
|
76
|
+
}
|
|
77
|
+
} catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
if (!best) return null;
|
|
81
|
+
const { savedAt: _savedAt, ...ctx } = best;
|
|
82
|
+
return ctx;
|
|
83
|
+
}
|
|
29
84
|
function urlParams() {
|
|
30
85
|
try {
|
|
31
86
|
return new URLSearchParams(window.location.search);
|
|
@@ -45,25 +100,39 @@ function resolveBuilderContext() {
|
|
|
45
100
|
guide: params?.get(BUILDER_GUIDE_PARAM) ?? null,
|
|
46
101
|
panelPath: params?.get(BUILDER_PANEL_PATH_PARAM) ?? null
|
|
47
102
|
};
|
|
48
|
-
|
|
49
|
-
session()?.setItem(CTX_KEY, JSON.stringify(ctx));
|
|
50
|
-
} catch {
|
|
51
|
-
}
|
|
103
|
+
persist(ctx);
|
|
52
104
|
return ctx;
|
|
53
105
|
}
|
|
54
106
|
try {
|
|
55
107
|
const raw = session()?.getItem(CTX_KEY);
|
|
56
|
-
|
|
108
|
+
if (raw) return JSON.parse(raw);
|
|
57
109
|
} catch {
|
|
58
|
-
return null;
|
|
59
110
|
}
|
|
111
|
+
const fromLocal = readFreshFromLocal();
|
|
112
|
+
if (fromLocal) {
|
|
113
|
+
try {
|
|
114
|
+
session()?.setItem(CTX_KEY, JSON.stringify(fromLocal));
|
|
115
|
+
} catch {
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return fromLocal;
|
|
60
119
|
}
|
|
61
120
|
function clearBuilderToken() {
|
|
62
121
|
if (!hasWindow()) return;
|
|
122
|
+
let guide = null;
|
|
123
|
+
try {
|
|
124
|
+
const raw = session()?.getItem(CTX_KEY);
|
|
125
|
+
if (raw) guide = JSON.parse(raw).guide ?? null;
|
|
126
|
+
} catch {
|
|
127
|
+
}
|
|
63
128
|
try {
|
|
64
129
|
session()?.removeItem(CTX_KEY);
|
|
65
130
|
} catch {
|
|
66
131
|
}
|
|
132
|
+
try {
|
|
133
|
+
local()?.removeItem(lsKey(guide));
|
|
134
|
+
} catch {
|
|
135
|
+
}
|
|
67
136
|
}
|
|
68
137
|
|
|
69
138
|
// src/plugins/guides/constants.ts
|
|
@@ -77,6 +146,16 @@ var WALKTHROUGH_STEP_SELECTOR_TIMEOUT_MS = 5e3;
|
|
|
77
146
|
function buildStepContent(step, doc, callbacks) {
|
|
78
147
|
const container = doc.createElement("div");
|
|
79
148
|
container.className = "veo-guide-content";
|
|
149
|
+
const blocks = normalizeBlocks(step);
|
|
150
|
+
if (blocks) {
|
|
151
|
+
renderBlocks(container, blocks, doc, callbacks);
|
|
152
|
+
} else {
|
|
153
|
+
renderLegacyStep(container, step, doc, callbacks);
|
|
154
|
+
}
|
|
155
|
+
container.appendChild(createCloseButton(doc, () => callbacks.onDismiss()));
|
|
156
|
+
return container;
|
|
157
|
+
}
|
|
158
|
+
function renderLegacyStep(container, step, doc, callbacks) {
|
|
80
159
|
if (typeof step.imageUrl === "string" && step.imageUrl && isSafeUrl(step.imageUrl)) {
|
|
81
160
|
const img = doc.createElement("img");
|
|
82
161
|
img.className = "veo-guide-image";
|
|
@@ -111,14 +190,119 @@ function buildStepContent(step, doc, callbacks) {
|
|
|
111
190
|
actions.appendChild(cta);
|
|
112
191
|
}
|
|
113
192
|
container.appendChild(actions);
|
|
193
|
+
}
|
|
194
|
+
function normalizeBlocks(step) {
|
|
195
|
+
if (!Array.isArray(step.blocks) || step.blocks.length === 0) return null;
|
|
196
|
+
const valid = step.blocks.filter((b) => {
|
|
197
|
+
if (!b || typeof b !== "object") return false;
|
|
198
|
+
if (b.type === "image") return typeof b.url === "string" && isSafeUrl(b.url);
|
|
199
|
+
return b.type === "title" || b.type === "text" || b.type === "button";
|
|
200
|
+
});
|
|
201
|
+
return valid.length > 0 ? valid : null;
|
|
202
|
+
}
|
|
203
|
+
function renderBlocks(container, blocks, doc, callbacks) {
|
|
204
|
+
let i = 0;
|
|
205
|
+
while (i < blocks.length) {
|
|
206
|
+
const block = blocks[i];
|
|
207
|
+
if (block?.type === "button") {
|
|
208
|
+
const actions = doc.createElement("div");
|
|
209
|
+
actions.className = "veo-guide-actions";
|
|
210
|
+
while (i < blocks.length && blocks[i]?.type === "button") {
|
|
211
|
+
const btnBlock = blocks[i];
|
|
212
|
+
actions.appendChild(buildButtonBlock(btnBlock, doc, callbacks));
|
|
213
|
+
i++;
|
|
214
|
+
}
|
|
215
|
+
container.appendChild(actions);
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
const node = buildContentBlock(block, doc);
|
|
219
|
+
if (node) container.appendChild(node);
|
|
220
|
+
i++;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
function buildContentBlock(block, doc) {
|
|
224
|
+
switch (block.type) {
|
|
225
|
+
case "title": {
|
|
226
|
+
const el = doc.createElement("h2");
|
|
227
|
+
el.className = "veo-guide-title";
|
|
228
|
+
el.textContent = typeof block.text === "string" ? block.text : "";
|
|
229
|
+
applyBlockStyle(el, block.style, "text");
|
|
230
|
+
return el;
|
|
231
|
+
}
|
|
232
|
+
case "text": {
|
|
233
|
+
const el = doc.createElement("p");
|
|
234
|
+
el.className = "veo-guide-text";
|
|
235
|
+
el.textContent = typeof block.text === "string" ? block.text : "";
|
|
236
|
+
applyBlockStyle(el, block.style, "text");
|
|
237
|
+
return el;
|
|
238
|
+
}
|
|
239
|
+
case "image": {
|
|
240
|
+
if (typeof block.url !== "string" || !isSafeUrl(block.url)) return null;
|
|
241
|
+
const el = doc.createElement("img");
|
|
242
|
+
el.className = "veo-guide-image";
|
|
243
|
+
el.src = block.url;
|
|
244
|
+
el.alt = "";
|
|
245
|
+
applyBlockStyle(el, block.style, "image");
|
|
246
|
+
return el;
|
|
247
|
+
}
|
|
248
|
+
default:
|
|
249
|
+
return null;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
function buildButtonBlock(block, doc, callbacks) {
|
|
253
|
+
const btn = doc.createElement("button");
|
|
254
|
+
btn.type = "button";
|
|
255
|
+
btn.className = "veo-guide-cta";
|
|
256
|
+
btn.textContent = typeof block.text === "string" && block.text ? block.text : "OK";
|
|
257
|
+
applyBlockStyle(btn, block.style, "button");
|
|
258
|
+
btn.addEventListener("click", () => {
|
|
259
|
+
const action = block.action ?? "dismiss";
|
|
260
|
+
const url = action === "url" && typeof block.url === "string" && isSafeUrl(block.url) ? block.url : void 0;
|
|
261
|
+
callbacks.onCtaClick(action, url);
|
|
262
|
+
});
|
|
263
|
+
return btn;
|
|
264
|
+
}
|
|
265
|
+
var BLOCK_ALIGN_SELF = {
|
|
266
|
+
left: "flex-start",
|
|
267
|
+
center: "center",
|
|
268
|
+
right: "flex-end"
|
|
269
|
+
};
|
|
270
|
+
function clampNum(n, min, max) {
|
|
271
|
+
return Math.min(max, Math.max(min, n));
|
|
272
|
+
}
|
|
273
|
+
function applyBlockStyle(el, style, kind) {
|
|
274
|
+
if (!style || typeof style !== "object") return;
|
|
275
|
+
const s = style;
|
|
276
|
+
const align = typeof s.align === "string" ? s.align : null;
|
|
277
|
+
if (align && (align === "left" || align === "center" || align === "right")) {
|
|
278
|
+
if (kind === "text") el.style.textAlign = align;
|
|
279
|
+
else el.style.alignSelf = BLOCK_ALIGN_SELF[align];
|
|
280
|
+
}
|
|
281
|
+
if (kind !== "image" && typeof s.color === "string") el.style.color = s.color;
|
|
282
|
+
if (kind === "text" && typeof s.fontSize === "number" && Number.isFinite(s.fontSize)) {
|
|
283
|
+
el.style.fontSize = `${clampNum(s.fontSize, 8, 72)}px`;
|
|
284
|
+
}
|
|
285
|
+
if (kind === "image" && typeof s.width === "number" && Number.isFinite(s.width)) {
|
|
286
|
+
el.style.width = `${clampNum(s.width, 10, 100)}%`;
|
|
287
|
+
}
|
|
288
|
+
if (kind === "image" && typeof s.radius === "number" && Number.isFinite(s.radius)) {
|
|
289
|
+
el.style.borderRadius = `${clampNum(s.radius, 0, 48)}px`;
|
|
290
|
+
}
|
|
291
|
+
if (typeof s.marginTop === "number" && Number.isFinite(s.marginTop)) {
|
|
292
|
+
el.style.marginTop = `${clampNum(s.marginTop, 0, 64)}px`;
|
|
293
|
+
}
|
|
294
|
+
if (typeof s.marginBottom === "number" && Number.isFinite(s.marginBottom)) {
|
|
295
|
+
el.style.marginBottom = `${clampNum(s.marginBottom, 0, 64)}px`;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
function createCloseButton(doc, onClose, className = "veo-guide-close") {
|
|
114
299
|
const closeBtn = doc.createElement("button");
|
|
115
300
|
closeBtn.type = "button";
|
|
116
|
-
closeBtn.className =
|
|
301
|
+
closeBtn.className = className;
|
|
117
302
|
closeBtn.setAttribute("aria-label", "Cerrar");
|
|
118
303
|
closeBtn.textContent = "\xD7";
|
|
119
|
-
closeBtn.addEventListener("click",
|
|
120
|
-
|
|
121
|
-
return container;
|
|
304
|
+
closeBtn.addEventListener("click", onClose);
|
|
305
|
+
return closeBtn;
|
|
122
306
|
}
|
|
123
307
|
function isSafeUrl(url) {
|
|
124
308
|
if (typeof url !== "string" || url.length === 0) return false;
|
|
@@ -136,6 +320,78 @@ function readCtaUrl(step) {
|
|
|
136
320
|
return isSafeUrl(candidate) ? candidate : void 0;
|
|
137
321
|
}
|
|
138
322
|
|
|
323
|
+
// src/plugins/guides/walkthrough-block-builder.ts
|
|
324
|
+
function buildWalkthroughStepContent(step, stepIndex, totalSteps, doc, callbacks) {
|
|
325
|
+
const container = doc.createElement("div");
|
|
326
|
+
container.className = "veo-guide-content";
|
|
327
|
+
const counter = doc.createElement("div");
|
|
328
|
+
counter.className = "veo-walkthrough-counter";
|
|
329
|
+
counter.textContent = `Paso ${stepIndex + 1} de ${totalSteps}`;
|
|
330
|
+
container.appendChild(counter);
|
|
331
|
+
const progress = doc.createElement("div");
|
|
332
|
+
progress.className = "veo-walkthrough-progress";
|
|
333
|
+
for (let i = 0; i < totalSteps; i++) {
|
|
334
|
+
const dot = doc.createElement("span");
|
|
335
|
+
dot.className = "veo-walkthrough-progress-dot";
|
|
336
|
+
if (i < stepIndex) dot.classList.add("completed");
|
|
337
|
+
if (i === stepIndex) dot.classList.add("active");
|
|
338
|
+
progress.appendChild(dot);
|
|
339
|
+
}
|
|
340
|
+
container.appendChild(progress);
|
|
341
|
+
if (typeof step.imageUrl === "string" && step.imageUrl && isSafeUrl(step.imageUrl)) {
|
|
342
|
+
const img = doc.createElement("img");
|
|
343
|
+
img.className = "veo-guide-image";
|
|
344
|
+
img.src = step.imageUrl;
|
|
345
|
+
img.alt = typeof step.title === "string" ? step.title : "";
|
|
346
|
+
container.appendChild(img);
|
|
347
|
+
}
|
|
348
|
+
if (typeof step.title === "string" && step.title) {
|
|
349
|
+
const heading = doc.createElement("h2");
|
|
350
|
+
heading.className = "veo-guide-title";
|
|
351
|
+
heading.textContent = step.title;
|
|
352
|
+
container.appendChild(heading);
|
|
353
|
+
}
|
|
354
|
+
if (typeof step.content === "string" && step.content) {
|
|
355
|
+
const paragraph = doc.createElement("p");
|
|
356
|
+
paragraph.className = "veo-guide-text";
|
|
357
|
+
paragraph.textContent = step.content;
|
|
358
|
+
container.appendChild(paragraph);
|
|
359
|
+
}
|
|
360
|
+
const actions = doc.createElement("div");
|
|
361
|
+
actions.className = "veo-walkthrough-actions";
|
|
362
|
+
const skipBtn = doc.createElement("button");
|
|
363
|
+
skipBtn.type = "button";
|
|
364
|
+
skipBtn.className = "veo-walkthrough-skip";
|
|
365
|
+
skipBtn.textContent = "Omitir";
|
|
366
|
+
skipBtn.addEventListener("click", () => callbacks.onSkip());
|
|
367
|
+
actions.appendChild(skipBtn);
|
|
368
|
+
const rightGroup = doc.createElement("div");
|
|
369
|
+
rightGroup.className = "veo-walkthrough-actions-right";
|
|
370
|
+
if (stepIndex > 0) {
|
|
371
|
+
const backBtn = doc.createElement("button");
|
|
372
|
+
backBtn.type = "button";
|
|
373
|
+
backBtn.className = "veo-walkthrough-btn-secondary";
|
|
374
|
+
backBtn.textContent = "Atr\xE1s";
|
|
375
|
+
backBtn.addEventListener("click", () => callbacks.onBack());
|
|
376
|
+
rightGroup.appendChild(backBtn);
|
|
377
|
+
}
|
|
378
|
+
const isLastStep = stepIndex === totalSteps - 1;
|
|
379
|
+
const primaryBtn = doc.createElement("button");
|
|
380
|
+
primaryBtn.type = "button";
|
|
381
|
+
primaryBtn.className = "veo-guide-cta";
|
|
382
|
+
const defaultLabel = isLastStep ? "Finalizar" : "Siguiente";
|
|
383
|
+
primaryBtn.textContent = typeof step.ctaText === "string" && step.ctaText ? step.ctaText : defaultLabel;
|
|
384
|
+
primaryBtn.addEventListener("click", () => {
|
|
385
|
+
if (isLastStep) callbacks.onComplete();
|
|
386
|
+
else callbacks.onNext();
|
|
387
|
+
});
|
|
388
|
+
rightGroup.appendChild(primaryBtn);
|
|
389
|
+
actions.appendChild(rightGroup);
|
|
390
|
+
container.appendChild(actions);
|
|
391
|
+
container.appendChild(createCloseButton(doc, () => callbacks.onSkip()));
|
|
392
|
+
return container;
|
|
393
|
+
}
|
|
394
|
+
|
|
139
395
|
// src/plugins/guides/guide-design.ts
|
|
140
396
|
var DARK_THEME = {
|
|
141
397
|
"--veo-bg": "#1f2937",
|
|
@@ -148,6 +404,16 @@ var ALIGN_JUSTIFY = {
|
|
|
148
404
|
center: "center",
|
|
149
405
|
right: "flex-end"
|
|
150
406
|
};
|
|
407
|
+
var ALIGN_SELF = {
|
|
408
|
+
left: "flex-start",
|
|
409
|
+
center: "center",
|
|
410
|
+
right: "flex-end"
|
|
411
|
+
};
|
|
412
|
+
var ELEMENT_ALIGN_MODE = {
|
|
413
|
+
title: "text",
|
|
414
|
+
text: "text",
|
|
415
|
+
image: "self"
|
|
416
|
+
};
|
|
151
417
|
var SHADOW = {
|
|
152
418
|
none: "none",
|
|
153
419
|
soft: "0 4px 14px rgba(0, 0, 0, 0.10)",
|
|
@@ -175,6 +441,9 @@ function applyDesignVars(host, style) {
|
|
|
175
441
|
if (typeof s.accentColor === "string" && COLOR_RE.test(s.accentColor.trim())) {
|
|
176
442
|
host.style.setProperty("--veo-primary", s.accentColor.trim());
|
|
177
443
|
}
|
|
444
|
+
if (typeof s.fontFamily === "string" && s.fontFamily.length <= 200 && !/[<>{}]/.test(s.fontFamily)) {
|
|
445
|
+
host.style.setProperty("--veo-font", s.fontFamily.trim());
|
|
446
|
+
}
|
|
178
447
|
if (s.theme === "dark") {
|
|
179
448
|
for (const [key, value] of Object.entries(DARK_THEME)) {
|
|
180
449
|
host.style.setProperty(key, value);
|
|
@@ -216,6 +485,43 @@ function applyDesignVars(host, style) {
|
|
|
216
485
|
host.style.setProperty("--veo-pos-x", `${px * 100}%`);
|
|
217
486
|
host.style.setProperty("--veo-pos-y", `${py * 100}%`);
|
|
218
487
|
}
|
|
488
|
+
if (s.elements && typeof s.elements === "object") {
|
|
489
|
+
const el = s.elements;
|
|
490
|
+
applyElementVars(host, "title", el.title);
|
|
491
|
+
applyElementVars(host, "text", el.text);
|
|
492
|
+
applyElementVars(host, "image", el.image);
|
|
493
|
+
const cta = el.cta;
|
|
494
|
+
if (cta && typeof cta.align === "string" && ALIGN_JUSTIFY[cta.align]) {
|
|
495
|
+
host.style.setProperty("--veo-actions-justify", ALIGN_JUSTIFY[cta.align]);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
function applyElementVars(host, prefix, raw) {
|
|
500
|
+
if (!raw || typeof raw !== "object") return;
|
|
501
|
+
const el = raw;
|
|
502
|
+
if (typeof el.align === "string" && (el.align === "left" || el.align === "center" || el.align === "right")) {
|
|
503
|
+
const mode = ELEMENT_ALIGN_MODE[prefix] ?? "text";
|
|
504
|
+
const value = mode === "self" ? ALIGN_SELF[el.align] : el.align;
|
|
505
|
+
host.style.setProperty(`--veo-${prefix}-align`, value);
|
|
506
|
+
}
|
|
507
|
+
if (typeof el.color === "string" && COLOR_RE.test(el.color.trim())) {
|
|
508
|
+
host.style.setProperty(`--veo-${prefix}-color`, el.color.trim());
|
|
509
|
+
}
|
|
510
|
+
if (typeof el.fontSize === "number" && Number.isFinite(el.fontSize)) {
|
|
511
|
+
host.style.setProperty(`--veo-${prefix}-size`, `${clamp(el.fontSize, 8, 72)}px`);
|
|
512
|
+
}
|
|
513
|
+
if (typeof el.width === "number" && Number.isFinite(el.width)) {
|
|
514
|
+
host.style.setProperty(`--veo-${prefix}-width`, `${clamp(el.width, 10, 100)}%`);
|
|
515
|
+
}
|
|
516
|
+
if (typeof el.radius === "number" && Number.isFinite(el.radius)) {
|
|
517
|
+
host.style.setProperty(`--veo-${prefix}-radius`, `${clamp(el.radius, 0, 48)}px`);
|
|
518
|
+
}
|
|
519
|
+
if (typeof el.marginTop === "number" && Number.isFinite(el.marginTop)) {
|
|
520
|
+
host.style.setProperty(`--veo-${prefix}-mt`, `${clamp(el.marginTop, 0, 64)}px`);
|
|
521
|
+
}
|
|
522
|
+
if (typeof el.marginBottom === "number" && Number.isFinite(el.marginBottom)) {
|
|
523
|
+
host.style.setProperty(`--veo-${prefix}-mb`, `${clamp(el.marginBottom, 0, 64)}px`);
|
|
524
|
+
}
|
|
219
525
|
}
|
|
220
526
|
|
|
221
527
|
// src/plugins/guides/styles.ts
|
|
@@ -231,7 +537,7 @@ var GUIDE_STYLES = `
|
|
|
231
537
|
--veo-actions-justify: flex-end;
|
|
232
538
|
all: initial;
|
|
233
539
|
}
|
|
234
|
-
* { box-sizing: border-box; font-family: -apple-system, system-ui, "Segoe UI", Roboto, sans-serif; }
|
|
540
|
+
* { box-sizing: border-box; font-family: var(--veo-font, -apple-system, system-ui, "Segoe UI", Roboto, sans-serif); }
|
|
235
541
|
|
|
236
542
|
.veo-modal-overlay {
|
|
237
543
|
position: fixed; inset: 0;
|
|
@@ -282,6 +588,15 @@ var GUIDE_STYLES = `
|
|
|
282
588
|
box-shadow: var(--veo-shadow, 0 8px 30px rgba(0, 0, 0, 0.18));
|
|
283
589
|
animation: veo-fade-in 150ms ease-out;
|
|
284
590
|
}
|
|
591
|
+
.veo-tooltip-arrow {
|
|
592
|
+
position: absolute;
|
|
593
|
+
width: 12px;
|
|
594
|
+
height: 12px;
|
|
595
|
+
background: var(--veo-bg);
|
|
596
|
+
border: var(--veo-border-width, 0) solid var(--veo-border-color, transparent);
|
|
597
|
+
transform: rotate(45deg);
|
|
598
|
+
pointer-events: none;
|
|
599
|
+
}
|
|
285
600
|
|
|
286
601
|
.veo-inline {
|
|
287
602
|
background: var(--veo-bg);
|
|
@@ -300,17 +615,25 @@ var GUIDE_STYLES = `
|
|
|
300
615
|
display: flex; flex-direction: column;
|
|
301
616
|
}
|
|
302
617
|
.veo-guide-image {
|
|
303
|
-
display: block;
|
|
304
|
-
|
|
305
|
-
|
|
618
|
+
display: block;
|
|
619
|
+
width: var(--veo-image-width, 100%);
|
|
620
|
+
max-height: 180px;
|
|
621
|
+
object-fit: cover;
|
|
622
|
+
align-self: var(--veo-image-align, stretch);
|
|
623
|
+
border-radius: var(--veo-image-radius, 8px);
|
|
624
|
+
margin: var(--veo-image-mt, 0) 0 var(--veo-image-mb, 12px);
|
|
306
625
|
}
|
|
307
626
|
.veo-guide-title {
|
|
308
|
-
font-size: 18px; font-weight: 600; line-height: 1.3;
|
|
309
|
-
|
|
627
|
+
font-size: var(--veo-title-size, 18px); font-weight: 600; line-height: 1.3;
|
|
628
|
+
text-align: var(--veo-title-align, left);
|
|
629
|
+
margin: var(--veo-title-mt, 0) 0 var(--veo-title-mb, 6px);
|
|
630
|
+
color: var(--veo-title-color, var(--veo-text));
|
|
310
631
|
}
|
|
311
632
|
.veo-guide-text {
|
|
312
|
-
font-size: 14px; line-height: 1.5;
|
|
313
|
-
|
|
633
|
+
font-size: var(--veo-text-size, 14px); line-height: 1.5;
|
|
634
|
+
text-align: var(--veo-text-align, left);
|
|
635
|
+
margin: var(--veo-text-mt, 0) 0 var(--veo-text-mb, 16px);
|
|
636
|
+
color: var(--veo-text-color, var(--veo-text-secondary));
|
|
314
637
|
}
|
|
315
638
|
.veo-guide-actions {
|
|
316
639
|
display: flex; gap: 8px; justify-content: var(--veo-actions-justify);
|
|
@@ -447,6 +770,7 @@ var GUIDE_STYLES = `
|
|
|
447
770
|
}
|
|
448
771
|
.veo-custom-inline {
|
|
449
772
|
display: block;
|
|
773
|
+
position: relative;
|
|
450
774
|
margin: 12px 0;
|
|
451
775
|
}
|
|
452
776
|
.veo-custom-frame {
|
|
@@ -456,6 +780,21 @@ var GUIDE_STYLES = `
|
|
|
456
780
|
background: transparent;
|
|
457
781
|
color-scheme: normal;
|
|
458
782
|
}
|
|
783
|
+
/*
|
|
784
|
+
* X de cerrar de las gu\xEDas custom: el HTML del cliente vive en un iframe
|
|
785
|
+
* sandbox, as\xED que el bot\xF3n lo pone el host POR ENCIMA del iframe. Con fondo
|
|
786
|
+
* propio para ser visible sobre cualquier contenido.
|
|
787
|
+
*/
|
|
788
|
+
.veo-custom-close {
|
|
789
|
+
position: absolute; top: 6px; right: 6px; z-index: 1;
|
|
790
|
+
width: 22px; height: 22px; padding: 0;
|
|
791
|
+
display: flex; align-items: center; justify-content: center;
|
|
792
|
+
font-size: 16px; line-height: 1; color: #6b7280;
|
|
793
|
+
background: rgba(255, 255, 255, 0.92);
|
|
794
|
+
border: 1px solid rgba(0, 0, 0, 0.08); border-radius: 50%;
|
|
795
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.14); cursor: pointer;
|
|
796
|
+
}
|
|
797
|
+
.veo-custom-close:hover { color: #111827; }
|
|
459
798
|
|
|
460
799
|
@keyframes veo-fade-in { from { opacity: 0; } to { opacity: 1; } }
|
|
461
800
|
@keyframes veo-slide-up {
|
|
@@ -529,7 +868,8 @@ var BaseRenderer = class {
|
|
|
529
868
|
// src/plugins/guides/renderers/banner-renderer.ts
|
|
530
869
|
var BannerRenderer = class extends BaseRenderer {
|
|
531
870
|
render(context) {
|
|
532
|
-
const
|
|
871
|
+
const idx = context.nav?.stepIndex ?? 0;
|
|
872
|
+
const step = context.guide.guideSteps[idx];
|
|
533
873
|
if (!step) return;
|
|
534
874
|
const { root } = this.createHost();
|
|
535
875
|
this.applyDesign(step.style);
|
|
@@ -538,27 +878,25 @@ var BannerRenderer = class extends BaseRenderer {
|
|
|
538
878
|
const banner = ownerDocument.createElement("div");
|
|
539
879
|
banner.className = `veo-banner veo-banner-${position}`;
|
|
540
880
|
const dismiss = (action, ctaUrl) => {
|
|
541
|
-
context.onInteraction({
|
|
542
|
-
guideId: context.guide.guideId,
|
|
543
|
-
stepIndex: 0,
|
|
544
|
-
action
|
|
545
|
-
});
|
|
881
|
+
context.onInteraction({ guideId: context.guide.guideId, stepIndex: idx, action });
|
|
546
882
|
if (ctaUrl) window.open(ctaUrl, "_blank", "noopener,noreferrer");
|
|
547
883
|
context.onClose();
|
|
548
884
|
};
|
|
549
|
-
const content =
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
885
|
+
const content = context.nav ? buildWalkthroughStepContent(
|
|
886
|
+
step,
|
|
887
|
+
context.nav.stepIndex,
|
|
888
|
+
context.nav.totalSteps,
|
|
889
|
+
ownerDocument,
|
|
890
|
+
context.nav.callbacks
|
|
891
|
+
) : buildStepContent(step, ownerDocument, {
|
|
892
|
+
onCtaClick: (action, url) => dismiss("cta_clicked", action === "url" ? url : void 0),
|
|
553
893
|
onDismiss: () => dismiss("dismissed")
|
|
554
894
|
});
|
|
555
895
|
banner.appendChild(content);
|
|
556
896
|
root.appendChild(banner);
|
|
557
|
-
context.
|
|
558
|
-
guideId: context.guide.guideId,
|
|
559
|
-
|
|
560
|
-
action: "shown"
|
|
561
|
-
});
|
|
897
|
+
if (!context.nav) {
|
|
898
|
+
context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
|
|
899
|
+
}
|
|
562
900
|
}
|
|
563
901
|
};
|
|
564
902
|
|
|
@@ -628,7 +966,11 @@ function mountCustomFrame(doc, step, context, opts) {
|
|
|
628
966
|
const hasFixedHeight = opts.fixedHeight !== void 0 && opts.fixedHeight !== null;
|
|
629
967
|
if (hasFixedHeight) iframe.style.height = toCssSize(opts.fixedHeight);
|
|
630
968
|
const token = uuidv7();
|
|
631
|
-
|
|
969
|
+
const wantsTailwind = step.style?.tailwind === true;
|
|
970
|
+
iframe.srcdoc = buildSrcdoc(step.html ?? "", step.css ?? "", step.js ?? "", token, {
|
|
971
|
+
tokens: collectHostTokens(),
|
|
972
|
+
tailwind: wantsTailwind
|
|
973
|
+
});
|
|
632
974
|
const close = () => context.onClose();
|
|
633
975
|
const onMessage = (event) => {
|
|
634
976
|
if (event.source !== iframe.contentWindow) return;
|
|
@@ -677,12 +1019,37 @@ function mountCustomFrame(doc, step, context, opts) {
|
|
|
677
1019
|
function toCssSize(value) {
|
|
678
1020
|
return typeof value === "number" ? `${value}px` : value;
|
|
679
1021
|
}
|
|
1022
|
+
function collectHostTokens() {
|
|
1023
|
+
if (typeof window === "undefined" || typeof document === "undefined") return "";
|
|
1024
|
+
try {
|
|
1025
|
+
const rootStyle = getComputedStyle(document.documentElement);
|
|
1026
|
+
const decls = [];
|
|
1027
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1028
|
+
for (let i = 0; i < rootStyle.length && decls.length < 400; i++) {
|
|
1029
|
+
const name = rootStyle.item(i);
|
|
1030
|
+
if (!name.startsWith("--") || seen.has(name)) continue;
|
|
1031
|
+
seen.add(name);
|
|
1032
|
+
const value = rootStyle.getPropertyValue(name).trim();
|
|
1033
|
+
if (!value || value.length > 200 || /[<>{}]/.test(value)) continue;
|
|
1034
|
+
decls.push(`${name}:${value}`);
|
|
1035
|
+
}
|
|
1036
|
+
const bodyFont = getComputedStyle(document.body).fontFamily;
|
|
1037
|
+
if (bodyFont && bodyFont.length <= 200 && !/[<>{}]/.test(bodyFont)) {
|
|
1038
|
+
decls.push(`--veo-host-font:${bodyFont}`);
|
|
1039
|
+
}
|
|
1040
|
+
return decls.length ? `:root{${decls.join(";")}}` : "";
|
|
1041
|
+
} catch {
|
|
1042
|
+
return "";
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
680
1045
|
function isRecord(value) {
|
|
681
1046
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
682
1047
|
}
|
|
683
|
-
function buildSrcdoc(html, css, js, token) {
|
|
1048
|
+
function buildSrcdoc(html, css, js, token, opts) {
|
|
684
1049
|
const bridge = `(function(){var T=${JSON.stringify(token)};function P(m){m.__veo=T;parent.postMessage(m,'*');}window.veo={dismiss:function(){P({type:'dismiss'});},cta:function(u){P({type:'cta',url:u});},track:function(n,p){P({type:'track',name:n,props:p||{}});},navigate:function(u){P({type:'navigate',url:u});}};document.addEventListener('click',function(e){var a=e.target&&e.target.closest?e.target.closest('a[href]'):null;if(!a)return;var h=a.getAttribute('href');if(!h||h.charAt(0)==='#'||/^(javascript|mailto|tel):/i.test(h))return;e.preventDefault();if(a.target==='_blank'){P({type:'cta',url:a.href,close:false});}else{P({type:'navigate',url:h});}},true);function H(){P({type:'resize',height:Math.ceil(document.documentElement.scrollHeight)});}window.addEventListener('load',H);try{if(typeof ResizeObserver!=='undefined'){new ResizeObserver(H).observe(document.documentElement);}}catch(e){}})();`;
|
|
685
|
-
|
|
1050
|
+
const tokensStyle = opts.tokens ? `<style>${escapeClosing(opts.tokens, "style")}</style>` : "";
|
|
1051
|
+
const tailwind = opts.tailwind ? '<script src="https://cdn.tailwindcss.com"></script>' : "";
|
|
1052
|
+
return '<!DOCTYPE html><html><head><meta charset="utf-8"><style>html,body{margin:0;padding:0;background:transparent;font-family:var(--veo-host-font,system-ui,sans-serif);}</style>' + tokensStyle + tailwind + `<style>${escapeClosing(css, "style")}</style></head><body>${html}<script>${bridge}</script>` + (js ? `<script>${escapeClosing(js, "script")}</script>` : "") + "</body></html>";
|
|
686
1053
|
}
|
|
687
1054
|
function escapeClosing(code, tag) {
|
|
688
1055
|
const rx = tag === "script" ? /<\/script/gi : /<\/style/gi;
|
|
@@ -715,6 +1082,20 @@ var CustomRenderer = class extends BaseRenderer {
|
|
|
715
1082
|
});
|
|
716
1083
|
this.registerCleanup(cleanup);
|
|
717
1084
|
wrapper.appendChild(iframe);
|
|
1085
|
+
wrapper.appendChild(
|
|
1086
|
+
createCloseButton(
|
|
1087
|
+
doc,
|
|
1088
|
+
() => {
|
|
1089
|
+
context.onInteraction({
|
|
1090
|
+
guideId: context.guide.guideId,
|
|
1091
|
+
stepIndex: 0,
|
|
1092
|
+
action: "dismissed"
|
|
1093
|
+
});
|
|
1094
|
+
context.onClose();
|
|
1095
|
+
},
|
|
1096
|
+
"veo-custom-close"
|
|
1097
|
+
)
|
|
1098
|
+
);
|
|
718
1099
|
root.appendChild(wrapper);
|
|
719
1100
|
if (placement.mode === "floating") {
|
|
720
1101
|
applyFloatingPosition(wrapper, placement);
|
|
@@ -852,13 +1233,7 @@ function mountFormContent(card, context, doc) {
|
|
|
852
1233
|
});
|
|
853
1234
|
});
|
|
854
1235
|
content.appendChild(form);
|
|
855
|
-
|
|
856
|
-
closeBtn.type = "button";
|
|
857
|
-
closeBtn.className = "veo-guide-close";
|
|
858
|
-
closeBtn.setAttribute("aria-label", "Cerrar");
|
|
859
|
-
closeBtn.textContent = "\xD7";
|
|
860
|
-
closeBtn.addEventListener("click", dismiss);
|
|
861
|
-
content.appendChild(closeBtn);
|
|
1236
|
+
content.appendChild(createCloseButton(doc, dismiss));
|
|
862
1237
|
card.appendChild(content);
|
|
863
1238
|
}
|
|
864
1239
|
function showThanks(card, doc, note) {
|
|
@@ -1122,6 +1497,20 @@ var InlineCustomRenderer = class extends BaseRenderer {
|
|
|
1122
1497
|
const { iframe, cleanup } = mountCustomFrame(doc, step, context, { width: "100%" });
|
|
1123
1498
|
this.registerCleanup(cleanup);
|
|
1124
1499
|
wrapper.appendChild(iframe);
|
|
1500
|
+
wrapper.appendChild(
|
|
1501
|
+
createCloseButton(
|
|
1502
|
+
doc,
|
|
1503
|
+
() => {
|
|
1504
|
+
context.onInteraction({
|
|
1505
|
+
guideId: context.guide.guideId,
|
|
1506
|
+
stepIndex: 0,
|
|
1507
|
+
action: "dismissed"
|
|
1508
|
+
});
|
|
1509
|
+
context.onClose();
|
|
1510
|
+
},
|
|
1511
|
+
"veo-custom-close"
|
|
1512
|
+
)
|
|
1513
|
+
);
|
|
1125
1514
|
root.appendChild(wrapper);
|
|
1126
1515
|
context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
|
|
1127
1516
|
}
|
|
@@ -1187,7 +1576,8 @@ var InlineRenderer = class extends BaseRenderer {
|
|
|
1187
1576
|
// src/plugins/guides/renderers/modal-renderer.ts
|
|
1188
1577
|
var ModalRenderer = class extends BaseRenderer {
|
|
1189
1578
|
render(context) {
|
|
1190
|
-
const
|
|
1579
|
+
const idx = context.nav?.stepIndex ?? 0;
|
|
1580
|
+
const step = context.guide.guideSteps[idx];
|
|
1191
1581
|
if (!step) return;
|
|
1192
1582
|
const { root } = this.createHost();
|
|
1193
1583
|
this.applyDesign(step.style);
|
|
@@ -1197,38 +1587,59 @@ var ModalRenderer = class extends BaseRenderer {
|
|
|
1197
1587
|
const card = ownerDocument.createElement("div");
|
|
1198
1588
|
card.className = "veo-modal-card";
|
|
1199
1589
|
const dismiss = (action, ctaUrl) => {
|
|
1200
|
-
context.onInteraction({
|
|
1201
|
-
guideId: context.guide.guideId,
|
|
1202
|
-
stepIndex: 0,
|
|
1203
|
-
action
|
|
1204
|
-
});
|
|
1590
|
+
context.onInteraction({ guideId: context.guide.guideId, stepIndex: idx, action });
|
|
1205
1591
|
if (ctaUrl) window.open(ctaUrl, "_blank", "noopener,noreferrer");
|
|
1206
1592
|
context.onClose();
|
|
1207
1593
|
};
|
|
1208
|
-
const
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1594
|
+
const onBackdropOrEsc = () => {
|
|
1595
|
+
if (context.nav) context.nav.callbacks.onSkip();
|
|
1596
|
+
else dismiss("dismissed");
|
|
1597
|
+
};
|
|
1598
|
+
const content = context.nav ? buildWalkthroughStepContent(
|
|
1599
|
+
step,
|
|
1600
|
+
context.nav.stepIndex,
|
|
1601
|
+
context.nav.totalSteps,
|
|
1602
|
+
ownerDocument,
|
|
1603
|
+
context.nav.callbacks
|
|
1604
|
+
) : buildStepContent(step, ownerDocument, {
|
|
1605
|
+
onCtaClick: (action, url) => dismiss("cta_clicked", action === "url" ? url : void 0),
|
|
1212
1606
|
onDismiss: () => dismiss("dismissed")
|
|
1213
1607
|
});
|
|
1214
1608
|
card.appendChild(content);
|
|
1215
1609
|
overlay.appendChild(card);
|
|
1216
1610
|
root.appendChild(overlay);
|
|
1217
1611
|
overlay.addEventListener("click", (e) => {
|
|
1218
|
-
if (e.target === overlay)
|
|
1612
|
+
if (e.target === overlay) onBackdropOrEsc();
|
|
1219
1613
|
});
|
|
1220
1614
|
const onKey = (e) => {
|
|
1221
|
-
if (e.key === "Escape")
|
|
1615
|
+
if (e.key === "Escape") onBackdropOrEsc();
|
|
1222
1616
|
};
|
|
1223
1617
|
document.addEventListener("keydown", onKey);
|
|
1224
1618
|
this.registerCleanup(() => document.removeEventListener("keydown", onKey));
|
|
1225
|
-
context.
|
|
1226
|
-
guideId: context.guide.guideId,
|
|
1227
|
-
|
|
1228
|
-
action: "shown"
|
|
1229
|
-
});
|
|
1619
|
+
if (!context.nav) {
|
|
1620
|
+
context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
|
|
1621
|
+
}
|
|
1230
1622
|
}
|
|
1231
1623
|
};
|
|
1624
|
+
|
|
1625
|
+
// src/plugins/guides/renderers/floating-arrow.ts
|
|
1626
|
+
var STATIC_SIDE = {
|
|
1627
|
+
top: "bottom",
|
|
1628
|
+
bottom: "top",
|
|
1629
|
+
left: "right",
|
|
1630
|
+
right: "left"
|
|
1631
|
+
};
|
|
1632
|
+
function positionArrow(arrowEl, placement, data) {
|
|
1633
|
+
const side = STATIC_SIDE[placement.split("-")[0] ?? "bottom"] ?? "top";
|
|
1634
|
+
for (const prop of ["top", "bottom", "left", "right"]) {
|
|
1635
|
+
arrowEl.style.setProperty(prop, "");
|
|
1636
|
+
}
|
|
1637
|
+
if (data?.x != null) arrowEl.style.setProperty("left", `${data.x}px`);
|
|
1638
|
+
if (data?.y != null) arrowEl.style.setProperty("top", `${data.y}px`);
|
|
1639
|
+
arrowEl.style.setProperty(side, "-6px");
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
// src/plugins/guides/renderers/tooltip-renderer.ts
|
|
1232
1643
|
var TooltipRenderer = class extends BaseRenderer {
|
|
1233
1644
|
async render(context) {
|
|
1234
1645
|
const step = context.guide.guideSteps[0];
|
|
@@ -1258,14 +1669,18 @@ var TooltipRenderer = class extends BaseRenderer {
|
|
|
1258
1669
|
onDismiss: () => dismiss("dismissed")
|
|
1259
1670
|
});
|
|
1260
1671
|
tooltip.appendChild(content);
|
|
1672
|
+
const arrowEl = ownerDocument.createElement("div");
|
|
1673
|
+
arrowEl.className = "veo-tooltip-arrow";
|
|
1674
|
+
tooltip.appendChild(arrowEl);
|
|
1261
1675
|
root.appendChild(tooltip);
|
|
1262
1676
|
const updatePosition = async () => {
|
|
1263
|
-
const { x, y } = await dom.computePosition(anchor, tooltip, {
|
|
1677
|
+
const { x, y, placement, middlewareData } = await dom.computePosition(anchor, tooltip, {
|
|
1264
1678
|
placement: "bottom",
|
|
1265
|
-
middleware: [dom.offset(
|
|
1679
|
+
middleware: [dom.offset(10), dom.flip(), dom.shift({ padding: 8 }), dom.arrow({ element: arrowEl })]
|
|
1266
1680
|
});
|
|
1267
1681
|
tooltip.style.left = `${x}px`;
|
|
1268
1682
|
tooltip.style.top = `${y}px`;
|
|
1683
|
+
positionArrow(arrowEl, placement, middlewareData.arrow);
|
|
1269
1684
|
};
|
|
1270
1685
|
await updatePosition();
|
|
1271
1686
|
const reposition = () => {
|
|
@@ -1284,79 +1699,6 @@ var TooltipRenderer = class extends BaseRenderer {
|
|
|
1284
1699
|
});
|
|
1285
1700
|
}
|
|
1286
1701
|
};
|
|
1287
|
-
|
|
1288
|
-
// src/plugins/guides/walkthrough-block-builder.ts
|
|
1289
|
-
function buildWalkthroughStepContent(step, stepIndex, totalSteps, doc, callbacks) {
|
|
1290
|
-
const container = doc.createElement("div");
|
|
1291
|
-
container.className = "veo-guide-content";
|
|
1292
|
-
const counter = doc.createElement("div");
|
|
1293
|
-
counter.className = "veo-walkthrough-counter";
|
|
1294
|
-
counter.textContent = `Paso ${stepIndex + 1} de ${totalSteps}`;
|
|
1295
|
-
container.appendChild(counter);
|
|
1296
|
-
const progress = doc.createElement("div");
|
|
1297
|
-
progress.className = "veo-walkthrough-progress";
|
|
1298
|
-
for (let i = 0; i < totalSteps; i++) {
|
|
1299
|
-
const dot = doc.createElement("span");
|
|
1300
|
-
dot.className = "veo-walkthrough-progress-dot";
|
|
1301
|
-
if (i < stepIndex) dot.classList.add("completed");
|
|
1302
|
-
if (i === stepIndex) dot.classList.add("active");
|
|
1303
|
-
progress.appendChild(dot);
|
|
1304
|
-
}
|
|
1305
|
-
container.appendChild(progress);
|
|
1306
|
-
if (typeof step.imageUrl === "string" && step.imageUrl && isSafeUrl(step.imageUrl)) {
|
|
1307
|
-
const img = doc.createElement("img");
|
|
1308
|
-
img.className = "veo-guide-image";
|
|
1309
|
-
img.src = step.imageUrl;
|
|
1310
|
-
img.alt = typeof step.title === "string" ? step.title : "";
|
|
1311
|
-
container.appendChild(img);
|
|
1312
|
-
}
|
|
1313
|
-
if (typeof step.title === "string" && step.title) {
|
|
1314
|
-
const heading = doc.createElement("h2");
|
|
1315
|
-
heading.className = "veo-guide-title";
|
|
1316
|
-
heading.textContent = step.title;
|
|
1317
|
-
container.appendChild(heading);
|
|
1318
|
-
}
|
|
1319
|
-
if (typeof step.content === "string" && step.content) {
|
|
1320
|
-
const paragraph = doc.createElement("p");
|
|
1321
|
-
paragraph.className = "veo-guide-text";
|
|
1322
|
-
paragraph.textContent = step.content;
|
|
1323
|
-
container.appendChild(paragraph);
|
|
1324
|
-
}
|
|
1325
|
-
const actions = doc.createElement("div");
|
|
1326
|
-
actions.className = "veo-walkthrough-actions";
|
|
1327
|
-
const skipBtn = doc.createElement("button");
|
|
1328
|
-
skipBtn.type = "button";
|
|
1329
|
-
skipBtn.className = "veo-walkthrough-skip";
|
|
1330
|
-
skipBtn.textContent = "Omitir";
|
|
1331
|
-
skipBtn.addEventListener("click", () => callbacks.onSkip());
|
|
1332
|
-
actions.appendChild(skipBtn);
|
|
1333
|
-
const rightGroup = doc.createElement("div");
|
|
1334
|
-
rightGroup.className = "veo-walkthrough-actions-right";
|
|
1335
|
-
if (stepIndex > 0) {
|
|
1336
|
-
const backBtn = doc.createElement("button");
|
|
1337
|
-
backBtn.type = "button";
|
|
1338
|
-
backBtn.className = "veo-walkthrough-btn-secondary";
|
|
1339
|
-
backBtn.textContent = "Atr\xE1s";
|
|
1340
|
-
backBtn.addEventListener("click", () => callbacks.onBack());
|
|
1341
|
-
rightGroup.appendChild(backBtn);
|
|
1342
|
-
}
|
|
1343
|
-
const isLastStep = stepIndex === totalSteps - 1;
|
|
1344
|
-
const primaryBtn = doc.createElement("button");
|
|
1345
|
-
primaryBtn.type = "button";
|
|
1346
|
-
primaryBtn.className = "veo-guide-cta";
|
|
1347
|
-
const defaultLabel = isLastStep ? "Finalizar" : "Siguiente";
|
|
1348
|
-
primaryBtn.textContent = typeof step.ctaText === "string" && step.ctaText ? step.ctaText : defaultLabel;
|
|
1349
|
-
primaryBtn.addEventListener("click", () => {
|
|
1350
|
-
if (isLastStep) callbacks.onComplete();
|
|
1351
|
-
else callbacks.onNext();
|
|
1352
|
-
});
|
|
1353
|
-
rightGroup.appendChild(primaryBtn);
|
|
1354
|
-
actions.appendChild(rightGroup);
|
|
1355
|
-
container.appendChild(actions);
|
|
1356
|
-
return container;
|
|
1357
|
-
}
|
|
1358
|
-
|
|
1359
|
-
// src/plugins/guides/renderers/walkthrough-renderer.ts
|
|
1360
1702
|
var WalkthroughRenderer = class extends BaseRenderer {
|
|
1361
1703
|
async render(context) {
|
|
1362
1704
|
const step = context.guide.guideSteps[context.currentStepIndex];
|
|
@@ -1404,27 +1746,13 @@ var WalkthroughRenderer = class extends BaseRenderer {
|
|
|
1404
1746
|
return true;
|
|
1405
1747
|
}
|
|
1406
1748
|
};
|
|
1407
|
-
var STATIC_SIDE = {
|
|
1408
|
-
top: "bottom",
|
|
1409
|
-
bottom: "top",
|
|
1410
|
-
left: "right",
|
|
1411
|
-
right: "left"
|
|
1412
|
-
};
|
|
1413
|
-
function positionArrow(arrowEl, placement, data) {
|
|
1414
|
-
const side = STATIC_SIDE[placement.split("-")[0] ?? "bottom"] ?? "top";
|
|
1415
|
-
for (const prop of ["top", "bottom", "left", "right"]) {
|
|
1416
|
-
arrowEl.style.setProperty(prop, "");
|
|
1417
|
-
}
|
|
1418
|
-
if (data?.x != null) arrowEl.style.setProperty("left", `${data.x}px`);
|
|
1419
|
-
if (data?.y != null) arrowEl.style.setProperty("top", `${data.y}px`);
|
|
1420
|
-
arrowEl.style.setProperty(side, "-6px");
|
|
1421
|
-
}
|
|
1422
1749
|
|
|
1423
1750
|
// src/plugins/guides/guide-preview.ts
|
|
1424
1751
|
var PREVIEW_GUIDE_ID = "__veo_preview__";
|
|
1425
1752
|
var GuidePreviewController = class {
|
|
1426
1753
|
constructor() {
|
|
1427
1754
|
this.singleStep = null;
|
|
1755
|
+
/** Renderer del paso de walkthrough activo (walkthrough tooltip o modal/banner). */
|
|
1428
1756
|
this.walkthrough = null;
|
|
1429
1757
|
this.walkthroughGuide = null;
|
|
1430
1758
|
this.walkthroughIndex = 0;
|
|
@@ -1497,7 +1825,6 @@ var GuidePreviewController = class {
|
|
|
1497
1825
|
safeDestroy(this.walkthrough);
|
|
1498
1826
|
this.walkthrough = null;
|
|
1499
1827
|
}
|
|
1500
|
-
const renderer = new WalkthroughRenderer();
|
|
1501
1828
|
const callbacks = {
|
|
1502
1829
|
onNext: () => {
|
|
1503
1830
|
const next = this.walkthroughIndex + 1;
|
|
@@ -1517,6 +1844,30 @@ var GuidePreviewController = class {
|
|
|
1517
1844
|
onSkip: () => this.close(),
|
|
1518
1845
|
onComplete: () => this.close()
|
|
1519
1846
|
};
|
|
1847
|
+
const step = guide.guideSteps[index];
|
|
1848
|
+
const render = step?.style?.render;
|
|
1849
|
+
if (render === "modal" || render === "banner") {
|
|
1850
|
+
const renderer2 = render === "modal" ? new ModalRenderer() : new BannerRenderer();
|
|
1851
|
+
try {
|
|
1852
|
+
renderer2.render({
|
|
1853
|
+
guide,
|
|
1854
|
+
onInteraction: () => {
|
|
1855
|
+
},
|
|
1856
|
+
onClose: () => this.close(),
|
|
1857
|
+
nav: { stepIndex: index, totalSteps: guide.guideSteps.length, callbacks }
|
|
1858
|
+
});
|
|
1859
|
+
} catch {
|
|
1860
|
+
safeDestroy(renderer2);
|
|
1861
|
+
return false;
|
|
1862
|
+
}
|
|
1863
|
+
if (this.walkthroughGuide !== guide) {
|
|
1864
|
+
safeDestroy(renderer2);
|
|
1865
|
+
return false;
|
|
1866
|
+
}
|
|
1867
|
+
this.walkthrough = renderer2;
|
|
1868
|
+
return true;
|
|
1869
|
+
}
|
|
1870
|
+
const renderer = new WalkthroughRenderer();
|
|
1520
1871
|
let success = false;
|
|
1521
1872
|
try {
|
|
1522
1873
|
success = await renderer.render({ guide, currentStepIndex: index, callbacks });
|