dembrandt 0.3.0 β 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +63 -360
- package/index.js +27 -42
- package/lib/display.js +582 -267
- package/lib/extractors.js +867 -219
- package/lib/w3c-exporter.js +497 -0
- package/package.json +7 -8
- package/lib/exporters.js +0 -402
package/README.md
CHANGED
|
@@ -1,420 +1,123 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Dembrandt.
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/dembrandt)
|
|
4
4
|
[](https://www.npmjs.com/package/dembrandt)
|
|
5
|
-
[](https://github.com/
|
|
5
|
+
[](https://github.com/dembrandt/dembrandt/blob/main/LICENSE)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Extract any websiteβs design system into design tokens in a few seconds: logo, colors, typography, borders, and more. One command.
|
|
8
8
|
|
|
9
9
|

|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Install
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
npx dembrandt
|
|
14
|
+
npx dembrandt bmw.de
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Or install globally: `npm install -g dembrandt` then run `dembrandt bmw.de`
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
Requires Node.js 18+
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
## What to expect from extraction?
|
|
22
22
|
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
- **Buttons** β Component styles with variants and states
|
|
32
|
-
- **Inputs** β Form field styles (input, textarea, select)
|
|
33
|
-
- **Links** β Link styles with hover states and decorations
|
|
34
|
-
- **Breakpoints** β Responsive design breakpoints from media queries
|
|
35
|
-
- **Icons** β Icon system detection (Font Awesome, Material Icons, SVG)
|
|
36
|
-
- **Frameworks** β CSS framework detection (Tailwind, Bootstrap, Material-UI, Chakra)
|
|
37
|
-
|
|
38
|
-
Perfect for competitive analysis, brand audits, or rebuilding a brand when you don't have design guidelines.
|
|
39
|
-
|
|
40
|
-
## Why It Matters
|
|
41
|
-
|
|
42
|
-
**Designers** β Analyze competitor systems, document production tokens, audit brand consistency
|
|
43
|
-
|
|
44
|
-
**Developers** β Migrate design tokens, reverse engineer components, validate implementations
|
|
45
|
-
|
|
46
|
-
**Product Managers** β Track competitor evolution, quantify design debt, evaluate vendors
|
|
47
|
-
|
|
48
|
-
**Marketing** β Audit competitor brands, plan rebrands, monitor brand compliance
|
|
49
|
-
|
|
50
|
-
**Engineering Leaders** β Measure technical debt, plan migrations, assess acquisition targets
|
|
51
|
-
|
|
52
|
-
## How It Works
|
|
53
|
-
|
|
54
|
-
Uses Playwright to render the page, extracts computed styles from the DOM, analyzes color usage and confidence, groups similar typography, detects spacing patterns, and returns actionable design tokens.
|
|
55
|
-
|
|
56
|
-
### Extraction Process
|
|
57
|
-
|
|
58
|
-
1. **Browser Launch** - Launches Chromium with stealth configuration
|
|
59
|
-
2. **Anti-Detection** - Injects scripts to bypass bot detection
|
|
60
|
-
3. **Navigation** - Navigates to target URL with retry logic
|
|
61
|
-
4. **Hydration** - Waits for SPAs to fully load (8s initial + 4s stabilization)
|
|
62
|
-
5. **Content Validation** - Verifies page content is substantial (>500 chars)
|
|
63
|
-
6. **Parallel Extraction** - Runs all extractors concurrently for speed
|
|
64
|
-
7. **Analysis** - Analyzes computed styles, DOM structure, and CSS variables
|
|
65
|
-
8. **Scoring** - Assigns confidence scores based on context and usage
|
|
66
|
-
|
|
67
|
-
### Color Confidence
|
|
68
|
-
|
|
69
|
-
- **High** β Logo, brand elements, primary buttons
|
|
70
|
-
- **Medium** β Interactive elements, icons, navigation
|
|
71
|
-
- **Low** β Generic UI components (filtered from display)
|
|
72
|
-
|
|
73
|
-
Only shows high and medium confidence colors in terminal. Full palette in JSON.
|
|
74
|
-
|
|
75
|
-
### Typography Detection
|
|
76
|
-
|
|
77
|
-
Samples all heading levels (h1-h6), body text, buttons, links. Groups by font family, size, and weight. Detects Google Fonts, Adobe Fonts, custom @font-face.
|
|
78
|
-
|
|
79
|
-
### Framework Detection
|
|
80
|
-
|
|
81
|
-
Recognizes Tailwind CSS, Bootstrap, Material-UI, and others by class patterns and CDN links.
|
|
82
|
-
|
|
83
|
-
## Installation
|
|
84
|
-
|
|
85
|
-
### Using npx (Recommended)
|
|
86
|
-
|
|
87
|
-
No installation needed! Run directly with `npx`:
|
|
88
|
-
|
|
89
|
-
```bash
|
|
90
|
-
npx dembrandt stripe.com
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
The first run will automatically install Chromium (~170MB).
|
|
94
|
-
|
|
95
|
-
### Global Installation
|
|
96
|
-
|
|
97
|
-
Install globally for repeated use:
|
|
98
|
-
|
|
99
|
-
```bash
|
|
100
|
-
npm install -g dembrandt
|
|
101
|
-
dembrandt stripe.com
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
### Prerequisites
|
|
105
|
-
|
|
106
|
-
- Node.js 18 or higher
|
|
107
|
-
|
|
108
|
-
### Development Setup
|
|
109
|
-
|
|
110
|
-
For contributors who want to work on dembrandt:
|
|
111
|
-
|
|
112
|
-
```bash
|
|
113
|
-
git clone https://github.com/thevangelist/dembrandt.git
|
|
114
|
-
cd dembrandt
|
|
115
|
-
npm install
|
|
116
|
-
npm link
|
|
117
|
-
```
|
|
23
|
+
- Colors (semantic, palette, CSS variables)
|
|
24
|
+
- Typography (fonts, sizes, weights, sources)
|
|
25
|
+
- Spacing (margin/padding scales)
|
|
26
|
+
- Borders (radius, widths, styles, colors)
|
|
27
|
+
- Shadows
|
|
28
|
+
- Components (buttons, badges, inputs, links)
|
|
29
|
+
- Breakpoints
|
|
30
|
+
- Icons & frameworks
|
|
118
31
|
|
|
119
32
|
## Usage
|
|
120
33
|
|
|
121
|
-
### Basic Usage
|
|
122
|
-
|
|
123
34
|
```bash
|
|
124
|
-
#
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
#
|
|
128
|
-
dembrandt
|
|
129
|
-
|
|
130
|
-
#
|
|
131
|
-
dembrandt
|
|
132
|
-
dembrandt https://github.com
|
|
133
|
-
dembrandt tailwindcss.com
|
|
35
|
+
dembrandt <url> # Basic extraction (terminal display only)
|
|
36
|
+
dembrandt bmw.de --json-only # Output raw JSON to terminal (no formatted display, no file save)
|
|
37
|
+
dembrandt bmw.de --save-output # Save JSON to output/bmw.de/YYYY-MM-DDTHH-MM-SS.json
|
|
38
|
+
dembrandt bmw.de --dtcg # Export in W3C Design Tokens (DTCG) format (auto-saves as .tokens.json)
|
|
39
|
+
dembrandt bmw.de --dark-mode # Extract colors from dark mode variant
|
|
40
|
+
dembrandt bmw.de --mobile # Use mobile viewport (390x844, iPhone 12/13/14/15) for responsive analysis
|
|
41
|
+
dembrandt bmw.de --slow # 3x longer timeouts (24s hydration) for JavaScript-heavy sites
|
|
42
|
+
dembrandt bmw.de --no-sandbox # Disable Chromium sandbox (required for Docker/CI)
|
|
134
43
|
```
|
|
135
44
|
|
|
136
|
-
|
|
45
|
+
Default: formatted terminal display only. Use `--save-output` to persist results as JSON files. Browser automatically retries in visible mode if headless extraction fails.
|
|
137
46
|
|
|
138
|
-
|
|
47
|
+
### W3C Design Tokens (DTCG) Format
|
|
139
48
|
|
|
140
|
-
|
|
141
|
-
dembrandt stripe.com --json-only > tokens.json
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
Note: JSON is automatically saved to `output/domain.com/` regardless of this flag.
|
|
145
|
-
|
|
146
|
-
**`-d, --debug`** - Run with visible browser and detailed logs
|
|
147
|
-
|
|
148
|
-
```bash
|
|
149
|
-
dembrandt stripe.com --debug
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
Useful for troubleshooting bot detection, timeouts, or extraction issues.
|
|
153
|
-
|
|
154
|
-
**`--verbose-colors`** - Show medium and low confidence colors in terminal output
|
|
49
|
+
Use `--dtcg` to export in the standardized [W3C Design Tokens Community Group](https://www.designtokens.org/) format:
|
|
155
50
|
|
|
156
51
|
```bash
|
|
157
|
-
dembrandt stripe.com --
|
|
52
|
+
dembrandt stripe.com --dtcg
|
|
53
|
+
# Saves to: output/stripe.com/TIMESTAMP.tokens.json
|
|
158
54
|
```
|
|
159
55
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
**`--dark-mode`** - Extract colors from dark mode
|
|
56
|
+
Transform DTCG tokens to platform-specific formats using [Style Dictionary](https://styledictionary.com):
|
|
163
57
|
|
|
164
58
|
```bash
|
|
165
|
-
|
|
59
|
+
# Generate CSS variables, SCSS, JavaScript, TypeScript
|
|
60
|
+
npm run build:tokens
|
|
166
61
|
```
|
|
167
62
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
**`--mobile`** - Extract from mobile viewport
|
|
171
|
-
|
|
172
|
-
```bash
|
|
173
|
-
dembrandt stripe.com --mobile
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
Simulates a mobile device viewport for responsive design token extraction.
|
|
177
|
-
|
|
178
|
-
**`--slow`** - Use 3x longer timeouts for slow-loading sites
|
|
179
|
-
|
|
180
|
-
```bash
|
|
181
|
-
dembrandt linkedin.com --slow
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
Helpful for sites with heavy JavaScript, complex SPAs, or aggressive bot detection that need extra time to fully load.
|
|
185
|
-
|
|
186
|
-
## Output
|
|
187
|
-
|
|
188
|
-
### Automatic JSON Saves
|
|
189
|
-
|
|
190
|
-
Every extraction is automatically saved to `output/domain.com/YYYY-MM-DDTHH-MM-SS.json` with:
|
|
191
|
-
|
|
192
|
-
- Complete design token data
|
|
193
|
-
- Timestamped for version tracking
|
|
194
|
-
- Organized by domain
|
|
195
|
-
|
|
196
|
-
Example: `output/stripe.com/2025-11-22T14-30-45.json`
|
|
197
|
-
|
|
198
|
-
### Terminal Output
|
|
199
|
-
|
|
200
|
-
Clean, formatted tables showing:
|
|
201
|
-
|
|
202
|
-
- Color palette with confidence ratings (with visual swatches)
|
|
203
|
-
- CSS variables with color previews
|
|
204
|
-
- Typography hierarchy with context
|
|
205
|
-
- Spacing scale (4px/8px grid detection)
|
|
206
|
-
- Shadow system
|
|
207
|
-
- Button variants
|
|
208
|
-
- Component style breakdowns
|
|
209
|
-
- Framework and icon system detection
|
|
210
|
-
|
|
211
|
-
### JSON Output Format
|
|
212
|
-
|
|
213
|
-
Complete extraction data for programmatic use:
|
|
214
|
-
|
|
215
|
-
```json
|
|
216
|
-
{
|
|
217
|
-
"url": "https://example.com",
|
|
218
|
-
"extractedAt": "2025-11-22T...",
|
|
219
|
-
"logo": { "source": "img", "url": "...", "width": 120, "height": 40 },
|
|
220
|
-
"colors": {
|
|
221
|
-
"semantic": { "primary": "#3b82f6", ... },
|
|
222
|
-
"palette": [{ "color": "#3b82f6", "confidence": "high", "count": 45, "sources": [...] }],
|
|
223
|
-
"cssVariables": { "--color-primary": "#3b82f6", ... }
|
|
224
|
-
},
|
|
225
|
-
"typography": {
|
|
226
|
-
"styles": [{ "fontFamily": "Inter", "fontSize": "16px", "fontWeight": "400", ... }],
|
|
227
|
-
"sources": { "googleFonts": [...], "adobeFonts": false, "customFonts": [...] }
|
|
228
|
-
},
|
|
229
|
-
"spacing": { "scaleType": "8px", "commonValues": [{ "px": "16px", "rem": "1rem", "count": 42 }, ...] },
|
|
230
|
-
"borderRadius": { "values": [{ "value": "8px", "count": 15, "confidence": "high" }, ...] },
|
|
231
|
-
"shadows": [{ "shadow": "0 2px 4px rgba(0,0,0,0.1)", "count": 8, "confidence": "high" }, ...],
|
|
232
|
-
"components": {
|
|
233
|
-
"buttons": [{ "backgroundColor": "...", "color": "...", "padding": "...", ... }],
|
|
234
|
-
"inputs": [{ "type": "input", "border": "...", "borderRadius": "...", ... }]
|
|
235
|
-
},
|
|
236
|
-
"breakpoints": [{ "px": "768px" }, ...],
|
|
237
|
-
"iconSystem": [{ "name": "Font Awesome", "type": "icon-font" }, ...],
|
|
238
|
-
"frameworks": [{ "name": "Tailwind CSS", "confidence": "high", "evidence": "class patterns" }]
|
|
239
|
-
}
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
## Examples
|
|
243
|
-
|
|
244
|
-
### Extract Design Tokens
|
|
245
|
-
|
|
246
|
-
```bash
|
|
247
|
-
# Analyze a single site (auto-saves JSON to output/stripe.com/)
|
|
248
|
-
dembrandt stripe.com
|
|
249
|
-
|
|
250
|
-
# View saved JSON files
|
|
251
|
-
ls output/stripe.com/
|
|
252
|
-
|
|
253
|
-
# Output to stdout for piping
|
|
254
|
-
dembrandt stripe.com --json-only | jq '.colors.semantic'
|
|
255
|
-
|
|
256
|
-
# Debug mode for difficult sites
|
|
257
|
-
dembrandt example.com --debug
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
### Compare Competitors
|
|
261
|
-
|
|
262
|
-
```bash
|
|
263
|
-
# Extract tokens from multiple competitors (auto-saved to output/)
|
|
264
|
-
for site in stripe.com square.com paypal.com; do
|
|
265
|
-
dembrandt $site
|
|
266
|
-
done
|
|
267
|
-
|
|
268
|
-
# Compare color palettes from most recent extractions
|
|
269
|
-
jq '.colors.palette[] | select(.confidence=="high")' output/stripe.com/2025-11-22T*.json output/square.com/2025-11-22T*.json
|
|
270
|
-
|
|
271
|
-
# Compare semantic colors across competitors
|
|
272
|
-
jq '.colors.semantic' output/*/2025-11-22T*.json
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
### Integration with Design Tools
|
|
276
|
-
|
|
277
|
-
```bash
|
|
278
|
-
# Extract and convert to custom config format
|
|
279
|
-
dembrandt mysite.com --json-only | jq '{
|
|
280
|
-
colors: .colors.semantic,
|
|
281
|
-
fontFamily: .typography.sources,
|
|
282
|
-
spacing: .spacing.commonValues
|
|
283
|
-
}' > design-tokens.json
|
|
284
|
-
|
|
285
|
-
# Or use the built-in Tailwind CSS exporter (see lib/exporters.js)
|
|
286
|
-
# Converts extracted tokens to Tailwind config format
|
|
287
|
-
```
|
|
63
|
+
Outputs CSS custom properties, SCSS variables, ES6 modules, and TypeScript definitions. See [STYLE-DICTIONARY.md](STYLE-DICTIONARY.md) for details.
|
|
288
64
|
|
|
289
65
|
## Use Cases
|
|
290
66
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
### Competitive Analysis
|
|
296
|
-
|
|
297
|
-
Compare design systems across competitors to identify trends and opportunities.
|
|
298
|
-
|
|
299
|
-
### Design System Migration
|
|
67
|
+
- Brand audits & competitive analysis
|
|
68
|
+
- Design system documentation
|
|
69
|
+
- Reverse engineering brands
|
|
70
|
+
- Multi-site brand consolidation
|
|
300
71
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
### Reverse Engineering
|
|
304
|
-
|
|
305
|
-
Rebuild a brand when original design guidelines are unavailable.
|
|
306
|
-
|
|
307
|
-
### Quality Assurance
|
|
308
|
-
|
|
309
|
-
Verify design consistency across different pages and environments.
|
|
310
|
-
|
|
311
|
-
## Advanced Features
|
|
312
|
-
|
|
313
|
-
### Bot Detection Avoidance
|
|
314
|
-
|
|
315
|
-
- Stealth mode with anti-detection scripts
|
|
316
|
-
- Automatic fallback to visible browser on detection
|
|
317
|
-
- Human-like interaction simulation (mouse movement, scrolling)
|
|
318
|
-
- Custom user agent and browser fingerprinting
|
|
319
|
-
|
|
320
|
-
### Smart Retry Logic
|
|
321
|
-
|
|
322
|
-
- Automatic retry on navigation failures (up to 2 attempts)
|
|
323
|
-
- SPA hydration detection and waiting
|
|
324
|
-
- Content validation to ensure page is fully loaded
|
|
325
|
-
- Detailed progress logging at each step
|
|
326
|
-
|
|
327
|
-
### Comprehensive Logging
|
|
328
|
-
|
|
329
|
-
- Real-time spinner with step-by-step progress
|
|
330
|
-
- Detailed extraction metrics (colors found, styles detected, etc.)
|
|
331
|
-
- Error context with URL, stage, and attempt information
|
|
332
|
-
- Debug mode with full stack traces
|
|
333
|
-
|
|
334
|
-
## Troubleshooting
|
|
335
|
-
|
|
336
|
-
### Bot Detection Issues
|
|
337
|
-
|
|
338
|
-
If you encounter timeouts or network errors:
|
|
339
|
-
|
|
340
|
-
```bash
|
|
341
|
-
dembrandt example.com --debug
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
This will automatically retry with a visible browser.
|
|
345
|
-
|
|
346
|
-
### Page Not Loading
|
|
347
|
-
|
|
348
|
-
Some sites require longer load times. The tool waits 8 seconds for SPA hydration, but you can modify this in the source.
|
|
72
|
+
## How It Works
|
|
349
73
|
|
|
350
|
-
|
|
74
|
+
Uses Playwright to render the page, extracts computed styles from the DOM, analyzes color usage and confidence, groups similar typography, detects spacing patterns, and returns actionable design tokens.
|
|
351
75
|
|
|
352
|
-
|
|
76
|
+
### Extraction Process
|
|
353
77
|
|
|
354
|
-
|
|
78
|
+
1. Browser Launch - Launches Chromium with stealth configuration
|
|
79
|
+
2. Anti-Detection - Injects scripts to bypass bot detection
|
|
80
|
+
3. Navigation - Navigates to target URL with retry logic
|
|
81
|
+
4. Hydration - Waits for SPAs to fully load (8s initial + 4s stabilization)
|
|
82
|
+
5. Content Validation - Verifies page content is substantial (>500 chars)
|
|
83
|
+
6. Parallel Extraction - Runs all extractors concurrently for speed
|
|
84
|
+
7. Analysis - Analyzes computed styles, DOM structure, and CSS variables
|
|
85
|
+
8. Scoring - Assigns confidence scores based on context and usage
|
|
355
86
|
|
|
356
|
-
|
|
87
|
+
### Color Confidence
|
|
357
88
|
|
|
358
|
-
-
|
|
359
|
-
-
|
|
360
|
-
-
|
|
361
|
-
-
|
|
89
|
+
- High β Logo, brand elements, primary buttons
|
|
90
|
+
- Medium β Interactive elements, icons, navigation
|
|
91
|
+
- Low β Generic UI components (filtered from display)
|
|
92
|
+
- Only shows high and medium confidence colors in terminal. Full palette in JSON.
|
|
362
93
|
|
|
363
94
|
## Limitations
|
|
364
95
|
|
|
365
|
-
- Dark mode requires
|
|
96
|
+
- Dark mode requires --dark-mode flag (not automatically detected)
|
|
366
97
|
- Hover/focus states extracted from CSS (not fully interactive)
|
|
367
98
|
- Canvas/WebGL-rendered sites cannot be analyzed (e.g., Tesla, Apple Vision Pro demos)
|
|
368
99
|
- JavaScript-heavy sites require hydration time (8s initial + 4s stabilization)
|
|
369
100
|
- Some dynamically-loaded content may be missed
|
|
370
|
-
- Default viewport is 1920x1080 (use
|
|
371
|
-
|
|
372
|
-
## Architecture
|
|
373
|
-
|
|
374
|
-
```
|
|
375
|
-
dembrandt/
|
|
376
|
-
βββ index.js # CLI entry point, command handling
|
|
377
|
-
βββ lib/
|
|
378
|
-
β βββ extractors.js # Core extraction logic with stealth mode
|
|
379
|
-
β βββ display.js # Terminal output formatting
|
|
380
|
-
β βββ exporters.js # Export to Tailwind CSS config (NEW)
|
|
381
|
-
βββ output/ # Auto-saved JSON extractions (gitignored)
|
|
382
|
-
β βββ stripe.com/
|
|
383
|
-
β β βββ 2025-11-22T14-30-45.json
|
|
384
|
-
β β βββ 2025-11-22T15-12-33.json
|
|
385
|
-
β βββ github.com/
|
|
386
|
-
β βββ 2025-11-22T14-35-12.json
|
|
387
|
-
βββ package.json
|
|
388
|
-
βββ README.md
|
|
389
|
-
```
|
|
101
|
+
- Default viewport is 1920x1080 (use --mobile for 390x844 iPhone viewport)
|
|
390
102
|
|
|
391
103
|
## Ethics & Legality
|
|
392
104
|
|
|
393
105
|
Dembrandt extracts publicly available design information (colors, fonts, spacing) from website DOMs for analysis purposes. This falls under fair use in most jurisdictions (USA's DMCA Β§ 1201(f), EU Software Directive 2009/24/EC) when used for competitive analysis, documentation, or learning.
|
|
394
106
|
|
|
395
|
-
|
|
107
|
+
Legal: Analyzing public HTML/CSS is generally legal. Does not bypass protections or violate copyright. Check site ToS before mass extraction.
|
|
396
108
|
|
|
397
|
-
|
|
109
|
+
Ethical: Use for inspiration and analysis, not direct copying. Respect servers (no mass crawling), give credit to sources, be transparent about data origin.
|
|
398
110
|
|
|
399
111
|
## Contributing
|
|
400
112
|
|
|
401
|
-
|
|
113
|
+
Bugs you found? Weird websites that make it cry? Pull requests (even one-liners make me happy)?
|
|
402
114
|
|
|
403
|
-
|
|
404
|
-
- Example URLs that demonstrate the problem
|
|
405
|
-
- Expected vs actual behavior
|
|
115
|
+
Spam me in [Issues](https://github.com/dembrandt/dembrandt/issues) or PRs. I reply to everything.
|
|
406
116
|
|
|
407
|
-
|
|
117
|
+
Let's keep the light alive together.
|
|
408
118
|
|
|
409
|
-
|
|
119
|
+
@thevangelist
|
|
410
120
|
|
|
411
|
-
|
|
121
|
+
---
|
|
412
122
|
|
|
413
|
-
|
|
414
|
-
- [x] Mobile viewport support (via `--mobile` flag)
|
|
415
|
-
- [x] Clickable terminal links for modern terminals
|
|
416
|
-
- [?] Figma integration branch: `figma-integration`
|
|
417
|
-
- [ ] Animation/transition detection
|
|
418
|
-
- [ ] Interactive state capture (hover, focus, active)
|
|
419
|
-
- [ ] Multi-page analysis
|
|
420
|
-
- [ ] Configuration file support
|
|
123
|
+
MIT β do whatever you want with it.
|
package/index.js
CHANGED
|
@@ -10,55 +10,49 @@
|
|
|
10
10
|
import { program } from "commander";
|
|
11
11
|
import chalk from "chalk";
|
|
12
12
|
import ora from "ora";
|
|
13
|
-
import { chromium } from "playwright";
|
|
13
|
+
import { chromium } from "playwright-core";
|
|
14
14
|
import { extractBranding } from "./lib/extractors.js";
|
|
15
15
|
import { displayResults } from "./lib/display.js";
|
|
16
|
+
import { toW3CFormat } from "./lib/w3c-exporter.js";
|
|
16
17
|
import { writeFileSync, mkdirSync } from "fs";
|
|
17
18
|
import { join } from "path";
|
|
18
19
|
|
|
19
20
|
program
|
|
20
21
|
.name("dembrandt")
|
|
21
22
|
.description("Extract design tokens from any website")
|
|
22
|
-
.version("
|
|
23
|
+
.version("0.5.0")
|
|
23
24
|
.argument("<url>")
|
|
24
25
|
.option("--json-only", "Output raw JSON")
|
|
25
|
-
.option("-
|
|
26
|
-
.option("--
|
|
26
|
+
.option("--save-output", "Save JSON file to output folder")
|
|
27
|
+
.option("--dtcg", "Export in W3C Design Tokens (DTCG) format")
|
|
27
28
|
.option("--dark-mode", "Extract colors from dark mode")
|
|
28
29
|
.option("--mobile", "Extract from mobile viewport")
|
|
29
30
|
.option("--slow", "3x longer timeouts for slow-loading sites")
|
|
31
|
+
.option("--no-sandbox", "Disable browser sandbox (needed for Docker/CI)")
|
|
30
32
|
.action(async (input, opts) => {
|
|
31
33
|
let url = input;
|
|
32
|
-
if (!url.
|
|
34
|
+
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
|
35
|
+
url = "https://" + url;
|
|
36
|
+
}
|
|
33
37
|
|
|
34
38
|
const spinner = ora("Starting extraction...").start();
|
|
35
39
|
let browser = null;
|
|
36
40
|
|
|
37
41
|
try {
|
|
38
|
-
let useHeaded =
|
|
42
|
+
let useHeaded = false;
|
|
39
43
|
let result;
|
|
40
44
|
|
|
41
45
|
while (true) {
|
|
42
|
-
spinner.text = `Launching browser (${
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
spinner.text = `Launching browser (${useHeaded ? "visible" : "headless"
|
|
47
|
+
} mode)`;
|
|
48
|
+
const launchArgs = ["--disable-blink-features=AutomationControlled"];
|
|
49
|
+
if (opts.noSandbox) {
|
|
50
|
+
launchArgs.push("--no-sandbox", "--disable-setuid-sandbox");
|
|
51
|
+
}
|
|
45
52
|
browser = await chromium.launch({
|
|
46
53
|
headless: !useHeaded,
|
|
47
|
-
args:
|
|
48
|
-
"--no-sandbox",
|
|
49
|
-
"--disable-setuid-sandbox",
|
|
50
|
-
"--disable-blink-features=AutomationControlled",
|
|
51
|
-
],
|
|
54
|
+
args: launchArgs,
|
|
52
55
|
});
|
|
53
|
-
if (opts.debug) {
|
|
54
|
-
console.log(
|
|
55
|
-
chalk.dim(
|
|
56
|
-
` β Browser launched in ${
|
|
57
|
-
useHeaded ? "visible" : "headless"
|
|
58
|
-
} mode`
|
|
59
|
-
)
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
56
|
|
|
63
57
|
try {
|
|
64
58
|
result = await extractBranding(url, spinner, browser, {
|
|
@@ -93,8 +87,11 @@ program
|
|
|
93
87
|
|
|
94
88
|
console.log();
|
|
95
89
|
|
|
96
|
-
//
|
|
97
|
-
|
|
90
|
+
// Convert to W3C format if requested
|
|
91
|
+
const outputData = opts.dtcg ? toW3CFormat(result) : result;
|
|
92
|
+
|
|
93
|
+
// Save JSON output if --save-output or --dtcg is specified
|
|
94
|
+
if ((opts.saveOutput || opts.dtcg) && !opts.jsonOnly) {
|
|
98
95
|
try {
|
|
99
96
|
const domain = new URL(url).hostname.replace("www.", "");
|
|
100
97
|
const timestamp = new Date()
|
|
@@ -105,9 +102,10 @@ program
|
|
|
105
102
|
const outputDir = join(process.cwd(), "output", domain);
|
|
106
103
|
mkdirSync(outputDir, { recursive: true });
|
|
107
104
|
|
|
108
|
-
const
|
|
105
|
+
const suffix = opts.dtcg ? '.tokens' : '';
|
|
106
|
+
const filename = `${timestamp}${suffix}.json`;
|
|
109
107
|
const filepath = join(outputDir, filename);
|
|
110
|
-
writeFileSync(filepath, JSON.stringify(
|
|
108
|
+
writeFileSync(filepath, JSON.stringify(outputData, null, 2));
|
|
111
109
|
|
|
112
110
|
console.log(
|
|
113
111
|
chalk.dim(
|
|
@@ -125,29 +123,16 @@ program
|
|
|
125
123
|
|
|
126
124
|
// Output to terminal
|
|
127
125
|
if (opts.jsonOnly) {
|
|
128
|
-
console.log(JSON.stringify(
|
|
126
|
+
console.log(JSON.stringify(outputData, null, 2));
|
|
129
127
|
} else {
|
|
130
128
|
console.log();
|
|
131
|
-
displayResults(result
|
|
129
|
+
displayResults(result);
|
|
132
130
|
}
|
|
133
131
|
} catch (err) {
|
|
134
132
|
spinner.fail("Failed");
|
|
135
133
|
console.error(chalk.red("\nβ Extraction failed"));
|
|
136
134
|
console.error(chalk.red(` Error: ${err.message}`));
|
|
137
135
|
console.error(chalk.dim(` URL: ${url}`));
|
|
138
|
-
|
|
139
|
-
if (opts.debug && err.stack) {
|
|
140
|
-
console.error(chalk.dim("\nStack trace:"));
|
|
141
|
-
console.error(chalk.dim(err.stack));
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (!opts.debug) {
|
|
145
|
-
console.log(
|
|
146
|
-
chalk.hex('#FFB86C')(
|
|
147
|
-
"\nTip: Try with --debug flag for tough sites and detailed error logs"
|
|
148
|
-
)
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
136
|
process.exit(1);
|
|
152
137
|
} finally {
|
|
153
138
|
if (browser) await browser.close();
|