bigpowers 2.42.1 → 2.43.1
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/.pi/package.json +2 -2
- package/.pi/prompts/extract-design.md +131 -0
- package/.pi/skills/extract-design/SKILL.md +133 -0
- package/CHANGELOG.md +14 -0
- package/CONTRIBUTING.md +119 -0
- package/SKILL-INDEX.md +2 -2
- package/extract-design/REFERENCE.md +52 -0
- package/extract-design/SKILL.md +77 -0
- package/extract-design/extract-design.feature +23 -0
- package/extract-design/scripts/classify-colors.js +21 -0
- package/extract-design/scripts/classify-rounded.js +10 -0
- package/extract-design/scripts/classify-spacing.js +14 -0
- package/extract-design/scripts/classify-typography.js +9 -0
- package/extract-design/scripts/collect-styles.js +149 -0
- package/extract-design/scripts/detect-components.js +12 -0
- package/extract-design/scripts/extract.js +306 -0
- package/extract-design/scripts/generate-prose.js +38 -0
- package/extract-design/scripts/lib/browser.js +206 -0
- package/extract-design/scripts/lib/constants.js +9 -0
- package/extract-design/scripts/lib/logging.js +8 -0
- package/extract-design/scripts/lib/retry.js +13 -0
- package/extract-design/scripts/lib/state.js +15 -0
- package/extract-design/scripts/lib/validator.js +11 -0
- package/extract-design/scripts/write-designd.js +16 -0
- package/extract-design/tests/fixtures/glassmorphism-dark/expected-tokens.json +1 -0
- package/extract-design/tests/fixtures/glassmorphism-dark/prototype.html +7 -0
- package/extract-design/tests/fixtures/minimal-no-styles/expected-tokens.json +1 -0
- package/extract-design/tests/fixtures/minimal-no-styles/prototype.html +2 -0
- package/extract-design/tests/fixtures/swiss-grid/expected-tokens.json +1 -0
- package/extract-design/tests/fixtures/swiss-grid/prototype.html +6 -0
- package/extract-design/tests/test-extraction.js +315 -0
- package/package.json +1 -1
- package/scripts/add-model-frontmatter.sh +9 -61
- package/scripts/generate-skill-index.sh +21 -81
- package/scripts/regenerate-lockfile.sh +6 -1
- package/scripts/sync-skills.sh +7 -0
- package/skills-lock.json +5 -0
package/.pi/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bigpowers",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.43.1",
|
|
4
|
+
"description": "72 skills — 70 agent skills for spec-driven, test-first software development by solo developers",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package"
|
|
7
7
|
],
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Extract a Google DESIGN.md file from an HTML prototype (claude.ai/design or any styled page) using Puppeteer, producing machine-readable tokens and AI-generated prose. Use when the user has an HTML prototype and wants a DESIGN.md to anchor their project's visual identity, or when seed-conventions has just scaffolded a new project."
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# Extract DESIGN.md from HTML
|
|
7
|
+
|
|
8
|
+
> **HARD GATE** — Do NOT write DESIGN.md without Puppeteer dual-pass extraction. Tokens from static HTML (Cheerio, regex, string scanning) are invalid — they miss cascade, custom properties, and Tailwind resolution.
|
|
9
|
+
>
|
|
10
|
+
> **HARD GATE** — Do NOT claim certainty where evidence is thin. Low-confidence color roles, component classifications, and prose assertions MUST be flagged with `<!-- AGENT NOTE: uncertain — validate during grill-me. Evidence: [what was observed] -->`.
|
|
11
|
+
>
|
|
12
|
+
> **HARD GATE** — Do NOT ship DESIGN.md without running `npx @google/design.md lint`. Unvalidated output is unverified output. If lint is unavailable (offline), flag prominently in terminal and in DESIGN.md prose.
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# First run — extract from HTML prototype
|
|
18
|
+
node extract-design/scripts/extract.js --source ./prototype.html
|
|
19
|
+
|
|
20
|
+
# From a published URL
|
|
21
|
+
node extract-design/scripts/extract.js --source https://my-prototype.example.com
|
|
22
|
+
|
|
23
|
+
# With a custom name
|
|
24
|
+
node extract-design/scripts/extract.js --source ./proto.html --name "My Design System"
|
|
25
|
+
|
|
26
|
+
# Update — re-extract from new HTML, diff against existing
|
|
27
|
+
node extract-design/scripts/extract.js --source ./proto-v2.html
|
|
28
|
+
|
|
29
|
+
# Lint-only — validate existing DESIGN.md without re-extraction
|
|
30
|
+
node extract-design/scripts/extract.js --lint-only
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Flow
|
|
34
|
+
|
|
35
|
+
1. **Launch Puppeteer** — dual-pass (light + dark) with retry + timeout. CI flags: `--headless=new --no-sandbox --disable-gpu --disable-dbus --use-gl=angle --use-angle=swiftshader`.
|
|
36
|
+
2. **Collect styles** — `page.evaluate()` collects computed styles from every element. Returns raw JSON to Node.js. Browser = sensor; Node = brain.
|
|
37
|
+
3. **Classify tokens** — modular pipeline: colors (Material 3 roles), typography (scale detection), spacing (tolerance GCD), rounded (clustering), components (visual signature + pseudo-state variants).
|
|
38
|
+
4. **Generate prose** — AI heuristics produce all 8 DESIGN.md sections. Overview and Do's/Don'ts flagged with agent notes.
|
|
39
|
+
5. **Write + validate** — serialize to `specs/tech-architecture/DESIGN_LATEST.md`, run `npx @google/design.md lint`, report to terminal.
|
|
40
|
+
6. **Handoff** — writes `handoff.next_skill: grill-me` to `specs/state.yaml` with uncertain decisions context.
|
|
41
|
+
|
|
42
|
+
## Inputs
|
|
43
|
+
|
|
44
|
+
| Parameter | Required | Description |
|
|
45
|
+
|-----------|----------|-------------|
|
|
46
|
+
| `--source <file\|url>` | First run: yes. Update: optional | HTML prototype path or URL |
|
|
47
|
+
| `--name <string>` | No | Design system name (defaults to `<title>` or directory name) |
|
|
48
|
+
| `--lint-only` | No | Validate existing DESIGN.md without re-extraction |
|
|
49
|
+
|
|
50
|
+
## Output
|
|
51
|
+
|
|
52
|
+
- `specs/tech-architecture/DESIGN_LATEST.md` — replaces `DESIGN_PLAN_LATEST.md` as the canonical design artifact
|
|
53
|
+
- Terminal summary: token counts, component count, lint result, uncertain decisions
|
|
54
|
+
- Structured JSON log to stderr: extraction events, timing, counts
|
|
55
|
+
- `specs/state.yaml` → `handoff.next_skill: grill-me` with context
|
|
56
|
+
|
|
57
|
+
## Error Tiers
|
|
58
|
+
|
|
59
|
+
| Tier | Condition | Response |
|
|
60
|
+
|------|-----------|----------|
|
|
61
|
+
| Fatal | No Chrome, page load timeout after retries | Exit non-zero, suggest fixes |
|
|
62
|
+
| Degraded | Zero colors, zero typography, SPA shell | Write DESIGN.md with degradation warning |
|
|
63
|
+
| Warned | Lint errors, uncertain decisions | Write DESIGN.md, flag in terminal, hand off to grill-me |
|
|
64
|
+
|
|
65
|
+
## Dependencies
|
|
66
|
+
|
|
67
|
+
- **Puppeteer** (Chrome binary) — wrapped behind `BrowserExtractor` interface for testability
|
|
68
|
+
- **`@google/design.md`** (soft, via `npx`) — wrapped behind `DesignValidator` interface. Warns and skips if offline.
|
|
69
|
+
|
|
70
|
+
## verify
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
node extract-design/tests/test-extraction.js
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
See [REFERENCE.md](REFERENCE.md) for extraction algorithms and heuristics.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
# Extract Design — Reference
|
|
81
|
+
|
|
82
|
+
## Extraction Algorithms
|
|
83
|
+
|
|
84
|
+
### Color Classification
|
|
85
|
+
1. Collect unique computed `background-color`, `color`, `border-color` from every DOM element.
|
|
86
|
+
2. Count frequency and context (text vs. bg vs. border vs. button).
|
|
87
|
+
3. Classify using Material 3 roles: surface = largest-area bg, on-surface = most-used text, tertiary = highest-saturation on CTAs, error = reddish.
|
|
88
|
+
4. Surface container levels: luminance-ordered. Hard-cap at 3 unless data supports 5.
|
|
89
|
+
5. Low-confidence → `<!-- AGENT NOTE -->` in Colors prose.
|
|
90
|
+
|
|
91
|
+
### Typography Classification
|
|
92
|
+
1. Collect unique (fontFamily, fontSize, fontWeight, lineHeight, letterSpacing) tuples.
|
|
93
|
+
2. Cluster by fontSize (±2px) → type scale.
|
|
94
|
+
3. Detect heading hierarchy from HTML tag + computed size.
|
|
95
|
+
4. Name levels: display-lg, headline-lg/md/sm, title-lg, body-lg/md/sm, label-lg/md/sm.
|
|
96
|
+
5. Parse `<link>` and `@font-face`; warn if computed font differs from declared.
|
|
97
|
+
|
|
98
|
+
### Spacing Classification
|
|
99
|
+
1. Collect unique padding, margin, gap values. Filter: ignore values < 3 occurrences.
|
|
100
|
+
2. Compute tolerance-based GCD (0.5px tolerance).
|
|
101
|
+
3. Declare base unit. Detect half-step if gcd/2 values present ≥3 times.
|
|
102
|
+
4. Map: unit×1 → sm, ×2 → md, ×4 → lg, ×8 → xl.
|
|
103
|
+
|
|
104
|
+
### Rounded Classification
|
|
105
|
+
1. Collect unique border-radius values. Cluster (1px tolerance).
|
|
106
|
+
2. Name: none (0), sm (smallest), md (median), lg (large), xl (largest), full (9999px).
|
|
107
|
+
|
|
108
|
+
### Component Detection
|
|
109
|
+
- Button: w<300px, h<64px, bg+radius set, short text. cursor:pointer is bonus.
|
|
110
|
+
- Card: bg+radius+padding set, larger area.
|
|
111
|
+
- Input: `<input>` or `<textarea>` tag.
|
|
112
|
+
- Pseudo-state variants: force :hover, :active, :focus for high-confidence components.
|
|
113
|
+
|
|
114
|
+
### Prose Generation
|
|
115
|
+
Generates all 8 DESIGN.md sections. Overview and Do's/Don'ts flagged with `<!-- AGENT NOTE: Generated from visual analysis. Grill-me should validate. -->`.
|
|
116
|
+
|
|
117
|
+
## Material 3 Token Conventions
|
|
118
|
+
Colors: primary, on-primary, secondary, tertiary, error, surface, on-surface, surface-container-*, outline, background, on-background.
|
|
119
|
+
Typography: display-lg/md/sm, headline-lg/md/sm, title-lg/md/sm, body-lg/md/sm, label-lg/md/sm.
|
|
120
|
+
Rounded: none, xs, sm, md, lg, xl, full.
|
|
121
|
+
|
|
122
|
+
## CI Compatibility
|
|
123
|
+
Chrome flags: `--headless=new --no-sandbox --disable-gpu --disable-dbus --use-gl=angle --use-angle=swiftshader`
|
|
124
|
+
|
|
125
|
+
## Defensive Code
|
|
126
|
+
Retry with backoff (3 attempts, 1s/2s/4s), Timeout (30s page load, 10s pseudo-state), Graceful degradation (Tier 2 on empty extraction).
|
|
127
|
+
|
|
128
|
+
## Known Risks
|
|
129
|
+
- DESIGN.md spec is alpha. Skill pins to current spec.
|
|
130
|
+
- Puppeteer is heavy (~300MB Chrome). Document requirement clearly.
|
|
131
|
+
- Prose quality depends on AI heuristics. grill-me is the validation gate.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: extract-design
|
|
3
|
+
description: "\"Extract a Google DESIGN.md file from an HTML prototype (claude.ai/design or any styled page) using Puppeteer, producing machine-readable tokens and AI-generated prose. Use when the user has an HTML prototype and wants a DESIGN.md to anchor their project's visual identity, or when seed-conventions has just scaffolded a new project.\""
|
|
4
|
+
model: sonnet
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Extract DESIGN.md from HTML
|
|
9
|
+
|
|
10
|
+
> **HARD GATE** — Do NOT write DESIGN.md without Puppeteer dual-pass extraction. Tokens from static HTML (Cheerio, regex, string scanning) are invalid — they miss cascade, custom properties, and Tailwind resolution.
|
|
11
|
+
>
|
|
12
|
+
> **HARD GATE** — Do NOT claim certainty where evidence is thin. Low-confidence color roles, component classifications, and prose assertions MUST be flagged with `<!-- AGENT NOTE: uncertain — validate during grill-me. Evidence: [what was observed] -->`.
|
|
13
|
+
>
|
|
14
|
+
> **HARD GATE** — Do NOT ship DESIGN.md without running `npx @google/design.md lint`. Unvalidated output is unverified output. If lint is unavailable (offline), flag prominently in terminal and in DESIGN.md prose.
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# First run — extract from HTML prototype
|
|
20
|
+
node extract-design/scripts/extract.js --source ./prototype.html
|
|
21
|
+
|
|
22
|
+
# From a published URL
|
|
23
|
+
node extract-design/scripts/extract.js --source https://my-prototype.example.com
|
|
24
|
+
|
|
25
|
+
# With a custom name
|
|
26
|
+
node extract-design/scripts/extract.js --source ./proto.html --name "My Design System"
|
|
27
|
+
|
|
28
|
+
# Update — re-extract from new HTML, diff against existing
|
|
29
|
+
node extract-design/scripts/extract.js --source ./proto-v2.html
|
|
30
|
+
|
|
31
|
+
# Lint-only — validate existing DESIGN.md without re-extraction
|
|
32
|
+
node extract-design/scripts/extract.js --lint-only
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Flow
|
|
36
|
+
|
|
37
|
+
1. **Launch Puppeteer** — dual-pass (light + dark) with retry + timeout. CI flags: `--headless=new --no-sandbox --disable-gpu --disable-dbus --use-gl=angle --use-angle=swiftshader`.
|
|
38
|
+
2. **Collect styles** — `page.evaluate()` collects computed styles from every element. Returns raw JSON to Node.js. Browser = sensor; Node = brain.
|
|
39
|
+
3. **Classify tokens** — modular pipeline: colors (Material 3 roles), typography (scale detection), spacing (tolerance GCD), rounded (clustering), components (visual signature + pseudo-state variants).
|
|
40
|
+
4. **Generate prose** — AI heuristics produce all 8 DESIGN.md sections. Overview and Do's/Don'ts flagged with agent notes.
|
|
41
|
+
5. **Write + validate** — serialize to `specs/tech-architecture/DESIGN_LATEST.md`, run `npx @google/design.md lint`, report to terminal.
|
|
42
|
+
6. **Handoff** — writes `handoff.next_skill: grill-me` to `specs/state.yaml` with uncertain decisions context.
|
|
43
|
+
|
|
44
|
+
## Inputs
|
|
45
|
+
|
|
46
|
+
| Parameter | Required | Description |
|
|
47
|
+
|-----------|----------|-------------|
|
|
48
|
+
| `--source <file\|url>` | First run: yes. Update: optional | HTML prototype path or URL |
|
|
49
|
+
| `--name <string>` | No | Design system name (defaults to `<title>` or directory name) |
|
|
50
|
+
| `--lint-only` | No | Validate existing DESIGN.md without re-extraction |
|
|
51
|
+
|
|
52
|
+
## Output
|
|
53
|
+
|
|
54
|
+
- `specs/tech-architecture/DESIGN_LATEST.md` — replaces `DESIGN_PLAN_LATEST.md` as the canonical design artifact
|
|
55
|
+
- Terminal summary: token counts, component count, lint result, uncertain decisions
|
|
56
|
+
- Structured JSON log to stderr: extraction events, timing, counts
|
|
57
|
+
- `specs/state.yaml` → `handoff.next_skill: grill-me` with context
|
|
58
|
+
|
|
59
|
+
## Error Tiers
|
|
60
|
+
|
|
61
|
+
| Tier | Condition | Response |
|
|
62
|
+
|------|-----------|----------|
|
|
63
|
+
| Fatal | No Chrome, page load timeout after retries | Exit non-zero, suggest fixes |
|
|
64
|
+
| Degraded | Zero colors, zero typography, SPA shell | Write DESIGN.md with degradation warning |
|
|
65
|
+
| Warned | Lint errors, uncertain decisions | Write DESIGN.md, flag in terminal, hand off to grill-me |
|
|
66
|
+
|
|
67
|
+
## Dependencies
|
|
68
|
+
|
|
69
|
+
- **Puppeteer** (Chrome binary) — wrapped behind `BrowserExtractor` interface for testability
|
|
70
|
+
- **`@google/design.md`** (soft, via `npx`) — wrapped behind `DesignValidator` interface. Warns and skips if offline.
|
|
71
|
+
|
|
72
|
+
## verify
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
node extract-design/tests/test-extraction.js
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
See [REFERENCE.md](REFERENCE.md) for extraction algorithms and heuristics.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
# Extract Design — Reference
|
|
83
|
+
|
|
84
|
+
## Extraction Algorithms
|
|
85
|
+
|
|
86
|
+
### Color Classification
|
|
87
|
+
1. Collect unique computed `background-color`, `color`, `border-color` from every DOM element.
|
|
88
|
+
2. Count frequency and context (text vs. bg vs. border vs. button).
|
|
89
|
+
3. Classify using Material 3 roles: surface = largest-area bg, on-surface = most-used text, tertiary = highest-saturation on CTAs, error = reddish.
|
|
90
|
+
4. Surface container levels: luminance-ordered. Hard-cap at 3 unless data supports 5.
|
|
91
|
+
5. Low-confidence → `<!-- AGENT NOTE -->` in Colors prose.
|
|
92
|
+
|
|
93
|
+
### Typography Classification
|
|
94
|
+
1. Collect unique (fontFamily, fontSize, fontWeight, lineHeight, letterSpacing) tuples.
|
|
95
|
+
2. Cluster by fontSize (±2px) → type scale.
|
|
96
|
+
3. Detect heading hierarchy from HTML tag + computed size.
|
|
97
|
+
4. Name levels: display-lg, headline-lg/md/sm, title-lg, body-lg/md/sm, label-lg/md/sm.
|
|
98
|
+
5. Parse `<link>` and `@font-face`; warn if computed font differs from declared.
|
|
99
|
+
|
|
100
|
+
### Spacing Classification
|
|
101
|
+
1. Collect unique padding, margin, gap values. Filter: ignore values < 3 occurrences.
|
|
102
|
+
2. Compute tolerance-based GCD (0.5px tolerance).
|
|
103
|
+
3. Declare base unit. Detect half-step if gcd/2 values present ≥3 times.
|
|
104
|
+
4. Map: unit×1 → sm, ×2 → md, ×4 → lg, ×8 → xl.
|
|
105
|
+
|
|
106
|
+
### Rounded Classification
|
|
107
|
+
1. Collect unique border-radius values. Cluster (1px tolerance).
|
|
108
|
+
2. Name: none (0), sm (smallest), md (median), lg (large), xl (largest), full (9999px).
|
|
109
|
+
|
|
110
|
+
### Component Detection
|
|
111
|
+
- Button: w<300px, h<64px, bg+radius set, short text. cursor:pointer is bonus.
|
|
112
|
+
- Card: bg+radius+padding set, larger area.
|
|
113
|
+
- Input: `<input>` or `<textarea>` tag.
|
|
114
|
+
- Pseudo-state variants: force :hover, :active, :focus for high-confidence components.
|
|
115
|
+
|
|
116
|
+
### Prose Generation
|
|
117
|
+
Generates all 8 DESIGN.md sections. Overview and Do's/Don'ts flagged with `<!-- AGENT NOTE: Generated from visual analysis. Grill-me should validate. -->`.
|
|
118
|
+
|
|
119
|
+
## Material 3 Token Conventions
|
|
120
|
+
Colors: primary, on-primary, secondary, tertiary, error, surface, on-surface, surface-container-*, outline, background, on-background.
|
|
121
|
+
Typography: display-lg/md/sm, headline-lg/md/sm, title-lg/md/sm, body-lg/md/sm, label-lg/md/sm.
|
|
122
|
+
Rounded: none, xs, sm, md, lg, xl, full.
|
|
123
|
+
|
|
124
|
+
## CI Compatibility
|
|
125
|
+
Chrome flags: `--headless=new --no-sandbox --disable-gpu --disable-dbus --use-gl=angle --use-angle=swiftshader`
|
|
126
|
+
|
|
127
|
+
## Defensive Code
|
|
128
|
+
Retry with backoff (3 attempts, 1s/2s/4s), Timeout (30s page load, 10s pseudo-state), Graceful degradation (Tier 2 on empty extraction).
|
|
129
|
+
|
|
130
|
+
## Known Risks
|
|
131
|
+
- DESIGN.md spec is alpha. Skill pins to current spec.
|
|
132
|
+
- Puppeteer is heavy (~300MB Chrome). Document requirement clearly.
|
|
133
|
+
- Prose quality depends on AI heuristics. grill-me is the validation gate.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [2.43.1](https://github.com/danielvm-git/bigpowers/compare/v2.43.0...v2.43.1) (2026-07-02)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **scripts:** replace declare -A with case for bash 3.2 compatibility ([92dacb6](https://github.com/danielvm-git/bigpowers/commit/92dacb6151a207f659edd2d4f48efc084936fc40))
|
|
7
|
+
|
|
8
|
+
# [2.43.0](https://github.com/danielvm-git/bigpowers/compare/v2.42.1...v2.43.0) (2026-06-29)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **skills:** add extract-design skill ([bda7ceb](https://github.com/danielvm-git/bigpowers/commit/bda7ceb113cba610d438dbb95af150b424e674b4))
|
|
14
|
+
|
|
1
15
|
## [2.42.1](https://github.com/danielvm-git/bigpowers/compare/v2.42.0...v2.42.1) (2026-06-29)
|
|
2
16
|
|
|
3
17
|
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Contributing to bigpowers
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in contributing! bigpowers is an open-source system of 69+ AI agent skills for spec-driven, test-first software development. This guide will help you get started.
|
|
4
|
+
|
|
5
|
+
## Code of Conduct
|
|
6
|
+
|
|
7
|
+
Be respectful, constructive, and patient. We're building tools for developers — treat each other like the professionals we are.
|
|
8
|
+
|
|
9
|
+
## Before You Start
|
|
10
|
+
|
|
11
|
+
1. **Read the docs.** Start with [`README.md`](README.md), [`CLAUDE.md`](CLAUDE.md), and [`CONVENTIONS.md`](CONVENTIONS.md). These define the project's architecture, conventions, and engineering standards.
|
|
12
|
+
2. **Run bigpowers.** Install with `npx bigpowers` and walk through the experience as a user before contributing as a developer.
|
|
13
|
+
3. **Understand the lifecycle.** bigpowers follows a 6-phase methodology: Discover → Elaborate → Plan → Build → Verify → Release. Read [`docs/WORKFLOW-SOP-v2.md`](docs/WORKFLOW-SOP-v2.md) for the full SOP.
|
|
14
|
+
|
|
15
|
+
## How to Contribute
|
|
16
|
+
|
|
17
|
+
### Reporting Bugs
|
|
18
|
+
|
|
19
|
+
- Search [existing issues](https://github.com/danielvm-git/bigpowers/issues) first.
|
|
20
|
+
- Use the bug report template if one exists.
|
|
21
|
+
- Include: what you did, what you expected, what happened, your OS, Node version, and any error output.
|
|
22
|
+
- If the bug is in a specific skill, name the skill.
|
|
23
|
+
|
|
24
|
+
### Proposing Features
|
|
25
|
+
|
|
26
|
+
- Open a [discussion](https://github.com/danielvm-git/bigpowers/discussions) before opening a PR for a new feature. We want to align on the approach before you invest time.
|
|
27
|
+
- New skills must follow the [verb-noun kebab-case naming convention](CONVENTIONS.md#skill-naming--conventions-and-exceptions) (ADR-0001).
|
|
28
|
+
- New features should include: a description of the problem, the proposed solution, and any alternatives considered.
|
|
29
|
+
|
|
30
|
+
### Proposing a New Skill
|
|
31
|
+
|
|
32
|
+
Skills are the core of bigpowers. To propose a new one:
|
|
33
|
+
|
|
34
|
+
1. **Check the index.** [`SKILL-INDEX.md`](SKILL-INDEX.md) lists all 69+ active skills. Make sure your idea doesn't overlap with an existing skill.
|
|
35
|
+
2. **Identify the phase.** Every skill belongs to one of 7 phases: Discover, Design, Plan, Build, Verify, Release, Sustain.
|
|
36
|
+
3. **Write a SKILL.md draft.** Follow the structure of existing skills. Every SKILL.md needs: a description, phase, instructions, and references.
|
|
37
|
+
4. **Open a discussion.** Share your SKILL.md draft in a GitHub discussion. We'll review whether it fits the roadmap before you invest in a full PR.
|
|
38
|
+
5. **Once approved,** open a PR with:
|
|
39
|
+
- A new directory: `[verb]-[noun]/` containing `SKILL.md`
|
|
40
|
+
- Evidence that you ran `bash scripts/sync-skills.sh` to regenerate artifacts
|
|
41
|
+
- A Conventional Commit: `feat(skills): add [verb]-[noun] skill`
|
|
42
|
+
|
|
43
|
+
### Pull Request Process
|
|
44
|
+
|
|
45
|
+
1. **Branch from main.** Use `kickoff-branch` or create a feature branch manually.
|
|
46
|
+
2. **Follow CONVENTIONS.md.** F.I.R.S.T tests. SOLID. Boy Scout Rule. No `any` types. Functions under 20 lines.
|
|
47
|
+
3. **Run sync.** After any SKILL.md change, run `bash scripts/sync-skills.sh` to regenerate generated artifacts. Do not edit `.cursor/rules` or `.gemini/extensions/` directly.
|
|
48
|
+
4. **Validate YAML.** Run `bash scripts/validate-specs-yaml.sh` if you've changed any `specs/*.yaml` files.
|
|
49
|
+
5. **Write Conventional Commits.** Format: `type(scope): description`. See [CONVENTIONS.md](CONVENTIONS.md#conventional-commits--semantic-versioning) for the full spec.
|
|
50
|
+
6. **Open a PR.** Use `gh pr create`. Include: what you changed, why, how to verify, and any open questions.
|
|
51
|
+
7. **Wait for review.** A maintainer will review within a few days. Be responsive to feedback.
|
|
52
|
+
|
|
53
|
+
### Writing Documentation
|
|
54
|
+
|
|
55
|
+
Documentation is a first-class contribution. You don't need to write code to help.
|
|
56
|
+
|
|
57
|
+
- **README improvements:** Clearer install instructions, better examples, fixed typos.
|
|
58
|
+
- **Skill docs:** Every SKILL.md should be clear enough for a new user to understand without asking.
|
|
59
|
+
- **Guides and tutorials:** Walkthroughs for specific use cases (e.g., "Using bigpowers with a Next.js project").
|
|
60
|
+
- **Translations:** Help make bigpowers accessible to non-English-speaking developers.
|
|
61
|
+
|
|
62
|
+
## Development Setup
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# Clone the repo
|
|
66
|
+
git clone https://github.com/danielvm-git/bigpowers.git
|
|
67
|
+
cd bigpowers
|
|
68
|
+
|
|
69
|
+
# Install dependencies + run postinstall sync
|
|
70
|
+
npm install
|
|
71
|
+
|
|
72
|
+
# Or manually:
|
|
73
|
+
bash scripts/install.sh
|
|
74
|
+
npm run sync
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Prerequisites
|
|
78
|
+
|
|
79
|
+
- **Bash** — required for all scripts
|
|
80
|
+
- **Node.js** v14+
|
|
81
|
+
- **jq** — highly recommended for YAML validation scripts
|
|
82
|
+
- **An AI coding tool** — Claude Code, Cursor, Gemini CLI, or pi (to test skills end-to-end)
|
|
83
|
+
|
|
84
|
+
### Project Structure
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
bigpowers/
|
|
88
|
+
├── [skill-name]/ # Each skill has its own directory with SKILL.md
|
|
89
|
+
├── scripts/ # Install, sync, validation, and compliance scripts
|
|
90
|
+
├── specs/ # YAML cockpit — state.yaml, release-plan.yaml, epics/
|
|
91
|
+
├── docs/ # Guides and references
|
|
92
|
+
├── profiles/ # Stack-specific conventions (TypeScript+Vue, Node Service, etc.)
|
|
93
|
+
├── .cursor/rules/ # Generated — do not edit directly
|
|
94
|
+
├── .gemini/extensions/ # Generated — do not edit directly
|
|
95
|
+
└── .pi/skills/ # Generated — do not edit directly
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**Never edit generated files.** Always edit the source `SKILL.md` and run `bash scripts/sync-skills.sh`.
|
|
99
|
+
|
|
100
|
+
## Testing
|
|
101
|
+
|
|
102
|
+
- **Skill validation:** `bash scripts/sync-skills.sh` validates SKILL.md syntax and structure.
|
|
103
|
+
- **YAML validation:** `bash scripts/validate-specs-yaml.sh` checks the YAML cockpit for correctness.
|
|
104
|
+
- **Compliance check:** `npm run compliance` runs a comprehensive check of the repo.
|
|
105
|
+
- **Manual testing:** The best test of a skill is using it with an AI coding tool. After changes, run `survey-context` or invoke the skill to verify it works.
|
|
106
|
+
|
|
107
|
+
## Getting Help
|
|
108
|
+
|
|
109
|
+
- **Questions?** Open a [GitHub discussion](https://github.com/danielvm-git/bigpowers/discussions).
|
|
110
|
+
- **Bug?** Open an [issue](https://github.com/danielvm-git/bigpowers/issues).
|
|
111
|
+
- **Want to chat?** [Link to Discord/Slack — to be added]
|
|
112
|
+
|
|
113
|
+
## Recognition
|
|
114
|
+
|
|
115
|
+
All contributors are recognized in the README and release notes. Significant contributions (new skills, major features) get a shoutout in the changelog and on social media. We believe in giving credit where it's due.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
*bigpowers is MIT-licensed. By contributing, you agree that your contributions will be licensed under the same terms.*
|
package/SKILL-INDEX.md
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
> **DO NOT EDIT** — This file is auto-generated by `scripts/generate-skill-index.sh`.
|
|
4
4
|
> Edit `SKILL.md` source files or `skills-lock.json` instead. Run `bash scripts/sync-skills.sh` to regenerate.
|
|
5
5
|
|
|
6
|
-
**Generated:** 2026-
|
|
7
|
-
**Skills:**
|
|
6
|
+
**Generated:** 2026-07-02T11:02:02Z
|
|
7
|
+
**Skills:** 72
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Extract Design — Reference
|
|
2
|
+
|
|
3
|
+
## Extraction Algorithms
|
|
4
|
+
|
|
5
|
+
### Color Classification
|
|
6
|
+
1. Collect unique computed `background-color`, `color`, `border-color` from every DOM element.
|
|
7
|
+
2. Count frequency and context (text vs. bg vs. border vs. button).
|
|
8
|
+
3. Classify using Material 3 roles: surface = largest-area bg, on-surface = most-used text, tertiary = highest-saturation on CTAs, error = reddish.
|
|
9
|
+
4. Surface container levels: luminance-ordered. Hard-cap at 3 unless data supports 5.
|
|
10
|
+
5. Low-confidence → `<!-- AGENT NOTE -->` in Colors prose.
|
|
11
|
+
|
|
12
|
+
### Typography Classification
|
|
13
|
+
1. Collect unique (fontFamily, fontSize, fontWeight, lineHeight, letterSpacing) tuples.
|
|
14
|
+
2. Cluster by fontSize (±2px) → type scale.
|
|
15
|
+
3. Detect heading hierarchy from HTML tag + computed size.
|
|
16
|
+
4. Name levels: display-lg, headline-lg/md/sm, title-lg, body-lg/md/sm, label-lg/md/sm.
|
|
17
|
+
5. Parse `<link>` and `@font-face`; warn if computed font differs from declared.
|
|
18
|
+
|
|
19
|
+
### Spacing Classification
|
|
20
|
+
1. Collect unique padding, margin, gap values. Filter: ignore values < 3 occurrences.
|
|
21
|
+
2. Compute tolerance-based GCD (0.5px tolerance).
|
|
22
|
+
3. Declare base unit. Detect half-step if gcd/2 values present ≥3 times.
|
|
23
|
+
4. Map: unit×1 → sm, ×2 → md, ×4 → lg, ×8 → xl.
|
|
24
|
+
|
|
25
|
+
### Rounded Classification
|
|
26
|
+
1. Collect unique border-radius values. Cluster (1px tolerance).
|
|
27
|
+
2. Name: none (0), sm (smallest), md (median), lg (large), xl (largest), full (9999px).
|
|
28
|
+
|
|
29
|
+
### Component Detection
|
|
30
|
+
- Button: w<300px, h<64px, bg+radius set, short text. cursor:pointer is bonus.
|
|
31
|
+
- Card: bg+radius+padding set, larger area.
|
|
32
|
+
- Input: `<input>` or `<textarea>` tag.
|
|
33
|
+
- Pseudo-state variants: force :hover, :active, :focus for high-confidence components.
|
|
34
|
+
|
|
35
|
+
### Prose Generation
|
|
36
|
+
Generates all 8 DESIGN.md sections. Overview and Do's/Don'ts flagged with `<!-- AGENT NOTE: Generated from visual analysis. Grill-me should validate. -->`.
|
|
37
|
+
|
|
38
|
+
## Material 3 Token Conventions
|
|
39
|
+
Colors: primary, on-primary, secondary, tertiary, error, surface, on-surface, surface-container-*, outline, background, on-background.
|
|
40
|
+
Typography: display-lg/md/sm, headline-lg/md/sm, title-lg/md/sm, body-lg/md/sm, label-lg/md/sm.
|
|
41
|
+
Rounded: none, xs, sm, md, lg, xl, full.
|
|
42
|
+
|
|
43
|
+
## CI Compatibility
|
|
44
|
+
Chrome flags: `--headless=new --no-sandbox --disable-gpu --disable-dbus --use-gl=angle --use-angle=swiftshader`
|
|
45
|
+
|
|
46
|
+
## Defensive Code
|
|
47
|
+
Retry with backoff (3 attempts, 1s/2s/4s), Timeout (30s page load, 10s pseudo-state), Graceful degradation (Tier 2 on empty extraction).
|
|
48
|
+
|
|
49
|
+
## Known Risks
|
|
50
|
+
- DESIGN.md spec is alpha. Skill pins to current spec.
|
|
51
|
+
- Puppeteer is heavy (~300MB Chrome). Document requirement clearly.
|
|
52
|
+
- Prose quality depends on AI heuristics. grill-me is the validation gate.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: extract-design
|
|
3
|
+
description: "Extract a Google DESIGN.md file from an HTML prototype (claude.ai/design or any styled page) using Puppeteer, producing machine-readable tokens and AI-generated prose. Use when the user has an HTML prototype and wants a DESIGN.md to anchor their project's visual identity, or when seed-conventions has just scaffolded a new project."
|
|
4
|
+
model: sonnet
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Extract DESIGN.md from HTML
|
|
8
|
+
|
|
9
|
+
> **HARD GATE** — Do NOT write DESIGN.md without Puppeteer dual-pass extraction. Tokens from static HTML (Cheerio, regex, string scanning) are invalid — they miss cascade, custom properties, and Tailwind resolution.
|
|
10
|
+
>
|
|
11
|
+
> **HARD GATE** — Do NOT claim certainty where evidence is thin. Low-confidence color roles, component classifications, and prose assertions MUST be flagged with `<!-- AGENT NOTE: uncertain — validate during grill-me. Evidence: [what was observed] -->`.
|
|
12
|
+
>
|
|
13
|
+
> **HARD GATE** — Do NOT ship DESIGN.md without running `npx @google/design.md lint`. Unvalidated output is unverified output. If lint is unavailable (offline), flag prominently in terminal and in DESIGN.md prose.
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# First run — extract from HTML prototype
|
|
19
|
+
node extract-design/scripts/extract.js --source ./prototype.html
|
|
20
|
+
|
|
21
|
+
# From a published URL
|
|
22
|
+
node extract-design/scripts/extract.js --source https://my-prototype.example.com
|
|
23
|
+
|
|
24
|
+
# With a custom name
|
|
25
|
+
node extract-design/scripts/extract.js --source ./proto.html --name "My Design System"
|
|
26
|
+
|
|
27
|
+
# Update — re-extract from new HTML, diff against existing
|
|
28
|
+
node extract-design/scripts/extract.js --source ./proto-v2.html
|
|
29
|
+
|
|
30
|
+
# Lint-only — validate existing DESIGN.md without re-extraction
|
|
31
|
+
node extract-design/scripts/extract.js --lint-only
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Flow
|
|
35
|
+
|
|
36
|
+
1. **Launch Puppeteer** — dual-pass (light + dark) with retry + timeout. CI flags: `--headless=new --no-sandbox --disable-gpu --disable-dbus --use-gl=angle --use-angle=swiftshader`.
|
|
37
|
+
2. **Collect styles** — `page.evaluate()` collects computed styles from every element. Returns raw JSON to Node.js. Browser = sensor; Node = brain.
|
|
38
|
+
3. **Classify tokens** — modular pipeline: colors (Material 3 roles), typography (scale detection), spacing (tolerance GCD), rounded (clustering), components (visual signature + pseudo-state variants).
|
|
39
|
+
4. **Generate prose** — AI heuristics produce all 8 DESIGN.md sections. Overview and Do's/Don'ts flagged with agent notes.
|
|
40
|
+
5. **Write + validate** — serialize to `specs/tech-architecture/DESIGN_LATEST.md`, run `npx @google/design.md lint`, report to terminal.
|
|
41
|
+
6. **Handoff** — writes `handoff.next_skill: grill-me` to `specs/state.yaml` with uncertain decisions context.
|
|
42
|
+
|
|
43
|
+
## Inputs
|
|
44
|
+
|
|
45
|
+
| Parameter | Required | Description |
|
|
46
|
+
|-----------|----------|-------------|
|
|
47
|
+
| `--source <file\|url>` | First run: yes. Update: optional | HTML prototype path or URL |
|
|
48
|
+
| `--name <string>` | No | Design system name (defaults to `<title>` or directory name) |
|
|
49
|
+
| `--lint-only` | No | Validate existing DESIGN.md without re-extraction |
|
|
50
|
+
|
|
51
|
+
## Output
|
|
52
|
+
|
|
53
|
+
- `specs/tech-architecture/DESIGN_LATEST.md` — replaces `DESIGN_PLAN_LATEST.md` as the canonical design artifact
|
|
54
|
+
- Terminal summary: token counts, component count, lint result, uncertain decisions
|
|
55
|
+
- Structured JSON log to stderr: extraction events, timing, counts
|
|
56
|
+
- `specs/state.yaml` → `handoff.next_skill: grill-me` with context
|
|
57
|
+
|
|
58
|
+
## Error Tiers
|
|
59
|
+
|
|
60
|
+
| Tier | Condition | Response |
|
|
61
|
+
|------|-----------|----------|
|
|
62
|
+
| Fatal | No Chrome, page load timeout after retries | Exit non-zero, suggest fixes |
|
|
63
|
+
| Degraded | Zero colors, zero typography, SPA shell | Write DESIGN.md with degradation warning |
|
|
64
|
+
| Warned | Lint errors, uncertain decisions | Write DESIGN.md, flag in terminal, hand off to grill-me |
|
|
65
|
+
|
|
66
|
+
## Dependencies
|
|
67
|
+
|
|
68
|
+
- **Puppeteer** (Chrome binary) — wrapped behind `BrowserExtractor` interface for testability
|
|
69
|
+
- **`@google/design.md`** (soft, via `npx`) — wrapped behind `DesignValidator` interface. Warns and skips if offline.
|
|
70
|
+
|
|
71
|
+
## verify
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
node extract-design/tests/test-extraction.js
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
See [REFERENCE.md](REFERENCE.md) for extraction algorithms and heuristics.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Feature: Extract DESIGN.md from HTML prototype
|
|
2
|
+
|
|
3
|
+
Scenario: Extract tokens from a styled prototype
|
|
4
|
+
Given an HTML prototype with 3+ colors, 2+ font sizes, spacing values, and rounded corners
|
|
5
|
+
When extract-design runs on the prototype
|
|
6
|
+
Then DESIGN_LATEST.md contains a colors token group with at least 3 entries
|
|
7
|
+
And DESIGN_LATEST.md contains a typography token group with at least 2 levels
|
|
8
|
+
And all 8 prose sections are present
|
|
9
|
+
|
|
10
|
+
Scenario: Handle dark mode prototypes
|
|
11
|
+
Given an HTML prototype that responds to prefers-color-scheme: dark
|
|
12
|
+
When extract-design runs dual-pass extraction
|
|
13
|
+
Then DESIGN_LATEST.md contains a colors-dark token group
|
|
14
|
+
|
|
15
|
+
Scenario: Degrade gracefully on unstyleable HTML
|
|
16
|
+
Given an HTML file with no computed styles
|
|
17
|
+
When extract-design runs on the prototype
|
|
18
|
+
Then DESIGN_LATEST.md is written with a degradation warning
|
|
19
|
+
|
|
20
|
+
Scenario: Flag uncertain classifications
|
|
21
|
+
Given an HTML prototype where one color is used ambiguously
|
|
22
|
+
When extract-design classifies colors
|
|
23
|
+
Then the uncertain color's prose rationale contains an AGENT NOTE comment
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
function norm(c) { if (!c || c === 'transparent' || c === 'rgba(0, 0, 0, 0)') return null; return c.trim(); }
|
|
2
|
+
function lum(c) { const m = c.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/); if (!m) return 0.5; const s = v => v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4); return 0.2126 * s(parseInt(m[1]) / 255) + 0.7152 * s(parseInt(m[2]) / 255) + 0.0722 * s(parseInt(m[3]) / 255); }
|
|
3
|
+
function isNeutral(c) { const m = c.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/); if (!m) return false; return Math.max(Math.abs(parseInt(m[1]) - parseInt(m[2])), Math.abs(parseInt(m[2]) - parseInt(m[3])), Math.abs(parseInt(m[1]) - parseInt(m[3]))) < 30; }
|
|
4
|
+
|
|
5
|
+
export function classifyColors(styles, mode = 'light') {
|
|
6
|
+
const u = []; const colors = {}; const stats = new Map();
|
|
7
|
+
for (const el of styles) {
|
|
8
|
+
for (const [prop, usage] of [['backgroundColor','bg'],['color','text'],['borderColor','border']]) {
|
|
9
|
+
const k = norm(el[prop]); if (!k) continue;
|
|
10
|
+
const s = stats.get(k) || { usage: new Set(), area: 0, count: 0 }; s.usage.add(usage); if (prop === 'backgroundColor') s.area += (el.width||0)*(el.height||0); s.count++; stats.set(k, s);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
const neutrals = [], accents = [];
|
|
14
|
+
for (const [c, s] of stats) { if (isNeutral(c)) neutrals.push({ color: c, ...s, luminance: lum(c) }); else accents.push({ color: c, ...s }); }
|
|
15
|
+
neutrals.sort((a,b) => b.area - a.area); accents.sort((a,b) => b.count - a.count);
|
|
16
|
+
if (neutrals.length) { colors.surface = neutrals[0].color; colors.background = neutrals[0].color; const txt = [...stats.entries()].filter(([,s]) => s.usage.has('text')).sort((a,b) => b[1].count - a[1].count); if (txt.length) colors['on-surface'] = txt[0][0]; }
|
|
17
|
+
const containers = neutrals.slice(1); if (containers.length) { const isDark = mode === 'dark'; containers.sort((a,b) => isDark ? b.luminance - a.luminance : a.luminance - b.luminance); const maxC = Math.min(containers.length >= 5 ? 5 : 3, containers.length); const lvls = ['surface-container-lowest','surface-container-low','surface-container','surface-container-high','surface-container-highest']; for (let i = 0; i < maxC; i++) colors[lvls[i]] = containers[i].color; }
|
|
18
|
+
if (accents.length) { colors.tertiary = accents[0].color; if (accents.length > 1) colors.secondary = accents[1].color; const err = accents.find(a => { const m = a.color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/); return m && parseInt(m[1]) > 180 && parseInt(m[2]) < 100 && parseInt(m[3]) < 100; }); if (err) colors.error = err.color; }
|
|
19
|
+
if (Object.keys(colors).length < 2) u.push('Fewer than 2 colors extracted.');
|
|
20
|
+
return { colors, uncertain: u };
|
|
21
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
function parseR(v) { if (!v || v === '0px' || v === '0') return 0; const m = v.match(/^([\d.]+)(px|rem)$/); return m ? parseFloat(m[1]) * (m[2] === 'rem' ? 16 : 1) : (parseFloat(v) || 0); }
|
|
2
|
+
export function classifyRounded(styles) { const u = []; const r = {}; const cnt = new Map();
|
|
3
|
+
for (const el of styles) { const px = parseR(el.borderRadius); cnt.set(px, (cnt.get(px) || 0) + 1); }
|
|
4
|
+
const freq = [...cnt.entries()].filter(([,c]) => c >= 2).map(([px]) => px).sort((a,b) => a - b);
|
|
5
|
+
if (!freq.length) { r.none = '0px'; return { rounded: r, uncertain: [] }; }
|
|
6
|
+
r.none = '0px'; const reg = freq.filter(v => v < 9998 && v > 0); const full = freq.find(v => v >= 9998);
|
|
7
|
+
if (full) r.full = `${full}px`; if (reg.length === 1) r.sm = `${reg[0]}px`;
|
|
8
|
+
else if (reg.length >= 2) { r.sm = `${reg[0]}px`; r.md = `${reg[1]}px`; } if (reg.length >= 3) r.lg = `${reg[2]}px`;
|
|
9
|
+
return { rounded: r, uncertain: u };
|
|
10
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function toPx(v) { if (!v) return 0; const m = v.match(/^([\d.]+)(px|rem|em)$/); if (!m) return 0; return parseFloat(m[1]) * (m[2] === 'rem' || m[2] === 'em' ? 16 : 1); }
|
|
2
|
+
function tGcd(nums, tol = 0.5) { if (!nums.length) return null; let r = nums[0]; for (let i = 1; i < nums.length; i++) { let a = Math.abs(r), b = Math.abs(nums[i]); while (b > tol) { const t = b; b = a % b; a = t; } r = a; } return r; }
|
|
3
|
+
function collect(css, counts) { if (!css) return; for (const p of css.split(/\s+/)) { const v = toPx(p); if (v > 0) counts.set(v, (counts.get(v) || 0) + 1); } }
|
|
4
|
+
|
|
5
|
+
export function classifySpacing(styles) { const u = []; const s = {}; const counts = new Map();
|
|
6
|
+
for (const el of styles) { collect(el.padding, counts); collect(el.margin, counts); if (el.gap) collect(el.gap, counts); }
|
|
7
|
+
const freq = new Map(); for (const [v, c] of counts) { if (c >= 3) freq.set(v, c); }
|
|
8
|
+
if (freq.size < 3) u.push('Fewer than 3 spacing values.');
|
|
9
|
+
const nums = [...freq.keys()].map(toPx).filter(v => v > 0); const g = tGcd(nums, 0.5);
|
|
10
|
+
if (!g || g < 2) { const sorted = [...freq.entries()].sort((a,b) => b[1] - a[1]); s.unit = sorted.length ? `${toPx(sorted[0][0])}px` : '8px'; return { spacing: s, uncertain: u }; }
|
|
11
|
+
s.unit = `${g}px`; const half = g / 2; if (nums.filter(v => Math.abs(v - half) < 0.5).length >= 3) s.half = `${half}px`;
|
|
12
|
+
s.sm = `${g * 2}px`; s.md = `${g * 4}px`; s.lg = `${g * 8}px`; if (nums.some(v => v >= g * 16)) s.xl = `${g * 16}px`;
|
|
13
|
+
return { spacing: s, uncertain: u };
|
|
14
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
function px(v) { if (!v) return 0; const n = parseFloat(v); return isNaN(n) ? 0 : n; }
|
|
2
|
+
export function classifyTypography(styles) { const u = []; const t = {}; const txt = styles.filter(s => s.text && s.text.length > 0 && s.fontSize); const clusters = new Map();
|
|
3
|
+
for (const el of txt) { const s = Math.round(px(el.fontSize)); if (!s) continue; if (!clusters.has(s)) clusters.set(s, { fontFamily: el.fontFamily, fontWeight: el.fontWeight, lineHeight: el.lineHeight, letterSpacing: el.letterSpacing, tag: el.tag, count: 0 }); clusters.get(s).count++; }
|
|
4
|
+
const sorted = [...clusters.entries()].sort((a,b) => b[0] - a[0]); if (!sorted.length) { u.push('No typography detected.'); return { typography: {}, uncertain: u }; }
|
|
5
|
+
let di = 0, hi = 0, bi = 0;
|
|
6
|
+
for (const [sz, cl] of sorted) { const hd = /^h[1-6]$/.test(cl.tag) || sz >= 32; let n; if (sz >= 64 && di === 0) { n = 'display-lg'; di++; } else if (hd && sz >= 40) { n = hi === 0 ? 'headline-lg' : 'headline-md'; hi++; } else if (hd && sz >= 28) { n = hi <= 1 ? 'headline-md' : 'headline-sm'; hi++; } else if (hd) { n = 'title-lg'; } else if (sz <= 18 && bi === 0) { n = 'body-lg'; bi++; } else if (sz <= 18) { n = 'body-md'; bi++; } else if (sz <= 14) { n = 'label-md'; } else { n = 'body-sm'; }
|
|
7
|
+
t[n] = { fontFamily: cl.fontFamily, fontSize: `${sz}px`, fontWeight: String(cl.fontWeight || '400') }; if (cl.lineHeight && cl.lineHeight !== 'normal') t[n].lineHeight = cl.lineHeight; if (cl.letterSpacing && cl.letterSpacing !== 'normal' && cl.letterSpacing !== '0px') t[n].letterSpacing = cl.letterSpacing; }
|
|
8
|
+
return { typography: t, uncertain: u };
|
|
9
|
+
}
|