veo-sdk 0.5.32 → 0.5.34

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,27 @@ 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
+
294
+ // src/plugins/guides/activation-rules.ts
295
+ function isAutomaticActivation(rules) {
296
+ const trigger = rules?.trigger ?? "immediate";
297
+ if (trigger === "immediate" || trigger === "delayed") return true;
298
+ if (trigger === "on_click" || trigger === "on_hover") return rules?.autoShow === true;
299
+ return false;
300
+ }
301
+ function activationDelayMs(rules) {
302
+ if (!isAutomaticActivation(rules)) return 0;
303
+ const ms = rules?.delayMs ?? 0;
304
+ return Number.isFinite(ms) && ms > 0 ? ms : 0;
305
+ }
306
+
286
307
  // src/plugins/guides/constants.ts
287
308
  var GUIDE_Z_INDEX = 2147483640;
288
309
  var DEFAULT_ANCHOR_WAIT_MS = 5e3;
@@ -357,22 +378,22 @@ function rebuildNode(node, doc, depth, budget) {
357
378
  return doc.createTextNode(node.textContent ?? "");
358
379
  }
359
380
  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];
381
+ const el2 = node;
382
+ if (DROP_TAGS.has(el2.tagName)) return null;
383
+ const mapped = ALLOWED_TAGS[el2.tagName];
363
384
  if (!mapped) {
364
385
  const frag = doc.createDocumentFragment();
365
- for (const child of Array.from(el.childNodes)) {
386
+ for (const child of Array.from(el2.childNodes)) {
366
387
  const rebuilt2 = rebuildNode(child, doc, depth + 1, budget);
367
388
  if (rebuilt2) frag.appendChild(rebuilt2);
368
389
  }
369
390
  return frag;
370
391
  }
371
392
  if (mapped === "a") {
372
- const href = el.getAttribute("href") ?? "";
393
+ const href = el2.getAttribute("href") ?? "";
373
394
  if (!isSafeUrl(href)) {
374
395
  const frag = doc.createDocumentFragment();
375
- for (const child of Array.from(el.childNodes)) {
396
+ for (const child of Array.from(el2.childNodes)) {
376
397
  const rebuilt2 = rebuildNode(child, doc, depth + 1, budget);
377
398
  if (rebuilt2) frag.appendChild(rebuilt2);
378
399
  }
@@ -382,14 +403,14 @@ function rebuildNode(node, doc, depth, budget) {
382
403
  a.setAttribute("href", href);
383
404
  a.setAttribute("target", "_blank");
384
405
  a.setAttribute("rel", "noopener noreferrer nofollow");
385
- for (const child of Array.from(el.childNodes)) {
406
+ for (const child of Array.from(el2.childNodes)) {
386
407
  const rebuilt2 = rebuildNode(child, doc, depth + 1, budget);
387
408
  if (rebuilt2) a.appendChild(rebuilt2);
388
409
  }
389
410
  return a;
390
411
  }
391
412
  const rebuilt = doc.createElement(mapped);
392
- for (const child of Array.from(el.childNodes)) {
413
+ for (const child of Array.from(el2.childNodes)) {
393
414
  const childNode = rebuildNode(child, doc, depth + 1, budget);
394
415
  if (childNode) rebuilt.appendChild(childNode);
395
416
  }
@@ -523,31 +544,31 @@ function renderBlocks(container, blocks, doc, callbacks) {
523
544
  function buildContentBlock(block, doc) {
524
545
  switch (block.type) {
525
546
  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;
547
+ const el2 = doc.createElement("h2");
548
+ el2.className = "veo-guide-title";
549
+ el2.textContent = typeof block.text === "string" ? block.text : "";
550
+ applyBlockStyle(el2, block.style, "text");
551
+ return el2;
531
552
  }
532
553
  case "text": {
533
- const el = doc.createElement("div");
534
- el.className = "veo-guide-text";
554
+ const el2 = doc.createElement("div");
555
+ el2.className = "veo-guide-text";
535
556
  if (typeof block.html === "string" && block.html) {
536
- el.appendChild(renderRichText(block.html, doc));
557
+ el2.appendChild(renderRichText(block.html, doc));
537
558
  } else {
538
- el.textContent = typeof block.text === "string" ? block.text : "";
559
+ el2.textContent = typeof block.text === "string" ? block.text : "";
539
560
  }
540
- applyBlockStyle(el, block.style, "text");
541
- return el;
561
+ applyBlockStyle(el2, block.style, "text");
562
+ return el2;
542
563
  }
543
564
  case "image": {
544
565
  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;
566
+ const el2 = doc.createElement("img");
567
+ el2.className = "veo-guide-image";
568
+ el2.src = block.url;
569
+ el2.alt = "";
570
+ applyBlockStyle(el2, block.style, "image");
571
+ return el2;
551
572
  }
552
573
  default:
553
574
  return null;
@@ -579,74 +600,74 @@ var BLOCK_ALIGN_SELF = {
579
600
  function clampNum(n, min, max) {
580
601
  return Math.min(max, Math.max(min, n));
581
602
  }
582
- function applyBlockStyle(el, style, kind) {
603
+ function applyBlockStyle(el2, style, kind) {
583
604
  if (!style || typeof style !== "object") return;
584
605
  const s = style;
585
606
  if (kind === "image" && s.bleed === true) {
586
- el.classList.add("veo-guide-image--bleed");
607
+ el2.classList.add("veo-guide-image--bleed");
587
608
  return;
588
609
  }
589
610
  const align = typeof s.align === "string" ? s.align : null;
590
611
  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];
612
+ if (kind === "text") el2.style.textAlign = align;
613
+ else if (kind === "image") el2.style.alignSelf = BLOCK_ALIGN_SELF[align];
593
614
  }
594
- if (kind !== "image" && typeof s.color === "string") el.style.color = s.color;
615
+ if (kind !== "image" && typeof s.color === "string") el2.style.color = s.color;
595
616
  if (kind === "text" && typeof s.fontSize === "number" && Number.isFinite(s.fontSize)) {
596
- el.style.fontSize = `${clampNum(s.fontSize, 8, 72)}px`;
617
+ el2.style.fontSize = `${clampNum(s.fontSize, 8, 72)}px`;
597
618
  }
598
619
  if (kind === "image" && typeof s.width === "number" && Number.isFinite(s.width)) {
599
- el.style.width = `${clampNum(s.width, 10, 100)}%`;
620
+ el2.style.width = `${clampNum(s.width, 10, 100)}%`;
600
621
  }
601
622
  if (kind === "image" && typeof s.radius === "number" && Number.isFinite(s.radius)) {
602
- el.style.borderRadius = `${clampNum(s.radius, 0, 48)}px`;
623
+ el2.style.borderRadius = `${clampNum(s.radius, 0, 48)}px`;
603
624
  }
604
625
  if (typeof s.marginTop === "number" && Number.isFinite(s.marginTop)) {
605
- el.style.marginTop = `${clampNum(s.marginTop, 0, 64)}px`;
626
+ el2.style.marginTop = `${clampNum(s.marginTop, 0, 64)}px`;
606
627
  }
607
628
  if (typeof s.marginBottom === "number" && Number.isFinite(s.marginBottom)) {
608
- el.style.marginBottom = `${clampNum(s.marginBottom, 0, 64)}px`;
629
+ el2.style.marginBottom = `${clampNum(s.marginBottom, 0, 64)}px`;
609
630
  }
610
631
  if (kind !== "image") {
611
632
  if (typeof s.fontSize === "number" && Number.isFinite(s.fontSize)) {
612
- el.style.fontSize = `${clampNum(s.fontSize, 8, 96)}px`;
633
+ el2.style.fontSize = `${clampNum(s.fontSize, 8, 96)}px`;
613
634
  }
614
635
  if (typeof s.fontFamily === "string" && s.fontFamily && s.fontFamily !== "inherit") {
615
- el.style.fontFamily = s.fontFamily;
636
+ el2.style.fontFamily = s.fontFamily;
616
637
  }
617
- if (typeof s.fontWeight === "string" && s.fontWeight) el.style.fontWeight = s.fontWeight;
638
+ if (typeof s.fontWeight === "string" && s.fontWeight) el2.style.fontWeight = s.fontWeight;
618
639
  if (typeof s.textTransform === "string" && s.textTransform && s.textTransform !== "none") {
619
- el.style.textTransform = s.textTransform;
640
+ el2.style.textTransform = s.textTransform;
620
641
  }
621
642
  if (typeof s.lineHeight === "number" && Number.isFinite(s.lineHeight)) {
622
- el.style.lineHeight = `${clampNum(s.lineHeight, 50, 300)}%`;
643
+ el2.style.lineHeight = `${clampNum(s.lineHeight, 50, 300)}%`;
623
644
  }
624
645
  if (typeof s.letterSpacing === "number" && Number.isFinite(s.letterSpacing)) {
625
- el.style.letterSpacing = `${clampNum(s.letterSpacing, -5, 20)}px`;
646
+ el2.style.letterSpacing = `${clampNum(s.letterSpacing, -5, 20)}px`;
626
647
  }
627
648
  }
628
- if (typeof s.background === "string" && s.background) el.style.background = s.background;
649
+ if (typeof s.background === "string" && s.background) el2.style.background = s.background;
629
650
  if (typeof s.borderWidth === "number" && s.borderWidth > 0) {
630
651
  const bc = typeof s.borderColor === "string" && s.borderColor ? s.borderColor : "#bbbbbb";
631
- el.style.border = `${clampNum(s.borderWidth, 0, 20)}px solid ${bc}`;
652
+ el2.style.border = `${clampNum(s.borderWidth, 0, 20)}px solid ${bc}`;
632
653
  }
633
654
  if (kind !== "image" && typeof s.radius === "number" && Number.isFinite(s.radius)) {
634
- el.style.borderRadius = `${clampNum(s.radius, 0, 64)}px`;
655
+ el2.style.borderRadius = `${clampNum(s.radius, 0, 64)}px`;
635
656
  }
636
657
  if (typeof s.height === "number" && Number.isFinite(s.height) && s.height > 0) {
637
- el.style.height = `${clampNum(s.height, 1, 2e3)}px`;
658
+ el2.style.height = `${clampNum(s.height, 1, 2e3)}px`;
638
659
  }
639
660
  if (kind !== "image" && typeof s.width === "number" && Number.isFinite(s.width) && s.width > 0) {
640
- el.style.width = `${clampNum(s.width, 1, 2e3)}px`;
661
+ el2.style.width = `${clampNum(s.width, 1, 2e3)}px`;
641
662
  }
642
663
  for (const side of ["Top", "Right", "Bottom", "Left"]) {
643
664
  const p = s[`padding${side}`];
644
665
  if (typeof p === "number" && Number.isFinite(p)) {
645
- el.style.setProperty(`padding-${side.toLowerCase()}`, `${clampNum(p, 0, 128)}px`);
666
+ el2.style.setProperty(`padding-${side.toLowerCase()}`, `${clampNum(p, 0, 128)}px`);
646
667
  }
647
668
  const m = s[`margin${side}`];
648
669
  if (typeof m === "number" && Number.isFinite(m)) {
649
- el.style.setProperty(`margin-${side.toLowerCase()}`, `${clampNum(m, 0, 128)}px`);
670
+ el2.style.setProperty(`margin-${side.toLowerCase()}`, `${clampNum(m, 0, 128)}px`);
650
671
  }
651
672
  }
652
673
  }
@@ -781,11 +802,11 @@ function applyDesignVars(host, style) {
781
802
  host.style.setProperty("--veo-pos-y", `${py * 100}%`);
782
803
  }
783
804
  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);
805
+ const el2 = s.elements;
806
+ applyElementVars(host, "title", el2.title);
807
+ applyElementVars(host, "text", el2.text);
808
+ applyElementVars(host, "image", el2.image);
809
+ applyCtaVars(host, el2.cta);
789
810
  }
790
811
  }
791
812
  function applyCtaVars(host, raw) {
@@ -809,29 +830,29 @@ function applyCtaVars(host, raw) {
809
830
  }
810
831
  function applyElementVars(host, prefix, raw) {
811
832
  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")) {
833
+ const el2 = raw;
834
+ if (typeof el2.align === "string" && (el2.align === "left" || el2.align === "center" || el2.align === "right")) {
814
835
  const mode = ELEMENT_ALIGN_MODE[prefix] ?? "text";
815
- const value = mode === "self" ? ALIGN_SELF[el.align] : el.align;
836
+ const value = mode === "self" ? ALIGN_SELF[el2.align] : el2.align;
816
837
  host.style.setProperty(`--veo-${prefix}-align`, value);
817
838
  }
818
- if (typeof el.color === "string" && COLOR_RE.test(el.color.trim())) {
819
- host.style.setProperty(`--veo-${prefix}-color`, el.color.trim());
839
+ if (typeof el2.color === "string" && COLOR_RE.test(el2.color.trim())) {
840
+ host.style.setProperty(`--veo-${prefix}-color`, el2.color.trim());
820
841
  }
821
- if (typeof el.fontSize === "number" && Number.isFinite(el.fontSize)) {
822
- host.style.setProperty(`--veo-${prefix}-size`, `${clamp(el.fontSize, 8, 72)}px`);
842
+ if (typeof el2.fontSize === "number" && Number.isFinite(el2.fontSize)) {
843
+ host.style.setProperty(`--veo-${prefix}-size`, `${clamp(el2.fontSize, 8, 72)}px`);
823
844
  }
824
- if (typeof el.width === "number" && Number.isFinite(el.width)) {
825
- host.style.setProperty(`--veo-${prefix}-width`, `${clamp(el.width, 10, 100)}%`);
845
+ if (typeof el2.width === "number" && Number.isFinite(el2.width)) {
846
+ host.style.setProperty(`--veo-${prefix}-width`, `${clamp(el2.width, 10, 100)}%`);
826
847
  }
827
- if (typeof el.radius === "number" && Number.isFinite(el.radius)) {
828
- host.style.setProperty(`--veo-${prefix}-radius`, `${clamp(el.radius, 0, 48)}px`);
848
+ if (typeof el2.radius === "number" && Number.isFinite(el2.radius)) {
849
+ host.style.setProperty(`--veo-${prefix}-radius`, `${clamp(el2.radius, 0, 48)}px`);
829
850
  }
830
- if (typeof el.marginTop === "number" && Number.isFinite(el.marginTop)) {
831
- host.style.setProperty(`--veo-${prefix}-mt`, `${clamp(el.marginTop, 0, 64)}px`);
851
+ if (typeof el2.marginTop === "number" && Number.isFinite(el2.marginTop)) {
852
+ host.style.setProperty(`--veo-${prefix}-mt`, `${clamp(el2.marginTop, 0, 64)}px`);
832
853
  }
833
- if (typeof el.marginBottom === "number" && Number.isFinite(el.marginBottom)) {
834
- host.style.setProperty(`--veo-${prefix}-mb`, `${clamp(el.marginBottom, 0, 64)}px`);
854
+ if (typeof el2.marginBottom === "number" && Number.isFinite(el2.marginBottom)) {
855
+ host.style.setProperty(`--veo-${prefix}-mb`, `${clamp(el2.marginBottom, 0, 64)}px`);
835
856
  }
836
857
  }
837
858
 
@@ -881,16 +902,16 @@ function waitForElement(selector, timeoutMs = DEFAULT_ANCHOR_WAIT_MS) {
881
902
  return;
882
903
  }
883
904
  let resolved = false;
884
- const finish = (el) => {
905
+ const finish = (el2) => {
885
906
  if (resolved) return;
886
907
  resolved = true;
887
908
  observer.disconnect();
888
909
  clearTimeout(timer);
889
- resolve(el);
910
+ resolve(el2);
890
911
  };
891
912
  const observer = new MutationObserver(() => {
892
- const el = safeQuery();
893
- if (el) finish(el);
913
+ const el2 = safeQuery();
914
+ if (el2) finish(el2);
894
915
  });
895
916
  observer.observe(document.body, { childList: true, subtree: true });
896
917
  const timer = setTimeout(() => finish(null), timeoutMs);
@@ -1950,10 +1971,10 @@ function collectHostStyles() {
1950
1971
  function hostHtmlAttrs() {
1951
1972
  if (typeof document === "undefined") return "";
1952
1973
  try {
1953
- const el = document.documentElement;
1974
+ const el2 = document.documentElement;
1954
1975
  const attrs = [];
1955
1976
  for (const name of ["class", "style", "data-theme", "data-mode", "data-color-mode"]) {
1956
- const v = el.getAttribute(name);
1977
+ const v = el2.getAttribute(name);
1957
1978
  if (v && v.length <= 1e3 && !/["<>]/.test(v)) attrs.push(`${name}="${v}"`);
1958
1979
  }
1959
1980
  return attrs.join(" ");
@@ -2336,6 +2357,188 @@ function buildScale(wrapper, doc, min, max, btnClass, symbol) {
2336
2357
  wrapper.appendChild(row);
2337
2358
  return { wrapper, read: () => selected };
2338
2359
  }
2360
+ 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";
2361
+ 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";
2362
+ 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";
2363
+ var CIRCLE = "M21 12a9 9 0 11-18 0 9 9 0 0118 0z";
2364
+ 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";
2365
+ var BADGE_ICONS = {
2366
+ info: { paths: [CIRCLE, INFO_GLYPH], dots: [[12, 8.25]] },
2367
+ exclamation: { paths: [CIRCLE, "M12 8v4.5"], dots: [[12, 15.75]] },
2368
+ question: { paths: [CIRCLE, QUESTION_GLYPH], dots: [[12, 17.25]] },
2369
+ star: { paths: [STAR_GLYPH] },
2370
+ "info-solid": { paths: [INFO_GLYPH], dots: [[12, 8.25]], solid: true, scale: 0.8 },
2371
+ "exclamation-solid": { paths: ["M12 8v4.5"], dots: [[12, 15.75]], solid: true, scale: 0.8 },
2372
+ "question-solid": { paths: [QUESTION_GLYPH], dots: [[12, 17.25]], solid: true, scale: 0.8 },
2373
+ "star-solid": { paths: [STAR_GLYPH], solid: true, scale: 0.55 },
2374
+ "target-solid": { paths: [TARGET_GLYPH], solid: true, scale: 0.6 },
2375
+ new: { paths: [], text: "New" }
2376
+ };
2377
+ var DEFAULT_MARKER_SIZE = 14;
2378
+ var DEFAULT_MARKER_Z = 19e3;
2379
+ var SIDE_GAP = 8;
2380
+ function num(v, fallback) {
2381
+ return typeof v === "number" && Number.isFinite(v) ? v : fallback;
2382
+ }
2383
+ function iconNode(doc, name, size, color) {
2384
+ const def = BADGE_ICONS[name] ?? BADGE_ICONS.info;
2385
+ if (def.text) {
2386
+ const t = doc.createElement("span");
2387
+ t.textContent = def.text;
2388
+ t.style.font = `700 ${Math.round(size * 0.85)}px/1 system-ui, -apple-system, sans-serif`;
2389
+ t.style.color = color;
2390
+ return t;
2391
+ }
2392
+ const ns = "http://www.w3.org/2000/svg";
2393
+ const svg2 = doc.createElementNS(ns, "svg");
2394
+ svg2.setAttribute("viewBox", "0 0 24 24");
2395
+ svg2.setAttribute("width", String(size));
2396
+ svg2.setAttribute("height", String(size));
2397
+ svg2.setAttribute("fill", "none");
2398
+ svg2.setAttribute("aria-hidden", "true");
2399
+ const glyphColor = def.solid ? "#ffffff" : color;
2400
+ if (def.solid) {
2401
+ const bg = doc.createElementNS(ns, "circle");
2402
+ bg.setAttribute("cx", "12");
2403
+ bg.setAttribute("cy", "12");
2404
+ bg.setAttribute("r", "12");
2405
+ bg.setAttribute("fill", color);
2406
+ svg2.appendChild(bg);
2407
+ }
2408
+ const g = doc.createElementNS(ns, "g");
2409
+ g.setAttribute("stroke", glyphColor);
2410
+ g.setAttribute("stroke-width", def.solid ? "2" : "1.8");
2411
+ g.setAttribute("stroke-linecap", "round");
2412
+ g.setAttribute("stroke-linejoin", "round");
2413
+ if (def.scale && def.scale !== 1) {
2414
+ const k = def.scale;
2415
+ const t = 12 - 12 * k;
2416
+ g.setAttribute("transform", `translate(${t} ${t}) scale(${k})`);
2417
+ }
2418
+ for (const d of def.paths) {
2419
+ const path = doc.createElementNS(ns, "path");
2420
+ path.setAttribute("d", d);
2421
+ g.appendChild(path);
2422
+ }
2423
+ for (const [cx, cy] of def.dots ?? []) {
2424
+ const dot = doc.createElementNS(ns, "circle");
2425
+ dot.setAttribute("cx", String(cx));
2426
+ dot.setAttribute("cy", String(cy));
2427
+ dot.setAttribute("r", "0.9");
2428
+ dot.setAttribute("fill", glyphColor);
2429
+ dot.setAttribute("stroke", "none");
2430
+ g.appendChild(dot);
2431
+ }
2432
+ svg2.appendChild(g);
2433
+ return svg2;
2434
+ }
2435
+ function buildMarkerElement(doc, marker) {
2436
+ const size = num(marker.size, DEFAULT_MARKER_SIZE);
2437
+ const color = marker.color || "#000000";
2438
+ const el2 = doc.createElement("span");
2439
+ el2.className = `veo-badge-marker veo-badge-marker-${marker.kind}`;
2440
+ el2.style.display = "inline-flex";
2441
+ el2.style.alignItems = "center";
2442
+ el2.style.justifyContent = "center";
2443
+ el2.style.boxSizing = "border-box";
2444
+ el2.style.lineHeight = "1";
2445
+ if (marker.kind === "image") {
2446
+ const img = doc.createElement("img");
2447
+ img.alt = "";
2448
+ img.src = marker.imageUrl || "";
2449
+ img.style.width = `${size}px`;
2450
+ img.style.height = `${size}px`;
2451
+ img.style.objectFit = "contain";
2452
+ img.style.display = "block";
2453
+ el2.appendChild(img);
2454
+ return el2;
2455
+ }
2456
+ if (marker.kind === "icon-text") {
2457
+ el2.style.gap = "6px";
2458
+ el2.style.color = color;
2459
+ el2.style.fontSize = `${size}px`;
2460
+ el2.style.fontFamily = "inherit";
2461
+ el2.style.fontWeight = "600";
2462
+ el2.style.whiteSpace = "nowrap";
2463
+ el2.style.background = marker.bgColor || "transparent";
2464
+ const bw = num(marker.borderWidth, 0);
2465
+ if (bw > 0) el2.style.border = `${bw}px solid ${marker.borderColor || color}`;
2466
+ el2.style.padding = `${num(marker.paddingY, 6)}px ${num(marker.paddingX, 10)}px`;
2467
+ el2.style.borderRadius = `${num(marker.radius, 999)}px`;
2468
+ if (marker.showIcon !== false)
2469
+ el2.appendChild(iconNode(doc, marker.icon || "star", size, color));
2470
+ const text = doc.createElement("span");
2471
+ text.textContent = marker.text || "";
2472
+ el2.appendChild(text);
2473
+ return el2;
2474
+ }
2475
+ el2.style.width = `${size}px`;
2476
+ el2.style.height = `${size}px`;
2477
+ el2.appendChild(iconNode(doc, marker.icon || "info", size, color));
2478
+ return el2;
2479
+ }
2480
+ function markerOffset(anchor, marker, position, offset6) {
2481
+ const right = anchor.left + anchor.width;
2482
+ const bottom = anchor.top + anchor.height;
2483
+ const midY = anchor.top + anchor.height / 2 - marker.height / 2;
2484
+ const midX = anchor.left + anchor.width / 2 - marker.width / 2;
2485
+ let x;
2486
+ let y;
2487
+ switch (position) {
2488
+ case "top-left":
2489
+ x = anchor.left - marker.width / 2;
2490
+ y = anchor.top - marker.height / 2;
2491
+ break;
2492
+ case "bottom-left":
2493
+ x = anchor.left - marker.width / 2;
2494
+ y = bottom - marker.height / 2;
2495
+ break;
2496
+ case "bottom-right":
2497
+ x = right - marker.width / 2;
2498
+ y = bottom - marker.height / 2;
2499
+ break;
2500
+ case "left":
2501
+ x = anchor.left - marker.width - SIDE_GAP;
2502
+ y = midY;
2503
+ break;
2504
+ case "right":
2505
+ x = right + SIDE_GAP;
2506
+ y = midY;
2507
+ break;
2508
+ case "center":
2509
+ x = midX;
2510
+ y = midY;
2511
+ break;
2512
+ default:
2513
+ x = right - marker.width / 2;
2514
+ y = anchor.top - marker.height / 2;
2515
+ }
2516
+ return { x: x + offset6.x, y: y + offset6.y };
2517
+ }
2518
+ function attachMarker(anchor, host, marker) {
2519
+ host.style.position = "absolute";
2520
+ host.style.zIndex = String(num(marker.zIndex, DEFAULT_MARKER_Z));
2521
+ host.style.margin = "0";
2522
+ const update = () => {
2523
+ const a = anchor.getBoundingClientRect();
2524
+ const m = host.getBoundingClientRect();
2525
+ const { x, y } = markerOffset(
2526
+ {
2527
+ left: a.left + window.scrollX,
2528
+ top: a.top + window.scrollY,
2529
+ width: a.width,
2530
+ height: a.height
2531
+ },
2532
+ { width: m.width, height: m.height },
2533
+ marker.position ?? "top-right",
2534
+ { x: num(marker.offsetX, 0), y: num(marker.offsetY, 0) }
2535
+ );
2536
+ host.style.left = `${Math.round(x)}px`;
2537
+ host.style.top = `${Math.round(y)}px`;
2538
+ };
2539
+ update();
2540
+ return dom.autoUpdate(anchor, host, update);
2541
+ }
2339
2542
 
2340
2543
  // src/plugins/guides/renderers/composite-renderer.ts
2341
2544
  var ROW_ALIGN = {
@@ -2631,7 +2834,9 @@ var CompositeRenderer = class extends BaseRenderer {
2631
2834
  badge.style.cursor = "pointer";
2632
2835
  badge.style.display = "inline-flex";
2633
2836
  badge.style.alignItems = "center";
2634
- this.buildContent(badge, step.badge?.blocks ?? [], doc, context, idx, [], host);
2837
+ const marker = step.badge?.marker ?? null;
2838
+ if (marker) badge.appendChild(buildMarkerElement(doc, marker));
2839
+ else this.buildContent(badge, step.badge?.blocks ?? [], doc, context, idx, [], host);
2635
2840
  root.appendChild(badge);
2636
2841
  if (design.fullWidth === true) {
2637
2842
  card.style.width = "auto";
@@ -2646,10 +2851,17 @@ var CompositeRenderer = class extends BaseRenderer {
2646
2851
  if (caretEl) wrapper.appendChild(caretEl);
2647
2852
  wrapper.appendChild(card);
2648
2853
  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));
2854
+ if (marker) {
2855
+ host.style.display = "inline-block";
2856
+ (anchor.ownerDocument ?? document).body.appendChild(host);
2857
+ this.registerCleanup(attachMarker(anchor, host, marker));
2858
+ this.registerCleanup(() => host.remove());
2859
+ } else {
2860
+ host.style.display = "inline-block";
2861
+ const pos = inlinePosOf(step.inlinePosition);
2862
+ insertHost(anchor, host, pos);
2863
+ this.registerCleanup(keepHostAttached(host, anchorSelector, pos));
2864
+ }
2653
2865
  let open = false;
2654
2866
  let stopFloat = null;
2655
2867
  let closeTimer = null;
@@ -2702,6 +2914,7 @@ var CompositeRenderer = class extends BaseRenderer {
2702
2914
  stopFloat?.();
2703
2915
  if (closeTimer !== null) clearTimeout(closeTimer);
2704
2916
  });
2917
+ if (context.badgeOpen) openTip();
2705
2918
  return closeTip;
2706
2919
  }
2707
2920
  };
@@ -2755,14 +2968,14 @@ function makeCaret(doc, design) {
2755
2968
  return caret;
2756
2969
  }
2757
2970
  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);
2971
+ const el2 = anchor;
2972
+ const prevPosition = el2.style.position;
2973
+ const prevZ = el2.style.zIndex;
2974
+ if (getComputedStyle(el2).position === "static") el2.style.position = "relative";
2975
+ el2.style.zIndex = String(zIndex);
2763
2976
  return () => {
2764
- el.style.position = prevPosition;
2765
- el.style.zIndex = prevZ;
2977
+ el2.style.position = prevPosition;
2978
+ el2.style.zIndex = prevZ;
2766
2979
  };
2767
2980
  }
2768
2981
  async function anchorPosition(anchor, wrapper, caret, preferred = "bottom") {
@@ -3278,7 +3491,8 @@ var GuidePreviewController = class {
3278
3491
  }
3279
3492
  preview(input) {
3280
3493
  const nextStep = input.guideSteps[0];
3281
- if (this.singleStep && this.input && input.guideType === this.input.guideType && input.guideType !== "walkthrough" && nextStep && this.singleStep.updateStep(nextStep)) {
3494
+ if (this.singleStep && this.input && input.guideType === this.input.guideType && input.guideType !== "walkthrough" && // Abrir/cerrar el tooltip del badge requiere remontar.
3495
+ input.badgeOpen === true === (this.input.badgeOpen === true) && nextStep && this.singleStep.updateStep(nextStep)) {
3282
3496
  this.input = input;
3283
3497
  return {
3284
3498
  close: () => this.close(),
@@ -3308,7 +3522,8 @@ var GuidePreviewController = class {
3308
3522
  }
3309
3523
  return null;
3310
3524
  }
3311
- close() {
3525
+ close(byUser = false) {
3526
+ const onClosed = byUser ? this.input?.onClosed : void 0;
3312
3527
  if (this.singleStep) {
3313
3528
  safeDestroy(this.singleStep);
3314
3529
  this.singleStep = null;
@@ -3320,6 +3535,7 @@ var GuidePreviewController = class {
3320
3535
  this.walkthroughGuide = null;
3321
3536
  this.walkthroughIndex = 0;
3322
3537
  this.input = null;
3538
+ onClosed?.();
3323
3539
  }
3324
3540
  async renderSingleStep(guide) {
3325
3541
  const renderer = createSingleStepRenderer(guide.guideType);
@@ -3332,7 +3548,10 @@ var GuidePreviewController = class {
3332
3548
  onInteraction: (event) => {
3333
3549
  if (event.action === "shown") shown = true;
3334
3550
  },
3335
- onClose: () => this.close(),
3551
+ onClose: () => this.close(true),
3552
+ // Sin indicación explícita, el badge se abre solo si la guía arranca
3553
+ // sola (como en el runtime); `manual` lo deja cerrado.
3554
+ badgeOpen: this.input?.badgeOpen ?? isAutomaticActivation(this.input?.activationRules),
3336
3555
  onTrack: () => {
3337
3556
  },
3338
3557
  // Encadenado en preview: no-op — el editor no puede montar otra guía
@@ -3398,7 +3617,7 @@ var GuidePreviewController = class {
3398
3617
  guide,
3399
3618
  onInteraction: () => {
3400
3619
  },
3401
- onClose: () => this.close(),
3620
+ onClose: () => this.close(true),
3402
3621
  nav: { stepIndex: index, totalSteps: guide.guideSteps.length, callbacks }
3403
3622
  });
3404
3623
  } catch {
@@ -3492,15 +3711,27 @@ function safeDestroy(renderer) {
3492
3711
  }
3493
3712
 
3494
3713
  // src/plugins/guides/url-match.ts
3714
+ function urlCandidates(pageUrl) {
3715
+ const out = [pageUrl];
3716
+ try {
3717
+ const u = new URL(pageUrl);
3718
+ out.push(`${u.pathname}${u.search}${u.hash}`);
3719
+ if (u.hash.startsWith("#/")) out.push(u.hash.slice(1));
3720
+ } catch {
3721
+ }
3722
+ return out;
3723
+ }
3495
3724
  function matchesUrl(matcher, pageUrl) {
3725
+ const candidates = urlCandidates(pageUrl);
3496
3726
  switch (matcher.type) {
3497
3727
  case "exact":
3498
- return matcher.pattern === pageUrl;
3728
+ return candidates.some((c) => c === matcher.pattern);
3499
3729
  case "prefix":
3500
- return pageUrl.startsWith(matcher.pattern);
3730
+ return candidates.some((c) => c.startsWith(matcher.pattern));
3501
3731
  case "regex":
3502
3732
  try {
3503
- return new RegExp(matcher.pattern).test(pageUrl);
3733
+ const re = new RegExp(matcher.pattern);
3734
+ return candidates.some((c) => re.test(c));
3504
3735
  } catch {
3505
3736
  return false;
3506
3737
  }
@@ -3514,17 +3745,375 @@ function stepMatchesPage(loc, pageUrl) {
3514
3745
  // src/plugins/builder/bridge-protocol.ts
3515
3746
  var VEO_BUILDER_SOURCE = "veo-builder";
3516
3747
 
3748
+ // src/plugins/builder/builder-chrome.ts
3749
+ var CHROME_TOPBAR_H = 64;
3750
+ var CHROME_STEPS_H = 128;
3751
+ var ICONS = {
3752
+ 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"/>',
3753
+ 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"/>',
3754
+ 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"/>',
3755
+ 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"/>',
3756
+ 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"/>',
3757
+ chevronDown: '<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5"/>',
3758
+ chevronUp: '<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 15.75 7.5-7.5 7.5 7.5"/>',
3759
+ plus: '<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15"/>',
3760
+ 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"/>',
3761
+ 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"/>'
3762
+ };
3763
+ function svg(name, size = 20) {
3764
+ 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>`;
3765
+ }
3766
+ function shortDate(iso) {
3767
+ if (!iso) return "";
3768
+ const d = new Date(iso);
3769
+ if (Number.isNaN(d.getTime())) return "";
3770
+ return d.toLocaleDateString("es", { day: "numeric", month: "short" }).replace(".", "");
3771
+ }
3772
+ function el(tag, className, html) {
3773
+ const node = document.createElement(tag);
3774
+ node.className = className;
3775
+ if (html !== void 0) node.innerHTML = html;
3776
+ return node;
3777
+ }
3778
+ function iconButton(name, title, className = "") {
3779
+ const b = el("button", `veo-tb-icon ${className}`.trim(), svg(name));
3780
+ b.type = "button";
3781
+ b.title = title;
3782
+ b.setAttribute("aria-label", title);
3783
+ return b;
3784
+ }
3785
+ function injectBuilderChrome(opts) {
3786
+ if (!hasWindow() || !hasDocument()) {
3787
+ return {
3788
+ setState: () => {
3789
+ },
3790
+ height: () => 0,
3791
+ dock: () => "top",
3792
+ setPreview: () => {
3793
+ },
3794
+ setHidden: () => {
3795
+ },
3796
+ onHeightChange: () => {
3797
+ },
3798
+ teardown: () => {
3799
+ }
3800
+ };
3801
+ }
3802
+ const host = document.createElement("div");
3803
+ host.setAttribute("data-veo-builder-chrome", "");
3804
+ host.setAttribute("data-veo-ignore", "");
3805
+ const shadow = host.attachShadow({ mode: "open" });
3806
+ const style = document.createElement("style");
3807
+ style.textContent = CHROME_CSS;
3808
+ const root = el("div", "veo-chrome");
3809
+ const topbar = el("div", "veo-tb");
3810
+ const brand = el("div", "veo-tb-brand", svg("bolt", 18));
3811
+ const nameEl = el("div", "veo-tb-name");
3812
+ const statusEl = el("span", "veo-tb-status");
3813
+ statusEl.hidden = true;
3814
+ const sep = el("span", "veo-tb-sep");
3815
+ const appEl = el("div", "veo-tb-app", `${svg("desktop", 16)}<span></span>`);
3816
+ appEl.querySelector("span").textContent = opts.appLabel;
3817
+ const left = el("div", "veo-tb-left");
3818
+ left.append(brand, nameEl, statusEl, sep, appEl);
3819
+ const right = el("div", "veo-tb-right");
3820
+ const dockBtn = iconButton("updown", "Mover la barra abajo");
3821
+ const showPanel = iconButton("map", "Mostrar el editor");
3822
+ const preview = iconButton("eye", "Vista previa");
3823
+ const exit = el("button", "veo-tb-text", "Salir");
3824
+ exit.type = "button";
3825
+ const saveGroup = el("div", "veo-tb-save");
3826
+ const save = el("button", "veo-tb-save-main", "<span>Guardar</span>");
3827
+ save.type = "button";
3828
+ const saveMore = el("button", "veo-tb-save-more", svg("chevronDown", 16));
3829
+ saveMore.type = "button";
3830
+ saveMore.title = "M\xE1s opciones de guardado";
3831
+ saveMore.setAttribute("aria-label", "M\xE1s opciones de guardado");
3832
+ const saveMenu = el("div", "veo-tb-menu");
3833
+ const saveExit = el("button", "veo-tb-menu-item", "Guardar y salir");
3834
+ saveExit.type = "button";
3835
+ saveMenu.append(saveExit);
3836
+ saveMenu.hidden = true;
3837
+ saveGroup.append(save, saveMore, saveMenu);
3838
+ right.append(dockBtn, showPanel, preview, el("span", "veo-tb-vsep"), exit, saveGroup);
3839
+ topbar.append(left, right);
3840
+ const stepsBar = el("div", "veo-steps");
3841
+ const stepsList = el("div", "veo-steps-list");
3842
+ stepsBar.append(stepsList);
3843
+ const togglePill = el("button", "veo-steps-toggle");
3844
+ togglePill.type = "button";
3845
+ root.append(topbar, stepsBar, togglePill);
3846
+ const previewPill = el("div", "veo-preview-pill");
3847
+ const previewLabel = el(
3848
+ "span",
3849
+ "veo-preview-label",
3850
+ `${svg("eye", 18)}<span>Est\xE1s en vista previa</span>`
3851
+ );
3852
+ const previewExit = el("button", "veo-preview-exit", "Salir");
3853
+ previewExit.type = "button";
3854
+ previewPill.append(previewLabel, previewExit);
3855
+ previewPill.hidden = true;
3856
+ shadow.append(style, root, previewPill);
3857
+ document.body.appendChild(host);
3858
+ const html = document.documentElement;
3859
+ const prevMarginTop = html.style.marginTop;
3860
+ const prevMarginBottom = html.style.marginBottom;
3861
+ let collapsed = false;
3862
+ let dock = "top";
3863
+ let previewOn = false;
3864
+ let hiddenOn = false;
3865
+ let heightCb = null;
3866
+ const height = () => CHROME_TOPBAR_H + (collapsed ? 0 : CHROME_STEPS_H);
3867
+ const applyHeight = () => {
3868
+ const h = previewOn || hiddenOn ? 0 : height();
3869
+ html.style.marginTop = dock === "top" ? `${h}px` : prevMarginTop;
3870
+ html.style.marginBottom = dock === "bottom" ? `${h}px` : prevMarginBottom;
3871
+ root.style.setProperty("--veo-chrome-h", `${height()}px`);
3872
+ root.classList.toggle("is-bottom", dock === "bottom");
3873
+ stepsBar.hidden = collapsed;
3874
+ const open = dock === "top" ? "chevronUp" : "chevronDown";
3875
+ const closedIcon = dock === "top" ? "chevronDown" : "chevronUp";
3876
+ togglePill.innerHTML = `<span>${collapsed ? "Mostrar pasos" : "Ocultar pasos"}</span>${svg(
3877
+ collapsed ? closedIcon : open,
3878
+ 16
3879
+ )}`;
3880
+ dockBtn.title = dock === "top" ? "Mover la barra abajo" : "Mover la barra arriba";
3881
+ dockBtn.setAttribute("aria-label", dockBtn.title);
3882
+ heightCb?.(h);
3883
+ };
3884
+ const setPreview = (on) => {
3885
+ previewOn = on;
3886
+ root.hidden = on || hiddenOn;
3887
+ previewPill.hidden = !on;
3888
+ applyHeight();
3889
+ };
3890
+ const setHidden = (on) => {
3891
+ hiddenOn = on;
3892
+ root.hidden = on || previewOn;
3893
+ applyHeight();
3894
+ };
3895
+ togglePill.addEventListener("click", () => {
3896
+ collapsed = !collapsed;
3897
+ applyHeight();
3898
+ });
3899
+ const act = (a) => opts.onAction(a);
3900
+ dockBtn.addEventListener("click", () => {
3901
+ dock = dock === "top" ? "bottom" : "top";
3902
+ applyHeight();
3903
+ act({ action: "dock", position: dock });
3904
+ });
3905
+ showPanel.addEventListener("click", () => act({ action: "show-panel" }));
3906
+ preview.addEventListener("click", () => act({ action: "preview" }));
3907
+ previewExit.addEventListener("click", () => act({ action: "exit-preview" }));
3908
+ exit.addEventListener("click", () => act({ action: "exit" }));
3909
+ save.addEventListener("click", () => act({ action: "save", exit: false }));
3910
+ saveMore.addEventListener("click", (e) => {
3911
+ e.stopPropagation();
3912
+ saveMenu.hidden = !saveMenu.hidden;
3913
+ });
3914
+ saveExit.addEventListener("click", () => {
3915
+ saveMenu.hidden = true;
3916
+ act({ action: "save", exit: true });
3917
+ });
3918
+ const closeMenu = () => {
3919
+ saveMenu.hidden = true;
3920
+ };
3921
+ document.addEventListener("click", closeMenu);
3922
+ let current = null;
3923
+ const render = () => {
3924
+ const s = current;
3925
+ if (!s) return;
3926
+ const date = shortDate(s.createdAt);
3927
+ nameEl.textContent = `${s.guideName.trim() || "Sin nombre"}${date ? ` \xB7 ${date}` : ""}`;
3928
+ if (s.status) {
3929
+ statusEl.hidden = false;
3930
+ statusEl.textContent = s.status.label;
3931
+ statusEl.dataset.tone = s.status.tone;
3932
+ statusEl.title = "Estado de la gu\xEDa (se cambia desde la lista de gu\xEDas)";
3933
+ } else {
3934
+ statusEl.hidden = true;
3935
+ }
3936
+ save.innerHTML = s.saving ? `${svg("spinner", 16)}<span>Guardando\u2026</span>` : "<span>Guardar</span>";
3937
+ save.disabled = s.saving;
3938
+ saveExit.disabled = s.saving;
3939
+ stepsList.replaceChildren();
3940
+ const activation = stepCard(svg("cursor", 22), "Activaci\xF3n", s.section === "activation");
3941
+ activation.addEventListener("click", () => act({ action: "select-activation" }));
3942
+ stepsList.append(activation);
3943
+ s.steps.forEach((st, i) => {
3944
+ 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>';
3945
+ const card = stepCard(thumb, st.label, s.section === "step" && s.activeStep === i);
3946
+ card.addEventListener("click", () => act({ action: "select-step", index: i }));
3947
+ stepsList.append(card);
3948
+ });
3949
+ const add = el("button", "veo-step-add", `${svg("plus", 18)}<span>Agregar paso</span>`);
3950
+ add.type = "button";
3951
+ add.addEventListener("click", () => act({ action: "add-step" }));
3952
+ stepsList.append(add);
3953
+ };
3954
+ applyHeight();
3955
+ return {
3956
+ setState: (state) => {
3957
+ current = state;
3958
+ render();
3959
+ },
3960
+ height,
3961
+ dock: () => dock,
3962
+ setPreview,
3963
+ setHidden,
3964
+ onHeightChange: (cb) => {
3965
+ heightCb = cb;
3966
+ },
3967
+ teardown: () => {
3968
+ document.removeEventListener("click", closeMenu);
3969
+ html.style.marginTop = prevMarginTop;
3970
+ html.style.marginBottom = prevMarginBottom;
3971
+ host.remove();
3972
+ }
3973
+ };
3974
+ }
3975
+ function stepCard(inner, label, active) {
3976
+ const b = el(
3977
+ "button",
3978
+ `veo-step${active ? " is-active" : ""}`,
3979
+ `<div class="veo-step-box">${inner}</div><span class="veo-step-label"></span>`
3980
+ );
3981
+ b.type = "button";
3982
+ b.querySelector(".veo-step-label").textContent = label;
3983
+ b.setAttribute("aria-pressed", active ? "true" : "false");
3984
+ return b;
3985
+ }
3986
+ var CHROME_CSS = `
3987
+ :host { all: initial; }
3988
+ .veo-chrome {
3989
+ position: fixed; top: 0; left: 0; right: 0;
3990
+ display: flex; flex-direction: column;
3991
+ z-index: 2147483646;
3992
+ font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
3993
+ font-size: 14px; color: #1c1917;
3994
+ }
3995
+ .veo-chrome[hidden] { display: none; }
3996
+ .veo-chrome.is-bottom { top: auto; bottom: 0; flex-direction: column-reverse; }
3997
+ .veo-chrome.is-bottom .veo-tb-menu { top: auto; bottom: calc(100% + 8px); }
3998
+ .veo-chrome.is-bottom .veo-steps { border-bottom: 0; border-top: 1px solid #d6cfc8; }
3999
+ .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%); }
4000
+ .veo-preview-pill {
4001
+ position: fixed; top: 28px; left: 50%; transform: translateX(-50%);
4002
+ z-index: 2147483646;
4003
+ display: inline-flex; align-items: center; gap: 12px;
4004
+ background: #2b2623; color: #fff; border-radius: 999px; padding: 12px 20px;
4005
+ font-family: system-ui, -apple-system, "Segoe UI", sans-serif; font-size: 15px; font-weight: 500;
4006
+ box-shadow: 0 8px 24px rgba(0,0,0,0.3);
4007
+ }
4008
+ .veo-preview-pill[hidden] { display: none; }
4009
+ .veo-preview-label { display: inline-flex; align-items: center; gap: 10px; padding-right: 12px; border-right: 1px solid rgba(255,255,255,0.3); }
4010
+ .veo-preview-exit { border: 0; background: transparent; color: #fff; font: inherit; cursor: pointer; padding: 0 2px; }
4011
+ .veo-preview-exit:hover { text-decoration: underline; }
4012
+ .veo-tb {
4013
+ height: ${CHROME_TOPBAR_H}px; box-sizing: border-box;
4014
+ display: flex; align-items: center; justify-content: space-between;
4015
+ padding: 0 20px; gap: 16px;
4016
+ background: #2b2623; color: #fff;
4017
+ }
4018
+ .veo-tb-left, .veo-tb-right { display: flex; align-items: center; gap: 12px; min-width: 0; }
4019
+ .veo-tb-brand {
4020
+ width: 36px; height: 36px; border-radius: 999px; background: #b74d00;
4021
+ display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
4022
+ }
4023
+ .veo-tb-name { font-weight: 700; font-size: 16px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
4024
+ .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; }
4025
+ .veo-tb-status[data-tone="live"] { background: #16a34a; }
4026
+ .veo-tb-status[data-tone="testing"] { background: #b45309; }
4027
+ .veo-tb-status[data-tone="review"] { background: #7c3aed; }
4028
+ .veo-tb-status[data-tone="draft"] { background: #0f766e; }
4029
+ .veo-tb-status[data-tone="paused"] { background: rgba(255,255,255,0.28); }
4030
+ .veo-tb-sep { width: 1px; height: 24px; background: rgba(255,255,255,0.25); }
4031
+ .veo-tb-vsep { width: 1px; height: 28px; background: rgba(255,255,255,0.25); margin: 0 4px; }
4032
+ .veo-tb-app { display: inline-flex; align-items: center; gap: 8px; color: rgba(255,255,255,0.85); white-space: nowrap; }
4033
+ .veo-tb-icon {
4034
+ width: 42px; height: 42px; border-radius: 10px; border: 0;
4035
+ background: rgba(255,255,255,0.12); color: #fff; cursor: pointer;
4036
+ display: inline-flex; align-items: center; justify-content: center;
4037
+ }
4038
+ .veo-tb-icon:hover { background: rgba(255,255,255,0.22); }
4039
+ .veo-tb-text { border: 0; background: transparent; color: #fff; font: inherit; font-size: 15px; cursor: pointer; padding: 8px 6px; }
4040
+ .veo-tb-text:hover { text-decoration: underline; }
4041
+ .veo-tb-save { position: relative; display: inline-flex; border-radius: 10px; overflow: visible; }
4042
+ .veo-tb-save-main, .veo-tb-save-more {
4043
+ border: 0; background: #b74d00; color: #fff; font: inherit; font-weight: 600; font-size: 15px;
4044
+ cursor: pointer; display: inline-flex; align-items: center; gap: 8px; height: 42px;
4045
+ }
4046
+ .veo-tb-save-main { padding: 0 8px 0 18px; border-radius: 10px 0 0 10px; }
4047
+ .veo-tb-save-more { padding: 0 12px 0 6px; border-radius: 0 10px 10px 0; }
4048
+ .veo-tb-save-main:hover, .veo-tb-save-more:hover { background: #953c00; }
4049
+ .veo-tb-save-main:disabled { opacity: 0.7; cursor: default; }
4050
+ .veo-tb-menu {
4051
+ position: absolute; right: 0; top: calc(100% + 8px); min-width: 220px;
4052
+ background: #fff; color: #1c1917; border-radius: 12px; padding: 6px;
4053
+ box-shadow: 0 12px 32px rgba(0,0,0,0.22);
4054
+ }
4055
+ .veo-tb-menu-item {
4056
+ display: block; width: 100%; text-align: left; border: 0; background: transparent;
4057
+ font: inherit; font-size: 15px; padding: 10px 14px; border-radius: 8px; cursor: pointer;
4058
+ }
4059
+ .veo-tb-menu-item:hover { background: #f5efe9; }
4060
+ .veo-steps {
4061
+ height: ${CHROME_STEPS_H}px; box-sizing: border-box;
4062
+ background: #e7e1dc; border-bottom: 1px solid #d6cfc8;
4063
+ padding: 16px 28px 0; overflow-x: auto;
4064
+ }
4065
+ .veo-steps[hidden] { display: none; }
4066
+ .veo-steps-list { display: flex; align-items: flex-start; gap: 20px; }
4067
+ .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; }
4068
+ .veo-step-box {
4069
+ width: 116px; height: 64px; border-radius: 10px; background: #fff; border: 2px solid transparent;
4070
+ display: flex; align-items: center; justify-content: center; color: #2b2623;
4071
+ box-shadow: 0 1px 2px rgba(0,0,0,0.06);
4072
+ }
4073
+ .veo-step.is-active .veo-step-box { border-color: #b74d00; }
4074
+ .veo-step-label { font-size: 13px; color: #6b635d; }
4075
+ .veo-step.is-active .veo-step-label { color: #2b2623; font-weight: 600; }
4076
+ .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; }
4077
+ .veo-thumb i { display: block; height: 3px; border-radius: 2px; background: #2b2623; width: 70%; }
4078
+ .veo-thumb i + i { width: 45%; background: #c9c2bb; }
4079
+ .veo-thumb b { display: block; height: 6px; width: 24px; border-radius: 2px; background: #b74d00; align-self: flex-end; }
4080
+ .veo-thumb-inline { border-style: dashed; }
4081
+ .veo-thumb-inline i { width: 100%; }
4082
+ .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; }
4083
+ .veo-step-add:hover { color: #2b2623; }
4084
+ .veo-steps-toggle {
4085
+ position: absolute; left: 50%; top: var(--veo-chrome-h, ${CHROME_TOPBAR_H + CHROME_STEPS_H}px);
4086
+ transform: translate(-50%, -50%);
4087
+ border: 0; background: #2b2623; color: #fff; font: inherit; font-size: 14px; font-weight: 500;
4088
+ border-radius: 999px; padding: 10px 18px; cursor: pointer;
4089
+ display: inline-flex; align-items: center; gap: 8px;
4090
+ box-shadow: 0 6px 18px rgba(0,0,0,0.25);
4091
+ }
4092
+ `;
4093
+
3517
4094
  // src/plugins/builder/builder-panel.ts
3518
4095
  var DEFAULT_PANEL_PATH = "/guides/design";
3519
- var PANEL_W = 400;
3520
- var PANEL_H = 640;
4096
+ var PANEL_W = 484;
4097
+ var PANEL_H = 660;
3521
4098
  var MIN_W = 320;
3522
4099
  var MIN_H = 320;
3523
4100
  var MARGIN = 16;
4101
+ var HEADER_H = 52;
3524
4102
  function injectBuilderPanel(opts) {
3525
4103
  if (!hasWindow() || !hasDocument()) {
3526
- return { target: () => null, frameRect: () => null, teardown: () => {
3527
- } };
4104
+ return {
4105
+ target: () => null,
4106
+ frameRect: () => null,
4107
+ teardown: () => {
4108
+ },
4109
+ setTitle: () => {
4110
+ },
4111
+ setVisible: () => {
4112
+ },
4113
+ isVisible: () => false,
4114
+ setTopOffset: () => {
4115
+ }
4116
+ };
3528
4117
  }
3529
4118
  const panelUrl = buildPanelUrl(opts);
3530
4119
  const host = document.createElement("div");
@@ -3534,43 +4123,68 @@ function injectBuilderPanel(opts) {
3534
4123
  style.textContent = PANEL_CSS;
3535
4124
  const card = document.createElement("div");
3536
4125
  card.className = "veo-panel";
3537
- let left = Math.max(MARGIN, window.innerWidth - PANEL_W - MARGIN);
3538
- let top = MARGIN;
4126
+ let topOffset = opts.topOffset ?? 0;
4127
+ let left = 40;
4128
+ let top = topOffset + MARGIN;
3539
4129
  card.style.left = `${left}px`;
3540
4130
  card.style.top = `${top}px`;
3541
4131
  card.style.width = `${PANEL_W}px`;
3542
4132
  card.style.height = `${PANEL_H}px`;
3543
4133
  const header = document.createElement("div");
3544
4134
  header.className = "veo-panel-header";
4135
+ const grip = document.createElement("span");
4136
+ grip.className = "veo-panel-grip";
4137
+ grip.setAttribute("aria-hidden", "true");
4138
+ grip.textContent = "\xB7\xB7\xB7";
3545
4139
  const title = document.createElement("span");
3546
4140
  title.className = "veo-panel-title";
3547
4141
  title.textContent = "OnUser \xB7 Editor";
3548
4142
  const actions = document.createElement("div");
3549
4143
  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);
4144
+ const popout = panelButton(ICON_POPOUT, "Abrir en ventana aparte");
4145
+ const maximize = panelButton(ICON_EXPAND, "Maximizar");
4146
+ const minimize = panelButton(ICON_MINUS, "Minimizar");
4147
+ actions.append(popout, maximize, minimize);
4148
+ header.append(grip, title, actions);
3564
4149
  const iframe = document.createElement("iframe");
3565
4150
  iframe.className = "veo-panel-frame";
3566
4151
  iframe.title = "Editor de Veo";
3567
4152
  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);
4153
+ const resizeGrip = document.createElement("div");
4154
+ resizeGrip.className = "veo-panel-resize";
4155
+ resizeGrip.setAttribute("aria-hidden", "true");
4156
+ card.append(header, iframe, resizeGrip);
3572
4157
  shadow.append(style, card);
3573
4158
  document.body.appendChild(host);
4159
+ let visible = true;
4160
+ let maximized = false;
4161
+ let restore = null;
4162
+ const setVisible = (next) => {
4163
+ visible = next;
4164
+ card.style.display = next && !(popped && !popped.closed) ? "" : "none";
4165
+ };
4166
+ minimize.addEventListener("click", () => setVisible(false));
4167
+ maximize.addEventListener("click", () => {
4168
+ maximized = !maximized;
4169
+ if (maximized) {
4170
+ restore = { left, top, w: card.offsetWidth, h: card.offsetHeight };
4171
+ left = MARGIN;
4172
+ top = topOffset + MARGIN;
4173
+ card.style.left = `${left}px`;
4174
+ card.style.top = `${top}px`;
4175
+ card.style.width = `${Math.max(MIN_W, window.innerWidth - MARGIN * 2)}px`;
4176
+ card.style.height = `${Math.max(MIN_H, window.innerHeight - top - MARGIN)}px`;
4177
+ maximize.title = "Restaurar";
4178
+ } else if (restore) {
4179
+ left = restore.left;
4180
+ top = restore.top;
4181
+ card.style.left = `${left}px`;
4182
+ card.style.top = `${top}px`;
4183
+ card.style.width = `${restore.w}px`;
4184
+ card.style.height = `${restore.h}px`;
4185
+ maximize.title = "Maximizar";
4186
+ }
4187
+ });
3574
4188
  let popped = null;
3575
4189
  let pollTimer;
3576
4190
  const stopPoll = () => {
@@ -3579,7 +4193,6 @@ function injectBuilderPanel(opts) {
3579
4193
  pollTimer = void 0;
3580
4194
  }
3581
4195
  };
3582
- close.addEventListener("click", () => opts.onClose());
3583
4196
  popout.addEventListener("click", () => {
3584
4197
  const win = window.open(
3585
4198
  panelUrl,
@@ -3597,7 +4210,7 @@ function injectBuilderPanel(opts) {
3597
4210
  }, 600);
3598
4211
  });
3599
4212
  header.addEventListener("mousedown", (e) => {
3600
- if (e.target?.classList.contains("veo-panel-btn")) return;
4213
+ if (e.target?.closest(".veo-panel-btn")) return;
3601
4214
  e.preventDefault();
3602
4215
  const sx = e.clientX;
3603
4216
  const sy = e.clientY;
@@ -3606,7 +4219,7 @@ function injectBuilderPanel(opts) {
3606
4219
  iframe.style.pointerEvents = "none";
3607
4220
  const move = (ev) => {
3608
4221
  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));
4222
+ top = clamp3(ot + (ev.clientY - sy), topOffset, Math.max(topOffset, window.innerHeight - 40));
3610
4223
  card.style.left = `${left}px`;
3611
4224
  card.style.top = `${top}px`;
3612
4225
  };
@@ -3618,7 +4231,7 @@ function injectBuilderPanel(opts) {
3618
4231
  window.addEventListener("mousemove", move);
3619
4232
  window.addEventListener("mouseup", up);
3620
4233
  });
3621
- grip.addEventListener("mousedown", (e) => {
4234
+ resizeGrip.addEventListener("mousedown", (e) => {
3622
4235
  e.preventDefault();
3623
4236
  e.stopPropagation();
3624
4237
  const sx = e.clientX;
@@ -3645,9 +4258,33 @@ function injectBuilderPanel(opts) {
3645
4258
  stopPoll();
3646
4259
  if (popped && !popped.closed) popped.close();
3647
4260
  host.remove();
4261
+ },
4262
+ setTitle: (t) => {
4263
+ title.textContent = t;
4264
+ },
4265
+ setVisible,
4266
+ isVisible: () => visible,
4267
+ setTopOffset: (px) => {
4268
+ topOffset = px;
4269
+ if (top < topOffset + MARGIN) {
4270
+ top = topOffset + MARGIN;
4271
+ card.style.top = `${top}px`;
4272
+ }
3648
4273
  }
3649
4274
  };
3650
4275
  }
4276
+ 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"/>';
4277
+ 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"/>';
4278
+ var ICON_MINUS = '<path stroke-linecap="round" stroke-linejoin="round" d="M5 12h14"/>';
4279
+ function panelButton(iconPath, label) {
4280
+ const b = document.createElement("button");
4281
+ b.className = "veo-panel-btn";
4282
+ b.type = "button";
4283
+ b.title = label;
4284
+ b.setAttribute("aria-label", label);
4285
+ 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>`;
4286
+ return b;
4287
+ }
3651
4288
  function clamp3(v, min, max) {
3652
4289
  return Math.min(Math.max(v, min), max);
3653
4290
  }
@@ -3667,28 +4304,33 @@ var PANEL_CSS = `
3667
4304
  max-width: 96vw; max-height: 94vh;
3668
4305
  display: flex; flex-direction: column;
3669
4306
  background: #fff;
3670
- border-radius: 12px;
3671
- box-shadow: 0 12px 40px rgba(0,0,0,0.28);
4307
+ border-radius: 14px;
4308
+ box-shadow: 0 12px 40px rgba(0,0,0,0.22);
3672
4309
  overflow: hidden;
3673
4310
  z-index: 2147483647;
3674
4311
  font-family: system-ui, -apple-system, sans-serif;
3675
4312
  }
3676
4313
  .veo-panel-header {
3677
- flex: 0 0 auto;
4314
+ position: relative; flex: 0 0 auto; height: ${HEADER_H}px; box-sizing: border-box;
3678
4315
  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;
4316
+ padding: 10px 14px 0 22px;
4317
+ background: #fbf8f5; color: #1c1917; border-bottom: 1px solid #e8e2dc;
4318
+ font-size: 17px; font-weight: 700;
3682
4319
  cursor: grab;
3683
4320
  }
3684
4321
  .veo-panel-header:active { cursor: grabbing; }
4322
+ .veo-panel-grip {
4323
+ position: absolute; top: 0; left: 50%; transform: translateX(-50%);
4324
+ color: #b5aca4; font-size: 14px; letter-spacing: 1px; line-height: 12px;
4325
+ }
4326
+ .veo-panel-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
3685
4327
  .veo-panel-actions { display: flex; gap: 2px; }
3686
4328
  .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;
4329
+ border: 0; background: transparent; color: #4b4540;
4330
+ cursor: pointer; line-height: 1; display: inline-flex; align-items: center;
4331
+ padding: 6px; border-radius: 8px;
3690
4332
  }
3691
- .veo-panel-btn:hover { background: rgba(255,255,255,0.15); }
4333
+ .veo-panel-btn:hover { background: rgba(0,0,0,0.06); color: #1c1917; }
3692
4334
  .veo-panel-frame { flex: 1 1 auto; width: 100%; border: 0; background: #fff; }
3693
4335
  .veo-panel-resize {
3694
4336
  position: absolute; right: 0; bottom: 0;
@@ -4028,9 +4670,9 @@ var PICKER_STYLES = `
4028
4670
  z-index: ${PICKER_Z};
4029
4671
  pointer-events: none;
4030
4672
  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;
4673
+ background: transparent;
4674
+ border: 3px dashed #b74d00;
4675
+ border-radius: 8px;
4034
4676
  transition: top 60ms ease-out, left 60ms ease-out, width 60ms ease-out, height 60ms ease-out;
4035
4677
  }
4036
4678
  .veo-pick-label {
@@ -4038,30 +4680,48 @@ var PICKER_STYLES = `
4038
4680
  z-index: ${PICKER_Z};
4039
4681
  pointer-events: none;
4040
4682
  max-width: 360px;
4041
- background: #1e1b4b;
4042
- color: #e0e7ff;
4683
+ background: #2b2623;
4684
+ color: #f5efe9;
4043
4685
  font: 12px/1.45 ui-monospace, SFMono-Regular, Menlo, monospace;
4044
4686
  padding: 6px 9px;
4045
4687
  border-radius: 6px;
4046
4688
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
4047
4689
  }
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; }
4690
+ .veo-pick-label .veo-pick-sel { color: #ffd1ad; word-break: break-all; }
4691
+ .veo-pick-label .veo-pick-dim { color: #c9c2bb; margin-top: 3px; }
4050
4692
  .veo-pick-hint {
4051
4693
  position: fixed;
4052
- top: 12px;
4694
+ top: 28px;
4053
4695
  left: 50%;
4054
4696
  transform: translateX(-50%);
4055
4697
  z-index: ${PICKER_Z};
4056
4698
  pointer-events: none;
4057
- background: #4338ca;
4699
+ display: flex; align-items: center; gap: 16px;
4700
+ max-width: min(760px, calc(100vw - 32px));
4701
+ background: #2b2623;
4058
4702
  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);
4703
+ font: 15px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
4704
+ padding: 14px 18px 14px 16px;
4705
+ border-radius: 14px;
4706
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
4707
+ }
4708
+ .veo-pick-hint-icon {
4709
+ flex: 0 0 auto; width: 44px; height: 44px; border-radius: 999px;
4710
+ background: rgba(255,255,255,0.12);
4711
+ display: inline-flex; align-items: center; justify-content: center;
4063
4712
  }
4713
+ .veo-pick-hint-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
4714
+ .veo-pick-hint-title { font-weight: 700; }
4715
+ .veo-pick-hint-sub { font-size: 13px; color: rgba(255,255,255,0.75); }
4716
+ .veo-pick-cancel {
4717
+ flex: 0 0 auto; pointer-events: auto;
4718
+ border: 0; border-radius: 10px; padding: 12px 18px;
4719
+ background: rgba(255,255,255,0.14); color: #fff;
4720
+ font: inherit; font-weight: 600; cursor: pointer;
4721
+ }
4722
+ .veo-pick-cancel:hover { background: rgba(255,255,255,0.24); }
4064
4723
  `;
4724
+ 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
4725
  var ElementPicker = class {
4066
4726
  constructor() {
4067
4727
  this.host = null;
@@ -4078,6 +4738,7 @@ var ElementPicker = class {
4078
4738
  this.draw(target);
4079
4739
  };
4080
4740
  this.onClick = (event) => {
4741
+ if (event.altKey) return;
4081
4742
  const target = this.current ?? (event.target instanceof Element ? event.target : null);
4082
4743
  if (!target || this.isOwn(target)) return;
4083
4744
  event.preventDefault();
@@ -4098,7 +4759,7 @@ var ElementPicker = class {
4098
4759
  };
4099
4760
  /** Traga mousedown/mouseup durante el picking (evita focus/active de la app). */
4100
4761
  this.swallow = (event) => {
4101
- if (this.isOwn(event.target)) return;
4762
+ if (event.altKey || this.isOwn(event.target)) return;
4102
4763
  event.preventDefault();
4103
4764
  event.stopPropagation();
4104
4765
  };
@@ -4124,7 +4785,29 @@ var ElementPicker = class {
4124
4785
  this.label.style.display = "none";
4125
4786
  const hint = document.createElement("div");
4126
4787
  hint.className = "veo-pick-hint";
4127
- hint.textContent = options.hint ?? "Selecciona un elemento \xB7 Esc para cancelar";
4788
+ const icon = document.createElement("span");
4789
+ icon.className = "veo-pick-hint-icon";
4790
+ icon.innerHTML = HINT_ICON;
4791
+ const text = document.createElement("div");
4792
+ text.className = "veo-pick-hint-text";
4793
+ const title = document.createElement("span");
4794
+ title.className = "veo-pick-hint-title";
4795
+ title.textContent = options.hint ?? "Selecciona un elemento existente para colocar la gu\xEDa.";
4796
+ const sub = document.createElement("span");
4797
+ sub.className = "veo-pick-hint-sub";
4798
+ sub.textContent = options.subhint ?? "Usa Alt + clic u Opci\xF3n + clic para abrir men\xFAs y desplegables.";
4799
+ text.append(title, sub);
4800
+ const cancel = document.createElement("button");
4801
+ cancel.className = "veo-pick-cancel";
4802
+ cancel.type = "button";
4803
+ cancel.textContent = "Cancelar";
4804
+ cancel.addEventListener("click", (e) => {
4805
+ e.stopPropagation();
4806
+ const cb = this.onCancel;
4807
+ this.stop();
4808
+ cb?.();
4809
+ });
4810
+ hint.append(icon, text, cancel);
4128
4811
  shadow.append(style, this.box, this.label, hint);
4129
4812
  document.body.appendChild(host);
4130
4813
  this.host = host;
@@ -4222,6 +4905,7 @@ function initBuilderMode() {
4222
4905
  if (!dashboardOrigin) return null;
4223
4906
  let picker = null;
4224
4907
  let panel = null;
4908
+ let chrome = null;
4225
4909
  let staticTarget = null;
4226
4910
  let manipulator = null;
4227
4911
  let dropTarget = null;
@@ -4229,26 +4913,98 @@ function initBuilderMode() {
4229
4913
  let previewStepIndex = 0;
4230
4914
  let pendingPreview = null;
4231
4915
  let lastPreview = null;
4916
+ let previewMode = false;
4917
+ let previewTrigger = null;
4918
+ let previewFired = false;
4919
+ let previewDelay = null;
4920
+ let previewDismissed = false;
4921
+ let panelWasVisible = true;
4232
4922
  const getTarget = () => panel ? panel.target() : staticTarget;
4233
4923
  const post = (event) => {
4234
4924
  getTarget()?.postMessage({ source: VEO_BUILDER_SOURCE, token, ...event }, dashboardOrigin);
4235
4925
  };
4236
- const previewMatchesPage = (cmd) => {
4926
+ const previewStep = (cmd) => {
4237
4927
  const steps = cmd.guide.guideSteps;
4238
- const step = steps?.[cmd.guide.startStepIndex ?? 0];
4239
- return stepMatchesPage(step?.pageLocation, window.location.href);
4928
+ return steps?.[cmd.guide.startStepIndex ?? 0];
4929
+ };
4930
+ const previewMatchesPage = (cmd) => stepMatchesPage(previewStep(cmd)?.pageLocation, window.location.href);
4931
+ const postPreviewStatus = (payload) => post({ type: "preview-status", payload });
4932
+ const withholdPreview = () => {
4933
+ manipulator?.detach();
4934
+ manipulator = null;
4935
+ dropTarget?.detach();
4936
+ dropTarget = null;
4937
+ closeGuidePreview();
4240
4938
  };
4241
4939
  const applyPreviewGated = (cmd) => {
4242
- 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();
4940
+ const showNow = !cmd.honorTrigger;
4941
+ if (showNow) {
4942
+ disarmPreviewTrigger();
4943
+ cancelPreviewDelay();
4944
+ previewFired = true;
4945
+ previewDismissed = false;
4946
+ }
4947
+ lastPreview = showNow ? { ...cmd, honorTrigger: true } : cmd;
4948
+ if (!previewMatchesPage(cmd)) {
4949
+ cancelPreviewDelay();
4950
+ withholdPreview();
4951
+ postPreviewStatus({
4952
+ state: "page-mismatch",
4953
+ pattern: previewStep(cmd)?.pageLocation?.pattern ?? ""
4954
+ });
4955
+ return;
4251
4956
  }
4957
+ const devices = cmd.guide.activationRules?.devices;
4958
+ if (!showNow && !previewMode && devices && !isDeviceAllowed(devices)) {
4959
+ cancelPreviewDelay();
4960
+ withholdPreview();
4961
+ postPreviewStatus({ state: "device-mismatch", devices });
4962
+ return;
4963
+ }
4964
+ applyPreview(lastPreview);
4965
+ };
4966
+ const disarmPreviewTrigger = () => {
4967
+ previewTrigger?.();
4968
+ previewTrigger = null;
4969
+ };
4970
+ const cancelPreviewDelay = () => {
4971
+ if (previewDelay) clearTimeout(previewDelay);
4972
+ previewDelay = null;
4973
+ };
4974
+ const armPreviewTrigger = (selector, trigger) => {
4975
+ disarmPreviewTrigger();
4976
+ const onFire = (e) => {
4977
+ const target = e.target;
4978
+ if (!(target instanceof Element)) return;
4979
+ if (target.closest("[data-veo-builder-chrome],[data-veo-builder-panel],[data-veo-picker]")) {
4980
+ return;
4981
+ }
4982
+ try {
4983
+ if (!target.closest(selector)) return;
4984
+ } catch {
4985
+ return;
4986
+ }
4987
+ disarmPreviewTrigger();
4988
+ previewFired = true;
4989
+ previewDismissed = false;
4990
+ if (lastPreview) applyPreviewGated(lastPreview);
4991
+ };
4992
+ document.addEventListener("click", onFire, true);
4993
+ if (trigger === "on_hover") document.addEventListener("mouseover", onFire, true);
4994
+ previewTrigger = () => {
4995
+ document.removeEventListener("click", onFire, true);
4996
+ document.removeEventListener("mouseover", onFire, true);
4997
+ };
4998
+ postPreviewStatus({ state: "waiting-trigger", trigger, selector });
4999
+ };
5000
+ const armPreviewDelay = (delayMs) => {
5001
+ cancelPreviewDelay();
5002
+ previewDelay = setTimeout(() => {
5003
+ previewDelay = null;
5004
+ previewFired = true;
5005
+ if (lastPreview) applyPreviewGated(lastPreview);
5006
+ }, delayMs);
5007
+ postPreviewStatus({ state: "waiting-delay", delayMs });
4252
5008
  };
4253
5009
  const applyPreview = (cmd) => {
4254
5010
  manipulator?.detach();
@@ -4256,8 +5012,47 @@ function initBuilderMode() {
4256
5012
  dropTarget?.detach();
4257
5013
  dropTarget = null;
4258
5014
  previewStepIndex = cmd.guide.startStepIndex ?? 0;
4259
- const handle = previewGuide(cmd.guide);
4260
- if (!cmd.editable) return;
5015
+ const rules = cmd.guide.activationRules;
5016
+ const trig = rules?.trigger;
5017
+ const sel = rules?.selector?.trim();
5018
+ const gate = cmd.honorTrigger && !previewFired && !previewMode;
5019
+ const waitsForTarget = (trig === "on_click" || trig === "on_hover") && (!isAutomaticActivation(rules) || previewDismissed);
5020
+ if (gate && waitsForTarget && sel) {
5021
+ cancelPreviewDelay();
5022
+ closeGuidePreview();
5023
+ armPreviewTrigger(sel, trig);
5024
+ return;
5025
+ }
5026
+ const delayMs = activationDelayMs(rules);
5027
+ if (gate && delayMs > 0) {
5028
+ disarmPreviewTrigger();
5029
+ closeGuidePreview();
5030
+ if (!previewDelay) armPreviewDelay(delayMs);
5031
+ return;
5032
+ }
5033
+ disarmPreviewTrigger();
5034
+ cancelPreviewDelay();
5035
+ const handle = previewGuide({
5036
+ ...cmd.guide,
5037
+ onClosed: () => {
5038
+ manipulator?.detach();
5039
+ manipulator = null;
5040
+ dropTarget?.detach();
5041
+ dropTarget = null;
5042
+ previewFired = false;
5043
+ previewDismissed = true;
5044
+ if (previewMode) {
5045
+ const r = lastPreview?.guide.activationRules;
5046
+ const t = r?.trigger;
5047
+ const selector = r?.selector?.trim();
5048
+ if ((t === "on_click" || t === "on_hover") && selector) armPreviewTrigger(selector, t);
5049
+ return;
5050
+ }
5051
+ if (lastPreview) applyPreviewGated(lastPreview);
5052
+ }
5053
+ });
5054
+ if (cmd.honorTrigger) postPreviewStatus({ state: "shown" });
5055
+ if (!cmd.editable || previewMode) return;
4261
5056
  void handle.ready.then((result) => {
4262
5057
  if (!result.rendered) return;
4263
5058
  const host = handle.host();
@@ -4325,26 +5120,57 @@ function initBuilderMode() {
4325
5120
  const handleCommand = (cmd) => {
4326
5121
  switch (cmd.type) {
4327
5122
  case "panel-ready":
4328
- post({ type: "ready" });
5123
+ post(chrome ? { type: "ready", chrome: true } : { type: "ready" });
5124
+ break;
5125
+ case "editor-state":
5126
+ chrome?.setState(cmd.state);
5127
+ panel?.setTitle(
5128
+ 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"
5129
+ );
5130
+ break;
5131
+ case "panel-visibility":
5132
+ panel?.setVisible(cmd.visible);
4329
5133
  break;
4330
- case "start-picker":
5134
+ case "start-picker": {
4331
5135
  picker?.stop();
5136
+ const panelWasShown = panel?.isVisible() ?? false;
5137
+ if (panelWasShown) panel?.setVisible(false);
5138
+ chrome?.setHidden(true);
5139
+ const previewToRestore = lastPreview;
5140
+ if (previewToRestore) {
5141
+ manipulator?.detach();
5142
+ manipulator = null;
5143
+ dropTarget?.detach();
5144
+ dropTarget = null;
5145
+ closeGuidePreview();
5146
+ }
5147
+ const restorePanel = () => {
5148
+ chrome?.setHidden(false);
5149
+ if (panelWasShown) panel?.setVisible(true);
5150
+ if (previewToRestore && lastPreview === previewToRestore) {
5151
+ applyPreviewGated(previewToRestore);
5152
+ }
5153
+ };
4332
5154
  picker = startElementPicker({
4333
5155
  onPick: (payload) => {
4334
5156
  picker = null;
5157
+ restorePanel();
4335
5158
  post({ type: "picked", payload });
4336
5159
  },
4337
5160
  onCancel: () => {
4338
5161
  picker = null;
5162
+ restorePanel();
4339
5163
  post({ type: "pick-cancelled" });
4340
5164
  },
4341
5165
  ...cmd.maxAncestors !== void 0 ? { maxAncestors: cmd.maxAncestors } : {},
4342
5166
  ...cmd.hint !== void 0 ? { hint: cmd.hint } : {}
4343
5167
  });
4344
5168
  break;
5169
+ }
4345
5170
  case "stop-picker":
4346
5171
  picker?.stop();
4347
5172
  picker = null;
5173
+ chrome?.setHidden(false);
4348
5174
  break;
4349
5175
  case "preview":
4350
5176
  if (!cmd.guide) break;
@@ -4362,6 +5188,10 @@ function initBuilderMode() {
4362
5188
  hideDragGhost();
4363
5189
  pendingPreview = null;
4364
5190
  lastPreview = null;
5191
+ disarmPreviewTrigger();
5192
+ cancelPreviewDelay();
5193
+ previewFired = false;
5194
+ previewDismissed = false;
4365
5195
  closeGuidePreview();
4366
5196
  break;
4367
5197
  case "teardown":
@@ -4413,21 +5243,62 @@ function initBuilderMode() {
4413
5243
  dropTarget = null;
4414
5244
  hideDragGhost();
4415
5245
  pendingPreview = null;
5246
+ disarmPreviewTrigger();
5247
+ cancelPreviewDelay();
4416
5248
  closeGuidePreview();
4417
5249
  post({ type: "closed" });
4418
5250
  panel?.teardown();
4419
5251
  panel = null;
5252
+ chrome?.teardown();
5253
+ chrome = null;
4420
5254
  clearBuilderToken();
4421
5255
  };
4422
5256
  if (ctx.panel) {
5257
+ chrome = injectBuilderChrome({
5258
+ appLabel: window.location.hostname,
5259
+ onAction: (action) => {
5260
+ switch (action.action) {
5261
+ // Controles que resuelve el SDK solo (no van al panel).
5262
+ case "dock":
5263
+ panel?.setTopOffset(action.position === "top" ? chrome?.height() ?? 0 : 0);
5264
+ return;
5265
+ case "show-panel":
5266
+ panel?.setVisible(true);
5267
+ return;
5268
+ case "preview":
5269
+ if (previewMode) return;
5270
+ previewMode = true;
5271
+ panelWasVisible = panel?.isVisible() ?? true;
5272
+ panel?.setVisible(false);
5273
+ chrome?.setPreview(true);
5274
+ if (lastPreview) applyPreviewGated(lastPreview);
5275
+ return;
5276
+ case "exit-preview":
5277
+ if (!previewMode) return;
5278
+ previewMode = false;
5279
+ chrome?.setPreview(false);
5280
+ panel?.setVisible(panelWasVisible);
5281
+ if (lastPreview) applyPreviewGated(lastPreview);
5282
+ return;
5283
+ case "select-activation":
5284
+ case "select-step":
5285
+ case "add-step":
5286
+ panel?.setVisible(true);
5287
+ break;
5288
+ }
5289
+ post({ type: "chrome-action", payload: action });
5290
+ }
5291
+ });
4423
5292
  panel = injectBuilderPanel({
4424
5293
  dashboardOrigin,
4425
5294
  token,
4426
5295
  appOrigin: window.location.origin,
4427
5296
  guide: ctx.guide,
4428
5297
  panelPath: ctx.panelPath,
4429
- onClose: () => teardown()
5298
+ onClose: () => teardown(),
5299
+ topOffset: chrome.height()
4430
5300
  });
5301
+ chrome.onHeightChange((h) => panel?.setTopOffset(chrome?.dock() === "top" ? h : 0));
4431
5302
  } else {
4432
5303
  staticTarget = resolveTarget();
4433
5304
  if (!staticTarget) return null;