veo-sdk 0.2.1 → 0.3.2

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/README.md CHANGED
@@ -50,6 +50,42 @@ Cargá `veo.js` desde unpkg y llamá `init` en el `onload` del `<script>`
50
50
  para no romperte con un release futuro. También por jsDelivr:
51
51
  `https://cdn.jsdelivr.net/npm/veo-sdk@0.2.1/dist/veo.js`.
52
52
 
53
+ ## Editor visual de guías (modo builder)
54
+
55
+ El dashboard de Veo puede abrir tu app en un editor visual para diseñar guías
56
+ sobre ella (elegir el elemento ancla, previsualizar). Para eso abre tu app en un
57
+ `<iframe>` con `?veoBuilder=<token>` y necesita cargar el bundle del builder
58
+ (`veo-builder.js`) para hacer el handshake. Solo se descarga cuando la URL trae
59
+ ese token: **tus usuarios finales nunca lo cargan**.
60
+
61
+ ### CDN (`<script>`)
62
+
63
+ Funciona sin configurar nada: el SDK deriva la URL de `veo-builder.js` a partir
64
+ del `<script>` que cargó `veo.js`.
65
+
66
+ ### Bundler (Next / Vite / React)
67
+
68
+ Funciona sin configurar nada: con `init({ guides: true })` el SDK carga el
69
+ builder con un `import()` dinámico cuando la app se abre con `?veoBuilder`. Tu
70
+ bundler lo code-splitea, así que **no se descarga para tus usuarios finales** y
71
+ **no tenés que copiar `veo-builder.js` ni setear `builderUrl`**.
72
+
73
+ ```ts
74
+ init({ apiKey: 'pk_xxx', guides: true });
75
+ ```
76
+
77
+ > `builderUrl` sigue disponible como override manual (CDN propio, CSP estricto),
78
+ > pero ya no hace falta.
79
+
80
+ ### App con login (Supabase, Clerk, …)
81
+
82
+ Si tu app tiene login, el editor no puede autenticarse dentro del iframe (Google
83
+ OAuth = 403, cookies de terceros). Para diseñar sobre páginas protegidas hace
84
+ falta un **"modo diseño"** en tu app: dejar que Veo la embeba + entrar como
85
+ usuario demo. El SDK trae los helpers (`veoBuilderProxy`, `ensureVeoDesignSession`
86
+ en `veo-sdk/next`) y la **receta completa con Supabase** (2 archivos, copiable)
87
+ está en **[`docs/integrations/visual-editor-embed.md`](./docs/integrations/visual-editor-embed.md)**.
88
+
53
89
  ## API
54
90
 
55
91
  (En desarrollo. Implementación completa en sprints siguientes.)
@@ -0,0 +1,5 @@
1
+ export { createBuilderSession, initBuilderMode, startElementPicker, stopElementPicker } from './chunk-7FVIFPGF.mjs';
2
+ import './chunk-GHP7ZJCW.mjs';
3
+ import './chunk-PTG3BTJE.mjs';
4
+ //# sourceMappingURL=builder-AGIBF67L.mjs.map
5
+ //# sourceMappingURL=builder-AGIBF67L.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"builder-AGIBF67L.mjs"}
package/dist/builder.cjs CHANGED
@@ -10,6 +10,62 @@ function hasDocument() {
10
10
  return typeof document !== "undefined";
11
11
  }
12
12
 
13
+ // src/core/builder-constants.ts
14
+ var BUILDER_TOKEN_PARAM = "veoBuilder";
15
+ var BUILDER_ORIGIN_PARAM = "veoBuilderOrigin";
16
+ var BUILDER_PANEL_PARAM = "veoBuilderPanel";
17
+ var BUILDER_PANEL_PATH_PARAM = "veoBuilderPanelPath";
18
+ var BUILDER_GUIDE_PARAM = "veoGuide";
19
+
20
+ // src/core/builder-token.ts
21
+ var CTX_KEY = "veo:builder_ctx";
22
+ function session() {
23
+ try {
24
+ return window.sessionStorage;
25
+ } catch {
26
+ return null;
27
+ }
28
+ }
29
+ function urlParams() {
30
+ try {
31
+ return new URLSearchParams(window.location.search);
32
+ } catch {
33
+ return null;
34
+ }
35
+ }
36
+ function resolveBuilderContext() {
37
+ if (!hasWindow()) return null;
38
+ const params = urlParams();
39
+ const fromUrl = params?.get(BUILDER_TOKEN_PARAM);
40
+ if (fromUrl) {
41
+ const ctx = {
42
+ token: fromUrl,
43
+ origin: params?.get(BUILDER_ORIGIN_PARAM) ?? null,
44
+ panel: params?.get(BUILDER_PANEL_PARAM) === "1",
45
+ guide: params?.get(BUILDER_GUIDE_PARAM) ?? null,
46
+ panelPath: params?.get(BUILDER_PANEL_PATH_PARAM) ?? null
47
+ };
48
+ try {
49
+ session()?.setItem(CTX_KEY, JSON.stringify(ctx));
50
+ } catch {
51
+ }
52
+ return ctx;
53
+ }
54
+ try {
55
+ const raw = session()?.getItem(CTX_KEY);
56
+ return raw ? JSON.parse(raw) : null;
57
+ } catch {
58
+ return null;
59
+ }
60
+ }
61
+ function clearBuilderToken() {
62
+ if (!hasWindow()) return;
63
+ try {
64
+ session()?.removeItem(CTX_KEY);
65
+ } catch {
66
+ }
67
+ }
68
+
13
69
  // src/plugins/guides/constants.ts
14
70
  var GUIDE_Z_INDEX = 2147483640;
15
71
  var DEFAULT_ANCHOR_WAIT_MS = 5e3;
@@ -1535,8 +1591,79 @@ function safeDestroy(renderer) {
1535
1591
 
1536
1592
  // src/plugins/builder/bridge-protocol.ts
1537
1593
  var VEO_BUILDER_SOURCE = "veo-builder";
1538
- var BUILDER_TOKEN_PARAM = "veoBuilder";
1539
- var BUILDER_ORIGIN_PARAM = "veoBuilderOrigin";
1594
+
1595
+ // src/plugins/builder/builder-panel.ts
1596
+ var DEFAULT_PANEL_PATH = "/guides/design";
1597
+ function injectBuilderPanel(opts) {
1598
+ if (!hasWindow() || !hasDocument()) {
1599
+ return { target: () => null, teardown: () => {
1600
+ } };
1601
+ }
1602
+ const host = document.createElement("div");
1603
+ host.setAttribute("data-veo-builder-panel", "");
1604
+ const shadow = host.attachShadow({ mode: "open" });
1605
+ const style = document.createElement("style");
1606
+ style.textContent = PANEL_CSS;
1607
+ const container = document.createElement("div");
1608
+ container.className = "veo-panel";
1609
+ const header = document.createElement("div");
1610
+ header.className = "veo-panel-header";
1611
+ const title = document.createElement("span");
1612
+ title.className = "veo-panel-title";
1613
+ title.textContent = "Veo \xB7 Editor";
1614
+ const close = document.createElement("button");
1615
+ close.className = "veo-panel-close";
1616
+ close.type = "button";
1617
+ close.setAttribute("aria-label", "Cerrar");
1618
+ close.textContent = "\u2715";
1619
+ close.addEventListener("click", () => opts.onClose());
1620
+ header.append(title, close);
1621
+ const iframe = document.createElement("iframe");
1622
+ iframe.className = "veo-panel-frame";
1623
+ iframe.title = "Editor de Veo";
1624
+ iframe.src = buildPanelUrl(opts);
1625
+ container.append(header, iframe);
1626
+ shadow.append(style, container);
1627
+ document.body.appendChild(host);
1628
+ return {
1629
+ target: () => iframe.contentWindow,
1630
+ teardown: () => host.remove()
1631
+ };
1632
+ }
1633
+ function buildPanelUrl(opts) {
1634
+ const path = opts.panelPath ?? DEFAULT_PANEL_PATH;
1635
+ const url = new URL(path, opts.dashboardOrigin);
1636
+ url.searchParams.set("veoToken", opts.token);
1637
+ url.searchParams.set("veoApp", opts.appOrigin);
1638
+ if (opts.guide) url.searchParams.set("veoGuide", opts.guide);
1639
+ return url.toString();
1640
+ }
1641
+ var PANEL_CSS = `
1642
+ :host { all: initial; }
1643
+ .veo-panel {
1644
+ position: fixed; top: 0; right: 0; bottom: 0;
1645
+ width: 420px; max-width: 100vw;
1646
+ display: flex; flex-direction: column;
1647
+ background: #fff;
1648
+ box-shadow: -8px 0 30px rgba(0,0,0,0.18);
1649
+ z-index: 2147483647;
1650
+ font-family: system-ui, -apple-system, sans-serif;
1651
+ }
1652
+ .veo-panel-header {
1653
+ flex: 0 0 auto;
1654
+ display: flex; align-items: center; justify-content: space-between;
1655
+ padding: 8px 12px;
1656
+ background: #0f172a; color: #fff;
1657
+ font-size: 13px; font-weight: 600;
1658
+ }
1659
+ .veo-panel-close {
1660
+ border: 0; background: transparent; color: #fff;
1661
+ font-size: 15px; cursor: pointer; line-height: 1;
1662
+ padding: 4px 6px; border-radius: 6px;
1663
+ }
1664
+ .veo-panel-close:hover { background: rgba(255,255,255,0.15); }
1665
+ .veo-panel-frame { flex: 1 1 auto; width: 100%; border: 0; background: #fff; }
1666
+ `;
1540
1667
 
1541
1668
  // src/plugins/autocapture/constants.ts
1542
1669
  var HASHED_CLASS_PATTERNS = [
@@ -1822,24 +1949,23 @@ function stopElementPicker() {
1822
1949
  // src/plugins/builder/builder-mode.ts
1823
1950
  function initBuilderMode() {
1824
1951
  if (!hasWindow() || !hasDocument()) return null;
1825
- let params;
1826
- try {
1827
- params = new URLSearchParams(window.location.search);
1828
- } catch {
1829
- return null;
1830
- }
1831
- const token = params.get(BUILDER_TOKEN_PARAM);
1832
- if (!token) return null;
1833
- const dashboardOrigin = resolveDashboardOrigin(params);
1952
+ const ctx = resolveBuilderContext();
1953
+ if (!ctx) return null;
1954
+ const token = ctx.token;
1955
+ const dashboardOrigin = resolveDashboardOrigin(ctx.origin);
1834
1956
  if (!dashboardOrigin) return null;
1835
- const target = resolveTarget();
1836
- if (!target) return null;
1837
1957
  let picker = null;
1958
+ let panel = null;
1959
+ let staticTarget = null;
1960
+ const getTarget = () => panel ? panel.target() : staticTarget;
1838
1961
  const post = (event) => {
1839
- target.postMessage({ source: VEO_BUILDER_SOURCE, token, ...event }, dashboardOrigin);
1962
+ getTarget()?.postMessage({ source: VEO_BUILDER_SOURCE, token, ...event }, dashboardOrigin);
1840
1963
  };
1841
1964
  const handleCommand = (cmd) => {
1842
1965
  switch (cmd.type) {
1966
+ case "panel-ready":
1967
+ post({ type: "ready" });
1968
+ break;
1843
1969
  case "start-picker":
1844
1970
  picker?.stop();
1845
1971
  picker = startElementPicker({
@@ -1884,11 +2010,27 @@ function initBuilderMode() {
1884
2010
  picker = null;
1885
2011
  closeGuidePreview();
1886
2012
  post({ type: "closed" });
2013
+ panel?.teardown();
2014
+ panel = null;
2015
+ clearBuilderToken();
1887
2016
  };
2017
+ if (ctx.panel) {
2018
+ panel = injectBuilderPanel({
2019
+ dashboardOrigin,
2020
+ token,
2021
+ appOrigin: window.location.origin,
2022
+ guide: ctx.guide,
2023
+ panelPath: ctx.panelPath,
2024
+ onClose: () => teardown()
2025
+ });
2026
+ } else {
2027
+ staticTarget = resolveTarget();
2028
+ if (!staticTarget) return null;
2029
+ }
1888
2030
  const activate = () => {
1889
2031
  window.addEventListener("message", onMessage);
1890
2032
  window.addEventListener("pagehide", onPageHide);
1891
- post({ type: "ready" });
2033
+ if (!ctx.panel) post({ type: "ready" });
1892
2034
  };
1893
2035
  const cfg = window.__veoBuilder;
1894
2036
  if (cfg?.apiKey && cfg?.apiUrl) {
@@ -1921,16 +2063,15 @@ function resolveTarget() {
1921
2063
  if (window.parent && window.parent !== window) return window.parent;
1922
2064
  return null;
1923
2065
  }
1924
- function resolveDashboardOrigin(params) {
1925
- const explicit = params.get(BUILDER_ORIGIN_PARAM);
1926
- if (explicit) {
2066
+ function resolveDashboardOrigin(originHint) {
2067
+ if (originHint) {
1927
2068
  try {
1928
- return new URL(explicit).origin;
2069
+ return new URL(originHint).origin;
1929
2070
  } catch {
1930
2071
  return null;
1931
2072
  }
1932
2073
  }
1933
- if (document.referrer) {
2074
+ if (hasDocument() && document.referrer) {
1934
2075
  try {
1935
2076
  return new URL(document.referrer).origin;
1936
2077
  } catch {