designlang 7.0.0 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/og-preview.png +0 -0
- package/.github/workflows/manavarya-bot.yml +17 -0
- package/.vercel/README.txt +11 -0
- package/.vercel/project.json +1 -0
- package/CHANGELOG.md +29 -0
- package/CONTRIBUTING.md +25 -0
- package/README.md +38 -11
- package/bin/design-extract.js +41 -2
- package/chrome-extension/README.md +41 -0
- package/chrome-extension/icons/favicon.svg +7 -0
- package/chrome-extension/icons/icon-128.png +0 -0
- package/chrome-extension/icons/icon-16.png +0 -0
- package/chrome-extension/icons/icon-32.png +0 -0
- package/chrome-extension/icons/icon-48.png +0 -0
- package/chrome-extension/manifest.json +26 -0
- package/chrome-extension/popup.html +167 -0
- package/chrome-extension/popup.js +59 -0
- package/docs/superpowers/specs/2026-04-18-website-redesign-design.md +120 -0
- package/docs/superpowers/specs/2026-04-19-designlang-v7-1-design.md +111 -0
- package/package.json +1 -1
- package/src/config.js +5 -1
- package/src/crawler.js +361 -2
- package/src/extractors/interaction-states.js +57 -0
- package/src/extractors/modern-css.js +100 -0
- package/src/extractors/token-sources.js +65 -0
- package/src/extractors/wide-gamut.js +47 -0
- package/src/formatters/routes-reconciliation.js +160 -0
- package/src/index.js +29 -0
- package/src/utils/color-gamut.js +82 -0
- package/src/utils-cookies.js +73 -0
- package/tests/cookies.test.js +98 -0
- package/tests/interaction-states.test.js +62 -0
- package/tests/modern-css.test.js +104 -0
- package/tests/routes-reconciliation.test.js +120 -0
- package/tests/wide-gamut.test.js +90 -0
- package/website/app/api/extract/route.js +216 -56
- package/website/app/components/A11ySlider.js +369 -0
- package/website/app/components/Comparison.js +286 -0
- package/website/app/components/CssHealth.js +243 -0
- package/website/app/components/HeroExtractor.js +455 -0
- package/website/app/components/Marginalia.js +3 -0
- package/website/app/components/McpSection.js +223 -0
- package/website/app/components/PlatformTabs.js +250 -0
- package/website/app/components/RegionsComponents.js +429 -0
- package/website/app/components/Rule.js +13 -0
- package/website/app/components/Specimens.js +237 -0
- package/website/app/components/StructuredData.js +144 -0
- package/website/app/components/TokenBrowser.js +344 -0
- package/website/app/components/token-browser-sample.js +65 -0
- package/website/app/globals.css +415 -633
- package/website/app/icon.svg +7 -0
- package/website/app/layout.js +113 -6
- package/website/app/opengraph-image.js +170 -0
- package/website/app/page.js +372 -148
- package/website/app/robots.js +15 -0
- package/website/app/seo-config.js +82 -0
- package/website/app/sitemap.js +18 -0
- package/website/lib/cache.js +73 -0
- package/website/lib/rate-limit.js +30 -0
- package/website/lib/rate-limit.test.js +55 -0
- package/website/lib/specimens.json +86 -0
- package/website/lib/token-helpers.js +70 -0
- package/website/lib/url-safety.js +103 -0
- package/website/lib/url-safety.test.js +116 -0
- package/website/lib/zip-files.js +15 -0
- package/website/package-lock.json +85 -0
- package/website/package.json +1 -0
- package/website/public/favicon.svg +7 -0
- package/website/public/logo-specimen.svg +76 -0
- package/website/public/mark.svg +12 -0
- package/website/public/site.webmanifest +13 -0
- package/website/app/favicon.ico +0 -0
- package/website/public/file.svg +0 -1
- package/website/public/globe.svg +0 -1
- package/website/public/next.svg +0 -1
- package/website/public/vercel.svg +0 -1
- package/website/public/window.svg +0 -1
|
Binary file
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Managed by bot-manavarya/reviewer — edits will be overwritten.
|
|
2
|
+
name: manavarya-bot review
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
pull_request:
|
|
6
|
+
types: [opened, synchronize, reopened, ready_for_review]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
review:
|
|
10
|
+
if: ${{ github.event.pull_request.draft == false }}
|
|
11
|
+
uses: bot-manavarya/reviewer/.github/workflows/review.yml@main
|
|
12
|
+
with:
|
|
13
|
+
provider: 'gemini'
|
|
14
|
+
model: 'gemini-2.5-flash'
|
|
15
|
+
secrets:
|
|
16
|
+
bot-token: ${{ secrets.MANAVARYA_BOT_TOKEN }}
|
|
17
|
+
gemini-api-key: ${{ secrets.GEMINI_API_KEY }}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
> Why do I have a folder named ".vercel" in my project?
|
|
2
|
+
The ".vercel" folder is created when you link a directory to a Vercel project.
|
|
3
|
+
|
|
4
|
+
> What does the "project.json" file contain?
|
|
5
|
+
The "project.json" file contains:
|
|
6
|
+
- The ID of the Vercel project that you linked ("projectId")
|
|
7
|
+
- The ID of the user or team your Vercel project is owned by ("orgId")
|
|
8
|
+
|
|
9
|
+
> Should I commit the ".vercel" folder?
|
|
10
|
+
No, you should not share the ".vercel" folder with anyone.
|
|
11
|
+
Upon creation, it will be automatically added to your ".gitignore" file.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"projectId":"prj_g6gBbA5TU1e3vLtU6w8FHJhy9Wmk","orgId":"team_NOd0BEOy4vNKC9vTwIRo0qdF","projectName":"website"}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [7.2.0] — 2026-04-19
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **Modern CSS surfacing (Tier 1a)** — crawler now captures pseudo-elements, variable-font axes (`font-variation-settings`), `@container` queries, and `env()` usage. Surfaced on `design.modernCss`. (#33)
|
|
8
|
+
- **Wide-gamut color + CSS source attribution (Tier 1b)** — `oklch()`, `oklab()`, `color-mix()`, `light-dark()`, Display P3, and Rec2020 references are collected on `design.wideGamut`. A new `design.tokenSources` maps each extracted token to the stylesheet URL it first appeared in. (#34)
|
|
9
|
+
- **Auto-interact pass (Tier 2)** — new `--deep-interact` flag (implied by `--full`) runs an interaction pass before extraction: full-page scroll in 4 steps, menu/dropdown opens, hover snapshots for the first batch of buttons/links with computed-style diffs, accordion clicks, and first-match modal trigger. Results populate `design.interactionStates` (hover deltas, menu/modal snapshots). Every step is wrapped in try/catch with per-step timeouts so interaction failures never kill the crawl.
|
|
10
|
+
- **Multi-page token reconciliation (Tier 2)** — when `--depth >= 1` the extractor now emits three new artifacts alongside the merged baseline: `*-tokens-shared.json` (tokens shared across every route), `*-tokens-routes/<slug>.json` (per-route `added` and `changed` deltas), and `*-routes-report.md` (readable summary). Slugs are derived from the route path (`/` → `index`) with automatic collision handling.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- `--full` now also enables `--deep-interact`.
|
|
15
|
+
- `--depth <n>` description updated to mention the new reconciliation outputs.
|
|
16
|
+
|
|
17
|
+
## [7.1.0] — 2026-04-19
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **Cookie file support** — `--cookie-file <path>` loads cookies from a JSON array, a Playwright `storageState.json`, or a Netscape `cookies.txt` (browser extensions, curl exports). The new loader lives in `src/utils-cookies.js` and merges cleanly with the existing `--cookie name=value` flag.
|
|
22
|
+
- **`--insecure`** — ignores HTTPS/SSL certificate errors. Useful for self-signed dev servers, internal staging environments behind corporate proxies, and local extraction through MITM tools. Passes `ignoreHTTPSErrors: true` to the Playwright context plus the matching Chromium launch flags.
|
|
23
|
+
- **`--user-agent <ua>`** — override the browser User-Agent string for extraction.
|
|
24
|
+
- **Chrome extension** — `chrome-extension/` ships a Manifest v3 popup that hands the current tab off to [designlang.manavaryasingh.com](https://designlang.manavaryasingh.com) with the URL prefilled. Also emits a "Copy CLI" button that drops `npx designlang <url>` into the clipboard. Developer-mode install for now; Chrome Web Store listing pending.
|
|
25
|
+
- **Website URL query parameter** — the extractor input on the hosted site now honours `?url=<encoded>` so the Chrome extension (and any deep link) can prefill.
|
|
26
|
+
- **CONTRIBUTING**: "Good first issues" and "Credits" sections.
|
|
27
|
+
|
|
28
|
+
### Thanks
|
|
29
|
+
|
|
30
|
+
- A developer from China opened a conversation proposing cookie-file handling, SSL bypass, and a Chrome packaging story — this release ships all three.
|
|
31
|
+
|
|
3
32
|
## [7.0.0] — 2026-04-18
|
|
4
33
|
|
|
5
34
|
### Breaking
|
package/CONTRIBUTING.md
CHANGED
|
@@ -61,3 +61,28 @@ When filing a bug, please include:
|
|
|
61
61
|
- One feature/fix per PR
|
|
62
62
|
- Keep PRs focused and small when possible
|
|
63
63
|
- Add a brief description of what changed and why
|
|
64
|
+
|
|
65
|
+
## Good first issues
|
|
66
|
+
|
|
67
|
+
Some work that's already scoped and ready for someone to pick up:
|
|
68
|
+
|
|
69
|
+
- **Extra cookie formats** — add support for browser-specific exports (EditThisCookie, Cookie-Editor) on top of the existing JSON / Playwright / Netscape loaders in `src/utils-cookies.js`.
|
|
70
|
+
- **Proxy support** — add `--proxy <url>` that passes through to Playwright's `proxy` launch option, paired with the existing `--insecure` flag for corporate networks.
|
|
71
|
+
- **Chrome extension: DevTools panel** — extend `chrome-extension/` with a DevTools panel that shows tokens for the current tab without leaving the browser. See the roadmap in `chrome-extension/README.md`.
|
|
72
|
+
- **Firefox + Edge packaging** — the manifest v3 extension already works in both; we just need store listings and icons.
|
|
73
|
+
- **Additional CSS-in-JS emitters** — Emotion, Stitches, Vanilla Extract, Panda, styled-components. Each is a thin formatter module under `src/formatters/`.
|
|
74
|
+
|
|
75
|
+
## Credits
|
|
76
|
+
|
|
77
|
+
Shipped thanks to a few specific contributors — PRs welcome for the list:
|
|
78
|
+
|
|
79
|
+
- [@Manavarya09](https://github.com/Manavarya09) — author, maintainer.
|
|
80
|
+
- Community suggestions that landed as features: cookie-file loading, `--insecure` SSL bypass, and the first Chrome extension popup came from an external developer's proposal in mid-April 2026.
|
|
81
|
+
|
|
82
|
+
If you open a PR or file an issue that materially shapes a release, we'll add you here.
|
|
83
|
+
|
|
84
|
+
## Code of conduct
|
|
85
|
+
|
|
86
|
+
Be decent. Disagreements about design, code, or approach are welcome — personal
|
|
87
|
+
attacks are not. Maintainer reserves the right to close issues and PRs that
|
|
88
|
+
don't engage in good faith.
|
package/README.md
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<
|
|
3
|
-
<p align="center">Reverse-engineer any website's complete design system in one command.</p>
|
|
2
|
+
<img src="website/public/logo-specimen.svg" alt="designlang — reads a website the way a developer reads a stylesheet" width="900">
|
|
4
3
|
</p>
|
|
5
4
|
|
|
6
5
|
<p align="center">
|
|
7
|
-
<a href="https://www.npmjs.com/package/designlang"><img src="https://img.shields.io/npm/v/designlang?color=
|
|
8
|
-
<a href="https://github.com/Manavarya09/design-extract/blob/main/LICENSE"><img src="https://img.shields.io/github/license/Manavarya09/design-extract" alt="license"></a>
|
|
9
|
-
<a href="https://nodejs.org"><img src="https://img.shields.io/node/v/designlang" alt="node version"></a>
|
|
10
|
-
<a href="https://
|
|
6
|
+
<a href="https://www.npmjs.com/package/designlang"><img src="https://img.shields.io/npm/v/designlang?color=0A0908&labelColor=F3F1EA&label=npm" alt="npm version"></a>
|
|
7
|
+
<a href="https://github.com/Manavarya09/design-extract/blob/main/LICENSE"><img src="https://img.shields.io/github/license/Manavarya09/design-extract?color=0A0908&labelColor=F3F1EA" alt="license"></a>
|
|
8
|
+
<a href="https://nodejs.org"><img src="https://img.shields.io/node/v/designlang?color=0A0908&labelColor=F3F1EA" alt="node version"></a>
|
|
9
|
+
<a href="https://designlang.manavaryasingh.com/"><img src="https://img.shields.io/badge/website-live-FF4800?labelColor=F3F1EA" alt="website"></a>
|
|
11
10
|
</p>
|
|
12
11
|
|
|
13
12
|
---
|
|
@@ -312,6 +311,26 @@ A dedicated audit pass surfaced on `design.cssHealth`:
|
|
|
312
311
|
|
|
313
312
|
Also contributes a `cssHealth` dimension to the overall design score.
|
|
314
313
|
|
|
314
|
+
### 22. Chrome Extension (NEW in v7.1)
|
|
315
|
+
|
|
316
|
+
A Manifest-v3 popup lives in [`chrome-extension/`](chrome-extension/). One click on any tab opens `designlang.manavaryasingh.com` with the URL prefilled — no copy-paste, no context switch. There is also a **Copy CLI** button that puts `npx designlang <url>` in your clipboard.
|
|
317
|
+
|
|
318
|
+
- **Permissions:** `activeTab` only, plus host access to the hosted extractor.
|
|
319
|
+
- **Install:** toggle developer mode at `chrome://extensions`, click *Load unpacked*, pick the `chrome-extension/` folder.
|
|
320
|
+
- **Firefox + Edge** work with the same MV3 manifest.
|
|
321
|
+
|
|
322
|
+
### 23. Better Auth + Network Control (NEW in v7.1)
|
|
323
|
+
|
|
324
|
+
Extracting from authenticated, self-signed, or non-default environments now takes one flag:
|
|
325
|
+
|
|
326
|
+
- **`--cookie-file <path>`** — loads cookies from JSON array, Playwright `storageState.json`, or Netscape `cookies.txt` (browser extensions, curl exports). Merges cleanly with the existing `--cookie name=value` flag.
|
|
327
|
+
- **`--insecure`** — ignore HTTPS/SSL certificate errors for self-signed dev servers, corporate staging, or MITM tools.
|
|
328
|
+
- **`--user-agent <ua>`** — override the browser User-Agent string.
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
designlang https://staging.internal --cookie-file ./session.json --insecure
|
|
332
|
+
```
|
|
333
|
+
|
|
315
334
|
## All Features
|
|
316
335
|
|
|
317
336
|
| Feature | Flag / Command | Description |
|
|
@@ -326,12 +345,16 @@ Also contributes a `cssHealth` dimension to the overall design score.
|
|
|
326
345
|
| Font files | automatic | Source detection (Google/self-hosted/CDN/system), @font-face CSS |
|
|
327
346
|
| Image styles | automatic | Aspect ratios, shapes, filters, pattern classification |
|
|
328
347
|
| Dark mode | `--dark` | Extracts dark color scheme + light/dark diff |
|
|
329
|
-
| Auth pages | `--cookie`, `--header` | Extract from authenticated/protected pages |
|
|
330
|
-
|
|
|
348
|
+
| Auth pages | `--cookie`, `--cookie-file`, `--header` | Extract from authenticated/protected pages; cookie files in JSON / Playwright storageState / Netscape formats |
|
|
349
|
+
| Self-signed / dev TLS | `--insecure` | Ignore HTTPS/SSL certificate errors |
|
|
350
|
+
| User-Agent override | `--user-agent <ua>` | Set a custom User-Agent string |
|
|
351
|
+
| Chrome extension | `chrome-extension/` | One-click handoff from any tab, MV3, `activeTab` only |
|
|
352
|
+
| Multi-page | `--depth <n>` | Crawl N internal pages; emits shared-vs-per-route token reconciliation (`*-tokens-shared.json`, `*-tokens-routes/<slug>.json`, `*-routes-report.md`) |
|
|
331
353
|
| Screenshots | `--screenshots` | Capture buttons, cards, inputs, nav, hero, full page |
|
|
332
354
|
| Responsive | `--responsive` | Crawl at 4 viewports, map breakpoint changes |
|
|
333
355
|
| Interactions | `--interactions` | Capture hover/focus/active state transitions |
|
|
334
|
-
|
|
|
356
|
+
| Auto-interact | `--deep-interact` | Scroll, open menus/modals/accordions, hover CTAs before extraction |
|
|
357
|
+
| Everything | `--full` | Enable screenshots + responsive + interactions + deep-interact |
|
|
335
358
|
| Apply | `designlang apply <url>` | Auto-detect framework and write tokens to your project |
|
|
336
359
|
| Clone | `designlang clone <url>` | Generate a working Next.js starter with extracted design |
|
|
337
360
|
| Score | `designlang score <url>` | Rate design quality with visual bar chart breakdown |
|
|
@@ -366,9 +389,13 @@ Options:
|
|
|
366
389
|
--screenshots Capture component screenshots
|
|
367
390
|
--responsive Capture at multiple breakpoints
|
|
368
391
|
--interactions Capture hover/focus/active states
|
|
369
|
-
--
|
|
392
|
+
--deep-interact Auto-interact pass (scroll, menus, modals, accordions, hover CTAs)
|
|
393
|
+
--full Enable all captures (implies --deep-interact)
|
|
370
394
|
--cookie <cookies...> Cookies for authenticated pages (name=value)
|
|
395
|
+
--cookie-file <path> Load cookies from JSON / storageState / Netscape cookies.txt
|
|
371
396
|
--header <headers...> Custom headers (name:value)
|
|
397
|
+
--user-agent <ua> Override the browser User-Agent string
|
|
398
|
+
--insecure Ignore HTTPS/SSL certificate errors (self-signed, dev, proxies)
|
|
372
399
|
--framework <type> Only generate specific theme (react, shadcn)
|
|
373
400
|
--platforms <csv> Additional platforms: web,ios,android,flutter,wordpress,all (additive)
|
|
374
401
|
--emit-agent-rules Emit Cursor / Claude Code / CLAUDE.md / agents.md rule files
|
|
@@ -453,7 +480,7 @@ In Claude Code, use `/extract-design <url>`.
|
|
|
453
480
|
|
|
454
481
|
## Website
|
|
455
482
|
|
|
456
|
-
**[
|
|
483
|
+
**[design-extract-beta.vercel.app](https://design-extract-beta.vercel.app)** — the brutalist product page.
|
|
457
484
|
|
|
458
485
|
## Contributing
|
|
459
486
|
|
package/bin/design-extract.js
CHANGED
|
@@ -21,6 +21,7 @@ import { formatFlutterDart } from '../src/formatters/flutter-dart.js';
|
|
|
21
21
|
import { formatVueTheme } from '../src/formatters/vue-theme.js';
|
|
22
22
|
import { formatSvelteTheme } from '../src/formatters/svelte-theme.js';
|
|
23
23
|
import { formatAgentRules } from '../src/formatters/agent-rules.js';
|
|
24
|
+
import { reconcileRoutes, formatRoutesReport } from '../src/formatters/routes-reconciliation.js';
|
|
24
25
|
import { loadConfig, mergeConfig } from '../src/config.js';
|
|
25
26
|
import { diffDesigns, formatDiffMarkdown, formatDiffHtml } from '../src/diff.js';
|
|
26
27
|
import { saveSnapshot, getHistory, formatHistoryMarkdown } from '../src/history.js';
|
|
@@ -63,9 +64,13 @@ program
|
|
|
63
64
|
.option('--framework <type>', 'generate framework theme (react, shadcn, vue, svelte)')
|
|
64
65
|
.option('--responsive', 'capture design at multiple breakpoints')
|
|
65
66
|
.option('--interactions', 'capture hover/focus/active states')
|
|
66
|
-
.option('--
|
|
67
|
+
.option('--deep-interact', 'auto-interact pass: scroll, open menus/modals/accordions, hover CTAs (implies --interactions)')
|
|
68
|
+
.option('--full', 'enable all extra captures (screenshots, responsive, interactions, deep-interact)')
|
|
67
69
|
.option('--cookie <cookies...>', 'cookies for authenticated pages (name=value)')
|
|
70
|
+
.option('--cookie-file <path>', 'load cookies from JSON, Playwright storageState, or Netscape cookies.txt')
|
|
68
71
|
.option('--header <headers...>', 'custom headers (name:value)')
|
|
72
|
+
.option('--user-agent <ua>', 'override the browser User-Agent string')
|
|
73
|
+
.option('--insecure', 'ignore HTTPS/SSL certificate errors (self-signed, dev, proxies)')
|
|
69
74
|
.option('--ignore <selectors...>', 'CSS selectors to remove before extraction')
|
|
70
75
|
.option('--tokens-legacy', 'Emit pre-v7 flat token JSON (backward compat)')
|
|
71
76
|
.option('--platforms <csv>', 'Additional platforms: web,ios,android,flutter,wordpress,all (web is always emitted)', 'web')
|
|
@@ -115,7 +120,19 @@ program
|
|
|
115
120
|
try {
|
|
116
121
|
spinner.text = `Crawling${merged.depth > 0 ? ` (depth: ${merged.depth})` : ''}...`;
|
|
117
122
|
// Parse auth options
|
|
118
|
-
const
|
|
123
|
+
const cliCookies = merged.cookie || [];
|
|
124
|
+
const fileCookies = [];
|
|
125
|
+
if (merged.cookieFile) {
|
|
126
|
+
try {
|
|
127
|
+
const { loadCookiesFromFile } = await import('../src/utils-cookies.js');
|
|
128
|
+
fileCookies.push(...loadCookiesFromFile(resolve(merged.cookieFile), url));
|
|
129
|
+
} catch (e) {
|
|
130
|
+
console.error(chalk.red(`\n cookie-file load failed: ${e.message}\n`));
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const { mergeCookies } = await import('../src/utils-cookies.js');
|
|
135
|
+
const cookies = mergeCookies(cliCookies, fileCookies, url);
|
|
119
136
|
const headers = {};
|
|
120
137
|
if (merged.header) {
|
|
121
138
|
for (const h of merged.header) {
|
|
@@ -135,6 +152,9 @@ program
|
|
|
135
152
|
ignore: merged.ignore,
|
|
136
153
|
cookies: cookies.length > 0 ? cookies : undefined,
|
|
137
154
|
headers: Object.keys(headers).length > 0 ? headers : undefined,
|
|
155
|
+
insecure: merged.insecure || false,
|
|
156
|
+
userAgent: merged.userAgent,
|
|
157
|
+
deepInteract: merged.deepInteract || merged.full,
|
|
138
158
|
});
|
|
139
159
|
|
|
140
160
|
// Responsive capture
|
|
@@ -241,6 +261,25 @@ program
|
|
|
241
261
|
}
|
|
242
262
|
}
|
|
243
263
|
|
|
264
|
+
// Multi-route token reconciliation (Tier 2). Only when --depth >= 1 and
|
|
265
|
+
// the crawler actually returned per-route token data.
|
|
266
|
+
if (merged.depth >= 1 && Array.isArray(design.routes) && design.routes.length > 0) {
|
|
267
|
+
const reconciled = reconcileRoutes(design.routes);
|
|
268
|
+
const sharedPath = join(outDir, `${prefix}-tokens-shared.json`);
|
|
269
|
+
writeFileSync(sharedPath, JSON.stringify(reconciled.shared, null, 2), 'utf-8');
|
|
270
|
+
platformFiles.push({ path: sharedPath, label: 'Shared tokens (multi-route)' });
|
|
271
|
+
const routesDir = join(outDir, `${prefix}-tokens-routes`);
|
|
272
|
+
mkdirSync(routesDir, { recursive: true });
|
|
273
|
+
for (const [slug, entry] of Object.entries(reconciled.perRoute)) {
|
|
274
|
+
const rp = join(routesDir, `${slug}.json`);
|
|
275
|
+
writeFileSync(rp, JSON.stringify({ url: entry.url, path: entry.path, added: entry.added, changed: entry.changed }, null, 2), 'utf-8');
|
|
276
|
+
platformFiles.push({ path: rp, label: `Route tokens (${slug})` });
|
|
277
|
+
}
|
|
278
|
+
const reportPath = join(outDir, `${prefix}-routes-report.md`);
|
|
279
|
+
writeFileSync(reportPath, formatRoutesReport(reconciled), 'utf-8');
|
|
280
|
+
platformFiles.push({ path: reportPath, label: 'Routes report (markdown)' });
|
|
281
|
+
}
|
|
282
|
+
|
|
244
283
|
// Agent rules (opt-in, also enabled by --full)
|
|
245
284
|
if (merged.emitAgentRules || merged.full) {
|
|
246
285
|
const agentFiles = formatAgentRules({ design, tokens: dtcgTokens, url });
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# designlang — Chrome extension
|
|
2
|
+
|
|
3
|
+
Right-click → Extract design on any page. The extension opens
|
|
4
|
+
[designlang.manavaryasingh.com](https://designlang.manavaryasingh.com) with the
|
|
5
|
+
current tab's URL prefilled, so extraction starts with one click instead of
|
|
6
|
+
switching windows and pasting.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- One-click handoff from any `http(s)://` page to the hosted extractor
|
|
11
|
+
- "Copy CLI" button — drops `npx designlang <url>` straight into your clipboard
|
|
12
|
+
- Zero tracking, zero analytics, zero permissions beyond `activeTab`
|
|
13
|
+
|
|
14
|
+
## Install (developer mode, pending Chrome Web Store publish)
|
|
15
|
+
|
|
16
|
+
1. Clone this repo.
|
|
17
|
+
2. Open `chrome://extensions` in Chromium / Chrome / Edge / Arc / Brave.
|
|
18
|
+
3. Toggle **Developer mode** (top right).
|
|
19
|
+
4. Click **Load unpacked** and pick the `chrome-extension/` folder in this repo.
|
|
20
|
+
5. Pin the extension from the puzzle-piece menu.
|
|
21
|
+
|
|
22
|
+
## Permissions
|
|
23
|
+
|
|
24
|
+
- `activeTab` — to read the URL of the tab you triggered the popup on.
|
|
25
|
+
- `host_permissions` scoped to `https://designlang.manavaryasingh.com/*` for the
|
|
26
|
+
handoff.
|
|
27
|
+
|
|
28
|
+
No content scripts, no remote code, no background service worker. The whole
|
|
29
|
+
extension is ~3 KB of static HTML/JS/CSS.
|
|
30
|
+
|
|
31
|
+
## Roadmap
|
|
32
|
+
|
|
33
|
+
- Chrome Web Store listing.
|
|
34
|
+
- Optional right-click context menu on links: *"Extract design from this link"*.
|
|
35
|
+
- DevTools panel that shows the extracted tokens for the current tab alongside
|
|
36
|
+
the real DOM selector that produced them.
|
|
37
|
+
- Firefox + Edge listings (the same MV3 manifest works for both).
|
|
38
|
+
|
|
39
|
+
## Contributing
|
|
40
|
+
|
|
41
|
+
PRs welcome. See the root [`CONTRIBUTING.md`](../CONTRIBUTING.md).
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" role="img" aria-label="designlang">
|
|
2
|
+
<title>designlang</title>
|
|
3
|
+
<rect width="32" height="32" fill="#F3F1EA"/>
|
|
4
|
+
<circle cx="13" cy="20" r="7.5" fill="none" stroke="#0A0908" stroke-width="4.5"/>
|
|
5
|
+
<rect x="19" y="4" width="4.5" height="25" fill="#0A0908"/>
|
|
6
|
+
<rect x="25" y="25" width="4" height="4" fill="#FF4800"/>
|
|
7
|
+
</svg>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manifest_version": 3,
|
|
3
|
+
"name": "designlang",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "Extract any website's complete design system — DTCG tokens, Tailwind, Figma variables, MCP server context, iOS/Android/Flutter/WordPress emitters.",
|
|
6
|
+
"author": "Manav Arya Singh",
|
|
7
|
+
"homepage_url": "https://designlang.manavaryasingh.com",
|
|
8
|
+
"action": {
|
|
9
|
+
"default_title": "Extract design system with designlang",
|
|
10
|
+
"default_popup": "popup.html",
|
|
11
|
+
"default_icon": {
|
|
12
|
+
"16": "icons/icon-16.png",
|
|
13
|
+
"32": "icons/icon-32.png",
|
|
14
|
+
"48": "icons/icon-48.png",
|
|
15
|
+
"128": "icons/icon-128.png"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"icons": {
|
|
19
|
+
"16": "icons/icon-16.png",
|
|
20
|
+
"32": "icons/icon-32.png",
|
|
21
|
+
"48": "icons/icon-48.png",
|
|
22
|
+
"128": "icons/icon-128.png"
|
|
23
|
+
},
|
|
24
|
+
"permissions": ["activeTab"],
|
|
25
|
+
"host_permissions": ["https://designlang.manavaryasingh.com/*"]
|
|
26
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=360" />
|
|
6
|
+
<title>designlang</title>
|
|
7
|
+
<link rel="icon" href="icons/favicon.svg" type="image/svg+xml" />
|
|
8
|
+
<style>
|
|
9
|
+
:root {
|
|
10
|
+
--paper: #f3f1ea;
|
|
11
|
+
--ink: #0a0908;
|
|
12
|
+
--ink-2: #403c34;
|
|
13
|
+
--ink-3: #8b8778;
|
|
14
|
+
--accent: #ff4800;
|
|
15
|
+
}
|
|
16
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
17
|
+
html, body { margin: 0; padding: 0; }
|
|
18
|
+
body {
|
|
19
|
+
width: 360px;
|
|
20
|
+
background: var(--paper);
|
|
21
|
+
color: var(--ink);
|
|
22
|
+
font-family: -apple-system, 'Segoe UI', system-ui, sans-serif;
|
|
23
|
+
font-size: 14px;
|
|
24
|
+
line-height: 1.4;
|
|
25
|
+
}
|
|
26
|
+
.pad { padding: 18px 18px 20px; }
|
|
27
|
+
.bar {
|
|
28
|
+
display: flex;
|
|
29
|
+
align-items: center;
|
|
30
|
+
justify-content: space-between;
|
|
31
|
+
padding: 12px 18px 10px;
|
|
32
|
+
border-bottom: 1px solid var(--ink);
|
|
33
|
+
}
|
|
34
|
+
.mono {
|
|
35
|
+
font-family: ui-monospace, 'SF Mono', Menlo, monospace;
|
|
36
|
+
font-size: 11px;
|
|
37
|
+
letter-spacing: 0.12em;
|
|
38
|
+
text-transform: uppercase;
|
|
39
|
+
color: var(--ink-2);
|
|
40
|
+
}
|
|
41
|
+
.mark {
|
|
42
|
+
display: inline-flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
gap: 8px;
|
|
45
|
+
font-family: ui-monospace, Menlo, monospace;
|
|
46
|
+
font-weight: 600;
|
|
47
|
+
}
|
|
48
|
+
.mark svg { width: 16px; height: 16px; }
|
|
49
|
+
h1 {
|
|
50
|
+
margin: 16px 0 10px;
|
|
51
|
+
font-family: 'Iowan Old Style', Georgia, serif;
|
|
52
|
+
font-size: 22px;
|
|
53
|
+
font-weight: 400;
|
|
54
|
+
line-height: 1.1;
|
|
55
|
+
letter-spacing: -0.02em;
|
|
56
|
+
}
|
|
57
|
+
h1 em { color: var(--accent); font-style: italic; }
|
|
58
|
+
p { margin: 0 0 14px; color: var(--ink-2); }
|
|
59
|
+
.url {
|
|
60
|
+
display: block;
|
|
61
|
+
background: #fff;
|
|
62
|
+
border: 1px solid var(--ink);
|
|
63
|
+
padding: 10px 12px;
|
|
64
|
+
font: 12px ui-monospace, Menlo, monospace;
|
|
65
|
+
color: var(--ink);
|
|
66
|
+
word-break: break-all;
|
|
67
|
+
white-space: pre-wrap;
|
|
68
|
+
margin-bottom: 12px;
|
|
69
|
+
}
|
|
70
|
+
button, a.button {
|
|
71
|
+
display: inline-flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
justify-content: space-between;
|
|
74
|
+
gap: 10px;
|
|
75
|
+
width: 100%;
|
|
76
|
+
padding: 12px 14px;
|
|
77
|
+
font: 13px ui-monospace, Menlo, monospace;
|
|
78
|
+
letter-spacing: 0.03em;
|
|
79
|
+
background: var(--ink);
|
|
80
|
+
color: var(--paper);
|
|
81
|
+
border: 1px solid var(--ink);
|
|
82
|
+
box-shadow: 4px 4px 0 var(--accent);
|
|
83
|
+
cursor: pointer;
|
|
84
|
+
text-decoration: none;
|
|
85
|
+
transition: transform 120ms, box-shadow 120ms;
|
|
86
|
+
}
|
|
87
|
+
button:hover, a.button:hover {
|
|
88
|
+
transform: translate(-1px, -1px);
|
|
89
|
+
box-shadow: 5px 5px 0 var(--accent);
|
|
90
|
+
}
|
|
91
|
+
button:active, a.button:active {
|
|
92
|
+
transform: translate(3px, 3px);
|
|
93
|
+
box-shadow: 1px 1px 0 var(--accent);
|
|
94
|
+
}
|
|
95
|
+
.row {
|
|
96
|
+
display: grid;
|
|
97
|
+
grid-template-columns: 1fr 1fr;
|
|
98
|
+
gap: 8px;
|
|
99
|
+
margin-top: 10px;
|
|
100
|
+
}
|
|
101
|
+
.secondary {
|
|
102
|
+
display: inline-flex;
|
|
103
|
+
justify-content: center;
|
|
104
|
+
padding: 9px 10px;
|
|
105
|
+
border: 1px solid var(--ink);
|
|
106
|
+
background: transparent;
|
|
107
|
+
color: var(--ink);
|
|
108
|
+
font: 11px ui-monospace, Menlo, monospace;
|
|
109
|
+
letter-spacing: 0.06em;
|
|
110
|
+
text-transform: uppercase;
|
|
111
|
+
text-decoration: none;
|
|
112
|
+
box-shadow: none;
|
|
113
|
+
}
|
|
114
|
+
.secondary:hover {
|
|
115
|
+
background: var(--ink);
|
|
116
|
+
color: var(--paper);
|
|
117
|
+
transform: none;
|
|
118
|
+
box-shadow: none;
|
|
119
|
+
}
|
|
120
|
+
.hint {
|
|
121
|
+
margin-top: 12px;
|
|
122
|
+
font: 11px ui-monospace, Menlo, monospace;
|
|
123
|
+
color: var(--ink-3);
|
|
124
|
+
line-height: 1.5;
|
|
125
|
+
}
|
|
126
|
+
.hint code { color: var(--ink); }
|
|
127
|
+
</style>
|
|
128
|
+
</head>
|
|
129
|
+
<body>
|
|
130
|
+
<div class="bar">
|
|
131
|
+
<span class="mark">
|
|
132
|
+
<svg viewBox="0 0 32 32" aria-hidden="true">
|
|
133
|
+
<rect width="32" height="32" fill="#F3F1EA"/>
|
|
134
|
+
<circle cx="13" cy="20" r="7.5" fill="none" stroke="#0A0908" stroke-width="4.5"/>
|
|
135
|
+
<rect x="19" y="4" width="4.5" height="25" fill="#0A0908"/>
|
|
136
|
+
<rect x="25" y="25" width="4" height="4" fill="#FF4800"/>
|
|
137
|
+
</svg>
|
|
138
|
+
designlang
|
|
139
|
+
</span>
|
|
140
|
+
<span class="mono">v7.0</span>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
<div class="pad">
|
|
144
|
+
<h1>Read this page as a <em>design system</em>.</h1>
|
|
145
|
+
<p>Extracts DTCG tokens, Tailwind config, Figma variables, an MCP companion, and native iOS / Android / Flutter / WordPress outputs.</p>
|
|
146
|
+
|
|
147
|
+
<span class="url" id="url-display">—</span>
|
|
148
|
+
|
|
149
|
+
<button id="extract-btn">
|
|
150
|
+
Extract on designlang
|
|
151
|
+
<span>↗</span>
|
|
152
|
+
</button>
|
|
153
|
+
|
|
154
|
+
<div class="row">
|
|
155
|
+
<a class="secondary" id="copy-cli" href="#">Copy CLI</a>
|
|
156
|
+
<a class="secondary" href="https://github.com/Manavarya09/design-extract" target="_blank" rel="noopener">GitHub</a>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
<div class="hint">
|
|
160
|
+
Prefer the CLI? <code>npx designlang <url></code>. MCP server:
|
|
161
|
+
<code>designlang mcp</code>.
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<script src="popup.js"></script>
|
|
166
|
+
</body>
|
|
167
|
+
</html>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// designlang Chrome extension popup script.
|
|
2
|
+
// On open, reads the active tab's URL, shows it, and wires the Extract button
|
|
3
|
+
// to hand off to designlang.manavaryasingh.com with the URL prefilled.
|
|
4
|
+
|
|
5
|
+
const SITE = 'https://designlang.manavaryasingh.com/';
|
|
6
|
+
|
|
7
|
+
function isExtractable(url) {
|
|
8
|
+
if (!url) return false;
|
|
9
|
+
try {
|
|
10
|
+
const u = new URL(url);
|
|
11
|
+
return u.protocol === 'http:' || u.protocol === 'https:';
|
|
12
|
+
} catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function getActiveTabUrl() {
|
|
18
|
+
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
|
|
19
|
+
return tabs[0]?.url || '';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function render(tabUrl) {
|
|
23
|
+
const display = document.getElementById('url-display');
|
|
24
|
+
const btn = document.getElementById('extract-btn');
|
|
25
|
+
const copy = document.getElementById('copy-cli');
|
|
26
|
+
|
|
27
|
+
if (!isExtractable(tabUrl)) {
|
|
28
|
+
display.textContent = '(open a regular https:// page to extract)';
|
|
29
|
+
btn.disabled = true;
|
|
30
|
+
btn.style.opacity = '0.55';
|
|
31
|
+
btn.style.cursor = 'not-allowed';
|
|
32
|
+
copy.setAttribute('aria-disabled', 'true');
|
|
33
|
+
copy.style.pointerEvents = 'none';
|
|
34
|
+
copy.style.opacity = '0.55';
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
display.textContent = tabUrl;
|
|
39
|
+
|
|
40
|
+
btn.addEventListener('click', () => {
|
|
41
|
+
const target = `${SITE}?url=${encodeURIComponent(tabUrl)}&source=chrome`;
|
|
42
|
+
chrome.tabs.create({ url: target });
|
|
43
|
+
window.close();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
copy.addEventListener('click', async (e) => {
|
|
47
|
+
e.preventDefault();
|
|
48
|
+
const line = `npx designlang ${tabUrl}`;
|
|
49
|
+
try {
|
|
50
|
+
await navigator.clipboard.writeText(line);
|
|
51
|
+
copy.textContent = 'Copied';
|
|
52
|
+
setTimeout(() => { copy.textContent = 'Copy CLI'; }, 1200);
|
|
53
|
+
} catch {
|
|
54
|
+
copy.textContent = 'Clipboard denied';
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
getActiveTabUrl().then(render);
|