@mseep/dembrandt 0.19.5
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/LICENSE +21 -0
- package/README.md +408 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +532 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/browser.d.ts +16 -0
- package/dist/lib/browser.js +27 -0
- package/dist/lib/browser.js.map +1 -0
- package/dist/lib/colors.d.ts +101 -0
- package/dist/lib/colors.js +405 -0
- package/dist/lib/colors.js.map +1 -0
- package/dist/lib/compare.d.ts +31 -0
- package/dist/lib/compare.js +46 -0
- package/dist/lib/compare.js.map +1 -0
- package/dist/lib/discovery.d.ts +31 -0
- package/dist/lib/discovery.js +243 -0
- package/dist/lib/discovery.js.map +1 -0
- package/dist/lib/drift.d.ts +64 -0
- package/dist/lib/drift.js +383 -0
- package/dist/lib/drift.js.map +1 -0
- package/dist/lib/dtcg/validate.d.ts +51 -0
- package/dist/lib/dtcg/validate.js +1403 -0
- package/dist/lib/dtcg/validate.js.map +1 -0
- package/dist/lib/exit-codes.d.ts +29 -0
- package/dist/lib/exit-codes.js +26 -0
- package/dist/lib/exit-codes.js.map +1 -0
- package/dist/lib/extractors/breakpoints.d.ts +5 -0
- package/dist/lib/extractors/breakpoints.js +450 -0
- package/dist/lib/extractors/breakpoints.js.map +1 -0
- package/dist/lib/extractors/colors.d.ts +2 -0
- package/dist/lib/extractors/colors.js +657 -0
- package/dist/lib/extractors/colors.js.map +1 -0
- package/dist/lib/extractors/components.d.ts +4 -0
- package/dist/lib/extractors/components.js +370 -0
- package/dist/lib/extractors/components.js.map +1 -0
- package/dist/lib/extractors/index.d.ts +9 -0
- package/dist/lib/extractors/index.js +1257 -0
- package/dist/lib/extractors/index.js.map +1 -0
- package/dist/lib/extractors/logo.d.ts +2 -0
- package/dist/lib/extractors/logo.js +626 -0
- package/dist/lib/extractors/logo.js.map +1 -0
- package/dist/lib/extractors/spacing.d.ts +4 -0
- package/dist/lib/extractors/spacing.js +163 -0
- package/dist/lib/extractors/spacing.js.map +1 -0
- package/dist/lib/extractors/teach.d.ts +1 -0
- package/dist/lib/extractors/teach.js +66 -0
- package/dist/lib/extractors/teach.js.map +1 -0
- package/dist/lib/extractors/typography.d.ts +1 -0
- package/dist/lib/extractors/typography.js +163 -0
- package/dist/lib/extractors/typography.js.map +1 -0
- package/dist/lib/findings.d.ts +34 -0
- package/dist/lib/findings.js +166 -0
- package/dist/lib/findings.js.map +1 -0
- package/dist/lib/formatters/dtcg.d.ts +10 -0
- package/dist/lib/formatters/dtcg.js +416 -0
- package/dist/lib/formatters/dtcg.js.map +1 -0
- package/dist/lib/formatters/html.d.ts +25 -0
- package/dist/lib/formatters/html.js +479 -0
- package/dist/lib/formatters/html.js.map +1 -0
- package/dist/lib/formatters/markdown.d.ts +5 -0
- package/dist/lib/formatters/markdown.js +568 -0
- package/dist/lib/formatters/markdown.js.map +1 -0
- package/dist/lib/formatters/pdf.d.ts +12 -0
- package/dist/lib/formatters/pdf.js +1121 -0
- package/dist/lib/formatters/pdf.js.map +1 -0
- package/dist/lib/formatters/terminal.d.ts +6 -0
- package/dist/lib/formatters/terminal.js +954 -0
- package/dist/lib/formatters/terminal.js.map +1 -0
- package/dist/lib/formatters/theme.d.ts +35 -0
- package/dist/lib/formatters/theme.js +37 -0
- package/dist/lib/formatters/theme.js.map +1 -0
- package/dist/lib/merger.d.ts +14 -0
- package/dist/lib/merger.js +362 -0
- package/dist/lib/merger.js.map +1 -0
- package/dist/lib/normalize.d.ts +29 -0
- package/dist/lib/normalize.js +59 -0
- package/dist/lib/normalize.js.map +1 -0
- package/dist/lib/robots.d.ts +12 -0
- package/dist/lib/robots.js +110 -0
- package/dist/lib/robots.js.map +1 -0
- package/dist/lib/run-summary.d.ts +40 -0
- package/dist/lib/run-summary.js +64 -0
- package/dist/lib/run-summary.js.map +1 -0
- package/dist/lib/types.d.ts +329 -0
- package/dist/lib/types.js +7 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/lib/version.d.ts +134 -0
- package/dist/lib/version.js +153 -0
- package/dist/lib/version.js.map +1 -0
- package/dist/mcp-server.d.ts +11 -0
- package/dist/mcp-server.js +311 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/package.json +106 -0
- package/dist/test/_vitest-shim.d.ts +13 -0
- package/dist/test/_vitest-shim.js +23 -0
- package/dist/test/_vitest-shim.js.map +1 -0
- package/dist/test/cli.test.d.ts +1 -0
- package/dist/test/cli.test.js +24 -0
- package/dist/test/cli.test.js.map +1 -0
- package/dist/test/colors.test.d.ts +1 -0
- package/dist/test/colors.test.js +64 -0
- package/dist/test/colors.test.js.map +1 -0
- package/dist/test/compare.test.d.ts +1 -0
- package/dist/test/compare.test.js +57 -0
- package/dist/test/compare.test.js.map +1 -0
- package/dist/test/drift.test.d.ts +1 -0
- package/dist/test/drift.test.js +53 -0
- package/dist/test/drift.test.js.map +1 -0
- package/dist/test/dtcg-formatter.test.d.ts +1 -0
- package/dist/test/dtcg-formatter.test.js +48 -0
- package/dist/test/dtcg-formatter.test.js.map +1 -0
- package/dist/test/dtcg-validate.test.d.ts +1 -0
- package/dist/test/dtcg-validate.test.js +2129 -0
- package/dist/test/dtcg-validate.test.js.map +1 -0
- package/dist/test/exit-codes.test.d.ts +1 -0
- package/dist/test/exit-codes.test.js +53 -0
- package/dist/test/exit-codes.test.js.map +1 -0
- package/dist/test/findings.test.d.ts +1 -0
- package/dist/test/findings.test.js +77 -0
- package/dist/test/findings.test.js.map +1 -0
- package/dist/test/html.test.d.ts +1 -0
- package/dist/test/html.test.js +95 -0
- package/dist/test/html.test.js.map +1 -0
- package/dist/test/markdown.test.d.ts +1 -0
- package/dist/test/markdown.test.js +145 -0
- package/dist/test/markdown.test.js.map +1 -0
- package/dist/test/merger.test.d.ts +1 -0
- package/dist/test/merger.test.js +98 -0
- package/dist/test/merger.test.js.map +1 -0
- package/dist/test/normalize.test.d.ts +1 -0
- package/dist/test/normalize.test.js +47 -0
- package/dist/test/normalize.test.js.map +1 -0
- package/dist/test/run-summary.test.d.ts +1 -0
- package/dist/test/run-summary.test.js +45 -0
- package/dist/test/run-summary.test.js.map +1 -0
- package/dist/test/version.test.d.ts +1 -0
- package/dist/test/version.test.js +73 -0
- package/dist/test/version.test.js.map +1 -0
- package/package.json +106 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Design-system findings — the honest, computable audits that back the report's
|
|
3
|
+
* summary scores. Every gauge number traces to a concrete finding here; nothing
|
|
4
|
+
* is a vanity metric. These are the decay signals product-strategy names: a hex
|
|
5
|
+
* off by a hair (ΔE), a role with no visual hierarchy, a brand colour that fails
|
|
6
|
+
* contrast, an off-scale spacing value. Pure and deterministic — given the same
|
|
7
|
+
* extraction it always returns the same findings, so it is safe to gate on.
|
|
8
|
+
*
|
|
9
|
+
* This mirrors the documented `--lint` categories (development-prompts.md): each
|
|
10
|
+
* finding has a severity, and the consistency score is derived from them.
|
|
11
|
+
*/
|
|
12
|
+
import { deltaE2000, relativeLuminance } from "./colors.js";
|
|
13
|
+
/** Parse a hex or rgb()/rgba() string to #rrggbb, or null if unparseable. */
|
|
14
|
+
function toHex(input) {
|
|
15
|
+
if (!input)
|
|
16
|
+
return null;
|
|
17
|
+
const s = String(input).trim();
|
|
18
|
+
if (/^#[0-9a-f]{6}$/i.test(s))
|
|
19
|
+
return s.toLowerCase();
|
|
20
|
+
if (/^#[0-9a-f]{3}$/i.test(s)) {
|
|
21
|
+
return ("#" + s.slice(1).split("").map((c) => c + c).join("")).toLowerCase();
|
|
22
|
+
}
|
|
23
|
+
const m = s.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/i);
|
|
24
|
+
if (m) {
|
|
25
|
+
const h = (n) => Math.max(0, Math.min(255, Number(n))).toString(16).padStart(2, "0");
|
|
26
|
+
return ("#" + h(m[1]) + h(m[2]) + h(m[3])).toLowerCase();
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
/** WCAG contrast ratio between two colours (any parseable form), or null. */
|
|
31
|
+
function contrastRatio(a, b) {
|
|
32
|
+
const ha = toHex(a);
|
|
33
|
+
const hb = toHex(b);
|
|
34
|
+
if (!ha || !hb)
|
|
35
|
+
return null;
|
|
36
|
+
const la = relativeLuminance(ha);
|
|
37
|
+
const lb = relativeLuminance(hb);
|
|
38
|
+
if (la == null || lb == null)
|
|
39
|
+
return null;
|
|
40
|
+
const hi = Math.max(la, lb);
|
|
41
|
+
const lo = Math.min(la, lb);
|
|
42
|
+
return (hi + 0.05) / (lo + 0.05);
|
|
43
|
+
}
|
|
44
|
+
/** Leading pixel value of a size string like "40px (2.50rem)", or null. */
|
|
45
|
+
function pxOf(size) {
|
|
46
|
+
if (!size)
|
|
47
|
+
return null;
|
|
48
|
+
const m = String(size).match(/(-?\d+(?:\.\d+)?)\s*px/);
|
|
49
|
+
return m ? Number(m[1]) : null;
|
|
50
|
+
}
|
|
51
|
+
export function computeFindings(result) {
|
|
52
|
+
const findings = [];
|
|
53
|
+
// 1. Near-duplicate palette colours (ΔE2000 < just-noticeable). The "#133074
|
|
54
|
+
// where #133174 should be" decay — two tokens that are visually one colour.
|
|
55
|
+
const palette = (result.colors?.palette ?? [])
|
|
56
|
+
.map((c) => (c.normalized || c.color || "").toLowerCase())
|
|
57
|
+
.filter((h) => /^#[0-9a-f]{6}$/i.test(h));
|
|
58
|
+
const seenDup = new Set();
|
|
59
|
+
for (let i = 0; i < palette.length; i++) {
|
|
60
|
+
for (let j = i + 1; j < palette.length; j++) {
|
|
61
|
+
const d = deltaE2000(palette[i], palette[j]);
|
|
62
|
+
// < 1.0 = indistinguishable. Looser (the 2.3 JND) flags intentional
|
|
63
|
+
// surface tokens (e.g. #fff vs #f6f6f7 at ΔE 1.9) and creates noise.
|
|
64
|
+
if (d < 1.0) {
|
|
65
|
+
const key = [palette[i], palette[j]].sort().join("|");
|
|
66
|
+
if (seenDup.has(key))
|
|
67
|
+
continue;
|
|
68
|
+
seenDup.add(key);
|
|
69
|
+
findings.push({
|
|
70
|
+
category: "duplication",
|
|
71
|
+
group: "Color",
|
|
72
|
+
severity: "warn",
|
|
73
|
+
message: `${palette[i]} and ${palette[j]} are perceptually identical (ΔE ${d.toFixed(1)}) — likely one token split in two.`,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// 2. Type roles with no hierarchy — two distinct roles sharing size + weight.
|
|
79
|
+
// Only flag where hierarchy is *expected*: two heading levels that collide,
|
|
80
|
+
// or a heading sharing a size with body. Body/link/ui sharing a size is the
|
|
81
|
+
// convention, not a smell, so those groups are skipped.
|
|
82
|
+
const isHierarchy = (r) => /heading|display|title|^h[1-6]$/i.test(r);
|
|
83
|
+
const isText = (r) => /body|text|paragraph|^p$/i.test(r);
|
|
84
|
+
const styles = result.typography?.styles ?? [];
|
|
85
|
+
const byKey = new Map();
|
|
86
|
+
for (const s of styles) {
|
|
87
|
+
const px = pxOf(s.size);
|
|
88
|
+
if (px == null || !s.context)
|
|
89
|
+
continue;
|
|
90
|
+
const rounded = Math.round(px);
|
|
91
|
+
const weight = String(s.weight ?? "");
|
|
92
|
+
const wmap = byKey.get(rounded) ?? new Map();
|
|
93
|
+
const arr = wmap.get(weight) ?? [];
|
|
94
|
+
if (!arr.includes(s.context))
|
|
95
|
+
arr.push(s.context);
|
|
96
|
+
wmap.set(weight, arr);
|
|
97
|
+
byKey.set(rounded, wmap);
|
|
98
|
+
}
|
|
99
|
+
for (const [px, wmap] of byKey) {
|
|
100
|
+
for (const [weight, roles] of wmap) {
|
|
101
|
+
if (roles.length < 2)
|
|
102
|
+
continue;
|
|
103
|
+
const relevant = roles.filter(isHierarchy).length >= 2 || (roles.some(isHierarchy) && roles.some(isText));
|
|
104
|
+
if (!relevant)
|
|
105
|
+
continue;
|
|
106
|
+
findings.push({
|
|
107
|
+
category: "consistency",
|
|
108
|
+
group: "Typography",
|
|
109
|
+
severity: "warn",
|
|
110
|
+
message: `${roles.slice(0, 4).join(", ")} share ${px}px${weight ? ` / ${weight}` : ""} — no visual hierarchy between them.`,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// 3. Brand colour contrast on white — the default canvas. A primary that can't
|
|
115
|
+
// meet AA as text on white is the "brand colour fails contrast" decay. Warn
|
|
116
|
+
// (not error): it may be used only as a fill behind white text, which we
|
|
117
|
+
// can't confirm here. Checked against white only — every colour trivially
|
|
118
|
+
// clears one of pure black/white, so "best of the two" would never fire.
|
|
119
|
+
const primary = result.colors?.semantic?.primary;
|
|
120
|
+
if (primary) {
|
|
121
|
+
const onWhite = contrastRatio(primary, "#ffffff");
|
|
122
|
+
if (onWhite != null && onWhite < 4.5) {
|
|
123
|
+
findings.push({
|
|
124
|
+
category: "contrast",
|
|
125
|
+
group: "Contrast",
|
|
126
|
+
severity: "warn",
|
|
127
|
+
message: `Primary ${primary} has low contrast on white (${onWhite.toFixed(1)}:1) — fails WCAG AA for text.`,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// 4. Off-scale spacing — values that break the detected base grid.
|
|
132
|
+
const scaleType = result.spacing?.scaleType ?? "";
|
|
133
|
+
const base = scaleType === "base-8" ? 8 : scaleType === "base-4" ? 4 : 0;
|
|
134
|
+
if (base) {
|
|
135
|
+
const off = (result.spacing?.commonValues ?? [])
|
|
136
|
+
.map((v) => v.px)
|
|
137
|
+
.filter((px) => typeof px === "number" && px >= base && px % base !== 0);
|
|
138
|
+
if (off.length) {
|
|
139
|
+
findings.push({
|
|
140
|
+
category: "consistency",
|
|
141
|
+
group: "Spacing",
|
|
142
|
+
severity: "warn",
|
|
143
|
+
message: `${off.slice(0, 5).map((p) => `${p}px`).join(", ")} ${off.length === 1 ? "is" : "are"} off the ${scaleType} spacing grid.`,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Two scores, each derived from its own findings — never invented. Errors
|
|
148
|
+
// cost more than warnings. Consistency = type/spacing/duplication; Contrast =
|
|
149
|
+
// accessibility. Both are always computable, so the header is never one lone
|
|
150
|
+
// gauge.
|
|
151
|
+
const cost = (f) => (f.severity === "error" ? 12 : 6);
|
|
152
|
+
const sum = (cat) => Math.max(0, 100 - findings.filter(cat).reduce((n, f) => n + cost(f), 0));
|
|
153
|
+
const consistency = sum((f) => f.category !== "contrast");
|
|
154
|
+
const contrast = sum((f) => f.category === "contrast");
|
|
155
|
+
// Coverage: how complete the captured token set is.
|
|
156
|
+
const present = [
|
|
157
|
+
(result.colors?.palette?.length ?? 0) > 0,
|
|
158
|
+
(result.typography?.styles?.length ?? 0) > 0,
|
|
159
|
+
(result.spacing?.commonValues?.length ?? 0) > 0,
|
|
160
|
+
(result.borderRadius?.values?.length ?? 0) > 0,
|
|
161
|
+
(result.shadows?.length ?? 0) > 0,
|
|
162
|
+
(result.breakpoints?.length ?? 0) > 0,
|
|
163
|
+
].filter(Boolean).length;
|
|
164
|
+
return { findings, consistency, contrast, coverage: { present, total: 6 } };
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=findings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"findings.js","sourceRoot":"","sources":["../../lib/findings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAwB5D,6EAA6E;AAC7E,SAAS,KAAK,CAAC,KAAgC;IAC7C,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/B,IAAI,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;IACtD,IAAI,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9B,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IAC/E,CAAC;IACD,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC9D,IAAI,CAAC,EAAE,CAAC;QACN,MAAM,CAAC,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC7F,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IAC3D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,6EAA6E;AAC7E,SAAS,aAAa,CAAC,CAAS,EAAE,CAAS;IACzC,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACpB,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACpB,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IAC5B,MAAM,EAAE,GAAG,iBAAiB,CAAC,EAAE,CAAC,CAAC;IACjC,MAAM,EAAE,GAAG,iBAAiB,CAAC,EAAE,CAAC,CAAC;IACjC,IAAI,EAAE,IAAI,IAAI,IAAI,EAAE,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IAC1C,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAC5B,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAC5B,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;AACnC,CAAC;AAED,2EAA2E;AAC3E,SAAS,IAAI,CAAC,IAAwB;IACpC,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;IACvD,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,MAAsB;IACpD,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,6EAA6E;IAC7E,+EAA+E;IAC/E,MAAM,OAAO,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,IAAI,EAAE,CAAC;SAC3C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;SACzD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,KAAK,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5C,MAAM,CAAC,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7C,oEAAoE;YACpE,qEAAqE;YACrE,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;gBACZ,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACtD,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;oBAAE,SAAS;gBAC/B,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACjB,QAAQ,CAAC,IAAI,CAAC;oBACZ,QAAQ,EAAE,aAAa;oBACvB,KAAK,EAAE,OAAO;oBACd,QAAQ,EAAE,MAAM;oBAChB,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,QAAQ,OAAO,CAAC,CAAC,CAAC,mCAAmC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,oCAAoC;iBAC5H,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,8EAA8E;IAC9E,+EAA+E;IAC/E,+EAA+E;IAC/E,2DAA2D;IAC3D,MAAM,WAAW,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,iCAAiC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7E,MAAM,MAAM,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,0BAA0B,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjE,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,EAAE,MAAM,IAAI,EAAE,CAAC;IAC/C,MAAM,KAAK,GAAG,IAAI,GAAG,EAAiC,CAAC;IACvD,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACxB,IAAI,EAAE,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,OAAO;YAAE,SAAS;QACvC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC/B,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,GAAG,EAAoB,CAAC;QAC/D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACnC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAClD,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACtB,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAC3B,CAAC;IACD,KAAK,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC;QAC/B,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;YACnC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;gBAAE,SAAS;YAC/B,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;YAC1G,IAAI,CAAC,QAAQ;gBAAE,SAAS;YACxB,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,aAAa;gBACvB,KAAK,EAAE,YAAY;gBACnB,QAAQ,EAAE,MAAM;gBAChB,OAAO,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,sCAAsC;aAC5H,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,+EAA+E;IAC/E,+EAA+E;IAC/E,4EAA4E;IAC5E,6EAA6E;IAC7E,4EAA4E;IAC5E,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,OAA6B,CAAC;IACvE,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAClD,IAAI,OAAO,IAAI,IAAI,IAAI,OAAO,GAAG,GAAG,EAAE,CAAC;YACrC,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,UAAU;gBACpB,KAAK,EAAE,UAAU;gBACjB,QAAQ,EAAE,MAAM;gBAChB,OAAO,EAAE,WAAW,OAAO,+BAA+B,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,+BAA+B;aAC5G,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,mEAAmE;IACnE,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,EAAE,SAAS,IAAI,EAAE,CAAC;IAClD,MAAM,IAAI,GAAG,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACzE,IAAI,IAAI,EAAE,CAAC;QACT,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,YAAY,IAAI,EAAE,CAAC;aAC7C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAChB,MAAM,CAAC,CAAC,EAAE,EAAgB,EAAE,CAAC,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,IAAI,IAAI,IAAI,EAAE,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC;QACzF,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;YACf,QAAQ,CAAC,IAAI,CAAC;gBACZ,QAAQ,EAAE,aAAa;gBACvB,KAAK,EAAE,SAAS;gBAChB,QAAQ,EAAE,MAAM;gBAChB,OAAO,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,YAAY,SAAS,gBAAgB;aACpI,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,0EAA0E;IAC1E,8EAA8E;IAC9E,6EAA6E;IAC7E,SAAS;IACT,MAAM,IAAI,GAAG,CAAC,CAAU,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/D,MAAM,GAAG,GAAG,CAAC,GAA4B,EAAE,EAAE,CAC3C,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3E,MAAM,WAAW,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC;IAC1D,MAAM,QAAQ,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC;IAEvD,oDAAoD;IACpD,MAAM,OAAO,GAAG;QACd,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC;QACzC,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC;QAC5C,CAAC,MAAM,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC;QAC/C,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC;QAC9C,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC;QACjC,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC;KACtC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;IAEzB,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;AAC9E,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DTCG (W3C Design Tokens CG) format exporter
|
|
3
|
+
* Converts dembrandt extraction output to W3C DTCG format
|
|
4
|
+
* Spec: https://www.designtokens.org/TR/2025.10/format/
|
|
5
|
+
*/
|
|
6
|
+
import type { BrandingResult } from '../types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Main export function - converts dembrandt output to W3C Design Tokens format
|
|
9
|
+
*/
|
|
10
|
+
export declare function toDtcgTokens(extractionResult: BrandingResult): Record<string, any>;
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DTCG (W3C Design Tokens CG) format exporter
|
|
3
|
+
* Converts dembrandt extraction output to W3C DTCG format
|
|
4
|
+
* Spec: https://www.designtokens.org/TR/2025.10/format/
|
|
5
|
+
*/
|
|
6
|
+
import { buildDembrandtProvenance, EXTENSION_KEY } from '../version.js';
|
|
7
|
+
/**
|
|
8
|
+
* Convert color value to DTCG color format
|
|
9
|
+
* Spec: https://www.designtokens.org/TR/2025.10/format/#color
|
|
10
|
+
*
|
|
11
|
+
* Based on spec examples (Example 7, 45, 54), color format includes:
|
|
12
|
+
* - colorSpace: "srgb"
|
|
13
|
+
* - components: [r, g, b] (normalized 0-1)
|
|
14
|
+
* - hex: "#rrggbb"
|
|
15
|
+
* - alpha: 0-1 (optional, for transparency)
|
|
16
|
+
*/
|
|
17
|
+
function hexToDtcgColor(color, alpha = 1) {
|
|
18
|
+
let r, g, b;
|
|
19
|
+
// Handle rgba(r, g, b, a) format
|
|
20
|
+
if (typeof color === 'string' && color.includes('rgba')) {
|
|
21
|
+
const match = color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
|
|
22
|
+
if (match) {
|
|
23
|
+
r = parseInt(match[1]) / 255;
|
|
24
|
+
g = parseInt(match[2]) / 255;
|
|
25
|
+
b = parseInt(match[3]) / 255;
|
|
26
|
+
alpha = match[4] ? parseFloat(match[4]) : 1;
|
|
27
|
+
const hexR = parseInt(match[1]).toString(16).padStart(2, '0');
|
|
28
|
+
const hexG = parseInt(match[2]).toString(16).padStart(2, '0');
|
|
29
|
+
const hexB = parseInt(match[3]).toString(16).padStart(2, '0');
|
|
30
|
+
const hexValue = `#${hexR}${hexG}${hexB}`;
|
|
31
|
+
const result = {
|
|
32
|
+
colorSpace: 'srgb',
|
|
33
|
+
components: [
|
|
34
|
+
Math.round(r * 1000) / 1000,
|
|
35
|
+
Math.round(g * 1000) / 1000,
|
|
36
|
+
Math.round(b * 1000) / 1000
|
|
37
|
+
],
|
|
38
|
+
hex: hexValue
|
|
39
|
+
};
|
|
40
|
+
// Include alpha only if it's not 1 (fully opaque)
|
|
41
|
+
if (alpha !== 1) {
|
|
42
|
+
result.alpha = Math.round(alpha * 1000) / 1000;
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Handle rgb(r, g, b) format
|
|
48
|
+
if (typeof color === 'string' && color.includes('rgb')) {
|
|
49
|
+
const match = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
|
|
50
|
+
if (match) {
|
|
51
|
+
r = parseInt(match[1]) / 255;
|
|
52
|
+
g = parseInt(match[2]) / 255;
|
|
53
|
+
b = parseInt(match[3]) / 255;
|
|
54
|
+
const hexR = parseInt(match[1]).toString(16).padStart(2, '0');
|
|
55
|
+
const hexG = parseInt(match[2]).toString(16).padStart(2, '0');
|
|
56
|
+
const hexB = parseInt(match[3]).toString(16).padStart(2, '0');
|
|
57
|
+
const hexValue = `#${hexR}${hexG}${hexB}`;
|
|
58
|
+
return {
|
|
59
|
+
colorSpace: 'srgb',
|
|
60
|
+
components: [
|
|
61
|
+
Math.round(r * 1000) / 1000,
|
|
62
|
+
Math.round(g * 1000) / 1000,
|
|
63
|
+
Math.round(b * 1000) / 1000
|
|
64
|
+
],
|
|
65
|
+
hex: hexValue
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Handle hex format
|
|
70
|
+
const cleanHex = color.replace('#', '');
|
|
71
|
+
// Ensure it's 6 characters
|
|
72
|
+
if (cleanHex.length !== 6) {
|
|
73
|
+
return {
|
|
74
|
+
colorSpace: 'srgb',
|
|
75
|
+
components: [0, 0, 0],
|
|
76
|
+
hex: '#000000'
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
// Parse RGB components
|
|
80
|
+
r = parseInt(cleanHex.substring(0, 2), 16) / 255;
|
|
81
|
+
g = parseInt(cleanHex.substring(2, 4), 16) / 255;
|
|
82
|
+
b = parseInt(cleanHex.substring(4, 6), 16) / 255;
|
|
83
|
+
const result = {
|
|
84
|
+
colorSpace: 'srgb',
|
|
85
|
+
components: [
|
|
86
|
+
Math.round(r * 1000) / 1000,
|
|
87
|
+
Math.round(g * 1000) / 1000,
|
|
88
|
+
Math.round(b * 1000) / 1000
|
|
89
|
+
],
|
|
90
|
+
hex: `#${cleanHex}`
|
|
91
|
+
};
|
|
92
|
+
// Include alpha only if it's not 1 (fully opaque)
|
|
93
|
+
if (alpha !== 1) {
|
|
94
|
+
result.alpha = Math.round(alpha * 1000) / 1000;
|
|
95
|
+
}
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Convert a dimension value to W3C DTCG dimension format.
|
|
100
|
+
*
|
|
101
|
+
* DTCG restricts dimensions to px or rem. We normalize what we can (em → rem,
|
|
102
|
+
* 1:1, the closest valid unit) and return null for anything inexpressible
|
|
103
|
+
* (%, vw, vh, unitless). Callers must skip the token/field on null rather than
|
|
104
|
+
* emit an invalid dimension. The value stays in the native output regardless.
|
|
105
|
+
*
|
|
106
|
+
* @param {string | number | { px?: number } | null | undefined} value
|
|
107
|
+
* @returns {{ value: number, unit: 'px' | 'rem' } | null}
|
|
108
|
+
*/
|
|
109
|
+
function toDtcgDimension(value) {
|
|
110
|
+
let num;
|
|
111
|
+
let unit = 'px';
|
|
112
|
+
if (typeof value === 'string') {
|
|
113
|
+
// Ignore anything in parentheses, e.g. "16px (1.00rem)"
|
|
114
|
+
const cleanValue = value.split('(')[0].trim();
|
|
115
|
+
const match = cleanValue.match(/^([-\d.]+)\s*([a-z%]*)$/i);
|
|
116
|
+
if (!match)
|
|
117
|
+
return null;
|
|
118
|
+
num = parseFloat(match[1]);
|
|
119
|
+
unit = (match[2] || 'px').toLowerCase();
|
|
120
|
+
}
|
|
121
|
+
else if (typeof value === 'number') {
|
|
122
|
+
num = value;
|
|
123
|
+
}
|
|
124
|
+
else if (value && typeof value === 'object' && value.px !== undefined) {
|
|
125
|
+
num = value.px;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
num = parseFloat(value);
|
|
129
|
+
}
|
|
130
|
+
if (!Number.isFinite(num))
|
|
131
|
+
return null;
|
|
132
|
+
if (unit === 'em')
|
|
133
|
+
unit = 'rem'; // closest valid DTCG unit; 1em ≈ 1rem at root
|
|
134
|
+
if (unit !== 'px' && unit !== 'rem')
|
|
135
|
+
return null; // %, vw, vh, etc.: inexpressible
|
|
136
|
+
return { value: num, unit };
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Sanitize token names to be W3C compliant
|
|
140
|
+
* - Cannot start with $
|
|
141
|
+
* - Cannot contain {, }, or .
|
|
142
|
+
*/
|
|
143
|
+
function sanitizeTokenName(name) {
|
|
144
|
+
return name
|
|
145
|
+
.replace(/^\$/, '')
|
|
146
|
+
.replace(/[{}.]/g, '-')
|
|
147
|
+
.replace(/\s+/g, '-')
|
|
148
|
+
.toLowerCase();
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Export colors to DTCG format
|
|
152
|
+
*/
|
|
153
|
+
function exportColors(colors) {
|
|
154
|
+
if (!colors || !colors.palette || colors.palette.length === 0) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
const colorTokens = {};
|
|
158
|
+
// Add semantic colors if available
|
|
159
|
+
if (colors.semantic) {
|
|
160
|
+
const semantic = {};
|
|
161
|
+
for (const [key, value] of Object.entries(colors.semantic)) {
|
|
162
|
+
if (value) {
|
|
163
|
+
// Handle both direct color values and objects with color property
|
|
164
|
+
const colorValue = typeof value === 'string' ? value : value.color;
|
|
165
|
+
if (colorValue) {
|
|
166
|
+
semantic[sanitizeTokenName(key)] = {
|
|
167
|
+
$type: 'color',
|
|
168
|
+
$value: hexToDtcgColor(colorValue)
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (Object.keys(semantic).length > 0) {
|
|
174
|
+
colorTokens.semantic = semantic;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Add palette colors (only high and medium confidence)
|
|
178
|
+
const palette = {};
|
|
179
|
+
colors.palette
|
|
180
|
+
.filter(colorEntry => colorEntry.confidence !== 'low')
|
|
181
|
+
.slice(0, 30) // Limit to top 30 colors
|
|
182
|
+
.forEach((colorEntry, index) => {
|
|
183
|
+
const name = `palette-${index + 1}`; // Use numbered names with prefix
|
|
184
|
+
palette[name] = {
|
|
185
|
+
$type: 'color',
|
|
186
|
+
$value: hexToDtcgColor(colorEntry.color),
|
|
187
|
+
$description: `Count: ${colorEntry.count || 0}, Confidence: ${colorEntry.confidence || 'unknown'}`
|
|
188
|
+
};
|
|
189
|
+
});
|
|
190
|
+
if (Object.keys(palette).length > 0) {
|
|
191
|
+
colorTokens.palette = palette;
|
|
192
|
+
}
|
|
193
|
+
return Object.keys(colorTokens).length > 0 ? colorTokens : null;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Export typography to DTCG format
|
|
197
|
+
*/
|
|
198
|
+
function exportTypography(typography) {
|
|
199
|
+
if (!typography || !typography.styles || typography.styles.length === 0) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
const typographyTokens = {};
|
|
203
|
+
// Export font families
|
|
204
|
+
const uniqueFamilies = new Set();
|
|
205
|
+
typography.styles.forEach(style => {
|
|
206
|
+
if (style.family) {
|
|
207
|
+
uniqueFamilies.add(style.family);
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
if (uniqueFamilies.size > 0) {
|
|
211
|
+
const fontFamilies = {};
|
|
212
|
+
Array.from(uniqueFamilies).forEach((family, index) => {
|
|
213
|
+
const name = sanitizeTokenName(family) || `font-${index + 1}`;
|
|
214
|
+
fontFamilies[name] = {
|
|
215
|
+
$type: 'fontFamily',
|
|
216
|
+
$value: family
|
|
217
|
+
};
|
|
218
|
+
});
|
|
219
|
+
typographyTokens['font-family'] = fontFamilies;
|
|
220
|
+
}
|
|
221
|
+
// Export text styles as composite typography tokens
|
|
222
|
+
const textStyles = {};
|
|
223
|
+
typography.styles.slice(0, 10).forEach((style, index) => {
|
|
224
|
+
// fontFamily and fontSize identify the style; without them a DTCG typography
|
|
225
|
+
// token cannot be both valid and honest, so skip rather than fabricate.
|
|
226
|
+
if (!style.family || !style.size)
|
|
227
|
+
return;
|
|
228
|
+
// fontSize must be expressible as a px/rem dimension; skip the token if not
|
|
229
|
+
// (e.g. a "%" size) rather than emit an invalid one.
|
|
230
|
+
const fontSize = toDtcgDimension(style.size);
|
|
231
|
+
if (!fontSize)
|
|
232
|
+
return;
|
|
233
|
+
const name = style.context
|
|
234
|
+
? `text-${sanitizeTokenName(style.context)}`
|
|
235
|
+
: `text-${index + 1}`;
|
|
236
|
+
const familyName = sanitizeTokenName(style.family) || `font-${index + 1}`;
|
|
237
|
+
// DTCG typography is a composite that requires all five sub-fields. weight,
|
|
238
|
+
// lineHeight and letterSpacing have legitimate CSS-normal defaults (400, 1.5,
|
|
239
|
+
// 0px), so fill them when the source did not capture one. That is the
|
|
240
|
+
// computed-normal value, not invented brand data.
|
|
241
|
+
// DTCG composite typography (§9.8): each sub-field is a RAW value of its
|
|
242
|
+
// type (or a reference), NOT a nested {$type,$value} token. fontFamily is a
|
|
243
|
+
// reference to the font-family token; the rest are raw dimension/number values.
|
|
244
|
+
textStyles[name] = {
|
|
245
|
+
$type: 'typography',
|
|
246
|
+
$value: {
|
|
247
|
+
fontFamily: `{typography.font-family.${familyName}}`,
|
|
248
|
+
fontSize,
|
|
249
|
+
fontWeight: typeof style.weight === 'number' ? style.weight : parseInt(style.weight) || 400,
|
|
250
|
+
lineHeight: style.lineHeight ? (parseFloat(style.lineHeight) || 1.5) : 1.5,
|
|
251
|
+
letterSpacing: (style.letterSpacing && toDtcgDimension(style.letterSpacing)) || { value: 0, unit: 'px' }
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
});
|
|
255
|
+
if (Object.keys(textStyles).length > 0) {
|
|
256
|
+
typographyTokens.style = textStyles;
|
|
257
|
+
}
|
|
258
|
+
return Object.keys(typographyTokens).length > 0 ? typographyTokens : null;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Export spacing to DTCG format
|
|
262
|
+
*/
|
|
263
|
+
function exportSpacing(spacing) {
|
|
264
|
+
if (!spacing || !spacing.commonValues || spacing.commonValues.length === 0) {
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
const spacingTokens = {};
|
|
268
|
+
spacing.commonValues.slice(0, 12).forEach((value, index) => {
|
|
269
|
+
const dim = toDtcgDimension(value.px || value);
|
|
270
|
+
if (!dim)
|
|
271
|
+
return; // inexpressible unit (%, vw, …): omit from DTCG output
|
|
272
|
+
spacingTokens[`spacing-${index + 1}`] = { $type: 'dimension', $value: dim };
|
|
273
|
+
});
|
|
274
|
+
return Object.keys(spacingTokens).length > 0 ? spacingTokens : null;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Export border radius to W3C format
|
|
278
|
+
*/
|
|
279
|
+
function exportBorderRadius(borderRadius) {
|
|
280
|
+
if (!borderRadius || !borderRadius.values || borderRadius.values.length === 0) {
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
const radiusTokens = {};
|
|
284
|
+
borderRadius.values
|
|
285
|
+
.filter(entry => entry.confidence !== 'low')
|
|
286
|
+
.slice(0, 6)
|
|
287
|
+
.forEach((entry, index) => {
|
|
288
|
+
const dim = toDtcgDimension(entry.value);
|
|
289
|
+
if (!dim)
|
|
290
|
+
return; // e.g. a "50%" pill radius: no valid px/rem form, omit
|
|
291
|
+
radiusTokens[`radius-${index + 1}`] = { $type: 'dimension', $value: dim };
|
|
292
|
+
});
|
|
293
|
+
return Object.keys(radiusTokens).length > 0 ? radiusTokens : null;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Export borders to DTCG format
|
|
297
|
+
*/
|
|
298
|
+
function exportBorders(borders) {
|
|
299
|
+
if (!borders || !borders.combinations || borders.combinations.length === 0) {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
const borderTokens = {};
|
|
303
|
+
const widths = {};
|
|
304
|
+
const colors = {};
|
|
305
|
+
const seenWidths = new Set();
|
|
306
|
+
const seenColors = new Set();
|
|
307
|
+
// Extract unique widths and colors from combinations
|
|
308
|
+
borders.combinations
|
|
309
|
+
.filter(combo => combo.confidence !== 'low')
|
|
310
|
+
.slice(0, 10)
|
|
311
|
+
.forEach((combo) => {
|
|
312
|
+
// Add width if not seen and expressible as px/rem
|
|
313
|
+
if (combo.width && !seenWidths.has(combo.width)) {
|
|
314
|
+
const dim = toDtcgDimension(combo.width);
|
|
315
|
+
if (dim) {
|
|
316
|
+
widths[`border-width-${seenWidths.size + 1}`] = { $type: 'dimension', $value: dim };
|
|
317
|
+
seenWidths.add(combo.width);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
// Add color if not seen and valid
|
|
321
|
+
if (combo.color && !seenColors.has(combo.color)) {
|
|
322
|
+
const colorIndex = seenColors.size + 1;
|
|
323
|
+
colors[`border-color-${colorIndex}`] = {
|
|
324
|
+
$type: 'color',
|
|
325
|
+
$value: hexToDtcgColor(combo.color)
|
|
326
|
+
};
|
|
327
|
+
seenColors.add(combo.color);
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
if (Object.keys(widths).length > 0) {
|
|
331
|
+
borderTokens.width = widths;
|
|
332
|
+
}
|
|
333
|
+
if (Object.keys(colors).length > 0) {
|
|
334
|
+
borderTokens.color = colors;
|
|
335
|
+
}
|
|
336
|
+
return Object.keys(borderTokens).length > 0 ? borderTokens : null;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Export shadows to DTCG format
|
|
340
|
+
*/
|
|
341
|
+
function exportShadows(shadows) {
|
|
342
|
+
if (!shadows || shadows.length === 0) {
|
|
343
|
+
return null;
|
|
344
|
+
}
|
|
345
|
+
const shadowTokens = {};
|
|
346
|
+
shadows
|
|
347
|
+
.filter(entry => entry.confidence !== 'low')
|
|
348
|
+
.slice(0, 6)
|
|
349
|
+
.forEach((entry, index) => {
|
|
350
|
+
const name = `shadow-${index + 1}`;
|
|
351
|
+
// Parse shadow string (simplified parsing)
|
|
352
|
+
// Format: offsetX offsetY blur spread color
|
|
353
|
+
const parts = entry.shadow.trim().split(/\s+/);
|
|
354
|
+
// Parse shadow (W3C format requires proper color object)
|
|
355
|
+
const shadowColor = parts[4] && parts[4].match(/^#[0-9a-fA-F]{6}$/)
|
|
356
|
+
? parts[4]
|
|
357
|
+
: '#000000';
|
|
358
|
+
const zero = { value: 0, unit: 'px' };
|
|
359
|
+
const dim = (v) => toDtcgDimension(v) || zero;
|
|
360
|
+
shadowTokens[name] = {
|
|
361
|
+
$type: 'shadow',
|
|
362
|
+
$value: {
|
|
363
|
+
offsetX: dim(parts[0]),
|
|
364
|
+
offsetY: dim(parts[1]),
|
|
365
|
+
blur: dim(parts[2]),
|
|
366
|
+
spread: dim(parts[3]),
|
|
367
|
+
color: hexToDtcgColor(shadowColor)
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
});
|
|
371
|
+
return Object.keys(shadowTokens).length > 0 ? shadowTokens : null;
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Main export function - converts dembrandt output to W3C Design Tokens format
|
|
375
|
+
*/
|
|
376
|
+
export function toDtcgTokens(extractionResult) {
|
|
377
|
+
const w3cTokens = {};
|
|
378
|
+
// Document-level provenance + version contract. The DTCG $extensions mechanism
|
|
379
|
+
// is the only sanctioned channel for vendor data, and the spec requires other
|
|
380
|
+
// tools to preserve it across a round-trip. Single source of truth: version.js.
|
|
381
|
+
w3cTokens.$extensions = {
|
|
382
|
+
[EXTENSION_KEY]: buildDembrandtProvenance(extractionResult),
|
|
383
|
+
};
|
|
384
|
+
// Export colors
|
|
385
|
+
const colors = exportColors(extractionResult.colors);
|
|
386
|
+
if (colors) {
|
|
387
|
+
w3cTokens.color = colors;
|
|
388
|
+
}
|
|
389
|
+
// Export typography
|
|
390
|
+
const typography = exportTypography(extractionResult.typography);
|
|
391
|
+
if (typography) {
|
|
392
|
+
w3cTokens.typography = typography;
|
|
393
|
+
}
|
|
394
|
+
// Export spacing
|
|
395
|
+
const spacing = exportSpacing(extractionResult.spacing);
|
|
396
|
+
if (spacing) {
|
|
397
|
+
w3cTokens.spacing = spacing;
|
|
398
|
+
}
|
|
399
|
+
// Export border radius
|
|
400
|
+
const borderRadius = exportBorderRadius(extractionResult.borderRadius);
|
|
401
|
+
if (borderRadius) {
|
|
402
|
+
w3cTokens.radius = borderRadius;
|
|
403
|
+
}
|
|
404
|
+
// Export borders
|
|
405
|
+
const borders = exportBorders(extractionResult.borders);
|
|
406
|
+
if (borders) {
|
|
407
|
+
w3cTokens.border = borders;
|
|
408
|
+
}
|
|
409
|
+
// Export shadows
|
|
410
|
+
const shadows = exportShadows(extractionResult.shadows);
|
|
411
|
+
if (shadows) {
|
|
412
|
+
w3cTokens.shadow = shadows;
|
|
413
|
+
}
|
|
414
|
+
return w3cTokens;
|
|
415
|
+
}
|
|
416
|
+
//# sourceMappingURL=dtcg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dtcg.js","sourceRoot":"","sources":["../../../lib/formatters/dtcg.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML report formatter — a single self-contained HTML file (inline CSS, no
|
|
3
|
+
* external fetches) that renders an extraction and, optionally, a drift diff.
|
|
4
|
+
*
|
|
5
|
+
* This is the pre-platform bridge (DEM-94): a CI artifact you can open offline
|
|
6
|
+
* and attach to a PR, before the hosted dashboard exists. It is a *view* over
|
|
7
|
+
* the same deterministic data the platform will later diff server-side — it
|
|
8
|
+
* renders structured tokens and single-render contrast only, never re-derives
|
|
9
|
+
* drift from rendered CSS. Mode B reuses the canonical `computeDrift` engine.
|
|
10
|
+
*
|
|
11
|
+
* The summary header is Lighthouse-style: big score gauges, each backed by a
|
|
12
|
+
* concrete finding (lib/findings.ts) so no number is a vanity metric. The file
|
|
13
|
+
* is the rendered view only — the machine-readable form is `--json-only`.
|
|
14
|
+
*/
|
|
15
|
+
import type { BrandingResult } from "../types.js";
|
|
16
|
+
import type { DriftReport } from "../drift.js";
|
|
17
|
+
export interface HtmlReportOptions {
|
|
18
|
+
/** When present, render a drift banner + changes at the top of the report. */
|
|
19
|
+
drift?: DriftReport;
|
|
20
|
+
/** Label for the baseline the drift was computed against (e.g. a filename). */
|
|
21
|
+
baselineLabel?: string;
|
|
22
|
+
/** CLI version, surfaced in the footer. */
|
|
23
|
+
version?: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function generateHtmlReport(result: BrandingResult, options?: HtmlReportOptions): string;
|