veo-sdk 0.5.31 → 0.5.33

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/builder.cjs CHANGED
@@ -11,7 +11,7 @@ function attachBlockDropTarget(options) {
11
11
  let spot = null;
12
12
  const contentEl = () => host.shadowRoot?.querySelector(".veo-guide-content") ?? null;
13
13
  const blockChildren = (container) => Array.from(container.children).filter(
14
- (el) => el instanceof HTMLElement && el.dataset.veoIdx !== void 0
14
+ (el2) => el2 instanceof HTMLElement && el2.dataset.veoIdx !== void 0
15
15
  );
16
16
  const hideLine = () => {
17
17
  line?.remove();
@@ -283,6 +283,14 @@ function clearBuilderToken() {
283
283
  }
284
284
  }
285
285
 
286
+ // src/plugins/guides/activation-devices.ts
287
+ var MOBILE_MAX_WIDTH = 767;
288
+ function isDeviceAllowed(devices, viewportWidth = typeof window === "undefined" ? 1024 : window.innerWidth) {
289
+ if (!devices || devices === "all") return true;
290
+ const mobile = viewportWidth <= MOBILE_MAX_WIDTH;
291
+ return devices === "mobile" ? mobile : !mobile;
292
+ }
293
+
286
294
  // src/plugins/guides/constants.ts
287
295
  var GUIDE_Z_INDEX = 2147483640;
288
296
  var DEFAULT_ANCHOR_WAIT_MS = 5e3;
@@ -357,22 +365,22 @@ function rebuildNode(node, doc, depth, budget) {
357
365
  return doc.createTextNode(node.textContent ?? "");
358
366
  }
359
367
  if (node.nodeType !== Node.ELEMENT_NODE) return null;
360
- const el = node;
361
- if (DROP_TAGS.has(el.tagName)) return null;
362
- const mapped = ALLOWED_TAGS[el.tagName];
368
+ const el2 = node;
369
+ if (DROP_TAGS.has(el2.tagName)) return null;
370
+ const mapped = ALLOWED_TAGS[el2.tagName];
363
371
  if (!mapped) {
364
372
  const frag = doc.createDocumentFragment();
365
- for (const child of Array.from(el.childNodes)) {
373
+ for (const child of Array.from(el2.childNodes)) {
366
374
  const rebuilt2 = rebuildNode(child, doc, depth + 1, budget);
367
375
  if (rebuilt2) frag.appendChild(rebuilt2);
368
376
  }
369
377
  return frag;
370
378
  }
371
379
  if (mapped === "a") {
372
- const href = el.getAttribute("href") ?? "";
380
+ const href = el2.getAttribute("href") ?? "";
373
381
  if (!isSafeUrl(href)) {
374
382
  const frag = doc.createDocumentFragment();
375
- for (const child of Array.from(el.childNodes)) {
383
+ for (const child of Array.from(el2.childNodes)) {
376
384
  const rebuilt2 = rebuildNode(child, doc, depth + 1, budget);
377
385
  if (rebuilt2) frag.appendChild(rebuilt2);
378
386
  }
@@ -382,14 +390,14 @@ function rebuildNode(node, doc, depth, budget) {
382
390
  a.setAttribute("href", href);
383
391
  a.setAttribute("target", "_blank");
384
392
  a.setAttribute("rel", "noopener noreferrer nofollow");
385
- for (const child of Array.from(el.childNodes)) {
393
+ for (const child of Array.from(el2.childNodes)) {
386
394
  const rebuilt2 = rebuildNode(child, doc, depth + 1, budget);
387
395
  if (rebuilt2) a.appendChild(rebuilt2);
388
396
  }
389
397
  return a;
390
398
  }
391
399
  const rebuilt = doc.createElement(mapped);
392
- for (const child of Array.from(el.childNodes)) {
400
+ for (const child of Array.from(el2.childNodes)) {
393
401
  const childNode = rebuildNode(child, doc, depth + 1, budget);
394
402
  if (childNode) rebuilt.appendChild(childNode);
395
403
  }
@@ -523,31 +531,31 @@ function renderBlocks(container, blocks, doc, callbacks) {
523
531
  function buildContentBlock(block, doc) {
524
532
  switch (block.type) {
525
533
  case "title": {
526
- const el = doc.createElement("h2");
527
- el.className = "veo-guide-title";
528
- el.textContent = typeof block.text === "string" ? block.text : "";
529
- applyBlockStyle(el, block.style, "text");
530
- return el;
534
+ const el2 = doc.createElement("h2");
535
+ el2.className = "veo-guide-title";
536
+ el2.textContent = typeof block.text === "string" ? block.text : "";
537
+ applyBlockStyle(el2, block.style, "text");
538
+ return el2;
531
539
  }
532
540
  case "text": {
533
- const el = doc.createElement("div");
534
- el.className = "veo-guide-text";
541
+ const el2 = doc.createElement("div");
542
+ el2.className = "veo-guide-text";
535
543
  if (typeof block.html === "string" && block.html) {
536
- el.appendChild(renderRichText(block.html, doc));
544
+ el2.appendChild(renderRichText(block.html, doc));
537
545
  } else {
538
- el.textContent = typeof block.text === "string" ? block.text : "";
546
+ el2.textContent = typeof block.text === "string" ? block.text : "";
539
547
  }
540
- applyBlockStyle(el, block.style, "text");
541
- return el;
548
+ applyBlockStyle(el2, block.style, "text");
549
+ return el2;
542
550
  }
543
551
  case "image": {
544
552
  if (typeof block.url !== "string" || !isSafeUrl(block.url)) return null;
545
- const el = doc.createElement("img");
546
- el.className = "veo-guide-image";
547
- el.src = block.url;
548
- el.alt = "";
549
- applyBlockStyle(el, block.style, "image");
550
- return el;
553
+ const el2 = doc.createElement("img");
554
+ el2.className = "veo-guide-image";
555
+ el2.src = block.url;
556
+ el2.alt = "";
557
+ applyBlockStyle(el2, block.style, "image");
558
+ return el2;
551
559
  }
552
560
  default:
553
561
  return null;
@@ -579,68 +587,74 @@ var BLOCK_ALIGN_SELF = {
579
587
  function clampNum(n, min, max) {
580
588
  return Math.min(max, Math.max(min, n));
581
589
  }
582
- function applyBlockStyle(el, style, kind) {
590
+ function applyBlockStyle(el2, style, kind) {
583
591
  if (!style || typeof style !== "object") return;
584
592
  const s = style;
585
593
  if (kind === "image" && s.bleed === true) {
586
- el.classList.add("veo-guide-image--bleed");
594
+ el2.classList.add("veo-guide-image--bleed");
587
595
  return;
588
596
  }
589
597
  const align = typeof s.align === "string" ? s.align : null;
590
598
  if (align && (align === "left" || align === "center" || align === "right")) {
591
- if (kind === "text") el.style.textAlign = align;
592
- else if (kind === "image") el.style.alignSelf = BLOCK_ALIGN_SELF[align];
599
+ if (kind === "text") el2.style.textAlign = align;
600
+ else if (kind === "image") el2.style.alignSelf = BLOCK_ALIGN_SELF[align];
593
601
  }
594
- if (kind !== "image" && typeof s.color === "string") el.style.color = s.color;
602
+ if (kind !== "image" && typeof s.color === "string") el2.style.color = s.color;
595
603
  if (kind === "text" && typeof s.fontSize === "number" && Number.isFinite(s.fontSize)) {
596
- el.style.fontSize = `${clampNum(s.fontSize, 8, 72)}px`;
604
+ el2.style.fontSize = `${clampNum(s.fontSize, 8, 72)}px`;
597
605
  }
598
606
  if (kind === "image" && typeof s.width === "number" && Number.isFinite(s.width)) {
599
- el.style.width = `${clampNum(s.width, 10, 100)}%`;
607
+ el2.style.width = `${clampNum(s.width, 10, 100)}%`;
600
608
  }
601
609
  if (kind === "image" && typeof s.radius === "number" && Number.isFinite(s.radius)) {
602
- el.style.borderRadius = `${clampNum(s.radius, 0, 48)}px`;
610
+ el2.style.borderRadius = `${clampNum(s.radius, 0, 48)}px`;
603
611
  }
604
612
  if (typeof s.marginTop === "number" && Number.isFinite(s.marginTop)) {
605
- el.style.marginTop = `${clampNum(s.marginTop, 0, 64)}px`;
613
+ el2.style.marginTop = `${clampNum(s.marginTop, 0, 64)}px`;
606
614
  }
607
615
  if (typeof s.marginBottom === "number" && Number.isFinite(s.marginBottom)) {
608
- el.style.marginBottom = `${clampNum(s.marginBottom, 0, 64)}px`;
616
+ el2.style.marginBottom = `${clampNum(s.marginBottom, 0, 64)}px`;
609
617
  }
610
618
  if (kind !== "image") {
611
619
  if (typeof s.fontSize === "number" && Number.isFinite(s.fontSize)) {
612
- el.style.fontSize = `${clampNum(s.fontSize, 8, 96)}px`;
620
+ el2.style.fontSize = `${clampNum(s.fontSize, 8, 96)}px`;
613
621
  }
614
622
  if (typeof s.fontFamily === "string" && s.fontFamily && s.fontFamily !== "inherit") {
615
- el.style.fontFamily = s.fontFamily;
623
+ el2.style.fontFamily = s.fontFamily;
616
624
  }
617
- if (typeof s.fontWeight === "string" && s.fontWeight) el.style.fontWeight = s.fontWeight;
625
+ if (typeof s.fontWeight === "string" && s.fontWeight) el2.style.fontWeight = s.fontWeight;
618
626
  if (typeof s.textTransform === "string" && s.textTransform && s.textTransform !== "none") {
619
- el.style.textTransform = s.textTransform;
627
+ el2.style.textTransform = s.textTransform;
620
628
  }
621
629
  if (typeof s.lineHeight === "number" && Number.isFinite(s.lineHeight)) {
622
- el.style.lineHeight = `${clampNum(s.lineHeight, 50, 300)}%`;
630
+ el2.style.lineHeight = `${clampNum(s.lineHeight, 50, 300)}%`;
623
631
  }
624
632
  if (typeof s.letterSpacing === "number" && Number.isFinite(s.letterSpacing)) {
625
- el.style.letterSpacing = `${clampNum(s.letterSpacing, -5, 20)}px`;
633
+ el2.style.letterSpacing = `${clampNum(s.letterSpacing, -5, 20)}px`;
626
634
  }
627
635
  }
628
- if (typeof s.background === "string" && s.background) el.style.background = s.background;
636
+ if (typeof s.background === "string" && s.background) el2.style.background = s.background;
629
637
  if (typeof s.borderWidth === "number" && s.borderWidth > 0) {
630
638
  const bc = typeof s.borderColor === "string" && s.borderColor ? s.borderColor : "#bbbbbb";
631
- el.style.border = `${clampNum(s.borderWidth, 0, 20)}px solid ${bc}`;
639
+ el2.style.border = `${clampNum(s.borderWidth, 0, 20)}px solid ${bc}`;
632
640
  }
633
641
  if (kind !== "image" && typeof s.radius === "number" && Number.isFinite(s.radius)) {
634
- el.style.borderRadius = `${clampNum(s.radius, 0, 64)}px`;
642
+ el2.style.borderRadius = `${clampNum(s.radius, 0, 64)}px`;
643
+ }
644
+ if (typeof s.height === "number" && Number.isFinite(s.height) && s.height > 0) {
645
+ el2.style.height = `${clampNum(s.height, 1, 2e3)}px`;
646
+ }
647
+ if (kind !== "image" && typeof s.width === "number" && Number.isFinite(s.width) && s.width > 0) {
648
+ el2.style.width = `${clampNum(s.width, 1, 2e3)}px`;
635
649
  }
636
650
  for (const side of ["Top", "Right", "Bottom", "Left"]) {
637
651
  const p = s[`padding${side}`];
638
652
  if (typeof p === "number" && Number.isFinite(p)) {
639
- el.style.setProperty(`padding-${side.toLowerCase()}`, `${clampNum(p, 0, 128)}px`);
653
+ el2.style.setProperty(`padding-${side.toLowerCase()}`, `${clampNum(p, 0, 128)}px`);
640
654
  }
641
655
  const m = s[`margin${side}`];
642
656
  if (typeof m === "number" && Number.isFinite(m)) {
643
- el.style.setProperty(`margin-${side.toLowerCase()}`, `${clampNum(m, 0, 128)}px`);
657
+ el2.style.setProperty(`margin-${side.toLowerCase()}`, `${clampNum(m, 0, 128)}px`);
644
658
  }
645
659
  }
646
660
  }
@@ -775,11 +789,11 @@ function applyDesignVars(host, style) {
775
789
  host.style.setProperty("--veo-pos-y", `${py * 100}%`);
776
790
  }
777
791
  if (s.elements && typeof s.elements === "object") {
778
- const el = s.elements;
779
- applyElementVars(host, "title", el.title);
780
- applyElementVars(host, "text", el.text);
781
- applyElementVars(host, "image", el.image);
782
- applyCtaVars(host, el.cta);
792
+ const el2 = s.elements;
793
+ applyElementVars(host, "title", el2.title);
794
+ applyElementVars(host, "text", el2.text);
795
+ applyElementVars(host, "image", el2.image);
796
+ applyCtaVars(host, el2.cta);
783
797
  }
784
798
  }
785
799
  function applyCtaVars(host, raw) {
@@ -803,29 +817,29 @@ function applyCtaVars(host, raw) {
803
817
  }
804
818
  function applyElementVars(host, prefix, raw) {
805
819
  if (!raw || typeof raw !== "object") return;
806
- const el = raw;
807
- if (typeof el.align === "string" && (el.align === "left" || el.align === "center" || el.align === "right")) {
820
+ const el2 = raw;
821
+ if (typeof el2.align === "string" && (el2.align === "left" || el2.align === "center" || el2.align === "right")) {
808
822
  const mode = ELEMENT_ALIGN_MODE[prefix] ?? "text";
809
- const value = mode === "self" ? ALIGN_SELF[el.align] : el.align;
823
+ const value = mode === "self" ? ALIGN_SELF[el2.align] : el2.align;
810
824
  host.style.setProperty(`--veo-${prefix}-align`, value);
811
825
  }
812
- if (typeof el.color === "string" && COLOR_RE.test(el.color.trim())) {
813
- host.style.setProperty(`--veo-${prefix}-color`, el.color.trim());
826
+ if (typeof el2.color === "string" && COLOR_RE.test(el2.color.trim())) {
827
+ host.style.setProperty(`--veo-${prefix}-color`, el2.color.trim());
814
828
  }
815
- if (typeof el.fontSize === "number" && Number.isFinite(el.fontSize)) {
816
- host.style.setProperty(`--veo-${prefix}-size`, `${clamp(el.fontSize, 8, 72)}px`);
829
+ if (typeof el2.fontSize === "number" && Number.isFinite(el2.fontSize)) {
830
+ host.style.setProperty(`--veo-${prefix}-size`, `${clamp(el2.fontSize, 8, 72)}px`);
817
831
  }
818
- if (typeof el.width === "number" && Number.isFinite(el.width)) {
819
- host.style.setProperty(`--veo-${prefix}-width`, `${clamp(el.width, 10, 100)}%`);
832
+ if (typeof el2.width === "number" && Number.isFinite(el2.width)) {
833
+ host.style.setProperty(`--veo-${prefix}-width`, `${clamp(el2.width, 10, 100)}%`);
820
834
  }
821
- if (typeof el.radius === "number" && Number.isFinite(el.radius)) {
822
- host.style.setProperty(`--veo-${prefix}-radius`, `${clamp(el.radius, 0, 48)}px`);
835
+ if (typeof el2.radius === "number" && Number.isFinite(el2.radius)) {
836
+ host.style.setProperty(`--veo-${prefix}-radius`, `${clamp(el2.radius, 0, 48)}px`);
823
837
  }
824
- if (typeof el.marginTop === "number" && Number.isFinite(el.marginTop)) {
825
- host.style.setProperty(`--veo-${prefix}-mt`, `${clamp(el.marginTop, 0, 64)}px`);
838
+ if (typeof el2.marginTop === "number" && Number.isFinite(el2.marginTop)) {
839
+ host.style.setProperty(`--veo-${prefix}-mt`, `${clamp(el2.marginTop, 0, 64)}px`);
826
840
  }
827
- if (typeof el.marginBottom === "number" && Number.isFinite(el.marginBottom)) {
828
- host.style.setProperty(`--veo-${prefix}-mb`, `${clamp(el.marginBottom, 0, 64)}px`);
841
+ if (typeof el2.marginBottom === "number" && Number.isFinite(el2.marginBottom)) {
842
+ host.style.setProperty(`--veo-${prefix}-mb`, `${clamp(el2.marginBottom, 0, 64)}px`);
829
843
  }
830
844
  }
831
845
 
@@ -875,16 +889,16 @@ function waitForElement(selector, timeoutMs = DEFAULT_ANCHOR_WAIT_MS) {
875
889
  return;
876
890
  }
877
891
  let resolved = false;
878
- const finish = (el) => {
892
+ const finish = (el2) => {
879
893
  if (resolved) return;
880
894
  resolved = true;
881
895
  observer.disconnect();
882
896
  clearTimeout(timer);
883
- resolve(el);
897
+ resolve(el2);
884
898
  };
885
899
  const observer = new MutationObserver(() => {
886
- const el = safeQuery();
887
- if (el) finish(el);
900
+ const el2 = safeQuery();
901
+ if (el2) finish(el2);
888
902
  });
889
903
  observer.observe(document.body, { childList: true, subtree: true });
890
904
  const timer = setTimeout(() => finish(null), timeoutMs);
@@ -1944,10 +1958,10 @@ function collectHostStyles() {
1944
1958
  function hostHtmlAttrs() {
1945
1959
  if (typeof document === "undefined") return "";
1946
1960
  try {
1947
- const el = document.documentElement;
1961
+ const el2 = document.documentElement;
1948
1962
  const attrs = [];
1949
1963
  for (const name of ["class", "style", "data-theme", "data-mode", "data-color-mode"]) {
1950
- const v = el.getAttribute(name);
1964
+ const v = el2.getAttribute(name);
1951
1965
  if (v && v.length <= 1e3 && !/["<>]/.test(v)) attrs.push(`${name}="${v}"`);
1952
1966
  }
1953
1967
  return attrs.join(" ");
@@ -2330,6 +2344,188 @@ function buildScale(wrapper, doc, min, max, btnClass, symbol) {
2330
2344
  wrapper.appendChild(row);
2331
2345
  return { wrapper, read: () => selected };
2332
2346
  }
2347
+ var INFO_GLYPH = "M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021";
2348
+ var QUESTION_GLYPH = "M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75";
2349
+ var STAR_GLYPH = "M11.48 3.499a.562.562 0 011.04 0l2.125 5.111a.563.563 0 00.475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 00-.182.557l1.285 5.385a.562.562 0 01-.84.61l-4.725-2.885a.563.563 0 00-.586 0L6.982 20.54a.562.562 0 01-.84-.61l1.285-5.386a.562.562 0 00-.182-.557l-4.204-3.602a.563.563 0 01.321-.988l5.518-.442a.563.563 0 00.475-.345L11.48 3.5z";
2350
+ var CIRCLE = "M21 12a9 9 0 11-18 0 9 9 0 0118 0z";
2351
+ var TARGET_GLYPH = "M7.5 3.75H6A2.25 2.25 0 0 0 3.75 6v1.5M16.5 3.75H18A2.25 2.25 0 0 1 20.25 6v1.5m0 9V18A2.25 2.25 0 0 1 18 20.25h-1.5m-9 0H6A2.25 2.25 0 0 1 3.75 18v-1.5M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z";
2352
+ var BADGE_ICONS = {
2353
+ info: { paths: [CIRCLE, INFO_GLYPH], dots: [[12, 8.25]] },
2354
+ exclamation: { paths: [CIRCLE, "M12 8v4.5"], dots: [[12, 15.75]] },
2355
+ question: { paths: [CIRCLE, QUESTION_GLYPH], dots: [[12, 17.25]] },
2356
+ star: { paths: [STAR_GLYPH] },
2357
+ "info-solid": { paths: [INFO_GLYPH], dots: [[12, 8.25]], solid: true, scale: 0.8 },
2358
+ "exclamation-solid": { paths: ["M12 8v4.5"], dots: [[12, 15.75]], solid: true, scale: 0.8 },
2359
+ "question-solid": { paths: [QUESTION_GLYPH], dots: [[12, 17.25]], solid: true, scale: 0.8 },
2360
+ "star-solid": { paths: [STAR_GLYPH], solid: true, scale: 0.55 },
2361
+ "target-solid": { paths: [TARGET_GLYPH], solid: true, scale: 0.6 },
2362
+ new: { paths: [], text: "New" }
2363
+ };
2364
+ var DEFAULT_MARKER_SIZE = 14;
2365
+ var DEFAULT_MARKER_Z = 19e3;
2366
+ var SIDE_GAP = 8;
2367
+ function num(v, fallback) {
2368
+ return typeof v === "number" && Number.isFinite(v) ? v : fallback;
2369
+ }
2370
+ function iconNode(doc, name, size, color) {
2371
+ const def = BADGE_ICONS[name] ?? BADGE_ICONS.info;
2372
+ if (def.text) {
2373
+ const t = doc.createElement("span");
2374
+ t.textContent = def.text;
2375
+ t.style.font = `700 ${Math.round(size * 0.85)}px/1 system-ui, -apple-system, sans-serif`;
2376
+ t.style.color = color;
2377
+ return t;
2378
+ }
2379
+ const ns = "http://www.w3.org/2000/svg";
2380
+ const svg2 = doc.createElementNS(ns, "svg");
2381
+ svg2.setAttribute("viewBox", "0 0 24 24");
2382
+ svg2.setAttribute("width", String(size));
2383
+ svg2.setAttribute("height", String(size));
2384
+ svg2.setAttribute("fill", "none");
2385
+ svg2.setAttribute("aria-hidden", "true");
2386
+ const glyphColor = def.solid ? "#ffffff" : color;
2387
+ if (def.solid) {
2388
+ const bg = doc.createElementNS(ns, "circle");
2389
+ bg.setAttribute("cx", "12");
2390
+ bg.setAttribute("cy", "12");
2391
+ bg.setAttribute("r", "12");
2392
+ bg.setAttribute("fill", color);
2393
+ svg2.appendChild(bg);
2394
+ }
2395
+ const g = doc.createElementNS(ns, "g");
2396
+ g.setAttribute("stroke", glyphColor);
2397
+ g.setAttribute("stroke-width", def.solid ? "2" : "1.8");
2398
+ g.setAttribute("stroke-linecap", "round");
2399
+ g.setAttribute("stroke-linejoin", "round");
2400
+ if (def.scale && def.scale !== 1) {
2401
+ const k = def.scale;
2402
+ const t = 12 - 12 * k;
2403
+ g.setAttribute("transform", `translate(${t} ${t}) scale(${k})`);
2404
+ }
2405
+ for (const d of def.paths) {
2406
+ const path = doc.createElementNS(ns, "path");
2407
+ path.setAttribute("d", d);
2408
+ g.appendChild(path);
2409
+ }
2410
+ for (const [cx, cy] of def.dots ?? []) {
2411
+ const dot = doc.createElementNS(ns, "circle");
2412
+ dot.setAttribute("cx", String(cx));
2413
+ dot.setAttribute("cy", String(cy));
2414
+ dot.setAttribute("r", "0.9");
2415
+ dot.setAttribute("fill", glyphColor);
2416
+ dot.setAttribute("stroke", "none");
2417
+ g.appendChild(dot);
2418
+ }
2419
+ svg2.appendChild(g);
2420
+ return svg2;
2421
+ }
2422
+ function buildMarkerElement(doc, marker) {
2423
+ const size = num(marker.size, DEFAULT_MARKER_SIZE);
2424
+ const color = marker.color || "#000000";
2425
+ const el2 = doc.createElement("span");
2426
+ el2.className = `veo-badge-marker veo-badge-marker-${marker.kind}`;
2427
+ el2.style.display = "inline-flex";
2428
+ el2.style.alignItems = "center";
2429
+ el2.style.justifyContent = "center";
2430
+ el2.style.boxSizing = "border-box";
2431
+ el2.style.lineHeight = "1";
2432
+ if (marker.kind === "image") {
2433
+ const img = doc.createElement("img");
2434
+ img.alt = "";
2435
+ img.src = marker.imageUrl || "";
2436
+ img.style.width = `${size}px`;
2437
+ img.style.height = `${size}px`;
2438
+ img.style.objectFit = "contain";
2439
+ img.style.display = "block";
2440
+ el2.appendChild(img);
2441
+ return el2;
2442
+ }
2443
+ if (marker.kind === "icon-text") {
2444
+ el2.style.gap = "6px";
2445
+ el2.style.color = color;
2446
+ el2.style.fontSize = `${size}px`;
2447
+ el2.style.fontFamily = "inherit";
2448
+ el2.style.fontWeight = "600";
2449
+ el2.style.whiteSpace = "nowrap";
2450
+ el2.style.background = marker.bgColor || "transparent";
2451
+ const bw = num(marker.borderWidth, 0);
2452
+ if (bw > 0) el2.style.border = `${bw}px solid ${marker.borderColor || color}`;
2453
+ el2.style.padding = `${num(marker.paddingY, 6)}px ${num(marker.paddingX, 10)}px`;
2454
+ el2.style.borderRadius = `${num(marker.radius, 999)}px`;
2455
+ if (marker.showIcon !== false)
2456
+ el2.appendChild(iconNode(doc, marker.icon || "star", size, color));
2457
+ const text = doc.createElement("span");
2458
+ text.textContent = marker.text || "";
2459
+ el2.appendChild(text);
2460
+ return el2;
2461
+ }
2462
+ el2.style.width = `${size}px`;
2463
+ el2.style.height = `${size}px`;
2464
+ el2.appendChild(iconNode(doc, marker.icon || "info", size, color));
2465
+ return el2;
2466
+ }
2467
+ function markerOffset(anchor, marker, position, offset6) {
2468
+ const right = anchor.left + anchor.width;
2469
+ const bottom = anchor.top + anchor.height;
2470
+ const midY = anchor.top + anchor.height / 2 - marker.height / 2;
2471
+ const midX = anchor.left + anchor.width / 2 - marker.width / 2;
2472
+ let x;
2473
+ let y;
2474
+ switch (position) {
2475
+ case "top-left":
2476
+ x = anchor.left - marker.width / 2;
2477
+ y = anchor.top - marker.height / 2;
2478
+ break;
2479
+ case "bottom-left":
2480
+ x = anchor.left - marker.width / 2;
2481
+ y = bottom - marker.height / 2;
2482
+ break;
2483
+ case "bottom-right":
2484
+ x = right - marker.width / 2;
2485
+ y = bottom - marker.height / 2;
2486
+ break;
2487
+ case "left":
2488
+ x = anchor.left - marker.width - SIDE_GAP;
2489
+ y = midY;
2490
+ break;
2491
+ case "right":
2492
+ x = right + SIDE_GAP;
2493
+ y = midY;
2494
+ break;
2495
+ case "center":
2496
+ x = midX;
2497
+ y = midY;
2498
+ break;
2499
+ default:
2500
+ x = right - marker.width / 2;
2501
+ y = anchor.top - marker.height / 2;
2502
+ }
2503
+ return { x: x + offset6.x, y: y + offset6.y };
2504
+ }
2505
+ function attachMarker(anchor, host, marker) {
2506
+ host.style.position = "absolute";
2507
+ host.style.zIndex = String(num(marker.zIndex, DEFAULT_MARKER_Z));
2508
+ host.style.margin = "0";
2509
+ const update = () => {
2510
+ const a = anchor.getBoundingClientRect();
2511
+ const m = host.getBoundingClientRect();
2512
+ const { x, y } = markerOffset(
2513
+ {
2514
+ left: a.left + window.scrollX,
2515
+ top: a.top + window.scrollY,
2516
+ width: a.width,
2517
+ height: a.height
2518
+ },
2519
+ { width: m.width, height: m.height },
2520
+ marker.position ?? "top-right",
2521
+ { x: num(marker.offsetX, 0), y: num(marker.offsetY, 0) }
2522
+ );
2523
+ host.style.left = `${Math.round(x)}px`;
2524
+ host.style.top = `${Math.round(y)}px`;
2525
+ };
2526
+ update();
2527
+ return dom.autoUpdate(anchor, host, update);
2528
+ }
2333
2529
 
2334
2530
  // src/plugins/guides/renderers/composite-renderer.ts
2335
2531
  var ROW_ALIGN = {
@@ -2625,7 +2821,9 @@ var CompositeRenderer = class extends BaseRenderer {
2625
2821
  badge.style.cursor = "pointer";
2626
2822
  badge.style.display = "inline-flex";
2627
2823
  badge.style.alignItems = "center";
2628
- this.buildContent(badge, step.badge?.blocks ?? [], doc, context, idx, [], host);
2824
+ const marker = step.badge?.marker ?? null;
2825
+ if (marker) badge.appendChild(buildMarkerElement(doc, marker));
2826
+ else this.buildContent(badge, step.badge?.blocks ?? [], doc, context, idx, [], host);
2629
2827
  root.appendChild(badge);
2630
2828
  if (design.fullWidth === true) {
2631
2829
  card.style.width = "auto";
@@ -2640,10 +2838,17 @@ var CompositeRenderer = class extends BaseRenderer {
2640
2838
  if (caretEl) wrapper.appendChild(caretEl);
2641
2839
  wrapper.appendChild(card);
2642
2840
  root.appendChild(wrapper);
2643
- host.style.display = "inline-block";
2644
- const pos = inlinePosOf(step.inlinePosition);
2645
- insertHost(anchor, host, pos);
2646
- this.registerCleanup(keepHostAttached(host, anchorSelector, pos));
2841
+ if (marker) {
2842
+ host.style.display = "inline-block";
2843
+ (anchor.ownerDocument ?? document).body.appendChild(host);
2844
+ this.registerCleanup(attachMarker(anchor, host, marker));
2845
+ this.registerCleanup(() => host.remove());
2846
+ } else {
2847
+ host.style.display = "inline-block";
2848
+ const pos = inlinePosOf(step.inlinePosition);
2849
+ insertHost(anchor, host, pos);
2850
+ this.registerCleanup(keepHostAttached(host, anchorSelector, pos));
2851
+ }
2647
2852
  let open = false;
2648
2853
  let stopFloat = null;
2649
2854
  let closeTimer = null;
@@ -2696,6 +2901,7 @@ var CompositeRenderer = class extends BaseRenderer {
2696
2901
  stopFloat?.();
2697
2902
  if (closeTimer !== null) clearTimeout(closeTimer);
2698
2903
  });
2904
+ if (context.badgeOpen) openTip();
2699
2905
  return closeTip;
2700
2906
  }
2701
2907
  };
@@ -2749,14 +2955,14 @@ function makeCaret(doc, design) {
2749
2955
  return caret;
2750
2956
  }
2751
2957
  function liftTarget(anchor, zIndex) {
2752
- const el = anchor;
2753
- const prevPosition = el.style.position;
2754
- const prevZ = el.style.zIndex;
2755
- if (getComputedStyle(el).position === "static") el.style.position = "relative";
2756
- el.style.zIndex = String(zIndex);
2958
+ const el2 = anchor;
2959
+ const prevPosition = el2.style.position;
2960
+ const prevZ = el2.style.zIndex;
2961
+ if (getComputedStyle(el2).position === "static") el2.style.position = "relative";
2962
+ el2.style.zIndex = String(zIndex);
2757
2963
  return () => {
2758
- el.style.position = prevPosition;
2759
- el.style.zIndex = prevZ;
2964
+ el2.style.position = prevPosition;
2965
+ el2.style.zIndex = prevZ;
2760
2966
  };
2761
2967
  }
2762
2968
  async function anchorPosition(anchor, wrapper, caret, preferred = "bottom") {
@@ -3272,7 +3478,8 @@ var GuidePreviewController = class {
3272
3478
  }
3273
3479
  preview(input) {
3274
3480
  const nextStep = input.guideSteps[0];
3275
- if (this.singleStep && this.input && input.guideType === this.input.guideType && input.guideType !== "walkthrough" && nextStep && this.singleStep.updateStep(nextStep)) {
3481
+ if (this.singleStep && this.input && input.guideType === this.input.guideType && input.guideType !== "walkthrough" && // Abrir/cerrar el tooltip del badge requiere remontar.
3482
+ input.badgeOpen === true === (this.input.badgeOpen === true) && nextStep && this.singleStep.updateStep(nextStep)) {
3276
3483
  this.input = input;
3277
3484
  return {
3278
3485
  close: () => this.close(),
@@ -3302,7 +3509,8 @@ var GuidePreviewController = class {
3302
3509
  }
3303
3510
  return null;
3304
3511
  }
3305
- close() {
3512
+ close(byUser = false) {
3513
+ const onClosed = byUser ? this.input?.onClosed : void 0;
3306
3514
  if (this.singleStep) {
3307
3515
  safeDestroy(this.singleStep);
3308
3516
  this.singleStep = null;
@@ -3314,6 +3522,7 @@ var GuidePreviewController = class {
3314
3522
  this.walkthroughGuide = null;
3315
3523
  this.walkthroughIndex = 0;
3316
3524
  this.input = null;
3525
+ onClosed?.();
3317
3526
  }
3318
3527
  async renderSingleStep(guide) {
3319
3528
  const renderer = createSingleStepRenderer(guide.guideType);
@@ -3326,7 +3535,8 @@ var GuidePreviewController = class {
3326
3535
  onInteraction: (event) => {
3327
3536
  if (event.action === "shown") shown = true;
3328
3537
  },
3329
- onClose: () => this.close(),
3538
+ onClose: () => this.close(true),
3539
+ badgeOpen: this.input?.badgeOpen === true,
3330
3540
  onTrack: () => {
3331
3541
  },
3332
3542
  // Encadenado en preview: no-op — el editor no puede montar otra guía
@@ -3392,7 +3602,7 @@ var GuidePreviewController = class {
3392
3602
  guide,
3393
3603
  onInteraction: () => {
3394
3604
  },
3395
- onClose: () => this.close(),
3605
+ onClose: () => this.close(true),
3396
3606
  nav: { stepIndex: index, totalSteps: guide.guideSteps.length, callbacks }
3397
3607
  });
3398
3608
  } catch {
@@ -3486,15 +3696,27 @@ function safeDestroy(renderer) {
3486
3696
  }
3487
3697
 
3488
3698
  // src/plugins/guides/url-match.ts
3699
+ function urlCandidates(pageUrl) {
3700
+ const out = [pageUrl];
3701
+ try {
3702
+ const u = new URL(pageUrl);
3703
+ out.push(`${u.pathname}${u.search}${u.hash}`);
3704
+ if (u.hash.startsWith("#/")) out.push(u.hash.slice(1));
3705
+ } catch {
3706
+ }
3707
+ return out;
3708
+ }
3489
3709
  function matchesUrl(matcher, pageUrl) {
3710
+ const candidates = urlCandidates(pageUrl);
3490
3711
  switch (matcher.type) {
3491
3712
  case "exact":
3492
- return matcher.pattern === pageUrl;
3713
+ return candidates.some((c) => c === matcher.pattern);
3493
3714
  case "prefix":
3494
- return pageUrl.startsWith(matcher.pattern);
3715
+ return candidates.some((c) => c.startsWith(matcher.pattern));
3495
3716
  case "regex":
3496
3717
  try {
3497
- return new RegExp(matcher.pattern).test(pageUrl);
3718
+ const re = new RegExp(matcher.pattern);
3719
+ return candidates.some((c) => re.test(c));
3498
3720
  } catch {
3499
3721
  return false;
3500
3722
  }
@@ -3508,17 +3730,375 @@ function stepMatchesPage(loc, pageUrl) {
3508
3730
  // src/plugins/builder/bridge-protocol.ts
3509
3731
  var VEO_BUILDER_SOURCE = "veo-builder";
3510
3732
 
3733
+ // src/plugins/builder/builder-chrome.ts
3734
+ var CHROME_TOPBAR_H = 64;
3735
+ var CHROME_STEPS_H = 128;
3736
+ var ICONS = {
3737
+ bolt: '<path stroke-linecap="round" stroke-linejoin="round" d="m3.75 13.5 10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75Z"/>',
3738
+ desktop: '<path stroke-linecap="round" stroke-linejoin="round" d="M9 17.25v1.007a3 3 0 0 1-.879 2.122L7.5 21h9l-.621-.621A3 3 0 0 1 15 18.257V17.25m6-12V15a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 15V5.25m18 0A2.25 2.25 0 0 0 18.75 3H5.25A2.25 2.25 0 0 0 3 5.25m18 0V12a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 12V5.25"/>',
3739
+ updown: '<path stroke-linecap="round" stroke-linejoin="round" d="M3 7.5 7.5 3m0 0L12 7.5M7.5 3v13.5m13.5 0L16.5 21m0 0L12 16.5m4.5 4.5V7.5"/>',
3740
+ map: '<path stroke-linecap="round" stroke-linejoin="round" d="M9 6.75V15m6-6v8.25m.503 3.498 4.875-2.437c.381-.19.622-.58.622-1.006V4.82c0-.836-.88-1.38-1.628-1.006l-3.869 1.934c-.317.159-.69.159-1.006 0L9.503 3.252a1.125 1.125 0 0 0-1.006 0L3.622 5.689C3.24 5.88 3 6.27 3 6.695V19.18c0 .836.88 1.38 1.628 1.006l3.869-1.934c.317-.159.69-.159 1.006 0l4.994 2.497c.317.158.69.158 1.006 0Z"/>',
3741
+ eye: '<path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z"/><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"/>',
3742
+ chevronDown: '<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"/>',
3743
+ chevronUp: '<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 15.75 7.5-7.5 7.5 7.5"/>',
3744
+ plus: '<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15"/>',
3745
+ cursor: '<path stroke-linecap="round" stroke-linejoin="round" d="M15.042 21.672 13.684 16.6m0 0-2.51 2.225.569-9.47 5.227 7.917-3.286-.672ZM12 2.25V4.5m5.834.166-1.591 1.591M20.25 10.5H18M7.757 14.743l-1.59 1.59M6 10.5H3.75m4.007-4.243-1.59-1.59"/>',
3746
+ spinner: '<path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0 3.181 3.183a8.25 8.25 0 0 0 13.803-3.7M4.031 9.865a8.25 8.25 0 0 1 13.803-3.7l3.181 3.182"/>'
3747
+ };
3748
+ function svg(name, size = 20) {
3749
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true">${ICONS[name]}</svg>`;
3750
+ }
3751
+ function shortDate(iso) {
3752
+ if (!iso) return "";
3753
+ const d = new Date(iso);
3754
+ if (Number.isNaN(d.getTime())) return "";
3755
+ return d.toLocaleDateString("es", { day: "numeric", month: "short" }).replace(".", "");
3756
+ }
3757
+ function el(tag, className, html) {
3758
+ const node = document.createElement(tag);
3759
+ node.className = className;
3760
+ if (html !== void 0) node.innerHTML = html;
3761
+ return node;
3762
+ }
3763
+ function iconButton(name, title, className = "") {
3764
+ const b = el("button", `veo-tb-icon ${className}`.trim(), svg(name));
3765
+ b.type = "button";
3766
+ b.title = title;
3767
+ b.setAttribute("aria-label", title);
3768
+ return b;
3769
+ }
3770
+ function injectBuilderChrome(opts) {
3771
+ if (!hasWindow() || !hasDocument()) {
3772
+ return {
3773
+ setState: () => {
3774
+ },
3775
+ height: () => 0,
3776
+ dock: () => "top",
3777
+ setPreview: () => {
3778
+ },
3779
+ setHidden: () => {
3780
+ },
3781
+ onHeightChange: () => {
3782
+ },
3783
+ teardown: () => {
3784
+ }
3785
+ };
3786
+ }
3787
+ const host = document.createElement("div");
3788
+ host.setAttribute("data-veo-builder-chrome", "");
3789
+ host.setAttribute("data-veo-ignore", "");
3790
+ const shadow = host.attachShadow({ mode: "open" });
3791
+ const style = document.createElement("style");
3792
+ style.textContent = CHROME_CSS;
3793
+ const root = el("div", "veo-chrome");
3794
+ const topbar = el("div", "veo-tb");
3795
+ const brand = el("div", "veo-tb-brand", svg("bolt", 18));
3796
+ const nameEl = el("div", "veo-tb-name");
3797
+ const statusEl = el("span", "veo-tb-status");
3798
+ statusEl.hidden = true;
3799
+ const sep = el("span", "veo-tb-sep");
3800
+ const appEl = el("div", "veo-tb-app", `${svg("desktop", 16)}<span></span>`);
3801
+ appEl.querySelector("span").textContent = opts.appLabel;
3802
+ const left = el("div", "veo-tb-left");
3803
+ left.append(brand, nameEl, statusEl, sep, appEl);
3804
+ const right = el("div", "veo-tb-right");
3805
+ const dockBtn = iconButton("updown", "Mover la barra abajo");
3806
+ const showPanel = iconButton("map", "Mostrar el editor");
3807
+ const preview = iconButton("eye", "Vista previa");
3808
+ const exit = el("button", "veo-tb-text", "Salir");
3809
+ exit.type = "button";
3810
+ const saveGroup = el("div", "veo-tb-save");
3811
+ const save = el("button", "veo-tb-save-main", "<span>Guardar</span>");
3812
+ save.type = "button";
3813
+ const saveMore = el("button", "veo-tb-save-more", svg("chevronDown", 16));
3814
+ saveMore.type = "button";
3815
+ saveMore.title = "M\xE1s opciones de guardado";
3816
+ saveMore.setAttribute("aria-label", "M\xE1s opciones de guardado");
3817
+ const saveMenu = el("div", "veo-tb-menu");
3818
+ const saveExit = el("button", "veo-tb-menu-item", "Guardar y salir");
3819
+ saveExit.type = "button";
3820
+ saveMenu.append(saveExit);
3821
+ saveMenu.hidden = true;
3822
+ saveGroup.append(save, saveMore, saveMenu);
3823
+ right.append(dockBtn, showPanel, preview, el("span", "veo-tb-vsep"), exit, saveGroup);
3824
+ topbar.append(left, right);
3825
+ const stepsBar = el("div", "veo-steps");
3826
+ const stepsList = el("div", "veo-steps-list");
3827
+ stepsBar.append(stepsList);
3828
+ const togglePill = el("button", "veo-steps-toggle");
3829
+ togglePill.type = "button";
3830
+ root.append(topbar, stepsBar, togglePill);
3831
+ const previewPill = el("div", "veo-preview-pill");
3832
+ const previewLabel = el(
3833
+ "span",
3834
+ "veo-preview-label",
3835
+ `${svg("eye", 18)}<span>Est\xE1s en vista previa</span>`
3836
+ );
3837
+ const previewExit = el("button", "veo-preview-exit", "Salir");
3838
+ previewExit.type = "button";
3839
+ previewPill.append(previewLabel, previewExit);
3840
+ previewPill.hidden = true;
3841
+ shadow.append(style, root, previewPill);
3842
+ document.body.appendChild(host);
3843
+ const html = document.documentElement;
3844
+ const prevMarginTop = html.style.marginTop;
3845
+ const prevMarginBottom = html.style.marginBottom;
3846
+ let collapsed = false;
3847
+ let dock = "top";
3848
+ let previewOn = false;
3849
+ let hiddenOn = false;
3850
+ let heightCb = null;
3851
+ const height = () => CHROME_TOPBAR_H + (collapsed ? 0 : CHROME_STEPS_H);
3852
+ const applyHeight = () => {
3853
+ const h = previewOn || hiddenOn ? 0 : height();
3854
+ html.style.marginTop = dock === "top" ? `${h}px` : prevMarginTop;
3855
+ html.style.marginBottom = dock === "bottom" ? `${h}px` : prevMarginBottom;
3856
+ root.style.setProperty("--veo-chrome-h", `${height()}px`);
3857
+ root.classList.toggle("is-bottom", dock === "bottom");
3858
+ stepsBar.hidden = collapsed;
3859
+ const open = dock === "top" ? "chevronUp" : "chevronDown";
3860
+ const closedIcon = dock === "top" ? "chevronDown" : "chevronUp";
3861
+ togglePill.innerHTML = `<span>${collapsed ? "Mostrar pasos" : "Ocultar pasos"}</span>${svg(
3862
+ collapsed ? closedIcon : open,
3863
+ 16
3864
+ )}`;
3865
+ dockBtn.title = dock === "top" ? "Mover la barra abajo" : "Mover la barra arriba";
3866
+ dockBtn.setAttribute("aria-label", dockBtn.title);
3867
+ heightCb?.(h);
3868
+ };
3869
+ const setPreview = (on) => {
3870
+ previewOn = on;
3871
+ root.hidden = on || hiddenOn;
3872
+ previewPill.hidden = !on;
3873
+ applyHeight();
3874
+ };
3875
+ const setHidden = (on) => {
3876
+ hiddenOn = on;
3877
+ root.hidden = on || previewOn;
3878
+ applyHeight();
3879
+ };
3880
+ togglePill.addEventListener("click", () => {
3881
+ collapsed = !collapsed;
3882
+ applyHeight();
3883
+ });
3884
+ const act = (a) => opts.onAction(a);
3885
+ dockBtn.addEventListener("click", () => {
3886
+ dock = dock === "top" ? "bottom" : "top";
3887
+ applyHeight();
3888
+ act({ action: "dock", position: dock });
3889
+ });
3890
+ showPanel.addEventListener("click", () => act({ action: "show-panel" }));
3891
+ preview.addEventListener("click", () => act({ action: "preview" }));
3892
+ previewExit.addEventListener("click", () => act({ action: "exit-preview" }));
3893
+ exit.addEventListener("click", () => act({ action: "exit" }));
3894
+ save.addEventListener("click", () => act({ action: "save", exit: false }));
3895
+ saveMore.addEventListener("click", (e) => {
3896
+ e.stopPropagation();
3897
+ saveMenu.hidden = !saveMenu.hidden;
3898
+ });
3899
+ saveExit.addEventListener("click", () => {
3900
+ saveMenu.hidden = true;
3901
+ act({ action: "save", exit: true });
3902
+ });
3903
+ const closeMenu = () => {
3904
+ saveMenu.hidden = true;
3905
+ };
3906
+ document.addEventListener("click", closeMenu);
3907
+ let current = null;
3908
+ const render = () => {
3909
+ const s = current;
3910
+ if (!s) return;
3911
+ const date = shortDate(s.createdAt);
3912
+ nameEl.textContent = `${s.guideName.trim() || "Sin nombre"}${date ? ` \xB7 ${date}` : ""}`;
3913
+ if (s.status) {
3914
+ statusEl.hidden = false;
3915
+ statusEl.textContent = s.status.label;
3916
+ statusEl.dataset.tone = s.status.tone;
3917
+ statusEl.title = "Estado de la gu\xEDa (se cambia desde la lista de gu\xEDas)";
3918
+ } else {
3919
+ statusEl.hidden = true;
3920
+ }
3921
+ save.innerHTML = s.saving ? `${svg("spinner", 16)}<span>Guardando\u2026</span>` : "<span>Guardar</span>";
3922
+ save.disabled = s.saving;
3923
+ saveExit.disabled = s.saving;
3924
+ stepsList.replaceChildren();
3925
+ const activation = stepCard(svg("cursor", 22), "Activaci\xF3n", s.section === "activation");
3926
+ activation.addEventListener("click", () => act({ action: "select-activation" }));
3927
+ stepsList.append(activation);
3928
+ s.steps.forEach((st, i) => {
3929
+ const thumb = st.presentation === "inline" ? '<div class="veo-thumb veo-thumb-inline"><i></i><i></i><i></i></div>' : '<div class="veo-thumb"><i></i><i></i><b></b></div>';
3930
+ const card = stepCard(thumb, st.label, s.section === "step" && s.activeStep === i);
3931
+ card.addEventListener("click", () => act({ action: "select-step", index: i }));
3932
+ stepsList.append(card);
3933
+ });
3934
+ const add = el("button", "veo-step-add", `${svg("plus", 18)}<span>Agregar paso</span>`);
3935
+ add.type = "button";
3936
+ add.addEventListener("click", () => act({ action: "add-step" }));
3937
+ stepsList.append(add);
3938
+ };
3939
+ applyHeight();
3940
+ return {
3941
+ setState: (state) => {
3942
+ current = state;
3943
+ render();
3944
+ },
3945
+ height,
3946
+ dock: () => dock,
3947
+ setPreview,
3948
+ setHidden,
3949
+ onHeightChange: (cb) => {
3950
+ heightCb = cb;
3951
+ },
3952
+ teardown: () => {
3953
+ document.removeEventListener("click", closeMenu);
3954
+ html.style.marginTop = prevMarginTop;
3955
+ html.style.marginBottom = prevMarginBottom;
3956
+ host.remove();
3957
+ }
3958
+ };
3959
+ }
3960
+ function stepCard(inner, label, active) {
3961
+ const b = el(
3962
+ "button",
3963
+ `veo-step${active ? " is-active" : ""}`,
3964
+ `<div class="veo-step-box">${inner}</div><span class="veo-step-label"></span>`
3965
+ );
3966
+ b.type = "button";
3967
+ b.querySelector(".veo-step-label").textContent = label;
3968
+ b.setAttribute("aria-pressed", active ? "true" : "false");
3969
+ return b;
3970
+ }
3971
+ var CHROME_CSS = `
3972
+ :host { all: initial; }
3973
+ .veo-chrome {
3974
+ position: fixed; top: 0; left: 0; right: 0;
3975
+ display: flex; flex-direction: column;
3976
+ z-index: 2147483646;
3977
+ font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
3978
+ font-size: 14px; color: #1c1917;
3979
+ }
3980
+ .veo-chrome[hidden] { display: none; }
3981
+ .veo-chrome.is-bottom { top: auto; bottom: 0; flex-direction: column-reverse; }
3982
+ .veo-chrome.is-bottom .veo-tb-menu { top: auto; bottom: calc(100% + 8px); }
3983
+ .veo-chrome.is-bottom .veo-steps { border-bottom: 0; border-top: 1px solid #d6cfc8; }
3984
+ .veo-chrome.is-bottom .veo-steps-toggle { top: auto; bottom: var(--veo-chrome-h, ${CHROME_TOPBAR_H + CHROME_STEPS_H}px); transform: translate(-50%, 50%); }
3985
+ .veo-preview-pill {
3986
+ position: fixed; top: 28px; left: 50%; transform: translateX(-50%);
3987
+ z-index: 2147483646;
3988
+ display: inline-flex; align-items: center; gap: 12px;
3989
+ background: #2b2623; color: #fff; border-radius: 999px; padding: 12px 20px;
3990
+ font-family: system-ui, -apple-system, "Segoe UI", sans-serif; font-size: 15px; font-weight: 500;
3991
+ box-shadow: 0 8px 24px rgba(0,0,0,0.3);
3992
+ }
3993
+ .veo-preview-pill[hidden] { display: none; }
3994
+ .veo-preview-label { display: inline-flex; align-items: center; gap: 10px; padding-right: 12px; border-right: 1px solid rgba(255,255,255,0.3); }
3995
+ .veo-preview-exit { border: 0; background: transparent; color: #fff; font: inherit; cursor: pointer; padding: 0 2px; }
3996
+ .veo-preview-exit:hover { text-decoration: underline; }
3997
+ .veo-tb {
3998
+ height: ${CHROME_TOPBAR_H}px; box-sizing: border-box;
3999
+ display: flex; align-items: center; justify-content: space-between;
4000
+ padding: 0 20px; gap: 16px;
4001
+ background: #2b2623; color: #fff;
4002
+ }
4003
+ .veo-tb-left, .veo-tb-right { display: flex; align-items: center; gap: 12px; min-width: 0; }
4004
+ .veo-tb-brand {
4005
+ width: 36px; height: 36px; border-radius: 999px; background: #b74d00;
4006
+ display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
4007
+ }
4008
+ .veo-tb-name { font-weight: 700; font-size: 16px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
4009
+ .veo-tb-status { display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 999px; font-size: 11px; font-weight: 600; letter-spacing: 0.02em; white-space: nowrap; background: rgba(255,255,255,0.18); color: #fff; }
4010
+ .veo-tb-status[data-tone="live"] { background: #16a34a; }
4011
+ .veo-tb-status[data-tone="testing"] { background: #b45309; }
4012
+ .veo-tb-status[data-tone="review"] { background: #7c3aed; }
4013
+ .veo-tb-status[data-tone="draft"] { background: #0f766e; }
4014
+ .veo-tb-status[data-tone="paused"] { background: rgba(255,255,255,0.28); }
4015
+ .veo-tb-sep { width: 1px; height: 24px; background: rgba(255,255,255,0.25); }
4016
+ .veo-tb-vsep { width: 1px; height: 28px; background: rgba(255,255,255,0.25); margin: 0 4px; }
4017
+ .veo-tb-app { display: inline-flex; align-items: center; gap: 8px; color: rgba(255,255,255,0.85); white-space: nowrap; }
4018
+ .veo-tb-icon {
4019
+ width: 42px; height: 42px; border-radius: 10px; border: 0;
4020
+ background: rgba(255,255,255,0.12); color: #fff; cursor: pointer;
4021
+ display: inline-flex; align-items: center; justify-content: center;
4022
+ }
4023
+ .veo-tb-icon:hover { background: rgba(255,255,255,0.22); }
4024
+ .veo-tb-text { border: 0; background: transparent; color: #fff; font: inherit; font-size: 15px; cursor: pointer; padding: 8px 6px; }
4025
+ .veo-tb-text:hover { text-decoration: underline; }
4026
+ .veo-tb-save { position: relative; display: inline-flex; border-radius: 10px; overflow: visible; }
4027
+ .veo-tb-save-main, .veo-tb-save-more {
4028
+ border: 0; background: #b74d00; color: #fff; font: inherit; font-weight: 600; font-size: 15px;
4029
+ cursor: pointer; display: inline-flex; align-items: center; gap: 8px; height: 42px;
4030
+ }
4031
+ .veo-tb-save-main { padding: 0 8px 0 18px; border-radius: 10px 0 0 10px; }
4032
+ .veo-tb-save-more { padding: 0 12px 0 6px; border-radius: 0 10px 10px 0; }
4033
+ .veo-tb-save-main:hover, .veo-tb-save-more:hover { background: #953c00; }
4034
+ .veo-tb-save-main:disabled { opacity: 0.7; cursor: default; }
4035
+ .veo-tb-menu {
4036
+ position: absolute; right: 0; top: calc(100% + 8px); min-width: 220px;
4037
+ background: #fff; color: #1c1917; border-radius: 12px; padding: 6px;
4038
+ box-shadow: 0 12px 32px rgba(0,0,0,0.22);
4039
+ }
4040
+ .veo-tb-menu-item {
4041
+ display: block; width: 100%; text-align: left; border: 0; background: transparent;
4042
+ font: inherit; font-size: 15px; padding: 10px 14px; border-radius: 8px; cursor: pointer;
4043
+ }
4044
+ .veo-tb-menu-item:hover { background: #f5efe9; }
4045
+ .veo-steps {
4046
+ height: ${CHROME_STEPS_H}px; box-sizing: border-box;
4047
+ background: #e7e1dc; border-bottom: 1px solid #d6cfc8;
4048
+ padding: 16px 28px 0; overflow-x: auto;
4049
+ }
4050
+ .veo-steps[hidden] { display: none; }
4051
+ .veo-steps-list { display: flex; align-items: flex-start; gap: 20px; }
4052
+ .veo-step { border: 0; background: transparent; padding: 0; cursor: pointer; font: inherit; display: flex; flex-direction: column; align-items: center; gap: 8px; flex: 0 0 auto; }
4053
+ .veo-step-box {
4054
+ width: 116px; height: 64px; border-radius: 10px; background: #fff; border: 2px solid transparent;
4055
+ display: flex; align-items: center; justify-content: center; color: #2b2623;
4056
+ box-shadow: 0 1px 2px rgba(0,0,0,0.06);
4057
+ }
4058
+ .veo-step.is-active .veo-step-box { border-color: #b74d00; }
4059
+ .veo-step-label { font-size: 13px; color: #6b635d; }
4060
+ .veo-step.is-active .veo-step-label { color: #2b2623; font-weight: 600; }
4061
+ .veo-thumb { width: 72px; height: 36px; border: 1.5px solid #c9c2bb; border-radius: 6px; padding: 6px 7px; box-sizing: border-box; display: flex; flex-direction: column; gap: 4px; }
4062
+ .veo-thumb i { display: block; height: 3px; border-radius: 2px; background: #2b2623; width: 70%; }
4063
+ .veo-thumb i + i { width: 45%; background: #c9c2bb; }
4064
+ .veo-thumb b { display: block; height: 6px; width: 24px; border-radius: 2px; background: #b74d00; align-self: flex-end; }
4065
+ .veo-thumb-inline { border-style: dashed; }
4066
+ .veo-thumb-inline i { width: 100%; }
4067
+ .veo-step-add { border: 0; background: transparent; font: inherit; font-size: 15px; color: #6b635d; cursor: pointer; display: inline-flex; align-items: center; gap: 8px; height: 64px; padding: 0 8px; }
4068
+ .veo-step-add:hover { color: #2b2623; }
4069
+ .veo-steps-toggle {
4070
+ position: absolute; left: 50%; top: var(--veo-chrome-h, ${CHROME_TOPBAR_H + CHROME_STEPS_H}px);
4071
+ transform: translate(-50%, -50%);
4072
+ border: 0; background: #2b2623; color: #fff; font: inherit; font-size: 14px; font-weight: 500;
4073
+ border-radius: 999px; padding: 10px 18px; cursor: pointer;
4074
+ display: inline-flex; align-items: center; gap: 8px;
4075
+ box-shadow: 0 6px 18px rgba(0,0,0,0.25);
4076
+ }
4077
+ `;
4078
+
3511
4079
  // src/plugins/builder/builder-panel.ts
3512
4080
  var DEFAULT_PANEL_PATH = "/guides/design";
3513
- var PANEL_W = 400;
3514
- var PANEL_H = 640;
4081
+ var PANEL_W = 484;
4082
+ var PANEL_H = 660;
3515
4083
  var MIN_W = 320;
3516
4084
  var MIN_H = 320;
3517
4085
  var MARGIN = 16;
4086
+ var HEADER_H = 52;
3518
4087
  function injectBuilderPanel(opts) {
3519
4088
  if (!hasWindow() || !hasDocument()) {
3520
- return { target: () => null, frameRect: () => null, teardown: () => {
3521
- } };
4089
+ return {
4090
+ target: () => null,
4091
+ frameRect: () => null,
4092
+ teardown: () => {
4093
+ },
4094
+ setTitle: () => {
4095
+ },
4096
+ setVisible: () => {
4097
+ },
4098
+ isVisible: () => false,
4099
+ setTopOffset: () => {
4100
+ }
4101
+ };
3522
4102
  }
3523
4103
  const panelUrl = buildPanelUrl(opts);
3524
4104
  const host = document.createElement("div");
@@ -3528,43 +4108,68 @@ function injectBuilderPanel(opts) {
3528
4108
  style.textContent = PANEL_CSS;
3529
4109
  const card = document.createElement("div");
3530
4110
  card.className = "veo-panel";
3531
- let left = Math.max(MARGIN, window.innerWidth - PANEL_W - MARGIN);
3532
- let top = MARGIN;
4111
+ let topOffset = opts.topOffset ?? 0;
4112
+ let left = 40;
4113
+ let top = topOffset + MARGIN;
3533
4114
  card.style.left = `${left}px`;
3534
4115
  card.style.top = `${top}px`;
3535
4116
  card.style.width = `${PANEL_W}px`;
3536
4117
  card.style.height = `${PANEL_H}px`;
3537
4118
  const header = document.createElement("div");
3538
4119
  header.className = "veo-panel-header";
4120
+ const grip = document.createElement("span");
4121
+ grip.className = "veo-panel-grip";
4122
+ grip.setAttribute("aria-hidden", "true");
4123
+ grip.textContent = "\xB7\xB7\xB7";
3539
4124
  const title = document.createElement("span");
3540
4125
  title.className = "veo-panel-title";
3541
4126
  title.textContent = "OnUser \xB7 Editor";
3542
4127
  const actions = document.createElement("div");
3543
4128
  actions.className = "veo-panel-actions";
3544
- const popout = document.createElement("button");
3545
- popout.className = "veo-panel-btn";
3546
- popout.type = "button";
3547
- popout.title = "Abrir en ventana aparte";
3548
- popout.setAttribute("aria-label", "Abrir en ventana aparte");
3549
- popout.textContent = "\u29C9";
3550
- const close = document.createElement("button");
3551
- close.className = "veo-panel-btn veo-panel-close";
3552
- close.type = "button";
3553
- close.title = "Cerrar";
3554
- close.setAttribute("aria-label", "Cerrar");
3555
- close.textContent = "\u2715";
3556
- actions.append(popout, close);
3557
- header.append(title, actions);
4129
+ const popout = panelButton(ICON_POPOUT, "Abrir en ventana aparte");
4130
+ const maximize = panelButton(ICON_EXPAND, "Maximizar");
4131
+ const minimize = panelButton(ICON_MINUS, "Minimizar");
4132
+ actions.append(popout, maximize, minimize);
4133
+ header.append(grip, title, actions);
3558
4134
  const iframe = document.createElement("iframe");
3559
4135
  iframe.className = "veo-panel-frame";
3560
4136
  iframe.title = "Editor de Veo";
3561
4137
  iframe.src = panelUrl;
3562
- const grip = document.createElement("div");
3563
- grip.className = "veo-panel-resize";
3564
- grip.setAttribute("aria-hidden", "true");
3565
- card.append(header, iframe, grip);
4138
+ const resizeGrip = document.createElement("div");
4139
+ resizeGrip.className = "veo-panel-resize";
4140
+ resizeGrip.setAttribute("aria-hidden", "true");
4141
+ card.append(header, iframe, resizeGrip);
3566
4142
  shadow.append(style, card);
3567
4143
  document.body.appendChild(host);
4144
+ let visible = true;
4145
+ let maximized = false;
4146
+ let restore = null;
4147
+ const setVisible = (next) => {
4148
+ visible = next;
4149
+ card.style.display = next && !(popped && !popped.closed) ? "" : "none";
4150
+ };
4151
+ minimize.addEventListener("click", () => setVisible(false));
4152
+ maximize.addEventListener("click", () => {
4153
+ maximized = !maximized;
4154
+ if (maximized) {
4155
+ restore = { left, top, w: card.offsetWidth, h: card.offsetHeight };
4156
+ left = MARGIN;
4157
+ top = topOffset + MARGIN;
4158
+ card.style.left = `${left}px`;
4159
+ card.style.top = `${top}px`;
4160
+ card.style.width = `${Math.max(MIN_W, window.innerWidth - MARGIN * 2)}px`;
4161
+ card.style.height = `${Math.max(MIN_H, window.innerHeight - top - MARGIN)}px`;
4162
+ maximize.title = "Restaurar";
4163
+ } else if (restore) {
4164
+ left = restore.left;
4165
+ top = restore.top;
4166
+ card.style.left = `${left}px`;
4167
+ card.style.top = `${top}px`;
4168
+ card.style.width = `${restore.w}px`;
4169
+ card.style.height = `${restore.h}px`;
4170
+ maximize.title = "Maximizar";
4171
+ }
4172
+ });
3568
4173
  let popped = null;
3569
4174
  let pollTimer;
3570
4175
  const stopPoll = () => {
@@ -3573,7 +4178,6 @@ function injectBuilderPanel(opts) {
3573
4178
  pollTimer = void 0;
3574
4179
  }
3575
4180
  };
3576
- close.addEventListener("click", () => opts.onClose());
3577
4181
  popout.addEventListener("click", () => {
3578
4182
  const win = window.open(
3579
4183
  panelUrl,
@@ -3591,7 +4195,7 @@ function injectBuilderPanel(opts) {
3591
4195
  }, 600);
3592
4196
  });
3593
4197
  header.addEventListener("mousedown", (e) => {
3594
- if (e.target?.classList.contains("veo-panel-btn")) return;
4198
+ if (e.target?.closest(".veo-panel-btn")) return;
3595
4199
  e.preventDefault();
3596
4200
  const sx = e.clientX;
3597
4201
  const sy = e.clientY;
@@ -3600,7 +4204,7 @@ function injectBuilderPanel(opts) {
3600
4204
  iframe.style.pointerEvents = "none";
3601
4205
  const move = (ev) => {
3602
4206
  left = clamp3(ol + (ev.clientX - sx), 0, Math.max(0, window.innerWidth - card.offsetWidth));
3603
- top = clamp3(ot + (ev.clientY - sy), 0, Math.max(0, window.innerHeight - 40));
4207
+ top = clamp3(ot + (ev.clientY - sy), topOffset, Math.max(topOffset, window.innerHeight - 40));
3604
4208
  card.style.left = `${left}px`;
3605
4209
  card.style.top = `${top}px`;
3606
4210
  };
@@ -3612,7 +4216,7 @@ function injectBuilderPanel(opts) {
3612
4216
  window.addEventListener("mousemove", move);
3613
4217
  window.addEventListener("mouseup", up);
3614
4218
  });
3615
- grip.addEventListener("mousedown", (e) => {
4219
+ resizeGrip.addEventListener("mousedown", (e) => {
3616
4220
  e.preventDefault();
3617
4221
  e.stopPropagation();
3618
4222
  const sx = e.clientX;
@@ -3639,9 +4243,33 @@ function injectBuilderPanel(opts) {
3639
4243
  stopPoll();
3640
4244
  if (popped && !popped.closed) popped.close();
3641
4245
  host.remove();
4246
+ },
4247
+ setTitle: (t) => {
4248
+ title.textContent = t;
4249
+ },
4250
+ setVisible,
4251
+ isVisible: () => visible,
4252
+ setTopOffset: (px) => {
4253
+ topOffset = px;
4254
+ if (top < topOffset + MARGIN) {
4255
+ top = topOffset + MARGIN;
4256
+ card.style.top = `${top}px`;
4257
+ }
3642
4258
  }
3643
4259
  };
3644
4260
  }
4261
+ var ICON_POPOUT = '<path stroke-linecap="round" stroke-linejoin="round" d="m6.75 7.5 3 2.25-3 2.25m4.5 0h3m-9 8.25h13.5A2.25 2.25 0 0 0 21 18V6a2.25 2.25 0 0 0-2.25-2.25H5.25A2.25 2.25 0 0 0 3 6v12a2.25 2.25 0 0 0 2.25 2.25Z"/>';
4262
+ var ICON_EXPAND = '<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15"/>';
4263
+ var ICON_MINUS = '<path stroke-linecap="round" stroke-linejoin="round" d="M5 12h14"/>';
4264
+ function panelButton(iconPath, label) {
4265
+ const b = document.createElement("button");
4266
+ b.className = "veo-panel-btn";
4267
+ b.type = "button";
4268
+ b.title = label;
4269
+ b.setAttribute("aria-label", label);
4270
+ b.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true">${iconPath}</svg>`;
4271
+ return b;
4272
+ }
3645
4273
  function clamp3(v, min, max) {
3646
4274
  return Math.min(Math.max(v, min), max);
3647
4275
  }
@@ -3661,28 +4289,33 @@ var PANEL_CSS = `
3661
4289
  max-width: 96vw; max-height: 94vh;
3662
4290
  display: flex; flex-direction: column;
3663
4291
  background: #fff;
3664
- border-radius: 12px;
3665
- box-shadow: 0 12px 40px rgba(0,0,0,0.28);
4292
+ border-radius: 14px;
4293
+ box-shadow: 0 12px 40px rgba(0,0,0,0.22);
3666
4294
  overflow: hidden;
3667
4295
  z-index: 2147483647;
3668
4296
  font-family: system-ui, -apple-system, sans-serif;
3669
4297
  }
3670
4298
  .veo-panel-header {
3671
- flex: 0 0 auto;
4299
+ position: relative; flex: 0 0 auto; height: ${HEADER_H}px; box-sizing: border-box;
3672
4300
  display: flex; align-items: center; justify-content: space-between;
3673
- padding: 8px 10px 8px 12px;
3674
- background: #1b1714; color: #f7f1eb;
3675
- font-size: 13px; font-weight: 600;
4301
+ padding: 10px 14px 0 22px;
4302
+ background: #fbf8f5; color: #1c1917; border-bottom: 1px solid #e8e2dc;
4303
+ font-size: 17px; font-weight: 700;
3676
4304
  cursor: grab;
3677
4305
  }
3678
4306
  .veo-panel-header:active { cursor: grabbing; }
4307
+ .veo-panel-grip {
4308
+ position: absolute; top: 0; left: 50%; transform: translateX(-50%);
4309
+ color: #b5aca4; font-size: 14px; letter-spacing: 1px; line-height: 12px;
4310
+ }
4311
+ .veo-panel-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
3679
4312
  .veo-panel-actions { display: flex; gap: 2px; }
3680
4313
  .veo-panel-btn {
3681
- border: 0; background: transparent; color: #fff;
3682
- font-size: 14px; cursor: pointer; line-height: 1;
3683
- padding: 4px 7px; border-radius: 6px;
4314
+ border: 0; background: transparent; color: #4b4540;
4315
+ cursor: pointer; line-height: 1; display: inline-flex; align-items: center;
4316
+ padding: 6px; border-radius: 8px;
3684
4317
  }
3685
- .veo-panel-btn:hover { background: rgba(255,255,255,0.15); }
4318
+ .veo-panel-btn:hover { background: rgba(0,0,0,0.06); color: #1c1917; }
3686
4319
  .veo-panel-frame { flex: 1 1 auto; width: 100%; border: 0; background: #fff; }
3687
4320
  .veo-panel-resize {
3688
4321
  position: absolute; right: 0; bottom: 0;
@@ -4022,9 +4655,9 @@ var PICKER_STYLES = `
4022
4655
  z-index: ${PICKER_Z};
4023
4656
  pointer-events: none;
4024
4657
  box-sizing: border-box;
4025
- background: rgba(99, 102, 241, 0.22);
4026
- border: 1px dashed rgba(99, 102, 241, 0.95);
4027
- border-radius: 2px;
4658
+ background: transparent;
4659
+ border: 3px dashed #b74d00;
4660
+ border-radius: 8px;
4028
4661
  transition: top 60ms ease-out, left 60ms ease-out, width 60ms ease-out, height 60ms ease-out;
4029
4662
  }
4030
4663
  .veo-pick-label {
@@ -4032,30 +4665,48 @@ var PICKER_STYLES = `
4032
4665
  z-index: ${PICKER_Z};
4033
4666
  pointer-events: none;
4034
4667
  max-width: 360px;
4035
- background: #1e1b4b;
4036
- color: #e0e7ff;
4668
+ background: #2b2623;
4669
+ color: #f5efe9;
4037
4670
  font: 12px/1.45 ui-monospace, SFMono-Regular, Menlo, monospace;
4038
4671
  padding: 6px 9px;
4039
4672
  border-radius: 6px;
4040
4673
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
4041
4674
  }
4042
- .veo-pick-label .veo-pick-sel { color: #c7d2fe; word-break: break-all; }
4043
- .veo-pick-label .veo-pick-dim { color: #a5b4fc; margin-top: 3px; }
4675
+ .veo-pick-label .veo-pick-sel { color: #ffd1ad; word-break: break-all; }
4676
+ .veo-pick-label .veo-pick-dim { color: #c9c2bb; margin-top: 3px; }
4044
4677
  .veo-pick-hint {
4045
4678
  position: fixed;
4046
- top: 12px;
4679
+ top: 28px;
4047
4680
  left: 50%;
4048
4681
  transform: translateX(-50%);
4049
4682
  z-index: ${PICKER_Z};
4050
4683
  pointer-events: none;
4051
- background: #4338ca;
4684
+ display: flex; align-items: center; gap: 16px;
4685
+ max-width: min(760px, calc(100vw - 32px));
4686
+ background: #2b2623;
4052
4687
  color: #fff;
4053
- font: 13px/1 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
4054
- padding: 8px 14px;
4055
- border-radius: 999px;
4056
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
4688
+ font: 15px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
4689
+ padding: 14px 18px 14px 16px;
4690
+ border-radius: 14px;
4691
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
4692
+ }
4693
+ .veo-pick-hint-icon {
4694
+ flex: 0 0 auto; width: 44px; height: 44px; border-radius: 999px;
4695
+ background: rgba(255,255,255,0.12);
4696
+ display: inline-flex; align-items: center; justify-content: center;
4697
+ }
4698
+ .veo-pick-hint-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
4699
+ .veo-pick-hint-title { font-weight: 700; }
4700
+ .veo-pick-hint-sub { font-size: 13px; color: rgba(255,255,255,0.75); }
4701
+ .veo-pick-cancel {
4702
+ flex: 0 0 auto; pointer-events: auto;
4703
+ border: 0; border-radius: 10px; padding: 12px 18px;
4704
+ background: rgba(255,255,255,0.14); color: #fff;
4705
+ font: inherit; font-weight: 600; cursor: pointer;
4057
4706
  }
4707
+ .veo-pick-cancel:hover { background: rgba(255,255,255,0.24); }
4058
4708
  `;
4709
+ var HINT_ICON = '<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M7.5 3.75H6A2.25 2.25 0 0 0 3.75 6v1.5M16.5 3.75H18A2.25 2.25 0 0 1 20.25 6v1.5m0 9V18A2.25 2.25 0 0 1 18 20.25h-1.5m-9 0H6A2.25 2.25 0 0 1 3.75 18v-1.5M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"/></svg>';
4059
4710
  var ElementPicker = class {
4060
4711
  constructor() {
4061
4712
  this.host = null;
@@ -4072,6 +4723,7 @@ var ElementPicker = class {
4072
4723
  this.draw(target);
4073
4724
  };
4074
4725
  this.onClick = (event) => {
4726
+ if (event.altKey) return;
4075
4727
  const target = this.current ?? (event.target instanceof Element ? event.target : null);
4076
4728
  if (!target || this.isOwn(target)) return;
4077
4729
  event.preventDefault();
@@ -4092,7 +4744,7 @@ var ElementPicker = class {
4092
4744
  };
4093
4745
  /** Traga mousedown/mouseup durante el picking (evita focus/active de la app). */
4094
4746
  this.swallow = (event) => {
4095
- if (this.isOwn(event.target)) return;
4747
+ if (event.altKey || this.isOwn(event.target)) return;
4096
4748
  event.preventDefault();
4097
4749
  event.stopPropagation();
4098
4750
  };
@@ -4118,7 +4770,29 @@ var ElementPicker = class {
4118
4770
  this.label.style.display = "none";
4119
4771
  const hint = document.createElement("div");
4120
4772
  hint.className = "veo-pick-hint";
4121
- hint.textContent = options.hint ?? "Selecciona un elemento \xB7 Esc para cancelar";
4773
+ const icon = document.createElement("span");
4774
+ icon.className = "veo-pick-hint-icon";
4775
+ icon.innerHTML = HINT_ICON;
4776
+ const text = document.createElement("div");
4777
+ text.className = "veo-pick-hint-text";
4778
+ const title = document.createElement("span");
4779
+ title.className = "veo-pick-hint-title";
4780
+ title.textContent = options.hint ?? "Selecciona un elemento existente para colocar la gu\xEDa.";
4781
+ const sub = document.createElement("span");
4782
+ sub.className = "veo-pick-hint-sub";
4783
+ sub.textContent = options.subhint ?? "Usa Alt + clic u Opci\xF3n + clic para abrir men\xFAs y desplegables.";
4784
+ text.append(title, sub);
4785
+ const cancel = document.createElement("button");
4786
+ cancel.className = "veo-pick-cancel";
4787
+ cancel.type = "button";
4788
+ cancel.textContent = "Cancelar";
4789
+ cancel.addEventListener("click", (e) => {
4790
+ e.stopPropagation();
4791
+ const cb = this.onCancel;
4792
+ this.stop();
4793
+ cb?.();
4794
+ });
4795
+ hint.append(icon, text, cancel);
4122
4796
  shadow.append(style, this.box, this.label, hint);
4123
4797
  document.body.appendChild(host);
4124
4798
  this.host = host;
@@ -4216,6 +4890,7 @@ function initBuilderMode() {
4216
4890
  if (!dashboardOrigin) return null;
4217
4891
  let picker = null;
4218
4892
  let panel = null;
4893
+ let chrome = null;
4219
4894
  let staticTarget = null;
4220
4895
  let manipulator = null;
4221
4896
  let dropTarget = null;
@@ -4223,26 +4898,89 @@ function initBuilderMode() {
4223
4898
  let previewStepIndex = 0;
4224
4899
  let pendingPreview = null;
4225
4900
  let lastPreview = null;
4901
+ let previewMode = false;
4902
+ let previewTrigger = null;
4903
+ let previewFired = false;
4904
+ let previewDelay = null;
4905
+ let panelWasVisible = true;
4226
4906
  const getTarget = () => panel ? panel.target() : staticTarget;
4227
4907
  const post = (event) => {
4228
4908
  getTarget()?.postMessage({ source: VEO_BUILDER_SOURCE, token, ...event }, dashboardOrigin);
4229
4909
  };
4230
- const previewMatchesPage = (cmd) => {
4910
+ const previewStep = (cmd) => {
4231
4911
  const steps = cmd.guide.guideSteps;
4232
- const step = steps?.[cmd.guide.startStepIndex ?? 0];
4233
- return stepMatchesPage(step?.pageLocation, window.location.href);
4912
+ return steps?.[cmd.guide.startStepIndex ?? 0];
4913
+ };
4914
+ const previewMatchesPage = (cmd) => stepMatchesPage(previewStep(cmd)?.pageLocation, window.location.href);
4915
+ const postPreviewStatus = (payload) => post({ type: "preview-status", payload });
4916
+ const withholdPreview = () => {
4917
+ manipulator?.detach();
4918
+ manipulator = null;
4919
+ dropTarget?.detach();
4920
+ dropTarget = null;
4921
+ closeGuidePreview();
4234
4922
  };
4235
4923
  const applyPreviewGated = (cmd) => {
4236
4924
  lastPreview = cmd;
4237
- if (previewMatchesPage(cmd)) {
4238
- applyPreview(cmd);
4239
- } else {
4240
- manipulator?.detach();
4241
- manipulator = null;
4242
- dropTarget?.detach();
4243
- dropTarget = null;
4244
- closeGuidePreview();
4925
+ if (!previewMatchesPage(cmd)) {
4926
+ cancelPreviewDelay();
4927
+ withholdPreview();
4928
+ postPreviewStatus({
4929
+ state: "page-mismatch",
4930
+ pattern: previewStep(cmd)?.pageLocation?.pattern ?? ""
4931
+ });
4932
+ return;
4933
+ }
4934
+ const devices = cmd.guide.activationRules?.devices;
4935
+ if (cmd.honorTrigger && !previewMode && devices && !isDeviceAllowed(devices)) {
4936
+ cancelPreviewDelay();
4937
+ withholdPreview();
4938
+ postPreviewStatus({ state: "device-mismatch", devices });
4939
+ return;
4245
4940
  }
4941
+ applyPreview(cmd);
4942
+ };
4943
+ const disarmPreviewTrigger = () => {
4944
+ previewTrigger?.();
4945
+ previewTrigger = null;
4946
+ };
4947
+ const cancelPreviewDelay = () => {
4948
+ if (previewDelay) clearTimeout(previewDelay);
4949
+ previewDelay = null;
4950
+ };
4951
+ const armPreviewTrigger = (selector, trigger) => {
4952
+ disarmPreviewTrigger();
4953
+ const onFire = (e) => {
4954
+ const target = e.target;
4955
+ if (!(target instanceof Element)) return;
4956
+ if (target.closest("[data-veo-builder-chrome],[data-veo-builder-panel],[data-veo-picker]")) {
4957
+ return;
4958
+ }
4959
+ try {
4960
+ if (!target.closest(selector)) return;
4961
+ } catch {
4962
+ return;
4963
+ }
4964
+ disarmPreviewTrigger();
4965
+ previewFired = true;
4966
+ if (lastPreview) applyPreviewGated(lastPreview);
4967
+ };
4968
+ document.addEventListener("click", onFire, true);
4969
+ if (trigger === "on_hover") document.addEventListener("mouseover", onFire, true);
4970
+ previewTrigger = () => {
4971
+ document.removeEventListener("click", onFire, true);
4972
+ document.removeEventListener("mouseover", onFire, true);
4973
+ };
4974
+ postPreviewStatus({ state: "waiting-trigger", trigger, selector });
4975
+ };
4976
+ const armPreviewDelay = (delayMs) => {
4977
+ cancelPreviewDelay();
4978
+ previewDelay = setTimeout(() => {
4979
+ previewDelay = null;
4980
+ previewFired = true;
4981
+ if (lastPreview) applyPreviewGated(lastPreview);
4982
+ }, delayMs);
4983
+ postPreviewStatus({ state: "waiting-delay", delayMs });
4246
4984
  };
4247
4985
  const applyPreview = (cmd) => {
4248
4986
  manipulator?.detach();
@@ -4250,8 +4988,38 @@ function initBuilderMode() {
4250
4988
  dropTarget?.detach();
4251
4989
  dropTarget = null;
4252
4990
  previewStepIndex = cmd.guide.startStepIndex ?? 0;
4253
- const handle = previewGuide(cmd.guide);
4254
- if (!cmd.editable) return;
4991
+ const rules = cmd.guide.activationRules;
4992
+ const trig = rules?.trigger;
4993
+ const sel = rules?.selector?.trim();
4994
+ const gate = cmd.honorTrigger && !previewFired && !previewMode;
4995
+ if (gate && (trig === "on_click" || trig === "on_hover") && sel) {
4996
+ cancelPreviewDelay();
4997
+ closeGuidePreview();
4998
+ armPreviewTrigger(sel, trig);
4999
+ return;
5000
+ }
5001
+ const delayMs = trig === "delayed" ? rules?.delayMs ?? 0 : 0;
5002
+ if (gate && delayMs > 0) {
5003
+ disarmPreviewTrigger();
5004
+ closeGuidePreview();
5005
+ if (!previewDelay) armPreviewDelay(delayMs);
5006
+ return;
5007
+ }
5008
+ disarmPreviewTrigger();
5009
+ cancelPreviewDelay();
5010
+ const handle = previewGuide({
5011
+ ...cmd.guide,
5012
+ onClosed: () => {
5013
+ manipulator?.detach();
5014
+ manipulator = null;
5015
+ dropTarget?.detach();
5016
+ dropTarget = null;
5017
+ previewFired = false;
5018
+ if (lastPreview) applyPreviewGated(lastPreview);
5019
+ }
5020
+ });
5021
+ if (cmd.honorTrigger) postPreviewStatus({ state: "shown" });
5022
+ if (!cmd.editable || previewMode) return;
4255
5023
  void handle.ready.then((result) => {
4256
5024
  if (!result.rendered) return;
4257
5025
  const host = handle.host();
@@ -4319,26 +5087,57 @@ function initBuilderMode() {
4319
5087
  const handleCommand = (cmd) => {
4320
5088
  switch (cmd.type) {
4321
5089
  case "panel-ready":
4322
- post({ type: "ready" });
5090
+ post(chrome ? { type: "ready", chrome: true } : { type: "ready" });
5091
+ break;
5092
+ case "editor-state":
5093
+ chrome?.setState(cmd.state);
5094
+ panel?.setTitle(
5095
+ cmd.state.section === "activation" ? "Ajustes de activaci\xF3n" : cmd.state.section === "badge" ? "Ajustes de badge" : cmd.state.section === "target" ? "Ajustes del elemento objetivo" : cmd.state.steps[cmd.state.activeStep]?.label ?? "Paso"
5096
+ );
5097
+ break;
5098
+ case "panel-visibility":
5099
+ panel?.setVisible(cmd.visible);
4323
5100
  break;
4324
- case "start-picker":
5101
+ case "start-picker": {
4325
5102
  picker?.stop();
5103
+ const panelWasShown = panel?.isVisible() ?? false;
5104
+ if (panelWasShown) panel?.setVisible(false);
5105
+ chrome?.setHidden(true);
5106
+ const previewToRestore = lastPreview;
5107
+ if (previewToRestore) {
5108
+ manipulator?.detach();
5109
+ manipulator = null;
5110
+ dropTarget?.detach();
5111
+ dropTarget = null;
5112
+ closeGuidePreview();
5113
+ }
5114
+ const restorePanel = () => {
5115
+ chrome?.setHidden(false);
5116
+ if (panelWasShown) panel?.setVisible(true);
5117
+ if (previewToRestore && lastPreview === previewToRestore) {
5118
+ applyPreviewGated(previewToRestore);
5119
+ }
5120
+ };
4326
5121
  picker = startElementPicker({
4327
5122
  onPick: (payload) => {
4328
5123
  picker = null;
5124
+ restorePanel();
4329
5125
  post({ type: "picked", payload });
4330
5126
  },
4331
5127
  onCancel: () => {
4332
5128
  picker = null;
5129
+ restorePanel();
4333
5130
  post({ type: "pick-cancelled" });
4334
5131
  },
4335
5132
  ...cmd.maxAncestors !== void 0 ? { maxAncestors: cmd.maxAncestors } : {},
4336
5133
  ...cmd.hint !== void 0 ? { hint: cmd.hint } : {}
4337
5134
  });
4338
5135
  break;
5136
+ }
4339
5137
  case "stop-picker":
4340
5138
  picker?.stop();
4341
5139
  picker = null;
5140
+ chrome?.setHidden(false);
4342
5141
  break;
4343
5142
  case "preview":
4344
5143
  if (!cmd.guide) break;
@@ -4356,6 +5155,9 @@ function initBuilderMode() {
4356
5155
  hideDragGhost();
4357
5156
  pendingPreview = null;
4358
5157
  lastPreview = null;
5158
+ disarmPreviewTrigger();
5159
+ cancelPreviewDelay();
5160
+ previewFired = false;
4359
5161
  closeGuidePreview();
4360
5162
  break;
4361
5163
  case "teardown":
@@ -4407,21 +5209,62 @@ function initBuilderMode() {
4407
5209
  dropTarget = null;
4408
5210
  hideDragGhost();
4409
5211
  pendingPreview = null;
5212
+ disarmPreviewTrigger();
5213
+ cancelPreviewDelay();
4410
5214
  closeGuidePreview();
4411
5215
  post({ type: "closed" });
4412
5216
  panel?.teardown();
4413
5217
  panel = null;
5218
+ chrome?.teardown();
5219
+ chrome = null;
4414
5220
  clearBuilderToken();
4415
5221
  };
4416
5222
  if (ctx.panel) {
5223
+ chrome = injectBuilderChrome({
5224
+ appLabel: window.location.hostname,
5225
+ onAction: (action) => {
5226
+ switch (action.action) {
5227
+ // Controles que resuelve el SDK solo (no van al panel).
5228
+ case "dock":
5229
+ panel?.setTopOffset(action.position === "top" ? chrome?.height() ?? 0 : 0);
5230
+ return;
5231
+ case "show-panel":
5232
+ panel?.setVisible(true);
5233
+ return;
5234
+ case "preview":
5235
+ if (previewMode) return;
5236
+ previewMode = true;
5237
+ panelWasVisible = panel?.isVisible() ?? true;
5238
+ panel?.setVisible(false);
5239
+ chrome?.setPreview(true);
5240
+ if (lastPreview) applyPreviewGated(lastPreview);
5241
+ return;
5242
+ case "exit-preview":
5243
+ if (!previewMode) return;
5244
+ previewMode = false;
5245
+ chrome?.setPreview(false);
5246
+ panel?.setVisible(panelWasVisible);
5247
+ if (lastPreview) applyPreviewGated(lastPreview);
5248
+ return;
5249
+ case "select-activation":
5250
+ case "select-step":
5251
+ case "add-step":
5252
+ panel?.setVisible(true);
5253
+ break;
5254
+ }
5255
+ post({ type: "chrome-action", payload: action });
5256
+ }
5257
+ });
4417
5258
  panel = injectBuilderPanel({
4418
5259
  dashboardOrigin,
4419
5260
  token,
4420
5261
  appOrigin: window.location.origin,
4421
5262
  guide: ctx.guide,
4422
5263
  panelPath: ctx.panelPath,
4423
- onClose: () => teardown()
5264
+ onClose: () => teardown(),
5265
+ topOffset: chrome.height()
4424
5266
  });
5267
+ chrome.onHeightChange((h) => panel?.setTopOffset(chrome?.dock() === "top" ? h : 0));
4425
5268
  } else {
4426
5269
  staticTarget = resolveTarget();
4427
5270
  if (!staticTarget) return null;