Commit Graph

57 Commits

Author SHA1 Message Date
Manav Arya Singh 602722d0cd refactor(theatre): relocate screencast util to src/ (core layer, used by crawler) 2026-06-25 11:24:58 +04:00
Manav Arya Singh 8d06697eac feat(clone): add --fidelity pass + fix -o/--out across clone/verify/visual-diff
Two things:

1. clone --fidelity: after generating the Next.js starter, rebuild its
   components from the extracted tokens, pixel-diff vs the live site, and write
   FIDELITY.md + fidelity.json + fidelity-card.svg + a ranked correction plan
   into the clone. Reuses the existing extraction (no second crawl) and points
   the user at `designlang fidelity` for the full visual+motion score of the
   running clone.

2. resolveOut(): the root command greedily owns -o/--out, and a sub-level
   *default* masked any override (opts.out was always truthy). clone/verify/
   visual-diff dropped their masking defaults and now resolve via resolveOut,
   which prefers an explicit sub value, else the root value only when actually
   passed on the CLI (getOptionValueSource === 'cli'), else the fallback.

Verified end-to-end: cloned stripe.com builds clean (next build, 4/4 pages),
served, and `fidelity` scored it 63/100 (motion 52 — the gap competitors miss).
-o now writes where asked. Adds a clone --fidelity CLI smoke test.
2026-06-22 22:52:55 +04:00
Manav Arya Singh ee68490eb7 docs: document fidelity + gallery, bump to 12.24.0
README quick-start, a dedicated 'Measured clone fidelity' section, the plugin
command table, and the full CLI reference now cover /fidelity and /gallery.
CHANGELOG 12.24.0 entry. Version bumped in lockstep across package.json,
plugin.json, marketplace.json (manifests describe the two new commands); the
version-sync test now guards fidelity + gallery too.
2026-06-22 22:33:52 +04:00
Manav Arya Singh d5f962ca80 feat(gallery): wire 'designlang gallery' command + report loader
designlang gallery [dir] scans a directory for fidelity.json reports, builds the
gallery model, and writes a deployable static site (index + permalink pages +
OG cards) to --out. load.js does a safe recursive scan (skips node_modules and
malformed files). Adds command doc + loader tests + 2 CLI smoke tests. Verified
end-to-end against real reports.
2026-06-22 22:30:39 +04:00
Manav Arya Singh 80ff54bd3a feat(gallery): static-site formatter — index grid, permalink pages, OG cards
formatGalleryIndex renders a dark card grid (best-first) with summary stats;
formatGalleryItemPage renders a permalink page that embeds the SVG score card,
OG/Twitter meta (so a shared link unfurls the number), a motion table, and the
ranked correction plan. renderGallerySite emits the full [{path,content}] tree
(index + per-clone page + per-clone card.svg). 4 unit tests.
2026-06-22 22:28:57 +04:00
Manav Arya Singh c919acc0dd feat(gallery): gallery item model + builder over fidelity reports
The distribution surface for the fidelity loop (feature C). buildGallery() turns
a set of fidelity reports into a deduped, best-first item list with summary
stats (count, avg, best/worst). slugify() + toGalleryItem() are tolerant of
partial reports. Pure data shaping; 6 unit tests.
2026-06-22 22:27:47 +04:00
Manav Arya Singh d67a82d85c feat(fidelity): wire 'designlang fidelity' command + clone-vs-original runner
New CLI verb: designlang fidelity <original> --clone <url>. Captures full-page
screenshots of both (pixel-diff → visual score), extracts motion from both
(→ motion score), blends them, and writes fidelity.md / fidelity.json /
fidelity-card.svg / fidelity-diff.png. --min gates CI; --motion-runtime captures
real durations + choreography.

run.js is the integration glue over existing primitives (extractDesignLanguage,
verify's diff) and the new scoring modules. Adds command doc + 2 CLI smoke
tests.
2026-06-22 22:22:48 +04:00
Manav Arya Singh 0cc19c665a feat(fidelity): report formatters — JSON, Markdown, shareable SVG score card
Renders a combined fidelity result three ways: machine JSON, a Markdown report
(headline score, motion breakdown table, ranked correction plan), and a
self-contained SVG share card ('88% · B · stripe.com'). The card is the gallery
share hook — a measured number no competitor can post. 6 unit tests.
2026-06-22 22:19:48 +04:00
Manav Arya Singh 8a0fba1dab feat(fidelity): correction-plan emitter + combiner + closed-loop driver
The wedge neither competitor ships: don't just measure fidelity — prescribe the
next edit and loop on it.

- correction-plan.js: turns verify's per-token-family attribution and the motion
  aspect scores into ranked, actionable directives (hardest-hitting first), each
  with an expectedGain heuristic so the loop spends its budget well.
- index.js: combineFidelity() blends visual (verify) + motion into one score and
  letter grade; runFidelityLoop() drives measure→plan→rebuild→re-measure until a
  threshold or round budget, with measure/rebuild injected for offline testing.

10 unit tests covering ranking, threshold gating, blend math, convergence, the
stall path, and the no-directives early exit.
2026-06-22 22:18:37 +04:00
Manav Arya Singh 1c5521fdb9 feat(fidelity): motion-fidelity comparator — score a clone's motion vs the original
verify/ scores static visual fidelity but deliberately ignores motion. This
fills that gap: a pure function over two extractMotion() outputs that blends
seven aspects (feel, durations, easings, springs, keyframe kinds, scroll-linked,
choreography/stagger) into a 0-100 motion fidelity score with per-aspect gaps.

Missing-on-clone motion is the dominant penalty; over-animation is penalised
gently. No browser, no I/O — fully unit-tested (7 cases).
2026-06-22 22:16:55 +04:00
Manav Arya Singh 53e42cc769 test(brand): cover deriveBrandEssence axes, archetype, copy, rendering
13 tests: warm/cool axis polarity, archetype selection from the twelve,
adjective/positioning/evidence shape, dangling-dash guard, empty/null
robustness, string-form inputs, and Essence chapter in html + md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 22:30:04 +04:00
Manav Arya Singh 62d1e78891 test(formatters): regression tests for #135 narrative bugs
Asserts no literal [object Object], semantic/easing/z-index values render,
and component class names keep their last character.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 14:16:35 +04:00
Sebastian Cao 32b98d6c0c feat(crawler): detect dark mode via prefers-color-scheme media queries (#145) 2026-06-18 13:54:25 +04:00
Manav Arya Singh 3d9fab5a0f fix(site): preserve full colour swatch fields in canonical
Synthesis reduced swatches to {hex,count}, dropping rgb/contexts and
crashing formatMarkdown (reads c.contexts.join and c.rgb) when the
canonical pack was emitted. Keep the whole swatch (spread) so every
emitter works. Caught by an end-to-end run on example.com; covered by a
regression test asserting canonical swatches retain rgb + contexts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 13:40:44 +04:00
Manav Arya Singh 78fdf58add chore(plugin): add version-sync guard script + test
scripts/check-plugin-version.mjs fails loud when plugin.json or
marketplace.json drift from package.json; wired as 'npm run check-plugin'
and enforced by tests/plugin-version.test.js (also asserts every new
command has a backing file and is described). Prevents the drift recurring.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 13:35:28 +04:00
Manav Arya Singh 762a0c4281 test(site): unit tests for whole-site synthesis engine
Covers scope thresholds, OKLab distance + clustering, canonical
election, outlier flagging, grade/letter, single-page no-grade, missing
fields/errored pages, and determinism. 9 tests, all pure (no browser).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 13:30:19 +04:00
Manav Arya Singh f10f4c4c08 feat(studio): Motion preview tab — playable easing curves + choreography (v3)
New Motion tab in the local studio renders extracted easing as SVG curves
(springs styled distinctly), duration chips, and runtime choreography +
scroll recipes. Click a curve to preview its timing; play a choreography
sequence to see the real captured stagger. Degrades to a --motion-runtime
prompt when no motion tokens are present.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 23:53:37 +04:00
Manav Arya Singh fc08b03931 feat(motionlang): emitters consume runtime choreography (v3 fidelity)
motion-tokens.json gains choreography + scroll-recipe + observed-duration
blocks. Framer Motion stagger and the new GSAP stagger() helper now use the
real runtime-observed staggerMs instead of a heuristic guess when
--motion-runtime captured one. Degrades cleanly with no runtime data.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 23:50:35 +04:00
Manav Arya Singh 294030e5aa feat(motionlang): runtime motion model + choreography/scroll detection (v3 core)
Pure processing layer for Motion Lang v3: processRuntimeMotion() turns raw
document.getAnimations() observations into a structured per-trigger model with
real durations; detectChoreography() finds evenly-staggered sequences;
detectScrollRecipes() classifies scroll motion into parallax/reveal/pin.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 23:45:23 +04:00
Manav Arya Singh a51e406c01 feat(studio): craft pass — intentional specimen + harmonised colors (v12.21.0)
The preview felt random. This rebuilds it as a deliberate design-system
specimen instead of a scatter of placeholder components.

- Composed preview: masthead, a centered hero using the site's own strongest
  heading + top CTA verbs, labeled Color and Type foundations, then Buttons /
  Inputs / Status / In-context cards / Metrics — each under a clear section
  eyebrow, on a consistent vertical rhythm. The rebuilt-Page tab reuses the
  same crafted components. Honest product copy, no lorem.
- Harmonised deriveTokens: guarantee a legible surface/text pair, filter greys
  out of accent selection (no neutral promoted to brand), deepen a near-
  invisible accent to read on the surface, hold secondary text above a
  legibility floor. Keep white-on-warm-accent convention for accent foreground.
- Calmer chrome: quieter hairlines, consistent 30px rounded controls, pill
  tabs, segmented light/dark — the frame recedes so the system is the focus.

Shared studio engine, so the CLI and website /studio get it identically.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 13:34:00 +04:00
Manav Arya Singh c652df1b96 feat(studio): one-click generated dark mode (v12.20.0)
Add a Light/Dark toggle that generates a dark variant from the extracted
light tokens: surface → near-black tinted toward the brand hue, text →
near-white, muted/border/card recomputed; the brand accent is preserved
(lightened only if it would vanish on dark). Type/shape/spacing/motion carry
over. Contrast grading recomputes live, inspector edits layer on top of
either theme, and the theme is encoded in the shareable URL.

Pure shared deriveDark() in src/studio-tokens.js keeps the CLI and website
studios in lock-step. New unit tests cover the dark derivation and control.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 03:42:56 +04:00
Manav Arya Singh 8a2c425f4f feat(studio): living design-system editor + /studio on the website (v12.19.0)
Turn `designlang studio` from a read-only viewer into an interactive
playground, and ship the same experience on the website.

CLI (`designlang studio`):
- Inspector (color/type/shape/spacing/motion) writes a small `--p-*` CSS
  variable set; a tabbed live preview — component wall + a page rebuilt from
  the extraction's reading order — restyles instantly off those variables.
- Live WCAG contrast grading per color, paper/white/dark backdrops, an edit
  counter, and smooth token-driven transitions.
- Export edited system as DTCG tokens / CSS variables / Tailwind theme
  (zero-dep client downloads); share via URL (edits encoded as hash deltas).

Website (`/studio`):
- Paste any URL → extract → embed the identical editable studio in a framed
  viewport. Shareable via `?url=`; shares the free 2/day demo budget.
- New POST /api/studio reuses the same `studioHtml` engine server-side.

Internals:
- Token derivation extracted to a pure, dep-free module (src/studio-tokens.js)
  shared by the CLI and the website route, keeping both surfaces in lock-step.
- New unit tests: derivation, WCAG contrast math, rendered studio document.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 02:49:47 +04:00
Manav Arya Singh f9e0c4770a feat(verify): fidelity loop — rebuild from tokens, pixel-diff vs live (v12.18.0) (#139)
`designlang verify <url>` closes the extract→prove loop. For each detected
component (button+card in v1) it rebuilds a clone styled ONLY by the extracted
tokens (each visual property snapped to nearest token, colours in CIE-Lab ΔE),
renders it, and pixel-diffs against the live component → 0-100 fidelity score +
per-token-family attribution + a verify.html triptych (original | rebuilt |
loss heatmap) + verify.json. `--min` gates CI.

Honest by construction: missing component → n/a (excluded, no silent 100);
property with no token → unmapped (counted as loss, surfaced in attribution);
size mismatch letterboxed, never stretched.

New isolated modules under src/verify/ (tokens/restyle/render/diff/index) +
formatters/verify.js + verify CLI command. Adds pixelmatch+pngjs. 13 unit tests,
full suite green (438). Live smoke vs github.com: button 89/100.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 17:50:42 +04:00
Manav Arya Singh 95eae1702d feat(motionlang): add GSAP and Web Animations API emitters (v12.17.0) (#138)
Two new framework targets for the extracted motion language:

- motion-gsap.js: registers extracted cubic-bezier curves as named GSAP
  CustomEases, emits gsap.from() reveal helpers + a ScrollTrigger batch
  reveal when the page uses scroll/view-timeline.
- motion-waapi.js: zero-dependency Web Animations API preset. WAAPI takes
  cubic-bezier easing strings verbatim, so curves reproduce exactly (no
  spring approximation); helpers honour prefers-reduced-motion.

Wired into the CLI emit list (`<host>-motion.gsap.js`, `-motion.waapi.js`),
the /api/motion exports, and the motionlang analyzer export cards. Adds 8
emitter tests (425 -> all green). Also ignores .npmrc.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 17:13:29 +04:00
lucaszhu-hue 1d4b071042 feat(atlascloud): support Atlas smart classifier 2026-06-09 11:35:54 +08:00
Manav Arya Singh 619d44ad21 v12.16.0 — motionlang CSS + Tailwind emitters; fix website brand-book PDF
motionlang:
- New formatter src/formatters/motion-css.js — <host>-motion.css with
  --duration-*/--ease-* custom properties, reusable + reconstructed
  @keyframes, .mo-* utility classes, and a prefers-reduced-motion guard.
- New formatter src/formatters/motion-tailwind.js — <host>-motion.tailwind.js
  require-able theme.extend (transitionDuration, transitionTimingFunction
  incl. a spring curve from detected overshoot beziers, keyframes, animation).
- Wired both through api.js (RENDERERS + ext map) and the bin files array as
  the motion-css and motion-tailwind renderer ids.
- 13 new tests; full suite 414 pass.

website PDF fix ("downloaded PDF won't open"):
- extract route awaits the Blob cache write before the stream closes, so the
  serverless instance can't freeze mid-write and leave /api/pdf/<hash> with
  no cached design (was a fire-and-forget write returning 404 JSON).
- ShareExtractionButton fetches + validates content-type is application/pdf
  before saving, surfacing an error instead of writing a broken file.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 22:46:52 +04:00
Manav Arya Singh 613948a09c motionlang: add Motion One emitter, derive springs in Framer Motion
- New formatter src/formatters/motion-one.js — emits <host>-motion.one.js
  with animate() helpers (fadeIn, slideUp, scaleIn, pop), springs derived
  from detected overshoot beziers, reconstructed keyframe maps, and
  inView/scroll helpers when the source uses scroll-/view-timeline.

- Framer Motion: springs now derived from detected overshoot beziers
  (was hard-coded), added pop variant on a spring, keyframe-derived
  variants reconstructed from @keyframes, and a whileInView inView
  block when the source uses scroll motion.

- Wire motion-one through api.js (RENDERERS + ext map) and the
  bin/design-extract.js files array so it ships alongside the existing
  Framer Motion preset.

- Tests: 7 new cases covering spring derivation, scroll-conditional
  emit, keyframe reconstruction, and minimal-design degradation. All
  pass; 154 existing formatter tests still green.

Shared springFromBezier heuristic keeps Motion One and Framer Motion
outputs feel-consistent for the same site.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 15:11:54 +04:00
Manav Arya Singh f403f8c536 v12.11.0: brand --pdf
Adds native PDF brand-guide output to the brand command:
  --pdf, --paper a4|letter|tabloid, --landscape,
  --no-print-background, --attach-tokens

PDF features: per-chapter page breaks, running footer with page
numbers, selectable text, embedded fonts, optional DTCG-tokens
JSON attached as a file inside the PDF (paperclip in Acrobat).

One new dep: pdf-lib (lazy-imported, only used when --attach-tokens
is passed).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 01:25:11 +04:00
Manav Arya Singh be37b82526 feat(v12.10): designlang stats + low-confidence note in grade card
Two tight quality-of-life additions on top of the v12.9 extraction pass.

stats command

  $ npx designlang stats stripe.com

    Grade        B · 87/100
    Primary      #533afd ×899 59% conf
    Fonts        sohne-var
    Type scale   14 sizes
    Spacing      base 2px · 13 steps
    Shape        5 radii · 6 shadows
    Colours      31 tokens
    WCAG         79%
    Stack        next · unknown
    Material     skeuomorphic
    Tone         neutral
    Intent       landing

  One-screen summary, no files written. Use -j / --as-json for
  machine-readable output (CI, scripting).

  (--json is already a global program flag for the main extract
  command's raw-JSON-to-stdout mode, so the subcommand flag is
  --as-json to avoid the clash. Both --as-json and -j shortcuts work.)

Low-confidence note in grade.html

  v12.9 added primary.confidence (0-1) on the colours payload but no
  surface used it. v12.10 renders an amber callout above the
  dimensions grid in the grade report card when confidence < 0.5:

    "Primary detection was low-confidence (32%). The brand colour
     may be a soft pick — review the palette below."

  Soft inline note, not an error. Stays hidden on the common high-
  confidence path. Dark-mode aware.

Tests
  - 2 new tests cover the present + absent paths of the confidence
    note.
  - 398/398 total.

No breaking changes, no new dependencies.
2026-05-13 23:36:13 +04:00
Manav Arya Singh 95a18d7211 feat(v12.9): extraction quality pass — fix the core MVP
Eight features rode on top of the same extractor. This release fixes
four real defects in that extractor — visible across every downstream
output (grade, battle, remix, pack, theme-swap, brand, pair).

Fixed

  1. clusterColors() representative bug. Before, the first-encountered
     colour seeded each cluster, so a sparsely-used pale shade could
     become the canonical hex of a cluster that mostly held a vivid
     brand colour. The brand-book primary slot was reading lavender
     for Stripe instead of #533afd. Now: representative is the
     most-counted member.

  2. detectScale() missed common production base units. Before, only
     [2, 4, 6, 8] were tried, so Bootstrap-style base-5 and base-7/10/12
     sites returned base:null. Now: [2, 4, 5, 6, 7, 8, 10, 12, 16] with
     a small bonus for 4 and 8 to keep results stable for production
     defaults.

  3. Typography noise. Generic CSS stacks (sans-serif, monospace,
     system-ui, inherit, -apple-system, ...) and icon fonts (Material
     Icons, Font Awesome, Lucide, Tabler, ...) polluted the families
     list. Brand books had icon fonts documented as the body family.
     Now: an explicit filter at the source.

Added

  4. primary.confidence (0–1) on design.colors.primary. Computed from
     the brand-score gap between rank 1 and rank 2. Runaway leader →
     1.0; near-tie → 0.3. Downstream consumers can surface uncertainty
     warnings on low-confidence extractions.

  5. asList(v) helper exported from src/utils.js. Coerces anything-
     shaped input (array / object / comma-string / scalar) into a clean
     string array. Consolidates ad-hoc per-formatter defenses.

Verified live on stripe.com

  Before: primary slot showed a washed-out lavender; multiple icon
  fonts polluted families; spacing base often null.

  After: primary #533afd (count 899, confidence 0.59), families
  ['sohne-var'], spacing base correctly detected.

No new dependencies, no schema breaks, no public-API changes.
396/396 tests pass (6 new).
2026-05-12 13:31:28 +04:00
Manav Arya Singh 03a2e7524a feat(v12.8): designlang pair — fuse two designs into a single hybrid identity
`designlang pair <urlA> <urlB>` extracts both sites in parallel, then
mixes their design systems along seven configurable axes (colours,
typography, spacing, shape, motion, voice, components). Default split
is "visuals from A, voice + type + components from B" — i.e. the most
distinctive crossover.

  $ npx designlang pair stripe.com linear.app

  stripe.com × linear.app
    A  Colours      · stripe.com
    B  Typography   · linear.app
    A  Spacing      · stripe.com
    A  Shape        · stripe.com
    A  Motion       · stripe.com
    B  Voice        · linear.app
    B  Components   · linear.app

  ✓ stripe-com-x-linear-app.pair.html
  ✓ stripe-com-x-linear-app.pair.md
  ✓ stripe-com-x-linear-app.pair.json

How it works

  - src/fuse.js — fuseDesigns(a, b, opts) deep-clones both inputs,
    picks each axis from the requested source, synthesises a
    pair-specific meta URL ("pair://<a>-x-<b>"), and strips score /
    cssHealth (those measurements belong to the source extractions,
    not the fusion).
  - Default axes calibrated for the most interesting crossover:
    colour/spacing/shape/motion from A, typography/voice/components
    from B. Override any axis with --<axis>-from a|b.
  - Layout follows colour (mixing primary from one site with neutrals
    from another tends to muddy the brand). Material language and
    imagery style track the colour pick. Variables follow colour.
  - Components carry their detected library label (componentLibrary)
    along with the anatomy.

Pair card formatter

  - src/formatters/pair.js — editorial three-card hero (A · B · Fused),
    per-axis source matrix, and a fused specimen using the *real*
    headline from whichever site contributed the voice axis.
  - --brand flag also emits a full brand-guidelines book of the fused
    identity (re-uses formatBrandBook on the fused design).

Plugin

  - /pair is the 8th slash command (commands/pair.md).
  - .claude-plugin/plugin.json + marketplace.json bumped to 12.8.0
    with an eight-command description.

Tests (12 new, 390/390 total)

  - Default split, per-axis overrides, score-stripping, meta synthesis
  - Source-design immutability, missing-input errors
  - HTML rendering, voice carry-through to specimen, XSS escaping
  - Markdown axis source table

Verified live: Stripe × Linear fuses Stripe's purple palette with
Linear's Inter Variable typography and Linear's actual top heading
("The product development system for teams and agents") as the
specimen quote.

Why

  battle (v12.2) answers "which is better"; pair answers "what would
  the intersection look like". Same parallel extraction, opposite
  operation. Pure logic, no LLM, no new dependencies.
2026-05-10 21:11:57 +04:00
Manav Arya Singh a935a1ae41 fix(v12.7.1): brand-book visual polish — extracted brand color leads, real text everywhere
The v12.7.0 brand book had a real-data deficit:
  - Cover used a generic grade-coloured accent (not the brand's actual primary).
  - Every section opened with a philosophical lede ("the felt pace of an
    interface", "form follows feeling", "quiet authority of restraint").
  - Components chapter was a metadata table, not a real mock.
  - Type specimen reused the same recycled aphorisms across files.

This pass replaces all of that with the extracted values themselves.

Cover

  Full-bleed band of the brand's actual primary (auto-detected via the
  most-saturated brand-classified colour; falls back through secondary,
  accent, most-used). The hex prints inside the band. Host name lives
  below the band in massive serif.

Section ledes

  Replaced philosophy with one-line data summaries.
  "1 primary · 1 secondary · 1 accent · 27 neutrals · 88 total" beats
  "Brand colours carry meaning. Neutrals carry structure."

Type specimen

  Now uses real headlines extracted from design.voice.sampleHeadings.
  Falls back to a single neutral pangram only when those are absent.
  Recycled aphorisms removed entirely.

Colour chapter

  Primary at full-width hero (aspect 2.6:1), secondary + accent
  half-width pair, neutrals flush horizontal strip, full palette grid.
  Hex labels render *inside* the swatch in auto-contrast text.

Components chapter

  Renders an actual primary + secondary button using extracted brand
  colour + radius, plus a card mock using extracted surface + text
  + accent for the link. Metadata moved below the mock.

Accessibility chapter

  Failing pairs render as actual stacked colour blocks ("Aa" in
  foreground on the failing background, ratio inline). Big WCAG score
  on top.

Tokens chapter

  Code blocks now have a header bar with language + target filename.

"How to use"

  Trimmed from 6 rules of thumb to 4 punchier ones. Dropped the
  "rule of thumb" framing.

Layout

  Section padding moved into the wrap (no gutter around the hero
  band). TOC sits on tinted sub-paper background. Chapter headers
  thinner with a bottom rule.

Same 13 chapters, same public API, no breaking changes.

378/378 tests pass (one assertion updated for the lowercase
"Brand guidelines" cover label).
2026-05-09 23:37:39 +04:00
Manav Arya Singh b249fb8f2e feat(v12.7): designlang brand — full editorial brand-guidelines book
`designlang brand <url>` produces a self-contained, print-ready HTML
"brand guidelines book" — a hand-off-ready document for designers and
recipients, distinct from `pack` (developer files) and `grade` (audit).

  $ npx designlang brand stripe.com

  Brand book · 54 tokens · 3 fonts · grade B · https://stripe.com
  ✓ stripe-com.brand.html
  ✓ stripe-com.brand.md
  ✓ stripe-com.brand.json

Layout — 13 chapters

  Cover           Big serif headline, 4 stat callouts (intent, grade,
                  score, token count)
  Contents        Two-column TOC with smooth-scroll anchors
  01 About        Page intent · material language · imagery style ·
                  component library · stack · voice tone
  02 Logo         Extracted SVG on canvas + dimensions/aspect/format
  03 Colour       Brand swatches w/ HEX/RGB/HSL/usage, neutrals grid,
                  full palette, A11y callout
  04 Typography   Display + body families, scale table w/ Aa samples,
                  large specimen lines in extracted family
  05 Spacing      Base unit + visual rhythm bars
  06 Shape        Border radii visualised, shadow elevation cards
  07 Iconography  Library detection + captured icons grid
  08 Motion       Feel + durations (animated dots) + easings list
  09 Components   Detected anatomy w/ slots, variants, sizes
  10 Voice        Tone, pronoun posture, heading case, CTA verbs, quotes
  11 Accessibility WCAG score, failing pairs table, remediation list
  12 Tokens       Drop-in CSS-var + Tailwind extend snippets
  13 How to use   Six rules of thumb for derivative work

Internals

  - src/formatters/brand-book.js — formatBrandBook + formatBrandBookMarkdown
  - bin/design-extract.js — `brand` command (--format html|md|json|all,
    --open). Defaults to deep-interact extraction since the book wants
    motion, voice, anatomy, accessibility surfaces.
  - asList() coercer for the component-anatomy fields, which arrive as
    arrays / objects / strings depending on the cluster (the bug that
    broke the first integration).
  - Editorial CSS: Instrument Serif display + Inter body + JetBrains Mono
    code, generous whitespace, dark-mode toggle, print stylesheet with
    page-breaks at chapter boundaries.

Plugin

  - /brand is the 7th slash command (commands/brand.md)
  - .claude-plugin/plugin.json + marketplace.json bumped to 12.7.0 with
    a seven-command description.

Tests

  - 7 new tests cover chapter coverage, host/colour/font rendering,
    XSS escaping, sparse-design fallback, mixed-shape anatomy.
  - 378/378 passing.

Verified live on stripe.com — 54 tokens, 3 fonts, grade B, all 13
chapters rendering correctly.
2026-05-09 23:25:52 +04:00
Manav Arya Singh f59fc8040d fix(ci): partial CI report on extraction failure (preserves stack via cause)
Replaces the (now-closed) PR #76 with a tighter version of the same idea.

Before: a Playwright crash, DNS miss, or ad-walled URL would surface as
an unhandled exception inside `runCi()` — the CI artifact directory ended
up empty, and downstream jobs (artifact upload, status comments,
dashboards) had nothing to point at.

Now:

  1. Wrap only the actual throw site (`extractDesignLanguage(url)`).
  2. On failure, write a partial `ci-report.md` with the message + full
     stack as a fenced block, plus a `ci-summary.json` keyed identically
     to the success path (`{url, score, grade, driftVerdict, timestamp}`)
     with two extra failure fields (`extractionFailed: true`, `error`).
  3. Surface the original error via `cause` on the returned object so
     callers can re-raise with a real stack — none of this string-prepending
     wrapping that loses the trace (which was the main issue with #76).
  4. Append the partial report to `GITHUB_STEP_SUMMARY` if present so
     GitHub Actions runs still get a step summary on the failure.

Tests (tests/ci.test.js, 4 new):

  - returns shouldFail=true without throwing
  - writes ci-report.md + ci-summary.json on failure
  - summary preserves the success-path schema + adds extractionFailed flag
  - failure is a structured return, never an exception

Test exercises the path with an unreachable host (DNS resolves fast).

371/371 tests pass.

Closes #76 conceptually (PR closed per code review at
https://github.com/Manavarya09/design-extract/pull/76#issuecomment-4406699448).
2026-05-08 17:20:21 +04:00
Manav Arya Singh 765139b992 feat(v12.6): designlang theme-swap — recolour around a new brand primary
Closes #57.

`designlang theme-swap <url> --primary <hex>` takes the existing
extraction and rotates the brand palette around a new primary while
preserving everything else: type scale, spacing rhythm, neutrals,
motion, component anatomy.

  $ npx designlang theme-swap stripe.com --primary "#ff4800"

  #533afd → #ff4800 · 91 colours · https://stripe.com
  Hue shift: 118.5° · neutrals preserved · type/spacing/motion untouched

  ✓ stripe-com-themeswap-ff4800.themeswap.html
  ✓ stripe-com-themeswap-ff4800.themeswap.md
  ✓ stripe-com-themeswap-ff4800.themeswap.json
  ✓ stripe-com-themeswap-ff4800.themeswap.tokens.json

How it works

  - Detects the source primary (extractor classification + usage-count
    fallback). Override with --from <hex>.
  - Computes hue delta in OKLCH → applies to every coloured token.
    Neutrals (chroma < 0.04) are explicitly preserved so body text,
    surfaces, and rule lines stay readable.
  - The user's --primary hex lands verbatim on the primary slot
    (no rounding through chroma).
  - Out-of-gamut targets get chroma-clamped via the existing
    oklchToHex helper.

The recoloured design is fed through every existing emitter (DTCG,
Tailwind, shadcn, Figma vars, CSS vars) so the swap propagates for
free — no new emitter code.

Internals

  - src/recolor.js — recolorDesign(design, opts)
  - src/formatters/theme-swap.js — formatThemeSwap (HTML preview),
    formatThemeSwapMarkdown
  - src/utils/color-gamut.js — adds srgbToOklab, srgbToOklch,
    hexToOklch, oklchToHex with chroma fallback
  - bin/design-extract.js — `theme-swap` command with --primary,
    --from, --format, --open

Plugin

  - Adds /theme-swap slash command (commands/theme-swap.md)
  - Bumps .claude-plugin/plugin.json + marketplace.json to 12.6.0
    with a six-command description.

Tests

  - 10 new tests covering primary-pinning, neutral preservation,
    hue rotation, error paths, --from override, HTML/markdown shapes,
    and XSS escaping.
  - 367/367 total, all passing.

Verified live on stripe.com (Stripe purple → orange, 91 colours,
118.5° rotation), JSON outputs parse cleanly, HTML preview renders
side-by-side palettes + mockup comparison correctly.
2026-05-08 12:57:09 +04:00
Manav Arya Singh 8ae995db70 feat(v12.4): designlang pack — one polished design-system bundle
Closes #59.

`designlang pack <url>` runs the full extraction once and writes every
emitter output into a single, signed, layered directory. One artifact a
designer or dev can clone, drop into a project, or zip up and send to a
client.

Layout:

  <host>-design-system/
  ├── README.md           bespoke, references source + grade + at-a-glance
  ├── LICENSE.txt         provenance + usage guidance
  ├── tokens/             DTCG + Tailwind + CSS vars + Figma vars + motion + theme.js
  ├── components/         typed React stubs (anatomy.tsx)
  ├── storybook/          runnable Storybook project
  ├── starter/            minimal HTML starter wired to tokens/variables.css
  ├── prompts/            v0 / Lovable / Cursor / Claude Artifacts + named recipes/*.md
  └── extras/             voice.json + prompt-pack.md rollup

Pure composition — zero new deps, zero new analysis. Reuses formatDtcg,
formatTokens, formatTailwind, formatCssVars, formatFigma, formatMotionTokens,
formatReactTheme, formatAnatomyStubs, formatStorybook, buildPromptPack,
generateClone (opt-in via --with-clone).

Internals:
- `toText()` normaliser handles the inconsistent return shapes across
  formatters (some return objects, some return JSON strings already).
- Recipes now write as `<name>.md` files, not array indices.
- 7 new tests cover directory shape, valid JSON token outputs, README
  content, starter wiring, recipe filenames, and a regression test for
  the double-stringify bug.

357/357 tests pass.

Verified live on stripe.com: 40 files, 232KB, all token JSON parses
cleanly.
2026-05-05 23:59:43 +04:00
Manav Arya Singh b62560309e feat(v12.3): designlang remix — restyle any site in another vocabulary
A genuinely new product surface. Take an extracted page-shape (sections,
voice, page-intent, anatomy) and re-render it under one of six opinionated
design vocabularies. Same content, different language.

  $ designlang remix stripe.com --as cyberpunk
  $ designlang remix stripe.com --all   # all 6 vocabularies, one extract

Six vocabularies:
  brutalist  — hard edges, mono type, single screaming accent
  swiss      — Helvetica, grids, restraint (post-Bauhaus default)
  art-deco   — gold on ink, geometric ornament, vertical type
  cyberpunk  — neon on midnight, scanlines, chromatic-aberration headings
  soft-ui    — cushioned shapes, low contrast, Vision-OS-adjacent
  editorial  — broadsheet serifs, generous whitespace, ink on paper

Each vocabulary is a self-contained tokens + font-stack + signature CSS
file in src/vocabularies/. The formatter (src/formatters/remix.js) maps
every section role (hero, feature-grid, pricing-table, stats, testimonial,
faq, logo-wall, steps, cta) to vocabulary-styled markup, using the host's
own copy (headings, ledes, CTA verbs from voice).

Hero deduplication: real-world section walkers — especially on SPA
marketing pages — often emit a hero wrapper + an inner hero with the
same h1. Remix dedupes by heading and excludes section-claimed headings
from the voice pool, so heading-less sections (cta bands, logo walls)
don't re-render an already-claimed heading.

14 new tests (350 total, all passing). Cover registry shape, per-vocab
token validity, dedup semantics, no-cross-claim heading pool, XSS
escaping, missing-input errors.

Why: Grade (v12.1) is the audit. Battle (v12.2) is the comparison.
Remix is the transformation. No competitor (Dembrandt, Superposition,
html.to.design, Builder Visual Copilot) ships site-shape-preserving
vocabulary swap.

Website: bumps v12.2 → v12.3 across home/features/gallery/spec/permalink,
adds /features#remix section, swaps Battle CTA chip to Remix.
2026-05-05 00:17:33 +04:00
Manav Arya Singh e2eced898e chore(codeql): remove unused imports across src + tests
Closes 8 CodeQL js/unused-local-variable alerts. All deletions are pure
import-list trims — no runtime behavior changes:

  src/history.js          -- existsSync          (#72)
  src/sync.js             -- readFileSync        (#73)
  src/visual-diff.js      -- basename            (#62)
  src/index.js            -- formatAnatomyStubs  (#57; still re-exported)
  src/index.js            -- formatMotionTokens  (#58; still re-exported)
  src/formatters/tailwind.js   -- rgbToHex       (#54)
  src/formatters/vue-theme.js  -- rgbToHsl       (#55)
  src/formatters/css-vars.js   -- pxToRem        (#52)
  tests/wide-gamut.test.js -- oklchToSrgb, oklabToSrgb (#64)

Confirmed via grep that none of the removed names had call sites in
their source file. The two src/index.js entries still appear as
`export { … } from '…'` re-exports on lines 201–202 — those are
self-contained re-exports, not affected by the removed imports.

Skipped intentionally:
- Unused functions (#66–#69, #61, #76–#77) — may be retained as
  internal API surface; need per-call review.
- File-system race conditions (#70, #71) in sync.js / studio.js — real
  bugs, but need a dedicated atomic-write fix, not in scope.

336/336 tests pass; all touched modules load cleanly.
2026-05-02 23:39:32 +04:00
Manav Arya Singh 3b47b0d02e fix(codeql): tighten host assertions, drop unused darkdiff import
Three CodeQL alerts (#78, #80, #81 — js/incomplete-url-substring-sanitization)
flagged `html.includes('example.com')` patterns in tests as potential URL
sanitization gaps. They are *test* assertions, not security checks, but the
same patterns are dangerous in real validators — replacing with regex
anchored inside rendered HTML markup ('>host<') makes the intent explicit
and silences the rule.

Also removes the unused `diffDarkMode` import in bin/design-extract.js
(alert #79 — js/unused-local-variable). The CLI uses `diffDesigns` from
src/diff.js, not the dark-mode-only `diffDarkMode` helper.

No behavior change. 336/336 tests still pass.
2026-05-02 23:35:00 +04:00
Manav Arya Singh 100cea608a feat(v12.2): designlang battle + design-score badges
Two ships on top of the v12.1 Grade flywheel.

`designlang battle <urlA> <urlB>` — head-to-head graded card, parallel
extraction, dimension-by-dimension bar table with verdict + win/tie/loss
tally. Self-contained HTML, also emits .battle.md and .battle.json.

Design-score SVG badges:
- `designlang grade --badge` writes *-badge.svg locally
- New formatter src/formatters/badge.js — shields.io style, color by
  letter grade, accessible (aria-label, role=img), Verdana-width math so
  it renders identically across GitHub / npm / arbitrary markdown
- Live endpoint at website/app/api/badge/[host]/route.js — reuses the
  same blob cache /api/extract writes to, edge-cached 6h with 24h SWR
  for friendly behavior under GitHub's image proxy
- Friendly URL rewrites: /badge/<host> and /badge/<host>.svg → /api/badge/<host>

Why both: Grade is the artifact, Battle is the viral content layer
("Stripe vs Vercel — guess who lost"), Badge is the permanent
distribution layer (every README adoption is a backlink to a live
grade page).

13 new tests (336 total, all passing). Cover battle markup, score
comparison thresholds, SVG escaping, grade-color mapping, missing-score
handling.

Website: bumps v12.1 → v12.2 across home/gallery/spec/features/permalink,
adds /features#battle section, swaps Grade CTA chip to Battle.
2026-05-02 23:29:36 +04:00
Manav Arya Singh 5aac5e707d feat(v12.1): designlang grade — shareable HTML Design Report Card
Adds `designlang grade <url>` — a self-contained, editorial-style HTML
audit page (plus JSON + Markdown variants). Big serif letter grade,
eight scored dimensions with arc gauges, evidence pulled from the
audited site itself (palette swatches, type specimen, spacing rhythm),
and a strengths / what-to-fix ledger. Paper/ink theme with a dark
toggle, print-ready, OG meta for shareable links.

Why this exists: html.to.design, Locofy, Builder, Polypane all ship
extraction or layout cloning. None of them grade the design system
itself in a form you can post on Twitter or send to a client.

- src/formatters/grade.js — new formatHTML + formatMarkdown formatters
- bin/design-extract.js — `grade` subcommand with --format / --open
- 6 new tests covering shape, escaping, scale-shape regression
- README — trimmed 649 → 241 lines, restructured around outputs/moats
- website — adds /features#grade section, bumps v12 → v12.1 throughout
2026-04-29 13:25:27 +04:00
Manav Arya Singh 25db0fa7cb feat(v10.5): form & state capture — the states LLMs always botch
- form-states.js surfaces:
  - forms (input count + style families)
  - modal/dialog/sheet containers
  - skeleton + spinner loading indicators
  - empty-state + error-state placeholders
  - toast library detection (Sonner, react-hot-toast, react-toastify,
    Radix Toast, Chakra Toast, Notistack)
- New output: *-form-states.json
- Closes the v10.x minor-release series started in v10.1 — all v11
  deferred items now shipped as minors.
- 317/317 tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 12:15:06 +04:00
Manav Arya Singh 161403511d feat(v10.4): icon system + background patterns + stack intel
- icon-system.js fingerprints the icon library from stroke/fill
  dominance, stroke width, grid size, and rounded-caps presence.
  Recognises Lucide, Heroicons (outline+solid), Phosphor, Tabler,
  Feather, Remix, Material.
- background-patterns.js classifies noise/dot-grid/line-grid/
  gradient-mesh/svg-pattern/plain from background-image values.
- stack-intel.js extends stack-fingerprint with 12 CMS, 13 analytics,
  and 7 experimentation platform detectors.
- Bin now reads its version from package.json — no more drift.
- New outputs: *-icon-system.json, *-stack-intel.json.
- background-patterns merged into *-visual-dna.json.
- 313/313 tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 12:13:13 +04:00
Manav Arya Singh 33e5cd1620 feat(v10.3): perf (core web vitals + bundle) + SEO + structured data
- perf.js: fresh Playwright context measures LCP/CLS/INP via
  PerformanceObserver, categorises network responses into
  js/css/font/image/document/other bytes, counts third-party requests
  against a known-host list, synthesises an interaction so INP
  reports. Returns grade buckets per vital.
- seo.js: pure extractor for OG, Twitter, canonical, manifest,
  theme-color, viewport, favicons, and inline JSON-LD blocks.
- Crawler captures favicons + manifest link + JSON-LD script text.
- New flag --perf, auto-on with --full.
- New outputs: *-seo.json, *-perf.json.
- 307/307 tests passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 12:09:54 +04:00
Manav Arya Singh bd354e2971 feat(v10.2): dark-mode pairing + responsive screenshots
- dark-mode-pair.js joins light+dark passes into semantic role pairs
  (primary/secondary/accent/background/text), pairs CSS variables
  that actually differ, and emits a drop-in Tailwind darkMode config
  plus an audit of tokens missing from either pass
- responsive-screenshots.js captures full-page PNGs at mobile/tablet/
  desktop/wide × (light,dark) with a JSON index
- New flag --responsive-shots, auto-on with --full
- New outputs: *-dark-mode.json, *-responsive.json
- CLI version test now reads from package.json so future minor
  releases don't need to update the test
- 303/303 tests passing

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 12:06:18 +04:00
Manav Arya Singh c878d09ce0 feat(v10.1): cluster-aware retina component screenshots
Replaces the v9 hardcoded-selector screenshot pass with a cluster-
aware, retina (2×), multi-variant pipeline.

- New src/extractors/component-screenshots.js groups candidates by
  kind + variantHint + sizeHint and captures up to three reps per
  group. Falls back to the v9 hardcoded list when nothing clusters
  (auth / docs pages).
- Dedicated Playwright context at deviceScaleFactor: 2 for crisp crops
- New *-screenshots.json index maps every crop to cluster/bounds
- Markdown gains a Component Screenshots section
- No new CLI flags — --screenshots / --full still opt into capture
- 299/299 tests passing

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 12:01:27 +04:00
Manav Arya Singh bcfe9f84ce feat(v10): the Intent release — semantic layer for LLM agents
Six new extractors (page-intent, section-roles, component-library,
material-language, imagery-style, logo), a multi-page crawl
orchestrator with cross-page consistency, an optional --smart LLM
fallback, and a ready-to-paste prompt pack for v0 / Lovable / Cursor /
Claude Artifacts.

v9 captured *how* a site looks. v10 captures *what it is* — the
semantic signal an LLM needs to rebuild a site faithfully.

- page-intent.js: landing/pricing/docs/blog/product/about/dashboard/auth/legal
- section-roles.js: hero/feature-grid/logo-wall/stats/testimonial/
  pricing-table/faq/steps/comparison/bento/cta/footer + slot copy +
  reading order
- component-library.js: shadcn/Radix/MUI/Chakra/Mantine/AntD/HeroUI/
  Vuetify/Bootstrap/Tailwind detection with evidence + alternates
- material-language.js: glass/neumorph/flat/brutalist/soft-ui/
  material-you/skeuo from shadow + geometry + saturation
- imagery-style.js: photo/3d/iso/flat-illustration/mesh/icon-only/
  screenshot + dominant aspect + image-radius profile
- logo.js: SVG/PNG extraction + sampled clearspace
- multipage.js: auto-discovers canonical pages from nav, runs full
  pipeline per page, emits Jaccard consistency
- classifiers/smart.js: opt-in LLM refinement via OPENAI_API_KEY or
  ANTHROPIC_API_KEY, zero-dep fetch
- formatters/prompt-pack.js: v0.txt, lovable.txt, cursor.md,
  claude-artifacts.md, per-component recipe cards

New CLI flags: --smart, --pages <n>, --no-prompts.
New output files: *-intent.json, *-visual-dna.json, *-library.json,
*-logo.{svg,png,json}, *-multipage.json, *-prompts/.
Extended markdown: 6 new sections.
297/297 tests passing.

Website: v10 capabilities section, version stamps bumped to v10,
and a fix for the Specimens IntersectionObserver that was hijacking
--accent on page load before the specimens strip was ever in view.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 05:39:15 +04:00
Manav Arya Singh 13af4d8d8a feat(v9): motion · anatomy · voice — and lint/drift/visual-diff
Six new capabilities that push designlang past "extract the paint" into
"extract the feel, the anatomy, and the voice":

- motion language: easing-family classification, spring detection via
  cubic-bezier geometry, duration bucketing (instant/xs/sm/md/lg/xl/xxl),
  scroll-linked animation capture (animation-timeline / view-timeline /
  scroll-timeline), keyframe kind classification, and a one-word `feel`
  fingerprint. emits *-motion-tokens.json.
- component anatomy v2: variant × size × state matrices, slot inference
  (icon/label/badge/heading/media/footer), typed React stubs.
  emits *-anatomy.tsx.
- brand voice: tone (friendly/formal/technical/playful/neutral), pronoun
  posture (we→you / you-only / we-only / third-person), heading style,
  top CTA verbs, microcopy patterns. emits *-voice.json.
- designlang lint <file>: audit local DTCG / flat / css-vars token files
  for color sprawl, scale variance, contrast. non-zero exit on errors.
- designlang drift <url> --tokens <file>: verdict + ratio between local
  tokens and live site. --fail-on <level> for CI gating.
- designlang visual-diff <a> <b>: single-file HTML report with embedded
  screenshots, file-size deltas, changed-color-tokens table.

Crawler extended to capture animation-timeline + candidate text/slots/
variantHint/sizeHint/disabled. Markdown output gains Motion Language,
Component Anatomy, and Brand Voice sections. Website adds §00 v9 block.

All 282 tests pass (21 new in tests/v9-features.test.js).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 00:55:53 +04:00
Manavarya09 48b92c794b feat(v8): credibility + distribution release
Reliability fixes that restore trust on real sites:

- colors.js: brand-color detection rewritten — ranks chromatic clusters by
  interactiveBg x100 + saturation x2 + log(usage), requires HSL sat >25 or
  an interactive-bg hit. Linear primary: #d0d6e0 (gray) -> #e4f222 (lime).
  Stripe: #533afd. Verified on 5 ground-truth sites.
- accessibility.js + crawler.js: crawler now emits hasText per element
  (direct text-node child with visible chars); WCAG extractor filters
  decorative glyph spans, transparent overlays, non-text containers.
  Linear: 25% (171 failing pairs) -> 83% (1 failing pair).
- scoring.js: rubric recalibrated against Stripe/Linear/Vercel/GitHub/Apple.
  Loosened color/shadow/radii/type-scale thresholds; softened spacing
  no-base penalty when tokenization is present; cssHealth now weighted in
  the overall. Linear 47->76, Stripe 81->88, Apple 83->86.

New CLI flags:

- --selector <css>: scope extraction to a DOM subtree. Stripe: 2409 -> 112
  elements when scoped to footer. Falls back to full doc if invalid.
- --system-chrome: channel: 'chrome' in Playwright launch; skips the
  bundled Chromium download.
- --json: full extraction to stdout (already partial in v7, now first-class
  and documented).

New distribution surfaces (each publishes to its own registry):

- vscode-extension/: Extract-from-URL + inject-into-workspace commands.
- raycast-extension/: Extract, Score, Copy-CLI commands.
- figma-plugin/: URL or paste-JSON -> Figma Variables with multi-mode.
- github-action/: "Design regression guard" runs designlang on PRs, diffs
  tokens vs baseline, comments the delta, optional fail-on-change.
- smithery.yaml + smithery.dockerfile + docs/MCP-REGISTRY.md: MCP registry
  submission checklist.
- chrome-extension/PRIVACY.md + STORE_LISTING.md: Chrome/Firefox/Edge
  store listing prep (extension code already existed in v7.1).
- docs/LAUNCH.md: PH + Show HN + Twitter copy + day-of checklist.
- docs/demo.tape: VHS tape for the animated README hero.

README updated: hero references demo.gif with PNG fallback, adds "Install
everywhere" table for the new surfaces, documents the three new flags.
.npmignore excludes every companion-surface directory so the npm tarball
stays 109KB / 73 files.

All 275 tests pass.
2026-04-20 03:14:04 +04:00
Manav Arya Singh c063bdb669 feat(extract): tier 2 — multi-page token reconciliation (shared vs per-route) (#36)
When --depth >= 1, the crawler now also returns per-route computedStyles.
The orchestrator runs the token extractors for each route and attaches
design.routes; the CLI emits three new artifacts:

- <prefix>-tokens-shared.json   — tokens present with the same value across
                                  every route
- <prefix>-tokens-routes/<slug>.json — per-route { added, changed }
- <prefix>-routes-report.md    — human-readable summary

A new formatters/routes-reconciliation.js exports reconcileRoutes() and
formatRoutesReport(). Slug generation collapses '/' to 'index' and resolves
collisions with numeric suffixes. The existing merged-depth extraction
continues to work unchanged — this is additive.
2026-04-19 03:36:13 +04:00