veo-sdk 0.3.6 → 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.
@@ -0,0 +1,5 @@
1
+ export { createBuilderSession, initBuilderMode, startElementPicker, stopElementPicker } from './chunk-734ORVMM.mjs';
2
+ import './chunk-4VJIILXF.mjs';
3
+ import './chunk-PTG3BTJE.mjs';
4
+ //# sourceMappingURL=builder-7MBMYL26.mjs.map
5
+ //# sourceMappingURL=builder-7MBMYL26.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"builder-MIPJRGOD.mjs"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"builder-7MBMYL26.mjs"}
package/dist/builder.cjs CHANGED
@@ -146,6 +146,16 @@ var WALKTHROUGH_STEP_SELECTOR_TIMEOUT_MS = 5e3;
146
146
  function buildStepContent(step, doc, callbacks) {
147
147
  const container = doc.createElement("div");
148
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) {
149
159
  if (typeof step.imageUrl === "string" && step.imageUrl && isSafeUrl(step.imageUrl)) {
150
160
  const img = doc.createElement("img");
151
161
  img.className = "veo-guide-image";
@@ -180,8 +190,110 @@ function buildStepContent(step, doc, callbacks) {
180
190
  actions.appendChild(cta);
181
191
  }
182
192
  container.appendChild(actions);
183
- container.appendChild(createCloseButton(doc, () => callbacks.onDismiss()));
184
- return container;
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
+ }
185
297
  }
186
298
  function createCloseButton(doc, onClose, className = "veo-guide-close") {
187
299
  const closeBtn = doc.createElement("button");
@@ -208,6 +320,78 @@ function readCtaUrl(step) {
208
320
  return isSafeUrl(candidate) ? candidate : void 0;
209
321
  }
210
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
+
211
395
  // src/plugins/guides/guide-design.ts
212
396
  var DARK_THEME = {
213
397
  "--veo-bg": "#1f2937",
@@ -257,6 +441,9 @@ function applyDesignVars(host, style) {
257
441
  if (typeof s.accentColor === "string" && COLOR_RE.test(s.accentColor.trim())) {
258
442
  host.style.setProperty("--veo-primary", s.accentColor.trim());
259
443
  }
444
+ if (typeof s.fontFamily === "string" && s.fontFamily.length <= 200 && !/[<>{}]/.test(s.fontFamily)) {
445
+ host.style.setProperty("--veo-font", s.fontFamily.trim());
446
+ }
260
447
  if (s.theme === "dark") {
261
448
  for (const [key, value] of Object.entries(DARK_THEME)) {
262
449
  host.style.setProperty(key, value);
@@ -350,7 +537,7 @@ var GUIDE_STYLES = `
350
537
  --veo-actions-justify: flex-end;
351
538
  all: initial;
352
539
  }
353
- * { 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); }
354
541
 
355
542
  .veo-modal-overlay {
356
543
  position: fixed; inset: 0;
@@ -681,7 +868,8 @@ var BaseRenderer = class {
681
868
  // src/plugins/guides/renderers/banner-renderer.ts
682
869
  var BannerRenderer = class extends BaseRenderer {
683
870
  render(context) {
684
- const step = context.guide.guideSteps[0];
871
+ const idx = context.nav?.stepIndex ?? 0;
872
+ const step = context.guide.guideSteps[idx];
685
873
  if (!step) return;
686
874
  const { root } = this.createHost();
687
875
  this.applyDesign(step.style);
@@ -690,27 +878,25 @@ var BannerRenderer = class extends BaseRenderer {
690
878
  const banner = ownerDocument.createElement("div");
691
879
  banner.className = `veo-banner veo-banner-${position}`;
692
880
  const dismiss = (action, ctaUrl) => {
693
- context.onInteraction({
694
- guideId: context.guide.guideId,
695
- stepIndex: 0,
696
- action
697
- });
881
+ context.onInteraction({ guideId: context.guide.guideId, stepIndex: idx, action });
698
882
  if (ctaUrl) window.open(ctaUrl, "_blank", "noopener,noreferrer");
699
883
  context.onClose();
700
884
  };
701
- const content = buildStepContent(step, ownerDocument, {
702
- onCtaClick: (action, url) => {
703
- dismiss("cta_clicked", action === "url" ? url : void 0);
704
- },
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),
705
893
  onDismiss: () => dismiss("dismissed")
706
894
  });
707
895
  banner.appendChild(content);
708
896
  root.appendChild(banner);
709
- context.onInteraction({
710
- guideId: context.guide.guideId,
711
- stepIndex: 0,
712
- action: "shown"
713
- });
897
+ if (!context.nav) {
898
+ context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
899
+ }
714
900
  }
715
901
  };
716
902
 
@@ -1390,7 +1576,8 @@ var InlineRenderer = class extends BaseRenderer {
1390
1576
  // src/plugins/guides/renderers/modal-renderer.ts
1391
1577
  var ModalRenderer = class extends BaseRenderer {
1392
1578
  render(context) {
1393
- const step = context.guide.guideSteps[0];
1579
+ const idx = context.nav?.stepIndex ?? 0;
1580
+ const step = context.guide.guideSteps[idx];
1394
1581
  if (!step) return;
1395
1582
  const { root } = this.createHost();
1396
1583
  this.applyDesign(step.style);
@@ -1400,36 +1587,38 @@ var ModalRenderer = class extends BaseRenderer {
1400
1587
  const card = ownerDocument.createElement("div");
1401
1588
  card.className = "veo-modal-card";
1402
1589
  const dismiss = (action, ctaUrl) => {
1403
- context.onInteraction({
1404
- guideId: context.guide.guideId,
1405
- stepIndex: 0,
1406
- action
1407
- });
1590
+ context.onInteraction({ guideId: context.guide.guideId, stepIndex: idx, action });
1408
1591
  if (ctaUrl) window.open(ctaUrl, "_blank", "noopener,noreferrer");
1409
1592
  context.onClose();
1410
1593
  };
1411
- const content = buildStepContent(step, ownerDocument, {
1412
- onCtaClick: (action, url) => {
1413
- dismiss("cta_clicked", action === "url" ? url : void 0);
1414
- },
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),
1415
1606
  onDismiss: () => dismiss("dismissed")
1416
1607
  });
1417
1608
  card.appendChild(content);
1418
1609
  overlay.appendChild(card);
1419
1610
  root.appendChild(overlay);
1420
1611
  overlay.addEventListener("click", (e) => {
1421
- if (e.target === overlay) dismiss("dismissed");
1612
+ if (e.target === overlay) onBackdropOrEsc();
1422
1613
  });
1423
1614
  const onKey = (e) => {
1424
- if (e.key === "Escape") dismiss("dismissed");
1615
+ if (e.key === "Escape") onBackdropOrEsc();
1425
1616
  };
1426
1617
  document.addEventListener("keydown", onKey);
1427
1618
  this.registerCleanup(() => document.removeEventListener("keydown", onKey));
1428
- context.onInteraction({
1429
- guideId: context.guide.guideId,
1430
- stepIndex: 0,
1431
- action: "shown"
1432
- });
1619
+ if (!context.nav) {
1620
+ context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
1621
+ }
1433
1622
  }
1434
1623
  };
1435
1624
 
@@ -1510,80 +1699,6 @@ var TooltipRenderer = class extends BaseRenderer {
1510
1699
  });
1511
1700
  }
1512
1701
  };
1513
-
1514
- // src/plugins/guides/walkthrough-block-builder.ts
1515
- function buildWalkthroughStepContent(step, stepIndex, totalSteps, doc, callbacks) {
1516
- const container = doc.createElement("div");
1517
- container.className = "veo-guide-content";
1518
- const counter = doc.createElement("div");
1519
- counter.className = "veo-walkthrough-counter";
1520
- counter.textContent = `Paso ${stepIndex + 1} de ${totalSteps}`;
1521
- container.appendChild(counter);
1522
- const progress = doc.createElement("div");
1523
- progress.className = "veo-walkthrough-progress";
1524
- for (let i = 0; i < totalSteps; i++) {
1525
- const dot = doc.createElement("span");
1526
- dot.className = "veo-walkthrough-progress-dot";
1527
- if (i < stepIndex) dot.classList.add("completed");
1528
- if (i === stepIndex) dot.classList.add("active");
1529
- progress.appendChild(dot);
1530
- }
1531
- container.appendChild(progress);
1532
- if (typeof step.imageUrl === "string" && step.imageUrl && isSafeUrl(step.imageUrl)) {
1533
- const img = doc.createElement("img");
1534
- img.className = "veo-guide-image";
1535
- img.src = step.imageUrl;
1536
- img.alt = typeof step.title === "string" ? step.title : "";
1537
- container.appendChild(img);
1538
- }
1539
- if (typeof step.title === "string" && step.title) {
1540
- const heading = doc.createElement("h2");
1541
- heading.className = "veo-guide-title";
1542
- heading.textContent = step.title;
1543
- container.appendChild(heading);
1544
- }
1545
- if (typeof step.content === "string" && step.content) {
1546
- const paragraph = doc.createElement("p");
1547
- paragraph.className = "veo-guide-text";
1548
- paragraph.textContent = step.content;
1549
- container.appendChild(paragraph);
1550
- }
1551
- const actions = doc.createElement("div");
1552
- actions.className = "veo-walkthrough-actions";
1553
- const skipBtn = doc.createElement("button");
1554
- skipBtn.type = "button";
1555
- skipBtn.className = "veo-walkthrough-skip";
1556
- skipBtn.textContent = "Omitir";
1557
- skipBtn.addEventListener("click", () => callbacks.onSkip());
1558
- actions.appendChild(skipBtn);
1559
- const rightGroup = doc.createElement("div");
1560
- rightGroup.className = "veo-walkthrough-actions-right";
1561
- if (stepIndex > 0) {
1562
- const backBtn = doc.createElement("button");
1563
- backBtn.type = "button";
1564
- backBtn.className = "veo-walkthrough-btn-secondary";
1565
- backBtn.textContent = "Atr\xE1s";
1566
- backBtn.addEventListener("click", () => callbacks.onBack());
1567
- rightGroup.appendChild(backBtn);
1568
- }
1569
- const isLastStep = stepIndex === totalSteps - 1;
1570
- const primaryBtn = doc.createElement("button");
1571
- primaryBtn.type = "button";
1572
- primaryBtn.className = "veo-guide-cta";
1573
- const defaultLabel = isLastStep ? "Finalizar" : "Siguiente";
1574
- primaryBtn.textContent = typeof step.ctaText === "string" && step.ctaText ? step.ctaText : defaultLabel;
1575
- primaryBtn.addEventListener("click", () => {
1576
- if (isLastStep) callbacks.onComplete();
1577
- else callbacks.onNext();
1578
- });
1579
- rightGroup.appendChild(primaryBtn);
1580
- actions.appendChild(rightGroup);
1581
- container.appendChild(actions);
1582
- container.appendChild(createCloseButton(doc, () => callbacks.onSkip()));
1583
- return container;
1584
- }
1585
-
1586
- // src/plugins/guides/renderers/walkthrough-renderer.ts
1587
1702
  var WalkthroughRenderer = class extends BaseRenderer {
1588
1703
  async render(context) {
1589
1704
  const step = context.guide.guideSteps[context.currentStepIndex];
@@ -1637,6 +1752,7 @@ var PREVIEW_GUIDE_ID = "__veo_preview__";
1637
1752
  var GuidePreviewController = class {
1638
1753
  constructor() {
1639
1754
  this.singleStep = null;
1755
+ /** Renderer del paso de walkthrough activo (walkthrough tooltip o modal/banner). */
1640
1756
  this.walkthrough = null;
1641
1757
  this.walkthroughGuide = null;
1642
1758
  this.walkthroughIndex = 0;
@@ -1709,7 +1825,6 @@ var GuidePreviewController = class {
1709
1825
  safeDestroy(this.walkthrough);
1710
1826
  this.walkthrough = null;
1711
1827
  }
1712
- const renderer = new WalkthroughRenderer();
1713
1828
  const callbacks = {
1714
1829
  onNext: () => {
1715
1830
  const next = this.walkthroughIndex + 1;
@@ -1729,6 +1844,30 @@ var GuidePreviewController = class {
1729
1844
  onSkip: () => this.close(),
1730
1845
  onComplete: () => this.close()
1731
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();
1732
1871
  let success = false;
1733
1872
  try {
1734
1873
  success = await renderer.render({ guide, currentStepIndex: index, callbacks });