412 Commits

Author SHA1 Message Date
Manav Arya Singh 47f75bb68c Merge pull request #189 from Manavarya09/feat/phase-a-trust
Phase A: releases, install, MCP v2, CI gate, and a reproducible benchmark
2026-09-16 09:31:33 +04:00
Manav Arya Singh 08113882ce ci(bench): run the benchmark on a clean runner; fix the report footer and median
- .github/workflows/bench.yml: manual (choose a set) plus weekly, installs the
  browser, runs held-out and training sets, writes both tables to the run
  summary and uploads bench/results as an artifact. Nothing is auto-committed.
- the report names the set it ran (a held-out run said bench/sites.json) and
  says when it is the held-out set
- median time rounds to one decimal (it printed 30.349999999999998s)
2026-09-16 09:28:26 +04:00
Manav Arya Singh 2d0e2d4134 fix(bench): make the per-run timeout actually end a run
Runs were recorded as timed out while still going: 363s for a 180s cap on
designlang, 3029s once for dembrandt through npx. Killing the direct child left
grandchildren (npx's node, Playwright's Chromium in its own process group)
alive and holding stdout open, and the runner waited for the pipes to close.

- each tool runs detached with its own TMPDIR, where the browser profile lands;
  on timeout the process group is killed, plus anything whose command line
  names that directory, and the result is recorded without waiting for pipes
- a timeout is recorded as a timeout, timed at the kill rather than after cleanup
- a run that fails with a network-level error (ERR_INTERNET_DISCONNECTED and
  similar) is retried once for either tool; the report counts retries
- BENCH_TIMEOUT_MS overrides the 180s cap

Verified with a 15s cap on airbnb.com: both runs end at 15s and no browser
processes remain.
2026-09-15 20:03:51 +04:00
Manav Arya Singh 203aeac1d0 feat(bench): held-out ground truth for 17 sites no extraction change was tuned on
Same sourcing and live-verification rules as bench/sites.json. Each colour
records whether its hex is stated in official material or only in secondary
sources. 15 sites are scored on colour and 14 on font; three are excluded with
the reason recorded.
2026-09-15 17:55:34 +04:00
Manav Arya Singh 42047041da docs(changelog): 13.3.0 extraction accuracy fixes found by the benchmark 2026-09-15 17:52:06 +04:00
Manav Arya Singh 31bd34f487 fix(extraction): code faces aren't body fonts; weigh colour evidence by chroma
Measured by replaying the extractors against cached crawls of the 27
benchmark sites, so only the code varied between runs.

Typography: syntax highlighting draws every token as its own text element, so
counting text-rendering elements made plexMono (tailwindcss, 450 vs Inter 131),
commitMono (resend) and Departure Mono (supabase) the body font. Families whose
name marks a code face are labelled "mono" and sort after the text faces.

Colours: usage, interactive backgrounds and area now count in proportion to
chroma (full weight from chroma 30), and area scores 60*sqrt(share).
Near-black #101214 (chroma 1.5) had won atlassian on 702 uses, #231e15 had
won mailchimp, and one element painting 84% of discord.com had won on area.

Replay, previous -> this: primary colour 10/17 -> 13/17, body font
24/27 -> 27/27, no site regressed. Still missed: linear (brand colour on one
element), slack, paypal, supabase (a lighter shade of the brand green). The three
new tests fail on the previous code.
2026-09-15 17:51:10 +04:00
Manav Arya Singh 7fe2afcd98 bench: name results after the site set so a held-out run can't overwrite training
bench/sites.json still writes <date>.*; any other set writes <date>-<set>.*.
2026-09-15 17:13:42 +04:00
Manav Arya Singh 9b3c3dfdd9 fix(colors): rank brand colours by chroma, repetition and area, not one CTA
Measured per cluster on real sites, the linear weights let a single button
decide: 100 points per interactive background against at most ~3.5 for usage
and ~2.5 for painted area. gov.uk's CTA green on 4 buttons outranked the
GOV.UK blue, Framer's one #0066ff button outranked Framer Blue used 44 times,
and HSL saturation scored near-black #002533 (l=10) as vivid as a real brand
colour.

- interactive backgrounds: 40 * log2(1 + n), strong with diminishing returns
- chroma instead of HSL saturation
- 25 * log10(count) and 300 * areaShare so repetition and surface count
- the browser's default link colours (#0000ee, #551a8b) are never the brand

Re-extracted: gov.uk, duolingo and framer now return the brand colour; hubspot
unchanged; supabase (lighter shade of the brand green), paypal and linear still
miss. Three of the five new tests fail on the previous ranking.
2026-09-15 17:12:08 +04:00
Manav Arya Singh 63efb00fc3 fix(typography): count a font only where it renders text; stop fallbacks taking the body slot
Head and metadata elements carry the browser default (Times) without drawing
anything. On gov.uk they were 35 of Times's 36 uses; on dropbox.com Times had
1257 elements but only 90 with text, against 201 for Atlas Grotesk, so
designlang reported Times as dropbox's body font.

- families count only text-rendering elements (records without hasText, from
  older snapshots, still count)
- a family used on neither headings nor body copy is labelled "other", not body
- React/MUI theme, Tailwind and CSS vars give the body slot to the most used
  body-capable family and never overwrite it (supabase's tailwind body had
  become Source Code Pro)
2026-09-15 17:07:20 +04:00
Manav Arya Singh 8737c427a0 bench: record the baseline, losses included
designlang 13.3.0 vs dembrandt 0.33.0 on 27 sites:
primary colour 9/17 vs 12/17, body font 25/27 vs 25/27, median 12.6s vs 24.6s.
Kept as the baseline so fixes are measured against it, not a moving target.
Also print unscored dimensions as - rather than a miss in the console.
2026-09-15 16:58:30 +04:00
Manav Arya Singh 0ef94fcdd1 docs(readme): add Trendshift daily and weekly badges 2026-09-15 16:41:04 +04:00
Manav Arya Singh 72d8e25c01 docs(readme): add Trendshift daily and weekly badges 2026-09-15 16:37:30 +04:00
Manav Arya Singh 49fcb190c1 fix(cli): --json output was truncated at 64KB when piped
process.exit() straight after process.stdout.write() drops whatever the pipe
hasn't flushed. `designlang https://gov.uk --json | jq` received exactly
65536 bytes of a 70583-byte document, so any site with a large design broke
JSON consumers in CI. Found by the benchmark runner, which reads through a pipe.

- writeThenExit(): exit once the write is handed to the OS; used by extraction
  --json and drift --json (which exits non-zero on drift right after writing)
- the export command's error path uses the new exit codes
- bench: a family designlang marks usage "all" counts as body text, and both
  readers take the most used body family
2026-09-15 16:34:53 +04:00
Manav Arya Singh 226c54d654 feat(bench): sourced, live-verified ground truth for 27 sites
Brand colours come from official brand guidelines and design-system docs, and
count only when bench/evidence.mjs finds them painted on the live homepage;
fonts are the family that actually renders body copy. 17 sites are scored on
colour, all 27 on font. Three sites are excluded with the reason recorded.
2026-09-15 16:12:10 +04:00
Manav Arya Singh b73e1a6b27 fix(bench): don't score a dimension that has no ground truth
Several brands are monochrome or render body text in a system stack, so no
sourced brand colour or font appears on the live page. Scoring either tool
against an absent truth would be noise. Such a dimension now scores null and
is left out of that dimension's denominator; a failed run still counts as a
miss wherever truth exists.
2026-09-15 16:08:13 +04:00
Manav Arya Singh 46c504a2bd feat(bench): reproducible head-to-head extraction benchmark tooling
- bench/score.js: unit-tested scoring. Primary colour counts within CIE76
  dE 10 of the brand colour; body font by normalised family name. The same
  reading rule for both tools: the family labelled body, else the most used
- bench/run.mjs: designlang (this checkout) vs pinned dembrandt 0.33.0,
  default settings, sequential, alternating order per site, hard timeout,
  writes bench/results/<date>.{json,md} with losses included
- bench/evidence.mjs: ground-truth evidence independent of either extractor
  (the family that actually renders body copy; closest painted colour to each
  claimed brand colour)
- keep bench/ out of the npm tarball
2026-09-15 15:57:20 +04:00
Manav Arya Singh 13cf3aed17 feat(ci): distinct exit codes, a pinned and gating Action; release 13.3.0
Every failure exited 1, so a CI gate could not tell a design regression from a
flaky preview deploy.

- exit 0 ok, 1 drift over threshold, 2 extraction failed, 3 navigation timeout;
  wired into extraction, drift, ci and lint
- the Action installs the designlang version it was tagged with, runs
  install-browser, fails on drift by default, annotates each changed token
  (escaped), says why a job failed, and lists Manavarya09 as author
- fix changed-count being written as "0" twice when nothing changed
- README: install-browser, live MCP tools, CLI reference
- 13.3.0 changelog with the breaking changes called out
2026-09-15 15:54:34 +04:00
Manav Arya Singh a6b8671864 feat(mcp): v2 server extracts live URLs as jobs; serve version-less initialize
The MCP server could only read an extraction already on disk, so an agent
could not point it at a URL.

- extract_design returns a job id; get_job_status, list_jobs, cancel_job
- get_tokens, get_colors, get_typography, get_components by job id
- compute_drift (job vs baseline job), get_findings (token lint + WCAG),
  export (dtcg, tailwind, shadcn, figma, css, design-md)
- existing folder-backed tools and resources are unchanged
- an initialize without protocolVersion is served on the default revision
  instead of rejected (#182, #183)
2026-09-15 15:48:24 +04:00
Manav Arya Singh 037db816b4 feat(install): drop the postinstall Chromium download; add install-browser and one launcher
postinstall downloaded Chromium on every npm install, which fails in CI, Docker
and behind proxies. Browsers are now installed on demand.

- designlang install-browser [--with-deps] installs the Chromium that matches
  the bundled playwright
- src/browser.js launchChromium(): bundled Chromium, then system Chrome, then a
  BROWSER_UNAVAILABLE error that names the fix; all 13 launch sites use it
- doctor, the extraction error and smithery.dockerfile point at install-browser
2026-09-15 15:45:12 +04:00
Manav Arya Singh ddd4ad1b2c ci(release): publish to npm on version tags; fix MCP version and designlang/mcp entry
- release.yml publishes with provenance when a vX.Y.Z tag matches package.json
- MCP serverInfo reports the package version instead of a hard-coded 7.0.0
- add src/mcp/index.js, which the ./mcp export pointed at but never existed
- test a stock SDK client handshake over stdio
2026-09-15 15:41:20 +04:00
Manav Arya Singh 591b87d666 Credit Manav Arya Singh and add repository metadata
Adds author, repository, homepage and bugs to package.json so the npm package links back to this repo, a byline under the README badges, and points the footer credit at www.manavaryasingh.com.

Claude-Session: https://claude.ai/code/session_01BuAT6TU8R8GHQh5CLPHNxJ
2026-09-15 10:37:18 +04:00
dependabot[bot] 675cc6aa31 chore(deps): bump the npm_and_yarn group across 2 directories with 4 updates (#187)
Bumps the npm_and_yarn group with 1 update in the /vscode-extension directory: [js-yaml](https://github.com/nodeca/js-yaml).
Bumps the npm_and_yarn group with 2 updates in the /website directory: [next](https://github.com/vercel/next.js) and [baseline-browser-mapping](https://github.com/web-platform-dx/baseline-browser-mapping).


Updates `js-yaml` from 4.3.1 to 4.3.2
- [Changelog](https://github.com/nodeca/js-yaml/blob/4.3.2/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.3.1...4.3.2)

Updates `next` from 16.3.2 to 16.3.4
- [Release notes](https://github.com/vercel/next.js/releases)
- [Commits](https://github.com/vercel/next.js/compare/v16.3.2...v16.3.4)

Updates `baseline-browser-mapping` from 2.10.19 to 2.11.22
- [Release notes](https://github.com/web-platform-dx/baseline-browser-mapping/releases)
- [Commits](https://github.com/web-platform-dx/baseline-browser-mapping/compare/v2.10.19...v2.11.22)

Updates `sharp` from 0.35.3 to 0.35.4
- [Release notes](https://github.com/lovell/sharp/releases)
- [Commits](https://github.com/lovell/sharp/compare/v0.35.3...v0.35.4)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 4.3.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: next
  dependency-version: 16.3.4
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: baseline-browser-mapping
  dependency-version: 2.11.22
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: sharp
  dependency-version: 0.35.4
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-11 04:48:19 +00:00
dependabot[bot] 7e12905ca5 chore(deps): bump hono in the npm_and_yarn group across 1 directory (#186)
Bumps the npm_and_yarn group with 1 update in the / directory: [hono](https://github.com/honojs/hono).


Updates `hono` from 4.13.4 to 4.13.7
- [Release notes](https://github.com/honojs/hono/releases)
- [Commits](https://github.com/honojs/hono/compare/v4.13.4...v4.13.7)

---
updated-dependencies:
- dependency-name: hono
  dependency-version: 4.13.7
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-10 02:08:00 +00:00
dependabot[bot] c7eb8b0d6e chore(deps): bump qs in the npm_and_yarn group across 1 directory (#185)
Bumps the npm_and_yarn group with 1 update in the / directory: [qs](https://github.com/ljharb/qs).


Updates `qs` from 6.15.2 to 6.16.0
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.15.2...v6.16.0)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.16.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-08 20:16:34 +00:00
Manav Arya Singh a9e832efe3 Merge pull request #184 from Manavarya09/feat/extraction-depth
feat: extraction depth pass — type, elevation, layout and colour systems (v13.2.0)
2026-08-31 10:14:52 +04:00
Manav Arya Singh 92b0b1bf0f docs: v13.2.0 — surface the new systems in DESIGN.md and the changelog
DESIGN.md (the agent-native single file) now carries the tonal ramps,
measured contrast pairs and area dominance in Colors; the ratio, measure,
fluid summary and named scale in Typography; the content column, column
system, section rhythm and gap scale in Layout; and the elevation ladder
with its redundancy figure in Elevation.
v13.2.0
2026-08-31 10:09:12 +04:00
Manav Arya Singh cd7d968b52 fix(extraction): tighten measure, container and role naming against real sites
Running the new extractors against Stripe, Linear, Vercel and Tailwind
surfaced four ways the heuristics lied:

- **Measure read paragraph boxes, not text.** A <p> is as wide as its
  container; its text usually is not, so Stripe reported 22 characters
  per line. The crawler now measures real line boxes with a Range over
  each prose element (budgeted at 120 elements) and the extractor takes
  the median of the reading column only — the narrow <p> furniture in
  navs and footers is excluded. Stripe now reports 53, Linear 47.
- **The content column was whatever repeated hardest** — a 400px card on
  Stripe. It is now the constrained width governing the most page area,
  with a genuine full-bleed shell reported as `fullBleed` plus the widest
  constrained block inside it, rather than having some card promoted in
  its place.
- **`display` went to the first untagged size**, which put a 32px step
  above a 48px <h1>. It is now reserved for the largest step on the page;
  other untagged sizes become ordered title rungs.
- **Fluid ranges serialized as null** via Infinity when no declaration
  resolved to px (`min(var(--x), 2.75vw)`), and measure asserted a verdict
  off a single sample. Both now carry their own caveat — `resolvedCount`
  and a confidence level.
2026-08-31 10:09:12 +04:00
Manav Arya Singh d417afef2c feat(colors): tonal ramps, measured semantic pairs, area-weighted dominance
Extraction handed back one hex per role and left every consumer to invent
the rest of the palette by hand. Three additions:

- **Tonal ramps.** A 50–950 ladder per role plus the dominant neutral,
  generated in OKLCH so the steps are perceptually even — HSL lightness
  is not, which is why hand-built ramps drift. Chroma peaks mid-ladder
  and falls off at both ends, and the site's own colour keeps its rung
  (reported as `anchor`), so the ramp contains the real brand colour
  rather than a re-derived approximation of it.
- **Semantic pairs.** surface.page, surface.raised and the action colours,
  each paired with the foreground that actually reaches contrast on it,
  with the ratio and WCAG level attached. A colour the site already uses
  for text wins; black or white is a marked fallback.
- **Area-weighted dominance.** Colour usage counted elements, so two
  hundred 16px icons outranked a hero background. Backgrounds now
  accumulate painted area, exposed as `dominance` and folded into brand
  ranking as a weak term.

Ramps and pairs are emitted through css-vars and the DTCG tokens, where
semantic action/surface tokens now carry a measured `onX` foreground.
Also stops `semantic.shadow.elevated` pointing at sh0 — the faintest
hairline on the page — in favour of a mid rung of the elevation ladder.
2026-08-31 10:09:12 +04:00
Manav Arya Singh e326c8bfec feat(layout): derive the layout system, not just a grid inventory
extractLayout counted grids and listed every max-width it saw. What a
clone actually needs is the system underneath: the content column width
and its gutters (read from rendered width, so percentage and clamp()
containers land in the ladder too), the column count the grids agree on
weighted by governed area rather than by instance count, the vertical
rhythm full-bleed sections use — the number that decides whether a clone
feels right or cramped, and one that never survives a spacing scale built
from every padding on the page — a numeric gap ladder, and the fluid
max-width/gap declarations.

Also fixes `gaps`, which was string-sorted: '10px' ranked before '4px'.
2026-08-31 10:09:12 +04:00
Manav Arya Singh 0ba177a78a feat(typography): resolve the size dump into an actual type system
`typography.scale` was an inventory — every distinct px size found, in
descending order, with no way to tell a display size from body copy. Four
additions turn it into a system:

- **Modular ratio inference.** Fits the scale against the eight standard
  ratios with a power penalty (without one, a minor second explains any
  scale by raising itself to the 4th) and reports fit, ladder coverage and
  the anchor size, or refuses to name a ratio at all.
- **Named scale.** Every step gets a usable role — heading tags claim
  their own, the rest are placed relative to body size.
- **Fluid type.** Computed styles resolve clamp() to one px value at the
  capture viewport, so a site's responsive ramp was invisible. The crawler
  now harvests authored fluid declarations off the stylesheets (including
  cross-origin sheets fetched as text) and the extractor parses them into
  min/max px — flagging clamps whose preferred term has no viewport unit
  and therefore never scale.
- **Measure.** Characters per line for body copy, with a narrow/
  comfortable/wide verdict. Needed rendered box widths, so computed styles
  now carry width, height and document-relative top.

Also fixes the DTCG `semantic.typography.body` token, which read from
scale[0] — the largest size — and so emitted the headline size as body
text. It now resolves the real body step, and primitive font sizes are
keyed by role rather than index.
2026-08-31 10:09:12 +04:00
Manav Arya Singh e421704d84 feat(shadows): parse multi-layer shadows and derive an elevation ladder
Composite box-shadow values (`0 1px 2px …, 0 10px 15px -3px …` — what
Tailwind and most design systems emit) were parsed as a single shadow:
the colour regex grabbed the first layer's colour and the numeric regex
swept offsets across every layer, so blur, offset and spread were wrong
for the majority of real sites.

Layers are now split on top-level commas and parsed individually; the
composite reports from the key (widest) layer, so an ambient hairline
stacked under a key shadow no longer drags the reported elevation down.

On top of that the raw shadow set collapses into an ordered elevation
ladder — buckets within 25% visual weight merge, the most-used raw value
wins its bucket — plus per-shadow tint (neutral vs brand-coloured, with
hue and alpha) and a system summary carrying level count and redundancy.

The css-vars, DTCG tokens and Tailwind emitters now key off the ladder.
They previously keyed off `label`, which collides whenever a site has
more than one shadow in the same size band — those tokens were silently
overwriting each other.
2026-08-31 10:09:12 +04:00
dependabot[bot] f7c2bec663 chore(deps): bump the npm_and_yarn group across 3 directories with 13 updates (#181)
Bumps the npm_and_yarn group with 5 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@hono/node-server](https://github.com/honojs/node-server) | `1.19.14` | `1.19.17` |
| [body-parser](https://github.com/expressjs/body-parser) | `2.2.2` | `2.3.0` |
| [fast-uri](https://github.com/fastify/fast-uri) | `3.1.2` | `3.1.6` |
| [hono](https://github.com/honojs/hono) | `4.12.26` | `4.13.4` |
| [ip-address](https://github.com/beaugunderson/ip-address) | `10.2.0` | `10.5.0` |

Bumps the npm_and_yarn group with 5 updates in the /vscode-extension directory:

| Package | From | To |
| --- | --- | --- |
| [fast-uri](https://github.com/fastify/fast-uri) | `3.1.2` | `3.1.6` |
| [brace-expansion](https://github.com/juliangruber/brace-expansion) | `5.0.5` | `5.0.9` |
| [brace-expansion](https://github.com/juliangruber/brace-expansion) | `1.1.14` | `1.1.18` |
| [js-yaml](https://github.com/nodeca/js-yaml) | `4.2.0` | `4.3.1` |
| [linkify-it](https://github.com/markdown-it/linkify-it) | `5.0.1` | `5.0.2` |
| [undici](https://github.com/nodejs/undici) | `7.28.0` | `7.29.0` |

Bumps the npm_and_yarn group with 4 updates in the /website directory: [undici](https://github.com/nodejs/undici), [next](https://github.com/vercel/next.js), [nanoid](https://github.com/ai/nanoid) and [postcss](https://github.com/postcss/postcss).


Updates `@hono/node-server` from 1.19.14 to 1.19.17
- [Release notes](https://github.com/honojs/node-server/releases)
- [Commits](https://github.com/honojs/node-server/compare/v1.19.14...v1.19.17)

Updates `body-parser` from 2.2.2 to 2.3.0
- [Release notes](https://github.com/expressjs/body-parser/releases)
- [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/body-parser/compare/v2.2.2...v2.3.0)

Updates `fast-uri` from 3.1.2 to 3.1.6
- [Release notes](https://github.com/fastify/fast-uri/releases)
- [Commits](https://github.com/fastify/fast-uri/compare/v3.1.2...v3.1.6)

Updates `hono` from 4.12.26 to 4.13.4
- [Release notes](https://github.com/honojs/hono/releases)
- [Commits](https://github.com/honojs/hono/compare/v4.12.26...v4.13.4)

Updates `ip-address` from 10.2.0 to 10.5.0
- [Release notes](https://github.com/beaugunderson/ip-address/releases)
- [Commits](https://github.com/beaugunderson/ip-address/compare/v10.2.0...v10.5.0)

Updates `fast-uri` from 3.1.2 to 3.1.6
- [Release notes](https://github.com/fastify/fast-uri/releases)
- [Commits](https://github.com/fastify/fast-uri/compare/v3.1.2...v3.1.6)

Updates `brace-expansion` from 5.0.5 to 5.0.9
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v5.0.5...v5.0.9)

Updates `brace-expansion` from 1.1.14 to 1.1.18
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v5.0.5...v5.0.9)

Updates `js-yaml` from 4.2.0 to 4.3.1
- [Changelog](https://github.com/nodeca/js-yaml/blob/4.3.1/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.2.0...4.3.1)

Updates `linkify-it` from 5.0.1 to 5.0.2
- [Changelog](https://github.com/markdown-it/linkify-it/blob/master/CHANGELOG.md)
- [Commits](https://github.com/markdown-it/linkify-it/compare/5.0.1...5.0.2)

Updates `undici` from 7.28.0 to 7.29.0
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v7.28.0...v7.29.0)

Updates `undici` from 6.27.0 to 6.28.0
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v7.28.0...v7.29.0)

Updates `next` from 16.2.6 to 16.3.2
- [Release notes](https://github.com/vercel/next.js/releases)
- [Commits](https://github.com/vercel/next.js/compare/v16.2.6...v16.3.2)

Updates `nanoid` from 3.3.11 to 3.3.18
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/3.3.18/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.3.11...3.3.18)

Updates `postcss` from 8.5.10 to 8.5.26
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.5.10...8.5.26)

Updates `sharp` from 0.34.5 to 0.35.3
- [Release notes](https://github.com/lovell/sharp/releases)
- [Commits](https://github.com/lovell/sharp/compare/v0.34.5...v0.35.3)

---
updated-dependencies:
- dependency-name: "@hono/node-server"
  dependency-version: 1.19.17
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: body-parser
  dependency-version: 2.3.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: fast-uri
  dependency-version: 3.1.6
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: hono
  dependency-version: 4.13.4
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: ip-address
  dependency-version: 10.5.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: fast-uri
  dependency-version: 3.1.6
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: brace-expansion
  dependency-version: 5.0.9
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: brace-expansion
  dependency-version: 1.1.18
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: js-yaml
  dependency-version: 4.3.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: linkify-it
  dependency-version: 5.0.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: undici
  dependency-version: 7.29.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: undici
  dependency-version: 6.28.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: next
  dependency-version: 16.3.2
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: nanoid
  dependency-version: 3.3.18
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: postcss
  dependency-version: 8.5.26
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: sharp
  dependency-version: 0.35.3
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-24 10:18:50 +00:00
Manav Arya Singh 73977ce180 Merge pull request #180 from Manavarya09/feat/design-dna
feat(dna): a measured design space — nearest systems, per-axis percentiles, outliers
2026-08-24 14:14:59 +04:00
Manav Arya Singh 18232b5a1a docs(dna): document the measured design space, v13.1.0
README gains a section on what `dna` measures and — as important — the three
things it refuses to do: invent a missing measurement, print a number
without its evidence, or compare across vector versions.

Registers /dna as the fourteenth plugin command and bumps the manifests.
2026-08-24 14:11:00 +04:00
Manav Arya Singh 2ade86c881 test(dna): the properties that keep the space honest
Unit coverage aside, these are the acceptance criteria for the space itself:

  * two dark minimal systems land nearer each other than either does to a
    playful one — the space discriminates by design family
  * extraction noise moves a vector by <0.02 while a real restyle moves it
    an order of magnitude further — it measures design, not jitter
  * features either side is missing are skipped, never invented
  * a corpus built for another vector version is refused, not compared

Two of these caught real bugs against live sites: the extractors emit HSL
on a 0-100 scale while the readers assumed 0-1, and font weights and
line-heights arrive as CSS strings. Stripe went from 93% to 100% of features
measurable once both were fixed.
2026-08-24 14:11:00 +04:00
Manav Arya Singh e2e15e9859 feat(dna): designlang dna and dna-corpus commands
`dna <url>` places a site in the design space and prints where it landed;
`dna-corpus <urls...>` builds the reference frame it ranks against, so teams
can measure against their own products or competitors rather than the
shipped corpus.

`grade` gains one line naming the nearest design system beside the letter.
A grade with no reference frame is half a claim. It is wrapped so DNA can
never be the reason a grade fails.

One unreachable site during a corpus build is reported and skipped rather
than discarding every other extraction in the run.
2026-08-24 14:11:00 +04:00
Manav Arya Singh b86b490bbd feat(dna): runner and the default 8-system corpus
`analyze()` places an extracted design in the space; `writeDnaOutputs()`
emits the vector plus a readable report. Both stay pure of CLI concerns so
the pieces remain testable on their own.

The shipped corpus is extracted live from linear, vercel, stripe, notion,
duolingo, netflix, figma and railway, so `designlang dna` has a reference
frame out of the box. A missing corpus is treated as "no reference frame"
rather than an error — the vector is still worth writing.
2026-08-24 14:08:54 +04:00
Manav Arya Singh cb3dd96cef feat(dna): the report
Everything else produces numbers; this says what they mean without
overclaiming. It never prints a distance without how many features backed
it, and never a percentile without naming the corpus it ranks against — a
design score with no stated reference frame is the thing this replaces.

Percentiles are framed as ranks, not judgements. A design far from the
middle of the corpus is unusual, which may be exactly the intent.
2026-08-24 14:08:54 +04:00
Manav Arya Singh e519ed5b83 feat(dna): corpus, percentiles and outlier features
A vector on its own says nothing — "radius 0.31" only means something next
to what real design systems do. The corpus is that reference frame, and
percentiles turn an absolute measurement into a rank against it.

`outliers()` answers the question this whole feature exists for: what makes
a design look the way it does, as the features furthest from the middle of
the corpus.

Loading checks the corpus version before anything compares against it. A
silently stale corpus is worse than no corpus, because it still produces
confident percentiles.
2026-08-24 14:08:54 +04:00
Manav Arya Singh 3f471d73e6 feat(dna): distance that explains itself
Mean absolute difference across the features both designs actually have,
reported per axis as well as overall — "0.31 apart, mostly on motion" is
the finding; a single opaque number is not.

Mean-absolute rather than Euclidean because the output has to be readable:
0.31 means the average feature is 31% of its range apart, which a person can
reason about. Nobody has intuition for Euclidean distance in 30 dimensions.

Every result carries the count of features that backed it. A distance from
four surviving features is a weaker claim than one from thirty, and callers
are given what they need to say so.
2026-08-24 14:08:54 +04:00
Manav Arya Singh da3c4c7fca feat(dna): reduce a design to a frozen 30-feature vector
Every designlang command so far answers what a design uses. None answers
where it sits, because there is no coordinate system to place it in.

`features.js` reads 30 measurements off an already-extracted design across
five axes — colour, type, space, shape, motion — keeping them in their
natural units so they stay readable in reports. `vector.js` normalizes them
into 0..1 against the ranges real design systems occupy.

Two rules make the numbers mean something later:

  * FEATURE_ORDER and the normalizers are frozen per VECTOR_VERSION, so a
    score published today still means the same thing next year.
  * A measurement the page never exposed stays null the whole way through.
    Substituting a midpoint would be indistinguishable from a real one and
    would quietly move the design in the space.
2026-08-24 14:08:54 +04:00
Manav Arya Singh 64e2d38bdc fix(cli): honour options typed after a subcommand
The root command declares both a positional `<url>` and `-o, --out`, so
Commander claimed any option appearing after a subcommand for the root
command, where the root's own `-o` shadowed the subcommand's. Every
subcommand's `-o`/`-n` was silently dropped and output always landed in
`./design-extract-output`, whatever the user asked for.

`enablePositionalOptions()` gives options typed after a subcommand back to
that subcommand. This also fixes three PDF renderer tests that had been
failing because the flags they passed never reached the command.
2026-08-24 14:08:54 +04:00
Manav Arya Singh 9809354a05 Merge pull request #178 from Manavarya09/feat/doctor-command
feat(cli): add `designlang doctor` health-check command
2026-08-22 07:39:36 +04:00
Manav Arya Singh d6c60b0a7f feat(cli): add designlang doctor health-check command
Prints a one-screen sanity check of the local install so a failing
extraction can be diagnosed in seconds: Node version against the
`engines` floor, designlang version, playwright version, whether the
resolved Chromium binary actually exists on disk, output-dir
writability, and network reachability.

Failing checks print a fix hint. Exits 0 when everything passes, 1 when
any check fails. The network probe uses a 3s timeout and reports WARN
rather than FAIL so an offline environment does not produce a
misleading exit code.

No new dependencies. Closes #107.
2026-08-22 07:36:59 +04:00
Manav Arya Singh ba56e9162d Merge pull request #177 from Manavarya09/feat/fidelity-loop
Release 13.0.0 (final) — Theatre + big /watch + 37 brand books
2026-06-25 12:38:07 +04:00
Manav Arya Singh 5687ea0d96 Merge pull request #176 from Manavarya09/feat/gallery-brandbooks
feat: 37 real brand books + big /watch with all files below
2026-06-25 12:33:01 +04:00
Manav Arya Singh 6aaa9ac645 feat(gallery): _meta palettes for the 24 v13 brand books 2026-06-25 12:31:52 +04:00
Manav Arya Singh 7c97c76e33 feat(gallery): pre-rendered brand book — v0.dev 2026-06-25 12:31:52 +04:00
Manav Arya Singh 96da0c455b feat(gallery): pre-rendered brand book — planetscale.com 2026-06-25 12:31:52 +04:00
Manav Arya Singh fad87f03bc feat(gallery): pre-rendered brand book — retool.com 2026-06-25 12:31:37 +04:00