veo-sdk 0.6.0 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/builder-ENKEIVF2.mjs +5 -0
  2. package/dist/{builder-3QPHB2VJ.mjs.map → builder-ENKEIVF2.mjs.map} +1 -1
  3. package/dist/builder.cjs +366 -20
  4. package/dist/builder.cjs.map +1 -1
  5. package/dist/builder.d.cts +2 -2
  6. package/dist/builder.d.ts +2 -2
  7. package/dist/builder.mjs +4 -4
  8. package/dist/{chunk-N4VFMZFA.mjs → chunk-4QZDNJJE.mjs} +3 -3
  9. package/dist/{chunk-N4VFMZFA.mjs.map → chunk-4QZDNJJE.mjs.map} +1 -1
  10. package/dist/{chunk-GMXQMZ4X.mjs → chunk-L3QQJLDJ.mjs} +3 -3
  11. package/dist/{chunk-GMXQMZ4X.mjs.map → chunk-L3QQJLDJ.mjs.map} +1 -1
  12. package/dist/{chunk-O6USA4TU.mjs → chunk-ZILDR4YE.mjs} +368 -22
  13. package/dist/chunk-ZILDR4YE.mjs.map +1 -0
  14. package/dist/detect-theme-X4K3TLUL.mjs +446 -0
  15. package/dist/detect-theme-X4K3TLUL.mjs.map +1 -0
  16. package/dist/{guide-preview-DylsEhrg.d.cts → guide-preview-DEFhfA4o.d.cts} +115 -0
  17. package/dist/{guide-preview-DylsEhrg.d.ts → guide-preview-DEFhfA4o.d.ts} +115 -0
  18. package/dist/index.cjs +994 -51
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +13 -1
  21. package/dist/index.d.ts +13 -1
  22. package/dist/index.mjs +141 -16
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/veo-builder.js +92 -20
  25. package/dist/veo-builder.js.map +1 -1
  26. package/dist/veo-guides.js +84 -12
  27. package/dist/veo-guides.js.map +1 -1
  28. package/dist/veo-theme.js +2 -0
  29. package/dist/veo-theme.js.map +1 -0
  30. package/dist/veo.js +2 -2
  31. package/dist/veo.js.map +1 -1
  32. package/package.json +2 -2
  33. package/dist/builder-3QPHB2VJ.mjs +0 -5
  34. package/dist/chunk-O6USA4TU.mjs.map +0 -1
package/dist/index.cjs CHANGED
@@ -902,6 +902,10 @@ function clampNum(n, min, max) {
902
902
  return Math.min(max, Math.max(min, n));
903
903
  }
904
904
  function applyBlockStyle(el2, style, kind) {
905
+ if (kind === "text") {
906
+ const explicit = style && typeof style === "object" ? style.preset : void 0;
907
+ el2.dataset.veoPreset = typeof explicit === "string" ? explicit : el2.classList.contains("veo-guide-title") ? "title" : "paragraph";
908
+ }
905
909
  if (!style || typeof style !== "object") return;
906
910
  const s = style;
907
911
  if (kind === "image" && s.bleed === true) {
@@ -918,8 +922,9 @@ function applyBlockStyle(el2, style, kind) {
918
922
  if (kind === "text" && typeof s.preset === "string" && s.preset in TEXT_PRESETS) {
919
923
  const preset = TEXT_PRESETS[s.preset];
920
924
  if (preset) {
921
- el2.style.fontSize = `${preset.size}px`;
922
- el2.style.fontWeight = String(preset.weight);
925
+ const token = s.preset === "small" ? null : `--veo-t-${s.preset}`;
926
+ el2.style.fontSize = token ? `var(${token}-size, ${preset.size}px)` : `${preset.size}px`;
927
+ el2.style.fontWeight = token ? `var(${token}-weight, ${preset.weight})` : String(preset.weight);
923
928
  el2.style.lineHeight = String(preset.lineHeight);
924
929
  }
925
930
  }
@@ -1635,20 +1640,92 @@ var init_styles = __esm({
1635
1640
  .veo-guide-actions {
1636
1641
  display: flex; gap: 8px; justify-content: var(--veo-actions-justify);
1637
1642
  }
1643
+ /* Tema del workspace (design system): cada preset de texto toma sus tokens de
1644
+ las variables que publica \`applyThemeVars\` en la tarjeta. Las reglas solo
1645
+ existen bajo .veo-card--themed; los estilos inline del bloque las pisan. El
1646
+ \`*\` cubre el rich text (la regla global de fuente aplica a cada hijo). */
1647
+ .veo-card--themed [data-veo-preset="title"] {
1648
+ color: var(--veo-t-title-color); font-size: var(--veo-t-title-size);
1649
+ font-weight: var(--veo-t-title-weight); text-align: var(--veo-t-title-align);
1650
+ }
1651
+ .veo-card--themed [data-veo-preset="subtitle"] {
1652
+ color: var(--veo-t-subtitle-color); font-size: var(--veo-t-subtitle-size);
1653
+ font-weight: var(--veo-t-subtitle-weight); text-align: var(--veo-t-subtitle-align);
1654
+ }
1655
+ .veo-card--themed [data-veo-preset="paragraph"] {
1656
+ color: var(--veo-t-paragraph-color); font-size: var(--veo-t-paragraph-size);
1657
+ font-weight: var(--veo-t-paragraph-weight); text-align: var(--veo-t-paragraph-align);
1658
+ }
1659
+ .veo-card--themed [data-veo-preset="small"] { color: var(--veo-t-paragraph-color); }
1660
+ .veo-card--themed [data-veo-preset="title"], .veo-card--themed [data-veo-preset="title"] * {
1661
+ font-family: var(--veo-t-title-font, var(--veo-font));
1662
+ }
1663
+ .veo-card--themed [data-veo-preset="subtitle"], .veo-card--themed [data-veo-preset="subtitle"] * {
1664
+ font-family: var(--veo-t-subtitle-font, var(--veo-font));
1665
+ }
1666
+ .veo-card--themed [data-veo-preset="paragraph"], .veo-card--themed [data-veo-preset="paragraph"] *,
1667
+ .veo-card--themed [data-veo-preset="small"], .veo-card--themed [data-veo-preset="small"] * {
1668
+ font-family: var(--veo-t-paragraph-font, var(--veo-font));
1669
+ }
1670
+ /* Formulario del tema (pesta\xF1a Formulario). Dentro de un .veo-form los tokens
1671
+ de texto \`--veo-t-*\` se REDEFINEN con \xABTexto principal\xBB: la pregunta es un
1672
+ bloque con preset (su tama\xF1o va inline con esas variables), as\xED toma los del
1673
+ formulario y no los del modal. Etiquetas y opciones = \xABTexto secundario\xBB. */
1674
+ .veo-card--themed-form .veo-form {
1675
+ background: var(--veo-f-bg); padding: var(--veo-f-pad);
1676
+ border: var(--veo-f-border, none); border-radius: var(--veo-f-radius, 8px);
1677
+ box-shadow: var(--veo-f-shadow, none);
1678
+ --veo-t-title-color: var(--veo-f-primary-color); --veo-t-title-size: var(--veo-f-primary-size);
1679
+ --veo-t-title-weight: var(--veo-f-primary-weight); --veo-t-title-align: var(--veo-f-primary-align);
1680
+ --veo-t-title-font: var(--veo-f-primary-font);
1681
+ --veo-t-subtitle-color: var(--veo-f-primary-color); --veo-t-subtitle-size: var(--veo-f-primary-size);
1682
+ --veo-t-subtitle-weight: var(--veo-f-primary-weight); --veo-t-subtitle-align: var(--veo-f-primary-align);
1683
+ --veo-t-subtitle-font: var(--veo-f-primary-font);
1684
+ --veo-t-paragraph-color: var(--veo-f-primary-color); --veo-t-paragraph-size: var(--veo-f-primary-size);
1685
+ --veo-t-paragraph-weight: var(--veo-f-primary-weight); --veo-t-paragraph-align: var(--veo-f-primary-align);
1686
+ --veo-t-paragraph-font: var(--veo-f-primary-font);
1687
+ }
1688
+ .veo-card--themed-form .veo-form-label,
1689
+ .veo-card--themed-form .veo-form-option,
1690
+ .veo-card--themed-form .veo-scale-label {
1691
+ color: var(--veo-f-secondary-color); font-weight: var(--veo-f-secondary-weight);
1692
+ font-family: var(--veo-f-secondary-font, var(--veo-font));
1693
+ }
1694
+ .veo-card--themed-form .veo-form-label,
1695
+ .veo-card--themed-form .veo-form-option {
1696
+ font-size: var(--veo-f-secondary-size); text-align: var(--veo-f-secondary-align);
1697
+ }
1698
+ .veo-card--themed-form .veo-form-input:focus { outline-color: var(--veo-f-accent); }
1699
+ .veo-card--themed-form .veo-form-option input { accent-color: var(--veo-f-accent); }
1700
+ .veo-card--themed-form .veo-nps-btn:hover { border-color: var(--veo-scale-color, var(--veo-f-accent)); }
1701
+ .veo-card--themed-form .veo-scale-active.veo-nps-btn {
1702
+ background: var(--veo-scale-color, var(--veo-f-accent)); color: var(--veo-f-on-accent);
1703
+ border-color: var(--veo-scale-color, var(--veo-f-accent));
1704
+ }
1638
1705
  .veo-guide-cta {
1639
1706
  background: var(--veo-cta-bg, var(--veo-primary));
1640
- color: var(--veo-cta-color, #fff); border: none;
1641
- padding: 9px 18px; border-radius: var(--veo-cta-radius, 8px);
1642
- font-size: var(--veo-cta-size, 14px); font-weight: 500; cursor: pointer;
1707
+ color: var(--veo-cta-color, #fff); border: var(--veo-cta-border, none);
1708
+ padding: var(--veo-cta-pad, 9px 18px); border-radius: var(--veo-cta-radius, 8px);
1709
+ font-size: var(--veo-cta-size, 14px); font-weight: var(--veo-cta-weight, 500); cursor: pointer;
1643
1710
  transition: opacity 120ms ease;
1644
1711
  }
1645
1712
  .veo-guide-cta:hover { opacity: 0.9; }
1646
1713
  /* Presets del boton (editor): secundario = contorno; enlace = solo texto. */
1647
1714
  .veo-guide-cta--secondary {
1648
- background: transparent; color: var(--veo-primary);
1649
- border: 1px solid var(--veo-primary);
1715
+ background: var(--veo-cta2-bg, transparent); color: var(--veo-cta2-color, var(--veo-primary));
1716
+ border: var(--veo-cta2-border, 1px solid var(--veo-primary));
1717
+ padding: var(--veo-cta2-pad, var(--veo-cta-pad, 9px 18px));
1718
+ border-radius: var(--veo-cta2-radius, var(--veo-cta-radius, 8px));
1719
+ font-size: var(--veo-cta2-size, var(--veo-cta-size, 14px));
1720
+ font-weight: var(--veo-cta2-weight, var(--veo-cta-weight, 500));
1721
+ }
1722
+ .veo-guide-cta--link {
1723
+ background: transparent; color: var(--veo-cta3-color, var(--veo-primary));
1724
+ border: var(--veo-cta3-border, none); padding: var(--veo-cta3-pad, 0);
1725
+ border-radius: var(--veo-cta3-radius, 0);
1726
+ font-size: var(--veo-cta3-size, var(--veo-cta-size, 14px));
1727
+ font-weight: var(--veo-cta3-weight, var(--veo-cta-weight, 500));
1650
1728
  }
1651
- .veo-guide-cta--link { background: transparent; color: var(--veo-primary); padding: 0; }
1652
1729
  .veo-guide-cta--link:hover { opacity: 1; text-decoration: underline; }
1653
1730
  .veo-guide-cta:active { transform: translateY(1px); }
1654
1731
  .veo-guide-close {
@@ -2030,8 +2107,18 @@ function positionArrow(arrowEl, placement, data) {
2030
2107
  if (data?.x != null) arrowEl.style.setProperty("left", `${data.x}px`);
2031
2108
  if (data?.y != null) arrowEl.style.setProperty("top", `${data.y}px`);
2032
2109
  arrowEl.style.setProperty(side, "-6px");
2110
+ const width = arrowEl.dataset.veoBorder;
2111
+ if (width) {
2112
+ for (const edge of ["top", "right", "bottom", "left"]) {
2113
+ arrowEl.style.setProperty(
2114
+ `border-${edge}-width`,
2115
+ OUTER_EDGES[side].includes(edge) ? `${width}px` : "0"
2116
+ );
2117
+ }
2118
+ arrowEl.style.zIndex = "1";
2119
+ }
2033
2120
  }
2034
- var STATIC_SIDE;
2121
+ var STATIC_SIDE, OUTER_EDGES;
2035
2122
  var init_floating_arrow = __esm({
2036
2123
  "src/plugins/guides/renderers/floating-arrow.ts"() {
2037
2124
  STATIC_SIDE = {
@@ -2040,6 +2127,12 @@ var init_floating_arrow = __esm({
2040
2127
  left: "right",
2041
2128
  right: "left"
2042
2129
  };
2130
+ OUTER_EDGES = {
2131
+ top: ["top", "left"],
2132
+ right: ["top", "right"],
2133
+ bottom: ["bottom", "right"],
2134
+ left: ["bottom", "left"]
2135
+ };
2043
2136
  }
2044
2137
  });
2045
2138
  function readBadgeConfig(step) {
@@ -3213,6 +3306,173 @@ var init_form_content = __esm({
3213
3306
  }
3214
3307
  });
3215
3308
 
3309
+ // src/plugins/guides/guide-theme.ts
3310
+ function channels(hex) {
3311
+ const h = hex.replace("#", "");
3312
+ const full = h.length === 3 ? [...h].map((x) => x + x).join("") : h.slice(0, 6);
3313
+ const ch = (i) => Number.parseInt(full.slice(i, i + 2), 16) || 0;
3314
+ return [ch(0), ch(2), ch(4)];
3315
+ }
3316
+ function boxShadow(shadow, hex) {
3317
+ if (shadow === "none") return "none";
3318
+ const spec = SHADOW_SPEC[shadow];
3319
+ const [r, g, b] = channels(hex);
3320
+ return `0 ${spec.y}px ${spec.blur}px rgba(${r}, ${g}, ${b}, ${spec.alpha})`;
3321
+ }
3322
+ function readableOn(hex) {
3323
+ const [r, g, b] = channels(hex);
3324
+ return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255 > 0.6 ? "#111827" : "#ffffff";
3325
+ }
3326
+ function themeModeFor(theme, design) {
3327
+ if (!theme || design.theme !== WORKSPACE_THEME_ID) return null;
3328
+ return (design.colorMode === "dark" ? theme.dark : theme.light) ?? null;
3329
+ }
3330
+ function themeRowJustify(mode, inline, preset) {
3331
+ const box = inline ? mode.embedded : mode.overlay;
3332
+ if (box.align === "distribute") return null;
3333
+ const button = preset === "secondary" ? mode.buttons?.secondary : preset === "link" ? mode.buttons?.tertiary : preset === void 0 || preset === null || preset === "primary" ? mode.buttons?.primary : void 0;
3334
+ return button ? JUSTIFY[button.align] ?? null : null;
3335
+ }
3336
+ function appFont(ref, doc) {
3337
+ const view = doc.defaultView;
3338
+ if (!view) return null;
3339
+ if (ref === "inherit") return view.getComputedStyle(doc.body).fontFamily || null;
3340
+ if (ref === "app-heading") {
3341
+ const heading = Array.from(doc.querySelectorAll("h1,h2,h3")).find(
3342
+ (el2) => el2.getClientRects().length > 0
3343
+ );
3344
+ return view.getComputedStyle(heading ?? doc.body).fontFamily || null;
3345
+ }
3346
+ return ref;
3347
+ }
3348
+ function themeContainerDesign(mode, inline, tooltip = false) {
3349
+ const box = inline ? mode.embedded : mode.overlay;
3350
+ const tip = tooltip ? mode.tooltip : void 0;
3351
+ const padding = tip?.padding ?? box.padding;
3352
+ const out = {
3353
+ // El embebido va en el flujo de la página: sin fondo propio.
3354
+ background: tip ? tip.background : inline ? "transparent" : box.background ?? mode.colors.background,
3355
+ color: mode.colors.text,
3356
+ paddingTop: padding.top,
3357
+ paddingRight: padding.right,
3358
+ paddingBottom: padding.bottom,
3359
+ paddingLeft: padding.left,
3360
+ borderColor: tip?.borderColor ?? box.borderColor,
3361
+ borderWidth: tip?.borderWidth ?? box.borderWidth,
3362
+ radius: tip?.borderRadius ?? box.borderRadius
3363
+ };
3364
+ const shadow = tip?.shadow ?? box.shadow;
3365
+ const shadowColor = tip?.shadowColor ?? box.shadowColor;
3366
+ if (shadow === "none") {
3367
+ Object.assign(out, { shadowColor: "transparent", shadowBlur: 0 });
3368
+ } else {
3369
+ const spec = SHADOW_SPEC[shadow];
3370
+ const [r, g, b] = channels(shadowColor);
3371
+ Object.assign(out, {
3372
+ shadowX: 0,
3373
+ shadowY: spec.y,
3374
+ shadowBlur: spec.blur,
3375
+ shadowSpread: 0,
3376
+ shadowColor: `rgba(${r}, ${g}, ${b}, ${spec.alpha})`
3377
+ });
3378
+ }
3379
+ return out;
3380
+ }
3381
+ function applyThemeVars(card, mode, inline, tooltip = false) {
3382
+ const doc = card.ownerDocument ?? document;
3383
+ const box = inline ? mode.embedded : mode.overlay;
3384
+ const set = (name, value) => {
3385
+ if (value) card.style.setProperty(name, value);
3386
+ };
3387
+ card.classList.add("veo-card--themed");
3388
+ set("--veo-primary", mode.colors.accent);
3389
+ set("--veo-cta-color", readableOn(mode.colors.accent));
3390
+ const primary = mode.buttons?.primary;
3391
+ const publish = (prefix, b) => {
3392
+ if (!b) return;
3393
+ const p = b.padding;
3394
+ set(`${prefix}-bg`, b.background);
3395
+ set(`${prefix}-color`, b.color);
3396
+ set(`${prefix}-pad`, `${p.top}px ${p.right}px ${p.bottom}px ${p.left}px`);
3397
+ set(`${prefix}-size`, `${b.fontSize}px`);
3398
+ set(`${prefix}-weight`, String(b.fontWeight));
3399
+ set(`${prefix}-radius`, `${b.borderRadius}px`);
3400
+ set(
3401
+ `${prefix}-border`,
3402
+ b.borderWidth > 0 ? `${b.borderWidth}px solid ${b.borderColor}` : "none"
3403
+ );
3404
+ };
3405
+ publish("--veo-cta", primary);
3406
+ publish("--veo-cta2", mode.buttons?.secondary);
3407
+ publish("--veo-cta3", mode.buttons?.tertiary);
3408
+ set("--veo-text", mode.colors.text);
3409
+ set("--veo-text-secondary", mode.colors.textMuted);
3410
+ set("--veo-font", appFont("inherit", doc));
3411
+ set(
3412
+ "--veo-actions-justify",
3413
+ box.align === "distribute" || !primary ? JUSTIFY[box.align] : JUSTIFY[primary.align]
3414
+ );
3415
+ const form = mode.form;
3416
+ if (form) {
3417
+ card.classList.add("veo-card--themed-form");
3418
+ const fp = form.padding;
3419
+ set("--veo-f-bg", form.background);
3420
+ set("--veo-f-accent", form.accent);
3421
+ set("--veo-f-on-accent", form.onAccent);
3422
+ set("--veo-f-pad", `${fp.top}px ${fp.right}px ${fp.bottom}px ${fp.left}px`);
3423
+ if (typeof form.borderWidth === "number" && form.borderColor) {
3424
+ set(
3425
+ "--veo-f-border",
3426
+ form.borderWidth > 0 ? `${form.borderWidth}px solid ${form.borderColor}` : "none"
3427
+ );
3428
+ }
3429
+ if (typeof form.borderRadius === "number") set("--veo-f-radius", `${form.borderRadius}px`);
3430
+ if (form.shadow && form.shadowColor) {
3431
+ set("--veo-f-shadow", boxShadow(form.shadow, form.shadowColor));
3432
+ }
3433
+ for (const [name, t] of [
3434
+ ["primary", form.text.primary],
3435
+ ["secondary", form.text.secondary]
3436
+ ]) {
3437
+ set(`--veo-f-${name}-color`, t.color);
3438
+ set(`--veo-f-${name}-size`, `${t.fontSize}px`);
3439
+ set(`--veo-f-${name}-weight`, String(t.fontWeight));
3440
+ set(`--veo-f-${name}-align`, t.align);
3441
+ set(`--veo-f-${name}-font`, appFont(t.fontFamily, doc));
3442
+ }
3443
+ }
3444
+ const tip = tooltip ? mode.tooltip?.text : void 0;
3445
+ const tokens = [
3446
+ ["title", tip?.primary ?? mode.text.title],
3447
+ ["subtitle", tip?.secondary ?? mode.text.subtitle],
3448
+ ["paragraph", tip?.secondary ?? mode.text.paragraph]
3449
+ ];
3450
+ for (const [name, t] of tokens) {
3451
+ set(`--veo-t-${name}-color`, t.color);
3452
+ set(`--veo-t-${name}-size`, `${t.fontSize}px`);
3453
+ set(`--veo-t-${name}-weight`, String(t.fontWeight));
3454
+ set(`--veo-t-${name}-align`, t.align);
3455
+ set(`--veo-t-${name}-font`, appFont(t.fontFamily, doc));
3456
+ }
3457
+ }
3458
+ var WORKSPACE_THEME_ID, SHADOW_SPEC, JUSTIFY;
3459
+ var init_guide_theme = __esm({
3460
+ "src/plugins/guides/guide-theme.ts"() {
3461
+ WORKSPACE_THEME_ID = "workspace";
3462
+ SHADOW_SPEC = {
3463
+ soft: { y: 4, blur: 14, alpha: 0.12 },
3464
+ medium: { y: 12, blur: 32, alpha: 0.22 },
3465
+ strong: { y: 24, blur: 64, alpha: 0.38 }
3466
+ };
3467
+ JUSTIFY = {
3468
+ left: "flex-start",
3469
+ center: "center",
3470
+ right: "flex-end",
3471
+ distribute: "space-between"
3472
+ };
3473
+ }
3474
+ });
3475
+
3216
3476
  // src/plugins/guides/layout.ts
3217
3477
  function groupLayout(blocks) {
3218
3478
  const rows = /* @__PURE__ */ new Map();
@@ -3470,13 +3730,68 @@ function markerOffset(anchor, marker, position, offset6) {
3470
3730
  }
3471
3731
  return { x: x + offset6.x, y: y + offset6.y };
3472
3732
  }
3733
+ function canNest(anchor) {
3734
+ const win = anchor.ownerDocument.defaultView;
3735
+ if (!win || !(anchor instanceof win.HTMLElement)) return false;
3736
+ if (NO_CHILDREN.has(anchor.tagName.toLowerCase())) return false;
3737
+ const cs = win.getComputedStyle(anchor);
3738
+ if (cs.display === "contents") return false;
3739
+ const tokens = `${cs.overflow} ${cs.overflowX} ${cs.overflowY}`.split(/\s+/).filter(Boolean);
3740
+ return tokens.every((t) => t === "visible");
3741
+ }
3473
3742
  function attachMarker(anchor, host, marker) {
3474
3743
  host.style.position = "absolute";
3475
3744
  host.style.zIndex = String(num(marker.zIndex, DEFAULT_MARKER_Z));
3476
3745
  host.style.margin = "0";
3746
+ const position = marker.position ?? "top-right";
3747
+ const offset6 = { x: num(marker.offsetX, 0), y: num(marker.offsetY, 0) };
3748
+ const markerSize = () => {
3749
+ const m = host.getBoundingClientRect();
3750
+ return { width: m.width, height: m.height };
3751
+ };
3752
+ if (canNest(anchor)) {
3753
+ const win = anchor.ownerDocument.defaultView;
3754
+ const prevPosition = anchor.style.position;
3755
+ const computed = win.getComputedStyle(anchor).position;
3756
+ if (!computed || computed === "static") anchor.style.position = "relative";
3757
+ anchor.appendChild(host);
3758
+ const swallowClick = (e) => {
3759
+ e.preventDefault();
3760
+ e.stopPropagation();
3761
+ };
3762
+ const swallow = (e) => e.stopPropagation();
3763
+ host.addEventListener("click", swallowClick);
3764
+ host.addEventListener("pointerdown", swallow);
3765
+ host.addEventListener("mousedown", swallow);
3766
+ const update2 = () => {
3767
+ const { x, y } = markerOffset(
3768
+ {
3769
+ left: -anchor.clientLeft,
3770
+ top: -anchor.clientTop,
3771
+ width: anchor.offsetWidth,
3772
+ height: anchor.offsetHeight
3773
+ },
3774
+ markerSize(),
3775
+ position,
3776
+ offset6
3777
+ );
3778
+ host.style.left = `${Math.round(x)}px`;
3779
+ host.style.top = `${Math.round(y)}px`;
3780
+ };
3781
+ update2();
3782
+ const stop2 = dom.autoUpdate(anchor, host, update2, { ancestorScroll: false, layoutShift: false });
3783
+ return () => {
3784
+ stop2();
3785
+ host.removeEventListener("click", swallowClick);
3786
+ host.removeEventListener("pointerdown", swallow);
3787
+ host.removeEventListener("mousedown", swallow);
3788
+ host.remove();
3789
+ anchor.style.position = prevPosition;
3790
+ };
3791
+ }
3792
+ anchor.ownerDocument.body.appendChild(host);
3477
3793
  const update = () => {
3478
3794
  const a = anchor.getBoundingClientRect();
3479
- const m = host.getBoundingClientRect();
3480
3795
  const { x, y } = markerOffset(
3481
3796
  {
3482
3797
  left: a.left + window.scrollX,
@@ -3484,17 +3799,21 @@ function attachMarker(anchor, host, marker) {
3484
3799
  width: a.width,
3485
3800
  height: a.height
3486
3801
  },
3487
- { width: m.width, height: m.height },
3488
- marker.position ?? "top-right",
3489
- { x: num(marker.offsetX, 0), y: num(marker.offsetY, 0) }
3802
+ markerSize(),
3803
+ position,
3804
+ offset6
3490
3805
  );
3491
3806
  host.style.left = `${Math.round(x)}px`;
3492
3807
  host.style.top = `${Math.round(y)}px`;
3493
3808
  };
3494
3809
  update();
3495
- return dom.autoUpdate(anchor, host, update);
3810
+ const stop = dom.autoUpdate(anchor, host, update);
3811
+ return () => {
3812
+ stop();
3813
+ host.remove();
3814
+ };
3496
3815
  }
3497
- var INFO_GLYPH, QUESTION_GLYPH, STAR_GLYPH, CIRCLE, TARGET_GLYPH, BADGE_ICONS, DEFAULT_MARKER_SIZE, DEFAULT_MARKER_Z, SIDE_GAP;
3816
+ var INFO_GLYPH, QUESTION_GLYPH, STAR_GLYPH, CIRCLE, TARGET_GLYPH, BADGE_ICONS, DEFAULT_MARKER_SIZE, DEFAULT_MARKER_Z, SIDE_GAP, NO_CHILDREN;
3498
3817
  var init_badge_marker = __esm({
3499
3818
  "src/plugins/guides/renderers/badge-marker.ts"() {
3500
3819
  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";
@@ -3517,6 +3836,21 @@ var init_badge_marker = __esm({
3517
3836
  DEFAULT_MARKER_SIZE = 14;
3518
3837
  DEFAULT_MARKER_Z = 19e3;
3519
3838
  SIDE_GAP = 8;
3839
+ NO_CHILDREN = /* @__PURE__ */ new Set([
3840
+ "img",
3841
+ "input",
3842
+ "textarea",
3843
+ "select",
3844
+ "iframe",
3845
+ "video",
3846
+ "audio",
3847
+ "canvas",
3848
+ "br",
3849
+ "hr",
3850
+ "svg",
3851
+ "object",
3852
+ "embed"
3853
+ ]);
3520
3854
  }
3521
3855
  });
3522
3856
  function applyRowStyle(rowEl, style) {
@@ -3599,6 +3933,7 @@ function makeCaret(doc, design) {
3599
3933
  if (typeof design.borderWidth === "number" && design.borderWidth > 0) {
3600
3934
  const bc = typeof design.borderColor === "string" ? design.borderColor : "#bbbbbb";
3601
3935
  s.border = `${design.borderWidth}px solid ${bc}`;
3936
+ caret.dataset.veoBorder = String(design.borderWidth);
3602
3937
  }
3603
3938
  return caret;
3604
3939
  }
@@ -3691,6 +4026,7 @@ var init_composite_renderer = __esm({
3691
4026
  init_checklist_block();
3692
4027
  init_code_block();
3693
4028
  init_form_content();
4029
+ init_guide_theme();
3694
4030
  init_inline_host();
3695
4031
  init_layout();
3696
4032
  init_rich_text();
@@ -3712,6 +4048,11 @@ var init_composite_renderer = __esm({
3712
4048
  bottom: "flex-end"
3713
4049
  };
3714
4050
  CompositeRenderer = class extends BaseRenderer {
4051
+ constructor() {
4052
+ super(...arguments);
4053
+ /** Alineación de filas de botones según el tema del workspace (null = sin tema). */
4054
+ this.themeRowJustify = null;
4055
+ }
3715
4056
  async render(context) {
3716
4057
  const idx = context.nav?.stepIndex ?? 0;
3717
4058
  const step = context.guide.guideSteps[idx];
@@ -3742,7 +4083,13 @@ var init_composite_renderer = __esm({
3742
4083
  if (typeof design.zIndex === "number") host.style.zIndex = String(design.zIndex);
3743
4084
  const card = doc.createElement("div");
3744
4085
  card.className = "veo-card";
3745
- applyCompositeDesign(card, design);
4086
+ const themeMode = themeModeFor(context.guide.theme, design);
4087
+ const inline = step.presentation === "inline";
4088
+ const tooltipLike = !inline && (badgeMode || step.position === "relative-to-element");
4089
+ if (themeMode) applyThemeVars(card, themeMode, inline, tooltipLike);
4090
+ this.themeRowJustify = themeMode ? (preset) => themeRowJustify(themeMode, inline, preset) : null;
4091
+ const cardDesign = themeMode ? { ...themeContainerDesign(themeMode, inline, tooltipLike), ...design } : design;
4092
+ applyCompositeDesign(card, cardDesign);
3746
4093
  if (typeof design.ariaLabel === "string" && design.ariaLabel.trim()) {
3747
4094
  card.setAttribute("role", "dialog");
3748
4095
  card.setAttribute("aria-label", design.ariaLabel.trim());
@@ -3813,7 +4160,7 @@ var init_composite_renderer = __esm({
3813
4160
  });
3814
4161
  }
3815
4162
  card.appendChild(closeBtn);
3816
- reserveCloseSpace(card, blocks, closePos, design);
4163
+ reserveCloseSpace(card, blocks, closePos, cardDesign);
3817
4164
  }
3818
4165
  if (badgeMode && anchor) {
3819
4166
  closeTooltip = this.renderBadgeMode(
@@ -3826,7 +4173,8 @@ var init_composite_renderer = __esm({
3826
4173
  doc,
3827
4174
  context,
3828
4175
  idx,
3829
- design
4176
+ // Con el tema ya fundido: la flecha del tooltip toma su fondo y borde.
4177
+ cardDesign
3830
4178
  );
3831
4179
  return;
3832
4180
  }
@@ -3849,7 +4197,7 @@ var init_composite_renderer = __esm({
3849
4197
  }
3850
4198
  const wrapper = doc.createElement("div");
3851
4199
  wrapper.className = "veo-composite-anchored";
3852
- const caretEl = design.caret === false ? null : makeCaret(doc, design);
4200
+ const caretEl = design.caret === false ? null : makeCaret(doc, cardDesign);
3853
4201
  if (caretEl) wrapper.appendChild(caretEl);
3854
4202
  wrapper.appendChild(card);
3855
4203
  root.appendChild(wrapper);
@@ -3909,6 +4257,7 @@ var init_composite_renderer = __esm({
3909
4257
  const row = doc.createElement("div");
3910
4258
  row.className = "veo-guide-actions";
3911
4259
  let rowAlign;
4260
+ const firstPreset = b.style?.preset;
3912
4261
  while (i < cell.length && cell[i]?.block.type === "button") {
3913
4262
  const { block: bb, index: bi } = cell[i];
3914
4263
  if (!rowAlign) {
@@ -3919,6 +4268,10 @@ var init_composite_renderer = __esm({
3919
4268
  i++;
3920
4269
  }
3921
4270
  if (rowAlign) row.style.justifyContent = ROW_ALIGN[rowAlign] ?? "flex-end";
4271
+ else {
4272
+ const themed = this.themeRowJustify?.(firstPreset);
4273
+ if (themed) row.style.justifyContent = themed;
4274
+ }
3922
4275
  container.appendChild(row);
3923
4276
  continue;
3924
4277
  }
@@ -4205,9 +4558,7 @@ var init_composite_renderer = __esm({
4205
4558
  root.appendChild(wrapper);
4206
4559
  if (marker) {
4207
4560
  host.style.display = "inline-block";
4208
- (anchor.ownerDocument ?? document).body.appendChild(host);
4209
4561
  this.registerCleanup(attachMarker(anchor, host, marker));
4210
- this.registerCleanup(() => host.remove());
4211
4562
  } else {
4212
4563
  host.style.display = "inline-block";
4213
4564
  const pos = inlinePosOf(step.inlinePosition);
@@ -4887,7 +5238,8 @@ function normalize(input) {
4887
5238
  guideSteps: input.guideSteps,
4888
5239
  activationRules,
4889
5240
  displayFrequency: "always",
4890
- displayPriority: 0
5241
+ displayPriority: 0,
5242
+ ...input.theme ? { theme: input.theme } : {}
4891
5243
  };
4892
5244
  }
4893
5245
  function createSingleStepRenderer(type) {
@@ -5669,9 +6021,9 @@ function injectBuilderPanel(opts) {
5669
6021
  card.className = "veo-panel";
5670
6022
  let topOffset = opts.topOffset ?? 0;
5671
6023
  let left = 40;
5672
- let top = topOffset + MARGIN;
6024
+ let top2 = topOffset + MARGIN;
5673
6025
  card.style.left = `${left}px`;
5674
- card.style.top = `${top}px`;
6026
+ card.style.top = `${top2}px`;
5675
6027
  card.style.width = `${PANEL_W}px`;
5676
6028
  card.style.height = `${PANEL_H}px`;
5677
6029
  const header = document.createElement("div");
@@ -5715,7 +6067,7 @@ function injectBuilderPanel(opts) {
5715
6067
  if (!compactRestore) compactRestore = { w: card.offsetWidth, h: card.offsetHeight };
5716
6068
  card.style.minHeight = "0";
5717
6069
  card.style.width = `${clamp3(Math.round(size.width), MIN_W, window.innerWidth - MARGIN * 2)}px`;
5718
- card.style.height = `${clamp3(Math.round(size.height) + HEADER_H, HEADER_H + 80, window.innerHeight - top - MARGIN)}px`;
6070
+ card.style.height = `${clamp3(Math.round(size.height) + HEADER_H, HEADER_H + 80, window.innerHeight - top2 - MARGIN)}px`;
5719
6071
  } else if (compactRestore) {
5720
6072
  card.style.minHeight = "";
5721
6073
  card.style.width = `${compactRestore.w}px`;
@@ -5726,19 +6078,19 @@ function injectBuilderPanel(opts) {
5726
6078
  maximize.addEventListener("click", () => {
5727
6079
  maximized = !maximized;
5728
6080
  if (maximized) {
5729
- restore = { left, top, w: card.offsetWidth, h: card.offsetHeight };
6081
+ restore = { left, top: top2, w: card.offsetWidth, h: card.offsetHeight };
5730
6082
  left = MARGIN;
5731
- top = topOffset + MARGIN;
6083
+ top2 = topOffset + MARGIN;
5732
6084
  card.style.left = `${left}px`;
5733
- card.style.top = `${top}px`;
6085
+ card.style.top = `${top2}px`;
5734
6086
  card.style.width = `${Math.max(MIN_W, window.innerWidth - MARGIN * 2)}px`;
5735
- card.style.height = `${Math.max(MIN_H, window.innerHeight - top - MARGIN)}px`;
6087
+ card.style.height = `${Math.max(MIN_H, window.innerHeight - top2 - MARGIN)}px`;
5736
6088
  maximize.title = "Restaurar";
5737
6089
  } else if (restore) {
5738
6090
  left = restore.left;
5739
- top = restore.top;
6091
+ top2 = restore.top;
5740
6092
  card.style.left = `${left}px`;
5741
- card.style.top = `${top}px`;
6093
+ card.style.top = `${top2}px`;
5742
6094
  card.style.width = `${restore.w}px`;
5743
6095
  card.style.height = `${restore.h}px`;
5744
6096
  maximize.title = "Maximizar";
@@ -5774,13 +6126,13 @@ function injectBuilderPanel(opts) {
5774
6126
  const sx = e.clientX;
5775
6127
  const sy = e.clientY;
5776
6128
  const ol = left;
5777
- const ot = top;
6129
+ const ot = top2;
5778
6130
  iframe.style.pointerEvents = "none";
5779
6131
  const move = (ev) => {
5780
6132
  left = clamp3(ol + (ev.clientX - sx), 0, Math.max(0, window.innerWidth - card.offsetWidth));
5781
- top = clamp3(ot + (ev.clientY - sy), topOffset, Math.max(topOffset, window.innerHeight - 40));
6133
+ top2 = clamp3(ot + (ev.clientY - sy), topOffset, Math.max(topOffset, window.innerHeight - 40));
5782
6134
  card.style.left = `${left}px`;
5783
- card.style.top = `${top}px`;
6135
+ card.style.top = `${top2}px`;
5784
6136
  };
5785
6137
  const up = () => {
5786
6138
  window.removeEventListener("mousemove", move);
@@ -5826,9 +6178,9 @@ function injectBuilderPanel(opts) {
5826
6178
  isVisible: () => visible,
5827
6179
  setTopOffset: (px) => {
5828
6180
  topOffset = px;
5829
- if (top < topOffset + MARGIN) {
5830
- top = topOffset + MARGIN;
5831
- card.style.top = `${top}px`;
6181
+ if (top2 < topOffset + MARGIN) {
6182
+ top2 = topOffset + MARGIN;
6183
+ card.style.top = `${top2}px`;
5832
6184
  }
5833
6185
  }
5834
6186
  };
@@ -6009,9 +6361,9 @@ function attachModal(opts, shadow, card) {
6009
6361
  const span = window.innerWidth - w;
6010
6362
  return span <= 0 ? 0.5 : round3(clamp4(left / span, 0, 1));
6011
6363
  };
6012
- const posYFor = (top, h) => {
6364
+ const posYFor = (top2, h) => {
6013
6365
  const span = window.innerHeight - h;
6014
- return span <= 0 ? 0.5 : round3(clamp4(top / span, 0, 1));
6366
+ return span <= 0 ? 0.5 : round3(clamp4(top2 / span, 0, 1));
6015
6367
  };
6016
6368
  const onCardDown = (e) => {
6017
6369
  if (isInteractive(e.target)) return;
@@ -7806,6 +8158,471 @@ var init_builder = __esm({
7806
8158
  }
7807
8159
  });
7808
8160
 
8161
+ // src/plugins/theme-detect/color.ts
8162
+ function parseRgb(value) {
8163
+ const m = RGB_RX.exec(value.trim());
8164
+ if (!m) return null;
8165
+ const a = m[4] === void 0 ? 1 : m[4].endsWith("%") ? Number.parseFloat(m[4]) / 100 : +m[4];
8166
+ return [+m[1], +m[2], +m[3], a];
8167
+ }
8168
+ function toCssColor(raw) {
8169
+ const v = raw.trim();
8170
+ const parts = v.split(/[\s,/]+/).filter(Boolean);
8171
+ const bare = parts.length >= 3 && parts.every((p) => /^-?[\d.]+(%|deg)?$/.test(p));
8172
+ if (!bare) return v;
8173
+ return v.includes("%") ? `hsl(${v})` : `rgb(${v})`;
8174
+ }
8175
+ function looksLikeColor(raw) {
8176
+ const v = raw.trim();
8177
+ if (!v || v.length > 64) return false;
8178
+ return /^(#|rgb|hsl|hwb|lab|lch|oklab|oklch|color\(|color-mix|light-dark)/i.test(v) || toCssColor(v) !== v;
8179
+ }
8180
+ function flatten(c, bg) {
8181
+ if (c[3] >= 1) return c;
8182
+ const mix = (i) => Math.round(c[i] * c[3] + bg[i] * (1 - c[3]));
8183
+ return [mix(0), mix(1), mix(2), 1];
8184
+ }
8185
+ function toHex(c) {
8186
+ const h = (n) => Math.round(n).toString(16).padStart(2, "0");
8187
+ return `#${h(c[0])}${h(c[1])}${h(c[2])}`.toUpperCase();
8188
+ }
8189
+ function luminance(c) {
8190
+ return (0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2]) / 255;
8191
+ }
8192
+ function saturation(c) {
8193
+ const mx = Math.max(c[0], c[1], c[2]) / 255;
8194
+ const mn = Math.min(c[0], c[1], c[2]) / 255;
8195
+ const l = (mx + mn) / 2;
8196
+ return mx === mn ? 0 : (mx - mn) / (1 - Math.abs(2 * l - 1));
8197
+ }
8198
+ function createColorResolver(doc) {
8199
+ const probe = doc.createElement("span");
8200
+ probe.style.display = "none";
8201
+ doc.body.appendChild(probe);
8202
+ const view = doc.defaultView;
8203
+ let ctx;
8204
+ const cache = /* @__PURE__ */ new Map();
8205
+ const viaCanvas = (computed) => {
8206
+ if (ctx === void 0) {
8207
+ try {
8208
+ const canvas = doc.createElement("canvas");
8209
+ canvas.width = 1;
8210
+ canvas.height = 1;
8211
+ ctx = canvas.getContext("2d", { willReadFrequently: true });
8212
+ } catch {
8213
+ ctx = null;
8214
+ }
8215
+ }
8216
+ if (!ctx) return null;
8217
+ ctx.clearRect(0, 0, 1, 1);
8218
+ ctx.fillStyle = "#000";
8219
+ ctx.fillStyle = computed;
8220
+ ctx.fillRect(0, 0, 1, 1);
8221
+ const d = ctx.getImageData(0, 0, 1, 1).data;
8222
+ return [d[0] ?? 0, d[1] ?? 0, d[2] ?? 0, (d[3] ?? 255) / 255];
8223
+ };
8224
+ return {
8225
+ resolve(raw) {
8226
+ const key = raw.trim();
8227
+ if (!key || !view) return null;
8228
+ const hit = cache.get(key);
8229
+ if (hit !== void 0) return hit;
8230
+ let out = null;
8231
+ probe.style.color = "";
8232
+ probe.style.color = toCssColor(key);
8233
+ if (probe.style.color) {
8234
+ const computed = view.getComputedStyle(probe).color;
8235
+ out = parseRgb(computed) ?? viaCanvas(computed);
8236
+ }
8237
+ cache.set(key, out);
8238
+ return out;
8239
+ },
8240
+ dispose() {
8241
+ probe.remove();
8242
+ }
8243
+ };
8244
+ }
8245
+ var RGB_RX;
8246
+ var init_color = __esm({
8247
+ "src/plugins/theme-detect/color.ts"() {
8248
+ RGB_RX = /^rgba?\(\s*([\d.]+)[,\s]+([\d.]+)[,\s]+([\d.]+)(?:\s*[,/]\s*([\d.]+%?))?\s*\)$/;
8249
+ }
8250
+ });
8251
+
8252
+ // src/plugins/theme-detect/fonts.ts
8253
+ function cleanFamily(name) {
8254
+ return name.trim().replace(/^["']|["']$/g, "");
8255
+ }
8256
+ function primaryFamily(stack) {
8257
+ const families = stack.split(",").map(cleanFamily).filter(Boolean);
8258
+ return families.find((f) => !GENERIC.test(f) && !/fallback/i.test(f)) ?? families[0] ?? "";
8259
+ }
8260
+ function prettyFamily(family) {
8261
+ return family.replace(/^__/, "").replace(/_Fallback_[0-9a-f]+$/i, " Fallback").replace(/_[0-9a-f]{6,}$/i, "").replace(/_/g, " ").trim();
8262
+ }
8263
+ function describeFont(doc, el2, faces) {
8264
+ const view = doc.defaultView;
8265
+ if (!el2 || !view) return void 0;
8266
+ const family = primaryFamily(view.getComputedStyle(el2).fontFamily);
8267
+ if (!family) return void 0;
8268
+ const font = {
8269
+ name: prettyFamily(family).slice(0, 80),
8270
+ family: family.slice(0, 120)
8271
+ };
8272
+ const weights = /* @__PURE__ */ new Set();
8273
+ try {
8274
+ for (const face of Array.from(doc.fonts ?? [])) {
8275
+ if (cleanFamily(face.family) === family && face.status === "loaded") weights.add(face.weight);
8276
+ }
8277
+ } catch {
8278
+ }
8279
+ if (weights.size > 0) font.weights = [...weights].sort().join(" ").slice(0, 80);
8280
+ const own = faces.filter((f) => f.family === family);
8281
+ const url = own.find((f) => f.url)?.url ?? null;
8282
+ let origin = null;
8283
+ try {
8284
+ origin = url ? new URL(url, doc.baseURI).origin : null;
8285
+ } catch {
8286
+ origin = null;
8287
+ }
8288
+ const links = Array.from(doc.querySelectorAll("link[href]")).map((l) => l.href);
8289
+ if (origin) {
8290
+ font.hosting = origin.includes("fonts.gstatic.com") ? "google" : origin === view.location.origin ? "self" : "external";
8291
+ } else if (own.length > 0) {
8292
+ font.hosting = "self";
8293
+ } else if (links.some((h) => h.includes("fonts.googleapis.com"))) {
8294
+ font.hosting = "google";
8295
+ } else if (links.some((h) => h.includes("use.typekit.net"))) {
8296
+ font.hosting = "adobe";
8297
+ } else {
8298
+ font.hosting = "system";
8299
+ }
8300
+ return font;
8301
+ }
8302
+ var GENERIC;
8303
+ var init_fonts = __esm({
8304
+ "src/plugins/theme-detect/fonts.ts"() {
8305
+ GENERIC = /^(serif|sans-serif|monospace|cursive|fantasy|system-ui|ui-sans-serif|ui-serif|ui-monospace|ui-rounded|emoji|math|fangsong|inherit|initial|-apple-system|BlinkMacSystemFont|Segoe UI|Roboto|Helvetica|Helvetica Neue|Arial|Noto Sans|Apple Color Emoji|Segoe UI Emoji|Segoe UI Symbol|Noto Color Emoji)$/i;
8306
+ }
8307
+ });
8308
+
8309
+ // src/plugins/theme-detect/detect-theme.ts
8310
+ var detect_theme_exports = {};
8311
+ __export(detect_theme_exports, {
8312
+ detectTheme: () => detectTheme,
8313
+ snapshotHash: () => snapshotHash
8314
+ });
8315
+ function scanSheets(doc) {
8316
+ const scan = {
8317
+ names: /* @__PURE__ */ new Set(),
8318
+ byMode: { light: /* @__PURE__ */ new Map(), dark: /* @__PURE__ */ new Map() },
8319
+ faces: []
8320
+ };
8321
+ const walk = (rules, inDarkMedia) => {
8322
+ for (const rule of Array.from(rules)) {
8323
+ const style = rule.style;
8324
+ const selector = rule.selectorText;
8325
+ if (rule.constructor.name === "CSSFontFaceRule" && style) {
8326
+ const src = style.getPropertyValue("src");
8327
+ const url = /url\(["']?([^"')]+)["']?\)/.exec(src)?.[1] ?? null;
8328
+ scan.faces.push({ family: cleanFamily(style.getPropertyValue("font-family")), url });
8329
+ } else if (style && typeof selector === "string") {
8330
+ const selectors = selector.split(",").map((s) => s.trim());
8331
+ const mode = selectors.some((s) => DARK_SELECTOR.test(s)) ? "dark" : selectors.some((s) => LIGHT_SELECTOR.test(s)) ? inDarkMedia ? "dark" : "light" : null;
8332
+ for (const prop of Array.from(style)) {
8333
+ if (!prop.startsWith("--")) continue;
8334
+ scan.names.add(prop);
8335
+ if (mode) scan.byMode[mode].set(prop, style.getPropertyValue(prop).trim());
8336
+ }
8337
+ }
8338
+ const nested = rule.cssRules;
8339
+ if (nested) {
8340
+ const media = rule.conditionText ?? "";
8341
+ walk(nested, inDarkMedia || /prefers-color-scheme:\s*dark/.test(media));
8342
+ }
8343
+ }
8344
+ };
8345
+ for (const sheet of Array.from(doc.styleSheets)) {
8346
+ try {
8347
+ walk(sheet.cssRules, false);
8348
+ } catch {
8349
+ }
8350
+ }
8351
+ return scan;
8352
+ }
8353
+ function top(counts) {
8354
+ let best = null;
8355
+ let max = 0;
8356
+ for (const [hex, n] of counts) {
8357
+ if (n > max) {
8358
+ max = n;
8359
+ best = hex;
8360
+ }
8361
+ }
8362
+ return best;
8363
+ }
8364
+ function detectTheme(doc = document) {
8365
+ const view = doc.defaultView;
8366
+ const body = doc.body;
8367
+ if (!view || !body) return null;
8368
+ const html = doc.documentElement;
8369
+ const colors = createColorResolver(doc);
8370
+ const modeProbe = doc.createElement("div");
8371
+ try {
8372
+ const bodyCs = view.getComputedStyle(body);
8373
+ let bg = [255, 255, 255, 1];
8374
+ for (const el2 of [body, html]) {
8375
+ const c = colors.resolve(view.getComputedStyle(el2).backgroundColor);
8376
+ if (c && c[3] > 0) {
8377
+ bg = flatten(c, [255, 255, 255, 1]);
8378
+ break;
8379
+ }
8380
+ }
8381
+ const text = flatten(colors.resolve(bodyCs.color) ?? [0, 0, 0, 1], bg);
8382
+ const textHex = toHex(text);
8383
+ const muted = /* @__PURE__ */ new Map();
8384
+ const border = /* @__PURE__ */ new Map();
8385
+ const accent = /* @__PURE__ */ new Map();
8386
+ const link = /* @__PURE__ */ new Map();
8387
+ const bump = (m, k) => {
8388
+ m.set(k, (m.get(k) ?? 0) + 1);
8389
+ };
8390
+ const contrast = Math.abs(luminance(text) - luminance(bg));
8391
+ const elements = Array.from(body.querySelectorAll("*")).slice(0, MAX_ELEMENTS);
8392
+ for (const el2 of elements) {
8393
+ if (el2.getClientRects().length === 0) continue;
8394
+ const cs = view.getComputedStyle(el2);
8395
+ const hasText = Array.from(el2.childNodes).some(
8396
+ (n) => n.nodeType === 3 && (n.textContent ?? "").trim() !== ""
8397
+ );
8398
+ if (hasText) {
8399
+ const c = colors.resolve(cs.color);
8400
+ if (c && c[3] > 0) {
8401
+ const flat = flatten(c, bg);
8402
+ const hex = toHex(flat);
8403
+ const softer = Math.abs(luminance(flat) - luminance(bg)) < contrast;
8404
+ if (hex !== textHex && saturation(flat) < 0.3 && softer) bump(muted, hex);
8405
+ if (el2.closest("a") && saturation(flat) >= 0.3) bump(link, hex);
8406
+ }
8407
+ }
8408
+ if (Number.parseFloat(cs.borderTopWidth) > 0 && cs.borderTopStyle !== "none") {
8409
+ const c = colors.resolve(cs.borderTopColor);
8410
+ if (c && c[3] > 0) {
8411
+ const flat = flatten(c, bg);
8412
+ if (saturation(flat) < 0.3 && toHex(flat) !== textHex) bump(border, toHex(flat));
8413
+ }
8414
+ }
8415
+ if (el2.matches('button,[role="button"],input[type="submit"],a[class*="btn"],a[class*="button"]')) {
8416
+ const c = colors.resolve(cs.backgroundColor);
8417
+ if (c && c[3] > 0.5 && saturation(c) >= 0.3) bump(accent, toHex(c));
8418
+ }
8419
+ }
8420
+ const meta = doc.querySelector('meta[name="theme-color"]');
8421
+ const metaColor = meta ? colors.resolve(meta.content) : null;
8422
+ const sampled = {
8423
+ background: toHex(bg),
8424
+ text: textHex
8425
+ };
8426
+ const sampledAccent = top(accent) ?? top(link) ?? (metaColor ? toHex(metaColor) : null);
8427
+ if (sampledAccent) sampled.accent = sampledAccent;
8428
+ const sampledMuted = top(muted);
8429
+ if (sampledMuted) sampled.textMuted = sampledMuted;
8430
+ const sampledBorder = top(border);
8431
+ if (sampledBorder) sampled.border = sampledBorder;
8432
+ const hints = [
8433
+ html.className,
8434
+ html.dataset.theme,
8435
+ html.dataset.mode,
8436
+ body.className,
8437
+ body.dataset.theme
8438
+ ].join(" ").toLowerCase();
8439
+ const mode = /\bdark\b/.test(hints) ? "dark" : /\blight\b/.test(hints) ? "light" : view.getComputedStyle(html).colorScheme === "dark" || luminance(bg) < 0.4 ? "dark" : "light";
8440
+ const other = mode === "dark" ? "light" : "dark";
8441
+ modeProbe.className = other;
8442
+ modeProbe.dataset.theme = other;
8443
+ modeProbe.dataset.mode = other;
8444
+ modeProbe.style.cssText = `display:none;color-scheme:${other}`;
8445
+ body.appendChild(modeProbe);
8446
+ const probeCs = view.getComputedStyle(modeProbe);
8447
+ const hexOf = (raw, over) => {
8448
+ if (!looksLikeColor(raw)) return null;
8449
+ const c = colors.resolve(raw);
8450
+ if (!c || c[3] === 0) return null;
8451
+ if (c[3] > 0.98) return toHex(c);
8452
+ return over ? toHex(flatten(c, over)) : null;
8453
+ };
8454
+ const scan = scanSheets(doc);
8455
+ const roleVar = {};
8456
+ const current = {};
8457
+ for (const role of ROLES) {
8458
+ for (const name of KNOWN_VARS[role]) {
8459
+ const hex = hexOf(bodyCs.getPropertyValue(name), bg);
8460
+ if (hex) {
8461
+ roleVar[role] = { name, confidence: 3 };
8462
+ current[role] = { hex, confidence: 3 };
8463
+ break;
8464
+ }
8465
+ }
8466
+ }
8467
+ const missing = ROLES.filter((r) => !roleVar[r] && sampled[r]);
8468
+ if (missing.length > 0) {
8469
+ const candidates = [];
8470
+ for (const name of Array.from(scan.names).slice(0, MAX_VARS)) {
8471
+ const cur = hexOf(bodyCs.getPropertyValue(name));
8472
+ if (cur) candidates.push({ name, cur, oth: hexOf(probeCs.getPropertyValue(name)) });
8473
+ }
8474
+ for (const role of missing) {
8475
+ let best = null;
8476
+ for (const c of candidates) {
8477
+ if (c.cur !== sampled[role]) continue;
8478
+ const score = (ROLE_NAME[role].test(c.name) ? 2 : 0) + (c.oth && c.oth !== c.cur ? 2 : 0) - (PALETTE_NAME.test(c.name) ? 1 : 0);
8479
+ if (!best || score > best.score) best = { name: c.name, score };
8480
+ }
8481
+ if (best && best.score >= 2) roleVar[role] = { name: best.name, confidence: 2 };
8482
+ }
8483
+ }
8484
+ for (const role of ROLES) {
8485
+ const hex = sampled[role];
8486
+ if (!current[role] && hex) current[role] = { hex, confidence: roleVar[role] ? 2 : 1 };
8487
+ }
8488
+ const otherPalette = {};
8489
+ let differs = false;
8490
+ const otherOrder = ["background", "accent", "text", "textMuted", "border"];
8491
+ let otherBg = null;
8492
+ const noteBg = (role, hex) => {
8493
+ if (role === "background") otherBg = colors.resolve(hex);
8494
+ };
8495
+ for (const role of otherOrder) {
8496
+ const ref = roleVar[role];
8497
+ if (!ref) continue;
8498
+ const hex = hexOf(probeCs.getPropertyValue(ref.name), otherBg);
8499
+ if (!hex) continue;
8500
+ noteBg(role, hex);
8501
+ otherPalette[role] = { hex, confidence: ref.confidence };
8502
+ if (hex !== current[role]?.hex) differs = true;
8503
+ }
8504
+ if (!differs) {
8505
+ for (const role of ROLES) delete otherPalette[role];
8506
+ otherBg = null;
8507
+ const sheet = scan.byMode[other];
8508
+ const resolveVar = (value, depth = 0) => depth > 6 ? value : value.replace(
8509
+ /var\(\s*(--[\w-]+)\s*(?:,\s*([^()]*))?\)/g,
8510
+ (_m, name, fb) => resolveVar(
8511
+ sheet.get(name) ?? scan.byMode.light.get(name) ?? (bodyCs.getPropertyValue(name).trim() || fb || ""),
8512
+ depth + 1
8513
+ )
8514
+ );
8515
+ for (const role of otherOrder) {
8516
+ const ref = roleVar[role];
8517
+ const raw = ref ? sheet.get(ref.name) : void 0;
8518
+ const hex = raw ? hexOf(resolveVar(raw), otherBg) : null;
8519
+ if (!hex) continue;
8520
+ noteBg(role, hex);
8521
+ otherPalette[role] = { hex, confidence: 2 };
8522
+ if (hex !== current[role]?.hex) differs = true;
8523
+ }
8524
+ }
8525
+ const palettes = { [mode]: current };
8526
+ if (differs) palettes[other] = otherPalette;
8527
+ const firstVisible = (selector) => Array.from(doc.querySelectorAll(selector)).find(
8528
+ (el2) => el2.getClientRects().length > 0 && (el2.textContent ?? "").trim() !== ""
8529
+ ) ?? null;
8530
+ const bodyFont = describeFont(doc, body, scan.faces);
8531
+ const headingFont = describeFont(doc, firstVisible("h1,h2,h3"), scan.faces);
8532
+ const fonts = {};
8533
+ if (bodyFont) fonts.body = bodyFont;
8534
+ if (headingFont) fonts.heading = headingFont;
8535
+ const snapshot = { host: view.location.host, mode, palettes };
8536
+ const appName = doc.title.trim().slice(0, 120);
8537
+ if (appName) snapshot.appName = appName;
8538
+ if (fonts.body || fonts.heading) snapshot.fonts = fonts;
8539
+ return snapshot;
8540
+ } finally {
8541
+ colors.dispose();
8542
+ modeProbe.remove();
8543
+ }
8544
+ }
8545
+ function snapshotHash(snapshot) {
8546
+ const colorsOf = (p) => ROLES.map((r) => {
8547
+ const c = p?.[r];
8548
+ return c ? `${c.hex}${c.confidence}` : "-";
8549
+ }).join("");
8550
+ return [
8551
+ snapshot.host,
8552
+ colorsOf(snapshot.palettes.light),
8553
+ colorsOf(snapshot.palettes.dark),
8554
+ snapshot.fonts?.body?.family ?? "",
8555
+ snapshot.fonts?.heading?.family ?? ""
8556
+ ].join("|");
8557
+ }
8558
+ var ROLES, KNOWN_VARS, ROLE_NAME, PALETTE_NAME, DARK_SELECTOR, LIGHT_SELECTOR, MAX_ELEMENTS, MAX_VARS;
8559
+ var init_detect_theme = __esm({
8560
+ "src/plugins/theme-detect/detect-theme.ts"() {
8561
+ init_color();
8562
+ init_fonts();
8563
+ ROLES = ["accent", "text", "textMuted", "background", "border"];
8564
+ KNOWN_VARS = {
8565
+ accent: [
8566
+ "--primary",
8567
+ "--color-primary",
8568
+ "--brand",
8569
+ "--color-brand",
8570
+ "--brand-primary",
8571
+ "--mui-palette-primary-main",
8572
+ "--bs-primary",
8573
+ "--ant-color-primary",
8574
+ "--chakra-colors-brand-500",
8575
+ "--accent-color",
8576
+ "--accent"
8577
+ ],
8578
+ text: [
8579
+ "--foreground",
8580
+ "--color-foreground",
8581
+ "--text",
8582
+ "--color-text",
8583
+ "--text-primary",
8584
+ "--bs-body-color",
8585
+ "--mui-palette-text-primary"
8586
+ ],
8587
+ textMuted: [
8588
+ "--muted-foreground",
8589
+ "--color-muted-foreground",
8590
+ "--text-muted",
8591
+ "--text-secondary",
8592
+ "--bs-secondary-color",
8593
+ "--mui-palette-text-secondary"
8594
+ ],
8595
+ background: [
8596
+ "--background",
8597
+ "--color-background",
8598
+ "--bg",
8599
+ "--color-bg",
8600
+ "--bs-body-bg",
8601
+ "--mui-palette-background-default"
8602
+ ],
8603
+ border: [
8604
+ "--border",
8605
+ "--color-border",
8606
+ "--border-color",
8607
+ "--bs-border-color",
8608
+ "--mui-palette-divider"
8609
+ ]
8610
+ };
8611
+ ROLE_NAME = {
8612
+ accent: /brand|primary|accent|main/i,
8613
+ text: /text|foreground|\bfg\b|-fg|ink|content|body/i,
8614
+ textMuted: /muted|secondary|subtle|soft|hint|placeholder|text|-fg/i,
8615
+ background: /background|\bbg\b|-bg|surface|canvas|page|base/i,
8616
+ border: /border|line|divider|stroke|outline|separator/i
8617
+ };
8618
+ PALETTE_NAME = /-(50|[1-9]00|950)$|white|black/i;
8619
+ DARK_SELECTOR = /\.dark\b|\[data-(theme|mode|color-scheme)=["']?dark|\.theme-dark|\.dark-theme/i;
8620
+ LIGHT_SELECTOR = /^(:root|html|:host|body)$|\.light\b|\[data-(theme|mode)=["']?light/i;
8621
+ MAX_ELEMENTS = 1500;
8622
+ MAX_VARS = 600;
8623
+ }
8624
+ });
8625
+
7809
8626
  // src/contract/v1.ts
7810
8627
  var SDK_VERSION = "0.0.1";
7811
8628
  var DEFAULT_FLUSH_INTERVAL_MS = 5e3;
@@ -8114,6 +8931,119 @@ function spaRouterPlugin() {
8114
8931
  });
8115
8932
  }
8116
8933
 
8934
+ // src/plugins/theme-detect/index.ts
8935
+ init_safe_env();
8936
+ var STORAGE_PREFIX = "veo_theme_";
8937
+ var RESEND_AFTER_MS = 24 * 60 * 60 * 1e3;
8938
+ var SETTLE_MS = 4e3;
8939
+ var MAX_RUNS_PER_PAGE = 4;
8940
+ function readState(key) {
8941
+ try {
8942
+ const raw = localStorage.getItem(key);
8943
+ const parsed = raw ? JSON.parse(raw) : null;
8944
+ return parsed && typeof parsed === "object" ? parsed : {};
8945
+ } catch {
8946
+ return {};
8947
+ }
8948
+ }
8949
+ function writeState(key, state) {
8950
+ try {
8951
+ localStorage.setItem(key, JSON.stringify(state));
8952
+ } catch {
8953
+ }
8954
+ }
8955
+ function guessMode(doc, prefersDark) {
8956
+ const hints = [
8957
+ doc.documentElement.className,
8958
+ doc.documentElement.dataset.theme,
8959
+ doc.documentElement.dataset.mode,
8960
+ doc.body?.className
8961
+ ].join(" ").toLowerCase();
8962
+ if (/\bdark\b/.test(hints)) return "dark";
8963
+ if (/\blight\b/.test(hints)) return "light";
8964
+ return prefersDark ? "dark" : "light";
8965
+ }
8966
+ function isFresh(state, mode, now) {
8967
+ const last = state[mode];
8968
+ return last !== void 0 && now - last.at < RESEND_AFTER_MS;
8969
+ }
8970
+ function shouldSendSnapshot(state, mode, hash, now) {
8971
+ const last = state[mode];
8972
+ return !last || last.hash !== hash || now - last.at >= RESEND_AFTER_MS;
8973
+ }
8974
+ function themeDetectPlugin(config) {
8975
+ const storageKey = `${STORAGE_PREFIX}${config.apiKey.slice(-16) || "default"}`;
8976
+ let observer = null;
8977
+ let timer = null;
8978
+ let destroyed = false;
8979
+ let runs = 0;
8980
+ const run = async () => {
8981
+ if (destroyed || runs >= MAX_RUNS_PER_PAGE) return;
8982
+ const prefersDark = typeof window.matchMedia === "function" && window.matchMedia("(prefers-color-scheme: dark)").matches;
8983
+ const guess = guessMode(document, prefersDark);
8984
+ if (isFresh(readState(storageKey), guess, Date.now())) return;
8985
+ runs++;
8986
+ try {
8987
+ const detector = await config.loadDetector();
8988
+ if (!detector || destroyed) return;
8989
+ const snapshot = detector.detectTheme();
8990
+ if (!snapshot) return;
8991
+ const hash = detector.snapshotHash(snapshot);
8992
+ const state = readState(storageKey);
8993
+ const now = Date.now();
8994
+ const mark = () => {
8995
+ const record = { hash, at: Date.now() };
8996
+ state[snapshot.mode] = record;
8997
+ state[guess] = state[guess] && guess !== snapshot.mode ? state[guess] : record;
8998
+ writeState(storageKey, state);
8999
+ };
9000
+ if (!shouldSendSnapshot(state, snapshot.mode, hash, now)) {
9001
+ mark();
9002
+ return;
9003
+ }
9004
+ if (config.debug) console.log("[veo] theme snapshot", snapshot);
9005
+ const res = await fetch(`${config.apiUrl}/v1/workspaces/theme-snapshot`, {
9006
+ method: "POST",
9007
+ headers: { "X-Api-Key": config.apiKey, "Content-Type": "application/json" },
9008
+ body: JSON.stringify(snapshot),
9009
+ credentials: "omit"
9010
+ });
9011
+ if (res.ok) mark();
9012
+ } catch (err) {
9013
+ if (config.debug) console.warn("[veo] theme detect failed", err);
9014
+ }
9015
+ };
9016
+ const schedule = (delayMs) => {
9017
+ if (timer) clearTimeout(timer);
9018
+ timer = setTimeout(() => {
9019
+ timer = null;
9020
+ const idle = window.requestIdleCallback;
9021
+ if (typeof idle === "function") idle(() => void run());
9022
+ else void run();
9023
+ }, delayMs);
9024
+ };
9025
+ return {
9026
+ name: "theme-detect",
9027
+ install() {
9028
+ if (!hasWindow() || !hasDocument()) return;
9029
+ if (document.readyState === "complete") schedule(SETTLE_MS);
9030
+ else window.addEventListener("load", () => schedule(SETTLE_MS), { once: true });
9031
+ if (typeof MutationObserver === "function") {
9032
+ observer = new MutationObserver(() => schedule(1500));
9033
+ observer.observe(document.documentElement, {
9034
+ attributes: true,
9035
+ attributeFilter: ["class", "data-theme", "data-mode"]
9036
+ });
9037
+ }
9038
+ },
9039
+ destroy() {
9040
+ destroyed = true;
9041
+ observer?.disconnect();
9042
+ if (timer) clearTimeout(timer);
9043
+ }
9044
+ };
9045
+ }
9046
+
8117
9047
  // src/core/client.ts
8118
9048
  init_safe_env();
8119
9049
  init_uuid();
@@ -8266,26 +9196,26 @@ var Session = class {
8266
9196
 
8267
9197
  // src/core/storage.ts
8268
9198
  init_safe_env();
8269
- var STORAGE_PREFIX = "veo:";
9199
+ var STORAGE_PREFIX2 = "veo:";
8270
9200
  var ONE_YEAR_MS = 365 * 24 * 60 * 60 * 1e3;
8271
9201
  var LocalStorageAdapter = class {
8272
9202
  get(key) {
8273
- return localStorage.getItem(STORAGE_PREFIX + key);
9203
+ return localStorage.getItem(STORAGE_PREFIX2 + key);
8274
9204
  }
8275
9205
  set(key, value) {
8276
9206
  try {
8277
- localStorage.setItem(STORAGE_PREFIX + key, value);
9207
+ localStorage.setItem(STORAGE_PREFIX2 + key, value);
8278
9208
  } catch {
8279
9209
  }
8280
9210
  }
8281
9211
  remove(key) {
8282
- localStorage.removeItem(STORAGE_PREFIX + key);
9212
+ localStorage.removeItem(STORAGE_PREFIX2 + key);
8283
9213
  }
8284
9214
  clear() {
8285
9215
  const toRemove = [];
8286
9216
  for (let i = 0; i < localStorage.length; i++) {
8287
9217
  const key = localStorage.key(i);
8288
- if (key?.startsWith(STORAGE_PREFIX)) toRemove.push(key);
9218
+ if (key?.startsWith(STORAGE_PREFIX2)) toRemove.push(key);
8289
9219
  }
8290
9220
  for (const key of toRemove) localStorage.removeItem(key);
8291
9221
  }
@@ -8296,7 +9226,7 @@ function readCookies() {
8296
9226
  }
8297
9227
  var CookieAdapter = class {
8298
9228
  get(key) {
8299
- const name = `${STORAGE_PREFIX}${key}=`;
9229
+ const name = `${STORAGE_PREFIX2}${key}=`;
8300
9230
  for (const cookie of readCookies()) {
8301
9231
  if (cookie.startsWith(name)) {
8302
9232
  return decodeURIComponent(cookie.slice(name.length));
@@ -8307,19 +9237,19 @@ var CookieAdapter = class {
8307
9237
  set(key, value, options) {
8308
9238
  const maxAgeSec = Math.floor((options?.ttlMs ?? ONE_YEAR_MS) / 1e3);
8309
9239
  const secure = typeof location !== "undefined" && location.protocol === "https:" ? "; Secure" : "";
8310
- document.cookie = `${STORAGE_PREFIX}${key}=${encodeURIComponent(
9240
+ document.cookie = `${STORAGE_PREFIX2}${key}=${encodeURIComponent(
8311
9241
  value
8312
9242
  )}; Path=/; Max-Age=${maxAgeSec}; SameSite=Lax${secure}`;
8313
9243
  }
8314
9244
  remove(key) {
8315
- document.cookie = `${STORAGE_PREFIX}${key}=; Path=/; Max-Age=0; SameSite=Lax`;
9245
+ document.cookie = `${STORAGE_PREFIX2}${key}=; Path=/; Max-Age=0; SameSite=Lax`;
8316
9246
  }
8317
9247
  clear() {
8318
9248
  for (const cookie of readCookies()) {
8319
9249
  const eq = cookie.indexOf("=");
8320
9250
  const name = eq === -1 ? cookie : cookie.slice(0, eq);
8321
- if (name.startsWith(STORAGE_PREFIX)) {
8322
- this.remove(name.slice(STORAGE_PREFIX.length));
9251
+ if (name.startsWith(STORAGE_PREFIX2)) {
9252
+ this.remove(name.slice(STORAGE_PREFIX2.length));
8323
9253
  }
8324
9254
  }
8325
9255
  }
@@ -8609,13 +9539,23 @@ var Client = class {
8609
9539
  };
8610
9540
 
8611
9541
  // src/core/setup.ts
8612
- function setupClient(config, onGuides, loadBuilder) {
9542
+ function setupClient(config, onGuides, loadBuilder, loadThemeDetector) {
8613
9543
  const client = new Client(config);
8614
9544
  client.use(spaRouterPlugin());
8615
9545
  if (config.autocapture !== false) {
8616
9546
  const autocaptureConfig = typeof config.autocapture === "object" ? config.autocapture : {};
8617
9547
  client.use(autocapturePlugin(autocaptureConfig));
8618
9548
  }
9549
+ if (config.detectTheme !== false && loadThemeDetector && !isBuilderMode()) {
9550
+ client.use(
9551
+ themeDetectPlugin({
9552
+ apiUrl: resolveApiUrl(config),
9553
+ apiKey: config.apiKey,
9554
+ loadDetector: loadThemeDetector,
9555
+ ...config.debug !== void 0 ? { debug: config.debug } : {}
9556
+ })
9557
+ );
9558
+ }
8619
9559
  onGuides(client);
8620
9560
  if (typeof window !== "undefined") {
8621
9561
  const stub = window.veo;
@@ -10319,7 +11259,10 @@ function init(config) {
10319
11259
  // Fallback de carga del builder para bundlers: `import()` dinámico → el bundler
10320
11260
  // lo code-splitea (solo se descarga en modo builder). Así el consumidor por
10321
11261
  // bundler NO necesita copiar `veo-builder.js` ni setear `builderUrl`.
10322
- () => Promise.resolve().then(() => (init_builder(), builder_exports)).then((m) => m.initBuilderMode())
11262
+ () => Promise.resolve().then(() => (init_builder(), builder_exports)).then((m) => m.initBuilderMode()),
11263
+ // Detector de tema: también por `import()` dinámico → chunk aparte que el
11264
+ // bundler solo descarga cuando toca detectar (como mucho una vez al día).
11265
+ () => Promise.resolve().then(() => (init_detect_theme(), detect_theme_exports))
10323
11266
  );
10324
11267
  }
10325
11268