veo-sdk 0.5.32 → 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,74 +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`;
635
643
  }
636
644
  if (typeof s.height === "number" && Number.isFinite(s.height) && s.height > 0) {
637
- el.style.height = `${clampNum(s.height, 1, 2e3)}px`;
645
+ el2.style.height = `${clampNum(s.height, 1, 2e3)}px`;
638
646
  }
639
647
  if (kind !== "image" && typeof s.width === "number" && Number.isFinite(s.width) && s.width > 0) {
640
- el.style.width = `${clampNum(s.width, 1, 2e3)}px`;
648
+ el2.style.width = `${clampNum(s.width, 1, 2e3)}px`;
641
649
  }
642
650
  for (const side of ["Top", "Right", "Bottom", "Left"]) {
643
651
  const p = s[`padding${side}`];
644
652
  if (typeof p === "number" && Number.isFinite(p)) {
645
- el.style.setProperty(`padding-${side.toLowerCase()}`, `${clampNum(p, 0, 128)}px`);
653
+ el2.style.setProperty(`padding-${side.toLowerCase()}`, `${clampNum(p, 0, 128)}px`);
646
654
  }
647
655
  const m = s[`margin${side}`];
648
656
  if (typeof m === "number" && Number.isFinite(m)) {
649
- el.style.setProperty(`margin-${side.toLowerCase()}`, `${clampNum(m, 0, 128)}px`);
657
+ el2.style.setProperty(`margin-${side.toLowerCase()}`, `${clampNum(m, 0, 128)}px`);
650
658
  }
651
659
  }
652
660
  }
@@ -781,11 +789,11 @@ function applyDesignVars(host, style) {
781
789
  host.style.setProperty("--veo-pos-y", `${py * 100}%`);
782
790
  }
783
791
  if (s.elements && typeof s.elements === "object") {
784
- const el = s.elements;
785
- applyElementVars(host, "title", el.title);
786
- applyElementVars(host, "text", el.text);
787
- applyElementVars(host, "image", el.image);
788
- 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);
789
797
  }
790
798
  }
791
799
  function applyCtaVars(host, raw) {
@@ -809,29 +817,29 @@ function applyCtaVars(host, raw) {
809
817
  }
810
818
  function applyElementVars(host, prefix, raw) {
811
819
  if (!raw || typeof raw !== "object") return;
812
- const el = raw;
813
- 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")) {
814
822
  const mode = ELEMENT_ALIGN_MODE[prefix] ?? "text";
815
- const value = mode === "self" ? ALIGN_SELF[el.align] : el.align;
823
+ const value = mode === "self" ? ALIGN_SELF[el2.align] : el2.align;
816
824
  host.style.setProperty(`--veo-${prefix}-align`, value);
817
825
  }
818
- if (typeof el.color === "string" && COLOR_RE.test(el.color.trim())) {
819
- 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());
820
828
  }
821
- if (typeof el.fontSize === "number" && Number.isFinite(el.fontSize)) {
822
- 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`);
823
831
  }
824
- if (typeof el.width === "number" && Number.isFinite(el.width)) {
825
- 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)}%`);
826
834
  }
827
- if (typeof el.radius === "number" && Number.isFinite(el.radius)) {
828
- 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`);
829
837
  }
830
- if (typeof el.marginTop === "number" && Number.isFinite(el.marginTop)) {
831
- 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`);
832
840
  }
833
- if (typeof el.marginBottom === "number" && Number.isFinite(el.marginBottom)) {
834
- 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`);
835
843
  }
836
844
  }
837
845
 
@@ -881,16 +889,16 @@ function waitForElement(selector, timeoutMs = DEFAULT_ANCHOR_WAIT_MS) {
881
889
  return;
882
890
  }
883
891
  let resolved = false;
884
- const finish = (el) => {
892
+ const finish = (el2) => {
885
893
  if (resolved) return;
886
894
  resolved = true;
887
895
  observer.disconnect();
888
896
  clearTimeout(timer);
889
- resolve(el);
897
+ resolve(el2);
890
898
  };
891
899
  const observer = new MutationObserver(() => {
892
- const el = safeQuery();
893
- if (el) finish(el);
900
+ const el2 = safeQuery();
901
+ if (el2) finish(el2);
894
902
  });
895
903
  observer.observe(document.body, { childList: true, subtree: true });
896
904
  const timer = setTimeout(() => finish(null), timeoutMs);
@@ -1950,10 +1958,10 @@ function collectHostStyles() {
1950
1958
  function hostHtmlAttrs() {
1951
1959
  if (typeof document === "undefined") return "";
1952
1960
  try {
1953
- const el = document.documentElement;
1961
+ const el2 = document.documentElement;
1954
1962
  const attrs = [];
1955
1963
  for (const name of ["class", "style", "data-theme", "data-mode", "data-color-mode"]) {
1956
- const v = el.getAttribute(name);
1964
+ const v = el2.getAttribute(name);
1957
1965
  if (v && v.length <= 1e3 && !/["<>]/.test(v)) attrs.push(`${name}="${v}"`);
1958
1966
  }
1959
1967
  return attrs.join(" ");
@@ -2336,6 +2344,188 @@ function buildScale(wrapper, doc, min, max, btnClass, symbol) {
2336
2344
  wrapper.appendChild(row);
2337
2345
  return { wrapper, read: () => selected };
2338
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
+ }
2339
2529
 
2340
2530
  // src/plugins/guides/renderers/composite-renderer.ts
2341
2531
  var ROW_ALIGN = {
@@ -2631,7 +2821,9 @@ var CompositeRenderer = class extends BaseRenderer {
2631
2821
  badge.style.cursor = "pointer";
2632
2822
  badge.style.display = "inline-flex";
2633
2823
  badge.style.alignItems = "center";
2634
- 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);
2635
2827
  root.appendChild(badge);
2636
2828
  if (design.fullWidth === true) {
2637
2829
  card.style.width = "auto";
@@ -2646,10 +2838,17 @@ var CompositeRenderer = class extends BaseRenderer {
2646
2838
  if (caretEl) wrapper.appendChild(caretEl);
2647
2839
  wrapper.appendChild(card);
2648
2840
  root.appendChild(wrapper);
2649
- host.style.display = "inline-block";
2650
- const pos = inlinePosOf(step.inlinePosition);
2651
- insertHost(anchor, host, pos);
2652
- 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
+ }
2653
2852
  let open = false;
2654
2853
  let stopFloat = null;
2655
2854
  let closeTimer = null;
@@ -2702,6 +2901,7 @@ var CompositeRenderer = class extends BaseRenderer {
2702
2901
  stopFloat?.();
2703
2902
  if (closeTimer !== null) clearTimeout(closeTimer);
2704
2903
  });
2904
+ if (context.badgeOpen) openTip();
2705
2905
  return closeTip;
2706
2906
  }
2707
2907
  };
@@ -2755,14 +2955,14 @@ function makeCaret(doc, design) {
2755
2955
  return caret;
2756
2956
  }
2757
2957
  function liftTarget(anchor, zIndex) {
2758
- const el = anchor;
2759
- const prevPosition = el.style.position;
2760
- const prevZ = el.style.zIndex;
2761
- if (getComputedStyle(el).position === "static") el.style.position = "relative";
2762
- 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);
2763
2963
  return () => {
2764
- el.style.position = prevPosition;
2765
- el.style.zIndex = prevZ;
2964
+ el2.style.position = prevPosition;
2965
+ el2.style.zIndex = prevZ;
2766
2966
  };
2767
2967
  }
2768
2968
  async function anchorPosition(anchor, wrapper, caret, preferred = "bottom") {
@@ -3278,7 +3478,8 @@ var GuidePreviewController = class {
3278
3478
  }
3279
3479
  preview(input) {
3280
3480
  const nextStep = input.guideSteps[0];
3281
- 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)) {
3282
3483
  this.input = input;
3283
3484
  return {
3284
3485
  close: () => this.close(),
@@ -3308,7 +3509,8 @@ var GuidePreviewController = class {
3308
3509
  }
3309
3510
  return null;
3310
3511
  }
3311
- close() {
3512
+ close(byUser = false) {
3513
+ const onClosed = byUser ? this.input?.onClosed : void 0;
3312
3514
  if (this.singleStep) {
3313
3515
  safeDestroy(this.singleStep);
3314
3516
  this.singleStep = null;
@@ -3320,6 +3522,7 @@ var GuidePreviewController = class {
3320
3522
  this.walkthroughGuide = null;
3321
3523
  this.walkthroughIndex = 0;
3322
3524
  this.input = null;
3525
+ onClosed?.();
3323
3526
  }
3324
3527
  async renderSingleStep(guide) {
3325
3528
  const renderer = createSingleStepRenderer(guide.guideType);
@@ -3332,7 +3535,8 @@ var GuidePreviewController = class {
3332
3535
  onInteraction: (event) => {
3333
3536
  if (event.action === "shown") shown = true;
3334
3537
  },
3335
- onClose: () => this.close(),
3538
+ onClose: () => this.close(true),
3539
+ badgeOpen: this.input?.badgeOpen === true,
3336
3540
  onTrack: () => {
3337
3541
  },
3338
3542
  // Encadenado en preview: no-op — el editor no puede montar otra guía
@@ -3398,7 +3602,7 @@ var GuidePreviewController = class {
3398
3602
  guide,
3399
3603
  onInteraction: () => {
3400
3604
  },
3401
- onClose: () => this.close(),
3605
+ onClose: () => this.close(true),
3402
3606
  nav: { stepIndex: index, totalSteps: guide.guideSteps.length, callbacks }
3403
3607
  });
3404
3608
  } catch {
@@ -3492,15 +3696,27 @@ function safeDestroy(renderer) {
3492
3696
  }
3493
3697
 
3494
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
+ }
3495
3709
  function matchesUrl(matcher, pageUrl) {
3710
+ const candidates = urlCandidates(pageUrl);
3496
3711
  switch (matcher.type) {
3497
3712
  case "exact":
3498
- return matcher.pattern === pageUrl;
3713
+ return candidates.some((c) => c === matcher.pattern);
3499
3714
  case "prefix":
3500
- return pageUrl.startsWith(matcher.pattern);
3715
+ return candidates.some((c) => c.startsWith(matcher.pattern));
3501
3716
  case "regex":
3502
3717
  try {
3503
- return new RegExp(matcher.pattern).test(pageUrl);
3718
+ const re = new RegExp(matcher.pattern);
3719
+ return candidates.some((c) => re.test(c));
3504
3720
  } catch {
3505
3721
  return false;
3506
3722
  }
@@ -3514,17 +3730,375 @@ function stepMatchesPage(loc, pageUrl) {
3514
3730
  // src/plugins/builder/bridge-protocol.ts
3515
3731
  var VEO_BUILDER_SOURCE = "veo-builder";
3516
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
+
3517
4079
  // src/plugins/builder/builder-panel.ts
3518
4080
  var DEFAULT_PANEL_PATH = "/guides/design";
3519
- var PANEL_W = 400;
3520
- var PANEL_H = 640;
4081
+ var PANEL_W = 484;
4082
+ var PANEL_H = 660;
3521
4083
  var MIN_W = 320;
3522
4084
  var MIN_H = 320;
3523
4085
  var MARGIN = 16;
4086
+ var HEADER_H = 52;
3524
4087
  function injectBuilderPanel(opts) {
3525
4088
  if (!hasWindow() || !hasDocument()) {
3526
- return { target: () => null, frameRect: () => null, teardown: () => {
3527
- } };
4089
+ return {
4090
+ target: () => null,
4091
+ frameRect: () => null,
4092
+ teardown: () => {
4093
+ },
4094
+ setTitle: () => {
4095
+ },
4096
+ setVisible: () => {
4097
+ },
4098
+ isVisible: () => false,
4099
+ setTopOffset: () => {
4100
+ }
4101
+ };
3528
4102
  }
3529
4103
  const panelUrl = buildPanelUrl(opts);
3530
4104
  const host = document.createElement("div");
@@ -3534,43 +4108,68 @@ function injectBuilderPanel(opts) {
3534
4108
  style.textContent = PANEL_CSS;
3535
4109
  const card = document.createElement("div");
3536
4110
  card.className = "veo-panel";
3537
- let left = Math.max(MARGIN, window.innerWidth - PANEL_W - MARGIN);
3538
- let top = MARGIN;
4111
+ let topOffset = opts.topOffset ?? 0;
4112
+ let left = 40;
4113
+ let top = topOffset + MARGIN;
3539
4114
  card.style.left = `${left}px`;
3540
4115
  card.style.top = `${top}px`;
3541
4116
  card.style.width = `${PANEL_W}px`;
3542
4117
  card.style.height = `${PANEL_H}px`;
3543
4118
  const header = document.createElement("div");
3544
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";
3545
4124
  const title = document.createElement("span");
3546
4125
  title.className = "veo-panel-title";
3547
4126
  title.textContent = "OnUser \xB7 Editor";
3548
4127
  const actions = document.createElement("div");
3549
4128
  actions.className = "veo-panel-actions";
3550
- const popout = document.createElement("button");
3551
- popout.className = "veo-panel-btn";
3552
- popout.type = "button";
3553
- popout.title = "Abrir en ventana aparte";
3554
- popout.setAttribute("aria-label", "Abrir en ventana aparte");
3555
- popout.textContent = "\u29C9";
3556
- const close = document.createElement("button");
3557
- close.className = "veo-panel-btn veo-panel-close";
3558
- close.type = "button";
3559
- close.title = "Cerrar";
3560
- close.setAttribute("aria-label", "Cerrar");
3561
- close.textContent = "\u2715";
3562
- actions.append(popout, close);
3563
- 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);
3564
4134
  const iframe = document.createElement("iframe");
3565
4135
  iframe.className = "veo-panel-frame";
3566
4136
  iframe.title = "Editor de Veo";
3567
4137
  iframe.src = panelUrl;
3568
- const grip = document.createElement("div");
3569
- grip.className = "veo-panel-resize";
3570
- grip.setAttribute("aria-hidden", "true");
3571
- 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);
3572
4142
  shadow.append(style, card);
3573
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
+ });
3574
4173
  let popped = null;
3575
4174
  let pollTimer;
3576
4175
  const stopPoll = () => {
@@ -3579,7 +4178,6 @@ function injectBuilderPanel(opts) {
3579
4178
  pollTimer = void 0;
3580
4179
  }
3581
4180
  };
3582
- close.addEventListener("click", () => opts.onClose());
3583
4181
  popout.addEventListener("click", () => {
3584
4182
  const win = window.open(
3585
4183
  panelUrl,
@@ -3597,7 +4195,7 @@ function injectBuilderPanel(opts) {
3597
4195
  }, 600);
3598
4196
  });
3599
4197
  header.addEventListener("mousedown", (e) => {
3600
- if (e.target?.classList.contains("veo-panel-btn")) return;
4198
+ if (e.target?.closest(".veo-panel-btn")) return;
3601
4199
  e.preventDefault();
3602
4200
  const sx = e.clientX;
3603
4201
  const sy = e.clientY;
@@ -3606,7 +4204,7 @@ function injectBuilderPanel(opts) {
3606
4204
  iframe.style.pointerEvents = "none";
3607
4205
  const move = (ev) => {
3608
4206
  left = clamp3(ol + (ev.clientX - sx), 0, Math.max(0, window.innerWidth - card.offsetWidth));
3609
- 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));
3610
4208
  card.style.left = `${left}px`;
3611
4209
  card.style.top = `${top}px`;
3612
4210
  };
@@ -3618,7 +4216,7 @@ function injectBuilderPanel(opts) {
3618
4216
  window.addEventListener("mousemove", move);
3619
4217
  window.addEventListener("mouseup", up);
3620
4218
  });
3621
- grip.addEventListener("mousedown", (e) => {
4219
+ resizeGrip.addEventListener("mousedown", (e) => {
3622
4220
  e.preventDefault();
3623
4221
  e.stopPropagation();
3624
4222
  const sx = e.clientX;
@@ -3645,9 +4243,33 @@ function injectBuilderPanel(opts) {
3645
4243
  stopPoll();
3646
4244
  if (popped && !popped.closed) popped.close();
3647
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
+ }
3648
4258
  }
3649
4259
  };
3650
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
+ }
3651
4273
  function clamp3(v, min, max) {
3652
4274
  return Math.min(Math.max(v, min), max);
3653
4275
  }
@@ -3667,28 +4289,33 @@ var PANEL_CSS = `
3667
4289
  max-width: 96vw; max-height: 94vh;
3668
4290
  display: flex; flex-direction: column;
3669
4291
  background: #fff;
3670
- border-radius: 12px;
3671
- 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);
3672
4294
  overflow: hidden;
3673
4295
  z-index: 2147483647;
3674
4296
  font-family: system-ui, -apple-system, sans-serif;
3675
4297
  }
3676
4298
  .veo-panel-header {
3677
- flex: 0 0 auto;
4299
+ position: relative; flex: 0 0 auto; height: ${HEADER_H}px; box-sizing: border-box;
3678
4300
  display: flex; align-items: center; justify-content: space-between;
3679
- padding: 8px 10px 8px 12px;
3680
- background: #1b1714; color: #f7f1eb;
3681
- 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;
3682
4304
  cursor: grab;
3683
4305
  }
3684
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; }
3685
4312
  .veo-panel-actions { display: flex; gap: 2px; }
3686
4313
  .veo-panel-btn {
3687
- border: 0; background: transparent; color: #fff;
3688
- font-size: 14px; cursor: pointer; line-height: 1;
3689
- 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;
3690
4317
  }
3691
- .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; }
3692
4319
  .veo-panel-frame { flex: 1 1 auto; width: 100%; border: 0; background: #fff; }
3693
4320
  .veo-panel-resize {
3694
4321
  position: absolute; right: 0; bottom: 0;
@@ -4028,9 +4655,9 @@ var PICKER_STYLES = `
4028
4655
  z-index: ${PICKER_Z};
4029
4656
  pointer-events: none;
4030
4657
  box-sizing: border-box;
4031
- background: rgba(99, 102, 241, 0.22);
4032
- border: 1px dashed rgba(99, 102, 241, 0.95);
4033
- border-radius: 2px;
4658
+ background: transparent;
4659
+ border: 3px dashed #b74d00;
4660
+ border-radius: 8px;
4034
4661
  transition: top 60ms ease-out, left 60ms ease-out, width 60ms ease-out, height 60ms ease-out;
4035
4662
  }
4036
4663
  .veo-pick-label {
@@ -4038,30 +4665,48 @@ var PICKER_STYLES = `
4038
4665
  z-index: ${PICKER_Z};
4039
4666
  pointer-events: none;
4040
4667
  max-width: 360px;
4041
- background: #1e1b4b;
4042
- color: #e0e7ff;
4668
+ background: #2b2623;
4669
+ color: #f5efe9;
4043
4670
  font: 12px/1.45 ui-monospace, SFMono-Regular, Menlo, monospace;
4044
4671
  padding: 6px 9px;
4045
4672
  border-radius: 6px;
4046
4673
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
4047
4674
  }
4048
- .veo-pick-label .veo-pick-sel { color: #c7d2fe; word-break: break-all; }
4049
- .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; }
4050
4677
  .veo-pick-hint {
4051
4678
  position: fixed;
4052
- top: 12px;
4679
+ top: 28px;
4053
4680
  left: 50%;
4054
4681
  transform: translateX(-50%);
4055
4682
  z-index: ${PICKER_Z};
4056
4683
  pointer-events: none;
4057
- background: #4338ca;
4684
+ display: flex; align-items: center; gap: 16px;
4685
+ max-width: min(760px, calc(100vw - 32px));
4686
+ background: #2b2623;
4058
4687
  color: #fff;
4059
- font: 13px/1 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
4060
- padding: 8px 14px;
4061
- border-radius: 999px;
4062
- 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;
4063
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;
4706
+ }
4707
+ .veo-pick-cancel:hover { background: rgba(255,255,255,0.24); }
4064
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>';
4065
4710
  var ElementPicker = class {
4066
4711
  constructor() {
4067
4712
  this.host = null;
@@ -4078,6 +4723,7 @@ var ElementPicker = class {
4078
4723
  this.draw(target);
4079
4724
  };
4080
4725
  this.onClick = (event) => {
4726
+ if (event.altKey) return;
4081
4727
  const target = this.current ?? (event.target instanceof Element ? event.target : null);
4082
4728
  if (!target || this.isOwn(target)) return;
4083
4729
  event.preventDefault();
@@ -4098,7 +4744,7 @@ var ElementPicker = class {
4098
4744
  };
4099
4745
  /** Traga mousedown/mouseup durante el picking (evita focus/active de la app). */
4100
4746
  this.swallow = (event) => {
4101
- if (this.isOwn(event.target)) return;
4747
+ if (event.altKey || this.isOwn(event.target)) return;
4102
4748
  event.preventDefault();
4103
4749
  event.stopPropagation();
4104
4750
  };
@@ -4124,7 +4770,29 @@ var ElementPicker = class {
4124
4770
  this.label.style.display = "none";
4125
4771
  const hint = document.createElement("div");
4126
4772
  hint.className = "veo-pick-hint";
4127
- 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);
4128
4796
  shadow.append(style, this.box, this.label, hint);
4129
4797
  document.body.appendChild(host);
4130
4798
  this.host = host;
@@ -4222,6 +4890,7 @@ function initBuilderMode() {
4222
4890
  if (!dashboardOrigin) return null;
4223
4891
  let picker = null;
4224
4892
  let panel = null;
4893
+ let chrome = null;
4225
4894
  let staticTarget = null;
4226
4895
  let manipulator = null;
4227
4896
  let dropTarget = null;
@@ -4229,26 +4898,89 @@ function initBuilderMode() {
4229
4898
  let previewStepIndex = 0;
4230
4899
  let pendingPreview = null;
4231
4900
  let lastPreview = null;
4901
+ let previewMode = false;
4902
+ let previewTrigger = null;
4903
+ let previewFired = false;
4904
+ let previewDelay = null;
4905
+ let panelWasVisible = true;
4232
4906
  const getTarget = () => panel ? panel.target() : staticTarget;
4233
4907
  const post = (event) => {
4234
4908
  getTarget()?.postMessage({ source: VEO_BUILDER_SOURCE, token, ...event }, dashboardOrigin);
4235
4909
  };
4236
- const previewMatchesPage = (cmd) => {
4910
+ const previewStep = (cmd) => {
4237
4911
  const steps = cmd.guide.guideSteps;
4238
- const step = steps?.[cmd.guide.startStepIndex ?? 0];
4239
- 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();
4240
4922
  };
4241
4923
  const applyPreviewGated = (cmd) => {
4242
4924
  lastPreview = cmd;
4243
- if (previewMatchesPage(cmd)) {
4244
- applyPreview(cmd);
4245
- } else {
4246
- manipulator?.detach();
4247
- manipulator = null;
4248
- dropTarget?.detach();
4249
- dropTarget = null;
4250
- closeGuidePreview();
4925
+ if (!previewMatchesPage(cmd)) {
4926
+ cancelPreviewDelay();
4927
+ withholdPreview();
4928
+ postPreviewStatus({
4929
+ state: "page-mismatch",
4930
+ pattern: previewStep(cmd)?.pageLocation?.pattern ?? ""
4931
+ });
4932
+ return;
4251
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;
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 });
4252
4984
  };
4253
4985
  const applyPreview = (cmd) => {
4254
4986
  manipulator?.detach();
@@ -4256,8 +4988,38 @@ function initBuilderMode() {
4256
4988
  dropTarget?.detach();
4257
4989
  dropTarget = null;
4258
4990
  previewStepIndex = cmd.guide.startStepIndex ?? 0;
4259
- const handle = previewGuide(cmd.guide);
4260
- 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;
4261
5023
  void handle.ready.then((result) => {
4262
5024
  if (!result.rendered) return;
4263
5025
  const host = handle.host();
@@ -4325,26 +5087,57 @@ function initBuilderMode() {
4325
5087
  const handleCommand = (cmd) => {
4326
5088
  switch (cmd.type) {
4327
5089
  case "panel-ready":
4328
- 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);
4329
5100
  break;
4330
- case "start-picker":
5101
+ case "start-picker": {
4331
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
+ };
4332
5121
  picker = startElementPicker({
4333
5122
  onPick: (payload) => {
4334
5123
  picker = null;
5124
+ restorePanel();
4335
5125
  post({ type: "picked", payload });
4336
5126
  },
4337
5127
  onCancel: () => {
4338
5128
  picker = null;
5129
+ restorePanel();
4339
5130
  post({ type: "pick-cancelled" });
4340
5131
  },
4341
5132
  ...cmd.maxAncestors !== void 0 ? { maxAncestors: cmd.maxAncestors } : {},
4342
5133
  ...cmd.hint !== void 0 ? { hint: cmd.hint } : {}
4343
5134
  });
4344
5135
  break;
5136
+ }
4345
5137
  case "stop-picker":
4346
5138
  picker?.stop();
4347
5139
  picker = null;
5140
+ chrome?.setHidden(false);
4348
5141
  break;
4349
5142
  case "preview":
4350
5143
  if (!cmd.guide) break;
@@ -4362,6 +5155,9 @@ function initBuilderMode() {
4362
5155
  hideDragGhost();
4363
5156
  pendingPreview = null;
4364
5157
  lastPreview = null;
5158
+ disarmPreviewTrigger();
5159
+ cancelPreviewDelay();
5160
+ previewFired = false;
4365
5161
  closeGuidePreview();
4366
5162
  break;
4367
5163
  case "teardown":
@@ -4413,21 +5209,62 @@ function initBuilderMode() {
4413
5209
  dropTarget = null;
4414
5210
  hideDragGhost();
4415
5211
  pendingPreview = null;
5212
+ disarmPreviewTrigger();
5213
+ cancelPreviewDelay();
4416
5214
  closeGuidePreview();
4417
5215
  post({ type: "closed" });
4418
5216
  panel?.teardown();
4419
5217
  panel = null;
5218
+ chrome?.teardown();
5219
+ chrome = null;
4420
5220
  clearBuilderToken();
4421
5221
  };
4422
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
+ });
4423
5258
  panel = injectBuilderPanel({
4424
5259
  dashboardOrigin,
4425
5260
  token,
4426
5261
  appOrigin: window.location.origin,
4427
5262
  guide: ctx.guide,
4428
5263
  panelPath: ctx.panelPath,
4429
- onClose: () => teardown()
5264
+ onClose: () => teardown(),
5265
+ topOffset: chrome.height()
4430
5266
  });
5267
+ chrome.onHeightChange((h) => panel?.setTopOffset(chrome?.dock() === "top" ? h : 0));
4431
5268
  } else {
4432
5269
  staticTarget = resolveTarget();
4433
5270
  if (!staticTarget) return null;