@tryvitris/website-harness 1.0.1 → 1.1.0
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/README.md +76 -74
- package/agent/graph.yaml +769 -756
- package/agent/refs/admin.md +11 -11
- package/agent/refs/browser.md +6 -2
- package/agent/refs/business.md +20 -9
- package/agent/refs/compose.md +2 -0
- package/agent/refs/design-quality.md +2 -0
- package/agent/refs/direction.md +2 -0
- package/agent/refs/implement.md +8 -4
- package/agent/refs/scheduling.md +13 -0
- package/agent/refs/template-adaptation.md +2 -0
- package/agent/refs/template-library.md +2 -0
- package/agent/refs/typography.md +15 -0
- package/bin/api/agent/evaluate.js +7 -6
- package/bin/api/agent/grade.js +7 -6
- package/bin/api/cli/api.js +15 -14
- package/bin/api/cli.js +17 -16
- package/bin/api/engine.js +8 -7
- package/bin/api/hosting.js +9 -8
- package/bin/api/index.js +4 -3
- package/bin/api/render.js +2 -1
- package/bin/api/theme.js +2 -1
- package/bin/chunks/{chunk-M72KKZIB.js → chunk-5KRJQKHB.js} +4 -3
- package/bin/chunks/{chunk-72Y4YMTV.js → chunk-AD7WAHW2.js} +2 -2
- package/bin/chunks/{chunk-RE4GOHSU.js → chunk-AS74YXGM.js} +3 -3
- package/bin/chunks/{chunk-AUO5I56E.js → chunk-C3F7XQD2.js} +194 -1
- package/bin/chunks/{chunk-445FZZEC.js → chunk-DDJ3QCRR.js} +9 -9
- package/bin/chunks/{chunk-LKN75JGA.js → chunk-GE6UUEG2.js} +13 -8
- package/bin/chunks/chunk-GPS3SAGQ.js +1020 -0
- package/bin/chunks/{chunk-37F7XIMH.js → chunk-JTXFTNEA.js} +6 -1
- package/bin/chunks/{chunk-JBSGJR7X.js → chunk-KR5ZUZV2.js} +2 -2
- package/bin/chunks/{chunk-65LRZIBP.js → chunk-MAPY3YVE.js} +2 -2
- package/bin/chunks/{chunk-VKU5TY5C.js → chunk-MYX4FET4.js} +1 -1
- package/bin/chunks/{chunk-54QPYUM2.js → chunk-OZD7ZWDX.js} +1 -1
- package/bin/chunks/chunk-S2XVNTQP.js +90 -0
- package/bin/chunks/{chunk-UR4NWYVW.js → chunk-SBOUSO3S.js} +3 -3
- package/bin/chunks/{chunk-KIWEFRNG.js → chunk-SXPLCCO7.js} +6 -6
- package/bin/chunks/{chunk-H3BS2EBR.js → chunk-WIT6364Y.js} +4 -4
- package/bin/chunks/{chunk-GQPUILFR.js → chunk-WK7HJI5H.js} +1301 -1252
- package/bin/chunks/{design-context-store-ETVS4N6W.js → design-context-store-PGXCHUOF.js} +7 -6
- package/bin/chunks/{packets-XFXKHSEY.js → packets-B7MCJ42T.js} +7 -6
- package/bin/chunks/{preparation-3OR7LVPQ.js → preparation-MVKICQPD.js} +7 -6
- package/bin/cloudflare/backend.js +90 -11
- package/bin/types/src/agent/browser-typography.d.ts +33 -0
- package/bin/types/src/agent/theme-suite-identity.d.ts +1 -0
- package/bin/types/src/agent/typography-policy.d.ts +6 -0
- package/bin/types/src/build/index.d.ts +2 -0
- package/bin/types/src/cli/admin.d.ts +1 -1
- package/bin/types/src/compile/site.d.ts +1 -1
- package/bin/types/src/scheduling/config.d.ts +11 -0
- package/bin/types/src/scheduling/management.d.ts +2 -0
- package/bin/types/src/scheduling/styles.d.ts +1 -0
- package/bin/types/src/scheduling/widget.d.ts +2 -0
- package/bin/types/src/theme.d.ts +3 -1
- package/bin/types/src/types.d.ts +6 -1
- package/bin/types/src/typography.d.ts +27 -0
- package/bin/vitris-art.js +7 -6
- package/bin/vitris-build.js +5 -4
- package/bin/vitris-create-site.js +9 -8
- package/bin/vitris-cursor-hook.js +7 -6
- package/bin/vitris-eval.js +9 -8
- package/bin/vitris-go.js +9 -8
- package/bin/vitris-harvest.js +8 -7
- package/bin/vitris-knowledge.js +8 -7
- package/bin/vitris-launch-preflight.js +7 -6
- package/bin/vitris-motion-view.js +7 -6
- package/bin/vitris-references.js +5 -4
- package/bin/vitris-refresh.js +8 -7
- package/bin/vitris-stock.js +1 -1
- package/bin/vitris-templates.js +91 -1
- package/bin/vitris-themes.js +7 -6
- package/bin/vitris-upgrade.js +8 -7
- package/bin/vitris-verify-browser.js +33 -8
- package/bin/vitris-verify-performance.js +5 -4
- package/bin/vitris.js +17 -16
- package/design-system/MASTER.md +4 -0
- package/docs/ADMIN.md +85 -85
- package/docs/CLI.md +314 -308
- package/docs/SCHEDULING.md +67 -0
- package/package.json +5 -4
- package/templates/site/site.json +1 -0
- package/templates/site/theme/defaults.json +78 -17
- package/bin/chunks/chunk-L44ZTNDM.js +0 -332
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
loadPersistedDesignContextPacket,
|
|
4
4
|
persistDesignContextArtifacts,
|
|
5
5
|
resolveDesignContextExpansion
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-WK7HJI5H.js";
|
|
7
7
|
import "./chunk-KVMZSQ3D.js";
|
|
8
8
|
import "./chunk-WOORR67N.js";
|
|
9
9
|
import "./chunk-PRLL7B63.js";
|
|
@@ -13,15 +13,16 @@ import "./chunk-M4LNHYYR.js";
|
|
|
13
13
|
import "./chunk-TY33XMHW.js";
|
|
14
14
|
import "./chunk-HL6JVA4A.js";
|
|
15
15
|
import "./chunk-M2ETYSVJ.js";
|
|
16
|
-
import "./chunk-
|
|
16
|
+
import "./chunk-C3F7XQD2.js";
|
|
17
17
|
import "./chunk-BIEDSEL2.js";
|
|
18
18
|
import "./chunk-IKNMBGGD.js";
|
|
19
19
|
import "./chunk-WEFDVXFX.js";
|
|
20
|
-
import "./chunk-
|
|
21
|
-
import "./chunk-
|
|
22
|
-
import "./chunk-
|
|
23
|
-
import "./chunk-
|
|
20
|
+
import "./chunk-WIT6364Y.js";
|
|
21
|
+
import "./chunk-GPS3SAGQ.js";
|
|
22
|
+
import "./chunk-JTXFTNEA.js";
|
|
23
|
+
import "./chunk-GE6UUEG2.js";
|
|
24
24
|
import "./chunk-YYPYYOEV.js";
|
|
25
|
+
import "./chunk-S2XVNTQP.js";
|
|
25
26
|
import "./chunk-GZDLXYZV.js";
|
|
26
27
|
import "./chunk-G7MNMULR.js";
|
|
27
28
|
import "./chunk-T6PE4KN4.js";
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
partitionByObligation,
|
|
11
11
|
rankEvidenceRelevance,
|
|
12
12
|
selectDiverseEvidence
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-WK7HJI5H.js";
|
|
14
14
|
import "./chunk-KVMZSQ3D.js";
|
|
15
15
|
import "./chunk-WOORR67N.js";
|
|
16
16
|
import "./chunk-PRLL7B63.js";
|
|
@@ -20,15 +20,16 @@ import "./chunk-M4LNHYYR.js";
|
|
|
20
20
|
import "./chunk-TY33XMHW.js";
|
|
21
21
|
import "./chunk-HL6JVA4A.js";
|
|
22
22
|
import "./chunk-M2ETYSVJ.js";
|
|
23
|
-
import "./chunk-
|
|
23
|
+
import "./chunk-C3F7XQD2.js";
|
|
24
24
|
import "./chunk-BIEDSEL2.js";
|
|
25
25
|
import "./chunk-IKNMBGGD.js";
|
|
26
26
|
import "./chunk-WEFDVXFX.js";
|
|
27
|
-
import "./chunk-
|
|
28
|
-
import "./chunk-
|
|
29
|
-
import "./chunk-
|
|
30
|
-
import "./chunk-
|
|
27
|
+
import "./chunk-WIT6364Y.js";
|
|
28
|
+
import "./chunk-GPS3SAGQ.js";
|
|
29
|
+
import "./chunk-JTXFTNEA.js";
|
|
30
|
+
import "./chunk-GE6UUEG2.js";
|
|
31
31
|
import "./chunk-YYPYYOEV.js";
|
|
32
|
+
import "./chunk-S2XVNTQP.js";
|
|
32
33
|
import "./chunk-GZDLXYZV.js";
|
|
33
34
|
import "./chunk-G7MNMULR.js";
|
|
34
35
|
import "./chunk-T6PE4KN4.js";
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
withMutationLock,
|
|
31
31
|
withMutationLockAsync,
|
|
32
32
|
writeCommitJournal
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-WK7HJI5H.js";
|
|
34
34
|
import "./chunk-KVMZSQ3D.js";
|
|
35
35
|
import "./chunk-WOORR67N.js";
|
|
36
36
|
import "./chunk-PRLL7B63.js";
|
|
@@ -40,15 +40,16 @@ import "./chunk-M4LNHYYR.js";
|
|
|
40
40
|
import "./chunk-TY33XMHW.js";
|
|
41
41
|
import "./chunk-HL6JVA4A.js";
|
|
42
42
|
import "./chunk-M2ETYSVJ.js";
|
|
43
|
-
import "./chunk-
|
|
43
|
+
import "./chunk-C3F7XQD2.js";
|
|
44
44
|
import "./chunk-BIEDSEL2.js";
|
|
45
45
|
import "./chunk-IKNMBGGD.js";
|
|
46
46
|
import "./chunk-WEFDVXFX.js";
|
|
47
|
-
import "./chunk-
|
|
48
|
-
import "./chunk-
|
|
49
|
-
import "./chunk-
|
|
50
|
-
import "./chunk-
|
|
47
|
+
import "./chunk-WIT6364Y.js";
|
|
48
|
+
import "./chunk-GPS3SAGQ.js";
|
|
49
|
+
import "./chunk-JTXFTNEA.js";
|
|
50
|
+
import "./chunk-GE6UUEG2.js";
|
|
51
51
|
import "./chunk-YYPYYOEV.js";
|
|
52
|
+
import "./chunk-S2XVNTQP.js";
|
|
52
53
|
import "./chunk-GZDLXYZV.js";
|
|
53
54
|
import "./chunk-G7MNMULR.js";
|
|
54
55
|
import "./chunk-T6PE4KN4.js";
|
|
@@ -14997,6 +14997,83 @@ function compileIcons(html2, route2 = "/") {
|
|
|
14997
14997
|
return html2;
|
|
14998
14998
|
}
|
|
14999
14999
|
|
|
15000
|
+
// src/typography.ts
|
|
15001
|
+
var TYPE_ROLES = ["display", "section", "subheading", "card-title", "body", "ui", "caption", "label"];
|
|
15002
|
+
var DEFAULT_TYPE_ROLES = {
|
|
15003
|
+
display: { size: "clamp(2.75rem, 1.8rem + 4vw, 5rem)", weight: 600, lineHeight: "1.08", tracking: "-0.03em", font: "display" },
|
|
15004
|
+
section: { size: "clamp(2rem, 1.5rem + 2vw, 3.5rem)", weight: 600, lineHeight: "1.15", tracking: "-0.025em", font: "display" },
|
|
15005
|
+
subheading: { size: "clamp(1.5rem, 1.3rem + 0.8vw, 2rem)", weight: 600, lineHeight: "1.25", tracking: "-0.015em", font: "display" },
|
|
15006
|
+
"card-title": { size: "1.5rem", weight: 600, lineHeight: "1.3", tracking: "-0.01em", font: "display" },
|
|
15007
|
+
body: { size: "1.125rem", weight: 400, lineHeight: "1.6", tracking: "normal", font: "body" },
|
|
15008
|
+
ui: { size: "1rem", weight: 600, lineHeight: "1.5", tracking: "normal", font: "body" },
|
|
15009
|
+
caption: { size: "0.875rem", weight: 400, lineHeight: "1.5", tracking: "normal", font: "body" },
|
|
15010
|
+
label: { size: "0.875rem", weight: 600, lineHeight: "1.5", tracking: "0.02em", font: "body" }
|
|
15011
|
+
};
|
|
15012
|
+
var TYPE_SELECTORS = {
|
|
15013
|
+
display: ["h1"],
|
|
15014
|
+
section: ["h2", ".stat__value", ".stat--sm .stat__value", ".price__amount"],
|
|
15015
|
+
subheading: ["h3", ".card__body > h3"],
|
|
15016
|
+
"card-title": ["h4", ".card__body > h4", ".brand", ".quote", ".quote--lg", ".quote--card"],
|
|
15017
|
+
body: ["body", ".lede", ".section__head .lede", ".lede--lg", ".list-check li", ".rating"],
|
|
15018
|
+
ui: ["h5", "h6", "button", "input", "select", "textarea", "nav a", ".nav-links a", "summary", ".accordion summary", ".btn", ".btn-outline", ".btn-ghost", ".btn-arrow", ".skip-link", ".c-cookie-banner__title", ".c-cookie-banner__more", ".announcement a"],
|
|
15019
|
+
caption: ["small", "figcaption", "sub", "sup", ".stat__label", ".price__period", ".byline", ".quote cite", ".quote footer", ".announcement", ".c-cookie-banner__text"],
|
|
15020
|
+
label: ["label", ".eyebrow", ".badge", ".table th", ".chips > legend", ".chips__label", ".chips span"]
|
|
15021
|
+
};
|
|
15022
|
+
function typographyEnabled(site) {
|
|
15023
|
+
if (site?.typography && site.typography.version !== 1) throw Error("Unsupported typography policy version");
|
|
15024
|
+
return site?.typography?.version === 1;
|
|
15025
|
+
}
|
|
15026
|
+
function resolvedTypography(theme = {}) {
|
|
15027
|
+
return Object.fromEntries(TYPE_ROLES.map((role) => [role, { ...DEFAULT_TYPE_ROLES[role], ...theme.typography?.roles?.[role] }]));
|
|
15028
|
+
}
|
|
15029
|
+
function validateTypography(value, requireRoles = false) {
|
|
15030
|
+
const errors = [];
|
|
15031
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return ["Typography requires a shared roles object in theme/defaults.json"];
|
|
15032
|
+
const system = value;
|
|
15033
|
+
if (!system.roles || typeof system.roles !== "object" || Array.isArray(system.roles)) return ["Typography roles must be an object"];
|
|
15034
|
+
if (requireRoles) {
|
|
15035
|
+
for (const role of TYPE_ROLES) if (!system.roles[role] || typeof system.roles[role] !== "object" || Object.keys(DEFAULT_TYPE_ROLES[role]).some((key2) => !(key2 in system.roles[role]))) errors.push(`Typography role ${role} must declare size, weight, lineHeight, tracking and font`);
|
|
15036
|
+
}
|
|
15037
|
+
const check = (style, name) => {
|
|
15038
|
+
if (!style || typeof style !== "object" || Array.isArray(style)) {
|
|
15039
|
+
errors.push(`Invalid typography style: ${name}`);
|
|
15040
|
+
return;
|
|
15041
|
+
}
|
|
15042
|
+
for (const key2 of ["size", "lineHeight", "tracking"]) if (style[key2] !== void 0 && (typeof style[key2] !== "string" || !style[key2].trim() || /[;{}<>]/.test(style[key2]))) errors.push(`Invalid typography ${name}.${key2}`);
|
|
15043
|
+
if (style.weight !== void 0 && (!Number.isFinite(style.weight) || style.weight < 1 || style.weight > 1e3)) errors.push(`Invalid typography ${name}.weight`);
|
|
15044
|
+
if (style.font !== void 0 && !["body", "display"].includes(style.font)) errors.push(`Invalid typography ${name}.font`);
|
|
15045
|
+
for (const key2 of Object.keys(style)) if (!["size", "weight", "lineHeight", "tracking", "font", "role", "reason"].includes(key2)) errors.push(`Unknown typography property ${name}.${key2}`);
|
|
15046
|
+
};
|
|
15047
|
+
for (const [role, style] of Object.entries(system.roles)) {
|
|
15048
|
+
if (!TYPE_ROLES.includes(role)) errors.push(`Unknown typography role: ${role}`);
|
|
15049
|
+
check(style, role);
|
|
15050
|
+
}
|
|
15051
|
+
if (system.variants !== void 0 && (!system.variants || typeof system.variants !== "object" || Array.isArray(system.variants))) errors.push("Typography variants must be an object");
|
|
15052
|
+
else for (const [name, variant] of Object.entries(system.variants || {})) {
|
|
15053
|
+
if (!/^[a-z][a-z0-9-]*$/.test(name) || !variant || !TYPE_ROLES.includes(variant.role) || typeof variant.reason !== "string" || !variant.reason.trim()) errors.push(`Typography variant ${name} needs a known role and reason`);
|
|
15054
|
+
check(variant, name);
|
|
15055
|
+
}
|
|
15056
|
+
return errors;
|
|
15057
|
+
}
|
|
15058
|
+
function typographyVariables(theme) {
|
|
15059
|
+
const errors = theme.typography ? validateTypography(theme.typography) : [];
|
|
15060
|
+
if (errors.length) throw Error(errors.join("\n"));
|
|
15061
|
+
const roles = resolvedTypography(theme);
|
|
15062
|
+
const values = (prefix, style) => ` --type-${prefix}-size: ${style.size};
|
|
15063
|
+
--type-${prefix}-weight: ${style.weight};
|
|
15064
|
+
--type-${prefix}-leading: ${style.lineHeight};
|
|
15065
|
+
--type-${prefix}-tracking: ${style.tracking};
|
|
15066
|
+
--type-${prefix}-font: var(--font-${style.font});`;
|
|
15067
|
+
return [...TYPE_ROLES.map((role) => values(role, roles[role])), ...Object.entries(theme.typography?.variants || {}).map(([name, variant]) => values(`variant-${name}`, { ...roles[variant.role], ...variant }))].join("\n");
|
|
15068
|
+
}
|
|
15069
|
+
function typographyCss() {
|
|
15070
|
+
const declarations = (name) => `font-size:var(--type-${name}-size);font-weight:var(--type-${name}-weight);line-height:var(--type-${name}-leading);letter-spacing:var(--type-${name}-tracking);font-family:var(--type-${name}-font);font-synthesis:none`;
|
|
15071
|
+
return [...TYPE_ROLES.map((role) => `${TYPE_SELECTORS[role].join(",")}{${declarations(role)}}`), ...TYPE_ROLES.map((role) => `[data-type-role="${role}"][data-type-role]{${declarations(role)}}`)].join("\n");
|
|
15072
|
+
}
|
|
15073
|
+
function typographyVariantCss(theme) {
|
|
15074
|
+
return Object.keys(theme.typography?.variants || {}).map((name) => `[data-type-variant="${name}"][data-type-variant][data-type-variant]{font-size:var(--type-variant-${name}-size);font-weight:var(--type-variant-${name}-weight);line-height:var(--type-variant-${name}-leading);letter-spacing:var(--type-variant-${name}-tracking);font-family:var(--type-variant-${name}-font)}`).join("\n");
|
|
15075
|
+
}
|
|
15076
|
+
|
|
15000
15077
|
// src/compile/site.ts
|
|
15001
15078
|
var baseCss = `*, *::before, *::after { box-sizing: border-box; }
|
|
15002
15079
|
html {
|
|
@@ -15336,8 +15413,9 @@ function usedComponents(pages) {
|
|
|
15336
15413
|
}
|
|
15337
15414
|
return [...names];
|
|
15338
15415
|
}
|
|
15339
|
-
function siteCss(pages, catalog) {
|
|
15416
|
+
function siteCss(pages, catalog, site) {
|
|
15340
15417
|
const chunks = [baseCss, primitivesCss];
|
|
15418
|
+
if (typographyEnabled(site)) chunks.push(typographyCss());
|
|
15341
15419
|
if (pages.some((page2) => page2.bodyHtml.includes("data-vitris-icon="))) chunks.push(iconCss);
|
|
15342
15420
|
for (const name of usedComponents(pages)) {
|
|
15343
15421
|
const css = catalog[name]?.css;
|
|
@@ -15480,8 +15558,9 @@ function mergeTheme(partial) {
|
|
|
15480
15558
|
if (!partial?.radiusLg) merged.radiusLg = partial?.radiusLg || defaultTheme.radiusLg;
|
|
15481
15559
|
return merged;
|
|
15482
15560
|
}
|
|
15483
|
-
function themeCss(tokens) {
|
|
15561
|
+
function themeCss(tokens, typography) {
|
|
15484
15562
|
const t = mergeTheme(tokens);
|
|
15563
|
+
const sharedType = typography?.version === 1;
|
|
15485
15564
|
return `:root {
|
|
15486
15565
|
--color-bg: ${t.colorBg};
|
|
15487
15566
|
--color-text: ${t.colorText};
|
|
@@ -15507,12 +15586,12 @@ function themeCss(tokens) {
|
|
|
15507
15586
|
--space-7: 3rem;
|
|
15508
15587
|
--space-8: 4.5rem;
|
|
15509
15588
|
--space-9: 6rem;
|
|
15510
|
-
--step-0: ${t.typeStep0 || "clamp(0.9rem, 0.85rem + 0.2vw, 1rem)"};
|
|
15511
|
-
--step-1: ${t.typeStep1 || "clamp(1rem, 0.95rem + 0.3vw, 1.125rem)"};
|
|
15512
|
-
--step-2: ${t.typeStep2 || "clamp(1.2rem, 1.05rem + 0.6vw, 1.5rem)"};
|
|
15513
|
-
--step-3: ${t.typeStep3 || "clamp(1.6rem, 1.25rem + 1.2vw, 2.25rem)"};
|
|
15589
|
+
--step-0: ${sharedType ? "var(--type-caption-size)" : t.typeStep0 || "clamp(0.9rem, 0.85rem + 0.2vw, 1rem)"};
|
|
15590
|
+
--step-1: ${sharedType ? "var(--type-body-size)" : t.typeStep1 || "clamp(1rem, 0.95rem + 0.3vw, 1.125rem)"};
|
|
15591
|
+
--step-2: ${sharedType ? "var(--type-subheading-size)" : t.typeStep2 || "clamp(1.2rem, 1.05rem + 0.6vw, 1.5rem)"};
|
|
15592
|
+
--step-3: ${sharedType ? "var(--type-section-size)" : t.typeStep3 || "clamp(1.6rem, 1.25rem + 1.2vw, 2.25rem)"};
|
|
15514
15593
|
--step-4: ${t.typeStep4 || "clamp(2.1rem, 1.5rem + 2.2vw, 3.5rem)"};
|
|
15515
|
-
--step-5: ${t.typeStep5 || "clamp(2.6rem, 1.7rem + 3.4vw, 4.75rem)"};
|
|
15594
|
+
--step-5: ${sharedType ? "var(--type-display-size)" : t.typeStep5 || "clamp(2.6rem, 1.7rem + 3.4vw, 4.75rem)"};
|
|
15516
15595
|
--tap-min: 44px;
|
|
15517
15596
|
--header-h: 4.75rem;
|
|
15518
15597
|
--section-y: ${t.sectionY || "clamp(3.5rem, 3rem + 3vw, 6rem)"};
|
|
@@ -15521,8 +15600,8 @@ function themeCss(tokens) {
|
|
|
15521
15600
|
--shadow-2: 0 8px 24px color-mix(in srgb, var(--color-text) 8%, transparent);
|
|
15522
15601
|
--shadow-3: 0 20px 48px color-mix(in srgb, var(--color-text) 12%, transparent);
|
|
15523
15602
|
--ring: 0 0 0 3px color-mix(in srgb, var(--color-focus) 35%, transparent);
|
|
15524
|
-
}
|
|
15525
|
-
`;
|
|
15603
|
+
${sharedType ? typographyVariables(t) + "\n" : ""}}
|
|
15604
|
+
${sharedType ? typographyVariantCss(t) + "\n" : ""}`;
|
|
15526
15605
|
}
|
|
15527
15606
|
|
|
15528
15607
|
// src/worker/github.ts
|
|
@@ -23629,12 +23708,12 @@ async function renderPreview(env, request) {
|
|
|
23629
23708
|
const compiled = compileAllPages(bundle.site, bundle.pages, bundle.catalog, origin);
|
|
23630
23709
|
if (rest === motionAssetHref.slice(1)) return compiled.some((page3) => page3.usesMotion) ? new Response(motionJs, { headers: { "content-type": "text/javascript; charset=utf-8", "cache-control": "public, max-age=31536000, immutable" } }) : text("Not found", 404);
|
|
23631
23710
|
if (rest === "site.css") {
|
|
23632
|
-
return new Response(siteCss(compiled, bundle.catalog), {
|
|
23711
|
+
return new Response(siteCss(compiled, bundle.catalog, bundle.site), {
|
|
23633
23712
|
headers: { "content-type": "text/css; charset=utf-8" }
|
|
23634
23713
|
});
|
|
23635
23714
|
}
|
|
23636
23715
|
if (rest === "theme.css") {
|
|
23637
|
-
return new Response(themeCss(bundle.theme), {
|
|
23716
|
+
return new Response(themeCss(bundle.theme, bundle.site.typography), {
|
|
23638
23717
|
headers: { "content-type": "text/css; charset=utf-8" }
|
|
23639
23718
|
});
|
|
23640
23719
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Page } from 'playwright';
|
|
2
|
+
import type { ThemeTokens } from '../types.ts';
|
|
3
|
+
export declare const TYPOGRAPHY_SCENARIO = "typography-v1-2026-10-28";
|
|
4
|
+
export type TypeObservation = {
|
|
5
|
+
selector: string;
|
|
6
|
+
text: string;
|
|
7
|
+
role: string;
|
|
8
|
+
variant?: string;
|
|
9
|
+
size: number;
|
|
10
|
+
renderedSize: number;
|
|
11
|
+
weight: string;
|
|
12
|
+
lineHeight: string;
|
|
13
|
+
tracking: string;
|
|
14
|
+
family: string;
|
|
15
|
+
};
|
|
16
|
+
export type TypographyCapture = {
|
|
17
|
+
path: string;
|
|
18
|
+
width: number;
|
|
19
|
+
state: string;
|
|
20
|
+
scenario: string;
|
|
21
|
+
observations: TypeObservation[];
|
|
22
|
+
failures: string[];
|
|
23
|
+
};
|
|
24
|
+
/** A serialized callback keeps browser DOM types out of the Worker/compiler TS program. */
|
|
25
|
+
export declare function captureTypography(page: Page, theme: Partial<ThemeTokens>, path: string, width: number, state: string): Promise<TypographyCapture>;
|
|
26
|
+
export declare function typographyConsistencyFailures(captures: TypographyCapture[]): string[];
|
|
27
|
+
export declare function typographyEvidenceFailures(report: {
|
|
28
|
+
scenario: string;
|
|
29
|
+
captures: TypographyCapture[];
|
|
30
|
+
failures: string[];
|
|
31
|
+
} | undefined, routes: string[]): string[];
|
|
32
|
+
/** Exercise ordinary local disclosure states without submitting forms or following links. */
|
|
33
|
+
export declare function captureTypographyStates(page: Page, theme: Partial<ThemeTokens>, path: string, width: number): Promise<TypographyCapture[]>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SchedulingConnection } from '../scheduling/config.ts';
|
|
1
2
|
import type { Catalog, PageSource, SiteConfig, ThemeTokens } from "../types.ts";
|
|
2
3
|
import type { PublicForm } from '../forms/config.ts';
|
|
3
4
|
export type BuildSiteInput = {
|
|
@@ -12,6 +13,7 @@ export type BuildSiteInput = {
|
|
|
12
13
|
themeHref?: string;
|
|
13
14
|
publicOrigin?: string;
|
|
14
15
|
forms?: PublicForm[];
|
|
16
|
+
scheduling?: SchedulingConnection;
|
|
15
17
|
webAnalyticsToken?: string;
|
|
16
18
|
};
|
|
17
19
|
export declare function loadSiteCatalog(siteDir: string): Promise<Catalog>;
|
|
@@ -4,7 +4,7 @@ export { beaconJs };
|
|
|
4
4
|
export declare const baseCss = "*, *::before, *::after { box-sizing: border-box; }\nhtml {\n -webkit-text-size-adjust: 100%;\n scroll-padding-top: calc(var(--header-h) + var(--space-3));\n}\nbody {\n margin: 0;\n font-family: var(--font-body);\n font-size: var(--step-1);\n background: var(--color-bg);\n color: var(--color-text);\n line-height: 1.6;\n}\nh1, h2, h3, h4 {\n font-family: var(--font-display);\n font-weight: 600;\n margin: 0;\n text-wrap: balance;\n overflow-wrap: anywhere;\n}\nh1 { font-size: var(--step-5); line-height: 1.05; letter-spacing: -0.03em; }\nh2 { font-size: var(--step-3); line-height: 1.15; letter-spacing: -0.025em; }\nh3 { font-size: var(--step-2); line-height: 1.25; letter-spacing: -0.015em; }\nh4 { font-size: var(--step-1); line-height: 1.3; letter-spacing: -0.01em; }\np { margin: 0 0 var(--space-4); text-wrap: pretty; }\np:last-child { margin-bottom: 0; }\na { color: inherit; }\na:hover { text-decoration-color: var(--color-accent); }\nimg { max-width: 100%; height: auto; display: block; }\nsvg { display: inline-block; vertical-align: middle; }\nul, ol { margin: 0 0 var(--space-4); padding-inline-start: 1.25rem; }\nul:last-child, ol:last-child { margin-bottom: 0; }\nsmall { font-size: var(--step-0); }\n:focus-visible {\n outline: 2px solid var(--color-focus);\n outline-offset: 3px;\n}\n.skip-link {\n position: absolute;\n left: var(--space-4);\n top: var(--space-4);\n z-index: 100;\n padding: var(--space-3) var(--space-4);\n background: var(--color-surface);\n color: var(--color-text);\n border: 1px solid var(--color-border);\n border-radius: var(--radius);\n text-decoration: none;\n font-weight: 600;\n transform: translateY(-160%);\n transition: transform 160ms ease;\n}\n.skip-link:focus {\n transform: translateY(0);\n}\n.visually-hidden {\n position: absolute !important;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\n.wrap {\n width: min(var(--max-width), calc(100% - 2rem));\n margin-inline: auto;\n}\n@media (min-width: 720px) {\n .wrap { width: min(var(--max-width), calc(100% - 2.5rem)); }\n}\n.btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n min-height: var(--tap-min);\n padding: 0.75rem 1.5rem;\n line-height: 1.2;\n white-space: nowrap;\n background: var(--color-accent);\n color: var(--color-accent-text);\n text-decoration: none;\n border: 1px solid transparent;\n border-radius: var(--radius-pill);\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n transition: background-color 180ms ease, color 180ms ease, border-color 180ms ease;\n}\n.btn:hover {\n background: color-mix(in srgb, var(--color-accent) 88%, var(--color-text));\n}\n.btn-outline {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n min-height: var(--tap-min);\n padding: 0.75rem 1.5rem;\n line-height: 1.2;\n white-space: nowrap;\n background: transparent;\n color: var(--color-text);\n text-decoration: none;\n border: 1px solid var(--color-border);\n border-radius: var(--radius-pill);\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n transition: background-color 180ms ease, color 180ms ease, border-color 180ms ease;\n}\n.btn-outline:hover {\n border-color: var(--color-text);\n background: color-mix(in srgb, var(--color-text) 6%, transparent);\n}\n.btn-arrow {\n display: inline-flex;\n align-items: center;\n gap: 0.4rem;\n min-height: var(--tap-min);\n white-space: nowrap;\n font-weight: 600;\n text-decoration: none;\n color: var(--color-text);\n transition: color 180ms ease;\n}\n.btn-arrow::after {\n content: \"\u2192\";\n transition: transform 180ms ease;\n}\n.btn-arrow:hover {\n color: var(--color-accent);\n}\n.btn-arrow:hover::after { transform: translateX(3px); }\n.btn-ghost {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n min-height: var(--tap-min);\n padding: 0.75rem 1.5rem;\n line-height: 1.2;\n white-space: nowrap;\n background: transparent;\n color: var(--color-text);\n text-decoration: none;\n border: 1px solid transparent;\n border-radius: var(--radius-pill);\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n transition: border-color 180ms ease, color 180ms ease, background-color 180ms ease;\n}\n.btn-ghost:hover {\n background: color-mix(in srgb, var(--color-text) 6%, transparent);\n}\n.c-cookie-banner {\n position: fixed;\n inset-inline: 0;\n bottom: 0;\n z-index: 200;\n padding: var(--space-4) 0 calc(var(--space-4) + env(safe-area-inset-bottom, 0px));\n background: var(--color-surface);\n color: var(--color-text);\n border-top: 1px solid var(--color-border);\n box-shadow: 0 -8px 24px color-mix(in srgb, var(--color-text) 8%, transparent);\n}\n.c-cookie-banner__inner {\n display: flex;\n flex-direction: column;\n gap: var(--space-4);\n}\n.c-cookie-banner__title {\n margin: 0 0 var(--space-2);\n font-family: var(--font-display);\n font-size: var(--step-1);\n font-weight: 600;\n letter-spacing: -0.02em;\n line-height: 1.25;\n}\n.c-cookie-banner__text {\n margin: 0;\n font-size: var(--step-0);\n color: var(--color-muted);\n max-width: 42rem;\n}\n.c-cookie-banner__actions {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-3);\n}\n.c-cookie-banner__more {\n min-height: var(--tap-min);\n display: inline-flex;\n align-items: center;\n font-weight: 600;\n font-size: var(--step-0);\n color: var(--color-text);\n text-decoration: underline;\n text-underline-offset: 0.15em;\n cursor: pointer;\n}\n.c-cookie-banner__more:hover { color: var(--color-accent); }\n@media (max-width: 719px) {\n .c-cookie-banner {\n /* A delayed first-visit beacon must not insert a banner into document flow. */\n max-height: 50dvh;\n overflow-y: auto;\n overscroll-behavior: contain;\n }\n}\n@media (min-width: 720px) {\n .c-cookie-banner__inner {\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n gap: var(--space-5);\n }\n .c-cookie-banner__actions { flex-shrink: 0; }\n}\n.c-lead-ok {\n background: var(--color-surface);\n border: 1px solid var(--color-border);\n border-radius: var(--radius);\n padding: var(--space-3) var(--space-4);\n margin: 0 0 var(--space-4);\n color: var(--color-text);\n}\n.c-lead-hp {\n position: absolute;\n left: -9999px;\n width: 1px;\n height: 1px;\n overflow: hidden;\n}\n.c-cookie-page {\n padding: var(--space-7) 0 var(--space-8);\n}\n.c-cookie-page h1 {\n margin-bottom: var(--space-3);\n}\n.c-cookie-page h2 {\n margin: var(--space-6) 0 var(--space-3);\n}\n.c-cookie-page .wrap { max-width: 42rem; }\n.c-cookie-page p { color: var(--color-muted); }\n.c-cookie-page__actions {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-3);\n margin-top: var(--space-5);\n}\n\n/* Scroll reveal \u2014 armed only after islands mark above-fold .is-in */\n[data-reveal] {\n --reveal-x: 0;\n --reveal-y: var(--reveal-dist, 16px);\n}\n[data-reveal-strength=\"whisper\"] {\n --reveal-dist: 8px;\n --reveal-dur: 400ms;\n --reveal-ease: cubic-bezier(0.22, 1, 0.36, 1);\n}\n[data-reveal-strength=\"editorial\"] {\n --reveal-dist: 16px;\n --reveal-dur: 550ms;\n --reveal-ease: cubic-bezier(0.22, 1, 0.36, 1);\n}\n[data-reveal-strength=\"noticeable\"] {\n --reveal-dist: 24px;\n --reveal-dur: 650ms;\n --reveal-ease: cubic-bezier(0.22, 1, 0.36, 1);\n}\n[data-reveal-from=\"up\"] { --reveal-x: 0; --reveal-y: var(--reveal-dist, 16px); }\n[data-reveal-from=\"down\"] { --reveal-x: 0; --reveal-y: calc(var(--reveal-dist, 16px) * -1); }\n[data-reveal-from=\"left\"] { --reveal-x: calc(var(--reveal-dist, 16px) * -1); --reveal-y: 0; }\n[data-reveal-from=\"right\"] { --reveal-x: var(--reveal-dist, 16px); --reveal-y: 0; }\n[data-reveal-child][data-reveal-from=\"up\"] {\n --reveal-child-x: 0;\n --reveal-child-y: var(--reveal-dist, 16px);\n}\n[data-reveal-child][data-reveal-from=\"down\"] {\n --reveal-child-x: 0;\n --reveal-child-y: calc(var(--reveal-dist, 16px) * -1);\n}\n[data-reveal-child][data-reveal-from=\"left\"] {\n --reveal-child-x: calc(var(--reveal-dist, 16px) * -1);\n --reveal-child-y: 0;\n}\n[data-reveal-child][data-reveal-from=\"right\"] {\n --reveal-child-x: var(--reveal-dist, 16px);\n --reveal-child-y: 0;\n}\n@media (prefers-reduced-motion: reduce) {\n *, *::before, *::after { animation: none !important; transition: none !important; }\n}\n";
|
|
5
5
|
export declare function compilePage(page: PageSource, catalog: Catalog, site: SiteConfig, kernelOrigin: string): CompiledPage;
|
|
6
6
|
export declare function usedComponents(pages: CompiledPage[]): string[];
|
|
7
|
-
export declare function siteCss(pages: CompiledPage[], catalog: Catalog): string;
|
|
7
|
+
export declare function siteCss(pages: CompiledPage[], catalog: Catalog, site?: SiteConfig): string;
|
|
8
8
|
export declare function siteIslands(pages: CompiledPage[], catalog: Catalog): string;
|
|
9
9
|
export declare function renderDocument(opts: {
|
|
10
10
|
site: SiteConfig;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface SchedulingConnection {
|
|
2
|
+
version: 1;
|
|
3
|
+
enabled: boolean;
|
|
4
|
+
apiOrigin: string;
|
|
5
|
+
siteId: string;
|
|
6
|
+
installationId: string;
|
|
7
|
+
route: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function schedulingConnection(value: unknown): SchedulingConnection | undefined;
|
|
10
|
+
export declare function bookingOrigin(value: unknown): string;
|
|
11
|
+
export declare function bookingPage(name: string): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const bookingCss = "\n.vb-site-header{max-width:71rem;margin:0 auto;padding:1.4rem 1.5rem;border-bottom:1px solid var(--color-border,#c3cfd3)}.vb-site-header a{font-family:var(--font-body,system-ui);font-weight:650;text-decoration:none;color:var(--color-text,#1d3340)}.vb-page{max-width:71rem;margin:0 auto;padding:2.5rem 1.5rem 4rem}.vb-page>h1{font-size:clamp(2rem,4vw,3rem);line-height:1.1;margin:.5rem 0 1rem}.vb-page>.vb-eyebrow{margin:0}.vb .vb-summary summary{font-family:inherit;font-size:1.1rem;font-weight:650;cursor:pointer}.vb h2,.vb h3{font-family:inherit}\n\n.vb{--vb-accent:var(--color-accent,#234957);--vb-accent-text:var(--color-accent-text,#fff);color:var(--color-text,#172c36);font-family:var(--font-body,system-ui);font-size:1rem;line-height:1.5;max-width:68rem;margin:2rem auto;isolation:isolate}.vb *{box-sizing:border-box}.vb [hidden]{display:none!important}.vb h2{font-size:1.65rem;line-height:1.25;margin:0 0 .6rem}.vb h3{font-size:1.15rem;margin:.2rem 0}.vb p{margin:.5rem 0 1rem}.vb button,.vb input,.vb select{font:inherit}.vb button,.vb input,.vb select{min-height:48px}.vb button{border:1px solid var(--color-border,#bdc8cd);background:var(--color-surface,#fff);color:inherit;border-radius:var(--radius,8px);padding:.7rem 1rem;cursor:pointer;text-align:center}.vb button:disabled{opacity:.55;cursor:wait}.vb .vb-primary{background:var(--vb-accent);color:var(--vb-accent-text);border-color:var(--vb-accent)}.vb button:hover:not(:disabled){filter:brightness(.96)}.vb :focus-visible{outline:3px solid var(--color-focus,#246c9a);outline-offset:3px}.vb fieldset{min-width:0;border:0;padding:0;margin:0 0 1.5rem}.vb legend{font-size:1.1rem;font-weight:650;margin-bottom:.8rem}.vb input:not([type=checkbox]):not([type=radio]),.vb select{width:100%;border:1px solid var(--color-border,#a8bbc2);border-radius:var(--radius,8px);background:var(--color-bg,#fff);color:inherit;padding:.75rem}.vb label{display:grid;gap:.4rem;margin-bottom:1rem}.vb .vb-check{display:flex;align-items:flex-start;gap:.7rem}.vb .vb-check input{min-height:24px;width:24px;flex:0 0 auto;margin-top:.1rem;accent-color:var(--vb-accent)}.vb .vb-steps{display:flex;gap:1.2rem;list-style:none;padding:0 0 1.1rem;margin:0 0 1.5rem;border-bottom:1px solid var(--color-border,#c7d2d6);flex-wrap:wrap}.vb .vb-steps li{color:var(--color-muted,#50636c)}.vb .vb-steps [aria-current=step]{color:var(--color-text,#172c36);font-weight:700}.vb .vb-layout{display:grid;grid-template-columns:minmax(0,1fr) 19rem;gap:2rem}.vb .vb-panel{background:var(--color-surface,#fff);border:1px solid var(--color-border,#c7d2d6);border-radius:var(--radius-lg,12px);padding:1.6rem;min-width:0}.vb .vb-summary{height:max-content}.vb .vb-services{display:grid;gap:.8rem}.vb .vb-service{display:grid;grid-template-columns:24px minmax(0,1fr);align-items:start;border:1px solid var(--color-border,#c7d2d6);border-radius:var(--radius,8px);padding:1rem;gap:.7rem;margin:0}.vb .vb-service:has(input:checked){border-color:var(--vb-accent);box-shadow:inset 0 0 0 1px var(--vb-accent)}.vb .vb-service input{width:20px;min-height:24px;margin:.15rem 0;accent-color:var(--vb-accent)}.vb .vb-service strong,.vb .vb-service small{display:block;overflow-wrap:anywhere}.vb .vb-service small{font-size:.9rem;margin-top:.25rem}.vb .vb-grid{display:grid;grid-template-columns:1fr 1fr;gap:1rem}.vb .vb-times{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:.6rem;margin:1.3rem 0}.vb .vb-times button{padding:.8rem .4rem}.vb .vb-times button[aria-pressed=true]{background:var(--vb-accent);color:var(--vb-accent-text);border-color:var(--vb-accent)}.vb .vb-times small{display:block;font-size:.8rem}.vb .vb-actions{display:flex;justify-content:space-between;gap:.8rem;margin-top:1.4rem;flex-wrap:wrap}.vb .vb-notice{padding:.8rem 1rem;background:var(--color-surface-2,#eef3f5);border-left:3px solid var(--vb-accent);margin-bottom:1.5rem;font-size:.92rem}.vb .vb-error{border-left:3px solid #aa3c34;padding:.8rem 1rem;background:var(--color-surface,#fff);margin:.8rem 0;overflow-wrap:anywhere}.vb .vb-policy{white-space:pre-wrap;overflow-wrap:anywhere}.vb dl{margin:.8rem 0}.vb dl div{padding:.7rem 0;border-bottom:1px solid var(--color-border,#dce2e5)}.vb dt{font-size:.85rem;opacity:.8}.vb dd{margin:.2rem 0 0;overflow-wrap:anywhere}.vb .vb-status{font-size:.9rem;font-weight:600;letter-spacing:.04em}.vb .vb-reference{font: .85rem ui-monospace,monospace;overflow-wrap:anywhere}.vb .vb-honey{position:absolute;left:-10000px;width:1px;height:1px;overflow:hidden}.vb .vb-hint{font-size:.9rem}.vb-eyebrow{font-size:.8rem;letter-spacing:.13em;font-weight:700}@media(max-width:760px){.vb .vb-layout{grid-template-columns:1fr;gap:1rem}.vb .vb-summary{order:-1}.vb .vb-summary dl div{display:inline-block;margin-right:1.4rem;border:0;padding:.2rem 0}.vb .vb-panel{padding:1.2rem}.vb .vb-steps{gap:.8rem;font-size:.85rem}.vb .vb-times{grid-template-columns:repeat(2,minmax(0,1fr))}.vb .vb-grid{grid-template-columns:1fr}.vb .vb-actions button{flex:1}}@media(prefers-reduced-motion:reduce){.vb *{scroll-behavior:auto!important;transition:none!important;animation:none!important}}\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/** Standard booking browser program, scoped to one explicit mount. Browser acceptance exercises this emitted JavaScript. */
|
|
2
|
+
export declare const bookingJs = "(function initializeBooking() {\n const root = document.querySelector(\"[data-vitris-booking]\");\n if (!root) return;\n const metadata = document.querySelector(\"script[data-vitris-booking-config]\");\n if (!metadata) return;\n root.classList.add(\"vb\");\n const node = (tag, text, className) => {\n const el = document.createElement(tag);\n if (text !== void 0) el.textContent = text;\n if (className) el.className = className;\n return el;\n };\n const notice = (text) => {\n const el = node(\"p\", text, \"vb-notice\");\n el.setAttribute(\"role\", \"status\");\n return el;\n };\n let connection;\n try {\n connection = JSON.parse(metadata.textContent || \"\");\n if (connection.siteOrigin !== location.origin) throw Error();\n } catch {\n root.append(notice(\"Booking is available on the approved business website. This preview is not connected.\"));\n return;\n }\n let catalog, step = 0, serviceId = \"\", locationId = \"\", providerId = \"\", day = \"\", slot = null, slots = [], revision = 0, busy = false, requestSerial = 0, error = \"\", status = \"\", customer = { name: \"\", email: \"\", phone: \"\" }, accepted = false, challengeToken = \"\", challengeId, receipt, company = \"\", attemptKey = \"\", attemptPayload = \"\", uncertain = false;\n let objectUrl = \"\";\n const api = async (path, data) => {\n const response = await fetch(connection.apiOrigin + \"/api/booking/v1\" + path, { mode: \"cors\", credentials: \"omit\", cache: \"no-store\", referrerPolicy: \"no-referrer\", signal: AbortSignal.timeout(25e3), ...data === void 0 ? {} : { method: \"POST\", headers: { \"content-type\": \"application/json\" }, body: JSON.stringify(data) } });\n const text = await response.text();\n if (text.length > 1e6) throw Error(\"The booking response could not be verified.\");\n let result;\n try {\n result = JSON.parse(text);\n } catch {\n throw Error(\"Booking is temporarily unavailable. Please contact the business.\");\n }\n if (!response.ok) {\n const failure = Object.assign(new Error(typeof result.error === \"string\" ? result.error : \"Booking could not be completed.\"), { status: response.status, code: result.code });\n throw failure;\n }\n return result;\n };\n const service = () => catalog.services.find((s) => s.id === serviceId);\n const place = () => catalog.locations.find((l) => l.id === locationId);\n const dateOf = (at, zone) => {\n const parts = new Intl.DateTimeFormat(\"en-US\", { timeZone: zone, year: \"numeric\", month: \"2-digit\", day: \"2-digit\" }).formatToParts(new Date(at)), get = (key) => parts.find((p) => p.type === key).value;\n return get(\"year\") + \"-\" + get(\"month\") + \"-\" + get(\"day\");\n };\n const addDays = (value, count) => new Date(Date.parse(value + \"T12:00:00Z\") + count * 864e5).toISOString().slice(0, 10);\n const dayStart = (value, zone) => {\n const target = Date.parse(value + \"T00:00:00Z\");\n for (let at = target - 14 * 36e5; at <= target + 14 * 36e5; at += 6e4) if (dateOf(at, zone) === value) return new Date(at).toISOString();\n throw Error(\"This date is unavailable in the selected timezone.\");\n };\n const time = (at, zone) => new Intl.DateTimeFormat(\"en-US\", { timeZone: zone, hour: \"numeric\", minute: \"2-digit\", timeZoneName: \"shortOffset\" }).format(new Date(at));\n const fullDate = (at, zone) => new Intl.DateTimeFormat(\"en-US\", { timeZone: zone, weekday: \"short\", month: \"long\", day: \"numeric\", year: \"numeric\" }).format(new Date(at));\n const price = (value) => {\n if (value.amountMinor === null) return value.label || \"Price agreed with the business\";\n try {\n const options2 = new Intl.NumberFormat(\"en-US\", { style: \"currency\", currency: value.currency }), digits = options2.resolvedOptions().maximumFractionDigits ?? 2;\n return options2.format(value.amountMinor / 10 ** digits);\n } catch {\n return value.label;\n }\n };\n const button = (label, action, primary = false) => {\n const el = node(\"button\", label, primary ? \"vb-primary\" : void 0);\n el.type = \"button\";\n el.dataset.focusKey = label;\n el.disabled = busy;\n el.addEventListener(\"click\", action);\n return el;\n };\n const field = (label, input) => {\n const el = node(\"label\");\n input.dataset.focusKey = label;\n el.append(node(\"span\", label), input);\n return el;\n };\n const detail = (list, label, value) => {\n const row = node(\"div\");\n row.append(node(\"dt\", label), node(\"dd\", value));\n list.append(row);\n };\n const move = (next) => {\n if (busy || uncertain) return;\n step = next;\n error = \"\";\n status = \"\";\n render(true);\n };\n const clearTimes = () => {\n requestSerial++;\n slots = [];\n slot = null;\n revision = 0;\n status = \"\";\n error = \"\";\n attemptKey = \"\";\n accepted = false;\n };\n const options = (select, items, value, all) => {\n if (all) {\n const opt = node(\"option\", all);\n opt.value = \"\";\n select.append(opt);\n }\n for (const item of items) {\n const opt = node(\"option\", item.name);\n opt.value = item.id;\n select.append(opt);\n }\n select.value = value;\n };\n const cleanupChallenge = () => {\n if (challengeId) {\n window.turnstile?.remove(challengeId);\n challengeId = void 0;\n }\n challengeToken = \"\";\n };\n const errorBlock = () => {\n const el = node(\"p\", error, \"vb-error\");\n el.setAttribute(\"role\", \"alert\");\n return el;\n };\n const showSummary = (panel) => {\n const disclosure = node(\"details\");\n disclosure.open = window.matchMedia(\"(min-width:761px)\").matches;\n disclosure.append(node(\"summary\", \"Your appointment\"));\n const list = node(\"dl\"), s = service();\n if (s) {\n detail(list, \"Service\", s.name);\n detail(list, \"Duration\", s.durationMinutes + \" minutes\");\n detail(list, \"Price\", price(s.price));\n detail(list, \"Confirmation\", s.bookingMode === \"approval\" ? \"Business approval required\" : \"Confirmed when booked\");\n }\n if (place()) detail(list, \"Location\", place().name);\n if (slot) {\n detail(list, \"Time\", fullDate(slot.startsAt, place().timezone) + \" \\xB7 \" + time(slot.startsAt, place().timezone));\n detail(list, \"Staff\", catalog.providers.find((p) => p.id === slot.providerId)?.name || \"Available staff\");\n }\n disclosure.append(list);\n panel.append(disclosure);\n };\n const normalizeSelection = () => {\n const s = service();\n if (!s) return;\n const places = catalog.locations.filter((l) => s.locationIds.includes(l.id));\n if (!places.some((p) => p.id === locationId)) locationId = places[0]?.id || \"\";\n const staff = catalog.providers.filter((p) => s.providerIds.includes(p.id) && p.locationIds.includes(locationId));\n if (!catalog.allowProviderChoice || !staff.some((p) => p.id === providerId)) providerId = staff.length === 1 ? staff[0].id : \"\";\n if (!day && place()) day = dateOf(Date.now() + catalog.minNoticeMinutes * 6e4, place().timezone);\n };\n const loadTimes = async (lookAhead = false) => {\n if (busy || uncertain) return;\n busy = true;\n slot = null;\n error = \"\";\n status = \"Checking availability\\u2026\";\n const serial = ++requestSerial;\n render();\n try {\n const zone = place().timezone, params = new URLSearchParams({ serviceId, locationId, startsAt: dayStart(day, zone), endsAt: dayStart(addDays(day, lookAhead ? 6 : 1), zone), ...providerId ? { providerId } : {} });\n const data = await api(\"/availability?\" + params);\n if (serial !== requestSerial) return;\n if (data.schemaVersion !== 1 || data.siteId !== connection.siteId || !Array.isArray(data.slots) || data.slots.length > 500 || !Number.isSafeInteger(data.revision)) throw Error(\"The availability response could not be verified.\");\n if (!data.terms || typeof data.policyText !== \"string\" || !data.location) throw Error(\"This service changed. Reload services before booking.\");\n const old = JSON.stringify(service());\n Object.assign(service(), data.terms);\n Object.assign(place(), data.location);\n if (catalog.policyText !== data.policyText) accepted = false;\n catalog.policyText = data.policyText;\n slots = data.slots;\n revision = data.revision;\n status = slots.length ? `${slots.length} available ${slots.length === 1 ? \"time\" : \"times\"}. ${zone}.` : \"No available times for this date. Try another day.\";\n if (old !== JSON.stringify(service())) status += \" Service details have changed; review the current price and duration.\";\n if (data.truncated) status += \" Showing the first available times; narrow the date or staff selection.\";\n } catch (failure) {\n if (serial === requestSerial) {\n slots = [];\n error = failure instanceof Error ? failure.message : \"Availability could not be loaded.\";\n status = \"\";\n }\n } finally {\n if (serial === requestSerial) {\n busy = false;\n render();\n }\n }\n };\n const mountChallenge = async (container) => {\n if (catalog.challenge.kind === \"local-fixture\") {\n container.append(notice(\"Local simulation: the security challenge is bypassed only for this isolated loopback preview.\"));\n return;\n }\n try {\n const win = window;\n if (!win.turnstile) await new Promise((resolve, reject) => {\n let script = document.querySelector(\"script[data-booking-turnstile]\");\n if (!script) {\n script = node(\"script\");\n script.src = \"https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit\";\n script.async = true;\n script.dataset.bookingTurnstile = \"\";\n document.head.append(script);\n }\n script.addEventListener(\"load\", () => resolve(), { once: true });\n script.addEventListener(\"error\", () => reject(Error(\"The security check could not load. Please retry or contact the business.\")), { once: true });\n });\n if (!container.isConnected || step !== 3) return;\n challengeId = win.turnstile.render(container, { sitekey: catalog.challenge.siteKey, action: \"booking\", callback: (value) => {\n challengeToken = value;\n }, \"expired-callback\": () => {\n challengeToken = \"\";\n }, \"error-callback\": () => {\n challengeToken = \"\";\n } });\n } catch (failure) {\n if (container.isConnected) container.append(errorBlockMessage(failure instanceof Error ? failure.message : \"Security check unavailable.\"));\n }\n };\n const errorBlockMessage = (text) => {\n const el = node(\"p\", text, \"vb-error\");\n el.setAttribute(\"role\", \"alert\");\n return el;\n };\n const submit = async () => {\n if (busy) return;\n if (!accepted) {\n error = \"Accept the booking policy before booking.\";\n render();\n return;\n }\n if (catalog.challenge.kind === \"turnstile\" && !challengeToken) {\n error = \"Complete the security check before booking.\";\n const existing = root.querySelector(\"[role=alert]\");\n if (existing) existing.textContent = error;\n else root.querySelector(\".vb-panel\")?.append(errorBlock());\n return;\n }\n const content = { revision, serviceId, locationId, providerId: slot.providerId, startsAt: slot.startsAt, customer, acceptedPolicy: true };\n const payload = JSON.stringify(content);\n if (!attemptKey || attemptPayload !== payload) {\n attemptKey = crypto.randomUUID();\n attemptPayload = payload;\n }\n busy = true;\n error = \"\";\n status = \"Saving your appointment\\u2026\";\n const token = challengeToken;\n render();\n try {\n const result = await api(\"/appointments\", { ...content, idempotencyKey: attemptKey, challengeToken: token, company });\n if (result.schemaVersion !== 1 || result.siteId !== connection.siteId || ![\"confirmed\", \"pending_approval\"].includes(result.status) || typeof result.reference !== \"string\" || typeof result.calendar?.content !== \"string\") throw Error(\"The booking response could not be verified. Keep this page open and retry the same booking.\");\n receipt = result;\n uncertain = false;\n step = 4;\n status = \"\";\n } catch (failure) {\n const f = failure;\n error = f.message || \"The booking response was interrupted.\";\n status = \"\";\n if (!f.status || f.status >= 500) {\n uncertain = true;\n error += \" Your result may still be pending. Retry this same booking; do not create another appointment.\";\n } else {\n uncertain = false;\n if (f.status === 409) {\n step = 1;\n slot = null;\n slots = [];\n revision = 0;\n attemptKey = \"\";\n accepted = false;\n status = \"Your details are saved. Find available times again.\";\n } else if (f.status === 400 && String(f.code).startsWith(\"challenge\")) {\n challengeToken = \"\";\n }\n }\n } finally {\n busy = false;\n render(true);\n }\n };\n function render(focus = false) {\n const focusKey = document.activeElement?.dataset.focusKey;\n cleanupChallenge();\n root.replaceChildren();\n root.append(notice(catalog.emailMode === \"send\" ? \"TEST BOOKING - Emails use the configured sender and allowed test recipients.\" : catalog.emailMode === \"disabled\" ? \"TEST BOOKING - Customer emails are not enabled. Keep your booking reference.\" : \"TEST BOOKING - This preview stores appointments in the admin calendar. Customer emails are captured, not sent.\"));\n const progress = node(\"ol\", void 0, \"vb-steps\");\n progress.setAttribute(\"aria-label\", \"Booking progress\");\n [\"Service\", \"Time\", \"Your details\", \"Review\", \"Receipt\"].forEach((name, index) => {\n const item = node(\"li\", index + 1 + \". \" + name);\n if (index === step) item.setAttribute(\"aria-current\", \"step\");\n progress.append(item);\n });\n root.append(progress);\n const layout = node(\"div\", void 0, \"vb-layout\"), panel = node(\"section\", void 0, \"vb-panel\"), summary = node(\"aside\", void 0, \"vb-panel vb-summary\");\n summary.setAttribute(\"aria-label\", \"Appointment summary\");\n layout.append(panel, summary);\n root.append(layout);\n showSummary(summary);\n const heading = node(\"h2\", [\"Choose a service\", \"Choose a time\", \"Your details\", \"Review your appointment\", receipt?.status === \"pending_approval\" ? \"Request received\" : \"Appointment confirmed\"][step]);\n heading.tabIndex = -1;\n panel.append(heading);\n if (error) panel.append(errorBlock());\n if (status) {\n const live = node(\"p\", status);\n live.setAttribute(\"role\", \"status\");\n panel.append(live);\n }\n if (step === 0) {\n if (!catalog.services.length) {\n panel.append(node(\"p\", \"No services are available for online booking. Please contact the business.\"));\n return;\n }\n const group = node(\"fieldset\"), legend = node(\"legend\", \"Available services\"), list = node(\"div\", void 0, \"vb-services\");\n group.append(legend, list);\n for (const s of catalog.services) {\n const radio = node(\"input\");\n radio.type = \"radio\";\n radio.name = \"booking-service\";\n radio.dataset.focusKey = \"service-\" + s.id;\n radio.value = s.id;\n radio.checked = s.id === serviceId;\n radio.addEventListener(\"change\", () => {\n serviceId = s.id;\n clearTimes();\n normalizeSelection();\n render();\n });\n const label = node(\"label\", void 0, \"vb-service\"), copy = node(\"span\");\n copy.append(node(\"strong\", s.name), node(\"small\", s.durationMinutes + \" minutes \\xB7 \" + price(s.price)), node(\"small\", s.bookingMode === \"approval\" ? \"Requires business approval\" : \"Immediate confirmation\"));\n label.append(radio, copy);\n list.append(label);\n }\n panel.append(group, button(\"Choose a time\", () => {\n normalizeSelection();\n move(1);\n }, true));\n } else if (step === 1) {\n const s = service(), grid = node(\"div\", void 0, \"vb-grid\"), places = catalog.locations.filter((l) => s.locationIds.includes(l.id));\n if (places.length > 1) {\n const select = node(\"select\");\n options(select, places, locationId);\n select.disabled = busy || uncertain;\n select.addEventListener(\"change\", () => {\n locationId = select.value;\n day = \"\";\n clearTimes();\n normalizeSelection();\n render();\n });\n grid.append(field(\"Location\", select));\n }\n const staff = catalog.providers.filter((p) => s.providerIds.includes(p.id) && p.locationIds.includes(locationId));\n if (catalog.allowProviderChoice && staff.length > 1) {\n const select = node(\"select\");\n options(select, staff, providerId, \"Any available staff\");\n select.disabled = busy || uncertain;\n select.addEventListener(\"change\", () => {\n providerId = select.value;\n clearTimes();\n render();\n });\n grid.append(field(\"Staff preference\", select));\n }\n const date = node(\"input\");\n date.type = \"date\";\n date.required = true;\n date.value = day;\n date.min = dateOf(Date.now(), place().timezone);\n date.max = dateOf(Date.now() + catalog.horizonDays * 864e5, place().timezone);\n date.disabled = busy || uncertain;\n date.addEventListener(\"change\", () => {\n if (date.value) {\n day = date.value;\n clearTimes();\n render();\n }\n });\n grid.append(field(\"Appointment date\", date));\n panel.append(grid, node(\"p\", \"Times are shown in \" + place().timezone + \". A time is reserved only after booking.\", \"vb-hint\"), button(busy ? \"Checking times\\u2026\" : \"Find available times\", () => {\n void loadTimes();\n }));\n const times = node(\"div\", void 0, \"vb-times\");\n times.setAttribute(\"aria-label\", \"Available times\");\n const seen = /* @__PURE__ */ new Set();\n for (const value of slots) {\n if (!providerId && seen.has(value.startsAt)) continue;\n seen.add(value.startsAt);\n const label = time(value.startsAt, place().timezone), b = button(label, () => {\n slot = value;\n render();\n });\n b.dataset.focusKey = value.startsAt + \"-\" + value.providerId;\n b.setAttribute(\"aria-pressed\", String(slot?.startsAt === value.startsAt && slot.providerId === value.providerId));\n b.setAttribute(\"aria-label\", fullDate(value.startsAt, place().timezone) + \" at \" + label);\n b.append(node(\"small\", fullDate(value.startsAt, place().timezone)));\n times.append(b);\n }\n panel.append(times);\n if (!slots.length && !busy) panel.append(button(\"Look ahead six days\", () => {\n void loadTimes(true);\n }));\n const actions = node(\"div\", void 0, \"vb-actions\"), next = button(\"Continue to your details\", () => move(2), true);\n next.disabled = !slot || busy;\n actions.append(button(\"Back to services\", () => move(0)), next);\n panel.append(actions);\n } else if (step === 2) {\n const form = node(\"form\");\n for (const [key, label, type, autocomplete] of [[\"name\", \"Full name\", \"text\", \"name\"], [\"email\", \"Email address\", \"email\", \"email\"], [\"phone\", \"Phone number (optional)\", \"tel\", \"tel\"]]) {\n const input = node(\"input\");\n input.name = key;\n input.type = type;\n input.autocomplete = autocomplete;\n input.required = key !== \"phone\";\n input.maxLength = key === \"email\" ? 254 : key === \"name\" ? 160 : 40;\n input.value = customer[key];\n input.addEventListener(\"input\", () => {\n customer[key] = input.value;\n });\n form.append(field(label, input));\n }\n const hidden = node(\"div\", void 0, \"vb-honey\");\n hidden.setAttribute(\"aria-hidden\", \"true\");\n const honey = node(\"input\");\n honey.name = \"company\";\n honey.tabIndex = -1;\n honey.autocomplete = \"off\";\n honey.addEventListener(\"input\", () => {\n company = honey.value;\n });\n hidden.append(field(\"Leave this field empty\", honey));\n form.append(hidden, node(\"p\", \"Your details are used to arrange this appointment. See the test email mode above before submitting.\", \"vb-hint\"));\n const actions = node(\"div\", void 0, \"vb-actions\"), next = button(\"Review appointment\", () => {\n }, true);\n next.type = \"submit\";\n actions.append(button(\"Back to times\", () => move(1)), next);\n form.append(actions);\n form.addEventListener(\"submit\", (event) => {\n event.preventDefault();\n if (form.reportValidity()) {\n customer = { name: customer.name.trim(), email: customer.email.trim(), phone: customer.phone.trim() };\n move(3);\n }\n });\n panel.append(form);\n } else if (step === 3) {\n const list = node(\"dl\");\n detail(list, \"Customer\", customer.name);\n detail(list, \"Email\", customer.email);\n if (customer.phone) detail(list, \"Phone\", customer.phone);\n panel.append(list, node(\"h3\", \"Booking policy\"), node(\"p\", catalog.policyText, \"vb-policy\"));\n if(catalog.customerManagement){const m=catalog.customerManagement;panel.append(node(\"p\",\"Online cancellation notice: \"+m.cancellationCutoffMinutes+\" minutes. Online rescheduling notice: \"+m.rescheduleCutoffMinutes+\" minutes. Zero permits changes until the appointment starts.\",\"vb-hint\"));}\n const check = node(\"input\");\n check.type = \"checkbox\";\n check.checked = accepted;\n check.disabled = busy || uncertain;\n check.addEventListener(\"change\", () => {\n accepted = check.checked;\n });\n const label = node(\"label\", void 0, \"vb-check\");\n label.append(check, node(\"span\", \"I have read and accept the booking policy.\"));\n panel.append(label);\n if (!busy) {\n const challenge = node(\"div\");\n panel.append(challenge);\n void mountChallenge(challenge);\n }\n const actions = node(\"div\", void 0, \"vb-actions\");\n if (!uncertain) actions.append(button(\"Back to your details\", () => move(2)));\n actions.append(button(busy ? \"Booking\\u2026\" : uncertain ? \"Retry same booking\" : service().bookingMode === \"approval\" ? \"Request appointment\" : \"Confirm appointment\", () => {\n void submit();\n }, true));\n panel.append(actions);\n } else if (receipt) {\n const pending = receipt.status === \"pending_approval\";\n panel.append(node(\"p\", pending ? \"The business needs to approve your request. Your time is held until the approval deadline. This request is not yet confirmed.\" : \"Your appointment is saved in the business calendar.\", \"vb-status\"));\n const list = node(\"dl\");\n detail(list, \"Service\", receipt.serviceName);\n detail(list, \"When\", fullDate(receipt.startsAt, receipt.timezone) + \" \\xB7 \" + time(receipt.startsAt, receipt.timezone));\n detail(list, \"Location\", receipt.locationName);\n detail(list, \"Staff\", receipt.providerName);\n detail(list, \"Price\", price(receipt.price));\n if (receipt.expiresAt) detail(list, \"Approval deadline\", fullDate(receipt.expiresAt, receipt.timezone) + \" \\xB7 \" + time(receipt.expiresAt, receipt.timezone));\n panel.append(list, node(\"p\", \"Booking reference\", \"vb-hint\"), node(\"p\", receipt.reference, \"vb-reference\"), node(\"p\", \"Keep this reference. \" + (receipt.emailStatus === \"captured\" ? \"Your test email is captured in the admin Customer emails inbox. Its private links can cancel or reschedule this appointment.\" : receipt.emailStatus === \"accepted\" ? \"Your email was accepted by the email provider. Check your inbox for private cancellation and rescheduling links.\" : receipt.emailStatus === \"blocked\" || receipt.emailStatus === \"unknown\" ? \"Your appointment is saved, but the email needs attention. Contact the business if you do not receive it.\" : (catalog.emailMode === \"disabled\" ? \"Customer emails are not enabled. Contact the business for appointment changes.\" : \"Open your appointment email for cancellation and rescheduling links. Email delivery may still be pending.\")) + \"\", \"vb-hint\"));\n if (objectUrl) URL.revokeObjectURL(objectUrl);\n objectUrl = URL.createObjectURL(new Blob([receipt.calendar.content], { type: \"text/calendar;charset=utf-8\" }));\n const link = node(\"a\", \"Download calendar file (.ics)\");\n link.href = objectUrl;\n link.download = \"appointment.ics\";\n panel.append(link, node(\"p\", \"A calendar file adds a copy to your calendar. It does not keep your calendar synchronized.\", \"vb-hint\"));\n }\n if (focus) heading.focus({ preventScroll: false });\n else if (focusKey) root.querySelector('[data-focus-key=\"' + CSS.escape(focusKey) + '\"]')?.focus({ preventScroll: true });\n }\n root.append(notice(\"Loading available services\\u2026\"));\n void api(\"/catalog\").then((data) => {\n if (data.schemaVersion !== 1 || data.siteId !== connection.siteId || data.installationId !== connection.installationId || data.siteOrigin !== connection.siteOrigin || data.mode !== \"test\" || !Array.isArray(data.services) || data.services.length > 128 || !Array.isArray(data.providers) || !Array.isArray(data.locations) || ![\"local-fixture\", \"turnstile\"].includes(data.challenge?.kind)) throw Error(\"This website booking connection could not be verified. Please contact the business.\");\n catalog = data;\n serviceId = new URL(location.href).searchParams.get(\"service\") || data.services[0]?.id || \"\";\n if (!data.services.some((s) => s.id === serviceId)) serviceId = data.services[0]?.id || \"\";\n normalizeSelection();\n render();\n }).catch((failure) => {\n root.replaceChildren(errorBlockMessage(failure instanceof Error ? failure.message : \"Booking is unavailable.\"));\n root.append(button(\"Try loading services again\", () => location.reload()));\n });\n window.addEventListener(\"beforeunload\", (event) => {\n if (uncertain || busy && step === 3) {\n event.preventDefault();\n event.returnValue = \"\";\n }\n });\n})();";
|
package/bin/types/src/theme.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import type { ThemeTokens } from "./types.ts";
|
|
2
2
|
export declare function mergeTheme(partial?: Partial<ThemeTokens> | null): ThemeTokens;
|
|
3
|
-
export declare function themeCss(tokens: ThemeTokens
|
|
3
|
+
export declare function themeCss(tokens: ThemeTokens, typography?: {
|
|
4
|
+
version: 1;
|
|
5
|
+
}): string;
|
package/bin/types/src/types.d.ts
CHANGED
|
@@ -26,6 +26,10 @@ export type SiteConfig = {
|
|
|
26
26
|
version: 2;
|
|
27
27
|
routes: "all-authored" | string[];
|
|
28
28
|
};
|
|
29
|
+
/** Shared type roles and rendered acceptance; omitted on historical sites. */
|
|
30
|
+
typography?: {
|
|
31
|
+
version: 1;
|
|
32
|
+
};
|
|
29
33
|
/** Design-run instrumentation; omitted from ordinary single-site authoring. */
|
|
30
34
|
designRun?: {
|
|
31
35
|
runId?: string;
|
|
@@ -61,13 +65,14 @@ export type ThemeTokens = {
|
|
|
61
65
|
/** Pill radius for `.btn` / `.btn-outline`. Cards keep `radius`. */
|
|
62
66
|
radiusPill: string;
|
|
63
67
|
maxWidth: string;
|
|
64
|
-
/**
|
|
68
|
+
/** Legacy scale and rhythm. Typography v1 resolves steps 0–3/5 from shared roles instead. */
|
|
65
69
|
typeStep0?: string;
|
|
66
70
|
typeStep1?: string;
|
|
67
71
|
typeStep2?: string;
|
|
68
72
|
typeStep3?: string;
|
|
69
73
|
typeStep4?: string;
|
|
70
74
|
typeStep5?: string;
|
|
75
|
+
typography?: import('./typography.ts').TypographySystem;
|
|
71
76
|
sectionY?: string;
|
|
72
77
|
};
|
|
73
78
|
/** @deprecated Editor-era content tree — dormant patch/workflow only. */
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { SiteConfig, ThemeTokens } from './types.ts';
|
|
2
|
+
export declare const TYPE_ROLES: readonly ["display", "section", "subheading", "card-title", "body", "ui", "caption", "label"];
|
|
3
|
+
export type TypeRole = typeof TYPE_ROLES[number];
|
|
4
|
+
export type TypeStyle = {
|
|
5
|
+
size: string;
|
|
6
|
+
weight: number;
|
|
7
|
+
lineHeight: string;
|
|
8
|
+
tracking: string;
|
|
9
|
+
font: 'body' | 'display';
|
|
10
|
+
};
|
|
11
|
+
export type TypographySystem = {
|
|
12
|
+
roles: Partial<Record<TypeRole, Partial<TypeStyle>>>;
|
|
13
|
+
variants?: Record<string, Partial<TypeStyle> & {
|
|
14
|
+
role: TypeRole;
|
|
15
|
+
reason: string;
|
|
16
|
+
}>;
|
|
17
|
+
};
|
|
18
|
+
export declare const MINIMUM_TYPE_PX = 12;
|
|
19
|
+
export declare const DEFAULT_TYPE_ROLES: Record<TypeRole, TypeStyle>;
|
|
20
|
+
export declare const TYPE_SELECTORS: Record<TypeRole, string[]>;
|
|
21
|
+
export declare function typographyEnabled(site?: Pick<SiteConfig, 'typography'>): boolean;
|
|
22
|
+
export declare function resolvedTypography(theme?: Pick<ThemeTokens, 'typography'>): Record<TypeRole, TypeStyle>;
|
|
23
|
+
/** Reject malformed values early; actual CSS lengths are resolved and checked in Chromium. */
|
|
24
|
+
export declare function validateTypography(value: unknown, requireRoles?: boolean): string[];
|
|
25
|
+
export declare function typographyVariables(theme: ThemeTokens): string;
|
|
26
|
+
export declare function typographyCss(): string;
|
|
27
|
+
export declare function typographyVariantCss(theme: ThemeTokens): string;
|
package/bin/vitris-art.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
retireProducedAsset,
|
|
13
13
|
validateAssetProduction,
|
|
14
14
|
withMutationLock
|
|
15
|
-
} from "./chunks/chunk-
|
|
15
|
+
} from "./chunks/chunk-WK7HJI5H.js";
|
|
16
16
|
import "./chunks/chunk-KVMZSQ3D.js";
|
|
17
17
|
import "./chunks/chunk-WOORR67N.js";
|
|
18
18
|
import "./chunks/chunk-PRLL7B63.js";
|
|
@@ -27,17 +27,18 @@ import {
|
|
|
27
27
|
} from "./chunks/chunk-TY33XMHW.js";
|
|
28
28
|
import "./chunks/chunk-HL6JVA4A.js";
|
|
29
29
|
import "./chunks/chunk-M2ETYSVJ.js";
|
|
30
|
-
import "./chunks/chunk-
|
|
30
|
+
import "./chunks/chunk-C3F7XQD2.js";
|
|
31
31
|
import "./chunks/chunk-BIEDSEL2.js";
|
|
32
32
|
import "./chunks/chunk-IKNMBGGD.js";
|
|
33
33
|
import "./chunks/chunk-WEFDVXFX.js";
|
|
34
34
|
import {
|
|
35
35
|
findHarnessRoot
|
|
36
|
-
} from "./chunks/chunk-
|
|
37
|
-
import "./chunks/chunk-
|
|
38
|
-
import "./chunks/chunk-
|
|
39
|
-
import "./chunks/chunk-
|
|
36
|
+
} from "./chunks/chunk-WIT6364Y.js";
|
|
37
|
+
import "./chunks/chunk-GPS3SAGQ.js";
|
|
38
|
+
import "./chunks/chunk-JTXFTNEA.js";
|
|
39
|
+
import "./chunks/chunk-GE6UUEG2.js";
|
|
40
40
|
import "./chunks/chunk-YYPYYOEV.js";
|
|
41
|
+
import "./chunks/chunk-S2XVNTQP.js";
|
|
41
42
|
import "./chunks/chunk-GZDLXYZV.js";
|
|
42
43
|
import {
|
|
43
44
|
mediaDimensions
|
package/bin/vitris-build.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
buildVerified
|
|
4
|
-
} from "./chunks/chunk-
|
|
5
|
-
import "./chunks/chunk-
|
|
6
|
-
import "./chunks/chunk-
|
|
7
|
-
import "./chunks/chunk-
|
|
4
|
+
} from "./chunks/chunk-WIT6364Y.js";
|
|
5
|
+
import "./chunks/chunk-GPS3SAGQ.js";
|
|
6
|
+
import "./chunks/chunk-JTXFTNEA.js";
|
|
7
|
+
import "./chunks/chunk-GE6UUEG2.js";
|
|
8
8
|
import "./chunks/chunk-YYPYYOEV.js";
|
|
9
|
+
import "./chunks/chunk-S2XVNTQP.js";
|
|
9
10
|
import "./chunks/chunk-GZDLXYZV.js";
|
|
10
11
|
import "./chunks/chunk-T6PE4KN4.js";
|
|
11
12
|
import "./chunks/chunk-7FS67SRF.js";
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
createSite
|
|
4
|
-
} from "./chunks/chunk-
|
|
4
|
+
} from "./chunks/chunk-5KRJQKHB.js";
|
|
5
5
|
import "./chunks/chunk-34H54KEY.js";
|
|
6
6
|
import "./chunks/chunk-JCRGE6AL.js";
|
|
7
7
|
import {
|
|
8
8
|
promptSourceUrl
|
|
9
|
-
} from "./chunks/chunk-
|
|
9
|
+
} from "./chunks/chunk-OZD7ZWDX.js";
|
|
10
10
|
import "./chunks/chunk-XGTHGWPW.js";
|
|
11
11
|
import {
|
|
12
12
|
siteInputsFromArgv
|
|
13
|
-
} from "./chunks/chunk-
|
|
13
|
+
} from "./chunks/chunk-WK7HJI5H.js";
|
|
14
14
|
import "./chunks/chunk-KVMZSQ3D.js";
|
|
15
15
|
import "./chunks/chunk-WOORR67N.js";
|
|
16
16
|
import "./chunks/chunk-PRLL7B63.js";
|
|
@@ -20,15 +20,16 @@ import "./chunks/chunk-M4LNHYYR.js";
|
|
|
20
20
|
import "./chunks/chunk-TY33XMHW.js";
|
|
21
21
|
import "./chunks/chunk-HL6JVA4A.js";
|
|
22
22
|
import "./chunks/chunk-M2ETYSVJ.js";
|
|
23
|
-
import "./chunks/chunk-
|
|
23
|
+
import "./chunks/chunk-C3F7XQD2.js";
|
|
24
24
|
import "./chunks/chunk-BIEDSEL2.js";
|
|
25
25
|
import "./chunks/chunk-IKNMBGGD.js";
|
|
26
26
|
import "./chunks/chunk-WEFDVXFX.js";
|
|
27
|
-
import "./chunks/chunk-
|
|
28
|
-
import "./chunks/chunk-
|
|
29
|
-
import "./chunks/chunk-
|
|
30
|
-
import "./chunks/chunk-
|
|
27
|
+
import "./chunks/chunk-WIT6364Y.js";
|
|
28
|
+
import "./chunks/chunk-GPS3SAGQ.js";
|
|
29
|
+
import "./chunks/chunk-JTXFTNEA.js";
|
|
30
|
+
import "./chunks/chunk-GE6UUEG2.js";
|
|
31
31
|
import "./chunks/chunk-YYPYYOEV.js";
|
|
32
|
+
import "./chunks/chunk-S2XVNTQP.js";
|
|
32
33
|
import "./chunks/chunk-GZDLXYZV.js";
|
|
33
34
|
import "./chunks/chunk-G7MNMULR.js";
|
|
34
35
|
import "./chunks/chunk-T6PE4KN4.js";
|