veo-sdk 0.6.1 → 0.6.3
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-PPLIAUCM.mjs +5 -0
- package/dist/{builder-BKPMANUB.mjs.map → builder-PPLIAUCM.mjs.map} +1 -1
- package/dist/builder.cjs +641 -105
- package/dist/builder.cjs.map +1 -1
- package/dist/builder.d.cts +78 -8
- package/dist/builder.d.ts +78 -8
- package/dist/builder.mjs +4 -4
- package/dist/{chunk-GVPAJFNW.mjs → chunk-3JEAPZ6A.mjs} +3 -3
- package/dist/{chunk-GVPAJFNW.mjs.map → chunk-3JEAPZ6A.mjs.map} +1 -1
- package/dist/{chunk-VWF4UHUU.mjs → chunk-DU6I4ZU2.mjs} +284 -15
- package/dist/chunk-DU6I4ZU2.mjs.map +1 -0
- package/dist/{chunk-NGP7IJVE.mjs → chunk-SZCIXA5X.mjs} +357 -82
- package/dist/chunk-SZCIXA5X.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 +1515 -56
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.mjs +442 -17
- package/dist/index.mjs.map +1 -1
- package/dist/veo-builder.js +132 -25
- 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-BKPMANUB.mjs +0 -5
- package/dist/chunk-NGP7IJVE.mjs.map +0 -1
- package/dist/chunk-VWF4UHUU.mjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -251,7 +251,8 @@ var init_constants = __esm({
|
|
|
251
251
|
throttleMs: 100,
|
|
252
252
|
maxTextLength: 200,
|
|
253
253
|
captureElementMetadata: true,
|
|
254
|
-
captureAncestors: 5
|
|
254
|
+
captureAncestors: 5,
|
|
255
|
+
captureClickQuality: true
|
|
255
256
|
};
|
|
256
257
|
MAX_ANCESTORS = 10;
|
|
257
258
|
ALWAYS_BLOCK_SELECTORS = [
|
|
@@ -902,6 +903,10 @@ function clampNum(n, min, max) {
|
|
|
902
903
|
return Math.min(max, Math.max(min, n));
|
|
903
904
|
}
|
|
904
905
|
function applyBlockStyle(el2, style, kind) {
|
|
906
|
+
if (kind === "text") {
|
|
907
|
+
const explicit = style && typeof style === "object" ? style.preset : void 0;
|
|
908
|
+
el2.dataset.veoPreset = typeof explicit === "string" ? explicit : el2.classList.contains("veo-guide-title") ? "title" : "paragraph";
|
|
909
|
+
}
|
|
905
910
|
if (!style || typeof style !== "object") return;
|
|
906
911
|
const s = style;
|
|
907
912
|
if (kind === "image" && s.bleed === true) {
|
|
@@ -918,8 +923,9 @@ function applyBlockStyle(el2, style, kind) {
|
|
|
918
923
|
if (kind === "text" && typeof s.preset === "string" && s.preset in TEXT_PRESETS) {
|
|
919
924
|
const preset = TEXT_PRESETS[s.preset];
|
|
920
925
|
if (preset) {
|
|
921
|
-
|
|
922
|
-
el2.style.
|
|
926
|
+
const token = s.preset === "small" ? null : `--veo-t-${s.preset}`;
|
|
927
|
+
el2.style.fontSize = token ? `var(${token}-size, ${preset.size}px)` : `${preset.size}px`;
|
|
928
|
+
el2.style.fontWeight = token ? `var(${token}-weight, ${preset.weight})` : String(preset.weight);
|
|
923
929
|
el2.style.lineHeight = String(preset.lineHeight);
|
|
924
930
|
}
|
|
925
931
|
}
|
|
@@ -1635,20 +1641,92 @@ var init_styles = __esm({
|
|
|
1635
1641
|
.veo-guide-actions {
|
|
1636
1642
|
display: flex; gap: 8px; justify-content: var(--veo-actions-justify);
|
|
1637
1643
|
}
|
|
1644
|
+
/* Tema del workspace (design system): cada preset de texto toma sus tokens de
|
|
1645
|
+
las variables que publica \`applyThemeVars\` en la tarjeta. Las reglas solo
|
|
1646
|
+
existen bajo .veo-card--themed; los estilos inline del bloque las pisan. El
|
|
1647
|
+
\`*\` cubre el rich text (la regla global de fuente aplica a cada hijo). */
|
|
1648
|
+
.veo-card--themed [data-veo-preset="title"] {
|
|
1649
|
+
color: var(--veo-t-title-color); font-size: var(--veo-t-title-size);
|
|
1650
|
+
font-weight: var(--veo-t-title-weight); text-align: var(--veo-t-title-align);
|
|
1651
|
+
}
|
|
1652
|
+
.veo-card--themed [data-veo-preset="subtitle"] {
|
|
1653
|
+
color: var(--veo-t-subtitle-color); font-size: var(--veo-t-subtitle-size);
|
|
1654
|
+
font-weight: var(--veo-t-subtitle-weight); text-align: var(--veo-t-subtitle-align);
|
|
1655
|
+
}
|
|
1656
|
+
.veo-card--themed [data-veo-preset="paragraph"] {
|
|
1657
|
+
color: var(--veo-t-paragraph-color); font-size: var(--veo-t-paragraph-size);
|
|
1658
|
+
font-weight: var(--veo-t-paragraph-weight); text-align: var(--veo-t-paragraph-align);
|
|
1659
|
+
}
|
|
1660
|
+
.veo-card--themed [data-veo-preset="small"] { color: var(--veo-t-paragraph-color); }
|
|
1661
|
+
.veo-card--themed [data-veo-preset="title"], .veo-card--themed [data-veo-preset="title"] * {
|
|
1662
|
+
font-family: var(--veo-t-title-font, var(--veo-font));
|
|
1663
|
+
}
|
|
1664
|
+
.veo-card--themed [data-veo-preset="subtitle"], .veo-card--themed [data-veo-preset="subtitle"] * {
|
|
1665
|
+
font-family: var(--veo-t-subtitle-font, var(--veo-font));
|
|
1666
|
+
}
|
|
1667
|
+
.veo-card--themed [data-veo-preset="paragraph"], .veo-card--themed [data-veo-preset="paragraph"] *,
|
|
1668
|
+
.veo-card--themed [data-veo-preset="small"], .veo-card--themed [data-veo-preset="small"] * {
|
|
1669
|
+
font-family: var(--veo-t-paragraph-font, var(--veo-font));
|
|
1670
|
+
}
|
|
1671
|
+
/* Formulario del tema (pesta\xF1a Formulario). Dentro de un .veo-form los tokens
|
|
1672
|
+
de texto \`--veo-t-*\` se REDEFINEN con \xABTexto principal\xBB: la pregunta es un
|
|
1673
|
+
bloque con preset (su tama\xF1o va inline con esas variables), as\xED toma los del
|
|
1674
|
+
formulario y no los del modal. Etiquetas y opciones = \xABTexto secundario\xBB. */
|
|
1675
|
+
.veo-card--themed-form .veo-form {
|
|
1676
|
+
background: var(--veo-f-bg); padding: var(--veo-f-pad);
|
|
1677
|
+
border: var(--veo-f-border, none); border-radius: var(--veo-f-radius, 8px);
|
|
1678
|
+
box-shadow: var(--veo-f-shadow, none);
|
|
1679
|
+
--veo-t-title-color: var(--veo-f-primary-color); --veo-t-title-size: var(--veo-f-primary-size);
|
|
1680
|
+
--veo-t-title-weight: var(--veo-f-primary-weight); --veo-t-title-align: var(--veo-f-primary-align);
|
|
1681
|
+
--veo-t-title-font: var(--veo-f-primary-font);
|
|
1682
|
+
--veo-t-subtitle-color: var(--veo-f-primary-color); --veo-t-subtitle-size: var(--veo-f-primary-size);
|
|
1683
|
+
--veo-t-subtitle-weight: var(--veo-f-primary-weight); --veo-t-subtitle-align: var(--veo-f-primary-align);
|
|
1684
|
+
--veo-t-subtitle-font: var(--veo-f-primary-font);
|
|
1685
|
+
--veo-t-paragraph-color: var(--veo-f-primary-color); --veo-t-paragraph-size: var(--veo-f-primary-size);
|
|
1686
|
+
--veo-t-paragraph-weight: var(--veo-f-primary-weight); --veo-t-paragraph-align: var(--veo-f-primary-align);
|
|
1687
|
+
--veo-t-paragraph-font: var(--veo-f-primary-font);
|
|
1688
|
+
}
|
|
1689
|
+
.veo-card--themed-form .veo-form-label,
|
|
1690
|
+
.veo-card--themed-form .veo-form-option,
|
|
1691
|
+
.veo-card--themed-form .veo-scale-label {
|
|
1692
|
+
color: var(--veo-f-secondary-color); font-weight: var(--veo-f-secondary-weight);
|
|
1693
|
+
font-family: var(--veo-f-secondary-font, var(--veo-font));
|
|
1694
|
+
}
|
|
1695
|
+
.veo-card--themed-form .veo-form-label,
|
|
1696
|
+
.veo-card--themed-form .veo-form-option {
|
|
1697
|
+
font-size: var(--veo-f-secondary-size); text-align: var(--veo-f-secondary-align);
|
|
1698
|
+
}
|
|
1699
|
+
.veo-card--themed-form .veo-form-input:focus { outline-color: var(--veo-f-accent); }
|
|
1700
|
+
.veo-card--themed-form .veo-form-option input { accent-color: var(--veo-f-accent); }
|
|
1701
|
+
.veo-card--themed-form .veo-nps-btn:hover { border-color: var(--veo-scale-color, var(--veo-f-accent)); }
|
|
1702
|
+
.veo-card--themed-form .veo-scale-active.veo-nps-btn {
|
|
1703
|
+
background: var(--veo-scale-color, var(--veo-f-accent)); color: var(--veo-f-on-accent);
|
|
1704
|
+
border-color: var(--veo-scale-color, var(--veo-f-accent));
|
|
1705
|
+
}
|
|
1638
1706
|
.veo-guide-cta {
|
|
1639
1707
|
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;
|
|
1708
|
+
color: var(--veo-cta-color, #fff); border: var(--veo-cta-border, none);
|
|
1709
|
+
padding: var(--veo-cta-pad, 9px 18px); border-radius: var(--veo-cta-radius, 8px);
|
|
1710
|
+
font-size: var(--veo-cta-size, 14px); font-weight: var(--veo-cta-weight, 500); cursor: pointer;
|
|
1643
1711
|
transition: opacity 120ms ease;
|
|
1644
1712
|
}
|
|
1645
1713
|
.veo-guide-cta:hover { opacity: 0.9; }
|
|
1646
1714
|
/* Presets del boton (editor): secundario = contorno; enlace = solo texto. */
|
|
1647
1715
|
.veo-guide-cta--secondary {
|
|
1648
|
-
background: transparent; color: var(--veo-primary);
|
|
1649
|
-
border: 1px solid var(--veo-primary);
|
|
1716
|
+
background: var(--veo-cta2-bg, transparent); color: var(--veo-cta2-color, var(--veo-primary));
|
|
1717
|
+
border: var(--veo-cta2-border, 1px solid var(--veo-primary));
|
|
1718
|
+
padding: var(--veo-cta2-pad, var(--veo-cta-pad, 9px 18px));
|
|
1719
|
+
border-radius: var(--veo-cta2-radius, var(--veo-cta-radius, 8px));
|
|
1720
|
+
font-size: var(--veo-cta2-size, var(--veo-cta-size, 14px));
|
|
1721
|
+
font-weight: var(--veo-cta2-weight, var(--veo-cta-weight, 500));
|
|
1722
|
+
}
|
|
1723
|
+
.veo-guide-cta--link {
|
|
1724
|
+
background: transparent; color: var(--veo-cta3-color, var(--veo-primary));
|
|
1725
|
+
border: var(--veo-cta3-border, none); padding: var(--veo-cta3-pad, 0);
|
|
1726
|
+
border-radius: var(--veo-cta3-radius, 0);
|
|
1727
|
+
font-size: var(--veo-cta3-size, var(--veo-cta-size, 14px));
|
|
1728
|
+
font-weight: var(--veo-cta3-weight, var(--veo-cta-weight, 500));
|
|
1650
1729
|
}
|
|
1651
|
-
.veo-guide-cta--link { background: transparent; color: var(--veo-primary); padding: 0; }
|
|
1652
1730
|
.veo-guide-cta--link:hover { opacity: 1; text-decoration: underline; }
|
|
1653
1731
|
.veo-guide-cta:active { transform: translateY(1px); }
|
|
1654
1732
|
.veo-guide-close {
|
|
@@ -2030,8 +2108,18 @@ function positionArrow(arrowEl, placement, data) {
|
|
|
2030
2108
|
if (data?.x != null) arrowEl.style.setProperty("left", `${data.x}px`);
|
|
2031
2109
|
if (data?.y != null) arrowEl.style.setProperty("top", `${data.y}px`);
|
|
2032
2110
|
arrowEl.style.setProperty(side, "-6px");
|
|
2111
|
+
const width = arrowEl.dataset.veoBorder;
|
|
2112
|
+
if (width) {
|
|
2113
|
+
for (const edge of ["top", "right", "bottom", "left"]) {
|
|
2114
|
+
arrowEl.style.setProperty(
|
|
2115
|
+
`border-${edge}-width`,
|
|
2116
|
+
OUTER_EDGES[side].includes(edge) ? `${width}px` : "0"
|
|
2117
|
+
);
|
|
2118
|
+
}
|
|
2119
|
+
arrowEl.style.zIndex = "1";
|
|
2120
|
+
}
|
|
2033
2121
|
}
|
|
2034
|
-
var STATIC_SIDE;
|
|
2122
|
+
var STATIC_SIDE, OUTER_EDGES;
|
|
2035
2123
|
var init_floating_arrow = __esm({
|
|
2036
2124
|
"src/plugins/guides/renderers/floating-arrow.ts"() {
|
|
2037
2125
|
STATIC_SIDE = {
|
|
@@ -2040,6 +2128,12 @@ var init_floating_arrow = __esm({
|
|
|
2040
2128
|
left: "right",
|
|
2041
2129
|
right: "left"
|
|
2042
2130
|
};
|
|
2131
|
+
OUTER_EDGES = {
|
|
2132
|
+
top: ["top", "left"],
|
|
2133
|
+
right: ["top", "right"],
|
|
2134
|
+
bottom: ["bottom", "right"],
|
|
2135
|
+
left: ["bottom", "left"]
|
|
2136
|
+
};
|
|
2043
2137
|
}
|
|
2044
2138
|
});
|
|
2045
2139
|
function readBadgeConfig(step) {
|
|
@@ -3213,6 +3307,173 @@ var init_form_content = __esm({
|
|
|
3213
3307
|
}
|
|
3214
3308
|
});
|
|
3215
3309
|
|
|
3310
|
+
// src/plugins/guides/guide-theme.ts
|
|
3311
|
+
function channels(hex) {
|
|
3312
|
+
const h = hex.replace("#", "");
|
|
3313
|
+
const full = h.length === 3 ? [...h].map((x) => x + x).join("") : h.slice(0, 6);
|
|
3314
|
+
const ch = (i) => Number.parseInt(full.slice(i, i + 2), 16) || 0;
|
|
3315
|
+
return [ch(0), ch(2), ch(4)];
|
|
3316
|
+
}
|
|
3317
|
+
function boxShadow(shadow, hex) {
|
|
3318
|
+
if (shadow === "none") return "none";
|
|
3319
|
+
const spec = SHADOW_SPEC[shadow];
|
|
3320
|
+
const [r, g, b] = channels(hex);
|
|
3321
|
+
return `0 ${spec.y}px ${spec.blur}px rgba(${r}, ${g}, ${b}, ${spec.alpha})`;
|
|
3322
|
+
}
|
|
3323
|
+
function readableOn(hex) {
|
|
3324
|
+
const [r, g, b] = channels(hex);
|
|
3325
|
+
return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255 > 0.6 ? "#111827" : "#ffffff";
|
|
3326
|
+
}
|
|
3327
|
+
function themeModeFor(theme, design) {
|
|
3328
|
+
if (!theme || design.theme !== WORKSPACE_THEME_ID) return null;
|
|
3329
|
+
return (design.colorMode === "dark" ? theme.dark : theme.light) ?? null;
|
|
3330
|
+
}
|
|
3331
|
+
function themeRowJustify(mode, inline, preset) {
|
|
3332
|
+
const box = inline ? mode.embedded : mode.overlay;
|
|
3333
|
+
if (box.align === "distribute") return null;
|
|
3334
|
+
const button = preset === "secondary" ? mode.buttons?.secondary : preset === "link" ? mode.buttons?.tertiary : preset === void 0 || preset === null || preset === "primary" ? mode.buttons?.primary : void 0;
|
|
3335
|
+
return button ? JUSTIFY[button.align] ?? null : null;
|
|
3336
|
+
}
|
|
3337
|
+
function appFont(ref, doc) {
|
|
3338
|
+
const view = doc.defaultView;
|
|
3339
|
+
if (!view) return null;
|
|
3340
|
+
if (ref === "inherit") return view.getComputedStyle(doc.body).fontFamily || null;
|
|
3341
|
+
if (ref === "app-heading") {
|
|
3342
|
+
const heading = Array.from(doc.querySelectorAll("h1,h2,h3")).find(
|
|
3343
|
+
(el2) => el2.getClientRects().length > 0
|
|
3344
|
+
);
|
|
3345
|
+
return view.getComputedStyle(heading ?? doc.body).fontFamily || null;
|
|
3346
|
+
}
|
|
3347
|
+
return ref;
|
|
3348
|
+
}
|
|
3349
|
+
function themeContainerDesign(mode, inline, tooltip = false) {
|
|
3350
|
+
const box = inline ? mode.embedded : mode.overlay;
|
|
3351
|
+
const tip = tooltip ? mode.tooltip : void 0;
|
|
3352
|
+
const padding = tip?.padding ?? box.padding;
|
|
3353
|
+
const out = {
|
|
3354
|
+
// El embebido va en el flujo de la página: sin fondo propio.
|
|
3355
|
+
background: tip ? tip.background : inline ? "transparent" : box.background ?? mode.colors.background,
|
|
3356
|
+
color: mode.colors.text,
|
|
3357
|
+
paddingTop: padding.top,
|
|
3358
|
+
paddingRight: padding.right,
|
|
3359
|
+
paddingBottom: padding.bottom,
|
|
3360
|
+
paddingLeft: padding.left,
|
|
3361
|
+
borderColor: tip?.borderColor ?? box.borderColor,
|
|
3362
|
+
borderWidth: tip?.borderWidth ?? box.borderWidth,
|
|
3363
|
+
radius: tip?.borderRadius ?? box.borderRadius
|
|
3364
|
+
};
|
|
3365
|
+
const shadow = tip?.shadow ?? box.shadow;
|
|
3366
|
+
const shadowColor = tip?.shadowColor ?? box.shadowColor;
|
|
3367
|
+
if (shadow === "none") {
|
|
3368
|
+
Object.assign(out, { shadowColor: "transparent", shadowBlur: 0 });
|
|
3369
|
+
} else {
|
|
3370
|
+
const spec = SHADOW_SPEC[shadow];
|
|
3371
|
+
const [r, g, b] = channels(shadowColor);
|
|
3372
|
+
Object.assign(out, {
|
|
3373
|
+
shadowX: 0,
|
|
3374
|
+
shadowY: spec.y,
|
|
3375
|
+
shadowBlur: spec.blur,
|
|
3376
|
+
shadowSpread: 0,
|
|
3377
|
+
shadowColor: `rgba(${r}, ${g}, ${b}, ${spec.alpha})`
|
|
3378
|
+
});
|
|
3379
|
+
}
|
|
3380
|
+
return out;
|
|
3381
|
+
}
|
|
3382
|
+
function applyThemeVars(card, mode, inline, tooltip = false) {
|
|
3383
|
+
const doc = card.ownerDocument ?? document;
|
|
3384
|
+
const box = inline ? mode.embedded : mode.overlay;
|
|
3385
|
+
const set = (name, value) => {
|
|
3386
|
+
if (value) card.style.setProperty(name, value);
|
|
3387
|
+
};
|
|
3388
|
+
card.classList.add("veo-card--themed");
|
|
3389
|
+
set("--veo-primary", mode.colors.accent);
|
|
3390
|
+
set("--veo-cta-color", readableOn(mode.colors.accent));
|
|
3391
|
+
const primary = mode.buttons?.primary;
|
|
3392
|
+
const publish = (prefix, b) => {
|
|
3393
|
+
if (!b) return;
|
|
3394
|
+
const p = b.padding;
|
|
3395
|
+
set(`${prefix}-bg`, b.background);
|
|
3396
|
+
set(`${prefix}-color`, b.color);
|
|
3397
|
+
set(`${prefix}-pad`, `${p.top}px ${p.right}px ${p.bottom}px ${p.left}px`);
|
|
3398
|
+
set(`${prefix}-size`, `${b.fontSize}px`);
|
|
3399
|
+
set(`${prefix}-weight`, String(b.fontWeight));
|
|
3400
|
+
set(`${prefix}-radius`, `${b.borderRadius}px`);
|
|
3401
|
+
set(
|
|
3402
|
+
`${prefix}-border`,
|
|
3403
|
+
b.borderWidth > 0 ? `${b.borderWidth}px solid ${b.borderColor}` : "none"
|
|
3404
|
+
);
|
|
3405
|
+
};
|
|
3406
|
+
publish("--veo-cta", primary);
|
|
3407
|
+
publish("--veo-cta2", mode.buttons?.secondary);
|
|
3408
|
+
publish("--veo-cta3", mode.buttons?.tertiary);
|
|
3409
|
+
set("--veo-text", mode.colors.text);
|
|
3410
|
+
set("--veo-text-secondary", mode.colors.textMuted);
|
|
3411
|
+
set("--veo-font", appFont("inherit", doc));
|
|
3412
|
+
set(
|
|
3413
|
+
"--veo-actions-justify",
|
|
3414
|
+
box.align === "distribute" || !primary ? JUSTIFY[box.align] : JUSTIFY[primary.align]
|
|
3415
|
+
);
|
|
3416
|
+
const form = mode.form;
|
|
3417
|
+
if (form) {
|
|
3418
|
+
card.classList.add("veo-card--themed-form");
|
|
3419
|
+
const fp = form.padding;
|
|
3420
|
+
set("--veo-f-bg", form.background);
|
|
3421
|
+
set("--veo-f-accent", form.accent);
|
|
3422
|
+
set("--veo-f-on-accent", form.onAccent);
|
|
3423
|
+
set("--veo-f-pad", `${fp.top}px ${fp.right}px ${fp.bottom}px ${fp.left}px`);
|
|
3424
|
+
if (typeof form.borderWidth === "number" && form.borderColor) {
|
|
3425
|
+
set(
|
|
3426
|
+
"--veo-f-border",
|
|
3427
|
+
form.borderWidth > 0 ? `${form.borderWidth}px solid ${form.borderColor}` : "none"
|
|
3428
|
+
);
|
|
3429
|
+
}
|
|
3430
|
+
if (typeof form.borderRadius === "number") set("--veo-f-radius", `${form.borderRadius}px`);
|
|
3431
|
+
if (form.shadow && form.shadowColor) {
|
|
3432
|
+
set("--veo-f-shadow", boxShadow(form.shadow, form.shadowColor));
|
|
3433
|
+
}
|
|
3434
|
+
for (const [name, t] of [
|
|
3435
|
+
["primary", form.text.primary],
|
|
3436
|
+
["secondary", form.text.secondary]
|
|
3437
|
+
]) {
|
|
3438
|
+
set(`--veo-f-${name}-color`, t.color);
|
|
3439
|
+
set(`--veo-f-${name}-size`, `${t.fontSize}px`);
|
|
3440
|
+
set(`--veo-f-${name}-weight`, String(t.fontWeight));
|
|
3441
|
+
set(`--veo-f-${name}-align`, t.align);
|
|
3442
|
+
set(`--veo-f-${name}-font`, appFont(t.fontFamily, doc));
|
|
3443
|
+
}
|
|
3444
|
+
}
|
|
3445
|
+
const tip = tooltip ? mode.tooltip?.text : void 0;
|
|
3446
|
+
const tokens = [
|
|
3447
|
+
["title", tip?.primary ?? mode.text.title],
|
|
3448
|
+
["subtitle", tip?.secondary ?? mode.text.subtitle],
|
|
3449
|
+
["paragraph", tip?.secondary ?? mode.text.paragraph]
|
|
3450
|
+
];
|
|
3451
|
+
for (const [name, t] of tokens) {
|
|
3452
|
+
set(`--veo-t-${name}-color`, t.color);
|
|
3453
|
+
set(`--veo-t-${name}-size`, `${t.fontSize}px`);
|
|
3454
|
+
set(`--veo-t-${name}-weight`, String(t.fontWeight));
|
|
3455
|
+
set(`--veo-t-${name}-align`, t.align);
|
|
3456
|
+
set(`--veo-t-${name}-font`, appFont(t.fontFamily, doc));
|
|
3457
|
+
}
|
|
3458
|
+
}
|
|
3459
|
+
var WORKSPACE_THEME_ID, SHADOW_SPEC, JUSTIFY;
|
|
3460
|
+
var init_guide_theme = __esm({
|
|
3461
|
+
"src/plugins/guides/guide-theme.ts"() {
|
|
3462
|
+
WORKSPACE_THEME_ID = "workspace";
|
|
3463
|
+
SHADOW_SPEC = {
|
|
3464
|
+
soft: { y: 4, blur: 14, alpha: 0.12 },
|
|
3465
|
+
medium: { y: 12, blur: 32, alpha: 0.22 },
|
|
3466
|
+
strong: { y: 24, blur: 64, alpha: 0.38 }
|
|
3467
|
+
};
|
|
3468
|
+
JUSTIFY = {
|
|
3469
|
+
left: "flex-start",
|
|
3470
|
+
center: "center",
|
|
3471
|
+
right: "flex-end",
|
|
3472
|
+
distribute: "space-between"
|
|
3473
|
+
};
|
|
3474
|
+
}
|
|
3475
|
+
});
|
|
3476
|
+
|
|
3216
3477
|
// src/plugins/guides/layout.ts
|
|
3217
3478
|
function groupLayout(blocks) {
|
|
3218
3479
|
const rows = /* @__PURE__ */ new Map();
|
|
@@ -3673,6 +3934,7 @@ function makeCaret(doc, design) {
|
|
|
3673
3934
|
if (typeof design.borderWidth === "number" && design.borderWidth > 0) {
|
|
3674
3935
|
const bc = typeof design.borderColor === "string" ? design.borderColor : "#bbbbbb";
|
|
3675
3936
|
s.border = `${design.borderWidth}px solid ${bc}`;
|
|
3937
|
+
caret.dataset.veoBorder = String(design.borderWidth);
|
|
3676
3938
|
}
|
|
3677
3939
|
return caret;
|
|
3678
3940
|
}
|
|
@@ -3765,6 +4027,7 @@ var init_composite_renderer = __esm({
|
|
|
3765
4027
|
init_checklist_block();
|
|
3766
4028
|
init_code_block();
|
|
3767
4029
|
init_form_content();
|
|
4030
|
+
init_guide_theme();
|
|
3768
4031
|
init_inline_host();
|
|
3769
4032
|
init_layout();
|
|
3770
4033
|
init_rich_text();
|
|
@@ -3786,6 +4049,11 @@ var init_composite_renderer = __esm({
|
|
|
3786
4049
|
bottom: "flex-end"
|
|
3787
4050
|
};
|
|
3788
4051
|
CompositeRenderer = class extends BaseRenderer {
|
|
4052
|
+
constructor() {
|
|
4053
|
+
super(...arguments);
|
|
4054
|
+
/** Alineación de filas de botones según el tema del workspace (null = sin tema). */
|
|
4055
|
+
this.themeRowJustify = null;
|
|
4056
|
+
}
|
|
3789
4057
|
async render(context) {
|
|
3790
4058
|
const idx = context.nav?.stepIndex ?? 0;
|
|
3791
4059
|
const step = context.guide.guideSteps[idx];
|
|
@@ -3816,7 +4084,13 @@ var init_composite_renderer = __esm({
|
|
|
3816
4084
|
if (typeof design.zIndex === "number") host.style.zIndex = String(design.zIndex);
|
|
3817
4085
|
const card = doc.createElement("div");
|
|
3818
4086
|
card.className = "veo-card";
|
|
3819
|
-
|
|
4087
|
+
const themeMode = themeModeFor(context.guide.theme, design);
|
|
4088
|
+
const inline = step.presentation === "inline";
|
|
4089
|
+
const tooltipLike = !inline && (badgeMode || step.position === "relative-to-element");
|
|
4090
|
+
if (themeMode) applyThemeVars(card, themeMode, inline, tooltipLike);
|
|
4091
|
+
this.themeRowJustify = themeMode ? (preset) => themeRowJustify(themeMode, inline, preset) : null;
|
|
4092
|
+
const cardDesign = themeMode ? { ...themeContainerDesign(themeMode, inline, tooltipLike), ...design } : design;
|
|
4093
|
+
applyCompositeDesign(card, cardDesign);
|
|
3820
4094
|
if (typeof design.ariaLabel === "string" && design.ariaLabel.trim()) {
|
|
3821
4095
|
card.setAttribute("role", "dialog");
|
|
3822
4096
|
card.setAttribute("aria-label", design.ariaLabel.trim());
|
|
@@ -3887,7 +4161,7 @@ var init_composite_renderer = __esm({
|
|
|
3887
4161
|
});
|
|
3888
4162
|
}
|
|
3889
4163
|
card.appendChild(closeBtn);
|
|
3890
|
-
reserveCloseSpace(card, blocks, closePos,
|
|
4164
|
+
reserveCloseSpace(card, blocks, closePos, cardDesign);
|
|
3891
4165
|
}
|
|
3892
4166
|
if (badgeMode && anchor) {
|
|
3893
4167
|
closeTooltip = this.renderBadgeMode(
|
|
@@ -3900,7 +4174,8 @@ var init_composite_renderer = __esm({
|
|
|
3900
4174
|
doc,
|
|
3901
4175
|
context,
|
|
3902
4176
|
idx,
|
|
3903
|
-
|
|
4177
|
+
// Con el tema ya fundido: la flecha del tooltip toma su fondo y borde.
|
|
4178
|
+
cardDesign
|
|
3904
4179
|
);
|
|
3905
4180
|
return;
|
|
3906
4181
|
}
|
|
@@ -3923,7 +4198,7 @@ var init_composite_renderer = __esm({
|
|
|
3923
4198
|
}
|
|
3924
4199
|
const wrapper = doc.createElement("div");
|
|
3925
4200
|
wrapper.className = "veo-composite-anchored";
|
|
3926
|
-
const caretEl = design.caret === false ? null : makeCaret(doc,
|
|
4201
|
+
const caretEl = design.caret === false ? null : makeCaret(doc, cardDesign);
|
|
3927
4202
|
if (caretEl) wrapper.appendChild(caretEl);
|
|
3928
4203
|
wrapper.appendChild(card);
|
|
3929
4204
|
root.appendChild(wrapper);
|
|
@@ -3983,6 +4258,7 @@ var init_composite_renderer = __esm({
|
|
|
3983
4258
|
const row = doc.createElement("div");
|
|
3984
4259
|
row.className = "veo-guide-actions";
|
|
3985
4260
|
let rowAlign;
|
|
4261
|
+
const firstPreset = b.style?.preset;
|
|
3986
4262
|
while (i < cell.length && cell[i]?.block.type === "button") {
|
|
3987
4263
|
const { block: bb, index: bi } = cell[i];
|
|
3988
4264
|
if (!rowAlign) {
|
|
@@ -3993,6 +4269,10 @@ var init_composite_renderer = __esm({
|
|
|
3993
4269
|
i++;
|
|
3994
4270
|
}
|
|
3995
4271
|
if (rowAlign) row.style.justifyContent = ROW_ALIGN[rowAlign] ?? "flex-end";
|
|
4272
|
+
else {
|
|
4273
|
+
const themed = this.themeRowJustify?.(firstPreset);
|
|
4274
|
+
if (themed) row.style.justifyContent = themed;
|
|
4275
|
+
}
|
|
3996
4276
|
container.appendChild(row);
|
|
3997
4277
|
continue;
|
|
3998
4278
|
}
|
|
@@ -4959,7 +5239,8 @@ function normalize(input) {
|
|
|
4959
5239
|
guideSteps: input.guideSteps,
|
|
4960
5240
|
activationRules,
|
|
4961
5241
|
displayFrequency: "always",
|
|
4962
|
-
displayPriority: 0
|
|
5242
|
+
displayPriority: 0,
|
|
5243
|
+
...input.theme ? { theme: input.theme } : {}
|
|
4963
5244
|
};
|
|
4964
5245
|
}
|
|
4965
5246
|
function createSingleStepRenderer(type) {
|
|
@@ -5410,14 +5691,19 @@ function injectBuilderChrome(opts) {
|
|
|
5410
5691
|
let previewOn = false;
|
|
5411
5692
|
let hiddenOn = false;
|
|
5412
5693
|
let heightCb = null;
|
|
5413
|
-
|
|
5694
|
+
let tagging = false;
|
|
5695
|
+
const height = () => CHROME_TOPBAR_H + (collapsed || tagging ? 0 : CHROME_STEPS_H);
|
|
5414
5696
|
const applyHeight = () => {
|
|
5415
5697
|
const h = previewOn || hiddenOn ? 0 : height();
|
|
5416
5698
|
html.style.marginTop = dock === "top" ? `${h}px` : prevMarginTop;
|
|
5417
5699
|
html.style.marginBottom = dock === "bottom" ? `${h}px` : prevMarginBottom;
|
|
5418
5700
|
root.style.setProperty("--veo-chrome-h", `${height()}px`);
|
|
5419
5701
|
root.classList.toggle("is-bottom", dock === "bottom");
|
|
5420
|
-
stepsBar.hidden = collapsed;
|
|
5702
|
+
stepsBar.hidden = collapsed || tagging;
|
|
5703
|
+
togglePill.hidden = tagging;
|
|
5704
|
+
preview.hidden = tagging;
|
|
5705
|
+
saveGroup.hidden = tagging;
|
|
5706
|
+
showPanel.title = tagging ? "Mostrar el panel" : "Mostrar el editor";
|
|
5421
5707
|
const open = dock === "top" ? "chevronUp" : "chevronDown";
|
|
5422
5708
|
const closedIcon = dock === "top" ? "chevronDown" : "chevronUp";
|
|
5423
5709
|
togglePill.innerHTML = `<span>${collapsed ? "Mostrar pasos" : "Ocultar pasos"}</span>${svg(
|
|
@@ -5470,8 +5756,13 @@ function injectBuilderChrome(opts) {
|
|
|
5470
5756
|
const render = () => {
|
|
5471
5757
|
const s = current;
|
|
5472
5758
|
if (!s) return;
|
|
5759
|
+
const nextTagging = s.variant === "tagging";
|
|
5760
|
+
if (nextTagging !== tagging) {
|
|
5761
|
+
tagging = nextTagging;
|
|
5762
|
+
applyHeight();
|
|
5763
|
+
}
|
|
5473
5764
|
const date = shortDate(s.createdAt);
|
|
5474
|
-
nameEl.textContent = `${s.guideName.trim() || "Sin nombre"}${date ? ` \xB7 ${date}` : ""}`;
|
|
5765
|
+
nameEl.textContent = tagging ? s.guideName.trim() || "Modo tagging" : `${s.guideName.trim() || "Sin nombre"}${date ? ` \xB7 ${date}` : ""}`;
|
|
5475
5766
|
if (s.status) {
|
|
5476
5767
|
statusEl.hidden = false;
|
|
5477
5768
|
statusEl.textContent = s.status.label;
|
|
@@ -5591,6 +5882,8 @@ var init_builder_chrome = __esm({
|
|
|
5591
5882
|
font-size: 14px; color: #1c1917;
|
|
5592
5883
|
}
|
|
5593
5884
|
.veo-chrome[hidden] { display: none; }
|
|
5885
|
+
/* Modo tagging oculta controles por atributo: gana a los display de sus clases. */
|
|
5886
|
+
.veo-chrome [hidden], .veo-steps-toggle[hidden] { display: none !important; }
|
|
5594
5887
|
.veo-chrome.is-bottom { top: auto; bottom: 0; flex-direction: column-reverse; }
|
|
5595
5888
|
.veo-chrome.is-bottom .veo-tb-menu { top: auto; bottom: calc(100% + 8px); }
|
|
5596
5889
|
.veo-chrome.is-bottom .veo-steps { border-bottom: 0; border-top: 1px solid #d6cfc8; }
|
|
@@ -5741,9 +6034,9 @@ function injectBuilderPanel(opts) {
|
|
|
5741
6034
|
card.className = "veo-panel";
|
|
5742
6035
|
let topOffset = opts.topOffset ?? 0;
|
|
5743
6036
|
let left = 40;
|
|
5744
|
-
let
|
|
6037
|
+
let top2 = topOffset + MARGIN;
|
|
5745
6038
|
card.style.left = `${left}px`;
|
|
5746
|
-
card.style.top = `${
|
|
6039
|
+
card.style.top = `${top2}px`;
|
|
5747
6040
|
card.style.width = `${PANEL_W}px`;
|
|
5748
6041
|
card.style.height = `${PANEL_H}px`;
|
|
5749
6042
|
const header = document.createElement("div");
|
|
@@ -5787,7 +6080,7 @@ function injectBuilderPanel(opts) {
|
|
|
5787
6080
|
if (!compactRestore) compactRestore = { w: card.offsetWidth, h: card.offsetHeight };
|
|
5788
6081
|
card.style.minHeight = "0";
|
|
5789
6082
|
card.style.width = `${clamp3(Math.round(size.width), MIN_W, window.innerWidth - MARGIN * 2)}px`;
|
|
5790
|
-
card.style.height = `${clamp3(Math.round(size.height) + HEADER_H, HEADER_H + 80, window.innerHeight -
|
|
6083
|
+
card.style.height = `${clamp3(Math.round(size.height) + HEADER_H, HEADER_H + 80, window.innerHeight - top2 - MARGIN)}px`;
|
|
5791
6084
|
} else if (compactRestore) {
|
|
5792
6085
|
card.style.minHeight = "";
|
|
5793
6086
|
card.style.width = `${compactRestore.w}px`;
|
|
@@ -5798,19 +6091,19 @@ function injectBuilderPanel(opts) {
|
|
|
5798
6091
|
maximize.addEventListener("click", () => {
|
|
5799
6092
|
maximized = !maximized;
|
|
5800
6093
|
if (maximized) {
|
|
5801
|
-
restore = { left, top, w: card.offsetWidth, h: card.offsetHeight };
|
|
6094
|
+
restore = { left, top: top2, w: card.offsetWidth, h: card.offsetHeight };
|
|
5802
6095
|
left = MARGIN;
|
|
5803
|
-
|
|
6096
|
+
top2 = topOffset + MARGIN;
|
|
5804
6097
|
card.style.left = `${left}px`;
|
|
5805
|
-
card.style.top = `${
|
|
6098
|
+
card.style.top = `${top2}px`;
|
|
5806
6099
|
card.style.width = `${Math.max(MIN_W, window.innerWidth - MARGIN * 2)}px`;
|
|
5807
|
-
card.style.height = `${Math.max(MIN_H, window.innerHeight -
|
|
6100
|
+
card.style.height = `${Math.max(MIN_H, window.innerHeight - top2 - MARGIN)}px`;
|
|
5808
6101
|
maximize.title = "Restaurar";
|
|
5809
6102
|
} else if (restore) {
|
|
5810
6103
|
left = restore.left;
|
|
5811
|
-
|
|
6104
|
+
top2 = restore.top;
|
|
5812
6105
|
card.style.left = `${left}px`;
|
|
5813
|
-
card.style.top = `${
|
|
6106
|
+
card.style.top = `${top2}px`;
|
|
5814
6107
|
card.style.width = `${restore.w}px`;
|
|
5815
6108
|
card.style.height = `${restore.h}px`;
|
|
5816
6109
|
maximize.title = "Maximizar";
|
|
@@ -5846,13 +6139,13 @@ function injectBuilderPanel(opts) {
|
|
|
5846
6139
|
const sx = e.clientX;
|
|
5847
6140
|
const sy = e.clientY;
|
|
5848
6141
|
const ol = left;
|
|
5849
|
-
const ot =
|
|
6142
|
+
const ot = top2;
|
|
5850
6143
|
iframe.style.pointerEvents = "none";
|
|
5851
6144
|
const move = (ev) => {
|
|
5852
6145
|
left = clamp3(ol + (ev.clientX - sx), 0, Math.max(0, window.innerWidth - card.offsetWidth));
|
|
5853
|
-
|
|
6146
|
+
top2 = clamp3(ot + (ev.clientY - sy), topOffset, Math.max(topOffset, window.innerHeight - 40));
|
|
5854
6147
|
card.style.left = `${left}px`;
|
|
5855
|
-
card.style.top = `${
|
|
6148
|
+
card.style.top = `${top2}px`;
|
|
5856
6149
|
};
|
|
5857
6150
|
const up = () => {
|
|
5858
6151
|
window.removeEventListener("mousemove", move);
|
|
@@ -5898,9 +6191,9 @@ function injectBuilderPanel(opts) {
|
|
|
5898
6191
|
isVisible: () => visible,
|
|
5899
6192
|
setTopOffset: (px) => {
|
|
5900
6193
|
topOffset = px;
|
|
5901
|
-
if (
|
|
5902
|
-
|
|
5903
|
-
card.style.top = `${
|
|
6194
|
+
if (top2 < topOffset + MARGIN) {
|
|
6195
|
+
top2 = topOffset + MARGIN;
|
|
6196
|
+
card.style.top = `${top2}px`;
|
|
5904
6197
|
}
|
|
5905
6198
|
}
|
|
5906
6199
|
};
|
|
@@ -6081,9 +6374,9 @@ function attachModal(opts, shadow, card) {
|
|
|
6081
6374
|
const span = window.innerWidth - w;
|
|
6082
6375
|
return span <= 0 ? 0.5 : round3(clamp4(left / span, 0, 1));
|
|
6083
6376
|
};
|
|
6084
|
-
const posYFor = (
|
|
6377
|
+
const posYFor = (top2, h) => {
|
|
6085
6378
|
const span = window.innerHeight - h;
|
|
6086
|
-
return span <= 0 ? 0.5 : round3(clamp4(
|
|
6379
|
+
return span <= 0 ? 0.5 : round3(clamp4(top2 / span, 0, 1));
|
|
6087
6380
|
};
|
|
6088
6381
|
const onCardDown = (e) => {
|
|
6089
6382
|
if (isInteractive(e.target)) return;
|
|
@@ -6223,6 +6516,65 @@ var init_design_manipulator = __esm({
|
|
|
6223
6516
|
}
|
|
6224
6517
|
});
|
|
6225
6518
|
|
|
6519
|
+
// src/plugins/builder/interactive-elements.ts
|
|
6520
|
+
function isInteractiveElement(element) {
|
|
6521
|
+
try {
|
|
6522
|
+
return element.matches(INTERACTIVE_SELECTOR);
|
|
6523
|
+
} catch {
|
|
6524
|
+
return false;
|
|
6525
|
+
}
|
|
6526
|
+
}
|
|
6527
|
+
function closestInteractive(element) {
|
|
6528
|
+
try {
|
|
6529
|
+
return element.closest(INTERACTIVE_SELECTOR);
|
|
6530
|
+
} catch {
|
|
6531
|
+
return null;
|
|
6532
|
+
}
|
|
6533
|
+
}
|
|
6534
|
+
function elementLabelText(element, max = 120) {
|
|
6535
|
+
const clean = (raw) => (raw ?? "").replace(EMOJI_RE, "").replace(/\s+/g, " ").trim();
|
|
6536
|
+
const text = clean(element.textContent);
|
|
6537
|
+
if (text) return text.slice(0, max);
|
|
6538
|
+
const fallback = clean(element.getAttribute("aria-label")) || clean(element.getAttribute("title"));
|
|
6539
|
+
return fallback.slice(0, max);
|
|
6540
|
+
}
|
|
6541
|
+
var INTERACTIVE_ROLES, INTERACTIVE_SELECTOR, EMOJI_RE;
|
|
6542
|
+
var init_interactive_elements = __esm({
|
|
6543
|
+
"src/plugins/builder/interactive-elements.ts"() {
|
|
6544
|
+
INTERACTIVE_ROLES = [
|
|
6545
|
+
"button",
|
|
6546
|
+
"link",
|
|
6547
|
+
"tab",
|
|
6548
|
+
"menuitem",
|
|
6549
|
+
"menuitemcheckbox",
|
|
6550
|
+
"menuitemradio",
|
|
6551
|
+
"checkbox",
|
|
6552
|
+
"radio",
|
|
6553
|
+
"switch",
|
|
6554
|
+
"option",
|
|
6555
|
+
"combobox",
|
|
6556
|
+
"textbox",
|
|
6557
|
+
"searchbox",
|
|
6558
|
+
"slider",
|
|
6559
|
+
"spinbutton"
|
|
6560
|
+
];
|
|
6561
|
+
INTERACTIVE_SELECTOR = [
|
|
6562
|
+
"button",
|
|
6563
|
+
"a[href]",
|
|
6564
|
+
'input:not([type="hidden"])',
|
|
6565
|
+
"select",
|
|
6566
|
+
"textarea",
|
|
6567
|
+
"form",
|
|
6568
|
+
"summary",
|
|
6569
|
+
'[contenteditable="true"]',
|
|
6570
|
+
"[onclick]",
|
|
6571
|
+
"[data-veo-tag]",
|
|
6572
|
+
...INTERACTIVE_ROLES.map((r) => `[role="${r}"]`)
|
|
6573
|
+
].join(", ");
|
|
6574
|
+
EMOJI_RE = /\p{Extended_Pictographic}|\p{Emoji_Modifier}|\u{FE0F}|\u{200D}|\u{20E3}/gu;
|
|
6575
|
+
}
|
|
6576
|
+
});
|
|
6577
|
+
|
|
6226
6578
|
// src/plugins/builder/element-picker.ts
|
|
6227
6579
|
function startElementPicker(options) {
|
|
6228
6580
|
if (!hasDocument()) {
|
|
@@ -6241,6 +6593,7 @@ var init_element_picker = __esm({
|
|
|
6241
6593
|
"src/plugins/builder/element-picker.ts"() {
|
|
6242
6594
|
init_safe_env();
|
|
6243
6595
|
init_selector_builder();
|
|
6596
|
+
init_interactive_elements();
|
|
6244
6597
|
PICKER_Z = 2147483646;
|
|
6245
6598
|
PICKER_HOST_ATTR = "data-veo-picker";
|
|
6246
6599
|
DEFAULT_MAX_ANCESTORS = 5;
|
|
@@ -6310,21 +6663,24 @@ var init_element_picker = __esm({
|
|
|
6310
6663
|
this.label = null;
|
|
6311
6664
|
this.current = null;
|
|
6312
6665
|
this.maxAncestors = DEFAULT_MAX_ANCESTORS;
|
|
6666
|
+
this.only = null;
|
|
6313
6667
|
this.onPick = null;
|
|
6314
6668
|
this.onCancel = null;
|
|
6315
6669
|
this.onMove = (event) => {
|
|
6316
|
-
|
|
6317
|
-
|
|
6670
|
+
if (!(event.target instanceof Element) || this.isOwn(event.target)) return;
|
|
6671
|
+
const target = this.resolveTarget(event.target);
|
|
6318
6672
|
this.current = target;
|
|
6319
|
-
this.draw(target);
|
|
6673
|
+
if (target) this.draw(target);
|
|
6674
|
+
else this.hideBox();
|
|
6320
6675
|
};
|
|
6321
6676
|
this.onClick = (event) => {
|
|
6322
6677
|
if (event.altKey) return;
|
|
6323
|
-
|
|
6324
|
-
if (!target || this.isOwn(target)) return;
|
|
6678
|
+
if (this.isOwn(event.target)) return;
|
|
6325
6679
|
event.preventDefault();
|
|
6326
6680
|
event.stopPropagation();
|
|
6327
6681
|
event.stopImmediatePropagation();
|
|
6682
|
+
const target = this.current ?? this.resolveTarget(event.target);
|
|
6683
|
+
if (!target) return;
|
|
6328
6684
|
const picked = this.describe(target);
|
|
6329
6685
|
const cb = this.onPick;
|
|
6330
6686
|
this.stop();
|
|
@@ -6351,6 +6707,7 @@ var init_element_picker = __esm({
|
|
|
6351
6707
|
start(options) {
|
|
6352
6708
|
this.stop();
|
|
6353
6709
|
this.maxAncestors = options.maxAncestors ?? DEFAULT_MAX_ANCESTORS;
|
|
6710
|
+
this.only = options.only ?? null;
|
|
6354
6711
|
this.onPick = options.onPick;
|
|
6355
6712
|
this.onCancel = options.onCancel ?? null;
|
|
6356
6713
|
const host = document.createElement("div");
|
|
@@ -6413,9 +6770,19 @@ var init_element_picker = __esm({
|
|
|
6413
6770
|
this.box = null;
|
|
6414
6771
|
this.label = null;
|
|
6415
6772
|
this.current = null;
|
|
6773
|
+
this.only = null;
|
|
6416
6774
|
this.onPick = null;
|
|
6417
6775
|
this.onCancel = null;
|
|
6418
6776
|
}
|
|
6777
|
+
/** Elemento que se elegiría para el nodo bajo el cursor (o null si no hay). */
|
|
6778
|
+
resolveTarget(target) {
|
|
6779
|
+
if (!(target instanceof Element) || this.isOwn(target)) return null;
|
|
6780
|
+
return this.only === "interactive" ? closestInteractive(target) : target;
|
|
6781
|
+
}
|
|
6782
|
+
hideBox() {
|
|
6783
|
+
if (this.box) this.box.style.display = "none";
|
|
6784
|
+
if (this.label) this.label.style.display = "none";
|
|
6785
|
+
}
|
|
6419
6786
|
/** ¿Es un nodo de nuestro propio overlay? (no debe ser seleccionable). */
|
|
6420
6787
|
isOwn(target) {
|
|
6421
6788
|
return target instanceof Node && this.host !== null && this.host.contains(target);
|
|
@@ -6452,7 +6819,7 @@ var init_element_picker = __esm({
|
|
|
6452
6819
|
tag: element.tagName.toLowerCase(),
|
|
6453
6820
|
id: element.id || null,
|
|
6454
6821
|
classes: filterHumanClasses(element),
|
|
6455
|
-
text: (element
|
|
6822
|
+
text: elementLabelText(element),
|
|
6456
6823
|
rect: {
|
|
6457
6824
|
x: Math.round(rect.left),
|
|
6458
6825
|
y: Math.round(rect.top),
|
|
@@ -7448,7 +7815,7 @@ function initBuilderMode() {
|
|
|
7448
7815
|
if (h && hoverCard && !previewMode) renderCardChrome(h, previewStepIndex);
|
|
7449
7816
|
}
|
|
7450
7817
|
panel?.setTitle(
|
|
7451
|
-
cmd.state.section === "activation" ? "Ajustes de activaci\xF3n" : cmd.state.section === "badge" ? "Ajustes de badge" : cmd.state.section === "target" ? "Ajustes del elemento objetivo" : cmd.state.section === "row" ? "Editar fila" : cmd.state.blockLabel ? `Editor \xB7 ${cmd.state.blockLabel}` : cmd.state.steps[cmd.state.activeStep]?.label ?? "Paso"
|
|
7818
|
+
cmd.state.panelTitle ?? (cmd.state.section === "activation" ? "Ajustes de activaci\xF3n" : cmd.state.section === "badge" ? "Ajustes de badge" : cmd.state.section === "target" ? "Ajustes del elemento objetivo" : cmd.state.section === "row" ? "Editar fila" : cmd.state.blockLabel ? `Editor \xB7 ${cmd.state.blockLabel}` : cmd.state.steps[cmd.state.activeStep]?.label ?? "Paso")
|
|
7452
7819
|
);
|
|
7453
7820
|
break;
|
|
7454
7821
|
case "panel-visibility":
|
|
@@ -7486,7 +7853,8 @@ function initBuilderMode() {
|
|
|
7486
7853
|
post({ type: "pick-cancelled" });
|
|
7487
7854
|
},
|
|
7488
7855
|
...cmd.maxAncestors !== void 0 ? { maxAncestors: cmd.maxAncestors } : {},
|
|
7489
|
-
...cmd.hint !== void 0 ? { hint: cmd.hint } : {}
|
|
7856
|
+
...cmd.hint !== void 0 ? { hint: cmd.hint } : {},
|
|
7857
|
+
...cmd.only !== void 0 ? { only: cmd.only } : {}
|
|
7490
7858
|
});
|
|
7491
7859
|
break;
|
|
7492
7860
|
}
|
|
@@ -7495,6 +7863,15 @@ function initBuilderMode() {
|
|
|
7495
7863
|
picker = null;
|
|
7496
7864
|
chrome?.setHidden(false);
|
|
7497
7865
|
break;
|
|
7866
|
+
case "scan-page":
|
|
7867
|
+
post({ type: "page-scan", payload: scanPage(cmd.selectors) });
|
|
7868
|
+
break;
|
|
7869
|
+
case "highlight":
|
|
7870
|
+
setHighlight(cmd.selector);
|
|
7871
|
+
break;
|
|
7872
|
+
case "show-confirmation":
|
|
7873
|
+
showConfirmation(cmd.payload);
|
|
7874
|
+
break;
|
|
7498
7875
|
case "preview":
|
|
7499
7876
|
if (!cmd.guide) break;
|
|
7500
7877
|
if (manipulator?.isDragging()) pendingPreview = cmd;
|
|
@@ -7579,10 +7956,154 @@ function initBuilderMode() {
|
|
|
7579
7956
|
post({ type: "closed" });
|
|
7580
7957
|
panel?.teardown();
|
|
7581
7958
|
panel = null;
|
|
7959
|
+
highlightBox?.remove();
|
|
7960
|
+
highlightBox = null;
|
|
7961
|
+
confirmation?.remove();
|
|
7962
|
+
confirmation = null;
|
|
7963
|
+
if (confirmationKey) document.removeEventListener("keydown", confirmationKey, true);
|
|
7964
|
+
confirmationKey = null;
|
|
7582
7965
|
chrome?.teardown();
|
|
7583
7966
|
chrome = null;
|
|
7584
7967
|
clearBuilderToken();
|
|
7585
7968
|
};
|
|
7969
|
+
const scanPage = (selectors) => {
|
|
7970
|
+
const present = selectors.filter((sel) => {
|
|
7971
|
+
try {
|
|
7972
|
+
return document.querySelector(sel) !== null;
|
|
7973
|
+
} catch {
|
|
7974
|
+
return false;
|
|
7975
|
+
}
|
|
7976
|
+
});
|
|
7977
|
+
const taggedEls = /* @__PURE__ */ new Set();
|
|
7978
|
+
for (const sel of present) {
|
|
7979
|
+
try {
|
|
7980
|
+
for (const e of Array.from(document.querySelectorAll(sel))) taggedEls.add(e);
|
|
7981
|
+
} catch {
|
|
7982
|
+
}
|
|
7983
|
+
}
|
|
7984
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7985
|
+
const candidates = [];
|
|
7986
|
+
const nodes = document.querySelectorAll(INTERACTIVE_SELECTOR);
|
|
7987
|
+
for (const element of Array.from(nodes)) {
|
|
7988
|
+
if (candidates.length >= 200) break;
|
|
7989
|
+
if (element.closest("[data-veo-builder-chrome],[data-veo-builder-panel],[data-veo-picker]")) {
|
|
7990
|
+
continue;
|
|
7991
|
+
}
|
|
7992
|
+
if (element.getClientRects().length === 0) continue;
|
|
7993
|
+
let tagged = false;
|
|
7994
|
+
for (let n = element; n; n = n.parentElement) {
|
|
7995
|
+
if (taggedEls.has(n)) {
|
|
7996
|
+
tagged = true;
|
|
7997
|
+
break;
|
|
7998
|
+
}
|
|
7999
|
+
}
|
|
8000
|
+
if (tagged) continue;
|
|
8001
|
+
const selector = buildSelectorPath(element, 5);
|
|
8002
|
+
if (!selector || seen.has(selector)) continue;
|
|
8003
|
+
seen.add(selector);
|
|
8004
|
+
candidates.push({
|
|
8005
|
+
selector,
|
|
8006
|
+
shortSelector: buildSelectorPath(element, 0),
|
|
8007
|
+
tag: element.tagName.toLowerCase(),
|
|
8008
|
+
id: element.id || null,
|
|
8009
|
+
classes: filterHumanClasses(element),
|
|
8010
|
+
text: elementLabelText(element)
|
|
8011
|
+
});
|
|
8012
|
+
}
|
|
8013
|
+
const { pathname, hash } = window.location;
|
|
8014
|
+
const routePath = /^#\/./.test(hash) ? `${pathname}${hash}` : pathname;
|
|
8015
|
+
return { pagePath: pathname, routePath, candidates, present };
|
|
8016
|
+
};
|
|
8017
|
+
let confirmation = null;
|
|
8018
|
+
let confirmationKey = null;
|
|
8019
|
+
const closeConfirmation = () => {
|
|
8020
|
+
if (!confirmation) return;
|
|
8021
|
+
confirmation.remove();
|
|
8022
|
+
confirmation = null;
|
|
8023
|
+
if (confirmationKey) document.removeEventListener("keydown", confirmationKey, true);
|
|
8024
|
+
confirmationKey = null;
|
|
8025
|
+
panel?.setVisible(true);
|
|
8026
|
+
post({ type: "confirmation-closed" });
|
|
8027
|
+
};
|
|
8028
|
+
const showConfirmation = (card) => {
|
|
8029
|
+
if (confirmation) {
|
|
8030
|
+
confirmation.remove();
|
|
8031
|
+
confirmation = null;
|
|
8032
|
+
}
|
|
8033
|
+
panel?.setVisible(false);
|
|
8034
|
+
const host = document.createElement("div");
|
|
8035
|
+
host.setAttribute("data-veo-builder-chrome", "");
|
|
8036
|
+
host.setAttribute("data-veo-confirmation", "");
|
|
8037
|
+
const shadow = host.attachShadow({ mode: "open" });
|
|
8038
|
+
const style = document.createElement("style");
|
|
8039
|
+
style.textContent = CONFIRMATION_STYLES;
|
|
8040
|
+
const backdrop = document.createElement("div");
|
|
8041
|
+
backdrop.className = "veo-confirm-backdrop";
|
|
8042
|
+
const dialog = document.createElement("div");
|
|
8043
|
+
dialog.className = "veo-confirm";
|
|
8044
|
+
dialog.setAttribute("role", "dialog");
|
|
8045
|
+
dialog.setAttribute("aria-modal", "true");
|
|
8046
|
+
dialog.setAttribute("aria-label", card.title);
|
|
8047
|
+
const badge = document.createElement("div");
|
|
8048
|
+
badge.className = "veo-confirm-check";
|
|
8049
|
+
badge.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg>';
|
|
8050
|
+
const title = document.createElement("h2");
|
|
8051
|
+
title.className = "veo-confirm-title";
|
|
8052
|
+
title.textContent = card.title;
|
|
8053
|
+
const message = document.createElement("p");
|
|
8054
|
+
message.className = "veo-confirm-msg";
|
|
8055
|
+
message.textContent = card.message;
|
|
8056
|
+
const rows = document.createElement("dl");
|
|
8057
|
+
rows.className = "veo-confirm-rows";
|
|
8058
|
+
for (const r of card.rows) {
|
|
8059
|
+
const row = document.createElement("div");
|
|
8060
|
+
row.className = "veo-confirm-row";
|
|
8061
|
+
const dt = document.createElement("dt");
|
|
8062
|
+
dt.textContent = r.label;
|
|
8063
|
+
const dd = document.createElement("dd");
|
|
8064
|
+
dd.textContent = r.value;
|
|
8065
|
+
row.append(dt, dd);
|
|
8066
|
+
rows.appendChild(row);
|
|
8067
|
+
}
|
|
8068
|
+
const button = document.createElement("button");
|
|
8069
|
+
button.type = "button";
|
|
8070
|
+
button.className = "veo-confirm-btn";
|
|
8071
|
+
button.textContent = card.buttonLabel ?? "Listo";
|
|
8072
|
+
button.addEventListener("click", closeConfirmation);
|
|
8073
|
+
dialog.append(badge, title, message);
|
|
8074
|
+
if (card.rows.length > 0) dialog.appendChild(rows);
|
|
8075
|
+
dialog.appendChild(button);
|
|
8076
|
+
shadow.append(style, backdrop, dialog);
|
|
8077
|
+
document.body.appendChild(host);
|
|
8078
|
+
confirmation = host;
|
|
8079
|
+
confirmationKey = (e) => {
|
|
8080
|
+
if (e.key !== "Escape") return;
|
|
8081
|
+
e.preventDefault();
|
|
8082
|
+
e.stopPropagation();
|
|
8083
|
+
closeConfirmation();
|
|
8084
|
+
};
|
|
8085
|
+
document.addEventListener("keydown", confirmationKey, true);
|
|
8086
|
+
button.focus();
|
|
8087
|
+
};
|
|
8088
|
+
let highlightBox = null;
|
|
8089
|
+
const setHighlight = (selector) => {
|
|
8090
|
+
highlightBox?.remove();
|
|
8091
|
+
highlightBox = null;
|
|
8092
|
+
if (!selector) return;
|
|
8093
|
+
let target = null;
|
|
8094
|
+
try {
|
|
8095
|
+
target = document.querySelector(selector);
|
|
8096
|
+
} catch {
|
|
8097
|
+
return;
|
|
8098
|
+
}
|
|
8099
|
+
if (!target) return;
|
|
8100
|
+
const r = target.getBoundingClientRect();
|
|
8101
|
+
const box = document.createElement("div");
|
|
8102
|
+
box.setAttribute("data-veo-builder-chrome", "");
|
|
8103
|
+
box.style.cssText = `position:fixed;left:${r.left - 3}px;top:${r.top - 3}px;width:${r.width + 6}px;height:${r.height + 6}px;border:2px solid #FF5B35;border-radius:6px;background:rgba(255,91,53,0.10);pointer-events:none;z-index:2147483646;`;
|
|
8104
|
+
document.body.appendChild(box);
|
|
8105
|
+
highlightBox = box;
|
|
8106
|
+
};
|
|
7586
8107
|
if (ctx.panel) {
|
|
7587
8108
|
chrome = injectBuilderChrome({
|
|
7588
8109
|
appLabel: window.location.hostname,
|
|
@@ -7710,10 +8231,12 @@ function previewProgressFetcher() {
|
|
|
7710
8231
|
return data.items ?? [];
|
|
7711
8232
|
};
|
|
7712
8233
|
}
|
|
8234
|
+
var CONFIRMATION_STYLES;
|
|
7713
8235
|
var init_builder_mode = __esm({
|
|
7714
8236
|
"src/plugins/builder/builder-mode.ts"() {
|
|
7715
8237
|
init_builder_token();
|
|
7716
8238
|
init_safe_env();
|
|
8239
|
+
init_selector_builder();
|
|
7717
8240
|
init_activation_devices();
|
|
7718
8241
|
init_activation_rules();
|
|
7719
8242
|
init_block_builder();
|
|
@@ -7728,6 +8251,42 @@ var init_builder_mode = __esm({
|
|
|
7728
8251
|
init_builder_panel();
|
|
7729
8252
|
init_design_manipulator();
|
|
7730
8253
|
init_element_picker();
|
|
8254
|
+
init_interactive_elements();
|
|
8255
|
+
CONFIRMATION_STYLES = `
|
|
8256
|
+
:host { all: initial; }
|
|
8257
|
+
.veo-confirm-backdrop {
|
|
8258
|
+
position: fixed; inset: 0; z-index: 2147483645;
|
|
8259
|
+
background: rgba(20, 16, 14, 0.45);
|
|
8260
|
+
}
|
|
8261
|
+
.veo-confirm {
|
|
8262
|
+
position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%);
|
|
8263
|
+
z-index: 2147483646; box-sizing: border-box;
|
|
8264
|
+
width: min(432px, calc(100vw - 32px)); padding: 28px 24px 24px;
|
|
8265
|
+
border-radius: 16px; background: #fff; color: #1d1917;
|
|
8266
|
+
box-shadow: 0 24px 64px rgba(0,0,0,0.28);
|
|
8267
|
+
font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
8268
|
+
text-align: center;
|
|
8269
|
+
}
|
|
8270
|
+
.veo-confirm-check {
|
|
8271
|
+
width: 64px; height: 64px; margin: 0 auto; border-radius: 50%;
|
|
8272
|
+
background: #e6f6df; color: #2f9a4e; display: flex; align-items: center; justify-content: center;
|
|
8273
|
+
}
|
|
8274
|
+
.veo-confirm-check svg { width: 30px; height: 30px; }
|
|
8275
|
+
.veo-confirm-title { margin: 18px 0 0; font-size: 17px; font-weight: 600; }
|
|
8276
|
+
.veo-confirm-msg { margin: 8px 0 0; font-size: 13px; color: #6b6461; }
|
|
8277
|
+
.veo-confirm-rows {
|
|
8278
|
+
margin: 18px 0 0; padding: 12px 14px; border-radius: 12px; background: #f6f2ef; text-align: left;
|
|
8279
|
+
}
|
|
8280
|
+
.veo-confirm-row { display: flex; justify-content: space-between; gap: 16px; font-size: 13px; padding: 4px 0; }
|
|
8281
|
+
.veo-confirm-row dt { color: #6b6461; margin: 0; }
|
|
8282
|
+
.veo-confirm-row dd { margin: 0; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
8283
|
+
.veo-confirm-btn {
|
|
8284
|
+
display: block; width: 100%; margin-top: 18px; height: 44px; border: 0; border-radius: 10px;
|
|
8285
|
+
background: #b74d00; color: #fff; font: inherit; font-size: 15px; font-weight: 600; cursor: pointer;
|
|
8286
|
+
}
|
|
8287
|
+
.veo-confirm-btn:hover { background: #953c00; }
|
|
8288
|
+
.veo-confirm-btn:focus-visible { outline: 3px solid rgba(183,77,0,0.35); outline-offset: 2px; }
|
|
8289
|
+
`;
|
|
7731
8290
|
}
|
|
7732
8291
|
});
|
|
7733
8292
|
|
|
@@ -7860,11 +8419,15 @@ var init_builder_session = __esm({
|
|
|
7860
8419
|
// src/plugins/builder/index.ts
|
|
7861
8420
|
var builder_exports = {};
|
|
7862
8421
|
__export(builder_exports, {
|
|
8422
|
+
INTERACTIVE_SELECTOR: () => INTERACTIVE_SELECTOR,
|
|
7863
8423
|
VEO_BLOCK_MIME: () => VEO_BLOCK_MIME,
|
|
7864
8424
|
attachBlockDropTarget: () => attachBlockDropTarget,
|
|
7865
8425
|
attachDesignManipulator: () => attachDesignManipulator,
|
|
8426
|
+
closestInteractive: () => closestInteractive,
|
|
7866
8427
|
createBuilderSession: () => createBuilderSession,
|
|
8428
|
+
elementLabelText: () => elementLabelText,
|
|
7867
8429
|
initBuilderMode: () => initBuilderMode,
|
|
8430
|
+
isInteractiveElement: () => isInteractiveElement,
|
|
7868
8431
|
startElementPicker: () => startElementPicker,
|
|
7869
8432
|
stopElementPicker: () => stopElementPicker
|
|
7870
8433
|
});
|
|
@@ -7875,6 +8438,472 @@ var init_builder = __esm({
|
|
|
7875
8438
|
init_builder_session();
|
|
7876
8439
|
init_design_manipulator();
|
|
7877
8440
|
init_element_picker();
|
|
8441
|
+
init_interactive_elements();
|
|
8442
|
+
}
|
|
8443
|
+
});
|
|
8444
|
+
|
|
8445
|
+
// src/plugins/theme-detect/color.ts
|
|
8446
|
+
function parseRgb(value) {
|
|
8447
|
+
const m = RGB_RX.exec(value.trim());
|
|
8448
|
+
if (!m) return null;
|
|
8449
|
+
const a = m[4] === void 0 ? 1 : m[4].endsWith("%") ? Number.parseFloat(m[4]) / 100 : +m[4];
|
|
8450
|
+
return [+m[1], +m[2], +m[3], a];
|
|
8451
|
+
}
|
|
8452
|
+
function toCssColor(raw) {
|
|
8453
|
+
const v = raw.trim();
|
|
8454
|
+
const parts = v.split(/[\s,/]+/).filter(Boolean);
|
|
8455
|
+
const bare = parts.length >= 3 && parts.every((p) => /^-?[\d.]+(%|deg)?$/.test(p));
|
|
8456
|
+
if (!bare) return v;
|
|
8457
|
+
return v.includes("%") ? `hsl(${v})` : `rgb(${v})`;
|
|
8458
|
+
}
|
|
8459
|
+
function looksLikeColor(raw) {
|
|
8460
|
+
const v = raw.trim();
|
|
8461
|
+
if (!v || v.length > 64) return false;
|
|
8462
|
+
return /^(#|rgb|hsl|hwb|lab|lch|oklab|oklch|color\(|color-mix|light-dark)/i.test(v) || toCssColor(v) !== v;
|
|
8463
|
+
}
|
|
8464
|
+
function flatten(c, bg) {
|
|
8465
|
+
if (c[3] >= 1) return c;
|
|
8466
|
+
const mix = (i) => Math.round(c[i] * c[3] + bg[i] * (1 - c[3]));
|
|
8467
|
+
return [mix(0), mix(1), mix(2), 1];
|
|
8468
|
+
}
|
|
8469
|
+
function toHex(c) {
|
|
8470
|
+
const h = (n) => Math.round(n).toString(16).padStart(2, "0");
|
|
8471
|
+
return `#${h(c[0])}${h(c[1])}${h(c[2])}`.toUpperCase();
|
|
8472
|
+
}
|
|
8473
|
+
function luminance(c) {
|
|
8474
|
+
return (0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2]) / 255;
|
|
8475
|
+
}
|
|
8476
|
+
function saturation(c) {
|
|
8477
|
+
const mx = Math.max(c[0], c[1], c[2]) / 255;
|
|
8478
|
+
const mn = Math.min(c[0], c[1], c[2]) / 255;
|
|
8479
|
+
const l = (mx + mn) / 2;
|
|
8480
|
+
return mx === mn ? 0 : (mx - mn) / (1 - Math.abs(2 * l - 1));
|
|
8481
|
+
}
|
|
8482
|
+
function createColorResolver(doc) {
|
|
8483
|
+
const probe = doc.createElement("span");
|
|
8484
|
+
probe.style.display = "none";
|
|
8485
|
+
doc.body.appendChild(probe);
|
|
8486
|
+
const view = doc.defaultView;
|
|
8487
|
+
let ctx;
|
|
8488
|
+
const cache = /* @__PURE__ */ new Map();
|
|
8489
|
+
const viaCanvas = (computed) => {
|
|
8490
|
+
if (ctx === void 0) {
|
|
8491
|
+
try {
|
|
8492
|
+
const canvas = doc.createElement("canvas");
|
|
8493
|
+
canvas.width = 1;
|
|
8494
|
+
canvas.height = 1;
|
|
8495
|
+
ctx = canvas.getContext("2d", { willReadFrequently: true });
|
|
8496
|
+
} catch {
|
|
8497
|
+
ctx = null;
|
|
8498
|
+
}
|
|
8499
|
+
}
|
|
8500
|
+
if (!ctx) return null;
|
|
8501
|
+
ctx.clearRect(0, 0, 1, 1);
|
|
8502
|
+
ctx.fillStyle = "#000";
|
|
8503
|
+
ctx.fillStyle = computed;
|
|
8504
|
+
ctx.fillRect(0, 0, 1, 1);
|
|
8505
|
+
const d = ctx.getImageData(0, 0, 1, 1).data;
|
|
8506
|
+
return [d[0] ?? 0, d[1] ?? 0, d[2] ?? 0, (d[3] ?? 255) / 255];
|
|
8507
|
+
};
|
|
8508
|
+
return {
|
|
8509
|
+
resolve(raw) {
|
|
8510
|
+
const key = raw.trim();
|
|
8511
|
+
if (!key || !view) return null;
|
|
8512
|
+
const hit = cache.get(key);
|
|
8513
|
+
if (hit !== void 0) return hit;
|
|
8514
|
+
let out = null;
|
|
8515
|
+
probe.style.color = "";
|
|
8516
|
+
probe.style.color = toCssColor(key);
|
|
8517
|
+
if (probe.style.color) {
|
|
8518
|
+
const computed = view.getComputedStyle(probe).color;
|
|
8519
|
+
out = parseRgb(computed) ?? viaCanvas(computed);
|
|
8520
|
+
}
|
|
8521
|
+
cache.set(key, out);
|
|
8522
|
+
return out;
|
|
8523
|
+
},
|
|
8524
|
+
dispose() {
|
|
8525
|
+
probe.remove();
|
|
8526
|
+
}
|
|
8527
|
+
};
|
|
8528
|
+
}
|
|
8529
|
+
var RGB_RX;
|
|
8530
|
+
var init_color = __esm({
|
|
8531
|
+
"src/plugins/theme-detect/color.ts"() {
|
|
8532
|
+
RGB_RX = /^rgba?\(\s*([\d.]+)[,\s]+([\d.]+)[,\s]+([\d.]+)(?:\s*[,/]\s*([\d.]+%?))?\s*\)$/;
|
|
8533
|
+
}
|
|
8534
|
+
});
|
|
8535
|
+
|
|
8536
|
+
// src/plugins/theme-detect/fonts.ts
|
|
8537
|
+
function cleanFamily(name) {
|
|
8538
|
+
return name.trim().replace(/^["']|["']$/g, "");
|
|
8539
|
+
}
|
|
8540
|
+
function primaryFamily(stack) {
|
|
8541
|
+
const families = stack.split(",").map(cleanFamily).filter(Boolean);
|
|
8542
|
+
return families.find((f) => !GENERIC.test(f) && !/fallback/i.test(f)) ?? families[0] ?? "";
|
|
8543
|
+
}
|
|
8544
|
+
function prettyFamily(family) {
|
|
8545
|
+
return family.replace(/^__/, "").replace(/_Fallback_[0-9a-f]+$/i, " Fallback").replace(/_[0-9a-f]{6,}$/i, "").replace(/_/g, " ").trim();
|
|
8546
|
+
}
|
|
8547
|
+
function describeFont(doc, el2, faces) {
|
|
8548
|
+
const view = doc.defaultView;
|
|
8549
|
+
if (!el2 || !view) return void 0;
|
|
8550
|
+
const family = primaryFamily(view.getComputedStyle(el2).fontFamily);
|
|
8551
|
+
if (!family) return void 0;
|
|
8552
|
+
const font = {
|
|
8553
|
+
name: prettyFamily(family).slice(0, 80),
|
|
8554
|
+
family: family.slice(0, 120)
|
|
8555
|
+
};
|
|
8556
|
+
const weights = /* @__PURE__ */ new Set();
|
|
8557
|
+
try {
|
|
8558
|
+
for (const face of Array.from(doc.fonts ?? [])) {
|
|
8559
|
+
if (cleanFamily(face.family) === family && face.status === "loaded") weights.add(face.weight);
|
|
8560
|
+
}
|
|
8561
|
+
} catch {
|
|
8562
|
+
}
|
|
8563
|
+
if (weights.size > 0) font.weights = [...weights].sort().join(" ").slice(0, 80);
|
|
8564
|
+
const own = faces.filter((f) => f.family === family);
|
|
8565
|
+
const url = own.find((f) => f.url)?.url ?? null;
|
|
8566
|
+
let origin = null;
|
|
8567
|
+
try {
|
|
8568
|
+
origin = url ? new URL(url, doc.baseURI).origin : null;
|
|
8569
|
+
} catch {
|
|
8570
|
+
origin = null;
|
|
8571
|
+
}
|
|
8572
|
+
const links = Array.from(doc.querySelectorAll("link[href]")).map((l) => l.href);
|
|
8573
|
+
if (origin) {
|
|
8574
|
+
font.hosting = origin.includes("fonts.gstatic.com") ? "google" : origin === view.location.origin ? "self" : "external";
|
|
8575
|
+
} else if (own.length > 0) {
|
|
8576
|
+
font.hosting = "self";
|
|
8577
|
+
} else if (links.some((h) => h.includes("fonts.googleapis.com"))) {
|
|
8578
|
+
font.hosting = "google";
|
|
8579
|
+
} else if (links.some((h) => h.includes("use.typekit.net"))) {
|
|
8580
|
+
font.hosting = "adobe";
|
|
8581
|
+
} else {
|
|
8582
|
+
font.hosting = "system";
|
|
8583
|
+
}
|
|
8584
|
+
return font;
|
|
8585
|
+
}
|
|
8586
|
+
var GENERIC;
|
|
8587
|
+
var init_fonts = __esm({
|
|
8588
|
+
"src/plugins/theme-detect/fonts.ts"() {
|
|
8589
|
+
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;
|
|
8590
|
+
}
|
|
8591
|
+
});
|
|
8592
|
+
|
|
8593
|
+
// src/plugins/theme-detect/detect-theme.ts
|
|
8594
|
+
var detect_theme_exports = {};
|
|
8595
|
+
__export(detect_theme_exports, {
|
|
8596
|
+
detectTheme: () => detectTheme,
|
|
8597
|
+
snapshotHash: () => snapshotHash
|
|
8598
|
+
});
|
|
8599
|
+
function scanSheets(doc) {
|
|
8600
|
+
const scan = {
|
|
8601
|
+
names: /* @__PURE__ */ new Set(),
|
|
8602
|
+
byMode: { light: /* @__PURE__ */ new Map(), dark: /* @__PURE__ */ new Map() },
|
|
8603
|
+
faces: []
|
|
8604
|
+
};
|
|
8605
|
+
const walk = (rules, inDarkMedia) => {
|
|
8606
|
+
for (const rule of Array.from(rules)) {
|
|
8607
|
+
const style = rule.style;
|
|
8608
|
+
const selector = rule.selectorText;
|
|
8609
|
+
if (rule.constructor.name === "CSSFontFaceRule" && style) {
|
|
8610
|
+
const src = style.getPropertyValue("src");
|
|
8611
|
+
const url = /url\(["']?([^"')]+)["']?\)/.exec(src)?.[1] ?? null;
|
|
8612
|
+
scan.faces.push({ family: cleanFamily(style.getPropertyValue("font-family")), url });
|
|
8613
|
+
} else if (style && typeof selector === "string") {
|
|
8614
|
+
const selectors = selector.split(",").map((s) => s.trim());
|
|
8615
|
+
const mode = selectors.some((s) => DARK_SELECTOR.test(s)) ? "dark" : selectors.some((s) => LIGHT_SELECTOR.test(s)) ? inDarkMedia ? "dark" : "light" : null;
|
|
8616
|
+
for (const prop of Array.from(style)) {
|
|
8617
|
+
if (!prop.startsWith("--")) continue;
|
|
8618
|
+
scan.names.add(prop);
|
|
8619
|
+
if (mode) scan.byMode[mode].set(prop, style.getPropertyValue(prop).trim());
|
|
8620
|
+
}
|
|
8621
|
+
}
|
|
8622
|
+
const nested = rule.cssRules;
|
|
8623
|
+
if (nested) {
|
|
8624
|
+
const media = rule.conditionText ?? "";
|
|
8625
|
+
walk(nested, inDarkMedia || /prefers-color-scheme:\s*dark/.test(media));
|
|
8626
|
+
}
|
|
8627
|
+
}
|
|
8628
|
+
};
|
|
8629
|
+
for (const sheet of Array.from(doc.styleSheets)) {
|
|
8630
|
+
try {
|
|
8631
|
+
walk(sheet.cssRules, false);
|
|
8632
|
+
} catch {
|
|
8633
|
+
}
|
|
8634
|
+
}
|
|
8635
|
+
return scan;
|
|
8636
|
+
}
|
|
8637
|
+
function top(counts) {
|
|
8638
|
+
let best = null;
|
|
8639
|
+
let max = 0;
|
|
8640
|
+
for (const [hex, n] of counts) {
|
|
8641
|
+
if (n > max) {
|
|
8642
|
+
max = n;
|
|
8643
|
+
best = hex;
|
|
8644
|
+
}
|
|
8645
|
+
}
|
|
8646
|
+
return best;
|
|
8647
|
+
}
|
|
8648
|
+
function detectTheme(doc = document) {
|
|
8649
|
+
const view = doc.defaultView;
|
|
8650
|
+
const body = doc.body;
|
|
8651
|
+
if (!view || !body) return null;
|
|
8652
|
+
const html = doc.documentElement;
|
|
8653
|
+
const colors = createColorResolver(doc);
|
|
8654
|
+
const modeProbe = doc.createElement("div");
|
|
8655
|
+
try {
|
|
8656
|
+
const bodyCs = view.getComputedStyle(body);
|
|
8657
|
+
let bg = [255, 255, 255, 1];
|
|
8658
|
+
for (const el2 of [body, html]) {
|
|
8659
|
+
const c = colors.resolve(view.getComputedStyle(el2).backgroundColor);
|
|
8660
|
+
if (c && c[3] > 0) {
|
|
8661
|
+
bg = flatten(c, [255, 255, 255, 1]);
|
|
8662
|
+
break;
|
|
8663
|
+
}
|
|
8664
|
+
}
|
|
8665
|
+
const text = flatten(colors.resolve(bodyCs.color) ?? [0, 0, 0, 1], bg);
|
|
8666
|
+
const textHex = toHex(text);
|
|
8667
|
+
const muted = /* @__PURE__ */ new Map();
|
|
8668
|
+
const border = /* @__PURE__ */ new Map();
|
|
8669
|
+
const accent = /* @__PURE__ */ new Map();
|
|
8670
|
+
const link = /* @__PURE__ */ new Map();
|
|
8671
|
+
const bump = (m, k) => {
|
|
8672
|
+
m.set(k, (m.get(k) ?? 0) + 1);
|
|
8673
|
+
};
|
|
8674
|
+
const contrast = Math.abs(luminance(text) - luminance(bg));
|
|
8675
|
+
const elements = Array.from(body.querySelectorAll("*")).slice(0, MAX_ELEMENTS);
|
|
8676
|
+
for (const el2 of elements) {
|
|
8677
|
+
if (el2.getClientRects().length === 0) continue;
|
|
8678
|
+
const cs = view.getComputedStyle(el2);
|
|
8679
|
+
const hasText = Array.from(el2.childNodes).some(
|
|
8680
|
+
(n) => n.nodeType === 3 && (n.textContent ?? "").trim() !== ""
|
|
8681
|
+
);
|
|
8682
|
+
if (hasText) {
|
|
8683
|
+
const c = colors.resolve(cs.color);
|
|
8684
|
+
if (c && c[3] > 0) {
|
|
8685
|
+
const flat = flatten(c, bg);
|
|
8686
|
+
const hex = toHex(flat);
|
|
8687
|
+
const softer = Math.abs(luminance(flat) - luminance(bg)) < contrast;
|
|
8688
|
+
if (hex !== textHex && saturation(flat) < 0.3 && softer) bump(muted, hex);
|
|
8689
|
+
if (el2.closest("a") && saturation(flat) >= 0.3) bump(link, hex);
|
|
8690
|
+
}
|
|
8691
|
+
}
|
|
8692
|
+
if (Number.parseFloat(cs.borderTopWidth) > 0 && cs.borderTopStyle !== "none") {
|
|
8693
|
+
const c = colors.resolve(cs.borderTopColor);
|
|
8694
|
+
if (c && c[3] > 0) {
|
|
8695
|
+
const flat = flatten(c, bg);
|
|
8696
|
+
if (saturation(flat) < 0.3 && toHex(flat) !== textHex) bump(border, toHex(flat));
|
|
8697
|
+
}
|
|
8698
|
+
}
|
|
8699
|
+
if (el2.matches('button,[role="button"],input[type="submit"],a[class*="btn"],a[class*="button"]')) {
|
|
8700
|
+
const c = colors.resolve(cs.backgroundColor);
|
|
8701
|
+
if (c && c[3] > 0.5 && saturation(c) >= 0.3) bump(accent, toHex(c));
|
|
8702
|
+
}
|
|
8703
|
+
}
|
|
8704
|
+
const meta = doc.querySelector('meta[name="theme-color"]');
|
|
8705
|
+
const metaColor = meta ? colors.resolve(meta.content) : null;
|
|
8706
|
+
const sampled = {
|
|
8707
|
+
background: toHex(bg),
|
|
8708
|
+
text: textHex
|
|
8709
|
+
};
|
|
8710
|
+
const sampledAccent = top(accent) ?? top(link) ?? (metaColor ? toHex(metaColor) : null);
|
|
8711
|
+
if (sampledAccent) sampled.accent = sampledAccent;
|
|
8712
|
+
const sampledMuted = top(muted);
|
|
8713
|
+
if (sampledMuted) sampled.textMuted = sampledMuted;
|
|
8714
|
+
const sampledBorder = top(border);
|
|
8715
|
+
if (sampledBorder) sampled.border = sampledBorder;
|
|
8716
|
+
const hints = [
|
|
8717
|
+
html.className,
|
|
8718
|
+
html.dataset.theme,
|
|
8719
|
+
html.dataset.mode,
|
|
8720
|
+
body.className,
|
|
8721
|
+
body.dataset.theme
|
|
8722
|
+
].join(" ").toLowerCase();
|
|
8723
|
+
const mode = /\bdark\b/.test(hints) ? "dark" : /\blight\b/.test(hints) ? "light" : view.getComputedStyle(html).colorScheme === "dark" || luminance(bg) < 0.4 ? "dark" : "light";
|
|
8724
|
+
const other = mode === "dark" ? "light" : "dark";
|
|
8725
|
+
modeProbe.className = other;
|
|
8726
|
+
modeProbe.dataset.theme = other;
|
|
8727
|
+
modeProbe.dataset.mode = other;
|
|
8728
|
+
modeProbe.style.cssText = `display:none;color-scheme:${other}`;
|
|
8729
|
+
body.appendChild(modeProbe);
|
|
8730
|
+
const probeCs = view.getComputedStyle(modeProbe);
|
|
8731
|
+
const hexOf = (raw, over) => {
|
|
8732
|
+
if (!looksLikeColor(raw)) return null;
|
|
8733
|
+
const c = colors.resolve(raw);
|
|
8734
|
+
if (!c || c[3] === 0) return null;
|
|
8735
|
+
if (c[3] > 0.98) return toHex(c);
|
|
8736
|
+
return over ? toHex(flatten(c, over)) : null;
|
|
8737
|
+
};
|
|
8738
|
+
const scan = scanSheets(doc);
|
|
8739
|
+
const roleVar = {};
|
|
8740
|
+
const current = {};
|
|
8741
|
+
for (const role of ROLES) {
|
|
8742
|
+
for (const name of KNOWN_VARS[role]) {
|
|
8743
|
+
const hex = hexOf(bodyCs.getPropertyValue(name), bg);
|
|
8744
|
+
if (hex) {
|
|
8745
|
+
roleVar[role] = { name, confidence: 3 };
|
|
8746
|
+
current[role] = { hex, confidence: 3 };
|
|
8747
|
+
break;
|
|
8748
|
+
}
|
|
8749
|
+
}
|
|
8750
|
+
}
|
|
8751
|
+
const missing = ROLES.filter((r) => !roleVar[r] && sampled[r]);
|
|
8752
|
+
if (missing.length > 0) {
|
|
8753
|
+
const candidates = [];
|
|
8754
|
+
for (const name of Array.from(scan.names).slice(0, MAX_VARS)) {
|
|
8755
|
+
const cur = hexOf(bodyCs.getPropertyValue(name));
|
|
8756
|
+
if (cur) candidates.push({ name, cur, oth: hexOf(probeCs.getPropertyValue(name)) });
|
|
8757
|
+
}
|
|
8758
|
+
for (const role of missing) {
|
|
8759
|
+
let best = null;
|
|
8760
|
+
for (const c of candidates) {
|
|
8761
|
+
if (c.cur !== sampled[role]) continue;
|
|
8762
|
+
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);
|
|
8763
|
+
if (!best || score > best.score) best = { name: c.name, score };
|
|
8764
|
+
}
|
|
8765
|
+
if (best && best.score >= 2) roleVar[role] = { name: best.name, confidence: 2 };
|
|
8766
|
+
}
|
|
8767
|
+
}
|
|
8768
|
+
for (const role of ROLES) {
|
|
8769
|
+
const hex = sampled[role];
|
|
8770
|
+
if (!current[role] && hex) current[role] = { hex, confidence: roleVar[role] ? 2 : 1 };
|
|
8771
|
+
}
|
|
8772
|
+
const otherPalette = {};
|
|
8773
|
+
let differs = false;
|
|
8774
|
+
const otherOrder = ["background", "accent", "text", "textMuted", "border"];
|
|
8775
|
+
let otherBg = null;
|
|
8776
|
+
const noteBg = (role, hex) => {
|
|
8777
|
+
if (role === "background") otherBg = colors.resolve(hex);
|
|
8778
|
+
};
|
|
8779
|
+
for (const role of otherOrder) {
|
|
8780
|
+
const ref = roleVar[role];
|
|
8781
|
+
if (!ref) continue;
|
|
8782
|
+
const hex = hexOf(probeCs.getPropertyValue(ref.name), otherBg);
|
|
8783
|
+
if (!hex) continue;
|
|
8784
|
+
noteBg(role, hex);
|
|
8785
|
+
otherPalette[role] = { hex, confidence: ref.confidence };
|
|
8786
|
+
if (hex !== current[role]?.hex) differs = true;
|
|
8787
|
+
}
|
|
8788
|
+
if (!differs) {
|
|
8789
|
+
for (const role of ROLES) delete otherPalette[role];
|
|
8790
|
+
otherBg = null;
|
|
8791
|
+
const sheet = scan.byMode[other];
|
|
8792
|
+
const resolveVar = (value, depth = 0) => depth > 6 ? value : value.replace(
|
|
8793
|
+
/var\(\s*(--[\w-]+)\s*(?:,\s*([^()]*))?\)/g,
|
|
8794
|
+
(_m, name, fb) => resolveVar(
|
|
8795
|
+
sheet.get(name) ?? scan.byMode.light.get(name) ?? (bodyCs.getPropertyValue(name).trim() || fb || ""),
|
|
8796
|
+
depth + 1
|
|
8797
|
+
)
|
|
8798
|
+
);
|
|
8799
|
+
for (const role of otherOrder) {
|
|
8800
|
+
const ref = roleVar[role];
|
|
8801
|
+
const raw = ref ? sheet.get(ref.name) : void 0;
|
|
8802
|
+
const hex = raw ? hexOf(resolveVar(raw), otherBg) : null;
|
|
8803
|
+
if (!hex) continue;
|
|
8804
|
+
noteBg(role, hex);
|
|
8805
|
+
otherPalette[role] = { hex, confidence: 2 };
|
|
8806
|
+
if (hex !== current[role]?.hex) differs = true;
|
|
8807
|
+
}
|
|
8808
|
+
}
|
|
8809
|
+
const palettes = { [mode]: current };
|
|
8810
|
+
if (differs) palettes[other] = otherPalette;
|
|
8811
|
+
const firstVisible = (selector) => Array.from(doc.querySelectorAll(selector)).find(
|
|
8812
|
+
(el2) => el2.getClientRects().length > 0 && (el2.textContent ?? "").trim() !== ""
|
|
8813
|
+
) ?? null;
|
|
8814
|
+
const bodyFont = describeFont(doc, body, scan.faces);
|
|
8815
|
+
const headingFont = describeFont(doc, firstVisible("h1,h2,h3"), scan.faces);
|
|
8816
|
+
const fonts = {};
|
|
8817
|
+
if (bodyFont) fonts.body = bodyFont;
|
|
8818
|
+
if (headingFont) fonts.heading = headingFont;
|
|
8819
|
+
const snapshot = { host: view.location.host, mode, palettes };
|
|
8820
|
+
const appName = doc.title.trim().slice(0, 120);
|
|
8821
|
+
if (appName) snapshot.appName = appName;
|
|
8822
|
+
if (fonts.body || fonts.heading) snapshot.fonts = fonts;
|
|
8823
|
+
return snapshot;
|
|
8824
|
+
} finally {
|
|
8825
|
+
colors.dispose();
|
|
8826
|
+
modeProbe.remove();
|
|
8827
|
+
}
|
|
8828
|
+
}
|
|
8829
|
+
function snapshotHash(snapshot) {
|
|
8830
|
+
const colorsOf = (p) => ROLES.map((r) => {
|
|
8831
|
+
const c = p?.[r];
|
|
8832
|
+
return c ? `${c.hex}${c.confidence}` : "-";
|
|
8833
|
+
}).join("");
|
|
8834
|
+
return [
|
|
8835
|
+
snapshot.host,
|
|
8836
|
+
colorsOf(snapshot.palettes.light),
|
|
8837
|
+
colorsOf(snapshot.palettes.dark),
|
|
8838
|
+
snapshot.fonts?.body?.family ?? "",
|
|
8839
|
+
snapshot.fonts?.heading?.family ?? ""
|
|
8840
|
+
].join("|");
|
|
8841
|
+
}
|
|
8842
|
+
var ROLES, KNOWN_VARS, ROLE_NAME, PALETTE_NAME, DARK_SELECTOR, LIGHT_SELECTOR, MAX_ELEMENTS, MAX_VARS;
|
|
8843
|
+
var init_detect_theme = __esm({
|
|
8844
|
+
"src/plugins/theme-detect/detect-theme.ts"() {
|
|
8845
|
+
init_color();
|
|
8846
|
+
init_fonts();
|
|
8847
|
+
ROLES = ["accent", "text", "textMuted", "background", "border"];
|
|
8848
|
+
KNOWN_VARS = {
|
|
8849
|
+
accent: [
|
|
8850
|
+
"--primary",
|
|
8851
|
+
"--color-primary",
|
|
8852
|
+
"--brand",
|
|
8853
|
+
"--color-brand",
|
|
8854
|
+
"--brand-primary",
|
|
8855
|
+
"--mui-palette-primary-main",
|
|
8856
|
+
"--bs-primary",
|
|
8857
|
+
"--ant-color-primary",
|
|
8858
|
+
"--chakra-colors-brand-500",
|
|
8859
|
+
"--accent-color",
|
|
8860
|
+
"--accent"
|
|
8861
|
+
],
|
|
8862
|
+
text: [
|
|
8863
|
+
"--foreground",
|
|
8864
|
+
"--color-foreground",
|
|
8865
|
+
"--text",
|
|
8866
|
+
"--color-text",
|
|
8867
|
+
"--text-primary",
|
|
8868
|
+
"--bs-body-color",
|
|
8869
|
+
"--mui-palette-text-primary"
|
|
8870
|
+
],
|
|
8871
|
+
textMuted: [
|
|
8872
|
+
"--muted-foreground",
|
|
8873
|
+
"--color-muted-foreground",
|
|
8874
|
+
"--text-muted",
|
|
8875
|
+
"--text-secondary",
|
|
8876
|
+
"--bs-secondary-color",
|
|
8877
|
+
"--mui-palette-text-secondary"
|
|
8878
|
+
],
|
|
8879
|
+
background: [
|
|
8880
|
+
"--background",
|
|
8881
|
+
"--color-background",
|
|
8882
|
+
"--bg",
|
|
8883
|
+
"--color-bg",
|
|
8884
|
+
"--bs-body-bg",
|
|
8885
|
+
"--mui-palette-background-default"
|
|
8886
|
+
],
|
|
8887
|
+
border: [
|
|
8888
|
+
"--border",
|
|
8889
|
+
"--color-border",
|
|
8890
|
+
"--border-color",
|
|
8891
|
+
"--bs-border-color",
|
|
8892
|
+
"--mui-palette-divider"
|
|
8893
|
+
]
|
|
8894
|
+
};
|
|
8895
|
+
ROLE_NAME = {
|
|
8896
|
+
accent: /brand|primary|accent|main/i,
|
|
8897
|
+
text: /text|foreground|\bfg\b|-fg|ink|content|body/i,
|
|
8898
|
+
textMuted: /muted|secondary|subtle|soft|hint|placeholder|text|-fg/i,
|
|
8899
|
+
background: /background|\bbg\b|-bg|surface|canvas|page|base/i,
|
|
8900
|
+
border: /border|line|divider|stroke|outline|separator/i
|
|
8901
|
+
};
|
|
8902
|
+
PALETTE_NAME = /-(50|[1-9]00|950)$|white|black/i;
|
|
8903
|
+
DARK_SELECTOR = /\.dark\b|\[data-(theme|mode|color-scheme)=["']?dark|\.theme-dark|\.dark-theme/i;
|
|
8904
|
+
LIGHT_SELECTOR = /^(:root|html|:host|body)$|\.light\b|\[data-(theme|mode)=["']?light/i;
|
|
8905
|
+
MAX_ELEMENTS = 1500;
|
|
8906
|
+
MAX_VARS = 600;
|
|
7878
8907
|
}
|
|
7879
8908
|
});
|
|
7880
8909
|
|
|
@@ -7949,6 +8978,298 @@ function definePlugin(plugin) {
|
|
|
7949
8978
|
|
|
7950
8979
|
// src/plugins/autocapture/dom-listener.ts
|
|
7951
8980
|
init_safe_env();
|
|
8981
|
+
|
|
8982
|
+
// src/plugins/autocapture/click-quality.ts
|
|
8983
|
+
init_safe_env();
|
|
8984
|
+
init_selector_builder();
|
|
8985
|
+
var NO_DEAD_TAGS = /* @__PURE__ */ new Set(["input", "textarea", "select", "option", "label"]);
|
|
8986
|
+
var MAX_ERROR_MESSAGE = 200;
|
|
8987
|
+
var HISTORY_MS = 3e3;
|
|
8988
|
+
var DEFAULTS = {
|
|
8989
|
+
reactionWindowMs: 1e3,
|
|
8990
|
+
errorWindowMs: 2e3,
|
|
8991
|
+
rageClicks: 3,
|
|
8992
|
+
rageWindowMs: 1e3,
|
|
8993
|
+
rageClicksSlow: 5,
|
|
8994
|
+
rageWindowSlowMs: 3e3,
|
|
8995
|
+
maxPerElement: 50
|
|
8996
|
+
};
|
|
8997
|
+
function describeError(value) {
|
|
8998
|
+
let text;
|
|
8999
|
+
if (value instanceof Error) text = `${value.name}: ${value.message}`;
|
|
9000
|
+
else if (typeof value === "string") text = value;
|
|
9001
|
+
else {
|
|
9002
|
+
try {
|
|
9003
|
+
text = JSON.stringify(value) ?? String(value);
|
|
9004
|
+
} catch {
|
|
9005
|
+
text = String(value);
|
|
9006
|
+
}
|
|
9007
|
+
}
|
|
9008
|
+
return text.slice(0, MAX_ERROR_MESSAGE);
|
|
9009
|
+
}
|
|
9010
|
+
var ClickQualityTracker = class {
|
|
9011
|
+
constructor(emit, options = {}) {
|
|
9012
|
+
this.emit = emit;
|
|
9013
|
+
this.installed = false;
|
|
9014
|
+
this.cleanups = [];
|
|
9015
|
+
// Sensores globales (contadores: cada observación compara su foto inicial).
|
|
9016
|
+
this.netCount = 0;
|
|
9017
|
+
this.navCount = 0;
|
|
9018
|
+
this.focusCount = 0;
|
|
9019
|
+
this.unloading = false;
|
|
9020
|
+
this.errors = [];
|
|
9021
|
+
this.history = /* @__PURE__ */ new Map();
|
|
9022
|
+
this.bursts = /* @__PURE__ */ new Map();
|
|
9023
|
+
this.emitted = /* @__PURE__ */ new Map();
|
|
9024
|
+
this.opts = { ...DEFAULTS, ...options };
|
|
9025
|
+
}
|
|
9026
|
+
install() {
|
|
9027
|
+
if (this.installed || !hasWindow()) return;
|
|
9028
|
+
this.installed = true;
|
|
9029
|
+
const on = (type, handler, capture = true) => {
|
|
9030
|
+
window.addEventListener(type, handler, capture);
|
|
9031
|
+
this.cleanups.push(() => window.removeEventListener(type, handler, capture));
|
|
9032
|
+
};
|
|
9033
|
+
on("error", (e) => {
|
|
9034
|
+
const err = e;
|
|
9035
|
+
this.pushError("error", err.error ?? err.message ?? "error");
|
|
9036
|
+
});
|
|
9037
|
+
on("unhandledrejection", (e) => {
|
|
9038
|
+
this.pushError("unhandledrejection", e.reason ?? "rejection");
|
|
9039
|
+
});
|
|
9040
|
+
on("popstate", () => this.navCount++);
|
|
9041
|
+
on("hashchange", () => this.navCount++);
|
|
9042
|
+
on("focusin", () => this.focusCount++);
|
|
9043
|
+
on("pagehide", () => {
|
|
9044
|
+
this.unloading = true;
|
|
9045
|
+
});
|
|
9046
|
+
on("beforeunload", () => {
|
|
9047
|
+
this.unloading = true;
|
|
9048
|
+
});
|
|
9049
|
+
this.patchHistory();
|
|
9050
|
+
this.patchConsole();
|
|
9051
|
+
this.patchFetch();
|
|
9052
|
+
this.patchXhr();
|
|
9053
|
+
}
|
|
9054
|
+
destroy() {
|
|
9055
|
+
for (const c of this.cleanups) c();
|
|
9056
|
+
this.cleanups = [];
|
|
9057
|
+
for (const b of this.bursts.values()) window.clearTimeout(b.timer);
|
|
9058
|
+
this.bursts.clear();
|
|
9059
|
+
this.history.clear();
|
|
9060
|
+
this.installed = false;
|
|
9061
|
+
}
|
|
9062
|
+
/** Llamar por cada clic real sobre un elemento interactivo, ANTES del throttle. */
|
|
9063
|
+
onClick(element, event) {
|
|
9064
|
+
if (!this.installed) return;
|
|
9065
|
+
const now = Date.now();
|
|
9066
|
+
const obs = { at: now, reacted: false };
|
|
9067
|
+
this.remember(element, obs);
|
|
9068
|
+
this.observeReaction(element, obs, event);
|
|
9069
|
+
this.observeErrors(element, now);
|
|
9070
|
+
this.trackRage(element, now);
|
|
9071
|
+
}
|
|
9072
|
+
// ---------------------------------------------------------------- dead ----
|
|
9073
|
+
observeReaction(element, obs, event) {
|
|
9074
|
+
const netAtClick = this.netCount;
|
|
9075
|
+
const navAtClick = this.navCount;
|
|
9076
|
+
const focusAtClick = this.focusCount;
|
|
9077
|
+
const hrefAtClick = window.location.href;
|
|
9078
|
+
const skipDead = this.isDeadExempt(element, event);
|
|
9079
|
+
let observer = null;
|
|
9080
|
+
if (typeof MutationObserver !== "undefined") {
|
|
9081
|
+
observer = new MutationObserver(() => {
|
|
9082
|
+
obs.reacted = true;
|
|
9083
|
+
observer?.disconnect();
|
|
9084
|
+
observer = null;
|
|
9085
|
+
});
|
|
9086
|
+
observer.observe(document.documentElement, {
|
|
9087
|
+
childList: true,
|
|
9088
|
+
attributes: true,
|
|
9089
|
+
characterData: true,
|
|
9090
|
+
subtree: true
|
|
9091
|
+
});
|
|
9092
|
+
}
|
|
9093
|
+
window.setTimeout(() => {
|
|
9094
|
+
observer?.disconnect();
|
|
9095
|
+
const reacted = obs.reacted || this.unloading || this.netCount !== netAtClick || this.navCount !== navAtClick || this.focusCount !== focusAtClick || window.location.href !== hrefAtClick;
|
|
9096
|
+
obs.reacted = reacted;
|
|
9097
|
+
if (!reacted && !skipDead) this.report("$dead_click", element, {});
|
|
9098
|
+
}, this.opts.reactionWindowMs);
|
|
9099
|
+
}
|
|
9100
|
+
isDeadExempt(element, event) {
|
|
9101
|
+
if (event && (event.ctrlKey || event.metaKey || event.shiftKey || event.altKey)) return true;
|
|
9102
|
+
if (event && typeof event.button === "number" && event.button !== 0) return true;
|
|
9103
|
+
const tag = element.tagName.toLowerCase();
|
|
9104
|
+
if (NO_DEAD_TAGS.has(tag)) return true;
|
|
9105
|
+
if (element.closest('[contenteditable=""],[contenteditable="true"]')) return true;
|
|
9106
|
+
const anchor = element.closest("a");
|
|
9107
|
+
if (anchor) {
|
|
9108
|
+
if (anchor.target === "_blank" || anchor.hasAttribute("download")) return true;
|
|
9109
|
+
const href = anchor.getAttribute("href") ?? "";
|
|
9110
|
+
if (/^(mailto|tel|sms):/i.test(href)) return true;
|
|
9111
|
+
}
|
|
9112
|
+
return false;
|
|
9113
|
+
}
|
|
9114
|
+
// --------------------------------------------------------------- error ----
|
|
9115
|
+
observeErrors(element, clickedAt) {
|
|
9116
|
+
const deadline = clickedAt + this.opts.errorWindowMs;
|
|
9117
|
+
const check = () => {
|
|
9118
|
+
const err = this.errors.find((e) => e.at >= clickedAt && e.at <= deadline);
|
|
9119
|
+
if (err) {
|
|
9120
|
+
this.report("$click_error", element, {
|
|
9121
|
+
$error_source: err.source,
|
|
9122
|
+
$error_message: err.message
|
|
9123
|
+
});
|
|
9124
|
+
return;
|
|
9125
|
+
}
|
|
9126
|
+
if (Date.now() < deadline) window.setTimeout(check, 250);
|
|
9127
|
+
};
|
|
9128
|
+
window.setTimeout(check, 250);
|
|
9129
|
+
}
|
|
9130
|
+
pushError(source, value) {
|
|
9131
|
+
const message = describeError(value);
|
|
9132
|
+
if (message.startsWith("[veo]")) return;
|
|
9133
|
+
this.errors.push({ at: Date.now(), source, message });
|
|
9134
|
+
const cutoff = Date.now() - this.opts.errorWindowMs - 1e3;
|
|
9135
|
+
while (this.errors.length > 0 && this.errors[0].at < cutoff) {
|
|
9136
|
+
this.errors.shift();
|
|
9137
|
+
}
|
|
9138
|
+
if (this.errors.length > 50) this.errors.splice(0, this.errors.length - 50);
|
|
9139
|
+
}
|
|
9140
|
+
// ---------------------------------------------------------------- rage ----
|
|
9141
|
+
remember(element, obs) {
|
|
9142
|
+
const list = this.history.get(element) ?? [];
|
|
9143
|
+
const cutoff = obs.at - HISTORY_MS;
|
|
9144
|
+
const kept = list.filter((o) => o.at >= cutoff);
|
|
9145
|
+
kept.push(obs);
|
|
9146
|
+
this.history.set(element, kept);
|
|
9147
|
+
if (this.history.size > 100) {
|
|
9148
|
+
for (const [el2, obsList] of this.history) {
|
|
9149
|
+
if (obsList.every((o) => o.at < cutoff)) this.history.delete(el2);
|
|
9150
|
+
}
|
|
9151
|
+
}
|
|
9152
|
+
}
|
|
9153
|
+
trackRage(element, now) {
|
|
9154
|
+
const list = this.history.get(element) ?? [];
|
|
9155
|
+
const fast = list.filter((o) => o.at >= now - this.opts.rageWindowMs).length;
|
|
9156
|
+
const slow = list.filter((o) => o.at >= now - this.opts.rageWindowSlowMs).length;
|
|
9157
|
+
const existing = this.bursts.get(element);
|
|
9158
|
+
const isRage = fast >= this.opts.rageClicks || slow >= this.opts.rageClicksSlow;
|
|
9159
|
+
if (!existing && !isRage) return;
|
|
9160
|
+
const burst = existing ?? {
|
|
9161
|
+
startedAt: (list.find((o) => o.at >= now - this.opts.rageWindowSlowMs) ?? { at: now }).at,
|
|
9162
|
+
count: 0,
|
|
9163
|
+
timer: 0
|
|
9164
|
+
};
|
|
9165
|
+
burst.count = list.filter((o) => o.at >= burst.startedAt).length;
|
|
9166
|
+
window.clearTimeout(burst.timer);
|
|
9167
|
+
burst.timer = window.setTimeout(() => {
|
|
9168
|
+
this.bursts.delete(element);
|
|
9169
|
+
const clicks = (this.history.get(element) ?? []).filter((o) => o.at >= burst.startedAt);
|
|
9170
|
+
this.report("$rage_click", element, {
|
|
9171
|
+
$click_count: Math.max(clicks.length, burst.count),
|
|
9172
|
+
$reacted_clicks: clicks.filter((o) => o.reacted).length
|
|
9173
|
+
});
|
|
9174
|
+
}, this.opts.reactionWindowMs + 50);
|
|
9175
|
+
this.bursts.set(element, burst);
|
|
9176
|
+
}
|
|
9177
|
+
// ---------------------------------------------------------------- emit ----
|
|
9178
|
+
report(kind, element, extra) {
|
|
9179
|
+
const key = buildSelectorPath(element, 5);
|
|
9180
|
+
const counts = this.emitted.get(key) ?? { $dead_click: 0, $rage_click: 0, $click_error: 0 };
|
|
9181
|
+
if (counts[kind] >= this.opts.maxPerElement) return;
|
|
9182
|
+
counts[kind]++;
|
|
9183
|
+
this.emitted.set(key, counts);
|
|
9184
|
+
try {
|
|
9185
|
+
this.emit(kind, element, extra);
|
|
9186
|
+
} catch {
|
|
9187
|
+
}
|
|
9188
|
+
}
|
|
9189
|
+
// ------------------------------------------------------------- patches ----
|
|
9190
|
+
patchHistory() {
|
|
9191
|
+
const h = window.history;
|
|
9192
|
+
if (!h) return;
|
|
9193
|
+
const origPush = h.pushState;
|
|
9194
|
+
const origReplace = h.replaceState;
|
|
9195
|
+
const self = this;
|
|
9196
|
+
h.pushState = function(...args) {
|
|
9197
|
+
self.navCount++;
|
|
9198
|
+
return origPush.apply(this, args);
|
|
9199
|
+
};
|
|
9200
|
+
h.replaceState = function(...args) {
|
|
9201
|
+
self.navCount++;
|
|
9202
|
+
return origReplace.apply(this, args);
|
|
9203
|
+
};
|
|
9204
|
+
this.cleanups.push(() => {
|
|
9205
|
+
h.pushState = origPush;
|
|
9206
|
+
h.replaceState = origReplace;
|
|
9207
|
+
});
|
|
9208
|
+
}
|
|
9209
|
+
patchConsole() {
|
|
9210
|
+
const orig = console.error;
|
|
9211
|
+
const self = this;
|
|
9212
|
+
console.error = function(...args) {
|
|
9213
|
+
try {
|
|
9214
|
+
self.pushError("console", args.map(describeError).join(" "));
|
|
9215
|
+
} catch {
|
|
9216
|
+
}
|
|
9217
|
+
return orig.apply(this, args);
|
|
9218
|
+
};
|
|
9219
|
+
this.cleanups.push(() => {
|
|
9220
|
+
console.error = orig;
|
|
9221
|
+
});
|
|
9222
|
+
}
|
|
9223
|
+
/**
|
|
9224
|
+
* `fetch`: cuenta la petición (reacción) y registra respuestas ≥500 o
|
|
9225
|
+
* rechazos como error. Se observa con `.then(ok, err)` sobre la promesa
|
|
9226
|
+
* original: eso marca el rechazo como manejado para el navegador, así que
|
|
9227
|
+
* un fetch fallido sin catch de la app no dispara `unhandledrejection` —
|
|
9228
|
+
* lo registramos nosotros como error de red.
|
|
9229
|
+
*/
|
|
9230
|
+
patchFetch() {
|
|
9231
|
+
if (typeof window.fetch !== "function") return;
|
|
9232
|
+
const orig = window.fetch;
|
|
9233
|
+
const self = this;
|
|
9234
|
+
window.fetch = function(...args) {
|
|
9235
|
+
self.netCount++;
|
|
9236
|
+
const p = orig.apply(this, args);
|
|
9237
|
+
if (p && typeof p.then === "function") {
|
|
9238
|
+
p.then(
|
|
9239
|
+
(res) => {
|
|
9240
|
+
if (res && res.status >= 500) self.pushError("fetch", `${res.status} ${res.url}`);
|
|
9241
|
+
},
|
|
9242
|
+
(err) => self.pushError("fetch", err)
|
|
9243
|
+
);
|
|
9244
|
+
}
|
|
9245
|
+
return p;
|
|
9246
|
+
};
|
|
9247
|
+
this.cleanups.push(() => {
|
|
9248
|
+
window.fetch = orig;
|
|
9249
|
+
});
|
|
9250
|
+
}
|
|
9251
|
+
patchXhr() {
|
|
9252
|
+
const proto = typeof XMLHttpRequest !== "undefined" ? XMLHttpRequest.prototype : null;
|
|
9253
|
+
if (!proto) return;
|
|
9254
|
+
const origSend = proto.send;
|
|
9255
|
+
const self = this;
|
|
9256
|
+
proto.send = function(...args) {
|
|
9257
|
+
self.netCount++;
|
|
9258
|
+
try {
|
|
9259
|
+
this.addEventListener("loadend", () => {
|
|
9260
|
+
if (this.status >= 500) self.pushError("xhr", `${this.status} ${this.responseURL}`);
|
|
9261
|
+
});
|
|
9262
|
+
} catch {
|
|
9263
|
+
}
|
|
9264
|
+
return origSend.apply(this, args);
|
|
9265
|
+
};
|
|
9266
|
+
this.cleanups.push(() => {
|
|
9267
|
+
proto.send = origSend;
|
|
9268
|
+
});
|
|
9269
|
+
}
|
|
9270
|
+
};
|
|
9271
|
+
|
|
9272
|
+
// src/plugins/autocapture/dom-listener.ts
|
|
7952
9273
|
init_constants();
|
|
7953
9274
|
|
|
7954
9275
|
// src/plugins/autocapture/element-serializer.ts
|
|
@@ -8059,6 +9380,7 @@ var DomListener = class {
|
|
|
8059
9380
|
constructor(client, config) {
|
|
8060
9381
|
this.client = client;
|
|
8061
9382
|
this.cleanups = [];
|
|
9383
|
+
this.quality = null;
|
|
8062
9384
|
const d = DEFAULT_AUTOCAPTURE_CONFIG;
|
|
8063
9385
|
this.config = {
|
|
8064
9386
|
enabled: config.enabled ?? d.enabled,
|
|
@@ -8071,12 +9393,20 @@ var DomListener = class {
|
|
|
8071
9393
|
throttleMs: config.throttleMs ?? d.throttleMs,
|
|
8072
9394
|
maxTextLength: config.maxTextLength ?? d.maxTextLength,
|
|
8073
9395
|
captureElementMetadata: config.captureElementMetadata ?? d.captureElementMetadata,
|
|
8074
|
-
captureAncestors: Math.min(config.captureAncestors ?? d.captureAncestors, MAX_ANCESTORS)
|
|
9396
|
+
captureAncestors: Math.min(config.captureAncestors ?? d.captureAncestors, MAX_ANCESTORS),
|
|
9397
|
+
captureClickQuality: config.captureClickQuality ?? d.captureClickQuality
|
|
8075
9398
|
};
|
|
8076
9399
|
this.throttle = new ElementThrottle(this.config.throttleMs);
|
|
8077
9400
|
}
|
|
8078
9401
|
install() {
|
|
8079
9402
|
if (!hasDocument() || !this.config.enabled) return;
|
|
9403
|
+
if (this.config.captureClicks && this.config.captureClickQuality) {
|
|
9404
|
+
this.quality = new ClickQualityTracker((kind, element, extra) => {
|
|
9405
|
+
const payload = buildAutocapturePayload("click", element, this.config);
|
|
9406
|
+
this.client.track(kind, { ...payload, ...extra });
|
|
9407
|
+
});
|
|
9408
|
+
this.quality.install();
|
|
9409
|
+
}
|
|
8080
9410
|
if (this.config.captureClicks) this.attach("click", (e) => this.handleClick(e));
|
|
8081
9411
|
if (this.config.captureSubmits) this.attach("submit", (e) => this.handleSubmit(e), true);
|
|
8082
9412
|
if (this.config.captureChanges) this.attach("change", (e) => this.handleChange(e), true);
|
|
@@ -8084,6 +9414,8 @@ var DomListener = class {
|
|
|
8084
9414
|
destroy() {
|
|
8085
9415
|
for (const cleanup of this.cleanups) cleanup();
|
|
8086
9416
|
this.cleanups = [];
|
|
9417
|
+
this.quality?.destroy();
|
|
9418
|
+
this.quality = null;
|
|
8087
9419
|
}
|
|
8088
9420
|
attach(eventName, handler, useCapture = false) {
|
|
8089
9421
|
const listener = (e) => {
|
|
@@ -8102,6 +9434,7 @@ var DomListener = class {
|
|
|
8102
9434
|
const meaningful = this.findMeaningfulAncestor(target);
|
|
8103
9435
|
if (!meaningful) return;
|
|
8104
9436
|
if (isElementBlocked(meaningful, this.config.blockSelectors)) return;
|
|
9437
|
+
this.quality?.onClick(meaningful, event);
|
|
8105
9438
|
if (!this.throttle.shouldFire(meaningful)) return;
|
|
8106
9439
|
this.emit("click", meaningful);
|
|
8107
9440
|
}
|
|
@@ -8186,6 +9519,119 @@ function spaRouterPlugin() {
|
|
|
8186
9519
|
});
|
|
8187
9520
|
}
|
|
8188
9521
|
|
|
9522
|
+
// src/plugins/theme-detect/index.ts
|
|
9523
|
+
init_safe_env();
|
|
9524
|
+
var STORAGE_PREFIX = "veo_theme_";
|
|
9525
|
+
var RESEND_AFTER_MS = 24 * 60 * 60 * 1e3;
|
|
9526
|
+
var SETTLE_MS = 4e3;
|
|
9527
|
+
var MAX_RUNS_PER_PAGE = 4;
|
|
9528
|
+
function readState(key) {
|
|
9529
|
+
try {
|
|
9530
|
+
const raw = localStorage.getItem(key);
|
|
9531
|
+
const parsed = raw ? JSON.parse(raw) : null;
|
|
9532
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
9533
|
+
} catch {
|
|
9534
|
+
return {};
|
|
9535
|
+
}
|
|
9536
|
+
}
|
|
9537
|
+
function writeState(key, state) {
|
|
9538
|
+
try {
|
|
9539
|
+
localStorage.setItem(key, JSON.stringify(state));
|
|
9540
|
+
} catch {
|
|
9541
|
+
}
|
|
9542
|
+
}
|
|
9543
|
+
function guessMode(doc, prefersDark) {
|
|
9544
|
+
const hints = [
|
|
9545
|
+
doc.documentElement.className,
|
|
9546
|
+
doc.documentElement.dataset.theme,
|
|
9547
|
+
doc.documentElement.dataset.mode,
|
|
9548
|
+
doc.body?.className
|
|
9549
|
+
].join(" ").toLowerCase();
|
|
9550
|
+
if (/\bdark\b/.test(hints)) return "dark";
|
|
9551
|
+
if (/\blight\b/.test(hints)) return "light";
|
|
9552
|
+
return prefersDark ? "dark" : "light";
|
|
9553
|
+
}
|
|
9554
|
+
function isFresh(state, mode, now) {
|
|
9555
|
+
const last = state[mode];
|
|
9556
|
+
return last !== void 0 && now - last.at < RESEND_AFTER_MS;
|
|
9557
|
+
}
|
|
9558
|
+
function shouldSendSnapshot(state, mode, hash, now) {
|
|
9559
|
+
const last = state[mode];
|
|
9560
|
+
return !last || last.hash !== hash || now - last.at >= RESEND_AFTER_MS;
|
|
9561
|
+
}
|
|
9562
|
+
function themeDetectPlugin(config) {
|
|
9563
|
+
const storageKey = `${STORAGE_PREFIX}${config.apiKey.slice(-16) || "default"}`;
|
|
9564
|
+
let observer = null;
|
|
9565
|
+
let timer = null;
|
|
9566
|
+
let destroyed = false;
|
|
9567
|
+
let runs = 0;
|
|
9568
|
+
const run = async () => {
|
|
9569
|
+
if (destroyed || runs >= MAX_RUNS_PER_PAGE) return;
|
|
9570
|
+
const prefersDark = typeof window.matchMedia === "function" && window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
9571
|
+
const guess = guessMode(document, prefersDark);
|
|
9572
|
+
if (isFresh(readState(storageKey), guess, Date.now())) return;
|
|
9573
|
+
runs++;
|
|
9574
|
+
try {
|
|
9575
|
+
const detector = await config.loadDetector();
|
|
9576
|
+
if (!detector || destroyed) return;
|
|
9577
|
+
const snapshot = detector.detectTheme();
|
|
9578
|
+
if (!snapshot) return;
|
|
9579
|
+
const hash = detector.snapshotHash(snapshot);
|
|
9580
|
+
const state = readState(storageKey);
|
|
9581
|
+
const now = Date.now();
|
|
9582
|
+
const mark = () => {
|
|
9583
|
+
const record = { hash, at: Date.now() };
|
|
9584
|
+
state[snapshot.mode] = record;
|
|
9585
|
+
state[guess] = state[guess] && guess !== snapshot.mode ? state[guess] : record;
|
|
9586
|
+
writeState(storageKey, state);
|
|
9587
|
+
};
|
|
9588
|
+
if (!shouldSendSnapshot(state, snapshot.mode, hash, now)) {
|
|
9589
|
+
mark();
|
|
9590
|
+
return;
|
|
9591
|
+
}
|
|
9592
|
+
if (config.debug) console.log("[veo] theme snapshot", snapshot);
|
|
9593
|
+
const res = await fetch(`${config.apiUrl}/v1/workspaces/theme-snapshot`, {
|
|
9594
|
+
method: "POST",
|
|
9595
|
+
headers: { "X-Api-Key": config.apiKey, "Content-Type": "application/json" },
|
|
9596
|
+
body: JSON.stringify(snapshot),
|
|
9597
|
+
credentials: "omit"
|
|
9598
|
+
});
|
|
9599
|
+
if (res.ok) mark();
|
|
9600
|
+
} catch (err) {
|
|
9601
|
+
if (config.debug) console.warn("[veo] theme detect failed", err);
|
|
9602
|
+
}
|
|
9603
|
+
};
|
|
9604
|
+
const schedule = (delayMs) => {
|
|
9605
|
+
if (timer) clearTimeout(timer);
|
|
9606
|
+
timer = setTimeout(() => {
|
|
9607
|
+
timer = null;
|
|
9608
|
+
const idle = window.requestIdleCallback;
|
|
9609
|
+
if (typeof idle === "function") idle(() => void run());
|
|
9610
|
+
else void run();
|
|
9611
|
+
}, delayMs);
|
|
9612
|
+
};
|
|
9613
|
+
return {
|
|
9614
|
+
name: "theme-detect",
|
|
9615
|
+
install() {
|
|
9616
|
+
if (!hasWindow() || !hasDocument()) return;
|
|
9617
|
+
if (document.readyState === "complete") schedule(SETTLE_MS);
|
|
9618
|
+
else window.addEventListener("load", () => schedule(SETTLE_MS), { once: true });
|
|
9619
|
+
if (typeof MutationObserver === "function") {
|
|
9620
|
+
observer = new MutationObserver(() => schedule(1500));
|
|
9621
|
+
observer.observe(document.documentElement, {
|
|
9622
|
+
attributes: true,
|
|
9623
|
+
attributeFilter: ["class", "data-theme", "data-mode"]
|
|
9624
|
+
});
|
|
9625
|
+
}
|
|
9626
|
+
},
|
|
9627
|
+
destroy() {
|
|
9628
|
+
destroyed = true;
|
|
9629
|
+
observer?.disconnect();
|
|
9630
|
+
if (timer) clearTimeout(timer);
|
|
9631
|
+
}
|
|
9632
|
+
};
|
|
9633
|
+
}
|
|
9634
|
+
|
|
8189
9635
|
// src/core/client.ts
|
|
8190
9636
|
init_safe_env();
|
|
8191
9637
|
init_uuid();
|
|
@@ -8338,26 +9784,26 @@ var Session = class {
|
|
|
8338
9784
|
|
|
8339
9785
|
// src/core/storage.ts
|
|
8340
9786
|
init_safe_env();
|
|
8341
|
-
var
|
|
9787
|
+
var STORAGE_PREFIX2 = "veo:";
|
|
8342
9788
|
var ONE_YEAR_MS = 365 * 24 * 60 * 60 * 1e3;
|
|
8343
9789
|
var LocalStorageAdapter = class {
|
|
8344
9790
|
get(key) {
|
|
8345
|
-
return localStorage.getItem(
|
|
9791
|
+
return localStorage.getItem(STORAGE_PREFIX2 + key);
|
|
8346
9792
|
}
|
|
8347
9793
|
set(key, value) {
|
|
8348
9794
|
try {
|
|
8349
|
-
localStorage.setItem(
|
|
9795
|
+
localStorage.setItem(STORAGE_PREFIX2 + key, value);
|
|
8350
9796
|
} catch {
|
|
8351
9797
|
}
|
|
8352
9798
|
}
|
|
8353
9799
|
remove(key) {
|
|
8354
|
-
localStorage.removeItem(
|
|
9800
|
+
localStorage.removeItem(STORAGE_PREFIX2 + key);
|
|
8355
9801
|
}
|
|
8356
9802
|
clear() {
|
|
8357
9803
|
const toRemove = [];
|
|
8358
9804
|
for (let i = 0; i < localStorage.length; i++) {
|
|
8359
9805
|
const key = localStorage.key(i);
|
|
8360
|
-
if (key?.startsWith(
|
|
9806
|
+
if (key?.startsWith(STORAGE_PREFIX2)) toRemove.push(key);
|
|
8361
9807
|
}
|
|
8362
9808
|
for (const key of toRemove) localStorage.removeItem(key);
|
|
8363
9809
|
}
|
|
@@ -8368,7 +9814,7 @@ function readCookies() {
|
|
|
8368
9814
|
}
|
|
8369
9815
|
var CookieAdapter = class {
|
|
8370
9816
|
get(key) {
|
|
8371
|
-
const name = `${
|
|
9817
|
+
const name = `${STORAGE_PREFIX2}${key}=`;
|
|
8372
9818
|
for (const cookie of readCookies()) {
|
|
8373
9819
|
if (cookie.startsWith(name)) {
|
|
8374
9820
|
return decodeURIComponent(cookie.slice(name.length));
|
|
@@ -8379,19 +9825,19 @@ var CookieAdapter = class {
|
|
|
8379
9825
|
set(key, value, options) {
|
|
8380
9826
|
const maxAgeSec = Math.floor((options?.ttlMs ?? ONE_YEAR_MS) / 1e3);
|
|
8381
9827
|
const secure = typeof location !== "undefined" && location.protocol === "https:" ? "; Secure" : "";
|
|
8382
|
-
document.cookie = `${
|
|
9828
|
+
document.cookie = `${STORAGE_PREFIX2}${key}=${encodeURIComponent(
|
|
8383
9829
|
value
|
|
8384
9830
|
)}; Path=/; Max-Age=${maxAgeSec}; SameSite=Lax${secure}`;
|
|
8385
9831
|
}
|
|
8386
9832
|
remove(key) {
|
|
8387
|
-
document.cookie = `${
|
|
9833
|
+
document.cookie = `${STORAGE_PREFIX2}${key}=; Path=/; Max-Age=0; SameSite=Lax`;
|
|
8388
9834
|
}
|
|
8389
9835
|
clear() {
|
|
8390
9836
|
for (const cookie of readCookies()) {
|
|
8391
9837
|
const eq = cookie.indexOf("=");
|
|
8392
9838
|
const name = eq === -1 ? cookie : cookie.slice(0, eq);
|
|
8393
|
-
if (name.startsWith(
|
|
8394
|
-
this.remove(name.slice(
|
|
9839
|
+
if (name.startsWith(STORAGE_PREFIX2)) {
|
|
9840
|
+
this.remove(name.slice(STORAGE_PREFIX2.length));
|
|
8395
9841
|
}
|
|
8396
9842
|
}
|
|
8397
9843
|
}
|
|
@@ -8681,13 +10127,23 @@ var Client = class {
|
|
|
8681
10127
|
};
|
|
8682
10128
|
|
|
8683
10129
|
// src/core/setup.ts
|
|
8684
|
-
function setupClient(config, onGuides, loadBuilder) {
|
|
10130
|
+
function setupClient(config, onGuides, loadBuilder, loadThemeDetector) {
|
|
8685
10131
|
const client = new Client(config);
|
|
8686
10132
|
client.use(spaRouterPlugin());
|
|
8687
10133
|
if (config.autocapture !== false) {
|
|
8688
10134
|
const autocaptureConfig = typeof config.autocapture === "object" ? config.autocapture : {};
|
|
8689
10135
|
client.use(autocapturePlugin(autocaptureConfig));
|
|
8690
10136
|
}
|
|
10137
|
+
if (config.detectTheme !== false && loadThemeDetector && !isBuilderMode()) {
|
|
10138
|
+
client.use(
|
|
10139
|
+
themeDetectPlugin({
|
|
10140
|
+
apiUrl: resolveApiUrl(config),
|
|
10141
|
+
apiKey: config.apiKey,
|
|
10142
|
+
loadDetector: loadThemeDetector,
|
|
10143
|
+
...config.debug !== void 0 ? { debug: config.debug } : {}
|
|
10144
|
+
})
|
|
10145
|
+
);
|
|
10146
|
+
}
|
|
8691
10147
|
onGuides(client);
|
|
8692
10148
|
if (typeof window !== "undefined") {
|
|
8693
10149
|
const stub = window.veo;
|
|
@@ -10391,7 +11847,10 @@ function init(config) {
|
|
|
10391
11847
|
// Fallback de carga del builder para bundlers: `import()` dinámico → el bundler
|
|
10392
11848
|
// lo code-splitea (solo se descarga en modo builder). Así el consumidor por
|
|
10393
11849
|
// bundler NO necesita copiar `veo-builder.js` ni setear `builderUrl`.
|
|
10394
|
-
() => Promise.resolve().then(() => (init_builder(), builder_exports)).then((m) => m.initBuilderMode())
|
|
11850
|
+
() => Promise.resolve().then(() => (init_builder(), builder_exports)).then((m) => m.initBuilderMode()),
|
|
11851
|
+
// Detector de tema: también por `import()` dinámico → chunk aparte que el
|
|
11852
|
+
// bundler solo descarga cuando toca detectar (como mucho una vez al día).
|
|
11853
|
+
() => Promise.resolve().then(() => (init_detect_theme(), detect_theme_exports))
|
|
10395
11854
|
);
|
|
10396
11855
|
}
|
|
10397
11856
|
|