veo-sdk 0.5.4 → 0.5.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 { VEO_BLOCK_MIME, attachBlockDropTarget, attachDesignManipulator, createBuilderSession, initBuilderMode, startElementPicker, stopElementPicker } from './chunk-A4RHMCCM.mjs';
2
+ import './chunk-764FSEHS.mjs';
3
+ import './chunk-PTG3BTJE.mjs';
4
+ //# sourceMappingURL=builder-AMCPHL4K.mjs.map
5
+ //# sourceMappingURL=builder-AMCPHL4K.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"builder-MFTOJV6F.mjs"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"builder-AMCPHL4K.mjs"}
package/dist/builder.cjs CHANGED
@@ -32,7 +32,8 @@ function attachBlockDropTarget(options) {
32
32
  const types = event.dataTransfer?.types;
33
33
  return !!types && Array.from(types).includes(VEO_BLOCK_MIME);
34
34
  };
35
- const locate = (event) => {
35
+ const NEAR_PX = 48;
36
+ const locate = (clientY) => {
36
37
  const container = contentEl();
37
38
  if (!container) return null;
38
39
  const rect = container.getBoundingClientRect();
@@ -41,7 +42,7 @@ function attachBlockDropTarget(options) {
41
42
  let y = kids.length === 0 ? rect.top + rect.height / 2 : 0;
42
43
  for (const kid of kids) {
43
44
  const r = kid.getBoundingClientRect();
44
- if (event.clientY < r.top + r.height / 2) {
45
+ if (clientY < r.top + r.height / 2) {
45
46
  const idx = Number(kid.dataset.veoIdx);
46
47
  beforeIndex = Number.isFinite(idx) ? idx : null;
47
48
  y = r.top;
@@ -51,9 +52,26 @@ function attachBlockDropTarget(options) {
51
52
  }
52
53
  return { beforeIndex, y, rect };
53
54
  };
55
+ const overContent = (x, y, rect) => x >= rect.left - NEAR_PX && x <= rect.right + NEAR_PX && y >= rect.top - NEAR_PX && y <= rect.bottom + NEAR_PX;
56
+ const hoverAt = (x, y) => {
57
+ const at = locate(y);
58
+ if (!at || !overContent(x, y, at.rect)) {
59
+ hideLine();
60
+ return false;
61
+ }
62
+ spot = at;
63
+ showLine(at);
64
+ return true;
65
+ };
66
+ const dropAt = (x, y) => {
67
+ const at = locate(y);
68
+ hideLine();
69
+ if (!at || !overContent(x, y, at.rect)) return null;
70
+ return { beforeIndex: at.beforeIndex };
71
+ };
54
72
  const onDragOver = (event) => {
55
73
  if (!isBlockDrag(event)) return;
56
- const at = locate(event);
74
+ const at = locate(event.clientY);
57
75
  if (!at) return;
58
76
  event.preventDefault();
59
77
  if (event.dataTransfer) event.dataTransfer.dropEffect = "copy";
@@ -64,7 +82,7 @@ function attachBlockDropTarget(options) {
64
82
  if (!isBlockDrag(event)) return;
65
83
  event.preventDefault();
66
84
  const blockType = event.dataTransfer?.getData(VEO_BLOCK_MIME) ?? "";
67
- const at = spot ?? locate(event);
85
+ const at = spot ?? locate(event.clientY);
68
86
  hideLine();
69
87
  if (!blockType || !at) return;
70
88
  onDrop({ stepIndex, blockType, beforeIndex: at.beforeIndex });
@@ -78,6 +96,9 @@ function attachBlockDropTarget(options) {
78
96
  host.addEventListener("drop", onDropEvent);
79
97
  host.addEventListener("dragleave", onDragLeave);
80
98
  return {
99
+ hoverAt,
100
+ dropAt,
101
+ cancelHover: hideLine,
81
102
  detach() {
82
103
  host.removeEventListener("dragover", onDragOver);
83
104
  host.removeEventListener("drop", onDropEvent);
@@ -501,7 +522,8 @@ function buildButtonBlock(block, doc, callbacks) {
501
522
  btn.textContent = typeof block.text === "string" && block.text ? block.text : "OK";
502
523
  applyBlockStyle(btn, block.style, "button");
503
524
  btn.addEventListener("click", () => {
504
- const action = block.action ?? "dismiss";
525
+ const raw = block.action ?? "dismiss";
526
+ const action = raw === "next" || raw === "url" || raw === "guide" ? raw : "dismiss";
505
527
  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;
506
528
  const meta = {
507
529
  ...typeof block.id === "string" && block.id ? { buttonId: block.id } : {},
@@ -897,6 +919,35 @@ var GUIDE_STYLES = `
897
919
  animation: veo-fade-in 160ms ease-out;
898
920
  }
899
921
 
922
+ /* \u2500\u2500 Modelo unificado (composite) \u2500\u2500 */
923
+ .veo-composite-overlay {
924
+ position: fixed; inset: 0;
925
+ z-index: ${GUIDE_Z_INDEX};
926
+ pointer-events: none;
927
+ }
928
+ /* Backdrop atenuado (solo centro con backdrop activo). */
929
+ .veo-composite-overlay--dim {
930
+ background: rgba(0, 0, 0, 0.5);
931
+ pointer-events: auto;
932
+ }
933
+ .veo-composite-anchored {
934
+ position: fixed;
935
+ z-index: ${GUIDE_Z_INDEX};
936
+ }
937
+ /* Card gen\xE9rica del modelo unificado. El dise\xF1o (bg/borde/padding/\u2026) lo aplica
938
+ el renderer como estilos inline desde step.design; ac\xE1 van los defaults. */
939
+ .veo-card {
940
+ pointer-events: auto;
941
+ background: var(--veo-bg, #ffffff);
942
+ color: var(--veo-text, #1f2937);
943
+ border-radius: var(--veo-radius, 12px);
944
+ padding: 20px;
945
+ max-width: 90vw;
946
+ box-shadow: var(--veo-shadow, 0 12px 40px rgba(0, 0, 0, 0.18));
947
+ animation: veo-fade-in 160ms ease-out;
948
+ }
949
+ .veo-composite-anchored .veo-card, .veo-composite-overlay .veo-card { position: relative; }
950
+
900
951
  .veo-guide-content {
901
952
  position: relative;
902
953
  display: flex; flex-direction: column;
@@ -1703,6 +1754,182 @@ var BannerRenderer = class extends BaseRenderer {
1703
1754
  function bannerClassOf(position, embedded) {
1704
1755
  return embedded ? "veo-banner veo-banner-embedded" : `veo-banner veo-banner-${position}`;
1705
1756
  }
1757
+ var CompositeRenderer = class extends BaseRenderer {
1758
+ async render(context) {
1759
+ const idx = context.nav?.stepIndex ?? 0;
1760
+ const step = context.guide.guideSteps[idx];
1761
+ if (!step) return;
1762
+ const anchorSelector = step.position === "relative-to-element" || step.presentation === "inline" ? step.anchorSelector?.trim() || void 0 : void 0;
1763
+ let anchor = null;
1764
+ if (anchorSelector) {
1765
+ anchor = await waitForElement(anchorSelector);
1766
+ if (!anchor) return;
1767
+ }
1768
+ const { host, root } = this.createHost();
1769
+ const doc = root.ownerDocument ?? document;
1770
+ const card = doc.createElement("div");
1771
+ card.className = "veo-card";
1772
+ applyCompositeDesign(card, step.design ?? {});
1773
+ const dismiss = (action, meta) => {
1774
+ context.onInteraction({
1775
+ guideId: context.guide.guideId,
1776
+ stepIndex: idx,
1777
+ action,
1778
+ ...meta?.id ? { metadata: { buttonId: meta.id } } : {}
1779
+ });
1780
+ };
1781
+ const synthetic = { blocks: step.blocks ?? [] };
1782
+ for (const node of buildContentNodes(synthetic, doc)) card.appendChild(node);
1783
+ const buttons = (step.blocks ?? []).filter((b) => b.type === "button");
1784
+ if (buttons.length) {
1785
+ const row = doc.createElement("div");
1786
+ row.className = "veo-guide-actions";
1787
+ for (const b of buttons) row.appendChild(this.buildButton(b, doc, context, dismiss));
1788
+ card.appendChild(row);
1789
+ }
1790
+ card.appendChild(
1791
+ createCloseButton(doc, () => {
1792
+ dismiss("dismissed");
1793
+ this.closeOrSkip(context);
1794
+ })
1795
+ );
1796
+ if (step.presentation === "inline" && anchor) {
1797
+ host.style.display = "block";
1798
+ const pos = inlinePosOf(step.inlinePosition);
1799
+ root.appendChild(card);
1800
+ insertHost(anchor, host, pos);
1801
+ this.registerCleanup(keepHostAttached(host, anchorSelector, pos));
1802
+ } else if (step.position === "relative-to-element" && anchor) {
1803
+ const wrapper = doc.createElement("div");
1804
+ wrapper.className = "veo-composite-anchored";
1805
+ wrapper.appendChild(card);
1806
+ root.appendChild(wrapper);
1807
+ this.registerCleanup(await anchorPosition(anchor, wrapper));
1808
+ } else {
1809
+ const overlay = doc.createElement("div");
1810
+ overlay.className = "veo-composite-overlay";
1811
+ if (step.position === "center" && step.design?.backdrop !== false) {
1812
+ overlay.classList.add("veo-composite-overlay--dim");
1813
+ overlay.addEventListener("click", (e) => {
1814
+ if (e.target === overlay) {
1815
+ dismiss("dismissed");
1816
+ this.closeOrSkip(context);
1817
+ }
1818
+ });
1819
+ }
1820
+ applyViewportPosition(card, step.position);
1821
+ overlay.appendChild(card);
1822
+ root.appendChild(overlay);
1823
+ }
1824
+ context.onInteraction({ guideId: context.guide.guideId, stepIndex: idx, action: "shown" });
1825
+ }
1826
+ /** Botón con enrutado de acción (navegación / cierre / url / guía). */
1827
+ buildButton(block, doc, context, emit) {
1828
+ const btn = doc.createElement("button");
1829
+ btn.type = "button";
1830
+ btn.className = "veo-guide-cta";
1831
+ btn.textContent = block.text?.trim() || "OK";
1832
+ btn.addEventListener("click", () => {
1833
+ emit("cta_clicked", block);
1834
+ const nav = context.nav?.callbacks;
1835
+ switch (block.action) {
1836
+ case "next":
1837
+ case "submit-forms":
1838
+ case "submit-forms-advance":
1839
+ if (nav) nav.onNext();
1840
+ else context.onClose();
1841
+ break;
1842
+ case "prev":
1843
+ nav?.onBack();
1844
+ break;
1845
+ case "url":
1846
+ if (typeof block.url === "string" && isSafeUrl(block.url)) {
1847
+ window.open(block.url, "_blank", "noopener,noreferrer");
1848
+ }
1849
+ break;
1850
+ case "guide":
1851
+ if (block.targetGuideId) context.onOpenGuide?.(block.targetGuideId);
1852
+ this.closeOrSkip(context);
1853
+ break;
1854
+ default:
1855
+ this.closeOrSkip(context);
1856
+ }
1857
+ });
1858
+ return btn;
1859
+ }
1860
+ closeOrSkip(context) {
1861
+ if (context.nav?.callbacks) context.nav.callbacks.onSkip();
1862
+ else context.onClose();
1863
+ }
1864
+ };
1865
+ function inlinePosOf(p) {
1866
+ return p === "before" || p === "prepend" || p === "append" ? p : "after";
1867
+ }
1868
+ function applyViewportPosition(card, position) {
1869
+ const s = card.style;
1870
+ s.position = "fixed";
1871
+ const M = "16px";
1872
+ const v = position.includes("top") ? "top" : position.includes("bottom") ? "bottom" : "middle";
1873
+ const h = position.includes("left") ? "left" : position.includes("right") ? "right" : "middle";
1874
+ if (position === "center") {
1875
+ s.top = "50%";
1876
+ s.left = "50%";
1877
+ s.transform = "translate(-50%, -50%)";
1878
+ return;
1879
+ }
1880
+ if (v === "top") s.top = M;
1881
+ else if (v === "bottom") s.bottom = M;
1882
+ else {
1883
+ s.top = "50%";
1884
+ s.transform = "translateY(-50%)";
1885
+ }
1886
+ if (h === "left") s.left = M;
1887
+ else if (h === "right") s.right = M;
1888
+ else {
1889
+ s.left = "50%";
1890
+ s.transform = `${s.transform} translateX(-50%)`.trim();
1891
+ }
1892
+ }
1893
+ async function anchorPosition(anchor, wrapper) {
1894
+ const update = async () => {
1895
+ const { x, y } = await dom.computePosition(anchor, wrapper, {
1896
+ placement: "bottom",
1897
+ strategy: "fixed",
1898
+ middleware: [dom.offset(8), dom.flip(), dom.shift({ padding: 8 })]
1899
+ });
1900
+ wrapper.style.left = `${x}px`;
1901
+ wrapper.style.top = `${y}px`;
1902
+ };
1903
+ await update();
1904
+ const reposition = () => void update();
1905
+ window.addEventListener("scroll", reposition, true);
1906
+ window.addEventListener("resize", reposition);
1907
+ return () => {
1908
+ window.removeEventListener("scroll", reposition, true);
1909
+ window.removeEventListener("resize", reposition);
1910
+ };
1911
+ }
1912
+ function applyCompositeDesign(card, design) {
1913
+ const s = card.style;
1914
+ const px = (v) => typeof v === "number" && Number.isFinite(v) ? `${v}px` : null;
1915
+ const strv = (v) => typeof v === "string" && v ? v : null;
1916
+ if (strv(design.background)) s.background = design.background;
1917
+ if (strv(design.textColor)) s.color = design.textColor;
1918
+ if (px(design.fontSize)) s.fontSize = px(design.fontSize);
1919
+ if (strv(design.fontWeight)) s.fontWeight = design.fontWeight;
1920
+ if (strv(design.align)) s.textAlign = design.align;
1921
+ if (typeof design.lineHeight === "number") s.lineHeight = `${design.lineHeight}%`;
1922
+ if (px(design.radius)) s.borderRadius = px(design.radius);
1923
+ if (px(design.width)) s.width = px(design.width);
1924
+ const bw = px(design.borderWidth);
1925
+ if (bw) s.border = `${bw} solid ${strv(design.borderColor) ?? "#e5e5e5"}`;
1926
+ for (const side of ["Top", "Right", "Bottom", "Left"]) {
1927
+ const p = px(design[`padding${side}`]);
1928
+ if (p) s.setProperty(`padding-${side.toLowerCase()}`, p);
1929
+ const m = px(design[`margin${side}`]);
1930
+ if (m) s.setProperty(`margin-${side.toLowerCase()}`, m);
1931
+ }
1932
+ }
1706
1933
 
1707
1934
  // src/utils/uuid.ts
1708
1935
  function uuidv7() {
@@ -2585,6 +2812,13 @@ var GuidePreviewController = class {
2585
2812
  this.input = input;
2586
2813
  const guide = normalize(input);
2587
2814
  const start = typeof input.startStepIndex === "number" ? input.startStepIndex : 0;
2815
+ if (guide.guideType === "composite") {
2816
+ const s = Math.min(Math.max(0, start), guide.guideSteps.length - 1);
2817
+ const active = guide.guideSteps[s];
2818
+ if (active) guide.guideSteps = [active];
2819
+ const ready2 = this.renderSingleStep(guide);
2820
+ return { close: () => this.close(), ready: ready2, host: () => this.activeHost() };
2821
+ }
2588
2822
  const ready = guide.guideType === "walkthrough" ? this.startWalkthrough(guide, start) : this.renderSingleStep(guide);
2589
2823
  return { close: () => this.close(), ready, host: () => this.activeHost() };
2590
2824
  }
@@ -2764,8 +2998,12 @@ function createSingleStepRenderer(type) {
2764
2998
  return new InlineFormRenderer();
2765
2999
  case "badge":
2766
3000
  return new BadgeRenderer();
3001
+ case "composite":
3002
+ return new CompositeRenderer();
2767
3003
  case "walkthrough":
2768
3004
  return null;
3005
+ default:
3006
+ return null;
2769
3007
  }
2770
3008
  }
2771
3009
  function safeDestroy(renderer) {
@@ -2787,7 +3025,7 @@ var MIN_H = 320;
2787
3025
  var MARGIN = 16;
2788
3026
  function injectBuilderPanel(opts) {
2789
3027
  if (!hasWindow() || !hasDocument()) {
2790
- return { target: () => null, teardown: () => {
3028
+ return { target: () => null, frameRect: () => null, teardown: () => {
2791
3029
  } };
2792
3030
  }
2793
3031
  const panelUrl = buildPanelUrl(opts);
@@ -2904,6 +3142,7 @@ function injectBuilderPanel(opts) {
2904
3142
  });
2905
3143
  return {
2906
3144
  target: () => popped && !popped.closed ? popped : iframe.contentWindow,
3145
+ frameRect: () => popped && !popped.closed ? null : iframe.getBoundingClientRect(),
2907
3146
  teardown: () => {
2908
3147
  stopPoll();
2909
3148
  if (popped && !popped.closed) popped.close();
@@ -3488,6 +3727,8 @@ function initBuilderMode() {
3488
3727
  let staticTarget = null;
3489
3728
  let manipulator = null;
3490
3729
  let dropTarget = null;
3730
+ let dragGhost = null;
3731
+ let previewStepIndex = 0;
3491
3732
  let pendingPreview = null;
3492
3733
  const getTarget = () => panel ? panel.target() : staticTarget;
3493
3734
  const post = (event) => {
@@ -3498,6 +3739,7 @@ function initBuilderMode() {
3498
3739
  manipulator = null;
3499
3740
  dropTarget?.detach();
3500
3741
  dropTarget = null;
3742
+ previewStepIndex = cmd.guide.startStepIndex ?? 0;
3501
3743
  const handle = previewGuide(cmd.guide);
3502
3744
  if (!cmd.editable) return;
3503
3745
  void handle.ready.then((result) => {
@@ -3523,6 +3765,47 @@ function initBuilderMode() {
3523
3765
  });
3524
3766
  });
3525
3767
  };
3768
+ const hideDragGhost = () => {
3769
+ dragGhost?.remove();
3770
+ dragGhost = null;
3771
+ };
3772
+ const moveDragGhost = (x, y, label) => {
3773
+ if (!dragGhost) {
3774
+ dragGhost = document.createElement("div");
3775
+ dragGhost.style.cssText = "position:fixed;padding:4px 10px;border-radius:999px;background:#1c1917;color:#fff;font:12px/1.4 system-ui,sans-serif;box-shadow:0 4px 14px rgba(0,0,0,0.35);border:1px solid rgba(255,255,255,0.25);pointer-events:none;z-index:2147483647;";
3776
+ document.body.appendChild(dragGhost);
3777
+ }
3778
+ dragGhost.textContent = label;
3779
+ dragGhost.style.left = `${x + 14}px`;
3780
+ dragGhost.style.top = `${y + 12}px`;
3781
+ };
3782
+ const handleBlockDrag = (cmd) => {
3783
+ const rect = panel?.frameRect();
3784
+ if (!rect || !dropTarget) {
3785
+ if (cmd.phase !== "move") hideDragGhost();
3786
+ dropTarget?.cancelHover();
3787
+ return;
3788
+ }
3789
+ const x = rect.left + cmd.x;
3790
+ const y = rect.top + cmd.y;
3791
+ if (cmd.phase === "move") {
3792
+ moveDragGhost(x, y, cmd.label || cmd.blockType);
3793
+ dropTarget.hoverAt(x, y);
3794
+ return;
3795
+ }
3796
+ hideDragGhost();
3797
+ if (cmd.phase === "cancel") {
3798
+ dropTarget.cancelHover();
3799
+ return;
3800
+ }
3801
+ const spotAt = dropTarget.dropAt(x, y);
3802
+ if (spotAt) {
3803
+ post({
3804
+ type: "block-dropped",
3805
+ payload: { stepIndex: previewStepIndex, blockType: cmd.blockType, ...spotAt }
3806
+ });
3807
+ }
3808
+ };
3526
3809
  const handleCommand = (cmd) => {
3527
3810
  switch (cmd.type) {
3528
3811
  case "panel-ready":
@@ -3552,11 +3835,15 @@ function initBuilderMode() {
3552
3835
  if (manipulator?.isDragging()) pendingPreview = cmd;
3553
3836
  else applyPreview(cmd);
3554
3837
  break;
3838
+ case "block-drag":
3839
+ handleBlockDrag(cmd);
3840
+ break;
3555
3841
  case "close-preview":
3556
3842
  manipulator?.detach();
3557
3843
  manipulator = null;
3558
3844
  dropTarget?.detach();
3559
3845
  dropTarget = null;
3846
+ hideDragGhost();
3560
3847
  pendingPreview = null;
3561
3848
  closeGuidePreview();
3562
3849
  break;
@@ -3581,6 +3868,7 @@ function initBuilderMode() {
3581
3868
  manipulator = null;
3582
3869
  dropTarget?.detach();
3583
3870
  dropTarget = null;
3871
+ hideDragGhost();
3584
3872
  pendingPreview = null;
3585
3873
  closeGuidePreview();
3586
3874
  post({ type: "closed" });