dembrandt 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/display.js ADDED
@@ -0,0 +1,317 @@
1
+ /**
2
+ * Terminal Display Formatter
3
+ *
4
+ * Formats extracted brand data into clean, readable terminal output
5
+ * with color swatches, tables, and confidence indicators.
6
+ */
7
+
8
+ import chalk from 'chalk';
9
+ import Table from 'cli-table3';
10
+
11
+ /**
12
+ * Main display function - outputs formatted extraction results to terminal
13
+ * @param {Object} data - Extraction results from extractBranding()
14
+ */
15
+ export function displayResults(data) {
16
+ console.log('\n' + chalk.bold.cyan('='.repeat(60)));
17
+ console.log(chalk.bold.cyan(' BRAND EXTRACTION REPORT'));
18
+ console.log(chalk.bold.cyan('='.repeat(60)) + '\n');
19
+
20
+ console.log(chalk.bold('URL: ') + chalk.blue(data.url));
21
+ console.log(chalk.dim('Extracted at: ' + data.extractedAt) + '\n');
22
+
23
+ displayLogo(data.logo);
24
+ displayColors(data.colors);
25
+ displayTypography(data.typography);
26
+ displaySpacing(data.spacing);
27
+ displayBorderRadius(data.borderRadius);
28
+ displayShadows(data.shadows);
29
+ displayButtons(data.components?.buttons);
30
+ displayInputs(data.components?.inputs);
31
+ displayBreakpoints(data.breakpoints);
32
+ displayIconSystem(data.iconSystem);
33
+ displayFrameworks(data.frameworks);
34
+
35
+ console.log('\n' + chalk.dim('─'.repeat(60)) + '\n');
36
+ }
37
+
38
+ function displayLogo(logo) {
39
+ console.log(chalk.bold.yellow('LOGO'));
40
+ console.log(chalk.dim('─'.repeat(60)));
41
+ if (logo) {
42
+ console.log(`Source: ${logo.source}`);
43
+ if (logo.url) console.log(`URL: ${chalk.blue(logo.url)}`);
44
+ if (logo.width) console.log(`Dimensions: ${logo.width}x${logo.height}px`);
45
+ if (logo.alt) console.log(`Alt text: ${logo.alt}`);
46
+ } else {
47
+ console.log(chalk.dim('No logo detected'));
48
+ }
49
+ console.log('');
50
+ }
51
+
52
+ function displayColors(colors) {
53
+ console.log(chalk.bold.yellow('COLORS'));
54
+ console.log(chalk.dim('─'.repeat(60)));
55
+
56
+ if (colors.semantic) {
57
+ const semantic = colors.semantic;
58
+ const semanticEntries = Object.entries(semantic).filter(([_, color]) => color);
59
+
60
+ if (semanticEntries.length > 0) {
61
+ console.log(chalk.bold('Semantic Colors:'));
62
+ semanticEntries.forEach(([role, color]) => {
63
+ const colorBlock = chalk.bgHex(color)(' ') + ' ';
64
+ console.log(` ${colorBlock}${chalk.cyan(role.padEnd(12))}: ${color}`);
65
+ });
66
+ console.log('');
67
+ }
68
+ }
69
+
70
+ if (colors.cssVariables && Object.keys(colors.cssVariables).length > 0) {
71
+ console.log(chalk.bold('CSS Variables (Brand-Specific):'));
72
+ const cssVarEntries = Object.entries(colors.cssVariables);
73
+
74
+ cssVarEntries.slice(0, 10).forEach(([name, value]) => {
75
+ try {
76
+ const colorBlock = chalk.bgHex(value)(' ') + ' ';
77
+ console.log(` ${colorBlock}${chalk.cyan(name)}: ${value}`);
78
+ } catch {
79
+ // Not a valid hex color, show without swatch
80
+ console.log(` ${chalk.cyan(name)}: ${value}`);
81
+ }
82
+ });
83
+
84
+ if (cssVarEntries.length > 10) {
85
+ console.log(chalk.dim(` (${cssVarEntries.length - 10} more variables in saved JSON file)`));
86
+ }
87
+ console.log('');
88
+ }
89
+
90
+ if (colors.palette.length > 0) {
91
+ const brandColors = colors.palette.filter(c =>
92
+ c.confidence === 'high' || c.confidence === 'medium'
93
+ );
94
+
95
+ if (brandColors.length > 0) {
96
+ const colorTable = new Table({
97
+ head: [chalk.bold('Color'), chalk.bold('Confidence'), chalk.bold('Usage'), chalk.bold('Sources')],
98
+ style: { head: [], border: [] },
99
+ colWidths: [20, 12, 8, 25]
100
+ });
101
+
102
+ brandColors.slice(0, 12).forEach(c => {
103
+ const colorBlock = chalk.bgHex(c.color)(' ') + ' ' + c.color;
104
+
105
+ let confidenceDisplay;
106
+ if (c.confidence === 'high') {
107
+ confidenceDisplay = chalk.green('● High');
108
+ } else if (c.confidence === 'medium') {
109
+ confidenceDisplay = chalk.yellow('● Medium');
110
+ } else {
111
+ confidenceDisplay = chalk.gray('● Low');
112
+ }
113
+
114
+ const sourcesDisplay = c.sources && c.sources.length > 0
115
+ ? c.sources.join(', ')
116
+ : '-';
117
+
118
+ colorTable.push([
119
+ colorBlock,
120
+ confidenceDisplay,
121
+ c.count,
122
+ sourcesDisplay
123
+ ]);
124
+ });
125
+
126
+ console.log(colorTable.toString());
127
+
128
+ const lowConfCount = colors.palette.length - brandColors.length;
129
+ if (lowConfCount > 0) {
130
+ console.log(chalk.dim(` (${lowConfCount} additional low-confidence colors hidden)`));
131
+ }
132
+ } else {
133
+ console.log(chalk.dim('No brand colors detected'));
134
+ }
135
+ } else {
136
+ console.log(chalk.dim('No dominant colors detected'));
137
+ }
138
+ console.log('');
139
+ }
140
+
141
+ function displayTypography(typography) {
142
+ console.log(chalk.bold.yellow('TYPOGRAPHY'));
143
+ console.log(chalk.dim('─'.repeat(60)));
144
+
145
+ if (typography.sources.googleFonts.length > 0) {
146
+ console.log(chalk.bold('Google Fonts: ') + typography.sources.googleFonts.join(', '));
147
+ }
148
+ if (typography.sources.adobeFonts) {
149
+ console.log(chalk.bold('Adobe Fonts: ') + 'Detected');
150
+ }
151
+ if (typography.sources.customFonts.length > 0) {
152
+ console.log(chalk.bold('Custom Fonts: ') + typography.sources.customFonts.join(', '));
153
+ }
154
+ console.log('');
155
+
156
+ if (typography.styles.length > 0) {
157
+ const highConfidenceFonts = typography.styles.filter(s => s.confidence === 'high');
158
+
159
+ if (highConfidenceFonts.length > 0) {
160
+ const fontTable = new Table({
161
+ head: [chalk.bold('Font Family'), chalk.bold('Size'), chalk.bold('Weight'), chalk.bold('Context')],
162
+ style: { head: [], border: [] },
163
+ colWidths: [32, 12, 10, 18]
164
+ });
165
+
166
+ highConfidenceFonts.slice(0, 6).forEach(style => {
167
+ fontTable.push([
168
+ style.fontFamily.split(',')[0].replace(/['"]/g, ''),
169
+ style.fontSize + '\n' + chalk.dim(style.fontSizeRem),
170
+ style.fontWeight,
171
+ style.contexts.slice(0, 2).join(', ')
172
+ ]);
173
+ });
174
+
175
+ console.log(fontTable.toString());
176
+
177
+ const mediumCount = typography.styles.filter(s => s.confidence === 'medium').length;
178
+ if (mediumCount > 0) {
179
+ console.log(chalk.dim(` (${mediumCount} additional medium-confidence font styles in saved JSON)`));
180
+ }
181
+ } else {
182
+ console.log(chalk.dim('No high-confidence typography detected'));
183
+ }
184
+ }
185
+ console.log('');
186
+ }
187
+
188
+ function displaySpacing(spacing) {
189
+ console.log(chalk.bold.yellow('SPACING'));
190
+ console.log(chalk.dim('─'.repeat(60)));
191
+ console.log(chalk.bold('Scale Type: ') + spacing.scaleType);
192
+ console.log(chalk.bold('Common Values: ') +
193
+ spacing.commonValues.slice(0, 8).map(v => v.px).join(', '));
194
+ console.log('');
195
+ }
196
+
197
+ function displayBorderRadius(borderRadius) {
198
+ console.log(chalk.bold.yellow('BORDER RADIUS'));
199
+ console.log(chalk.dim('─'.repeat(60)));
200
+ if (borderRadius && borderRadius.values.length > 0) {
201
+ const highConfRadius = borderRadius.values.filter(r => r.confidence === 'high' || r.confidence === 'medium');
202
+ if (highConfRadius.length > 0) {
203
+ console.log(chalk.bold('Common Values: ') +
204
+ highConfRadius.slice(0, 5).map(v => `${v.value} (${v.count}×)`).join(', '));
205
+ } else {
206
+ console.log(chalk.dim('No common border radius detected'));
207
+ }
208
+ } else {
209
+ console.log(chalk.dim('No border radius values detected'));
210
+ }
211
+ console.log('');
212
+ }
213
+
214
+ function displayShadows(shadows) {
215
+ console.log(chalk.bold.yellow('SHADOWS'));
216
+ console.log(chalk.dim('─'.repeat(60)));
217
+ if (shadows && shadows.length > 0) {
218
+ const highConfShadows = shadows.filter(s => s.confidence === 'high' || s.confidence === 'medium');
219
+ if (highConfShadows.length > 0) {
220
+ highConfShadows.slice(0, 4).forEach((s, i) => {
221
+ const confIndicator = s.confidence === 'high' ? chalk.green('●') : chalk.yellow('●');
222
+ console.log(`${confIndicator} ${s.shadow} ${chalk.dim('(' + s.count + '×)')}`);
223
+ });
224
+ } else {
225
+ console.log(chalk.dim('No common shadows detected'));
226
+ }
227
+ } else {
228
+ console.log(chalk.dim('No shadows detected'));
229
+ }
230
+ console.log('');
231
+ }
232
+
233
+ function displayButtons(buttons) {
234
+ console.log(chalk.bold.yellow('BUTTON VARIANTS'));
235
+ console.log(chalk.dim('─'.repeat(60)));
236
+ if (buttons && buttons.length > 0) {
237
+ const highConfButtons = buttons.filter(b => b.confidence === 'high');
238
+
239
+ if (highConfButtons.length > 0) {
240
+ highConfButtons.slice(0, 4).forEach((btn, i) => {
241
+ console.log(`${chalk.green('●')} ${chalk.bgHex(btn.backgroundColor)(' ')} ${btn.backgroundColor} / ${btn.color}`);
242
+ console.log(` Padding: ${btn.padding}, Radius: ${btn.borderRadius}, Weight: ${btn.fontWeight}`);
243
+ if (btn.classes) console.log(` ${chalk.dim('Classes: ' + btn.classes)}`);
244
+ console.log('');
245
+ });
246
+
247
+ const mediumCount = buttons.filter(b => b.confidence === 'medium').length;
248
+ if (mediumCount > 0) {
249
+ console.log(chalk.dim(` (${mediumCount} additional medium-confidence button variants in saved JSON)`));
250
+ }
251
+ } else {
252
+ console.log(chalk.dim('No high-confidence button styles detected'));
253
+ console.log('');
254
+ }
255
+ } else {
256
+ console.log(chalk.dim('No button styles detected'));
257
+ console.log('');
258
+ }
259
+ }
260
+
261
+ function displayInputs(inputs) {
262
+ console.log(chalk.bold.yellow('INPUT STYLES'));
263
+ console.log(chalk.dim('─'.repeat(60)));
264
+ if (inputs && inputs.length > 0) {
265
+ inputs.slice(0, 3).forEach((input, i) => {
266
+ console.log(`${i + 1}. ${chalk.bold(input.type)}`);
267
+ console.log(` Border: ${input.border}`);
268
+ console.log(` Padding: ${input.padding}, Radius: ${input.borderRadius}`);
269
+ if (input.focusStyles && input.focusStyles.outline !== 'none') {
270
+ console.log(` Focus: ${input.focusStyles.outline}`);
271
+ }
272
+ console.log('');
273
+ });
274
+ } else {
275
+ console.log(chalk.dim('No input styles detected'));
276
+ console.log('');
277
+ }
278
+ }
279
+
280
+ function displayBreakpoints(breakpoints) {
281
+ console.log(chalk.bold.yellow('BREAKPOINTS'));
282
+ console.log(chalk.dim('─'.repeat(60)));
283
+ if (breakpoints && breakpoints.length > 0) {
284
+ console.log(breakpoints.map(bp => bp.px).join(' → '));
285
+ } else {
286
+ console.log(chalk.dim('No breakpoints detected'));
287
+ }
288
+ console.log('');
289
+ }
290
+
291
+ function displayIconSystem(iconSystem) {
292
+ console.log(chalk.bold.yellow('ICON SYSTEM'));
293
+ console.log(chalk.dim('─'.repeat(60)));
294
+ if (iconSystem && iconSystem.length > 0) {
295
+ iconSystem.forEach(system => {
296
+ console.log(`• ${chalk.bold(system.name)} ${chalk.dim('(' + system.type + ')')}`);
297
+ if (system.sizes) console.log(` Sizes: ${system.sizes.join(', ')}`);
298
+ });
299
+ } else {
300
+ console.log(chalk.dim('No icon system detected'));
301
+ }
302
+ console.log('');
303
+ }
304
+
305
+ function displayFrameworks(frameworks) {
306
+ console.log(chalk.bold.yellow('FRAMEWORKS'));
307
+ console.log(chalk.dim('─'.repeat(60)));
308
+ if (frameworks.length > 0) {
309
+ frameworks.forEach(fw => {
310
+ const confidence = fw.confidence === 'high' ? chalk.green('●') : chalk.yellow('◐');
311
+ console.log(`${confidence} ${chalk.bold(fw.name)} ${chalk.dim('(' + fw.evidence + ')')}`);
312
+ });
313
+ } else {
314
+ console.log(chalk.dim('No frameworks detected'));
315
+ }
316
+ console.log('\n' + chalk.dim('─'.repeat(60)) + '\n');
317
+ }