@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,954 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal Display Formatter
|
|
3
|
+
*
|
|
4
|
+
* Formats extracted brand data into clean, readable terminal output
|
|
5
|
+
* with color swatches and minimal design.
|
|
6
|
+
*/
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
import { color } from './theme.js';
|
|
9
|
+
import { convertColor } from '../colors.js';
|
|
10
|
+
/**
|
|
11
|
+
* Creates a clickable terminal link using ANSI escape codes
|
|
12
|
+
* Supported in iTerm2, VSCode terminal, GNOME Terminal, Windows Terminal
|
|
13
|
+
* @param {string} url - The URL to link to
|
|
14
|
+
* @param {string} text - The text to display (defaults to url)
|
|
15
|
+
* @returns {string} ANSI-formatted clickable link
|
|
16
|
+
*/
|
|
17
|
+
function terminalLink(url, text = url) {
|
|
18
|
+
// OSC 8 hyperlink format: \x1b]8;;URL\x1b\\TEXT\x1b]8;;\x1b\\
|
|
19
|
+
return `\x1b]8;;${url}\x1b\\${text}\x1b]8;;\x1b\\`;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Main display function - outputs formatted extraction results to terminal
|
|
23
|
+
* @param {Object} data - Extraction results from extractBranding()
|
|
24
|
+
*/
|
|
25
|
+
export function displayResults(data) {
|
|
26
|
+
console.log('\n' + chalk.bold.cyan('🎨 Brand Extraction'));
|
|
27
|
+
console.log(chalk.dim('│'));
|
|
28
|
+
console.log(chalk.dim('├─') + ' ' + chalk.blue(terminalLink(data.url)));
|
|
29
|
+
const timeString = new Date(data.extractedAt).toLocaleTimeString('en-US', {
|
|
30
|
+
minute: '2-digit',
|
|
31
|
+
second: '2-digit'
|
|
32
|
+
});
|
|
33
|
+
console.log(chalk.dim('├─') + ' ' + chalk.dim(timeString));
|
|
34
|
+
if (data.pages && data.pages.length > 1) {
|
|
35
|
+
const paths = data.pages.map(p => new URL(p.url).pathname || '/').join(', ');
|
|
36
|
+
console.log(chalk.dim('├─') + ' ' + chalk.dim(`${data.pages.length} pages: ${paths}`));
|
|
37
|
+
}
|
|
38
|
+
console.log(chalk.dim('│'));
|
|
39
|
+
displayLogo(data.logo);
|
|
40
|
+
displayFavicons(data.favicons);
|
|
41
|
+
displayColors(data.colors);
|
|
42
|
+
displayTypography(data.typography);
|
|
43
|
+
displaySpacing(data.spacing);
|
|
44
|
+
displayBorderRadius(data.borderRadius);
|
|
45
|
+
displayBorders(data.borders);
|
|
46
|
+
displayShadows(data.shadows);
|
|
47
|
+
displayGradients(data.gradients);
|
|
48
|
+
displayButtons(data.components?.buttons);
|
|
49
|
+
displayBadges(data.components?.badges);
|
|
50
|
+
displayInputs(data.components?.inputs);
|
|
51
|
+
displayLinks(data.components?.links);
|
|
52
|
+
displayBreakpoints(data.breakpoints);
|
|
53
|
+
displayIconSystem(data.iconSystem);
|
|
54
|
+
displayFrameworks(data.frameworks);
|
|
55
|
+
displayMotion(data.motion);
|
|
56
|
+
displayWcag(data.wcag);
|
|
57
|
+
console.log(chalk.dim('│'));
|
|
58
|
+
console.log(chalk.dim('└─') + ' ' + color.success('✓ Complete'));
|
|
59
|
+
console.log('');
|
|
60
|
+
}
|
|
61
|
+
function displayLogo(logo) {
|
|
62
|
+
if (!logo)
|
|
63
|
+
return;
|
|
64
|
+
console.log(chalk.dim('├─') + ' ' + chalk.bold('Logo'));
|
|
65
|
+
if (logo.inline) {
|
|
66
|
+
const colorInfo = logo.color ? ` · ${logo.color}` : '';
|
|
67
|
+
console.log(chalk.dim('│ ├─') + ' ' + chalk.dim(`inline ${logo.source || 'svg'}${colorInfo}`));
|
|
68
|
+
if (logo.url && logo.url !== '/') {
|
|
69
|
+
console.log(chalk.dim('│ ├─') + ' ' + chalk.blue(terminalLink(logo.url)));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
else if (logo.url) {
|
|
73
|
+
console.log(chalk.dim('│ ├─') + ' ' + chalk.blue(terminalLink(logo.url)));
|
|
74
|
+
}
|
|
75
|
+
if (logo.width && logo.height) {
|
|
76
|
+
console.log(chalk.dim('│ ├─') + ' ' + chalk.dim(`${logo.width}×${logo.height}px`));
|
|
77
|
+
}
|
|
78
|
+
if (logo.safeZone) {
|
|
79
|
+
const { top, right, bottom, left } = logo.safeZone;
|
|
80
|
+
if (top > 0 || right > 0 || bottom > 0 || left > 0) {
|
|
81
|
+
console.log(chalk.dim('│ └─') + ' ' + chalk.dim(`Safe zone: ${top}px ${right}px ${bottom}px ${left}px`));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
console.log(chalk.dim('│'));
|
|
85
|
+
}
|
|
86
|
+
function displayFavicons(favicons) {
|
|
87
|
+
if (!favicons || favicons.length === 0)
|
|
88
|
+
return;
|
|
89
|
+
console.log(chalk.dim('├─') + ' ' + chalk.bold('Favicons'));
|
|
90
|
+
favicons.forEach((favicon, index) => {
|
|
91
|
+
const isLast = index === favicons.length - 1;
|
|
92
|
+
const branch = isLast ? '└─' : '├─';
|
|
93
|
+
const sizeInfo = favicon.sizes ? chalk.dim(` · ${favicon.sizes}`) : '';
|
|
94
|
+
console.log(chalk.dim(`│ ${branch}`) + ' ' + `${color.info(favicon.type.padEnd(18))} ${terminalLink(favicon.url)}${sizeInfo}`);
|
|
95
|
+
});
|
|
96
|
+
console.log(chalk.dim('│'));
|
|
97
|
+
}
|
|
98
|
+
function normalizeColorFormat(colorString) {
|
|
99
|
+
// Use the centralized color conversion utility
|
|
100
|
+
const converted = convertColor(colorString);
|
|
101
|
+
if (converted) {
|
|
102
|
+
return converted;
|
|
103
|
+
}
|
|
104
|
+
// Fallback for unparseable colors
|
|
105
|
+
return {
|
|
106
|
+
hex: colorString,
|
|
107
|
+
rgb: colorString,
|
|
108
|
+
lch: colorString,
|
|
109
|
+
oklch: colorString,
|
|
110
|
+
hasAlpha: false
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function displayColors(colors) {
|
|
114
|
+
console.log(chalk.dim('├─') + ' ' + chalk.bold('Colors'));
|
|
115
|
+
// All colors in one list with consistent formatting
|
|
116
|
+
const allColors = [];
|
|
117
|
+
// Add semantic colors
|
|
118
|
+
if (colors.semantic) {
|
|
119
|
+
Object.entries(colors.semantic)
|
|
120
|
+
.filter(([_, color]) => color && color !== 'rgba(0, 0, 0, 0)' && color !== 'transparent')
|
|
121
|
+
.forEach(([role, color]) => {
|
|
122
|
+
const formats = normalizeColorFormat(color);
|
|
123
|
+
allColors.push({
|
|
124
|
+
hex: formats.hex,
|
|
125
|
+
rgb: formats.rgb,
|
|
126
|
+
lch: formats.lch,
|
|
127
|
+
oklch: formats.oklch,
|
|
128
|
+
hasAlpha: formats.hasAlpha,
|
|
129
|
+
label: role,
|
|
130
|
+
type: 'semantic',
|
|
131
|
+
confidence: 'high'
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
// Add CSS variables
|
|
136
|
+
if (colors.cssVariables) {
|
|
137
|
+
const limit = 15;
|
|
138
|
+
Object.entries(colors.cssVariables).slice(0, limit).forEach(([name, varData]) => {
|
|
139
|
+
try {
|
|
140
|
+
// Handle both old format (string) and new format (object with value, lch, oklch)
|
|
141
|
+
const colorValue = typeof varData === 'string' ? varData : varData.value;
|
|
142
|
+
const formats = normalizeColorFormat(colorValue);
|
|
143
|
+
// Use pre-computed LCH/OKLCH from extractor if available
|
|
144
|
+
allColors.push({
|
|
145
|
+
hex: formats.hex,
|
|
146
|
+
rgb: formats.rgb,
|
|
147
|
+
lch: (typeof varData === 'object' && varData.lch) || formats.lch,
|
|
148
|
+
oklch: (typeof varData === 'object' && varData.oklch) || formats.oklch,
|
|
149
|
+
hasAlpha: formats.hasAlpha,
|
|
150
|
+
label: name,
|
|
151
|
+
type: 'variable',
|
|
152
|
+
confidence: 'high'
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
// Skip invalid colors
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
// Add palette colors - show high and medium confidence
|
|
161
|
+
if (colors.palette) {
|
|
162
|
+
const limit = 20;
|
|
163
|
+
const filtered = colors.palette.filter(c => c.confidence === 'high' || c.confidence === 'medium');
|
|
164
|
+
filtered.slice(0, limit).forEach(c => {
|
|
165
|
+
const formats = normalizeColorFormat(c.color);
|
|
166
|
+
allColors.push({
|
|
167
|
+
hex: formats.hex,
|
|
168
|
+
rgb: formats.rgb,
|
|
169
|
+
lch: c.lch || formats.lch,
|
|
170
|
+
oklch: c.oklch || formats.oklch,
|
|
171
|
+
hasAlpha: formats.hasAlpha,
|
|
172
|
+
label: '',
|
|
173
|
+
type: 'palette',
|
|
174
|
+
confidence: c.confidence,
|
|
175
|
+
role: c.role,
|
|
176
|
+
onColor: c.onColor,
|
|
177
|
+
hover: c.hover,
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
// Deduplicate colors by hex value
|
|
182
|
+
const colorMap = new Map();
|
|
183
|
+
allColors.forEach(color => {
|
|
184
|
+
const key = color.hex.toLowerCase();
|
|
185
|
+
if (colorMap.has(key)) {
|
|
186
|
+
const existing = colorMap.get(key);
|
|
187
|
+
// Merge labels
|
|
188
|
+
if (color.label && !existing.label) {
|
|
189
|
+
existing.label = color.label;
|
|
190
|
+
}
|
|
191
|
+
else if (color.label && existing.label) {
|
|
192
|
+
// Split existing labels and check for exact match
|
|
193
|
+
const existingLabels = existing.label.split(', ');
|
|
194
|
+
if (!existingLabels.includes(color.label)) {
|
|
195
|
+
existing.label = `${existing.label}, ${color.label}`;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// Keep highest confidence
|
|
199
|
+
const confidenceOrder = { high: 3, medium: 2, low: 1 };
|
|
200
|
+
if (confidenceOrder[color.confidence] > confidenceOrder[existing.confidence]) {
|
|
201
|
+
existing.confidence = color.confidence;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
colorMap.set(key, { ...color });
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
const uniqueColors = Array.from(colorMap.values());
|
|
209
|
+
// Display each color on a single line: swatch, hex, role, rgb, oklch.
|
|
210
|
+
// lch is omitted here for compactness but remains in JSON output.
|
|
211
|
+
uniqueColors.forEach(({ hex, rgb, label, confidence, role, onColor, hover }, index) => {
|
|
212
|
+
const isLast = index === uniqueColors.length - 1;
|
|
213
|
+
const branch = isLast ? '└─' : '├─';
|
|
214
|
+
let conf;
|
|
215
|
+
if (confidence === 'high')
|
|
216
|
+
conf = color.success('●');
|
|
217
|
+
else if (confidence === 'medium')
|
|
218
|
+
conf = color.warning('●');
|
|
219
|
+
else
|
|
220
|
+
conf = chalk.gray('●');
|
|
221
|
+
let swatch;
|
|
222
|
+
try {
|
|
223
|
+
swatch = chalk.bgHex(hex)(' ');
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
swatch = ' ';
|
|
227
|
+
}
|
|
228
|
+
let onSwatch = '';
|
|
229
|
+
if (onColor && role === 'accent') {
|
|
230
|
+
try {
|
|
231
|
+
onSwatch = ' on:' + chalk.bgHex(hex)(chalk.hex(onColor)(' Aa '));
|
|
232
|
+
}
|
|
233
|
+
catch { }
|
|
234
|
+
}
|
|
235
|
+
const rawLabel = label || (role && role !== 'palette' ? role : '');
|
|
236
|
+
const truncated = rawLabel.length > 14 ? rawLabel.slice(0, 13) + '…' : rawLabel;
|
|
237
|
+
const labelText = chalk.dim(truncated.padEnd(15));
|
|
238
|
+
const rgbText = chalk.dim((rgb || '').padEnd(20));
|
|
239
|
+
const hoverText = (hover && role === 'accent') ? chalk.dim(` hover:${hover}`) : '';
|
|
240
|
+
console.log(chalk.dim(`│ ${branch}`) + ' ' +
|
|
241
|
+
`${conf} ${swatch} ${hex} ` +
|
|
242
|
+
labelText + ' ' +
|
|
243
|
+
rgbText +
|
|
244
|
+
onSwatch +
|
|
245
|
+
hoverText);
|
|
246
|
+
});
|
|
247
|
+
const cssVarLimit = 15;
|
|
248
|
+
const paletteLimit = 20;
|
|
249
|
+
const remaining = (colors.cssVariables ? Math.max(0, Object.keys(colors.cssVariables).length - cssVarLimit) : 0) +
|
|
250
|
+
(colors.palette ? Math.max(0, colors.palette.length - paletteLimit) : 0);
|
|
251
|
+
if (remaining > 0) {
|
|
252
|
+
console.log(chalk.dim(`│ └─`) + ' ' + chalk.dim(`+${remaining} more in JSON`));
|
|
253
|
+
}
|
|
254
|
+
console.log(chalk.dim('│'));
|
|
255
|
+
}
|
|
256
|
+
function displayTypography(typography) {
|
|
257
|
+
console.log(chalk.dim('├─') + ' ' + chalk.bold('Typography'));
|
|
258
|
+
// Font sources with font-display
|
|
259
|
+
const sources = [];
|
|
260
|
+
if (typography.sources?.googleFonts?.length > 0) {
|
|
261
|
+
sources.push(...typography.sources.googleFonts);
|
|
262
|
+
}
|
|
263
|
+
if (sources.length > 0) {
|
|
264
|
+
const fontDisplayInfo = typography.sources?.fontDisplay ? ` · font-display: ${typography.sources.fontDisplay}` : '';
|
|
265
|
+
console.log(chalk.dim('│ ├─') + ' ' + chalk.dim(`Fonts: ${sources.slice(0, 3).join(', ')}${fontDisplayInfo}`));
|
|
266
|
+
if (sources.length > 3) {
|
|
267
|
+
console.log(chalk.dim('│ ├─') + ' ' + chalk.dim(`+${sources.length - 3} more`));
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// Group styles by font family: collect unique sizes (largest first) and weights
|
|
271
|
+
if (typography.styles?.length > 0) {
|
|
272
|
+
const fontFamilies = new Map();
|
|
273
|
+
typography.styles.forEach(style => {
|
|
274
|
+
if (!style.family)
|
|
275
|
+
return;
|
|
276
|
+
if (!fontFamilies.has(style.family)) {
|
|
277
|
+
fontFamilies.set(style.family, { sizeContexts: new Map(), weights: new Set() });
|
|
278
|
+
}
|
|
279
|
+
const familyData = fontFamilies.get(style.family);
|
|
280
|
+
if (style.size) {
|
|
281
|
+
const px = Math.round(parseFloat(style.size) || 0);
|
|
282
|
+
if (px && !familyData.sizeContexts.has(px)) {
|
|
283
|
+
familyData.sizeContexts.set(px, style.context || null);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
if (style.weight && style.weight !== 400) {
|
|
287
|
+
familyData.weights.add(style.weight);
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
let fontIndex = 0;
|
|
291
|
+
const totalFonts = fontFamilies.size;
|
|
292
|
+
for (const [family, data] of fontFamilies) {
|
|
293
|
+
fontIndex++;
|
|
294
|
+
const isFontLast = fontIndex === totalFonts;
|
|
295
|
+
const fontBranch = isFontLast ? '└─' : '├─';
|
|
296
|
+
const sizeTokens = [...data.sizeContexts.entries()]
|
|
297
|
+
.sort((a, b) => b[0] - a[0])
|
|
298
|
+
.map(([px, ctx]) => ctx ? `${px}px ${chalk.dim(`(${ctx})`)}` : `${px}px`);
|
|
299
|
+
const sizeList = sizeTokens.length
|
|
300
|
+
? ' ' + chalk.dim('[ ') + sizeTokens.join(', ') + chalk.dim(' ]')
|
|
301
|
+
: '';
|
|
302
|
+
console.log(chalk.dim(`│ ${fontBranch}`) + ' ' + chalk.bold(family) + sizeList);
|
|
303
|
+
const weights = [...data.weights].sort((a, b) => a - b);
|
|
304
|
+
if (weights.length) {
|
|
305
|
+
const indent = isFontLast ? ' ' : '│ ';
|
|
306
|
+
console.log(chalk.dim(`│ ${indent}└─`) + ' ' + chalk.dim('Weights: ') + weights.join(', '));
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
console.log(chalk.dim('│'));
|
|
311
|
+
}
|
|
312
|
+
function displaySpacing(spacing) {
|
|
313
|
+
console.log(chalk.dim('├─') + ' ' + chalk.bold('Spacing'));
|
|
314
|
+
console.log(chalk.dim('│ ├─') + ' ' + chalk.dim(`System: ${spacing.scaleType}`));
|
|
315
|
+
spacing.commonValues.slice(0, 15).forEach((v, index) => {
|
|
316
|
+
const isLast = index === Math.min(spacing.commonValues.length, 15) - 1;
|
|
317
|
+
const branch = isLast ? '└─' : '├─';
|
|
318
|
+
console.log(chalk.dim(`│ ${branch}`) + ' ' + `${v.px.padEnd(8)} ${chalk.dim(v.rem)}`);
|
|
319
|
+
});
|
|
320
|
+
console.log(chalk.dim('│'));
|
|
321
|
+
}
|
|
322
|
+
function displayBorderRadius(borderRadius) {
|
|
323
|
+
if (!borderRadius || borderRadius.values.length === 0)
|
|
324
|
+
return;
|
|
325
|
+
const highConfRadius = borderRadius.values.filter(r => r.confidence === 'high' || r.confidence === 'medium');
|
|
326
|
+
if (highConfRadius.length === 0)
|
|
327
|
+
return;
|
|
328
|
+
console.log(chalk.dim('├─') + ' ' + chalk.bold('Border Radius'));
|
|
329
|
+
highConfRadius.slice(0, 12).forEach((r, index) => {
|
|
330
|
+
const isLast = index === highConfRadius.slice(0, 12).length - 1;
|
|
331
|
+
const branch = isLast ? '└─' : '├─';
|
|
332
|
+
const elements = r.elements && r.elements.length > 0
|
|
333
|
+
? chalk.dim(` (${r.elements.join(', ')})`)
|
|
334
|
+
: '';
|
|
335
|
+
console.log(chalk.dim(`│ ${branch}`) + ' ' + `${r.value}${elements}`);
|
|
336
|
+
});
|
|
337
|
+
console.log(chalk.dim('│'));
|
|
338
|
+
}
|
|
339
|
+
function displayBorders(borders) {
|
|
340
|
+
if (!borders)
|
|
341
|
+
return;
|
|
342
|
+
const hasCombinations = borders.combinations && borders.combinations.length > 0;
|
|
343
|
+
if (!hasCombinations)
|
|
344
|
+
return;
|
|
345
|
+
const highConfCombos = borders.combinations.filter(c => c.confidence === 'high' || c.confidence === 'medium');
|
|
346
|
+
if (highConfCombos.length === 0)
|
|
347
|
+
return;
|
|
348
|
+
console.log(chalk.dim('├─') + ' ' + chalk.bold('Borders'));
|
|
349
|
+
highConfCombos.slice(0, 10).forEach((combo, index) => {
|
|
350
|
+
const isLast = index === Math.min(highConfCombos.length, 10) - 1;
|
|
351
|
+
const branch = isLast ? '└─' : '├─';
|
|
352
|
+
const conf = combo.confidence === 'high' ? color.success('●') : color.warning('●');
|
|
353
|
+
try {
|
|
354
|
+
const formats = normalizeColorFormat(combo.color);
|
|
355
|
+
const colorBlock = chalk.bgHex(formats.hex)(' ');
|
|
356
|
+
const elementsText = combo.elements && combo.elements.length > 0
|
|
357
|
+
? chalk.dim(` (${combo.elements.join(', ')})`)
|
|
358
|
+
: '';
|
|
359
|
+
console.log(chalk.dim(`│ ${branch}`) + ' ' +
|
|
360
|
+
`${conf} ${colorBlock} ${combo.width} ${combo.style} ${formats.hex.padEnd(9)} ${formats.rgb}` +
|
|
361
|
+
elementsText);
|
|
362
|
+
}
|
|
363
|
+
catch {
|
|
364
|
+
const elementsText = combo.elements && combo.elements.length > 0
|
|
365
|
+
? chalk.dim(` (${combo.elements.join(', ')})`)
|
|
366
|
+
: '';
|
|
367
|
+
console.log(chalk.dim(`│ ${branch}`) + ' ' +
|
|
368
|
+
`${conf} ${combo.width} ${combo.style} ${combo.color}` +
|
|
369
|
+
elementsText);
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
if (highConfCombos.length > 10) {
|
|
373
|
+
console.log(chalk.dim('│ └─') + ' ' + chalk.dim(`+${highConfCombos.length - 10} more`));
|
|
374
|
+
}
|
|
375
|
+
console.log(chalk.dim('│'));
|
|
376
|
+
}
|
|
377
|
+
function displayShadows(shadows) {
|
|
378
|
+
if (!shadows || shadows.length === 0)
|
|
379
|
+
return;
|
|
380
|
+
const highConfShadows = shadows.filter(s => s.confidence === 'high' || s.confidence === 'medium');
|
|
381
|
+
if (highConfShadows.length === 0)
|
|
382
|
+
return;
|
|
383
|
+
// Sort by confidence first (high > medium), then by count
|
|
384
|
+
const sorted = highConfShadows.sort((a, b) => {
|
|
385
|
+
const confOrder = { 'high': 2, 'medium': 1 };
|
|
386
|
+
const confDiff = (confOrder[b.confidence] || 0) - (confOrder[a.confidence] || 0);
|
|
387
|
+
if (confDiff !== 0)
|
|
388
|
+
return confDiff;
|
|
389
|
+
return (b.count || 0) - (a.count || 0); // Higher count first
|
|
390
|
+
});
|
|
391
|
+
console.log(chalk.dim('├─') + ' ' + chalk.bold('Shadows'));
|
|
392
|
+
sorted.slice(0, 8).forEach((s, index) => {
|
|
393
|
+
const isLast = index === Math.min(sorted.length, 8) - 1 && sorted.length <= 8;
|
|
394
|
+
const branch = isLast ? '└─' : '├─';
|
|
395
|
+
const conf = s.confidence === 'high' ? color.success('●') : color.warning('●');
|
|
396
|
+
console.log(chalk.dim(`│ ${branch}`) + ' ' + `${conf} ${s.shadow}`);
|
|
397
|
+
});
|
|
398
|
+
if (highConfShadows.length > 8) {
|
|
399
|
+
console.log(chalk.dim('│ └─') + ' ' + chalk.dim(`+${highConfShadows.length - 8} more`));
|
|
400
|
+
}
|
|
401
|
+
console.log(chalk.dim('│'));
|
|
402
|
+
}
|
|
403
|
+
function displayGradients(gradients) {
|
|
404
|
+
if (!gradients || gradients.length === 0)
|
|
405
|
+
return;
|
|
406
|
+
console.log(chalk.dim('├─') + ' ' + chalk.bold('Gradients'));
|
|
407
|
+
gradients.slice(0, 5).forEach((g, i) => {
|
|
408
|
+
const isLast = i === Math.min(gradients.length, 5) - 1;
|
|
409
|
+
const branch = isLast ? '└─' : '├─';
|
|
410
|
+
const typeLabel = g.type ? chalk.dim(`${g.type} · `) : '';
|
|
411
|
+
const uniqueStops = [...new Set((g.stopColors || []).map(c => {
|
|
412
|
+
const m = c && c.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);
|
|
413
|
+
return m ? `#${parseInt(m[1]).toString(16).padStart(2, '0')}${parseInt(m[2]).toString(16).padStart(2, '0')}${parseInt(m[3]).toString(16).padStart(2, '0')}` : null;
|
|
414
|
+
}).filter(Boolean))];
|
|
415
|
+
const stops = uniqueStops.slice(0, 5).map((hex) => chalk.bgHex(hex)(' ')).join(' ');
|
|
416
|
+
const countLabel = g.count > 1 ? chalk.dim(` ×${g.count}`) : '';
|
|
417
|
+
console.log(chalk.dim(`│ ${branch}`) + ' ' + typeLabel + (stops || chalk.dim(g.gradient.slice(0, 50) + '…')) + countLabel);
|
|
418
|
+
});
|
|
419
|
+
console.log(chalk.dim('│'));
|
|
420
|
+
}
|
|
421
|
+
function displayButtons(buttons) {
|
|
422
|
+
if (!buttons || buttons.length === 0)
|
|
423
|
+
return;
|
|
424
|
+
const highConfButtons = buttons.filter(b => b.confidence === 'high');
|
|
425
|
+
if (highConfButtons.length === 0)
|
|
426
|
+
return;
|
|
427
|
+
console.log(chalk.dim('├─') + ' ' + chalk.bold('Buttons'));
|
|
428
|
+
highConfButtons.slice(0, 6).forEach((btn, btnIndex) => {
|
|
429
|
+
const isLastBtn = btnIndex === Math.min(highConfButtons.length, 6) - 1 && highConfButtons.length <= 6;
|
|
430
|
+
const btnBranch = isLastBtn ? '└─' : '├─';
|
|
431
|
+
const btnIndent = isLastBtn ? ' ' : '│ ';
|
|
432
|
+
// Show button variant header
|
|
433
|
+
try {
|
|
434
|
+
const defaultBg = btn.states.default.backgroundColor;
|
|
435
|
+
const isTransparent = defaultBg.includes('rgba(0, 0, 0, 0)') || defaultBg === 'transparent';
|
|
436
|
+
if (isTransparent) {
|
|
437
|
+
console.log(chalk.dim(`│ ${btnBranch}`) + ' ' + chalk.bold('Variant: transparent'));
|
|
438
|
+
}
|
|
439
|
+
else {
|
|
440
|
+
const formats = normalizeColorFormat(defaultBg);
|
|
441
|
+
const colorBlock = chalk.bgHex(formats.hex)(' ');
|
|
442
|
+
console.log(chalk.dim(`│ ${btnBranch}`) + ' ' + chalk.bold(`Variant: ${colorBlock} ${formats.hex.padEnd(9)} ${formats.rgb}`));
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
catch {
|
|
446
|
+
console.log(chalk.dim(`│ ${btnBranch}`) + ' ' + chalk.bold(`Variant: ${btn.states.default.backgroundColor}`));
|
|
447
|
+
}
|
|
448
|
+
// Display states
|
|
449
|
+
const stateOrder = [
|
|
450
|
+
{ key: 'default', label: 'Default' },
|
|
451
|
+
{ key: 'hover', label: 'Hover' },
|
|
452
|
+
{ key: 'active', label: 'Active' },
|
|
453
|
+
{ key: 'focus', label: 'Focus' },
|
|
454
|
+
];
|
|
455
|
+
const availableStates = stateOrder.filter(s => btn.states[s.key]);
|
|
456
|
+
availableStates.forEach((stateInfo, stateIndex) => {
|
|
457
|
+
const state = btn.states[stateInfo.key];
|
|
458
|
+
const isLastState = stateIndex === availableStates.length - 1;
|
|
459
|
+
const stateBranch = isLastState ? '└─' : '├─';
|
|
460
|
+
const stateIndent = isLastState ? ' ' : '│ ';
|
|
461
|
+
console.log(chalk.dim(`│ ${btnIndent}${stateBranch}`) + ' ' + color.info(stateInfo.label));
|
|
462
|
+
const props = [];
|
|
463
|
+
// Only show properties that exist and are meaningful
|
|
464
|
+
if (state.backgroundColor && state.backgroundColor !== 'rgba(0, 0, 0, 0)' && state.backgroundColor !== 'transparent') {
|
|
465
|
+
try {
|
|
466
|
+
const formats = normalizeColorFormat(state.backgroundColor);
|
|
467
|
+
const colorBlock = chalk.bgHex(formats.hex)(' ');
|
|
468
|
+
props.push({ key: 'bg', value: `${colorBlock} ${formats.hex.padEnd(9)} ${formats.rgb}` });
|
|
469
|
+
}
|
|
470
|
+
catch {
|
|
471
|
+
props.push({ key: 'bg', value: state.backgroundColor });
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
if (state.color) {
|
|
475
|
+
try {
|
|
476
|
+
const formats = normalizeColorFormat(state.color);
|
|
477
|
+
const colorBlock = chalk.bgHex(formats.hex)(' ');
|
|
478
|
+
props.push({ key: 'text', value: `${colorBlock} ${formats.hex.padEnd(9)} ${formats.rgb}` });
|
|
479
|
+
}
|
|
480
|
+
catch {
|
|
481
|
+
props.push({ key: 'text', value: state.color });
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
if (stateInfo.key === 'default') {
|
|
485
|
+
if (state.padding && state.padding !== '0px') {
|
|
486
|
+
props.push({ key: 'padding', value: state.padding });
|
|
487
|
+
}
|
|
488
|
+
if (state.borderRadius && state.borderRadius !== '0px') {
|
|
489
|
+
props.push({ key: 'radius', value: state.borderRadius });
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
if (state.border && state.border !== 'none' && !state.border.includes('0px')) {
|
|
493
|
+
props.push({ key: 'border', value: state.border });
|
|
494
|
+
}
|
|
495
|
+
if (state.boxShadow && state.boxShadow !== 'none') {
|
|
496
|
+
const shortShadow = state.boxShadow.length > 40
|
|
497
|
+
? state.boxShadow.substring(0, 37) + '...'
|
|
498
|
+
: state.boxShadow;
|
|
499
|
+
props.push({ key: 'shadow', value: shortShadow });
|
|
500
|
+
}
|
|
501
|
+
if (state.outline && state.outline !== 'none') {
|
|
502
|
+
props.push({ key: 'outline', value: state.outline });
|
|
503
|
+
}
|
|
504
|
+
if (state.transform && state.transform !== 'none') {
|
|
505
|
+
props.push({ key: 'transform', value: state.transform });
|
|
506
|
+
}
|
|
507
|
+
if (state.opacity && state.opacity !== '1') {
|
|
508
|
+
props.push({ key: 'opacity', value: state.opacity });
|
|
509
|
+
}
|
|
510
|
+
// Display properties
|
|
511
|
+
props.forEach((prop, propIndex) => {
|
|
512
|
+
const isLastProp = propIndex === props.length - 1;
|
|
513
|
+
const propBranch = isLastProp ? '└─' : '├─';
|
|
514
|
+
console.log(chalk.dim(`│ ${btnIndent}${stateIndent}${propBranch}`) + ' ' +
|
|
515
|
+
chalk.dim(`${prop.key}: `) + `${prop.value}`);
|
|
516
|
+
});
|
|
517
|
+
});
|
|
518
|
+
});
|
|
519
|
+
if (highConfButtons.length > 6) {
|
|
520
|
+
console.log(chalk.dim('│ └─') + ' ' + chalk.dim(`+${highConfButtons.length - 6} more`));
|
|
521
|
+
}
|
|
522
|
+
console.log(chalk.dim('│'));
|
|
523
|
+
}
|
|
524
|
+
function displayBadges(badges) {
|
|
525
|
+
if (!badges || !badges.all || badges.all.length === 0)
|
|
526
|
+
return;
|
|
527
|
+
const highConfBadges = badges.all.filter(b => b.confidence === 'high');
|
|
528
|
+
if (highConfBadges.length === 0)
|
|
529
|
+
return;
|
|
530
|
+
console.log(chalk.dim('├─') + ' ' + chalk.bold('Badges / Tags / Pills'));
|
|
531
|
+
// Group by variant
|
|
532
|
+
const variants = ['error', 'warning', 'success', 'info', 'neutral'];
|
|
533
|
+
const variantLabels = {
|
|
534
|
+
error: 'Error',
|
|
535
|
+
warning: 'Warning',
|
|
536
|
+
success: 'Success',
|
|
537
|
+
info: 'Info',
|
|
538
|
+
neutral: 'Neutral'
|
|
539
|
+
};
|
|
540
|
+
let displayedCount = 0;
|
|
541
|
+
const maxDisplay = 8;
|
|
542
|
+
variants.forEach((variantKey, variantIndex) => {
|
|
543
|
+
if (displayedCount >= maxDisplay)
|
|
544
|
+
return;
|
|
545
|
+
const variantBadges = highConfBadges.filter(b => b.variant === variantKey);
|
|
546
|
+
if (variantBadges.length === 0)
|
|
547
|
+
return;
|
|
548
|
+
const isLastVariant = variantIndex === variants.length - 1 || displayedCount + variantBadges.length >= maxDisplay;
|
|
549
|
+
const variantBranch = isLastVariant && displayedCount + variantBadges.length >= maxDisplay ? '└─' : '├─';
|
|
550
|
+
const variantIndent = isLastVariant && displayedCount + variantBadges.length >= maxDisplay ? ' ' : '│ ';
|
|
551
|
+
console.log(chalk.dim(`│ ${variantBranch}`) + ' ' + chalk.bold(variantLabels[variantKey]));
|
|
552
|
+
const badgesToShow = variantBadges.slice(0, Math.min(2, maxDisplay - displayedCount));
|
|
553
|
+
badgesToShow.forEach((badge, badgeIndex) => {
|
|
554
|
+
if (displayedCount >= maxDisplay)
|
|
555
|
+
return;
|
|
556
|
+
const isLastBadge = badgeIndex === badgesToShow.length - 1;
|
|
557
|
+
const badgeBranch = isLastBadge ? '└─' : '├─';
|
|
558
|
+
const badgeIndent = isLastBadge ? ' ' : '│ ';
|
|
559
|
+
// Show badge type
|
|
560
|
+
const typeLabel = badge.isRounded ? 'Pill' : badge.styleType === 'outline' ? 'Outline' : badge.styleType === 'subtle' ? 'Subtle' : 'Filled';
|
|
561
|
+
console.log(chalk.dim(`│ ${variantIndent}${badgeBranch}`) + ' ' + color.info(typeLabel));
|
|
562
|
+
const props = [];
|
|
563
|
+
// Background color
|
|
564
|
+
if (badge.backgroundColor && badge.backgroundColor !== 'rgba(0, 0, 0, 0)' && badge.backgroundColor !== 'transparent') {
|
|
565
|
+
try {
|
|
566
|
+
const formats = normalizeColorFormat(badge.backgroundColor);
|
|
567
|
+
const colorBlock = chalk.bgHex(formats.hex)(' ');
|
|
568
|
+
props.push({ key: 'bg', value: `${colorBlock} ${formats.hex.padEnd(9)} ${formats.rgb}` });
|
|
569
|
+
}
|
|
570
|
+
catch {
|
|
571
|
+
props.push({ key: 'bg', value: badge.backgroundColor });
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
// Text color
|
|
575
|
+
if (badge.color) {
|
|
576
|
+
try {
|
|
577
|
+
const formats = normalizeColorFormat(badge.color);
|
|
578
|
+
const colorBlock = chalk.bgHex(formats.hex)(' ');
|
|
579
|
+
props.push({ key: 'text', value: `${colorBlock} ${formats.hex.padEnd(9)} ${formats.rgb}` });
|
|
580
|
+
}
|
|
581
|
+
catch {
|
|
582
|
+
props.push({ key: 'text', value: badge.color });
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
// Other properties
|
|
586
|
+
if (badge.padding && badge.padding !== '0px') {
|
|
587
|
+
props.push({ key: 'padding', value: badge.padding });
|
|
588
|
+
}
|
|
589
|
+
if (badge.borderRadius && badge.borderRadius !== '0px') {
|
|
590
|
+
props.push({ key: 'radius', value: badge.borderRadius });
|
|
591
|
+
}
|
|
592
|
+
if (badge.fontSize) {
|
|
593
|
+
props.push({ key: 'font-size', value: badge.fontSize });
|
|
594
|
+
}
|
|
595
|
+
if (badge.fontWeight && badge.fontWeight !== '400' && badge.fontWeight !== 'normal') {
|
|
596
|
+
props.push({ key: 'font-weight', value: badge.fontWeight });
|
|
597
|
+
}
|
|
598
|
+
if (badge.border && badge.border !== 'none' && !badge.border.includes('0px')) {
|
|
599
|
+
props.push({ key: 'border', value: badge.border });
|
|
600
|
+
}
|
|
601
|
+
// Display properties
|
|
602
|
+
props.forEach((prop, propIndex) => {
|
|
603
|
+
const isLastProp = propIndex === props.length - 1;
|
|
604
|
+
const propBranch = isLastProp ? '└─' : '├─';
|
|
605
|
+
console.log(chalk.dim(`│ ${variantIndent}${badgeIndent}${propBranch}`) + ' ' +
|
|
606
|
+
chalk.dim(`${prop.key}: `) + `${prop.value}`);
|
|
607
|
+
});
|
|
608
|
+
displayedCount++;
|
|
609
|
+
});
|
|
610
|
+
});
|
|
611
|
+
if (highConfBadges.length > maxDisplay) {
|
|
612
|
+
console.log(chalk.dim('│ └─') + ' ' + chalk.dim(`+${highConfBadges.length - maxDisplay} more`));
|
|
613
|
+
}
|
|
614
|
+
console.log(chalk.dim('│'));
|
|
615
|
+
}
|
|
616
|
+
function displayInputs(inputs) {
|
|
617
|
+
if (!inputs)
|
|
618
|
+
return;
|
|
619
|
+
const hasText = inputs.text && inputs.text.length > 0;
|
|
620
|
+
const hasCheckbox = inputs.checkbox && inputs.checkbox.length > 0;
|
|
621
|
+
const hasRadio = inputs.radio && inputs.radio.length > 0;
|
|
622
|
+
const hasSelect = inputs.select && inputs.select.length > 0;
|
|
623
|
+
if (!hasText && !hasCheckbox && !hasRadio && !hasSelect)
|
|
624
|
+
return;
|
|
625
|
+
console.log(chalk.dim('├─') + ' ' + chalk.bold('Inputs'));
|
|
626
|
+
const displayGroup = (groupName, items, isLastGroup) => {
|
|
627
|
+
if (!items || items.length === 0)
|
|
628
|
+
return;
|
|
629
|
+
const groupBranch = isLastGroup ? '└─' : '├─';
|
|
630
|
+
const groupIndent = isLastGroup ? ' ' : '│ ';
|
|
631
|
+
console.log(chalk.dim(`│ ${groupBranch}`) + ' ' + chalk.bold(groupName));
|
|
632
|
+
items.forEach((input, index) => {
|
|
633
|
+
const isLast = index === items.length - 1;
|
|
634
|
+
const branch = isLast ? '└─' : '├─';
|
|
635
|
+
const indent = isLast ? ' ' : '│ ';
|
|
636
|
+
console.log(chalk.dim(`│ ${groupIndent}${branch}`) + ' ' + color.info(input.specificType));
|
|
637
|
+
// Display default state
|
|
638
|
+
const defaultState = input.states.default;
|
|
639
|
+
console.log(chalk.dim(`│ ${groupIndent}${indent}├─`) + ' ' + color.info('Default'));
|
|
640
|
+
const defaultProps = [];
|
|
641
|
+
if (defaultState.backgroundColor && defaultState.backgroundColor !== 'rgba(0, 0, 0, 0)' && defaultState.backgroundColor !== 'transparent') {
|
|
642
|
+
try {
|
|
643
|
+
const formats = normalizeColorFormat(defaultState.backgroundColor);
|
|
644
|
+
const colorBlock = chalk.bgHex(formats.hex)(' ');
|
|
645
|
+
defaultProps.push({ key: 'bg', value: `${colorBlock} ${formats.hex.padEnd(9)} ${formats.rgb}` });
|
|
646
|
+
}
|
|
647
|
+
catch {
|
|
648
|
+
defaultProps.push({ key: 'bg', value: defaultState.backgroundColor });
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
if (defaultState.color) {
|
|
652
|
+
try {
|
|
653
|
+
const formats = normalizeColorFormat(defaultState.color);
|
|
654
|
+
const colorBlock = chalk.bgHex(formats.hex)(' ');
|
|
655
|
+
defaultProps.push({ key: 'text', value: `${colorBlock} ${formats.hex.padEnd(9)} ${formats.rgb}` });
|
|
656
|
+
}
|
|
657
|
+
catch {
|
|
658
|
+
defaultProps.push({ key: 'text', value: defaultState.color });
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
if (defaultState.border && defaultState.border !== 'none' && !defaultState.border.includes('0px')) {
|
|
662
|
+
defaultProps.push({ key: 'border', value: defaultState.border });
|
|
663
|
+
}
|
|
664
|
+
if (defaultState.padding && defaultState.padding !== '0px') {
|
|
665
|
+
defaultProps.push({ key: 'padding', value: defaultState.padding });
|
|
666
|
+
}
|
|
667
|
+
if (defaultState.borderRadius && defaultState.borderRadius !== '0px') {
|
|
668
|
+
defaultProps.push({ key: 'radius', value: defaultState.borderRadius });
|
|
669
|
+
}
|
|
670
|
+
defaultProps.forEach((prop, propIndex) => {
|
|
671
|
+
const isLastProp = propIndex === defaultProps.length - 1 && !input.states.focus;
|
|
672
|
+
const propBranch = isLastProp ? '└─' : '├─';
|
|
673
|
+
console.log(chalk.dim(`│ ${groupIndent}${indent}│ ${propBranch}`) + ' ' +
|
|
674
|
+
chalk.dim(`${prop.key}: `) + `${prop.value}`);
|
|
675
|
+
});
|
|
676
|
+
// Display focus state if available
|
|
677
|
+
if (input.states.focus) {
|
|
678
|
+
const focusState = input.states.focus;
|
|
679
|
+
console.log(chalk.dim(`│ ${groupIndent}${indent}└─`) + ' ' + color.info('Focus'));
|
|
680
|
+
const focusProps = [];
|
|
681
|
+
if (focusState.backgroundColor) {
|
|
682
|
+
try {
|
|
683
|
+
const formats = normalizeColorFormat(focusState.backgroundColor);
|
|
684
|
+
const colorBlock = chalk.bgHex(formats.hex)(' ');
|
|
685
|
+
focusProps.push({ key: 'bg', value: `${colorBlock} ${formats.hex.padEnd(9)} ${formats.rgb}` });
|
|
686
|
+
}
|
|
687
|
+
catch {
|
|
688
|
+
focusProps.push({ key: 'bg', value: focusState.backgroundColor });
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
if (focusState.border) {
|
|
692
|
+
focusProps.push({ key: 'border', value: focusState.border });
|
|
693
|
+
}
|
|
694
|
+
if (focusState.borderColor) {
|
|
695
|
+
try {
|
|
696
|
+
const formats = normalizeColorFormat(focusState.borderColor);
|
|
697
|
+
const colorBlock = chalk.bgHex(formats.hex)(' ');
|
|
698
|
+
focusProps.push({ key: 'border-color', value: `${colorBlock} ${formats.hex.padEnd(9)} ${formats.rgb}` });
|
|
699
|
+
}
|
|
700
|
+
catch {
|
|
701
|
+
focusProps.push({ key: 'border-color', value: focusState.borderColor });
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
if (focusState.boxShadow && focusState.boxShadow !== 'none') {
|
|
705
|
+
const shortShadow = focusState.boxShadow.length > 40
|
|
706
|
+
? focusState.boxShadow.substring(0, 37) + '...'
|
|
707
|
+
: focusState.boxShadow;
|
|
708
|
+
focusProps.push({ key: 'shadow', value: shortShadow });
|
|
709
|
+
}
|
|
710
|
+
if (focusState.outline && focusState.outline !== 'none') {
|
|
711
|
+
focusProps.push({ key: 'outline', value: focusState.outline });
|
|
712
|
+
}
|
|
713
|
+
focusProps.forEach((prop, propIndex) => {
|
|
714
|
+
const isLastProp = propIndex === focusProps.length - 1;
|
|
715
|
+
const propBranch = isLastProp ? '└─' : '├─';
|
|
716
|
+
console.log(chalk.dim(`│ ${groupIndent}${indent} ${propBranch}`) + ' ' +
|
|
717
|
+
chalk.dim(`${prop.key}: `) + `${prop.value}`);
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
});
|
|
721
|
+
};
|
|
722
|
+
let remaining = 0;
|
|
723
|
+
if (hasText)
|
|
724
|
+
remaining++;
|
|
725
|
+
if (hasCheckbox)
|
|
726
|
+
remaining++;
|
|
727
|
+
if (hasRadio)
|
|
728
|
+
remaining++;
|
|
729
|
+
if (hasSelect)
|
|
730
|
+
remaining++;
|
|
731
|
+
if (hasText) {
|
|
732
|
+
remaining--;
|
|
733
|
+
displayGroup('Text Inputs', inputs.text, remaining === 0);
|
|
734
|
+
}
|
|
735
|
+
if (hasCheckbox) {
|
|
736
|
+
remaining--;
|
|
737
|
+
displayGroup('Checkboxes', inputs.checkbox, remaining === 0);
|
|
738
|
+
}
|
|
739
|
+
if (hasRadio) {
|
|
740
|
+
remaining--;
|
|
741
|
+
displayGroup('Radio Buttons', inputs.radio, remaining === 0);
|
|
742
|
+
}
|
|
743
|
+
if (hasSelect) {
|
|
744
|
+
remaining--;
|
|
745
|
+
displayGroup('Select Dropdowns', inputs.select, remaining === 0);
|
|
746
|
+
}
|
|
747
|
+
console.log(chalk.dim('│'));
|
|
748
|
+
}
|
|
749
|
+
function displayBreakpoints(breakpoints) {
|
|
750
|
+
if (!breakpoints || breakpoints.length === 0)
|
|
751
|
+
return;
|
|
752
|
+
// Sort from larger to smaller, filtering out invalid entries
|
|
753
|
+
const sorted = [...breakpoints]
|
|
754
|
+
.filter(bp => bp.px && !isNaN(parseFloat(bp.px)))
|
|
755
|
+
.sort((a, b) => {
|
|
756
|
+
const aVal = parseFloat(a.px);
|
|
757
|
+
const bVal = parseFloat(b.px);
|
|
758
|
+
return bVal - aVal;
|
|
759
|
+
});
|
|
760
|
+
if (sorted.length === 0)
|
|
761
|
+
return;
|
|
762
|
+
console.log(chalk.dim('├─') + ' ' + chalk.bold('Breakpoints'));
|
|
763
|
+
console.log(chalk.dim('│ └─') + ' ' + `${sorted.map(bp => bp.px).join(' → ')}`);
|
|
764
|
+
console.log(chalk.dim('│'));
|
|
765
|
+
}
|
|
766
|
+
function displayLinks(links) {
|
|
767
|
+
if (!links || links.length === 0)
|
|
768
|
+
return;
|
|
769
|
+
console.log(chalk.dim('├─') + ' ' + chalk.bold('Links'));
|
|
770
|
+
links.slice(0, 6).forEach((link, linkIndex) => {
|
|
771
|
+
const isLastLink = linkIndex === Math.min(links.length, 6) - 1;
|
|
772
|
+
const linkBranch = isLastLink ? '└─' : '├─';
|
|
773
|
+
const linkIndent = isLastLink ? ' ' : '│ ';
|
|
774
|
+
// Show link variant header with color
|
|
775
|
+
try {
|
|
776
|
+
const formats = normalizeColorFormat(link.color);
|
|
777
|
+
const colorBlock = chalk.bgHex(formats.hex)(' ');
|
|
778
|
+
console.log(chalk.dim(`│ ${linkBranch}`) + ' ' + `${colorBlock} ${formats.hex.padEnd(9)} ${formats.rgb}`);
|
|
779
|
+
}
|
|
780
|
+
catch {
|
|
781
|
+
console.log(chalk.dim(`│ ${linkBranch}`) + ' ' + `${link.color}`);
|
|
782
|
+
}
|
|
783
|
+
// Display default state
|
|
784
|
+
if (link.states && link.states.default) {
|
|
785
|
+
const defaultState = link.states.default;
|
|
786
|
+
const hasHover = link.states.hover;
|
|
787
|
+
const hasDecoration = defaultState.textDecoration && defaultState.textDecoration !== 'none';
|
|
788
|
+
// Only show default state if there's decoration or hover state
|
|
789
|
+
if (hasDecoration || hasHover) {
|
|
790
|
+
console.log(chalk.dim(`│ ${linkIndent}├─`) + ' ' + color.info('Default'));
|
|
791
|
+
if (hasDecoration) {
|
|
792
|
+
const decorBranch = hasHover ? '├─' : '└─';
|
|
793
|
+
console.log(chalk.dim(`│ ${linkIndent}│ ${decorBranch}`) + ' ' + chalk.dim(`decoration: ${defaultState.textDecoration}`));
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
// Display hover state if available
|
|
797
|
+
if (hasHover) {
|
|
798
|
+
const hoverState = link.states.hover;
|
|
799
|
+
console.log(chalk.dim(`│ ${linkIndent}└─`) + ' ' + color.info('Hover'));
|
|
800
|
+
const hoverProps = [];
|
|
801
|
+
if (hoverState.color) {
|
|
802
|
+
try {
|
|
803
|
+
const formats = normalizeColorFormat(hoverState.color);
|
|
804
|
+
const colorBlock = chalk.bgHex(formats.hex)(' ');
|
|
805
|
+
hoverProps.push({ key: 'color', value: `${colorBlock} ${formats.hex.padEnd(9)} ${formats.rgb}` });
|
|
806
|
+
}
|
|
807
|
+
catch {
|
|
808
|
+
hoverProps.push({ key: 'color', value: hoverState.color });
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
if (hoverState.textDecoration) {
|
|
812
|
+
hoverProps.push({ key: 'decoration', value: hoverState.textDecoration });
|
|
813
|
+
}
|
|
814
|
+
hoverProps.forEach((prop, propIndex) => {
|
|
815
|
+
const isLastProp = propIndex === hoverProps.length - 1;
|
|
816
|
+
const propBranch = isLastProp ? '└─' : '├─';
|
|
817
|
+
console.log(chalk.dim(`│ ${linkIndent} ${propBranch}`) + ' ' +
|
|
818
|
+
chalk.dim(`${prop.key}: `) + `${prop.value}`);
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
else {
|
|
823
|
+
// Fallback for old format
|
|
824
|
+
if (link.textDecoration && link.textDecoration !== 'none') {
|
|
825
|
+
console.log(chalk.dim(`│ ${linkIndent}└─`) + ' ' + chalk.dim(`decoration: ${link.textDecoration}`));
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
});
|
|
829
|
+
if (links.length > 6) {
|
|
830
|
+
console.log(chalk.dim('│ └─') + ' ' + chalk.dim(`+${links.length - 6} more`));
|
|
831
|
+
}
|
|
832
|
+
console.log(chalk.dim('│'));
|
|
833
|
+
}
|
|
834
|
+
function displayIconSystem(iconSystem) {
|
|
835
|
+
if (!iconSystem || iconSystem.length === 0)
|
|
836
|
+
return;
|
|
837
|
+
console.log(chalk.dim('├─') + ' ' + chalk.bold('Icon System'));
|
|
838
|
+
iconSystem.forEach((system, index) => {
|
|
839
|
+
const isLast = index === iconSystem.length - 1;
|
|
840
|
+
const branch = isLast ? '└─' : '├─';
|
|
841
|
+
const sizes = system.sizes ? ` · ${system.sizes.join(', ')}` : '';
|
|
842
|
+
console.log(chalk.dim(`│ ${branch}`) + ' ' + `${system.name} ${chalk.dim(system.type)}${sizes}`);
|
|
843
|
+
});
|
|
844
|
+
console.log(chalk.dim('│'));
|
|
845
|
+
}
|
|
846
|
+
function displayFrameworks(frameworks) {
|
|
847
|
+
if (!frameworks || frameworks.length === 0)
|
|
848
|
+
return;
|
|
849
|
+
console.log(chalk.dim('├─') + ' ' + chalk.bold('Frameworks'));
|
|
850
|
+
frameworks.forEach((fw, index) => {
|
|
851
|
+
const isLast = index === frameworks.length - 1;
|
|
852
|
+
const branch = isLast ? '└─' : '├─';
|
|
853
|
+
const conf = fw.confidence === 'high' ? color.success('●') : color.warning('●');
|
|
854
|
+
console.log(chalk.dim(`│ ${branch}`) + ' ' + `${conf} ${fw.name} ${chalk.dim(fw.evidence)}`);
|
|
855
|
+
});
|
|
856
|
+
console.log(chalk.dim('│'));
|
|
857
|
+
}
|
|
858
|
+
function displayMotion(motion) {
|
|
859
|
+
if (!motion || (motion.durations.length === 0 && motion.animations.length === 0))
|
|
860
|
+
return;
|
|
861
|
+
console.log(chalk.dim('├─') + ' ' + chalk.bold('Motion'));
|
|
862
|
+
// Duration scale
|
|
863
|
+
if (motion.durations.length > 0) {
|
|
864
|
+
const vals = motion.durations.map(d => chalk.bold(d.value)).join(' ');
|
|
865
|
+
console.log(chalk.dim('│ ├─') + ' ' + chalk.dim('Scale ') + vals);
|
|
866
|
+
}
|
|
867
|
+
// Dominant easing
|
|
868
|
+
if (motion.easings.length > 0) {
|
|
869
|
+
const top = motion.easings[0];
|
|
870
|
+
const typeLabel = top.type && top.type !== 'custom' ? chalk.dim(` (${top.type})`) : '';
|
|
871
|
+
console.log(chalk.dim('│ ├─') + ' ' + chalk.dim('Easing ') + top.value + typeLabel);
|
|
872
|
+
}
|
|
873
|
+
// Per-context profiles
|
|
874
|
+
const ctxEntries = Object.entries(motion.contexts || {}).filter(([, v]) => v.count > 0);
|
|
875
|
+
if (ctxEntries.length > 0) {
|
|
876
|
+
console.log(chalk.dim('│ ├─') + ' ' + chalk.dim('By context'));
|
|
877
|
+
ctxEntries.forEach(([ctx, v], i) => {
|
|
878
|
+
const isLast = i === ctxEntries.length - 1 && (motion.interactiveDeltas || []).length === 0 && motion.animations.length === 0;
|
|
879
|
+
const branch = isLast ? '└─' : '├─';
|
|
880
|
+
const dur = v.durations.join(' / ');
|
|
881
|
+
const easingLabel = v.easingType && v.easingType !== 'custom' ? ` · ${v.easingType}` : '';
|
|
882
|
+
const props = v.props.length > 0 ? chalk.dim(` [${v.props.slice(0, 3).join(', ')}]`) : '';
|
|
883
|
+
console.log(chalk.dim(`│ │ ${branch}`) + ' ' + chalk.bold(ctx) + chalk.dim(` ${dur}${easingLabel}`) + props);
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
// Interaction deltas (hover patterns)
|
|
887
|
+
const deltas = (motion.interactiveDeltas || []);
|
|
888
|
+
if (deltas.length > 0) {
|
|
889
|
+
const seen = new Map();
|
|
890
|
+
deltas.forEach(d => {
|
|
891
|
+
const key = `${d.tag}:${d.pattern}`;
|
|
892
|
+
if (!seen.has(key))
|
|
893
|
+
seen.set(key, d);
|
|
894
|
+
});
|
|
895
|
+
const unique = Array.from(seen.values());
|
|
896
|
+
console.log(chalk.dim('│ ├─') + ' ' + chalk.dim('Hover patterns'));
|
|
897
|
+
unique.slice(0, 6).forEach((d, i) => {
|
|
898
|
+
const isLast = i === Math.min(unique.length, 6) - 1 && motion.animations.length === 0;
|
|
899
|
+
const branch = isLast ? '└─' : '├─';
|
|
900
|
+
const label = d.text ? chalk.dim(` "${d.text}"`) : '';
|
|
901
|
+
console.log(chalk.dim(`│ │ ${branch}`) + ' ' + chalk.bold(d.pattern) + chalk.dim(` ${d.tag}`) + label);
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
// Keyframe animations
|
|
905
|
+
if (motion.animations.length > 0) {
|
|
906
|
+
console.log(chalk.dim('│ └─') + ' ' + chalk.dim('Keyframes'));
|
|
907
|
+
motion.animations.slice(0, 6).forEach((a, i) => {
|
|
908
|
+
const isLast = i === Math.min(motion.animations.length, 6) - 1;
|
|
909
|
+
const branch = isLast ? '└─' : '├─';
|
|
910
|
+
const dur = a.duration ? chalk.dim(` ${a.duration}`) : '';
|
|
911
|
+
const ctx = a.contexts?.length ? chalk.dim(` [${a.contexts.join(', ')}]`) : '';
|
|
912
|
+
console.log(chalk.dim(`│ ${branch}`) + ' ' + a.name + dur + ctx);
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
console.log(chalk.dim('│'));
|
|
916
|
+
}
|
|
917
|
+
function displayWcag(wcag) {
|
|
918
|
+
if (!wcag || wcag.length === 0)
|
|
919
|
+
return;
|
|
920
|
+
console.log(chalk.dim('├─') + ' ' + chalk.bold('WCAG Contrast'));
|
|
921
|
+
const staticPairs = wcag.filter(p => !p.source);
|
|
922
|
+
const statePairs = wcag.filter(p => p.source === 'state');
|
|
923
|
+
const passing = staticPairs.filter(p => p.aa);
|
|
924
|
+
const failing = staticPairs.filter(p => !p.aa);
|
|
925
|
+
const all = [...passing.slice(0, 5), ...failing.slice(0, 3)];
|
|
926
|
+
function renderPair(pair, branch) {
|
|
927
|
+
const fgSwatch = chalk.bgHex(pair.fg)(' ');
|
|
928
|
+
const bgSwatch = chalk.bgHex(pair.bg)(' ');
|
|
929
|
+
const grade = pair.aaa
|
|
930
|
+
? color.success('AAA')
|
|
931
|
+
: pair.aa
|
|
932
|
+
? color.success('AA ')
|
|
933
|
+
: pair.aaLarge
|
|
934
|
+
? color.warning('AA-Large')
|
|
935
|
+
: color.error('fail');
|
|
936
|
+
const ratio = chalk.bold(`${pair.ratio}:1`);
|
|
937
|
+
const stateTag = pair.state ? chalk.dim(` [${pair.state}]`) : '';
|
|
938
|
+
console.log(chalk.dim(`│ ${branch}`) + ' ' + `${fgSwatch} ${bgSwatch} ${ratio} ${grade}${stateTag} ${chalk.dim(pair.fg + ' / ' + pair.bg)}`);
|
|
939
|
+
}
|
|
940
|
+
all.forEach((pair, i) => {
|
|
941
|
+
const isLast = i === all.length - 1 && statePairs.length === 0;
|
|
942
|
+
renderPair(pair, isLast ? '└─' : '├─');
|
|
943
|
+
});
|
|
944
|
+
if (statePairs.length > 0) {
|
|
945
|
+
console.log(chalk.dim('│ ├─') + ' ' + chalk.bold('Interactive states'));
|
|
946
|
+
const stateFailingFirst = [...statePairs.filter(p => !p.aa), ...statePairs.filter(p => p.aa)].slice(0, 8);
|
|
947
|
+
stateFailingFirst.forEach((pair, i) => {
|
|
948
|
+
const isLast = i === stateFailingFirst.length - 1;
|
|
949
|
+
renderPair(pair, isLast ? '└─' : '├─');
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
console.log(chalk.dim('│'));
|
|
953
|
+
}
|
|
954
|
+
//# sourceMappingURL=terminal.js.map
|