designkit-ai 1.1.2 → 1.4.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/AI-AGENT.md +22 -1
- package/README.md +5 -4
- package/bin/designkit.js +35 -6
- package/package.json +1 -1
- package/skills/autogen-design.md +367 -0
- package/src/commands/autogen.js +405 -0
- package/src/commands/config.js +55 -0
- package/src/commands/imagine.js +8 -1
- package/src/lib/config.js +54 -0
package/AI-AGENT.md
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
| Skill | File | What it does |
|
|
8
8
|
|-------|------|-------------|
|
|
9
|
+
| **AutoGen Design** | [skills/autogen-design.md](skills/autogen-design.md) | Generate a full design project: color spec + HTML for every screen/page |
|
|
9
10
|
| **Design** | [skills/design.md](skills/design.md) | Generate a pixel-perfect single-file HTML screen or page |
|
|
10
11
|
| **Contribute** | [skills/contribute.md](skills/contribute.md) | Create a valid new component or validate an existing one |
|
|
11
12
|
| **React** | [skills/react.md](skills/react.md) | Convert to React TypeScript component (inline styles) |
|
|
@@ -21,7 +22,27 @@
|
|
|
21
22
|
|
|
22
23
|
## Usage
|
|
23
24
|
|
|
24
|
-
###
|
|
25
|
+
### AutoGen — full design project (recommended for new projects)
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
Read skills/autogen-design.md, then generate a complete design project for:
|
|
29
|
+
"Personal finance tracking app" — dark, professional
|
|
30
|
+
Platform: mobile
|
|
31
|
+
Screens: splash, home, transactions, cards, profile, settings
|
|
32
|
+
Output to: output/finance-app/
|
|
33
|
+
Use placehold.jp for images.
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
Read skills/autogen-design.md, then generate a full design project for:
|
|
38
|
+
"SaaS analytics dashboard" — clean, indigo
|
|
39
|
+
Platform: web
|
|
40
|
+
Screens: landing, dashboard, reports, settings, pricing, onboarding
|
|
41
|
+
Output to: output/saas-dashboard/
|
|
42
|
+
Images folder: ./assets/marketing/
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Design only — single screen
|
|
25
46
|
|
|
26
47
|
```
|
|
27
48
|
Read skills/design.md, then design:
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://stackblitz.com/github/pixeliro-sys/designkit-source-for-ai)
|
|
4
4
|
|
|
5
|
-
> **502 ready-to-use HTML components** + **
|
|
5
|
+
> **502 ready-to-use HTML components** + **2 full-page design previews** for Web and Mobile.
|
|
6
6
|
> Token-based design system. Works with any AI agent to generate beautiful single-file HTML designs.
|
|
7
7
|
> Built by **[Pixeliro](https://pixeliro.com)** — AI-powered design tool for everyone.
|
|
8
8
|
|
|
@@ -24,7 +24,7 @@ No Figma license. No design handoff. No waiting. Just open an HTML file, see the
|
|
|
24
24
|
|
|
25
25
|
### Useful in design
|
|
26
26
|
|
|
27
|
-
- **Instant visual prototypes** — 502 components +
|
|
27
|
+
- **Instant visual prototypes** — 502 components + 2 full-page designs ready to open in any browser. No build step, no setup.
|
|
28
28
|
- **Token-driven theming** — change one CSS variable (`--kit-primary`) and the entire design recolors. Perfect for testing brand colors, dark mode, or client themes in seconds.
|
|
29
29
|
- **AI-ready structure** — feed components to Claude, GPT-4, or Gemini. The AI understands the token system and generates pixel-perfect screens that match your design language.
|
|
30
30
|
- **Mobile + Web in one kit** — 204 mobile components (iOS/Android) and 200 web components share the same token system, so mobile and web feel consistent by default.
|
|
@@ -120,8 +120,8 @@ DesignKit/
|
|
|
120
120
|
│
|
|
121
121
|
└── previews/
|
|
122
122
|
└── full-designs/
|
|
123
|
-
├── mobile/
|
|
124
|
-
└── web/
|
|
123
|
+
├── mobile/ 1 complete app design (Pixeliro app — 12 screens)
|
|
124
|
+
└── web/ 1 complete web design (SaaS Landing — 14 pages)
|
|
125
125
|
```
|
|
126
126
|
|
|
127
127
|
**Each component** is a self-contained HTML snippet using CSS custom properties (`--kit-*`).
|
|
@@ -402,6 +402,7 @@ npm install -g designkit-ai
|
|
|
402
402
|
| `designkit init` | Add design tokens (css / js / ts / json) |
|
|
403
403
|
| `designkit design "<prompt>"` | Generate UI with AI (Claude / Gemini / GPT-4o) |
|
|
404
404
|
| `designkit convert <file> --to <framework>` | Convert HTML to any framework with AI |
|
|
405
|
+
| `designkit autogen "<prompt>"` | Generate a full multi-screen design project with gallery |
|
|
405
406
|
| `designkit imagine "<prompt>"` | Generate images with Gemini Imagen 3 or DALL-E 3 |
|
|
406
407
|
|
|
407
408
|
**→ Full CLI reference: [CLI.md](CLI.md)**
|
package/bin/designkit.js
CHANGED
|
@@ -8,6 +8,9 @@ import { designCommand } from '../src/commands/design.js'
|
|
|
8
8
|
import { convertCommand } from '../src/commands/convert.js'
|
|
9
9
|
import { imagineCommand } from '../src/commands/imagine.js'
|
|
10
10
|
import { projectCommand } from '../src/commands/project.js'
|
|
11
|
+
import { autogenCommand } from '../src/commands/autogen.js'
|
|
12
|
+
import { configCommand } from '../src/commands/config.js'
|
|
13
|
+
import { getDefaultProvider, getDefaultPlatform } from '../src/lib/config.js'
|
|
11
14
|
import { readFileSync } from 'fs'
|
|
12
15
|
import { fileURLToPath } from 'url'
|
|
13
16
|
import { dirname, join } from 'path'
|
|
@@ -50,17 +53,17 @@ program
|
|
|
50
53
|
.command('design <prompt>')
|
|
51
54
|
.description('Generate a UI component with AI')
|
|
52
55
|
.option('-s, --skill <skill>', 'Skill: design, react, nextjs, vue, svelte, tailwind, flutter, swiftui, react-native', 'design')
|
|
53
|
-
.option('-p, --provider <provider>',
|
|
56
|
+
.option('-p, --provider <provider>', `AI provider: anthropic, gemini, openai (default: ${getDefaultProvider()})`)
|
|
54
57
|
.option('-o, --output <file>', 'Save output to file')
|
|
55
|
-
.action(designCommand)
|
|
58
|
+
.action((prompt, opts) => designCommand(prompt, { ...opts, provider: opts.provider || getDefaultProvider() }))
|
|
56
59
|
|
|
57
60
|
program
|
|
58
61
|
.command('convert <file>')
|
|
59
62
|
.description('Convert an HTML component to another framework with AI')
|
|
60
63
|
.requiredOption('--to <framework>', 'Target: react, nextjs, vue, svelte, tailwind, flutter, swiftui, react-native')
|
|
61
|
-
.option('-p, --provider <provider>',
|
|
64
|
+
.option('-p, --provider <provider>', `AI provider: anthropic, gemini, openai (default: ${getDefaultProvider()})`)
|
|
62
65
|
.option('-o, --output <file>', 'Save output to file (default: same name, new extension)')
|
|
63
|
-
.action(convertCommand)
|
|
66
|
+
.action((file, opts) => convertCommand(file, { ...opts, provider: opts.provider || getDefaultProvider() }))
|
|
64
67
|
|
|
65
68
|
program
|
|
66
69
|
.command('project')
|
|
@@ -70,8 +73,8 @@ program
|
|
|
70
73
|
.action(projectCommand)
|
|
71
74
|
|
|
72
75
|
program
|
|
73
|
-
.command('imagine
|
|
74
|
-
.description('Generate an image with AI (Gemini
|
|
76
|
+
.command('imagine [prompt]')
|
|
77
|
+
.description('Generate an image with AI (Gemini or DALL-E 3)')
|
|
75
78
|
.option('-p, --provider <provider>', 'AI provider: gemini, openai', 'gemini')
|
|
76
79
|
.option('-o, --output <dir>', 'Output directory', '.')
|
|
77
80
|
.option('-n, --name <name>', 'Output filename (without extension)', 'image')
|
|
@@ -83,4 +86,30 @@ program
|
|
|
83
86
|
.option('--quality <quality>', 'DALL-E quality: standard, hd', 'standard')
|
|
84
87
|
.action(imagineCommand)
|
|
85
88
|
|
|
89
|
+
program
|
|
90
|
+
.command('autogen <project>')
|
|
91
|
+
.description('Generate a full design project: color spec + HTML files for every screen')
|
|
92
|
+
.option('-p, --provider <provider>', `AI provider: anthropic, gemini, openai (default: ${getDefaultProvider()})`)
|
|
93
|
+
.option('--platform <platform>', `Target platform: mobile, web (default: ${getDefaultPlatform()})`)
|
|
94
|
+
.option('--screens <list>', 'Comma-separated screen/page names (auto-generated if omitted)')
|
|
95
|
+
.option('--images <folder>', 'Path to local image assets folder (uses placehold.jp if omitted)')
|
|
96
|
+
.option('-o, --output <dir>', 'Output directory (default: output/<project-slug>)')
|
|
97
|
+
.action((project, opts) => autogenCommand(project, {
|
|
98
|
+
...opts,
|
|
99
|
+
provider: opts.provider || getDefaultProvider(),
|
|
100
|
+
platform: opts.platform || getDefaultPlatform()
|
|
101
|
+
}))
|
|
102
|
+
|
|
103
|
+
program
|
|
104
|
+
.command('config [action] [args...]')
|
|
105
|
+
.description('Get or set default config values (provider, platform)')
|
|
106
|
+
.addHelpText('after', `
|
|
107
|
+
Examples:
|
|
108
|
+
designkit config # show all config
|
|
109
|
+
designkit config set provider gemini # set default provider
|
|
110
|
+
designkit config set provider anthropic
|
|
111
|
+
designkit config set platform web
|
|
112
|
+
designkit config get provider`)
|
|
113
|
+
.action(configCommand)
|
|
114
|
+
|
|
86
115
|
program.parse()
|
package/package.json
CHANGED
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
# Skill: AutoGen Design Pipeline
|
|
2
|
+
|
|
3
|
+
> **For:** Claude, GPT-4o, Gemini, Cursor, Copilot, or any AI agent.
|
|
4
|
+
> **Goal:** Generate a complete design project — color system spec → per-screen/page HTML files.
|
|
5
|
+
> **Output:** `design-spec.json` + individual `.html` files for every screen or page.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
This skill runs a **two-phase pipeline**:
|
|
12
|
+
|
|
13
|
+
| Phase | Input | Output |
|
|
14
|
+
|-------|-------|--------|
|
|
15
|
+
| **1 — Design Spec** | Project description + platform + screens list | `design-spec.json` |
|
|
16
|
+
| **2 — HTML Generation** | design-spec.json + per-screen description | One `.html` file per screen |
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Phase 1 — Generate Design Spec
|
|
21
|
+
|
|
22
|
+
### Your role in Phase 1
|
|
23
|
+
|
|
24
|
+
You are a **senior product designer**. Given a project description, you will create a complete, coherent design system specification — colors, typography, spacing mood, and a screen/page plan.
|
|
25
|
+
|
|
26
|
+
### Input format
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
Project: {description}
|
|
30
|
+
Platform: mobile | web | both
|
|
31
|
+
Screens: {optional comma-separated list, e.g. "home, transactions, profile, settings"}
|
|
32
|
+
Images folder: {optional path, e.g. "./assets/images" — use real images from here}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Output format — design-spec.json
|
|
36
|
+
|
|
37
|
+
Respond with a **single JSON object** (no markdown fences, no commentary). Structure:
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"project": "Project Name",
|
|
42
|
+
"platform": "mobile",
|
|
43
|
+
"mood": "Short mood description — e.g. Finance / Banking, dark professional",
|
|
44
|
+
"imageFolder": null,
|
|
45
|
+
"colors": {
|
|
46
|
+
"primary": "#6366F1",
|
|
47
|
+
"primaryText": "#FFFFFF",
|
|
48
|
+
"secondary": "#64748B",
|
|
49
|
+
"accent": "#F59E0B",
|
|
50
|
+
"bg": "#0A0E1A",
|
|
51
|
+
"surface": "#141926",
|
|
52
|
+
"surface2": "#1E2538",
|
|
53
|
+
"surface3": "#334155",
|
|
54
|
+
"text": "#F1F5F9",
|
|
55
|
+
"text2": "#94A3B8",
|
|
56
|
+
"text3": "#64748B",
|
|
57
|
+
"textInverse": "#0F172A",
|
|
58
|
+
"border": "#1E2538",
|
|
59
|
+
"borderStrong": "#334155",
|
|
60
|
+
"success": "#22C55E",
|
|
61
|
+
"successBg": "#052E16",
|
|
62
|
+
"error": "#EF4444",
|
|
63
|
+
"errorBg": "#450A0A",
|
|
64
|
+
"warning": "#F59E0B",
|
|
65
|
+
"warningBg": "#431407",
|
|
66
|
+
"info": "#3B82F6",
|
|
67
|
+
"infoBg": "#172554"
|
|
68
|
+
},
|
|
69
|
+
"typography": {
|
|
70
|
+
"fontFamily": "Inter, system-ui, sans-serif",
|
|
71
|
+
"scale": {
|
|
72
|
+
"xs": "11px",
|
|
73
|
+
"sm": "13px",
|
|
74
|
+
"md": "15px",
|
|
75
|
+
"lg": "17px",
|
|
76
|
+
"xl": "20px",
|
|
77
|
+
"2xl": "24px",
|
|
78
|
+
"3xl": "32px",
|
|
79
|
+
"4xl": "48px"
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"radius": {
|
|
83
|
+
"sm": "6px",
|
|
84
|
+
"md": "10px",
|
|
85
|
+
"lg": "14px",
|
|
86
|
+
"xl": "20px",
|
|
87
|
+
"full": "9999px"
|
|
88
|
+
},
|
|
89
|
+
"shadow": {
|
|
90
|
+
"sm": "0 1px 3px rgba(0,0,0,0.08)",
|
|
91
|
+
"md": "0 4px 12px rgba(0,0,0,0.10)",
|
|
92
|
+
"lg": "0 8px 32px rgba(0,0,0,0.12)",
|
|
93
|
+
"xl": "0 20px 60px rgba(0,0,0,0.15)"
|
|
94
|
+
},
|
|
95
|
+
"screens": [
|
|
96
|
+
{
|
|
97
|
+
"id": "home",
|
|
98
|
+
"name": "Home Screen",
|
|
99
|
+
"platform": "mobile",
|
|
100
|
+
"description": "Main dashboard showing balance, quick actions, recent transactions, and spending chart",
|
|
101
|
+
"components": [
|
|
102
|
+
"iOS Status Bar",
|
|
103
|
+
"Top App Bar with greeting and avatar",
|
|
104
|
+
"Balance Card (gradient)",
|
|
105
|
+
"Quick Action Buttons row (Send, Receive, Pay, Top Up)",
|
|
106
|
+
"Spending Chart (donut or bar)",
|
|
107
|
+
"Recent Transactions list (3–5 items)",
|
|
108
|
+
"Bottom Tab Navigation (Home, Cards, History, Profile)"
|
|
109
|
+
],
|
|
110
|
+
"file": "home.html"
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Color mood presets — pick the closest and customize
|
|
117
|
+
|
|
118
|
+
| Mood | primary | bg | surface | text |
|
|
119
|
+
|------|---------|----|---------|------|
|
|
120
|
+
| Finance / Banking (dark) | `#6366F1` | `#0A0E1A` | `#141926` | `#F1F5F9` |
|
|
121
|
+
| Health / Wellness (light) | `#10B981` | `#F0FDF4` | `#FFFFFF` | `#064E3B` |
|
|
122
|
+
| E-commerce (orange) | `#F97316` | `#FFFFFF` | `#FFF7ED` | `#1C1917` |
|
|
123
|
+
| SaaS / Productivity (indigo) | `#6366F1` | `#FFFFFF` | `#F8FAFC` | `#0F172A` |
|
|
124
|
+
| Social / Dating (pink) | `#EC4899` | `#FFFFFF` | `#FDF2F8` | `#1F2937` |
|
|
125
|
+
| Travel (blue-teal) | `#0EA5E9` | `#F0F9FF` | `#FFFFFF` | `#0C4A6E` |
|
|
126
|
+
| News / Editorial (neutral) | `#1D4ED8` | `#FFFFFF` | `#F9FAFB` | `#111827` |
|
|
127
|
+
| Fitness (dark orange) | `#F97316` | `#0C0A09` | `#1C1917` | `#F5F5F4` |
|
|
128
|
+
| Education (purple) | `#7C3AED` | `#FFFFFF` | `#F5F3FF` | `#1E1B4B` |
|
|
129
|
+
|
|
130
|
+
### Screen count guidelines
|
|
131
|
+
|
|
132
|
+
| Platform | Recommended screens |
|
|
133
|
+
|----------|-------------------|
|
|
134
|
+
| Mobile app | 4–8 screens |
|
|
135
|
+
| Web app | 3–6 pages |
|
|
136
|
+
| Landing site | 1–3 pages |
|
|
137
|
+
| Both | 4–8 mobile + 3–5 web |
|
|
138
|
+
|
|
139
|
+
### Rules for Phase 1
|
|
140
|
+
|
|
141
|
+
1. Output **only valid JSON** — no markdown, no explanation
|
|
142
|
+
2. Colors must form a **cohesive, professional palette** — avoid random colors
|
|
143
|
+
3. `screens[].components` is an **ordered list** matching top-to-bottom layout
|
|
144
|
+
4. `screens[].id` is kebab-case, matches the `file` name: `home` → `home.html`
|
|
145
|
+
5. If user provides `imageFolder`, set `"imageFolder": "./path/to/images"` in the spec
|
|
146
|
+
6. `platform` per screen can override the project platform for multi-platform projects
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Phase 2 — Generate HTML Screens
|
|
151
|
+
|
|
152
|
+
### Your role in Phase 2
|
|
153
|
+
|
|
154
|
+
You are a **UI/UX designer and front-end developer**.
|
|
155
|
+
For each screen entry in `design-spec.json`, generate a **complete, self-contained HTML file**.
|
|
156
|
+
|
|
157
|
+
### Input per screen
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
Design spec: {full design-spec.json content}
|
|
161
|
+
Screen: {screens[i] object}
|
|
162
|
+
Image folder: {null | "./path/to/images"}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### CSS token mapping
|
|
166
|
+
|
|
167
|
+
Map `design-spec.json` colors to `--kit-*` CSS variables:
|
|
168
|
+
|
|
169
|
+
```css
|
|
170
|
+
:root {
|
|
171
|
+
--kit-primary: {colors.primary};
|
|
172
|
+
--kit-primary-text: {colors.primaryText};
|
|
173
|
+
--kit-secondary: {colors.secondary};
|
|
174
|
+
--kit-accent: {colors.accent};
|
|
175
|
+
--kit-bg: {colors.bg};
|
|
176
|
+
--kit-surface: {colors.surface};
|
|
177
|
+
--kit-surface-2: {colors.surface2};
|
|
178
|
+
--kit-surface-3: {colors.surface3};
|
|
179
|
+
--kit-text: {colors.text};
|
|
180
|
+
--kit-text-2: {colors.text2};
|
|
181
|
+
--kit-text-3: {colors.text3};
|
|
182
|
+
--kit-text-inverse: {colors.textInverse};
|
|
183
|
+
--kit-border: {colors.border};
|
|
184
|
+
--kit-border-strong: {colors.borderStrong};
|
|
185
|
+
--kit-success: {colors.success};
|
|
186
|
+
--kit-success-bg: {colors.successBg};
|
|
187
|
+
--kit-error: {colors.error};
|
|
188
|
+
--kit-error-bg: {colors.errorBg};
|
|
189
|
+
--kit-warning: {colors.warning};
|
|
190
|
+
--kit-warning-bg: {colors.warningBg};
|
|
191
|
+
--kit-info: {colors.info};
|
|
192
|
+
--kit-info-bg: {colors.infoBg};
|
|
193
|
+
|
|
194
|
+
--kit-font: {typography.fontFamily};
|
|
195
|
+
--kit-text-xs: {typography.scale.xs};
|
|
196
|
+
--kit-text-sm: {typography.scale.sm};
|
|
197
|
+
--kit-text-md: {typography.scale.md};
|
|
198
|
+
--kit-text-lg: {typography.scale.lg};
|
|
199
|
+
--kit-text-xl: {typography.scale.xl};
|
|
200
|
+
--kit-text-2xl: {typography.scale["2xl"]};
|
|
201
|
+
--kit-text-3xl: {typography.scale["3xl"]};
|
|
202
|
+
--kit-text-4xl: {typography.scale["4xl"]};
|
|
203
|
+
|
|
204
|
+
--kit-radius-sm: {radius.sm};
|
|
205
|
+
--kit-radius: {radius.md};
|
|
206
|
+
--kit-radius-lg: {radius.lg};
|
|
207
|
+
--kit-radius-xl: {radius.xl};
|
|
208
|
+
--kit-radius-full: {radius.full};
|
|
209
|
+
|
|
210
|
+
--kit-shadow-sm: {shadow.sm};
|
|
211
|
+
--kit-shadow: {shadow.md};
|
|
212
|
+
--kit-shadow-lg: {shadow.lg};
|
|
213
|
+
--kit-shadow-xl: {shadow.xl};
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Image handling
|
|
218
|
+
|
|
219
|
+
- If `imageFolder` is **null**: use `https://placehold.jp/{W}x{H}.png` for all images
|
|
220
|
+
- If `imageFolder` is set: use `{imageFolder}/{filename}` for images — pick realistic filenames
|
|
221
|
+
- Use descriptive filenames: `avatar-user.jpg`, `product-hero.jpg`, `banner-sale.png`
|
|
222
|
+
- If the file might not exist, add `onerror="this.src='https://placehold.jp/{W}x{H}.png'"` as fallback
|
|
223
|
+
|
|
224
|
+
### Output format — single HTML file
|
|
225
|
+
|
|
226
|
+
Follow the exact structure from [design.md](design.md):
|
|
227
|
+
|
|
228
|
+
```html
|
|
229
|
+
<!DOCTYPE html>
|
|
230
|
+
<html lang="en">
|
|
231
|
+
<head>
|
|
232
|
+
<meta charset="UTF-8">
|
|
233
|
+
<meta name="viewport" content="width=390, initial-scale=1.0"><!-- 390 mobile, 1440 web -->
|
|
234
|
+
<title>{screen.name} — {project.name}</title>
|
|
235
|
+
<style>
|
|
236
|
+
/* ── 1. Design Spec Tokens ─────────────────────────────── */
|
|
237
|
+
:root { /* mapped from design-spec.json */ }
|
|
238
|
+
|
|
239
|
+
/* ── 2. Reset ────────────────────────────────────────────── */
|
|
240
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
241
|
+
body {
|
|
242
|
+
font-family: var(--kit-font);
|
|
243
|
+
background: var(--kit-bg);
|
|
244
|
+
color: var(--kit-text);
|
|
245
|
+
-webkit-font-smoothing: antialiased;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/* ── 3. Screen wrapper ─────────────────────────────────── */
|
|
249
|
+
.screen { /* mobile: width:390px / web: max-width:1440px */ }
|
|
250
|
+
</style>
|
|
251
|
+
</head>
|
|
252
|
+
<body>
|
|
253
|
+
<div class="screen">
|
|
254
|
+
<!-- Components from screen.components, top to bottom -->
|
|
255
|
+
</div>
|
|
256
|
+
</body>
|
|
257
|
+
</html>
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Rules for Phase 2
|
|
261
|
+
|
|
262
|
+
1. **All colors via `var(--kit-*)`** — never hardcode `#hex` inside component HTML
|
|
263
|
+
2. **Inline styles only** — no Tailwind, no Bootstrap, no class-based CSS
|
|
264
|
+
3. **Semantic HTML** — `<button>`, `<nav>`, `<header>`, `<a>`, `<ul>/<li>`, `<h1>`–`<h6>`
|
|
265
|
+
4. **No JavaScript** — static design preview only
|
|
266
|
+
5. **`data-component="..."`** on each component root element
|
|
267
|
+
6. **Looks complete and polished** — not a skeleton, real content and realistic data
|
|
268
|
+
7. **Consistent** — all screens share the same color tokens from the spec
|
|
269
|
+
8. **Platform-aware** — mobile uses 390px viewport, web uses 1440px
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Full Pipeline — Agent Instructions
|
|
274
|
+
|
|
275
|
+
When given an autogen-design task, run these steps in order:
|
|
276
|
+
|
|
277
|
+
### Step 1 — Parse request
|
|
278
|
+
|
|
279
|
+
Extract:
|
|
280
|
+
- Project description (required)
|
|
281
|
+
- Platform: `mobile` | `web` | `both` (default: `mobile`)
|
|
282
|
+
- Screens list: from user input, or auto-generate based on project type
|
|
283
|
+
- Output directory: where to save files
|
|
284
|
+
- Images folder: `null` or a provided path
|
|
285
|
+
|
|
286
|
+
### Step 2 — Generate design-spec.json
|
|
287
|
+
|
|
288
|
+
Call AI with Phase 1 prompt. Save result as `{outputDir}/design-spec.json`.
|
|
289
|
+
|
|
290
|
+
### Step 3 — Generate HTML for each screen
|
|
291
|
+
|
|
292
|
+
For each screen in `spec.screens`, call AI with Phase 2 prompt.
|
|
293
|
+
Save each result as `{outputDir}/{screen.file}`.
|
|
294
|
+
|
|
295
|
+
### Step 4 — Generate index.html (screen gallery)
|
|
296
|
+
|
|
297
|
+
Create a simple HTML gallery that links to all generated screens:
|
|
298
|
+
|
|
299
|
+
```html
|
|
300
|
+
<!DOCTYPE html>
|
|
301
|
+
<html lang="en">
|
|
302
|
+
<head>
|
|
303
|
+
<meta charset="UTF-8">
|
|
304
|
+
<title>{project} — Design Preview</title>
|
|
305
|
+
<style>
|
|
306
|
+
/* Grid of screen preview cards */
|
|
307
|
+
</style>
|
|
308
|
+
</head>
|
|
309
|
+
<body>
|
|
310
|
+
<h1>{project} — Design Preview</h1>
|
|
311
|
+
<div class="grid">
|
|
312
|
+
{screens.map(s => `<a href="${s.file}"><div class="card">${s.name}</div></a>`)}
|
|
313
|
+
</div>
|
|
314
|
+
</body>
|
|
315
|
+
</html>
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Step 5 — Output summary
|
|
319
|
+
|
|
320
|
+
Print:
|
|
321
|
+
```
|
|
322
|
+
✓ design-spec.json
|
|
323
|
+
✓ home.html
|
|
324
|
+
✓ transactions.html
|
|
325
|
+
✓ profile.html
|
|
326
|
+
✓ settings.html
|
|
327
|
+
✓ index.html
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## Example invocations
|
|
333
|
+
|
|
334
|
+
### CLI (designkit autogen)
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
# Mobile finance app — auto-generate screens
|
|
338
|
+
designkit autogen "Personal finance tracking app" --platform mobile
|
|
339
|
+
|
|
340
|
+
# Web SaaS dashboard — specify screens
|
|
341
|
+
designkit autogen "SaaS analytics dashboard" --platform web \
|
|
342
|
+
--screens "landing,dashboard,reports,settings,pricing" \
|
|
343
|
+
--output output/saas-dashboard
|
|
344
|
+
|
|
345
|
+
# App with real images
|
|
346
|
+
designkit autogen "Travel booking app" --platform mobile \
|
|
347
|
+
--images ./assets/travel-images \
|
|
348
|
+
--output output/travel-app
|
|
349
|
+
|
|
350
|
+
# Use Gemini provider
|
|
351
|
+
designkit autogen "E-commerce store" --provider gemini --output output/ecommerce
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### AI Agent (no CLI)
|
|
355
|
+
|
|
356
|
+
```
|
|
357
|
+
Read skills/autogen-design.md, then:
|
|
358
|
+
Generate a complete design project for: "Food delivery app — dark, modern"
|
|
359
|
+
Platform: mobile
|
|
360
|
+
Screens: splash, home, restaurant-list, restaurant-detail, cart, checkout, order-tracking, profile
|
|
361
|
+
Output directory: output/food-app/
|
|
362
|
+
Use placehold.jp for images.
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
*Part of [DesignKit](../README.md) · See [skills/](../skills/) for individual screen/component skills.*
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'
|
|
2
|
+
import { fileURLToPath } from 'url'
|
|
3
|
+
import { dirname, join, resolve } from 'path'
|
|
4
|
+
import { streamText, PROVIDERS, stripCodeFences } from '../lib/providers.js'
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
7
|
+
const ROOT = join(__dirname, '../../')
|
|
8
|
+
|
|
9
|
+
// ─── Skill loaders ────────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
function loadSkill(name) {
|
|
12
|
+
const skillPath = join(ROOT, 'skills', `${name}.md`)
|
|
13
|
+
if (!existsSync(skillPath)) return ''
|
|
14
|
+
return readFileSync(skillPath, 'utf8')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// ─── Phase 1: Generate design spec ───────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
function buildSpecPrompt(projectDesc, platform, screens, imageFolder) {
|
|
20
|
+
const screenHint = screens?.length
|
|
21
|
+
? `Screens/pages to generate: ${screens.join(', ')}`
|
|
22
|
+
: `Auto-determine the most important screens/pages for this type of app or site.`
|
|
23
|
+
|
|
24
|
+
const imgHint = imageFolder
|
|
25
|
+
? `Images folder: ${imageFolder} — use real image filenames from this folder. Add onerror fallback to placehold.jp.`
|
|
26
|
+
: `Images folder: null — use https://placehold.jp/{W}x{H}.png for all images.`
|
|
27
|
+
|
|
28
|
+
return [
|
|
29
|
+
`Project: ${projectDesc}`,
|
|
30
|
+
`Platform: ${platform}`,
|
|
31
|
+
screenHint,
|
|
32
|
+
imgHint,
|
|
33
|
+
'',
|
|
34
|
+
'Generate the design-spec.json for this project.',
|
|
35
|
+
'Output ONLY the raw JSON object — no markdown fences, no explanation, no commentary.',
|
|
36
|
+
'The JSON must be valid and complete per the autogen-design.md Phase 1 spec.'
|
|
37
|
+
].join('\n')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function generateSpec(prompt, options) {
|
|
41
|
+
const provider = options.provider || 'anthropic'
|
|
42
|
+
const skill = loadSkill('autogen-design')
|
|
43
|
+
const systemPrompt = [
|
|
44
|
+
skill,
|
|
45
|
+
'---',
|
|
46
|
+
'# Your task: Phase 1 — Generate Design Spec',
|
|
47
|
+
'Output ONLY valid JSON. No markdown. No commentary.'
|
|
48
|
+
].join('\n\n')
|
|
49
|
+
|
|
50
|
+
process.stderr.write('\n[Phase 1] Generating design spec...\n')
|
|
51
|
+
|
|
52
|
+
let fullOutput = ''
|
|
53
|
+
await streamText({
|
|
54
|
+
provider,
|
|
55
|
+
systemPrompt,
|
|
56
|
+
userMessage: prompt,
|
|
57
|
+
onText: (text) => {
|
|
58
|
+
process.stderr.write('.')
|
|
59
|
+
fullOutput += text
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
process.stderr.write('\n')
|
|
63
|
+
|
|
64
|
+
// Strip fences if AI wrapped it anyway
|
|
65
|
+
const cleaned = stripCodeFences(fullOutput).trim()
|
|
66
|
+
try {
|
|
67
|
+
return JSON.parse(cleaned)
|
|
68
|
+
} catch {
|
|
69
|
+
// Try to extract JSON object from the output
|
|
70
|
+
const match = cleaned.match(/\{[\s\S]*\}/)
|
|
71
|
+
if (match) {
|
|
72
|
+
try {
|
|
73
|
+
return JSON.parse(match[0])
|
|
74
|
+
} catch {
|
|
75
|
+
// ignore
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
throw new Error(`Phase 1 output was not valid JSON.\n\nRaw output:\n${cleaned.slice(0, 500)}`)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ─── Phase 2: Generate HTML for each screen ───────────────────────────────────
|
|
83
|
+
|
|
84
|
+
function buildScreenPrompt(spec, screen) {
|
|
85
|
+
const imageNote = spec.imageFolder
|
|
86
|
+
? `Use images from: ${spec.imageFolder}/. Use descriptive filenames (e.g. avatar-user.jpg). Add onerror="this.src='https://placehold.jp/{W}x{H}.png'" fallback on each <img>.`
|
|
87
|
+
: `Use https://placehold.jp/{W}x{H}.png for all images (no external images needed).`
|
|
88
|
+
|
|
89
|
+
return [
|
|
90
|
+
'# Design Spec',
|
|
91
|
+
'```json',
|
|
92
|
+
JSON.stringify(spec, null, 2),
|
|
93
|
+
'```',
|
|
94
|
+
'',
|
|
95
|
+
'# Screen to generate',
|
|
96
|
+
'```json',
|
|
97
|
+
JSON.stringify(screen, null, 2),
|
|
98
|
+
'```',
|
|
99
|
+
'',
|
|
100
|
+
imageNote,
|
|
101
|
+
'',
|
|
102
|
+
`Generate the complete, self-contained HTML file for "${screen.name}".`,
|
|
103
|
+
'Output ONLY the raw HTML — no markdown fences, no explanation.'
|
|
104
|
+
].join('\n')
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function generateScreen(spec, screen, options) {
|
|
108
|
+
const provider = options.provider || 'anthropic'
|
|
109
|
+
const skillDesign = loadSkill('design')
|
|
110
|
+
const skillAutogen = loadSkill('autogen-design')
|
|
111
|
+
|
|
112
|
+
const systemPrompt = [
|
|
113
|
+
skillAutogen,
|
|
114
|
+
'---',
|
|
115
|
+
'# Active Skill: design (HTML output)',
|
|
116
|
+
skillDesign,
|
|
117
|
+
'---',
|
|
118
|
+
'# Your task: Phase 2 — Generate HTML screen',
|
|
119
|
+
'Output ONLY the raw HTML file content. No markdown fences. No explanation.'
|
|
120
|
+
].join('\n\n')
|
|
121
|
+
|
|
122
|
+
const userMessage = buildScreenPrompt(spec, screen)
|
|
123
|
+
|
|
124
|
+
let fullOutput = ''
|
|
125
|
+
await streamText({
|
|
126
|
+
provider,
|
|
127
|
+
systemPrompt,
|
|
128
|
+
userMessage,
|
|
129
|
+
onText: (text) => {
|
|
130
|
+
process.stderr.write('.')
|
|
131
|
+
fullOutput += text
|
|
132
|
+
}
|
|
133
|
+
})
|
|
134
|
+
process.stderr.write('\n')
|
|
135
|
+
|
|
136
|
+
return stripCodeFences(fullOutput).trim()
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ─── Index gallery ─────────────────────────────────────────────────────────────
|
|
140
|
+
|
|
141
|
+
function generateIndexHtml(spec) {
|
|
142
|
+
const isMobile = spec.platform === 'mobile'
|
|
143
|
+
const frameW = isMobile ? 390 : 1280
|
|
144
|
+
const frameH = isMobile ? 844 : 800
|
|
145
|
+
|
|
146
|
+
const cards = spec.screens.map(s => `
|
|
147
|
+
<a class="card" href="${s.file}" target="_blank">
|
|
148
|
+
<div class="frame-wrap">
|
|
149
|
+
<iframe src="${s.file}" width="${frameW}" height="${frameH}" scrolling="no" loading="lazy" title="${s.name}"></iframe>
|
|
150
|
+
<div class="frame-overlay"></div>
|
|
151
|
+
</div>
|
|
152
|
+
<div class="card-label">
|
|
153
|
+
<span class="card-name">${s.name}</span>
|
|
154
|
+
<span class="card-file">${s.file}</span>
|
|
155
|
+
</div>
|
|
156
|
+
</a>`).join('\n')
|
|
157
|
+
|
|
158
|
+
return `<!DOCTYPE html>
|
|
159
|
+
<html lang="en">
|
|
160
|
+
<head>
|
|
161
|
+
<meta charset="UTF-8">
|
|
162
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
163
|
+
<title>${spec.project} — Design Preview</title>
|
|
164
|
+
<style>
|
|
165
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
166
|
+
body {
|
|
167
|
+
font-family: Inter, system-ui, -apple-system, sans-serif;
|
|
168
|
+
background: #09090B;
|
|
169
|
+
color: #F1F5F9;
|
|
170
|
+
min-height: 100vh;
|
|
171
|
+
padding: 40px 24px;
|
|
172
|
+
-webkit-font-smoothing: antialiased;
|
|
173
|
+
}
|
|
174
|
+
header {
|
|
175
|
+
max-width: 1200px;
|
|
176
|
+
margin: 0 auto 40px;
|
|
177
|
+
}
|
|
178
|
+
header h1 {
|
|
179
|
+
font-size: 28px;
|
|
180
|
+
font-weight: 700;
|
|
181
|
+
letter-spacing: -0.5px;
|
|
182
|
+
margin-bottom: 8px;
|
|
183
|
+
}
|
|
184
|
+
header p {
|
|
185
|
+
font-size: 14px;
|
|
186
|
+
color: #64748B;
|
|
187
|
+
}
|
|
188
|
+
.meta {
|
|
189
|
+
display: flex;
|
|
190
|
+
gap: 12px;
|
|
191
|
+
margin-top: 16px;
|
|
192
|
+
flex-wrap: wrap;
|
|
193
|
+
}
|
|
194
|
+
.badge {
|
|
195
|
+
display: inline-flex;
|
|
196
|
+
align-items: center;
|
|
197
|
+
gap: 6px;
|
|
198
|
+
padding: 4px 12px;
|
|
199
|
+
background: #1E293B;
|
|
200
|
+
border: 1px solid #334155;
|
|
201
|
+
border-radius: 9999px;
|
|
202
|
+
font-size: 12px;
|
|
203
|
+
color: #94A3B8;
|
|
204
|
+
}
|
|
205
|
+
.color-dot {
|
|
206
|
+
width: 10px;
|
|
207
|
+
height: 10px;
|
|
208
|
+
border-radius: 50%;
|
|
209
|
+
background: ${spec.colors?.primary || '#6366F1'};
|
|
210
|
+
display: inline-block;
|
|
211
|
+
}
|
|
212
|
+
.grid {
|
|
213
|
+
max-width: 1200px;
|
|
214
|
+
margin: 0 auto;
|
|
215
|
+
display: grid;
|
|
216
|
+
grid-template-columns: repeat(auto-fill, minmax(${isMobile ? '220px' : '380px'}, 1fr));
|
|
217
|
+
gap: 24px;
|
|
218
|
+
}
|
|
219
|
+
.card {
|
|
220
|
+
display: flex;
|
|
221
|
+
flex-direction: column;
|
|
222
|
+
border-radius: 12px;
|
|
223
|
+
border: 1px solid #1E293B;
|
|
224
|
+
overflow: hidden;
|
|
225
|
+
background: #111827;
|
|
226
|
+
text-decoration: none;
|
|
227
|
+
color: inherit;
|
|
228
|
+
transition: border-color 0.2s, transform 0.2s, box-shadow 0.2s;
|
|
229
|
+
}
|
|
230
|
+
.card:hover {
|
|
231
|
+
border-color: #334155;
|
|
232
|
+
transform: translateY(-2px);
|
|
233
|
+
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
|
|
234
|
+
}
|
|
235
|
+
.frame-wrap {
|
|
236
|
+
position: relative;
|
|
237
|
+
width: 100%;
|
|
238
|
+
aspect-ratio: ${isMobile ? '390/844' : '16/10'};
|
|
239
|
+
overflow: hidden;
|
|
240
|
+
background: #0F172A;
|
|
241
|
+
}
|
|
242
|
+
.frame-wrap iframe {
|
|
243
|
+
position: absolute;
|
|
244
|
+
top: 0;
|
|
245
|
+
left: 0;
|
|
246
|
+
width: ${frameW}px;
|
|
247
|
+
height: ${frameH}px;
|
|
248
|
+
border: none;
|
|
249
|
+
transform-origin: top left;
|
|
250
|
+
}
|
|
251
|
+
.frame-overlay {
|
|
252
|
+
position: absolute;
|
|
253
|
+
inset: 0;
|
|
254
|
+
cursor: pointer;
|
|
255
|
+
}
|
|
256
|
+
.card-label {
|
|
257
|
+
padding: 12px 16px;
|
|
258
|
+
border-top: 1px solid #1E293B;
|
|
259
|
+
display: flex;
|
|
260
|
+
justify-content: space-between;
|
|
261
|
+
align-items: center;
|
|
262
|
+
}
|
|
263
|
+
.card-name {
|
|
264
|
+
font-size: 13px;
|
|
265
|
+
font-weight: 600;
|
|
266
|
+
color: #F1F5F9;
|
|
267
|
+
}
|
|
268
|
+
.card-file {
|
|
269
|
+
font-size: 11px;
|
|
270
|
+
color: #475569;
|
|
271
|
+
font-family: monospace;
|
|
272
|
+
}
|
|
273
|
+
footer {
|
|
274
|
+
max-width: 1200px;
|
|
275
|
+
margin: 48px auto 0;
|
|
276
|
+
padding-top: 24px;
|
|
277
|
+
border-top: 1px solid #1E293B;
|
|
278
|
+
font-size: 12px;
|
|
279
|
+
color: #334155;
|
|
280
|
+
}
|
|
281
|
+
</style>
|
|
282
|
+
</head>
|
|
283
|
+
<body>
|
|
284
|
+
<header>
|
|
285
|
+
<h1>${spec.project}</h1>
|
|
286
|
+
<p>${spec.mood || ''}</p>
|
|
287
|
+
<div class="meta">
|
|
288
|
+
<span class="badge"><span class="color-dot"></span> Primary: ${spec.colors?.primary || 'N/A'}</span>
|
|
289
|
+
<span class="badge">Platform: ${spec.platform}</span>
|
|
290
|
+
<span class="badge">${spec.screens.length} screens</span>
|
|
291
|
+
${spec.typography?.fontFamily ? `<span class="badge">${spec.typography.fontFamily.split(',')[0]}</span>` : ''}
|
|
292
|
+
</div>
|
|
293
|
+
</header>
|
|
294
|
+
<div class="grid">
|
|
295
|
+
${cards}
|
|
296
|
+
</div>
|
|
297
|
+
<footer>Generated by DesignKit AutoGen · ${new Date().toLocaleDateString()}</footer>
|
|
298
|
+
<script>
|
|
299
|
+
function scaleFrames() {
|
|
300
|
+
document.querySelectorAll('.frame-wrap').forEach(wrap => {
|
|
301
|
+
const iframe = wrap.querySelector('iframe')
|
|
302
|
+
const w = wrap.offsetWidth
|
|
303
|
+
if (iframe && w > 0) iframe.style.transform = 'scale(' + (w / ${frameW}) + ')'
|
|
304
|
+
})
|
|
305
|
+
}
|
|
306
|
+
window.addEventListener('resize', scaleFrames)
|
|
307
|
+
window.addEventListener('load', scaleFrames)
|
|
308
|
+
requestAnimationFrame(scaleFrames)
|
|
309
|
+
</script>
|
|
310
|
+
</body>
|
|
311
|
+
</html>`
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// ─── Main command ──────────────────────────────────────────────────────────────
|
|
315
|
+
|
|
316
|
+
export async function autogenCommand(projectDesc, options) {
|
|
317
|
+
const provider = options.provider || 'anthropic'
|
|
318
|
+
const platform = options.platform || 'mobile'
|
|
319
|
+
const outputDir = resolve(options.output || `output/${projectDesc.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')}`)
|
|
320
|
+
const imageFolder = options.images || null
|
|
321
|
+
const screens = options.screens
|
|
322
|
+
? options.screens.split(',').map(s => s.trim()).filter(Boolean)
|
|
323
|
+
: []
|
|
324
|
+
|
|
325
|
+
// Validate provider
|
|
326
|
+
if (!Object.keys(PROVIDERS).includes(provider)) {
|
|
327
|
+
console.error(`Error: Unknown provider "${provider}". Available: ${Object.keys(PROVIDERS).join(', ')}`)
|
|
328
|
+
process.exit(1)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
console.error(`\nDesignKit AutoGen`)
|
|
332
|
+
console.error(`─────────────────────────────────────`)
|
|
333
|
+
console.error(`Project: ${projectDesc}`)
|
|
334
|
+
console.error(`Platform: ${platform}`)
|
|
335
|
+
console.error(`Provider: ${PROVIDERS[provider].label}`)
|
|
336
|
+
console.error(`Output: ${outputDir}`)
|
|
337
|
+
if (screens.length) console.error(`Screens: ${screens.join(', ')}`)
|
|
338
|
+
if (imageFolder) console.error(`Images: ${imageFolder}`)
|
|
339
|
+
console.error(`─────────────────────────────────────\n`)
|
|
340
|
+
|
|
341
|
+
// Ensure output dir exists
|
|
342
|
+
if (!existsSync(outputDir)) mkdirSync(outputDir, { recursive: true })
|
|
343
|
+
|
|
344
|
+
// ── Phase 1: Design Spec ────────────────────────────────────────────────────
|
|
345
|
+
const specPrompt = buildSpecPrompt(projectDesc, platform, screens, imageFolder)
|
|
346
|
+
let spec
|
|
347
|
+
|
|
348
|
+
try {
|
|
349
|
+
spec = await generateSpec(specPrompt, options)
|
|
350
|
+
} catch (err) {
|
|
351
|
+
console.error(`\nError in Phase 1 (spec generation):\n${err.message}`)
|
|
352
|
+
process.exit(1)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Inject imageFolder into spec if provided
|
|
356
|
+
if (imageFolder) spec.imageFolder = imageFolder
|
|
357
|
+
|
|
358
|
+
// Save spec
|
|
359
|
+
const specPath = join(outputDir, 'design-spec.json')
|
|
360
|
+
writeFileSync(specPath, JSON.stringify(spec, null, 2), 'utf8')
|
|
361
|
+
console.error(`✓ design-spec.json (${spec.screens?.length || 0} screens, primary: ${spec.colors?.primary || 'N/A'})`)
|
|
362
|
+
|
|
363
|
+
if (!spec.screens?.length) {
|
|
364
|
+
console.error('Error: Spec has no screens. Aborting.')
|
|
365
|
+
process.exit(1)
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// ── Phase 2: Generate HTML per screen ──────────────────────────────────────
|
|
369
|
+
const generated = []
|
|
370
|
+
let failed = 0
|
|
371
|
+
|
|
372
|
+
for (let i = 0; i < spec.screens.length; i++) {
|
|
373
|
+
const screen = spec.screens[i]
|
|
374
|
+
const screenFile = screen.file || `${screen.id || `screen-${i + 1}`}.html`
|
|
375
|
+
const screenPath = join(outputDir, screenFile)
|
|
376
|
+
|
|
377
|
+
process.stderr.write(`[Phase 2 — ${i + 1}/${spec.screens.length}] ${screen.name}... `)
|
|
378
|
+
|
|
379
|
+
try {
|
|
380
|
+
const html = await generateScreen(spec, screen, options)
|
|
381
|
+
writeFileSync(screenPath, html, 'utf8')
|
|
382
|
+
console.error(`✓ ${screenFile}`)
|
|
383
|
+
generated.push({ ...screen, file: screenFile })
|
|
384
|
+
} catch (err) {
|
|
385
|
+
console.error(`✗ FAILED: ${err.message}`)
|
|
386
|
+
failed++
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// ── Index gallery ────────────────────────────────────────────────────────────
|
|
391
|
+
// Update spec with actually-generated screens
|
|
392
|
+
const finalSpec = { ...spec, screens: generated }
|
|
393
|
+
const indexHtml = generateIndexHtml(finalSpec)
|
|
394
|
+
const indexPath = join(outputDir, 'index.html')
|
|
395
|
+
writeFileSync(indexPath, indexHtml, 'utf8')
|
|
396
|
+
console.error(`✓ index.html (preview gallery)`)
|
|
397
|
+
|
|
398
|
+
// ── Summary ──────────────────────────────────────────────────────────────────
|
|
399
|
+
console.error(`\n─────────────────────────────────────`)
|
|
400
|
+
console.error(`Done! ${generated.length} screens generated${failed ? `, ${failed} failed` : ''}.`)
|
|
401
|
+
console.error(`Output: ${outputDir}`)
|
|
402
|
+
console.error(`\nOpen in browser:`)
|
|
403
|
+
console.error(` open ${join(outputDir, 'index.html')}`)
|
|
404
|
+
console.error(`─────────────────────────────────────\n`)
|
|
405
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { readConfig, setConfigValue } from '../lib/config.js'
|
|
2
|
+
|
|
3
|
+
export function configCommand(action, args, options) {
|
|
4
|
+
// designkit config list (or just: designkit config)
|
|
5
|
+
if (!action || action === 'list') {
|
|
6
|
+
const config = readConfig()
|
|
7
|
+
console.log('\nDesignKit config (~/.designkit/config.json)\n')
|
|
8
|
+
console.log(` provider ${config.provider}`)
|
|
9
|
+
console.log(` platform ${config.platform}`)
|
|
10
|
+
console.log()
|
|
11
|
+
return
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// designkit config get <key>
|
|
15
|
+
if (action === 'get') {
|
|
16
|
+
const key = args[0]
|
|
17
|
+
if (!key) {
|
|
18
|
+
console.error('Usage: designkit config get <key>')
|
|
19
|
+
process.exit(1)
|
|
20
|
+
}
|
|
21
|
+
const config = readConfig()
|
|
22
|
+
if (!(key in config)) {
|
|
23
|
+
console.error(`Unknown key: "${key}"`)
|
|
24
|
+
process.exit(1)
|
|
25
|
+
}
|
|
26
|
+
console.log(config[key])
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// designkit config set <key> <value>
|
|
31
|
+
if (action === 'set') {
|
|
32
|
+
const key = args[0]
|
|
33
|
+
const value = args[1]
|
|
34
|
+
if (!key || !value) {
|
|
35
|
+
console.error('Usage: designkit config set <key> <value>')
|
|
36
|
+
console.error(' Keys: provider, platform')
|
|
37
|
+
console.error(' Examples:')
|
|
38
|
+
console.error(' designkit config set provider gemini')
|
|
39
|
+
console.error(' designkit config set provider anthropic')
|
|
40
|
+
console.error(' designkit config set platform web')
|
|
41
|
+
process.exit(1)
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
setConfigValue(key, value)
|
|
45
|
+
console.log(`✓ Set ${key} = ${value}`)
|
|
46
|
+
} catch (err) {
|
|
47
|
+
console.error(`Error: ${err.message}`)
|
|
48
|
+
process.exit(1)
|
|
49
|
+
}
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.error(`Unknown subcommand: "${action}". Use: list, get, set`)
|
|
54
|
+
process.exit(1)
|
|
55
|
+
}
|
package/src/commands/imagine.js
CHANGED
|
@@ -30,7 +30,7 @@ const GEMINI_MODELS = [
|
|
|
30
30
|
{ model: 'imagen-4.0-ultra-generate-001', type: 'imagen', note: 'Imagen 4 Ultra — best quality' },
|
|
31
31
|
]
|
|
32
32
|
|
|
33
|
-
export async function imagineCommand(prompt, options) {
|
|
33
|
+
export async function imagineCommand(prompt = '', options) {
|
|
34
34
|
const provider = options.provider || 'gemini'
|
|
35
35
|
const validProviders = Object.keys(PROVIDERS)
|
|
36
36
|
|
|
@@ -50,6 +50,13 @@ export async function imagineCommand(prompt, options) {
|
|
|
50
50
|
return
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
if (!prompt) {
|
|
54
|
+
console.error('Error: prompt is required')
|
|
55
|
+
console.error('Usage: designkit imagine "your prompt" [options]')
|
|
56
|
+
console.error(' designkit imagine --list-models')
|
|
57
|
+
process.exit(1)
|
|
58
|
+
}
|
|
59
|
+
|
|
53
60
|
if (!validProviders.includes(provider)) {
|
|
54
61
|
console.error(`Error: Unknown provider "${provider}". Available: ${validProviders.join(', ')}`)
|
|
55
62
|
process.exit(1)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs'
|
|
2
|
+
import { join } from 'path'
|
|
3
|
+
import { homedir } from 'os'
|
|
4
|
+
|
|
5
|
+
const CONFIG_DIR = join(homedir(), '.designkit')
|
|
6
|
+
const CONFIG_PATH = join(CONFIG_DIR, 'config.json')
|
|
7
|
+
|
|
8
|
+
const DEFAULTS = {
|
|
9
|
+
provider: 'anthropic',
|
|
10
|
+
platform: 'mobile'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const VALID = {
|
|
14
|
+
provider: ['anthropic', 'gemini', 'openai'],
|
|
15
|
+
platform: ['mobile', 'web']
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function readConfig() {
|
|
19
|
+
if (!existsSync(CONFIG_PATH)) return { ...DEFAULTS }
|
|
20
|
+
try {
|
|
21
|
+
return { ...DEFAULTS, ...JSON.parse(readFileSync(CONFIG_PATH, 'utf8')) }
|
|
22
|
+
} catch {
|
|
23
|
+
return { ...DEFAULTS }
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function writeConfig(config) {
|
|
28
|
+
if (!existsSync(CONFIG_DIR)) mkdirSync(CONFIG_DIR, { recursive: true })
|
|
29
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), 'utf8')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function getConfigValue(key) {
|
|
33
|
+
return readConfig()[key]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function setConfigValue(key, value) {
|
|
37
|
+
if (!Object.keys(DEFAULTS).includes(key)) {
|
|
38
|
+
throw new Error(`Unknown config key: "${key}". Valid keys: ${Object.keys(DEFAULTS).join(', ')}`)
|
|
39
|
+
}
|
|
40
|
+
if (VALID[key] && !VALID[key].includes(value)) {
|
|
41
|
+
throw new Error(`Invalid value "${value}" for "${key}". Valid values: ${VALID[key].join(', ')}`)
|
|
42
|
+
}
|
|
43
|
+
const config = readConfig()
|
|
44
|
+
config[key] = value
|
|
45
|
+
writeConfig(config)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function getDefaultProvider() {
|
|
49
|
+
return getConfigValue('provider') || 'anthropic'
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function getDefaultPlatform() {
|
|
53
|
+
return getConfigValue('platform') || 'mobile'
|
|
54
|
+
}
|