designkit-ai 1.1.2 → 1.3.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 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
- ### Design only
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
  [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/pixeliro-sys/designkit-source-for-ai)
4
4
 
5
- > **502 ready-to-use HTML components** + **33 full-page design previews** for Web and Mobile.
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 + 33 full-page designs ready to open in any browser. No build step, no setup.
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/ 17 complete app designs (Finance, Fitness, Food, Social, )
124
- └── web/ 16 complete web designs (SaaS, Analytics, Blog, CRM, )
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,7 @@ 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'
11
12
  import { readFileSync } from 'fs'
12
13
  import { fileURLToPath } from 'url'
13
14
  import { dirname, join } from 'path'
@@ -70,8 +71,8 @@ program
70
71
  .action(projectCommand)
71
72
 
72
73
  program
73
- .command('imagine <prompt>')
74
- .description('Generate an image with AI (Gemini Imagen 3 or DALL-E 3)')
74
+ .command('imagine [prompt]')
75
+ .description('Generate an image with AI (Gemini or DALL-E 3)')
75
76
  .option('-p, --provider <provider>', 'AI provider: gemini, openai', 'gemini')
76
77
  .option('-o, --output <dir>', 'Output directory', '.')
77
78
  .option('-n, --name <name>', 'Output filename (without extension)', 'image')
@@ -83,4 +84,14 @@ program
83
84
  .option('--quality <quality>', 'DALL-E quality: standard, hd', 'standard')
84
85
  .action(imagineCommand)
85
86
 
87
+ program
88
+ .command('autogen <project>')
89
+ .description('Generate a full design project: color spec + HTML files for every screen')
90
+ .option('-p, --provider <provider>', 'AI provider: anthropic, gemini, openai', 'anthropic')
91
+ .option('--platform <platform>', 'Target platform: mobile, web, both', 'mobile')
92
+ .option('--screens <list>', 'Comma-separated screen/page names (auto-generated if omitted)')
93
+ .option('--images <folder>', 'Path to local image assets folder (uses placehold.jp if omitted)')
94
+ .option('-o, --output <dir>', 'Output directory (default: output/<project-slug>)')
95
+ .action(autogenCommand)
96
+
86
97
  program.parse()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "designkit-ai",
3
- "version": "1.1.2",
3
+ "version": "1.3.0",
4
4
  "description": "502 HTML UI components + AI skills for designing and building web and mobile apps",
5
5
  "keywords": [
6
6
  "design-system",
@@ -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,394 @@
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
+ transform: scale(calc(100% / ${frameW}));
251
+ }
252
+ .frame-overlay {
253
+ position: absolute;
254
+ inset: 0;
255
+ cursor: pointer;
256
+ }
257
+ .card-label {
258
+ padding: 12px 16px;
259
+ border-top: 1px solid #1E293B;
260
+ display: flex;
261
+ justify-content: space-between;
262
+ align-items: center;
263
+ }
264
+ .card-name {
265
+ font-size: 13px;
266
+ font-weight: 600;
267
+ color: #F1F5F9;
268
+ }
269
+ .card-file {
270
+ font-size: 11px;
271
+ color: #475569;
272
+ font-family: monospace;
273
+ }
274
+ footer {
275
+ max-width: 1200px;
276
+ margin: 48px auto 0;
277
+ padding-top: 24px;
278
+ border-top: 1px solid #1E293B;
279
+ font-size: 12px;
280
+ color: #334155;
281
+ }
282
+ </style>
283
+ </head>
284
+ <body>
285
+ <header>
286
+ <h1>${spec.project}</h1>
287
+ <p>${spec.mood || ''}</p>
288
+ <div class="meta">
289
+ <span class="badge"><span class="color-dot"></span> Primary: ${spec.colors?.primary || 'N/A'}</span>
290
+ <span class="badge">Platform: ${spec.platform}</span>
291
+ <span class="badge">${spec.screens.length} screens</span>
292
+ ${spec.typography?.fontFamily ? `<span class="badge">${spec.typography.fontFamily.split(',')[0]}</span>` : ''}
293
+ </div>
294
+ </header>
295
+ <div class="grid">
296
+ ${cards}
297
+ </div>
298
+ <footer>Generated by DesignKit AutoGen &middot; ${new Date().toLocaleDateString()}</footer>
299
+ </body>
300
+ </html>`
301
+ }
302
+
303
+ // ─── Main command ──────────────────────────────────────────────────────────────
304
+
305
+ export async function autogenCommand(projectDesc, options) {
306
+ const provider = options.provider || 'anthropic'
307
+ const platform = options.platform || 'mobile'
308
+ const outputDir = resolve(options.output || `output/${projectDesc.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')}`)
309
+ const imageFolder = options.images || null
310
+ const screens = options.screens
311
+ ? options.screens.split(',').map(s => s.trim()).filter(Boolean)
312
+ : []
313
+
314
+ // Validate provider
315
+ if (!Object.keys(PROVIDERS).includes(provider)) {
316
+ console.error(`Error: Unknown provider "${provider}". Available: ${Object.keys(PROVIDERS).join(', ')}`)
317
+ process.exit(1)
318
+ }
319
+
320
+ console.error(`\nDesignKit AutoGen`)
321
+ console.error(`─────────────────────────────────────`)
322
+ console.error(`Project: ${projectDesc}`)
323
+ console.error(`Platform: ${platform}`)
324
+ console.error(`Provider: ${PROVIDERS[provider].label}`)
325
+ console.error(`Output: ${outputDir}`)
326
+ if (screens.length) console.error(`Screens: ${screens.join(', ')}`)
327
+ if (imageFolder) console.error(`Images: ${imageFolder}`)
328
+ console.error(`─────────────────────────────────────\n`)
329
+
330
+ // Ensure output dir exists
331
+ if (!existsSync(outputDir)) mkdirSync(outputDir, { recursive: true })
332
+
333
+ // ── Phase 1: Design Spec ────────────────────────────────────────────────────
334
+ const specPrompt = buildSpecPrompt(projectDesc, platform, screens, imageFolder)
335
+ let spec
336
+
337
+ try {
338
+ spec = await generateSpec(specPrompt, options)
339
+ } catch (err) {
340
+ console.error(`\nError in Phase 1 (spec generation):\n${err.message}`)
341
+ process.exit(1)
342
+ }
343
+
344
+ // Inject imageFolder into spec if provided
345
+ if (imageFolder) spec.imageFolder = imageFolder
346
+
347
+ // Save spec
348
+ const specPath = join(outputDir, 'design-spec.json')
349
+ writeFileSync(specPath, JSON.stringify(spec, null, 2), 'utf8')
350
+ console.error(`✓ design-spec.json (${spec.screens?.length || 0} screens, primary: ${spec.colors?.primary || 'N/A'})`)
351
+
352
+ if (!spec.screens?.length) {
353
+ console.error('Error: Spec has no screens. Aborting.')
354
+ process.exit(1)
355
+ }
356
+
357
+ // ── Phase 2: Generate HTML per screen ──────────────────────────────────────
358
+ const generated = []
359
+ let failed = 0
360
+
361
+ for (let i = 0; i < spec.screens.length; i++) {
362
+ const screen = spec.screens[i]
363
+ const screenFile = screen.file || `${screen.id || `screen-${i + 1}`}.html`
364
+ const screenPath = join(outputDir, screenFile)
365
+
366
+ process.stderr.write(`[Phase 2 — ${i + 1}/${spec.screens.length}] ${screen.name}... `)
367
+
368
+ try {
369
+ const html = await generateScreen(spec, screen, options)
370
+ writeFileSync(screenPath, html, 'utf8')
371
+ console.error(`✓ ${screenFile}`)
372
+ generated.push({ ...screen, file: screenFile })
373
+ } catch (err) {
374
+ console.error(`✗ FAILED: ${err.message}`)
375
+ failed++
376
+ }
377
+ }
378
+
379
+ // ── Index gallery ────────────────────────────────────────────────────────────
380
+ // Update spec with actually-generated screens
381
+ const finalSpec = { ...spec, screens: generated }
382
+ const indexHtml = generateIndexHtml(finalSpec)
383
+ const indexPath = join(outputDir, 'index.html')
384
+ writeFileSync(indexPath, indexHtml, 'utf8')
385
+ console.error(`✓ index.html (preview gallery)`)
386
+
387
+ // ── Summary ──────────────────────────────────────────────────────────────────
388
+ console.error(`\n─────────────────────────────────────`)
389
+ console.error(`Done! ${generated.length} screens generated${failed ? `, ${failed} failed` : ''}.`)
390
+ console.error(`Output: ${outputDir}`)
391
+ console.error(`\nOpen in browser:`)
392
+ console.error(` open ${join(outputDir, 'index.html')}`)
393
+ console.error(`─────────────────────────────────────\n`)
394
+ }
@@ -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)