designlang 8.0.0 → 10.0.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.
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": "0.0.1",
3
+ "configurations": [
4
+ {
5
+ "name": "designlang-site",
6
+ "runtimeExecutable": "npm",
7
+ "runtimeArgs": ["--prefix", "website", "run", "dev"],
8
+ "port": 3000
9
+ }
10
+ ]
11
+ }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,81 @@
1
1
  # Changelog
2
2
 
3
+ ## [10.0.0] — 2026-04-22
4
+
5
+ **The Intent Release.** v9 captured *how* a site looks; v10 captures *what it is* — the semantic layer LLM agents need to rebuild a site faithfully, not just restyle a generic scaffold. Six new extractors, a multi-page crawl orchestrator, an optional smart-classifier LLM fallback, and a ready-to-paste prompt pack. 297/297 tests passing.
6
+
7
+ ### Added — extraction
8
+
9
+ - **Page Intent classifier** (`src/extractors/page-intent.js`) — labels the crawled URL as `landing` / `pricing` / `docs` / `blog` / `blog-post` / `product` / `about` / `dashboard` / `auth` / `legal`, with URL + title + meta + DOM-shape signals, a confidence score, and ranked alternates.
10
+ - **Section Roles** (`src/extractors/section-roles.js`) — annotates every semantic region with a role (`hero`, `feature-grid`, `logo-wall`, `stats`, `testimonial`, `pricing-table`, `faq`, `steps`, `comparison`, `gallery`, `bento`, `cta`, `footer`), extracts slot copy (headings, lede, CTA counts), and emits reading order.
11
+ - **Material Language** (`src/extractors/material-language.js`) — classifies the visual vocabulary (`glassmorphism` / `neumorphism` / `flat` / `brutalist` / `skeuomorphic` / `material-you` / `soft-ui` / `mixed`) from shadow complexity, backdrop-filter usage, saturation, and geometry.
12
+ - **Imagery Style** (`src/extractors/imagery-style.js`) — fingerprints the imagery (`photography` / `3d-render` / `isometric` / `flat-illustration` / `gradient-mesh` / `icon-only` / `screenshot` / `mixed`), plus dominant aspect ratio and image-radius profile.
13
+ - **Component Library detector** (`src/extractors/component-library.js`) — identifies shadcn/ui, Radix, Headless UI, MUI, Chakra, Mantine, Ant Design, Bootstrap, HeroUI/NextUI, Tailwind UI, Vuetify, or plain Tailwind, with evidence and alternates.
14
+ - **Logo extractor** (`src/extractors/logo.js`) — pulls the site's logo (SVG source or `<img>` bytes) and samples clearspace; writes `*-logo.svg` or `.png` plus `*-logo.json`.
15
+
16
+ ### Added — orchestration
17
+
18
+ - **Multi-page crawl** (`src/multipage.js`) — `--full` or `--pages <n>` auto-discovers canonical pages from nav (pricing/docs/blog/about/product), runs the full extractor pipeline on each, and emits a cross-page consistency report with shared tokens, per-page uniques, and pairwise Jaccard scores.
19
+ - **Smart classifier fallback** (`src/classifiers/smart.js`) — opt-in `--smart` flag routes low-confidence classifications through the OpenAI or Anthropic API (via `OPENAI_API_KEY` / `ANTHROPIC_API_KEY`). Gracefully no-ops when no key is set. Zero-dep — uses global `fetch`.
20
+
21
+ ### Added — LLM-native outputs
22
+
23
+ - **Prompt pack** (`src/formatters/prompt-pack.js`) — writes a `*-prompts/` directory with `v0.txt`, `lovable.txt`, `cursor.md`, `claude-artifacts.md`, and atomic `recipe-<component>.md` cards. Tokens, section order, voice, and library guidance are all inlined so one paste is enough.
24
+ - **Markdown sections** (`src/formatters/markdown.js`) — adds Page Intent, Section Roles, Material Language, Imagery Style, Component Library, and (when `--full`) Multi-Page Map sections to `*-design-language.md`.
25
+
26
+ ### Added — output files
27
+
28
+ - `*-intent.json` — page-type + section-role map
29
+ - `*-visual-dna.json` — material language + imagery style
30
+ - `*-library.json` — component library detection + evidence
31
+ - `*-logo.svg` | `*-logo.png` + `*-logo.json` (with `--full`)
32
+ - `*-multipage.json` — per-page design languages + consistency (with `--full` / `--pages`)
33
+ - `*-prompts/` — prompt pack directory
34
+
35
+ ### New CLI flags
36
+
37
+ - `--smart` — enable optional LLM refinement for low-confidence classifiers
38
+ - `--pages <n>` — explicitly crawl N canonical pages
39
+ - `--no-prompts` — skip the prompt-pack directory
40
+
41
+ ### Tests
42
+
43
+ - `tests/v10-features.test.js` — 15 new subtests covering page intent, section roles, component library, material language, imagery style, multi-page discovery, cross-page consistency, and prompt pack. Full suite: 297 passing.
44
+
45
+ ## [9.0.0] — 2026-04-21
46
+
47
+ **The Motion & Voice release.** Six new capabilities that push designlang past "extract the paint" and into "extract the *feel*, the *anatomy*, and the *voice*." No competing tool does any of these. All work ships with tests (282/282 passing).
48
+
49
+ ### Added — extraction
50
+
51
+ - **Motion language extractor** (`src/extractors/motion.js`) — easings are classified into families (`ease-in`, `ease-in-out`, `ease-out`, `linear`, `steps`, `spring`, `custom`) via cubic-bezier geometry, durations are bucketed into a named scale (`instant`/`xs`/`sm`/`md`/`lg`/`xl`/`xxl`), spring/overshoot cubic-beziers are surfaced, scroll-linked animation usage is detected via `animation-timeline` / `view-timeline-name` / `scroll-timeline-name`, and each `@keyframes` rule is classified by kind (`slide-x`, `slide-y`, `fade`, `reveal`, `rotate`, `scale`, `pulse`, `custom`). A one-word `feel` fingerprint (`springy`/`responsive`/`smooth`/`mechanical`/`mixed`) summarizes the whole system.
52
+ - **Motion tokens formatter** (`src/formatters/motion-tokens.js`) — emits `*-motion-tokens.json` in a DTCG-flavored shape with `$type: duration` / `$type: cubicBezier`.
53
+ - **Component Anatomy v2** (`src/extractors/component-anatomy.js`) — groups components by variant-class hints, infers slot roles (icon / label / badge / heading / media / footer), builds a variant × size × state matrix, captures sample button labels, and emits typed React stubs via `formatAnatomyStubs`. Output: `*-anatomy.tsx`.
54
+ - **Brand voice extractor** (`src/extractors/voice.js`) — classifies tone (friendly / formal / technical / playful / neutral) from lexical markers, picks pronoun posture (`we→you`, `you-only`, `we-only`, `third-person`), detects heading style, top CTA verbs, and microcopy patterns. Output: `*-voice.json`.
55
+ - **Crawler extensions** (`src/crawler.js`) — per-element `animation-timeline`, view/scroll timeline names; per-candidate `text`, `slots[]`, `disabled`, `variantHint`, `sizeHint` to feed anatomy + voice.
56
+
57
+ ### Added — new commands
58
+
59
+ - **`designlang lint <file>`** — audits DTCG / flat-JSON / CSS-vars token files for color sprawl, spacing-scale drift, radius/shadow bloat, and WCAG AA fg/bg contrast. Exits non-zero on `error`-level findings. CI-ready.
60
+ - **`designlang drift <url> --tokens <file>`** — compares local tokens against a live site, reports `in-sync` / `minor-drift` / `notable-drift` / `major-drift` with a drift ratio. `--fail-on <level>` controls CI exit code.
61
+ - **`designlang visual-diff <before> <after>`** — single-file HTML side-by-side report with embedded base64 screenshots, file-size deltas, and a changed-color-tokens table.
62
+
63
+ ### Added — markdown output
64
+
65
+ Three new sections in `*-design-language.md`: **Motion Language**, **Component Anatomy**, **Brand Voice**.
66
+
67
+ ### Changed
68
+
69
+ - Default extraction now writes **11+ files** (up from 8): `*-motion-tokens.json`, `*-anatomy.tsx` (when candidates exist), `*-voice.json`.
70
+ - `bin/design-extract.js` version → `9.0.0`.
71
+ - `package.json` — description refreshed; new keywords: `motion`, `animation`, `component-anatomy`, `brand-voice`, `token-lint`, `visual-diff`.
72
+ - README: "What's New in v9" hero block, new feature sections 24-29, new CLI entries (`lint`, `drift`, `visual-diff`).
73
+
74
+ ### Tests
75
+
76
+ - New `tests/v9-features.test.js` — 7 suites, 21 assertions across motion, anatomy, voice, and lint.
77
+ - Full suite: **282/282 passing**.
78
+
3
79
  ## [8.0.0] — 2026-04-20
4
80
 
5
81
  A credibility-and-distribution release. Three reliability bugs that hurt trust on real sites are fixed; three DX flags close the most-requested CLI gaps; five new surfaces (VS Code, Raycast, Figma, GitHub Actions, MCP registry) ship alongside.
package/README.md CHANGED
@@ -15,9 +15,34 @@
15
15
  <img src="designlang.png" alt="designlang in action" width="100%">
16
16
  </p>
17
17
 
18
- **designlang** crawls any website with a headless browser, extracts every computed style from the live DOM, and generates **8 output files** — including an AI-optimized markdown file, visual HTML preview, Tailwind config, React theme, shadcn/ui theme, Figma variables, W3C design tokens, and CSS custom properties.
18
+ [![designlang on npm](https://pkgfolio.vercel.app/embed/pkg/designlang?v=2)](https://www.npmjs.com/package/designlang)
19
19
 
20
- But unlike every other tool out there, it also extracts **layout patterns** (grids, flexbox, containers), captures **responsive behavior** across 4 breakpoints, records **interaction states** (hover, focus, active), scores **WCAG accessibility**, and lets you **compare multiple brands** or **sync live sites to local tokens**.
20
+ **designlang** crawls any website with a headless browser, extracts every computed style from the live DOM, and generates **17+ output files** — including an AI-optimized markdown file, visual HTML preview, Tailwind config, React theme, shadcn/ui theme, Figma variables, W3C design tokens, CSS custom properties, motion tokens, typed component anatomy stubs, a brand voice summary, **page intent + section roles**, **visual DNA** (material language + imagery style), **component library detection**, a **logo file**, a **multi-page consistency report**, and a **prompt pack** of ready-to-paste prompts for v0, Lovable, Cursor, and Claude Artifacts.
21
+
22
+ But unlike every other tool out there, it also extracts **layout patterns** (grids, flexbox, containers), **motion language** (durations, easings, springs, scroll-linked animations), **component anatomy** (slots, variant × size × state matrices), **brand voice** (tone, CTA verbs, heading style), captures **responsive behavior** across 4 breakpoints, records **interaction states** (hover, focus, active), scores **WCAG accessibility**, lints your own token files, and lets you **drift-check a codebase against a live site**, **visual-diff two URLs**, **compare multiple brands**, or **sync live sites to local tokens**.
23
+
24
+ ## What's New in v10 — The Intent Release
25
+
26
+ Everything else captures *how* a site looks. v10 captures *what it is* — the semantic signal an LLM needs to rebuild a site faithfully instead of restyling a generic scaffold.
27
+
28
+ - **Page Intent** — classifier labels the URL as `landing` / `pricing` / `docs` / `blog` / `blog-post` / `product` / `about` / `dashboard` / `auth` / `legal`, with a confidence score and rival alternates. URL + title + meta + DOM-shape signals. Heuristic-only by default; opt into `--smart` for LLM refinement.
29
+ - **Section Roles** — every semantic region gets a role (`hero`, `feature-grid`, `logo-wall`, `stats`, `testimonial`, `pricing-table`, `faq`, `steps`, `comparison`, `gallery`, `bento`, `cta`, `footer`), plus reading order and extracted slot copy (headings, lede, CTA counts).
30
+ - **Multi-Page Crawl** — `--full` (or `--pages <n>`) auto-discovers the site's own canonical pages from its nav (pricing/docs/blog/about/product) and runs the full pipeline on each, then emits a cross-page consistency report — shared tokens, per-page uniques, and pairwise Jaccard scores. LLMs get a real design language, not just a homepage snapshot.
31
+ - **Material Language** — classifies the visual vocabulary as `glassmorphism` / `neumorphism` / `flat` / `brutalist` / `skeuomorphic` / `material-you` / `soft-ui` / `mixed` from shadow complexity, backdrop-filter usage, saturation, and geometry.
32
+ - **Imagery Style** — fingerprints the images: `photography` / `3d-render` / `isometric` / `flat-illustration` / `gradient-mesh` / `icon-only` / `screenshot` / `mixed`, plus dominant aspect ratio and image-radius profile.
33
+ - **Component Library Detection** — identifies `shadcn/ui`, `radix-ui`, `headlessui`, `mui`, `chakra-ui`, `mantine`, `ant-design`, `bootstrap`, `heroui`, `tailwind-ui`, `vuetify`, or plain `tailwindcss`, with evidence and alternates.
34
+ - **Logo Extraction** — `--full` writes `*-logo.svg` (or `.png`) plus `*-logo.json` with dimensions, aspect, and sampled clearspace.
35
+ - **Prompt Pack** — a `*-prompts/` directory with `v0.txt`, `lovable.txt`, `cursor.md`, `claude-artifacts.md`, and atomic `recipe-<component>.md` cards — tokens, section order, voice, and library inlined so one paste is enough.
36
+ - **`--smart` mode** — when a heuristic classifier returns low confidence, fall back to a small LLM call (uses `OPENAI_API_KEY` or `ANTHROPIC_API_KEY` from env). Completely optional — no key, no behavior change.
37
+
38
+ ## What's New in v9 — The Motion & Voice Release
39
+
40
+ - **Motion Language** — durations bucketed into semantic tokens (`instant`/`xs`/`sm`/`md`/`lg`/`xl`), easings classified into families (ease-out, spring-overshoot, steps), scroll-linked animation detection (`animation-timeline`, `view-timeline-name`), keyframe kind classification (slide / fade / reveal / rotate / scale / pulse), and a `feel` fingerprint — *springy*, *responsive*, *smooth*, *mechanical*, or *mixed*.
41
+ - **Component Anatomy v2** — every component cluster is now an *anatomy tree* with slots (label, icon, badge, heading, media), variant × size × state matrices, and an emitted `*-anatomy.tsx` file of typed React stubs you can wire into your design system.
42
+ - **Brand Voice** — extracts tone (friendly / formal / technical / playful / neutral), pronoun posture (`we→you` / `you-only` / `we-only` / `third-person`), heading style (Title Case / Sentence case / all-lowercase), top CTA verbs, and a microcopy inventory. Feeds LLMs the *voice*, not just the paint.
43
+ - **`designlang lint`** — audit your own `design-tokens.json` (DTCG or flat) or `variables.css` for color sprawl, spacing-scale drift, radius/shadow bloat, and WCAG fg/bg contrast fails. Exits non-zero on errors — CI-ready.
44
+ - **`designlang drift`** — point at a live site, pass your local token file, and get a verdict: `in-sync` / `minor-drift` / `notable-drift` / `major-drift`. Integrates cleanly with the existing GitHub Action.
45
+ - **`designlang visual-diff`** — capture two URLs side-by-side and emit a single-file HTML report with component screenshots, file-size deltas, and changed color tokens. No heavy pixel-diff dependencies — runs in pure Node + Playwright.
21
46
 
22
47
  ## Quick Start
23
48
 
@@ -31,7 +56,7 @@ Get everything at once:
31
56
  npx designlang https://stripe.com --full
32
57
  ```
33
58
 
34
- ## What You Get (8 Files)
59
+ ## What You Get (11+ Files)
35
60
 
36
61
  | File | What it is |
37
62
  |------|------------|
@@ -43,6 +68,9 @@ npx designlang https://stripe.com --full
43
68
  | `*-figma-variables.json` | Figma Variables import (with dark mode support) |
44
69
  | `*-theme.js` | React/CSS-in-JS theme (Chakra, Stitches, Vanilla Extract) |
45
70
  | `*-shadcn-theme.css` | shadcn/ui globals.css variables |
71
+ | `*-motion-tokens.json` | **(v9)** Motion tokens — durations, easings, springs, scroll-linked flag |
72
+ | `*-anatomy.tsx` | **(v9)** Typed React stubs for every detected component + variants |
73
+ | `*-voice.json` | **(v9)** Brand voice fingerprint — tone, CTA verbs, heading style |
46
74
 
47
75
  The markdown output has **19 sections**: Color Palette, Typography, Spacing, Border Radii, Box Shadows, CSS Custom Properties, Breakpoints, Transitions & Animations, Component Patterns (with full CSS snippets), Layout System, Responsive Design, Interaction States, Accessibility (WCAG 2.1), Gradients, Z-Index Map, SVG Icons, Font Files, Image Style Patterns, and Quick Start.
48
76
 
@@ -319,7 +347,112 @@ A Manifest-v3 popup lives in [`chrome-extension/`](chrome-extension/). One click
319
347
  - **Install:** toggle developer mode at `chrome://extensions`, click *Load unpacked*, pick the `chrome-extension/` folder.
320
348
  - **Firefox + Edge** work with the same MV3 manifest.
321
349
 
322
- ### 23. Better Auth + Network Control (NEW in v7.1)
350
+ ### 24. Motion Language (NEW in v9)
351
+
352
+ Extracts the full motion fingerprint, not just transition strings:
353
+
354
+ ```bash
355
+ designlang https://linear.app
356
+ # emits linear-app-motion-tokens.json
357
+ ```
358
+
359
+ ```
360
+ Motion: feel = springy, 2 spring easings, scroll-linked = yes
361
+ Durations: instant (80ms), xs (150ms), sm (220ms), md (380ms)
362
+ Easings: ease-out (61%), spring-overshoot (18%), ease-in-out (21%)
363
+ Keyframes: fade-up (slide-y, used 18x), scale-in (reveal, used 4x)
364
+ ```
365
+
366
+ ### 25. Component Anatomy v2 (NEW in v9)
367
+
368
+ Every detected component becomes an anatomy tree with typed React stubs:
369
+
370
+ ```bash
371
+ designlang https://stripe.com
372
+ # emits stripe-com-anatomy.tsx
373
+ ```
374
+
375
+ ```tsx
376
+ export interface ButtonProps {
377
+ variant?: 'primary' | 'secondary' | 'ghost';
378
+ size?: 'sm' | 'md' | 'lg';
379
+ disabled?: boolean;
380
+ leadingIcon?: React.ReactNode;
381
+ badge?: React.ReactNode;
382
+ children?: React.ReactNode;
383
+ }
384
+ ```
385
+
386
+ ### 26. Brand Voice (NEW in v9)
387
+
388
+ Pulls the voice alongside the visual:
389
+
390
+ ```bash
391
+ designlang https://vercel.com
392
+ # emits vercel-com-voice.json + a Brand Voice section in the markdown
393
+ ```
394
+
395
+ ```
396
+ Tone: technical · Pronoun: we→you · Headings: Sentence case (tight)
397
+ Top CTA verbs: start (14), get (8), deploy (5), try (3)
398
+ Sample headings:
399
+ > Develop. Preview. Ship.
400
+ > The React framework for the web.
401
+ ```
402
+
403
+ ### 27. `designlang lint` — Token Quality Linter (NEW in v9)
404
+
405
+ Audit your own token file with the same rules the scorer runs against live sites:
406
+
407
+ ```bash
408
+ designlang lint ./src/tokens/design-tokens.json
409
+ ```
410
+
411
+ ```
412
+ Score: 74/100 Grade: C Tokens: 126
413
+
414
+ colorDiscipline ██████████████░░░░░░ 72
415
+ spacingSystem ████████████████░░░░ 84
416
+ borderRadii ████████████░░░░░░░░ 60
417
+ shadows ██████████░░░░░░░░░░ 50
418
+ accessibility █████████████████░░░ 88
419
+
420
+ WARN [color-sprawl] 3 near-duplicate color pair(s) within 8 RGB units
421
+ ERROR [contrast-wcag-aa] 2 fg/bg pair(s) fail WCAG AA (4.5:1)
422
+ ```
423
+
424
+ Exits non-zero on any `error`-level finding — drop into CI.
425
+
426
+ ### 28. `designlang drift` — Codebase ↔ Live Site Sync Check (NEW in v9)
427
+
428
+ Point at a deployed site, pass your local tokens, and get a verdict:
429
+
430
+ ```bash
431
+ designlang drift https://yourapp.com --tokens ./src/tokens.json --tolerance 8
432
+ ```
433
+
434
+ ```
435
+ Verdict: notable-drift (drift ratio: 0.24)
436
+
437
+ | token | local | nearest live | Δ |
438
+ |----------------|----------|--------------------|----|
439
+ | color.primary | #4338CA | #5B4CF5 (primary) | 22 |
440
+ | color.border | #D4D4D8 | #E5E5EA (surface) | 18 |
441
+ ```
442
+
443
+ Configurable `--fail-on <level>` for CI: `minor-drift` / `notable-drift` / `major-drift`.
444
+
445
+ ### 29. `designlang visual-diff` — Two-URL Side-by-Side (NEW in v9)
446
+
447
+ Capture screenshots + token deltas for two URLs in a single self-contained HTML report:
448
+
449
+ ```bash
450
+ designlang visual-diff https://staging.app.com https://app.com
451
+ ```
452
+
453
+ Emits `visual-diff-<timestamp>.html` with embedded images (base64), file-size deltas, and a changed-color-tokens table. Nothing else to serve — just open the file.
454
+
455
+ ### 30. Better Auth + Network Control (v7.1)
323
456
 
324
457
  Extracting from authenticated, self-signed, or non-default environments now takes one flag:
325
458
 
@@ -407,15 +540,18 @@ Options:
407
540
  --verbose Detailed progress output
408
541
 
409
542
  Commands:
410
- apply <url> Extract and apply design directly to your project
411
- clone <url> Generate a working Next.js starter from extracted design
412
- score <url> Rate design quality (7 categories, A-F, bar chart)
413
- watch <url> Monitor for design changes on interval
414
- diff <urlA> <urlB> Compare two sites' design languages
415
- brands <urls...> Multi-brand comparison matrix
416
- sync <url> Sync local tokens with live site
417
- history <url> View design change history
418
- mcp Launch stdio MCP server (--output-dir <dir>)
543
+ apply <url> Extract and apply design directly to your project
544
+ clone <url> Generate a working Next.js starter from extracted design
545
+ score <url> Rate design quality (7 categories, A-F, bar chart)
546
+ watch <url> Monitor for design changes on interval
547
+ diff <urlA> <urlB> Compare two sites' design languages
548
+ brands <urls...> Multi-brand comparison matrix
549
+ sync <url> Sync local tokens with live site
550
+ history <url> View design change history
551
+ mcp Launch stdio MCP server (--output-dir <dir>)
552
+ lint <file> (v9) Audit a local token file (.json/.css) — CI-ready
553
+ drift <url> --tokens <file> (v9) Check local tokens for drift against a live site
554
+ visual-diff <before> <after> (v9) Side-by-side HTML diff of two URLs
419
555
  ```
420
556
 
421
557
  ## Example Output
@@ -506,3 +642,5 @@ See [CONTRIBUTING.md](CONTRIBUTING.md). PRs welcome!
506
642
  ## License
507
643
 
508
644
  [MIT](LICENSE) - Manav Arya Singh
645
+
646
+
@@ -6,6 +6,10 @@ import { resolve, join } from 'path';
6
6
  import chalk from 'chalk';
7
7
  import ora from 'ora';
8
8
  import { extractDesignLanguage } from '../src/index.js';
9
+ import { refineWithSmart } from '../src/classifiers/smart.js';
10
+ import { crawlCanonicalPages } from '../src/multipage.js';
11
+ import { extractLogo } from '../src/extractors/logo.js';
12
+ import { buildPromptPack } from '../src/formatters/prompt-pack.js';
9
13
  import { formatMarkdown } from '../src/formatters/markdown.js';
10
14
  import { formatTokens } from '../src/formatters/tokens.js';
11
15
  import { formatDtcgTokens } from '../src/formatters/dtcg-tokens.js';
@@ -48,7 +52,7 @@ const program = new Command();
48
52
  program
49
53
  .name('designlang')
50
54
  .description('Extract the complete design language from any website')
51
- .version('8.0.0');
55
+ .version('10.0.0');
52
56
 
53
57
  // ── Main command: extract ──────────────────────────────────────
54
58
  program
@@ -77,6 +81,9 @@ program
77
81
  .option('--tokens-legacy', 'Emit pre-v7 flat token JSON (backward compat)')
78
82
  .option('--platforms <csv>', 'Additional platforms: web,ios,android,flutter,wordpress,all (web is always emitted)', 'web')
79
83
  .option('--emit-agent-rules', 'Emit Cursor/Claude Code/generic agent rules')
84
+ .option('--smart', 'use optional LLM fallback when heuristic classifiers have low confidence (needs OPENAI_API_KEY or ANTHROPIC_API_KEY)')
85
+ .option('--pages <n>', 'crawl N canonical pages (pricing/docs/blog/about/product) in addition to the homepage', parseInt)
86
+ .option('--no-prompts', 'skip writing the prompt-pack directory')
80
87
  .option('--json', 'output raw JSON to stdout (for CI/CD)')
81
88
  .option('--json-pretty', 'output formatted JSON to stdout')
82
89
  .option('--no-history', 'skip saving to history')
@@ -173,6 +180,65 @@ program
173
180
  design.interactions = await captureInteractions(url, { width: merged.width, height: parseInt(merged.height) || 800, wait: merged.wait });
174
181
  }
175
182
 
183
+ // v10: optional LLM refinement for low-confidence classifiers.
184
+ if (merged.smart) {
185
+ spinner.text = 'Refining classifiers with smart mode...';
186
+ try {
187
+ const refined = await refineWithSmart({
188
+ enabled: true,
189
+ rawData: design._raw,
190
+ design,
191
+ pageIntent: design.pageIntent,
192
+ sectionRoles: design.sectionRoles,
193
+ materialLanguage: design.materialLanguage,
194
+ componentLibrary: design.componentLibrary,
195
+ });
196
+ if (refined.applied) {
197
+ if (refined.updates?.pageIntent) design.pageIntent = { ...design.pageIntent, ...refined.updates.pageIntent };
198
+ if (refined.updates?.materialLanguage) design.materialLanguage = { ...design.materialLanguage, ...refined.updates.materialLanguage };
199
+ if (refined.updates?.componentLibrary) design.componentLibrary = { ...design.componentLibrary, ...refined.updates.componentLibrary };
200
+ design._smart = { provider: refined.provider, errors: refined.errors };
201
+ } else {
202
+ design._smart = { skipped: refined.reason };
203
+ }
204
+ } catch (e) { design._smart = { error: e.message }; }
205
+ }
206
+
207
+ // v10: logo extraction via a fresh Playwright session.
208
+ if (merged.full || merged.screenshots) {
209
+ spinner.text = 'Extracting logo...';
210
+ try {
211
+ const { chromium } = await import('playwright');
212
+ const browser = await chromium.launch({ headless: true, ...(merged.systemChrome && { channel: 'chrome' }) });
213
+ const ctx = await browser.newContext({ viewport: { width: merged.width, height: parseInt(merged.height) || 800 } });
214
+ const lp = await ctx.newPage();
215
+ await lp.goto(url, { waitUntil: 'domcontentloaded', timeout: 20000 }).catch(() => {});
216
+ await lp.waitForLoadState('networkidle').catch(() => {});
217
+ mkdirSync(outDir, { recursive: true });
218
+ design.logo = await extractLogo(lp, outDir, prefix);
219
+ await browser.close();
220
+ } catch (e) { design.logo = { found: false, error: e.message }; }
221
+ }
222
+
223
+ // v10: multi-page canonical crawl (pricing/docs/blog/about/product).
224
+ const pagesArg = merged.pages != null ? merged.pages : (merged.full ? 5 : 0);
225
+ if (pagesArg > 0) {
226
+ spinner.text = `Crawling ${pagesArg} canonical pages...`;
227
+ try {
228
+ const mp = await crawlCanonicalPages({
229
+ homepageUrl: url,
230
+ homepageRawData: design._raw,
231
+ maxPages: pagesArg,
232
+ crawlerOptions: { width: merged.width, height: parseInt(merged.height) || 800 },
233
+ extract: (u, o) => extractDesignLanguage(u, o),
234
+ });
235
+ design.multiPage = mp;
236
+ } catch (e) { design.multiPage = { error: e.message }; }
237
+ }
238
+
239
+ // Drop the internal raw stash before JSON/output serialization.
240
+ delete design._raw;
241
+
176
242
  // JSON mode: output and exit
177
243
  if (jsonMode) {
178
244
  const output = opts.jsonPretty ? JSON.stringify(design, null, 2) : JSON.stringify(design);
@@ -221,6 +287,39 @@ program
221
287
  };
222
288
  files.push({ name: `${prefix}-mcp.json`, content: JSON.stringify(mcpPayload, null, 2), label: 'MCP companion' });
223
289
 
290
+ // v9: motion tokens + component anatomy stubs + voice
291
+ const { formatMotionTokens } = await import('../src/formatters/motion-tokens.js');
292
+ const { formatAnatomyStubs } = await import('../src/extractors/component-anatomy.js');
293
+ files.push({ name: `${prefix}-motion-tokens.json`, content: formatMotionTokens(design.motion), label: 'Motion Tokens' });
294
+ if ((design.componentAnatomy || []).length) {
295
+ files.push({ name: `${prefix}-anatomy.tsx`, content: formatAnatomyStubs(design.componentAnatomy), label: 'Component Anatomy (stubs)' });
296
+ }
297
+ files.push({ name: `${prefix}-voice.json`, content: JSON.stringify(design.voice || {}, null, 2), label: 'Brand Voice' });
298
+
299
+ // v10: page intent + section roles + visual DNA + component library + multi-page + prompt pack.
300
+ files.push({ name: `${prefix}-intent.json`, content: JSON.stringify({ pageIntent: design.pageIntent, sectionRoles: design.sectionRoles }, null, 2), label: 'Page Intent + Section Roles' });
301
+ files.push({ name: `${prefix}-visual-dna.json`, content: JSON.stringify({ materialLanguage: design.materialLanguage, imageryStyle: design.imageryStyle }, null, 2), label: 'Visual DNA' });
302
+ files.push({ name: `${prefix}-library.json`, content: JSON.stringify(design.componentLibrary || {}, null, 2), label: 'Component Library Detection' });
303
+ if (design.logo && design.logo.found) {
304
+ files.push({ name: `${prefix}-logo.json`, content: JSON.stringify(design.logo, null, 2), label: 'Logo Metadata' });
305
+ }
306
+ if (design.multiPage) {
307
+ files.push({ name: `${prefix}-multipage.json`, content: JSON.stringify(design.multiPage, null, 2), label: 'Multi-Page Crawl' });
308
+ }
309
+ if (merged.prompts !== false) {
310
+ const pack = buildPromptPack(design);
311
+ const promptsDir = join(outDir, `${prefix}-prompts`);
312
+ mkdirSync(promptsDir, { recursive: true });
313
+ writeFileSync(join(promptsDir, 'v0.txt'), pack['v0.txt'], 'utf-8');
314
+ writeFileSync(join(promptsDir, 'lovable.txt'), pack['lovable.txt'], 'utf-8');
315
+ writeFileSync(join(promptsDir, 'cursor.md'), pack['cursor.md'], 'utf-8');
316
+ writeFileSync(join(promptsDir, 'claude-artifacts.md'), pack['claude-artifacts.md'], 'utf-8');
317
+ for (const r of pack.recipes) {
318
+ const slug = r.name.replace(/[^a-z0-9]+/gi, '-').toLowerCase() || 'component';
319
+ writeFileSync(join(promptsDir, `recipe-${slug}.md`), r.content, 'utf-8');
320
+ }
321
+ }
322
+
224
323
  for (const file of files) {
225
324
  writeFileSync(join(outDir, file.name), file.content, 'utf-8');
226
325
  }
@@ -804,6 +903,86 @@ program
804
903
  }
805
904
  });
806
905
 
906
+ // ── Token lint (v9) ────────────────────────────────────────
907
+ program
908
+ .command('lint <file>')
909
+ .description('Audit a local token file (.json / .css) for color sprawl, scale drift, contrast fails')
910
+ .option('--json', 'emit machine-readable JSON')
911
+ .action(async (file, opts) => {
912
+ try {
913
+ const { lintTokens } = await import('../src/lint.js');
914
+ const r = lintTokens(resolve(file));
915
+ if (opts.json) { process.stdout.write(JSON.stringify(r, null, 2) + '\n'); return; }
916
+ console.log('');
917
+ console.log(chalk.bold(` designlang lint — ${file}`));
918
+ console.log(` Score: ${chalk.bold(r.score + '/100')} Grade: ${chalk.bold(r.grade)} Tokens: ${r.tokenCount}`);
919
+ console.log('');
920
+ for (const [k, v] of Object.entries(r.scorecard)) {
921
+ const bar = '█'.repeat(Math.round(v / 5)) + '░'.repeat(20 - Math.round(v / 5));
922
+ console.log(` ${k.padEnd(20)} ${bar} ${v}`);
923
+ }
924
+ console.log('');
925
+ for (const f of r.findings) {
926
+ const color = f.severity === 'error' ? chalk.red : f.severity === 'warn' ? chalk.yellow : chalk.cyan;
927
+ console.log(` ${color(f.severity.toUpperCase())} [${f.rule}] ${f.message}`);
928
+ }
929
+ if (!r.findings.length) console.log(chalk.green(' ✓ no issues found'));
930
+ console.log('');
931
+ process.exit(r.findings.some(f => f.severity === 'error') ? 1 : 0);
932
+ } catch (err) {
933
+ process.stderr.write(chalk.red(`\n Error: ${err.message}\n\n`));
934
+ process.exit(1);
935
+ }
936
+ });
937
+
938
+ // ── Drift (v9) ─────────────────────────────────────────────
939
+ program
940
+ .command('drift <url>')
941
+ .description('Compare local tokens against a live site and report drift (CI-friendly)')
942
+ .requiredOption('--tokens <file>', 'local tokens file (.json or .css)')
943
+ .option('--tolerance <n>', 'color distance tolerance (0-50)', parseInt, 8)
944
+ .option('--fail-on <level>', 'exit non-zero on: minor-drift | notable-drift | major-drift', 'notable-drift')
945
+ .option('--json', 'emit machine-readable JSON')
946
+ .action(async (url, opts) => {
947
+ if (!url.startsWith('http')) url = `https://${url}`;
948
+ validateUrl(url);
949
+ try {
950
+ const { checkDrift, formatDriftMarkdown } = await import('../src/drift.js');
951
+ const r = await checkDrift(url, { tokens: resolve(opts.tokens), tolerance: opts.tolerance });
952
+ if (opts.json) { process.stdout.write(JSON.stringify(r, null, 2) + '\n'); }
953
+ else { console.log('\n' + formatDriftMarkdown(r) + '\n'); }
954
+ const order = ['in-sync', 'minor-drift', 'notable-drift', 'major-drift'];
955
+ if (order.indexOf(r.verdict) >= order.indexOf(opts.failOn)) process.exit(1);
956
+ } catch (err) {
957
+ process.stderr.write(chalk.red(`\n Error: ${err.message}\n\n`));
958
+ process.exit(1);
959
+ }
960
+ });
961
+
962
+ // ── Visual diff (v9) ───────────────────────────────────────
963
+ program
964
+ .command('visual-diff <before> <after>')
965
+ .description('Side-by-side HTML diff of two URLs with screenshots + token changes')
966
+ .option('-o, --out <dir>', 'output directory', './design-extract-output')
967
+ .action(async (before, after, opts) => {
968
+ if (!before.startsWith('http')) before = `https://${before}`;
969
+ if (!after.startsWith('http')) after = `https://${after}`;
970
+ validateUrl(before); validateUrl(after);
971
+ const spinner = ora('Capturing before + after').start();
972
+ try {
973
+ const { visualDiff, formatVisualDiffHtml } = await import('../src/visual-diff.js');
974
+ const r = await visualDiff({ beforeUrl: before, afterUrl: after });
975
+ const html = formatVisualDiffHtml(r);
976
+ mkdirSync(resolve(opts.out), { recursive: true });
977
+ const path = join(resolve(opts.out), `visual-diff-${Date.now()}.html`);
978
+ writeFileSync(path, html, 'utf8');
979
+ spinner.succeed(`Visual diff written → ${path}`);
980
+ } catch (err) {
981
+ spinner.fail(err.message);
982
+ process.exit(1);
983
+ }
984
+ });
985
+
807
986
  // ── MCP server command ─────────────────────────────────────
808
987
  program
809
988
  .command('mcp')
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "designlang",
3
- "version": "8.0.0",
4
- "description": "Extract the complete design language from any website — colors, typography, spacing, shadows, and more. Outputs AI-optimized markdown, W3C design tokens, Tailwind config, and CSS variables.",
3
+ "version": "10.0.0",
4
+ "description": "Extract the complete design language from any website — colors, typography, spacing, shadows, motion, component anatomy, brand voice, page intent, section roles, material language, component library, imagery style, and logo. Outputs AI-optimized markdown, W3C design tokens, motion tokens, typed component stubs, Tailwind config, and ready-to-paste v0 / Lovable / Cursor / Claude-Artifacts prompts.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "designlang": "./bin/design-extract.js"
@@ -38,7 +38,13 @@
38
38
  "vue",
39
39
  "svelte",
40
40
  "json",
41
- "ci-cd"
41
+ "ci-cd",
42
+ "motion",
43
+ "animation",
44
+ "component-anatomy",
45
+ "brand-voice",
46
+ "token-lint",
47
+ "visual-diff"
42
48
  ],
43
49
  "author": "masyv",
44
50
  "license": "MIT"