veo-sdk 0.3.4 → 0.3.6

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-A73VSAZP.mjs';
2
+ import './chunk-FUMGOQJR.mjs';
3
+ import './chunk-PTG3BTJE.mjs';
4
+ //# sourceMappingURL=builder-MIPJRGOD.mjs.map
5
+ //# sourceMappingURL=builder-MIPJRGOD.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"builder-LXIR3WMJ.mjs"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"builder-MIPJRGOD.mjs"}
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
- try {
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
- return raw ? JSON.parse(raw) : null;
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
@@ -111,14 +180,17 @@ function buildStepContent(step, doc, callbacks) {
111
180
  actions.appendChild(cta);
112
181
  }
113
182
  container.appendChild(actions);
183
+ container.appendChild(createCloseButton(doc, () => callbacks.onDismiss()));
184
+ return container;
185
+ }
186
+ function createCloseButton(doc, onClose, className = "veo-guide-close") {
114
187
  const closeBtn = doc.createElement("button");
115
188
  closeBtn.type = "button";
116
- closeBtn.className = "veo-guide-close";
189
+ closeBtn.className = className;
117
190
  closeBtn.setAttribute("aria-label", "Cerrar");
118
191
  closeBtn.textContent = "\xD7";
119
- closeBtn.addEventListener("click", () => callbacks.onDismiss());
120
- container.appendChild(closeBtn);
121
- return container;
192
+ closeBtn.addEventListener("click", onClose);
193
+ return closeBtn;
122
194
  }
123
195
  function isSafeUrl(url) {
124
196
  if (typeof url !== "string" || url.length === 0) return false;
@@ -148,6 +220,16 @@ var ALIGN_JUSTIFY = {
148
220
  center: "center",
149
221
  right: "flex-end"
150
222
  };
223
+ var ALIGN_SELF = {
224
+ left: "flex-start",
225
+ center: "center",
226
+ right: "flex-end"
227
+ };
228
+ var ELEMENT_ALIGN_MODE = {
229
+ title: "text",
230
+ text: "text",
231
+ image: "self"
232
+ };
151
233
  var SHADOW = {
152
234
  none: "none",
153
235
  soft: "0 4px 14px rgba(0, 0, 0, 0.10)",
@@ -216,6 +298,43 @@ function applyDesignVars(host, style) {
216
298
  host.style.setProperty("--veo-pos-x", `${px * 100}%`);
217
299
  host.style.setProperty("--veo-pos-y", `${py * 100}%`);
218
300
  }
301
+ if (s.elements && typeof s.elements === "object") {
302
+ const el = s.elements;
303
+ applyElementVars(host, "title", el.title);
304
+ applyElementVars(host, "text", el.text);
305
+ applyElementVars(host, "image", el.image);
306
+ const cta = el.cta;
307
+ if (cta && typeof cta.align === "string" && ALIGN_JUSTIFY[cta.align]) {
308
+ host.style.setProperty("--veo-actions-justify", ALIGN_JUSTIFY[cta.align]);
309
+ }
310
+ }
311
+ }
312
+ function applyElementVars(host, prefix, raw) {
313
+ if (!raw || typeof raw !== "object") return;
314
+ const el = raw;
315
+ if (typeof el.align === "string" && (el.align === "left" || el.align === "center" || el.align === "right")) {
316
+ const mode = ELEMENT_ALIGN_MODE[prefix] ?? "text";
317
+ const value = mode === "self" ? ALIGN_SELF[el.align] : el.align;
318
+ host.style.setProperty(`--veo-${prefix}-align`, value);
319
+ }
320
+ if (typeof el.color === "string" && COLOR_RE.test(el.color.trim())) {
321
+ host.style.setProperty(`--veo-${prefix}-color`, el.color.trim());
322
+ }
323
+ if (typeof el.fontSize === "number" && Number.isFinite(el.fontSize)) {
324
+ host.style.setProperty(`--veo-${prefix}-size`, `${clamp(el.fontSize, 8, 72)}px`);
325
+ }
326
+ if (typeof el.width === "number" && Number.isFinite(el.width)) {
327
+ host.style.setProperty(`--veo-${prefix}-width`, `${clamp(el.width, 10, 100)}%`);
328
+ }
329
+ if (typeof el.radius === "number" && Number.isFinite(el.radius)) {
330
+ host.style.setProperty(`--veo-${prefix}-radius`, `${clamp(el.radius, 0, 48)}px`);
331
+ }
332
+ if (typeof el.marginTop === "number" && Number.isFinite(el.marginTop)) {
333
+ host.style.setProperty(`--veo-${prefix}-mt`, `${clamp(el.marginTop, 0, 64)}px`);
334
+ }
335
+ if (typeof el.marginBottom === "number" && Number.isFinite(el.marginBottom)) {
336
+ host.style.setProperty(`--veo-${prefix}-mb`, `${clamp(el.marginBottom, 0, 64)}px`);
337
+ }
219
338
  }
220
339
 
221
340
  // src/plugins/guides/styles.ts
@@ -282,6 +401,15 @@ var GUIDE_STYLES = `
282
401
  box-shadow: var(--veo-shadow, 0 8px 30px rgba(0, 0, 0, 0.18));
283
402
  animation: veo-fade-in 150ms ease-out;
284
403
  }
404
+ .veo-tooltip-arrow {
405
+ position: absolute;
406
+ width: 12px;
407
+ height: 12px;
408
+ background: var(--veo-bg);
409
+ border: var(--veo-border-width, 0) solid var(--veo-border-color, transparent);
410
+ transform: rotate(45deg);
411
+ pointer-events: none;
412
+ }
285
413
 
286
414
  .veo-inline {
287
415
  background: var(--veo-bg);
@@ -300,17 +428,25 @@ var GUIDE_STYLES = `
300
428
  display: flex; flex-direction: column;
301
429
  }
302
430
  .veo-guide-image {
303
- display: block; width: 100%; max-height: 180px;
304
- object-fit: cover; border-radius: 8px;
305
- margin-bottom: 12px;
431
+ display: block;
432
+ width: var(--veo-image-width, 100%);
433
+ max-height: 180px;
434
+ object-fit: cover;
435
+ align-self: var(--veo-image-align, stretch);
436
+ border-radius: var(--veo-image-radius, 8px);
437
+ margin: var(--veo-image-mt, 0) 0 var(--veo-image-mb, 12px);
306
438
  }
307
439
  .veo-guide-title {
308
- font-size: 18px; font-weight: 600; line-height: 1.3;
309
- margin: 0 0 6px; color: var(--veo-text);
440
+ font-size: var(--veo-title-size, 18px); font-weight: 600; line-height: 1.3;
441
+ text-align: var(--veo-title-align, left);
442
+ margin: var(--veo-title-mt, 0) 0 var(--veo-title-mb, 6px);
443
+ color: var(--veo-title-color, var(--veo-text));
310
444
  }
311
445
  .veo-guide-text {
312
- font-size: 14px; line-height: 1.5;
313
- margin: 0 0 16px; color: var(--veo-text-secondary);
446
+ font-size: var(--veo-text-size, 14px); line-height: 1.5;
447
+ text-align: var(--veo-text-align, left);
448
+ margin: var(--veo-text-mt, 0) 0 var(--veo-text-mb, 16px);
449
+ color: var(--veo-text-color, var(--veo-text-secondary));
314
450
  }
315
451
  .veo-guide-actions {
316
452
  display: flex; gap: 8px; justify-content: var(--veo-actions-justify);
@@ -447,6 +583,7 @@ var GUIDE_STYLES = `
447
583
  }
448
584
  .veo-custom-inline {
449
585
  display: block;
586
+ position: relative;
450
587
  margin: 12px 0;
451
588
  }
452
589
  .veo-custom-frame {
@@ -456,6 +593,21 @@ var GUIDE_STYLES = `
456
593
  background: transparent;
457
594
  color-scheme: normal;
458
595
  }
596
+ /*
597
+ * X de cerrar de las gu\xEDas custom: el HTML del cliente vive en un iframe
598
+ * sandbox, as\xED que el bot\xF3n lo pone el host POR ENCIMA del iframe. Con fondo
599
+ * propio para ser visible sobre cualquier contenido.
600
+ */
601
+ .veo-custom-close {
602
+ position: absolute; top: 6px; right: 6px; z-index: 1;
603
+ width: 22px; height: 22px; padding: 0;
604
+ display: flex; align-items: center; justify-content: center;
605
+ font-size: 16px; line-height: 1; color: #6b7280;
606
+ background: rgba(255, 255, 255, 0.92);
607
+ border: 1px solid rgba(0, 0, 0, 0.08); border-radius: 50%;
608
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.14); cursor: pointer;
609
+ }
610
+ .veo-custom-close:hover { color: #111827; }
459
611
 
460
612
  @keyframes veo-fade-in { from { opacity: 0; } to { opacity: 1; } }
461
613
  @keyframes veo-slide-up {
@@ -628,7 +780,11 @@ function mountCustomFrame(doc, step, context, opts) {
628
780
  const hasFixedHeight = opts.fixedHeight !== void 0 && opts.fixedHeight !== null;
629
781
  if (hasFixedHeight) iframe.style.height = toCssSize(opts.fixedHeight);
630
782
  const token = uuidv7();
631
- iframe.srcdoc = buildSrcdoc(step.html ?? "", step.css ?? "", step.js ?? "", token);
783
+ const wantsTailwind = step.style?.tailwind === true;
784
+ iframe.srcdoc = buildSrcdoc(step.html ?? "", step.css ?? "", step.js ?? "", token, {
785
+ tokens: collectHostTokens(),
786
+ tailwind: wantsTailwind
787
+ });
632
788
  const close = () => context.onClose();
633
789
  const onMessage = (event) => {
634
790
  if (event.source !== iframe.contentWindow) return;
@@ -677,12 +833,37 @@ function mountCustomFrame(doc, step, context, opts) {
677
833
  function toCssSize(value) {
678
834
  return typeof value === "number" ? `${value}px` : value;
679
835
  }
836
+ function collectHostTokens() {
837
+ if (typeof window === "undefined" || typeof document === "undefined") return "";
838
+ try {
839
+ const rootStyle = getComputedStyle(document.documentElement);
840
+ const decls = [];
841
+ const seen = /* @__PURE__ */ new Set();
842
+ for (let i = 0; i < rootStyle.length && decls.length < 400; i++) {
843
+ const name = rootStyle.item(i);
844
+ if (!name.startsWith("--") || seen.has(name)) continue;
845
+ seen.add(name);
846
+ const value = rootStyle.getPropertyValue(name).trim();
847
+ if (!value || value.length > 200 || /[<>{}]/.test(value)) continue;
848
+ decls.push(`${name}:${value}`);
849
+ }
850
+ const bodyFont = getComputedStyle(document.body).fontFamily;
851
+ if (bodyFont && bodyFont.length <= 200 && !/[<>{}]/.test(bodyFont)) {
852
+ decls.push(`--veo-host-font:${bodyFont}`);
853
+ }
854
+ return decls.length ? `:root{${decls.join(";")}}` : "";
855
+ } catch {
856
+ return "";
857
+ }
858
+ }
680
859
  function isRecord(value) {
681
860
  return typeof value === "object" && value !== null && !Array.isArray(value);
682
861
  }
683
- function buildSrcdoc(html, css, js, token) {
862
+ function buildSrcdoc(html, css, js, token, opts) {
684
863
  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
- return `<!DOCTYPE html><html><head><meta charset="utf-8"><style>html,body{margin:0;padding:0;background:transparent;}</style><style>${escapeClosing(css, "style")}</style></head><body>${html}<script>${bridge}</script>` + (js ? `<script>${escapeClosing(js, "script")}</script>` : "") + "</body></html>";
864
+ const tokensStyle = opts.tokens ? `<style>${escapeClosing(opts.tokens, "style")}</style>` : "";
865
+ const tailwind = opts.tailwind ? '<script src="https://cdn.tailwindcss.com"></script>' : "";
866
+ 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
867
  }
687
868
  function escapeClosing(code, tag) {
688
869
  const rx = tag === "script" ? /<\/script/gi : /<\/style/gi;
@@ -715,6 +896,20 @@ var CustomRenderer = class extends BaseRenderer {
715
896
  });
716
897
  this.registerCleanup(cleanup);
717
898
  wrapper.appendChild(iframe);
899
+ wrapper.appendChild(
900
+ createCloseButton(
901
+ doc,
902
+ () => {
903
+ context.onInteraction({
904
+ guideId: context.guide.guideId,
905
+ stepIndex: 0,
906
+ action: "dismissed"
907
+ });
908
+ context.onClose();
909
+ },
910
+ "veo-custom-close"
911
+ )
912
+ );
718
913
  root.appendChild(wrapper);
719
914
  if (placement.mode === "floating") {
720
915
  applyFloatingPosition(wrapper, placement);
@@ -852,13 +1047,7 @@ function mountFormContent(card, context, doc) {
852
1047
  });
853
1048
  });
854
1049
  content.appendChild(form);
855
- const closeBtn = doc.createElement("button");
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);
1050
+ content.appendChild(createCloseButton(doc, dismiss));
862
1051
  card.appendChild(content);
863
1052
  }
864
1053
  function showThanks(card, doc, note) {
@@ -1122,6 +1311,20 @@ var InlineCustomRenderer = class extends BaseRenderer {
1122
1311
  const { iframe, cleanup } = mountCustomFrame(doc, step, context, { width: "100%" });
1123
1312
  this.registerCleanup(cleanup);
1124
1313
  wrapper.appendChild(iframe);
1314
+ wrapper.appendChild(
1315
+ createCloseButton(
1316
+ doc,
1317
+ () => {
1318
+ context.onInteraction({
1319
+ guideId: context.guide.guideId,
1320
+ stepIndex: 0,
1321
+ action: "dismissed"
1322
+ });
1323
+ context.onClose();
1324
+ },
1325
+ "veo-custom-close"
1326
+ )
1327
+ );
1125
1328
  root.appendChild(wrapper);
1126
1329
  context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
1127
1330
  }
@@ -1229,6 +1432,25 @@ var ModalRenderer = class extends BaseRenderer {
1229
1432
  });
1230
1433
  }
1231
1434
  };
1435
+
1436
+ // src/plugins/guides/renderers/floating-arrow.ts
1437
+ var STATIC_SIDE = {
1438
+ top: "bottom",
1439
+ bottom: "top",
1440
+ left: "right",
1441
+ right: "left"
1442
+ };
1443
+ function positionArrow(arrowEl, placement, data) {
1444
+ const side = STATIC_SIDE[placement.split("-")[0] ?? "bottom"] ?? "top";
1445
+ for (const prop of ["top", "bottom", "left", "right"]) {
1446
+ arrowEl.style.setProperty(prop, "");
1447
+ }
1448
+ if (data?.x != null) arrowEl.style.setProperty("left", `${data.x}px`);
1449
+ if (data?.y != null) arrowEl.style.setProperty("top", `${data.y}px`);
1450
+ arrowEl.style.setProperty(side, "-6px");
1451
+ }
1452
+
1453
+ // src/plugins/guides/renderers/tooltip-renderer.ts
1232
1454
  var TooltipRenderer = class extends BaseRenderer {
1233
1455
  async render(context) {
1234
1456
  const step = context.guide.guideSteps[0];
@@ -1258,14 +1480,18 @@ var TooltipRenderer = class extends BaseRenderer {
1258
1480
  onDismiss: () => dismiss("dismissed")
1259
1481
  });
1260
1482
  tooltip.appendChild(content);
1483
+ const arrowEl = ownerDocument.createElement("div");
1484
+ arrowEl.className = "veo-tooltip-arrow";
1485
+ tooltip.appendChild(arrowEl);
1261
1486
  root.appendChild(tooltip);
1262
1487
  const updatePosition = async () => {
1263
- const { x, y } = await dom.computePosition(anchor, tooltip, {
1488
+ const { x, y, placement, middlewareData } = await dom.computePosition(anchor, tooltip, {
1264
1489
  placement: "bottom",
1265
- middleware: [dom.offset(8), dom.flip(), dom.shift({ padding: 8 })]
1490
+ middleware: [dom.offset(10), dom.flip(), dom.shift({ padding: 8 }), dom.arrow({ element: arrowEl })]
1266
1491
  });
1267
1492
  tooltip.style.left = `${x}px`;
1268
1493
  tooltip.style.top = `${y}px`;
1494
+ positionArrow(arrowEl, placement, middlewareData.arrow);
1269
1495
  };
1270
1496
  await updatePosition();
1271
1497
  const reposition = () => {
@@ -1353,6 +1579,7 @@ function buildWalkthroughStepContent(step, stepIndex, totalSteps, doc, callbacks
1353
1579
  rightGroup.appendChild(primaryBtn);
1354
1580
  actions.appendChild(rightGroup);
1355
1581
  container.appendChild(actions);
1582
+ container.appendChild(createCloseButton(doc, () => callbacks.onSkip()));
1356
1583
  return container;
1357
1584
  }
1358
1585
 
@@ -1404,21 +1631,6 @@ var WalkthroughRenderer = class extends BaseRenderer {
1404
1631
  return true;
1405
1632
  }
1406
1633
  };
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
1634
 
1423
1635
  // src/plugins/guides/guide-preview.ts
1424
1636
  var PREVIEW_GUIDE_ID = "__veo_preview__";
@@ -2145,7 +2357,7 @@ function initBuilderMode() {
2145
2357
  if (!ctx.panel) post({ type: "ready" });
2146
2358
  };
2147
2359
  const cfg = window.__veoBuilder;
2148
- if (cfg?.apiKey && cfg?.apiUrl) {
2360
+ if (!ctx.panel && cfg?.apiKey && cfg?.apiUrl) {
2149
2361
  void verifyBuilderToken(cfg.apiUrl, cfg.apiKey, token).then((ok) => {
2150
2362
  if (ok) activate();
2151
2363
  else if (window.console) console.warn("[veo] token de builder inv\xE1lido \u2014 modo no activado");