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.
- package/dist/builder-ENKEIVF2.mjs +5 -0
- package/dist/{builder-3QPHB2VJ.mjs.map → builder-ENKEIVF2.mjs.map} +1 -1
- package/dist/builder.cjs +366 -20
- package/dist/builder.cjs.map +1 -1
- package/dist/builder.d.cts +2 -2
- package/dist/builder.d.ts +2 -2
- package/dist/builder.mjs +4 -4
- package/dist/{chunk-N4VFMZFA.mjs → chunk-4QZDNJJE.mjs} +3 -3
- package/dist/{chunk-N4VFMZFA.mjs.map → chunk-4QZDNJJE.mjs.map} +1 -1
- package/dist/{chunk-GMXQMZ4X.mjs → chunk-L3QQJLDJ.mjs} +3 -3
- package/dist/{chunk-GMXQMZ4X.mjs.map → chunk-L3QQJLDJ.mjs.map} +1 -1
- package/dist/{chunk-O6USA4TU.mjs → chunk-ZILDR4YE.mjs} +368 -22
- package/dist/chunk-ZILDR4YE.mjs.map +1 -0
- package/dist/detect-theme-X4K3TLUL.mjs +446 -0
- package/dist/detect-theme-X4K3TLUL.mjs.map +1 -0
- package/dist/{guide-preview-DylsEhrg.d.cts → guide-preview-DEFhfA4o.d.cts} +115 -0
- package/dist/{guide-preview-DylsEhrg.d.ts → guide-preview-DEFhfA4o.d.ts} +115 -0
- package/dist/index.cjs +994 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.mjs +141 -16
- package/dist/index.mjs.map +1 -1
- package/dist/veo-builder.js +92 -20
- package/dist/veo-builder.js.map +1 -1
- package/dist/veo-guides.js +84 -12
- package/dist/veo-guides.js.map +1 -1
- package/dist/veo-theme.js +2 -0
- package/dist/veo-theme.js.map +1 -0
- package/dist/veo.js +2 -2
- package/dist/veo.js.map +1 -1
- package/package.json +2 -2
- package/dist/builder-3QPHB2VJ.mjs +0 -5
- 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
|
-
|
|
922
|
-
el2.style.
|
|
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
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
|
6024
|
+
let top2 = topOffset + MARGIN;
|
|
5673
6025
|
card.style.left = `${left}px`;
|
|
5674
|
-
card.style.top = `${
|
|
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 -
|
|
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
|
-
|
|
6083
|
+
top2 = topOffset + MARGIN;
|
|
5732
6084
|
card.style.left = `${left}px`;
|
|
5733
|
-
card.style.top = `${
|
|
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 -
|
|
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
|
-
|
|
6091
|
+
top2 = restore.top;
|
|
5740
6092
|
card.style.left = `${left}px`;
|
|
5741
|
-
card.style.top = `${
|
|
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 =
|
|
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
|
-
|
|
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 = `${
|
|
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 (
|
|
5830
|
-
|
|
5831
|
-
card.style.top = `${
|
|
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 = (
|
|
6364
|
+
const posYFor = (top2, h) => {
|
|
6013
6365
|
const span = window.innerHeight - h;
|
|
6014
|
-
return span <= 0 ? 0.5 : round3(clamp4(
|
|
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
|
|
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(
|
|
9203
|
+
return localStorage.getItem(STORAGE_PREFIX2 + key);
|
|
8274
9204
|
}
|
|
8275
9205
|
set(key, value) {
|
|
8276
9206
|
try {
|
|
8277
|
-
localStorage.setItem(
|
|
9207
|
+
localStorage.setItem(STORAGE_PREFIX2 + key, value);
|
|
8278
9208
|
} catch {
|
|
8279
9209
|
}
|
|
8280
9210
|
}
|
|
8281
9211
|
remove(key) {
|
|
8282
|
-
localStorage.removeItem(
|
|
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(
|
|
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 = `${
|
|
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 = `${
|
|
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 = `${
|
|
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(
|
|
8322
|
-
this.remove(name.slice(
|
|
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
|
|