veo-sdk 0.3.14 → 0.3.16

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/index.cjs CHANGED
@@ -944,6 +944,24 @@ var init_styles = __esm({
944
944
  cursor: pointer; color: #9ca3af; padding: 0;
945
945
  }
946
946
  .veo-guide-close:hover { color: var(--veo-text); }
947
+ /*
948
+ * Si el PRIMER bloque es una imagen, la \u2715 quedar\xEDa sobre la esquina de la foto
949
+ * (invisible seg\xFAn el color). En ese caso pasa a ser una pastilla flotante con
950
+ * fondo propio, legible sobre cualquier imagen. (Sin soporte de :has() queda el
951
+ * estilo base \u2014 mismo comportamiento de antes, sin regresi\xF3n.)
952
+ */
953
+ .veo-guide-content:has(> .veo-guide-image:first-child) > .veo-guide-close {
954
+ top: 2px; right: 2px;
955
+ display: flex; align-items: center; justify-content: center;
956
+ font-size: 16px; color: #374151;
957
+ background: rgba(255, 255, 255, 0.92);
958
+ border: 1px solid rgba(0, 0, 0, 0.08);
959
+ border-radius: 50%;
960
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
961
+ }
962
+ .veo-guide-content:has(> .veo-guide-image:first-child) > .veo-guide-close:hover {
963
+ color: #111827;
964
+ }
947
965
 
948
966
  .veo-form { display: flex; flex-direction: column; gap: 14px; margin-bottom: 16px; }
949
967
  .veo-form-field { display: flex; flex-direction: column; gap: 6px; }
@@ -1096,6 +1114,37 @@ var init_base_renderer = __esm({
1096
1114
  this.host = null;
1097
1115
  this.shadow = null;
1098
1116
  this.cleanups = [];
1117
+ // ── Update in-place (preview en vivo del editor) ──
1118
+ // La subclase setea estas piezas en render() para habilitarlo: el contenedor
1119
+ // del contenido, el nodo de contenido actual y cómo reconstruirlo.
1120
+ this.liveContainer = null;
1121
+ this.liveContent = null;
1122
+ this.liveBuild = null;
1123
+ /** Clave de anclaje/estructura fijada en render(); si cambia → remontar. */
1124
+ this.liveKey = "";
1125
+ }
1126
+ /** Clave de anclaje derivada del step (override por subclase). */
1127
+ liveKeyOf(_step) {
1128
+ return "";
1129
+ }
1130
+ /** Hook post-swap (reposicionar tooltip, actualizar clase del banner…). */
1131
+ onLiveUpdate(_step) {
1132
+ }
1133
+ /**
1134
+ * Actualiza el contenido del step SIN remontar el host — así el preview del
1135
+ * editor no parpadea en cada tecla. Devuelve `false` cuando el renderer no
1136
+ * lo soporta o el cambio requiere remontar (cambió el ancla/estructura);
1137
+ * en ese caso el caller hace el re-render completo.
1138
+ */
1139
+ updateStep(step) {
1140
+ if (!this.host || !this.liveContainer || !this.liveContent || !this.liveBuild) return false;
1141
+ if (this.liveKeyOf(step) !== this.liveKey) return false;
1142
+ const next = this.liveBuild(step);
1143
+ this.liveContainer.replaceChild(next, this.liveContent);
1144
+ this.liveContent = next;
1145
+ this.applyDesign(step.style);
1146
+ this.onLiveUpdate(step);
1147
+ return true;
1099
1148
  }
1100
1149
  /**
1101
1150
  * Crea un `<div data-veo-guide>` adjunto a `document.body`, le adjunta
@@ -1186,9 +1235,21 @@ var init_banner_renderer = __esm({
1186
1235
  banner.appendChild(content);
1187
1236
  root.appendChild(banner);
1188
1237
  if (!context.nav) {
1238
+ this.liveContainer = banner;
1239
+ this.liveContent = content;
1240
+ this.liveBuild = (s) => buildStepContent(s, ownerDocument, {
1241
+ onCtaClick: (action, url) => dismiss("cta_clicked", action === "url" ? url : void 0),
1242
+ onDismiss: () => dismiss("dismissed")
1243
+ });
1189
1244
  context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
1190
1245
  }
1191
1246
  }
1247
+ onLiveUpdate(step) {
1248
+ if (this.liveContainer) {
1249
+ const position = step.style?.position === "bottom" ? "bottom" : "top";
1250
+ this.liveContainer.className = `veo-banner veo-banner-${position}`;
1251
+ }
1252
+ }
1192
1253
  };
1193
1254
  }
1194
1255
  });
@@ -1508,7 +1569,7 @@ function mountFormContent(card, context, doc) {
1508
1569
  const empty = value === void 0 || value === null || value === "";
1509
1570
  if (empty) {
1510
1571
  if (field.required) {
1511
- error.textContent = `Complet\xE1 "${field.label}"`;
1572
+ error.textContent = `Completa "${field.label}"`;
1512
1573
  error.style.display = "block";
1513
1574
  return;
1514
1575
  }
@@ -1517,7 +1578,7 @@ function mountFormContent(card, context, doc) {
1517
1578
  answers[field.key] = value;
1518
1579
  }
1519
1580
  if (Object.keys(answers).length === 0) {
1520
- error.textContent = "Respond\xE9 al menos un campo";
1581
+ error.textContent = "Responde al menos un campo";
1521
1582
  error.style.display = "block";
1522
1583
  return;
1523
1584
  }
@@ -1526,7 +1587,7 @@ function mountFormContent(card, context, doc) {
1526
1587
  void submitFn(answers).then((ok) => {
1527
1588
  if (!ok) {
1528
1589
  submit.disabled = false;
1529
- error.textContent = "No se pudo enviar. Prob\xE1 de nuevo.";
1590
+ error.textContent = "No se pudo enviar. Prueba de nuevo.";
1530
1591
  error.style.display = "block";
1531
1592
  return;
1532
1593
  }
@@ -1585,7 +1646,7 @@ function buildField(field, doc) {
1585
1646
  select.className = "veo-form-input";
1586
1647
  const blank = doc.createElement("option");
1587
1648
  blank.value = "";
1588
- blank.textContent = field.placeholder || "Eleg\xED una opci\xF3n\u2026";
1649
+ blank.textContent = field.placeholder || "Elige una opci\xF3n\u2026";
1589
1650
  select.appendChild(blank);
1590
1651
  for (const opt of field.options ?? []) {
1591
1652
  const option = doc.createElement("option");
@@ -1900,8 +1961,18 @@ var init_inline_renderer = __esm({
1900
1961
  });
1901
1962
  card.appendChild(content);
1902
1963
  root.appendChild(card);
1964
+ this.liveContainer = card;
1965
+ this.liveContent = content;
1966
+ this.liveKey = `${selector}|${position}`;
1967
+ this.liveBuild = (s) => buildStepContent(s, ownerDocument, {
1968
+ onCtaClick: (action, url) => dismiss("cta_clicked", action === "url" ? url : void 0),
1969
+ onDismiss: () => dismiss("dismissed")
1970
+ });
1903
1971
  context.onInteraction({ guideId: context.guide.guideId, stepIndex: 0, action: "shown" });
1904
1972
  }
1973
+ liveKeyOf(step) {
1974
+ return `${step.selector ?? ""}|${readInlinePosition(step.style)}`;
1975
+ }
1905
1976
  };
1906
1977
  }
1907
1978
  });
@@ -1947,6 +2018,14 @@ var init_modal_renderer = __esm({
1947
2018
  card.appendChild(content);
1948
2019
  overlay.appendChild(card);
1949
2020
  root.appendChild(overlay);
2021
+ if (!context.nav) {
2022
+ this.liveContainer = card;
2023
+ this.liveContent = content;
2024
+ this.liveBuild = (s) => buildStepContent(s, ownerDocument, {
2025
+ onCtaClick: (action, url) => dismiss("cta_clicked", action === "url" ? url : void 0),
2026
+ onDismiss: () => dismiss("dismissed")
2027
+ });
2028
+ }
1950
2029
  overlay.addEventListener("click", (e) => {
1951
2030
  if (e.target === overlay) onBackdropOrEsc();
1952
2031
  });
@@ -1992,6 +2071,12 @@ var init_tooltip_renderer = __esm({
1992
2071
  init_base_renderer();
1993
2072
  init_floating_arrow();
1994
2073
  TooltipRenderer = class extends BaseRenderer {
2074
+ constructor() {
2075
+ super(...arguments);
2076
+ /** Lado preferido (mutable: el update in-place puede cambiarlo). */
2077
+ this.side = "bottom";
2078
+ this.reposition = null;
2079
+ }
1995
2080
  async render(context) {
1996
2081
  const step = context.guide.guideSteps[0];
1997
2082
  if (!step) return;
@@ -2000,7 +2085,7 @@ var init_tooltip_renderer = __esm({
2000
2085
  const anchor = await waitForElement(selector);
2001
2086
  if (!anchor) return;
2002
2087
  const rawSide = step.style?.tooltipPlacement;
2003
- const side = rawSide === "top" || rawSide === "left" || rawSide === "right" ? rawSide : "bottom";
2088
+ this.side = rawSide === "top" || rawSide === "left" || rawSide === "right" ? rawSide : "bottom";
2004
2089
  const { root } = this.createHost();
2005
2090
  this.applyDesign(step.style);
2006
2091
  const ownerDocument = root.ownerDocument ?? document;
@@ -2028,7 +2113,7 @@ var init_tooltip_renderer = __esm({
2028
2113
  root.appendChild(tooltip);
2029
2114
  const updatePosition = async () => {
2030
2115
  const { x, y, placement, middlewareData } = await dom.computePosition(anchor, tooltip, {
2031
- placement: side,
2116
+ placement: this.side,
2032
2117
  middleware: [dom.offset(10), dom.flip(), dom.shift({ padding: 8 }), dom.arrow({ element: arrowEl })]
2033
2118
  });
2034
2119
  tooltip.style.left = `${x}px`;
@@ -2039,18 +2124,34 @@ var init_tooltip_renderer = __esm({
2039
2124
  const reposition = () => {
2040
2125
  void updatePosition();
2041
2126
  };
2127
+ this.reposition = reposition;
2042
2128
  window.addEventListener("scroll", reposition, true);
2043
2129
  window.addEventListener("resize", reposition);
2044
2130
  this.registerCleanup(() => {
2045
2131
  window.removeEventListener("scroll", reposition, true);
2046
2132
  window.removeEventListener("resize", reposition);
2047
2133
  });
2134
+ this.liveContainer = tooltip;
2135
+ this.liveContent = content;
2136
+ this.liveKey = selector;
2137
+ this.liveBuild = (s) => buildStepContent(s, ownerDocument, {
2138
+ onCtaClick: (action, url) => dismiss("cta_clicked", action === "url" ? url : void 0),
2139
+ onDismiss: () => dismiss("dismissed")
2140
+ });
2048
2141
  context.onInteraction({
2049
2142
  guideId: context.guide.guideId,
2050
2143
  stepIndex: 0,
2051
2144
  action: "shown"
2052
2145
  });
2053
2146
  }
2147
+ liveKeyOf(step) {
2148
+ return step.selector ?? "";
2149
+ }
2150
+ onLiveUpdate(step) {
2151
+ const raw = step.style?.tooltipPlacement;
2152
+ this.side = raw === "top" || raw === "left" || raw === "right" ? raw : "bottom";
2153
+ this.reposition?.();
2154
+ }
2054
2155
  };
2055
2156
  }
2056
2157
  });
@@ -2196,6 +2297,11 @@ var init_guide_preview = __esm({
2196
2297
  this.input = null;
2197
2298
  }
2198
2299
  preview(input) {
2300
+ const nextStep = input.guideSteps[0];
2301
+ if (this.singleStep && this.input && input.guideType === this.input.guideType && input.guideType !== "walkthrough" && nextStep && this.singleStep.updateStep(nextStep)) {
2302
+ this.input = input;
2303
+ return { close: () => this.close(), ready: Promise.resolve({ rendered: true }) };
2304
+ }
2199
2305
  this.close();
2200
2306
  this.input = input;
2201
2307
  const guide = normalize(input);
@@ -2646,7 +2752,7 @@ var init_element_picker = __esm({
2646
2752
  this.label.style.display = "none";
2647
2753
  const hint = document.createElement("div");
2648
2754
  hint.className = "veo-pick-hint";
2649
- hint.textContent = options.hint ?? "Seleccion\xE1 un elemento \xB7 Esc para cancelar";
2755
+ hint.textContent = options.hint ?? "Selecciona un elemento \xB7 Esc para cancelar";
2650
2756
  shadow.append(style, this.box, this.label, hint);
2651
2757
  document.body.appendChild(host);
2652
2758
  this.host = host;
@@ -4021,7 +4127,7 @@ var GuideResolverClient = class {
4021
4127
  * matchea, pero el walkthrough activo debe poder consultarla igual.
4022
4128
  *
4023
4129
  * El endpoint backend (`GET /v1/guides/:guideId`) devuelve `GuideResponse`,
4024
- * un superset de `ResolvedGuide` con campos de audit. Acá descartamos los
4130
+ * un superset de `ResolvedGuide` con campos de audit. Aquí descartamos los
4025
4131
  * extra y nos quedamos con el shape que el resto del SDK ya consume.
4026
4132
  *
4027
4133
  * Devuelve `null` en cualquier error (404, red, parse) — el caller debe