@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.
Files changed (139) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +408 -0
  3. package/dist/index.d.ts +8 -0
  4. package/dist/index.js +532 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/lib/browser.d.ts +16 -0
  7. package/dist/lib/browser.js +27 -0
  8. package/dist/lib/browser.js.map +1 -0
  9. package/dist/lib/colors.d.ts +101 -0
  10. package/dist/lib/colors.js +405 -0
  11. package/dist/lib/colors.js.map +1 -0
  12. package/dist/lib/compare.d.ts +31 -0
  13. package/dist/lib/compare.js +46 -0
  14. package/dist/lib/compare.js.map +1 -0
  15. package/dist/lib/discovery.d.ts +31 -0
  16. package/dist/lib/discovery.js +243 -0
  17. package/dist/lib/discovery.js.map +1 -0
  18. package/dist/lib/drift.d.ts +64 -0
  19. package/dist/lib/drift.js +383 -0
  20. package/dist/lib/drift.js.map +1 -0
  21. package/dist/lib/dtcg/validate.d.ts +51 -0
  22. package/dist/lib/dtcg/validate.js +1403 -0
  23. package/dist/lib/dtcg/validate.js.map +1 -0
  24. package/dist/lib/exit-codes.d.ts +29 -0
  25. package/dist/lib/exit-codes.js +26 -0
  26. package/dist/lib/exit-codes.js.map +1 -0
  27. package/dist/lib/extractors/breakpoints.d.ts +5 -0
  28. package/dist/lib/extractors/breakpoints.js +450 -0
  29. package/dist/lib/extractors/breakpoints.js.map +1 -0
  30. package/dist/lib/extractors/colors.d.ts +2 -0
  31. package/dist/lib/extractors/colors.js +657 -0
  32. package/dist/lib/extractors/colors.js.map +1 -0
  33. package/dist/lib/extractors/components.d.ts +4 -0
  34. package/dist/lib/extractors/components.js +370 -0
  35. package/dist/lib/extractors/components.js.map +1 -0
  36. package/dist/lib/extractors/index.d.ts +9 -0
  37. package/dist/lib/extractors/index.js +1257 -0
  38. package/dist/lib/extractors/index.js.map +1 -0
  39. package/dist/lib/extractors/logo.d.ts +2 -0
  40. package/dist/lib/extractors/logo.js +626 -0
  41. package/dist/lib/extractors/logo.js.map +1 -0
  42. package/dist/lib/extractors/spacing.d.ts +4 -0
  43. package/dist/lib/extractors/spacing.js +163 -0
  44. package/dist/lib/extractors/spacing.js.map +1 -0
  45. package/dist/lib/extractors/teach.d.ts +1 -0
  46. package/dist/lib/extractors/teach.js +66 -0
  47. package/dist/lib/extractors/teach.js.map +1 -0
  48. package/dist/lib/extractors/typography.d.ts +1 -0
  49. package/dist/lib/extractors/typography.js +163 -0
  50. package/dist/lib/extractors/typography.js.map +1 -0
  51. package/dist/lib/findings.d.ts +34 -0
  52. package/dist/lib/findings.js +166 -0
  53. package/dist/lib/findings.js.map +1 -0
  54. package/dist/lib/formatters/dtcg.d.ts +10 -0
  55. package/dist/lib/formatters/dtcg.js +416 -0
  56. package/dist/lib/formatters/dtcg.js.map +1 -0
  57. package/dist/lib/formatters/html.d.ts +25 -0
  58. package/dist/lib/formatters/html.js +479 -0
  59. package/dist/lib/formatters/html.js.map +1 -0
  60. package/dist/lib/formatters/markdown.d.ts +5 -0
  61. package/dist/lib/formatters/markdown.js +568 -0
  62. package/dist/lib/formatters/markdown.js.map +1 -0
  63. package/dist/lib/formatters/pdf.d.ts +12 -0
  64. package/dist/lib/formatters/pdf.js +1121 -0
  65. package/dist/lib/formatters/pdf.js.map +1 -0
  66. package/dist/lib/formatters/terminal.d.ts +6 -0
  67. package/dist/lib/formatters/terminal.js +954 -0
  68. package/dist/lib/formatters/terminal.js.map +1 -0
  69. package/dist/lib/formatters/theme.d.ts +35 -0
  70. package/dist/lib/formatters/theme.js +37 -0
  71. package/dist/lib/formatters/theme.js.map +1 -0
  72. package/dist/lib/merger.d.ts +14 -0
  73. package/dist/lib/merger.js +362 -0
  74. package/dist/lib/merger.js.map +1 -0
  75. package/dist/lib/normalize.d.ts +29 -0
  76. package/dist/lib/normalize.js +59 -0
  77. package/dist/lib/normalize.js.map +1 -0
  78. package/dist/lib/robots.d.ts +12 -0
  79. package/dist/lib/robots.js +110 -0
  80. package/dist/lib/robots.js.map +1 -0
  81. package/dist/lib/run-summary.d.ts +40 -0
  82. package/dist/lib/run-summary.js +64 -0
  83. package/dist/lib/run-summary.js.map +1 -0
  84. package/dist/lib/types.d.ts +329 -0
  85. package/dist/lib/types.js +7 -0
  86. package/dist/lib/types.js.map +1 -0
  87. package/dist/lib/version.d.ts +134 -0
  88. package/dist/lib/version.js +153 -0
  89. package/dist/lib/version.js.map +1 -0
  90. package/dist/mcp-server.d.ts +11 -0
  91. package/dist/mcp-server.js +311 -0
  92. package/dist/mcp-server.js.map +1 -0
  93. package/dist/package.json +106 -0
  94. package/dist/test/_vitest-shim.d.ts +13 -0
  95. package/dist/test/_vitest-shim.js +23 -0
  96. package/dist/test/_vitest-shim.js.map +1 -0
  97. package/dist/test/cli.test.d.ts +1 -0
  98. package/dist/test/cli.test.js +24 -0
  99. package/dist/test/cli.test.js.map +1 -0
  100. package/dist/test/colors.test.d.ts +1 -0
  101. package/dist/test/colors.test.js +64 -0
  102. package/dist/test/colors.test.js.map +1 -0
  103. package/dist/test/compare.test.d.ts +1 -0
  104. package/dist/test/compare.test.js +57 -0
  105. package/dist/test/compare.test.js.map +1 -0
  106. package/dist/test/drift.test.d.ts +1 -0
  107. package/dist/test/drift.test.js +53 -0
  108. package/dist/test/drift.test.js.map +1 -0
  109. package/dist/test/dtcg-formatter.test.d.ts +1 -0
  110. package/dist/test/dtcg-formatter.test.js +48 -0
  111. package/dist/test/dtcg-formatter.test.js.map +1 -0
  112. package/dist/test/dtcg-validate.test.d.ts +1 -0
  113. package/dist/test/dtcg-validate.test.js +2129 -0
  114. package/dist/test/dtcg-validate.test.js.map +1 -0
  115. package/dist/test/exit-codes.test.d.ts +1 -0
  116. package/dist/test/exit-codes.test.js +53 -0
  117. package/dist/test/exit-codes.test.js.map +1 -0
  118. package/dist/test/findings.test.d.ts +1 -0
  119. package/dist/test/findings.test.js +77 -0
  120. package/dist/test/findings.test.js.map +1 -0
  121. package/dist/test/html.test.d.ts +1 -0
  122. package/dist/test/html.test.js +95 -0
  123. package/dist/test/html.test.js.map +1 -0
  124. package/dist/test/markdown.test.d.ts +1 -0
  125. package/dist/test/markdown.test.js +145 -0
  126. package/dist/test/markdown.test.js.map +1 -0
  127. package/dist/test/merger.test.d.ts +1 -0
  128. package/dist/test/merger.test.js +98 -0
  129. package/dist/test/merger.test.js.map +1 -0
  130. package/dist/test/normalize.test.d.ts +1 -0
  131. package/dist/test/normalize.test.js +47 -0
  132. package/dist/test/normalize.test.js.map +1 -0
  133. package/dist/test/run-summary.test.d.ts +1 -0
  134. package/dist/test/run-summary.test.js +45 -0
  135. package/dist/test/run-summary.test.js.map +1 -0
  136. package/dist/test/version.test.d.ts +1 -0
  137. package/dist/test/version.test.js +73 -0
  138. package/dist/test/version.test.js.map +1 -0
  139. 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