@su-record/vibe 2.6.3 → 2.6.5
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/README.md +41 -1
- package/agents/ui-syncer.md +143 -43
- package/dist/lib/gemini-api.js +5 -5
- package/hooks/scripts/gemini-ui-gen.js +293 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,7 +43,9 @@ vibe init
|
|
|
43
43
|
| `/vibe.review` | 13+ agent parallel review |
|
|
44
44
|
| `/vibe.analyze` | Code analysis |
|
|
45
45
|
| `/vibe.reason "problem"` | Systematic reasoning |
|
|
46
|
-
| `/vibe.utils` |
|
|
46
|
+
| `/vibe.utils --ui` | UI preview (Gemini image / ASCII fallback) |
|
|
47
|
+
| `/vibe.utils --ui-sync` | Sync design files to code (v2.6.3) |
|
|
48
|
+
| `/vibe.utils --e2e` | E2E testing, diagrams, etc. |
|
|
47
49
|
|
|
48
50
|
## Workflow
|
|
49
51
|
|
|
@@ -211,6 +213,44 @@ await generateChangelog('feature-name');
|
|
|
211
213
|
- Automatic changelog generation
|
|
212
214
|
- Baseline tagging for releases
|
|
213
215
|
|
|
216
|
+
## UI Design Tools (v2.6.3)
|
|
217
|
+
|
|
218
|
+
### UI Preview (`--ui`)
|
|
219
|
+
|
|
220
|
+
Generate UI previews from text description or design folder:
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
/vibe.utils --ui "login form with email/password"
|
|
224
|
+
/vibe.utils --ui ./design/dashboard/
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
**Features:**
|
|
228
|
+
|
|
229
|
+
- **Gemini enabled**: Generates actual UI mockup image
|
|
230
|
+
- **Gemini disabled**: ASCII art fallback
|
|
231
|
+
- Supports: HTML, PNG, JPG, CSS, JSON, SVG, MD
|
|
232
|
+
|
|
233
|
+
### Design-to-Code Sync (`--ui-sync`)
|
|
234
|
+
|
|
235
|
+
Sync design files to existing UI code:
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
/vibe.utils --ui-sync ./design/ui/
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**Workflow:**
|
|
242
|
+
|
|
243
|
+
1. Read all design files (HTML, images, CSS, tokens)
|
|
244
|
+
2. Extract design specs (colors, typography, spacing)
|
|
245
|
+
3. Compare with existing code
|
|
246
|
+
4. Generate update plan
|
|
247
|
+
5. Apply changes (with confirmation)
|
|
248
|
+
|
|
249
|
+
**Mode:**
|
|
250
|
+
|
|
251
|
+
- **Gemini enabled**: Gemini generates code from design
|
|
252
|
+
- **Gemini disabled**: Claude handles analysis and generation
|
|
253
|
+
|
|
214
254
|
## Multi-model Orchestration
|
|
215
255
|
|
|
216
256
|
**Hook-based automatic routing** - Keywords in your prompt trigger the right LLM automatically:
|
package/agents/ui-syncer.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Sync UI code with design files
|
|
2
|
+
description: Sync UI code with design files (Gemini or Claude)
|
|
3
3
|
argument-hint: "design folder path"
|
|
4
4
|
---
|
|
5
5
|
|
|
@@ -7,6 +7,9 @@ argument-hint: "design folder path"
|
|
|
7
7
|
|
|
8
8
|
Analyze design files and update existing UI code to match.
|
|
9
9
|
|
|
10
|
+
- **Gemini enabled**: Gemini analyzes designs and generates code
|
|
11
|
+
- **Gemini disabled**: Claude handles directly
|
|
12
|
+
|
|
10
13
|
## Usage
|
|
11
14
|
|
|
12
15
|
```
|
|
@@ -17,11 +20,88 @@ Analyze design files and update existing UI code to match.
|
|
|
17
20
|
|
|
18
21
|
## Process
|
|
19
22
|
|
|
20
|
-
###
|
|
23
|
+
### 0. Check Gemini Status
|
|
21
24
|
|
|
22
|
-
**
|
|
25
|
+
**FIRST: Check if Gemini is available.**
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
vibe gemini status
|
|
29
|
+
```
|
|
23
30
|
|
|
24
|
-
|
|
31
|
+
Or check: `~/.config/vibe/gemini.json` or `~/.config/vibe/gemini-apikey.json`
|
|
32
|
+
|
|
33
|
+
- **Gemini available** → Use Gemini for code generation (Step 1A)
|
|
34
|
+
- **Gemini NOT available** → Claude handles directly (Step 1B)
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Path A: Gemini Enabled
|
|
39
|
+
|
|
40
|
+
### 1A. Collect ALL Design Files
|
|
41
|
+
|
|
42
|
+
Scan folder and collect all files to send to Gemini:
|
|
43
|
+
|
|
44
|
+
| Format | What to Collect |
|
|
45
|
+
| ------ | --------------- |
|
|
46
|
+
| `*.html` | Full HTML content |
|
|
47
|
+
| `*.png` / `*.jpg` / `*.webp` | Image as base64 |
|
|
48
|
+
| `*.json` | Design tokens, theme config |
|
|
49
|
+
| `*.css` / `*.scss` | Style variables |
|
|
50
|
+
| `*.md` | Design guidelines |
|
|
51
|
+
| `*.svg` | Vector content |
|
|
52
|
+
|
|
53
|
+
### 2A. Send to Gemini
|
|
54
|
+
|
|
55
|
+
Use the Gemini UI Generator script:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
node hooks/scripts/gemini-ui-gen.js \
|
|
59
|
+
--design-folder ./design/ui/ \
|
|
60
|
+
--framework react \
|
|
61
|
+
--output ./src/components
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Or call Gemini API directly with all files:**
|
|
65
|
+
|
|
66
|
+
```javascript
|
|
67
|
+
// Prepare multipart content
|
|
68
|
+
const parts = [
|
|
69
|
+
// Images as inline data
|
|
70
|
+
{ inlineData: { mimeType: "image/png", data: imageBase64 } },
|
|
71
|
+
// HTML/CSS/JSON as text
|
|
72
|
+
{ text: `HTML Mockup:\n${htmlContent}` },
|
|
73
|
+
{ text: `CSS Styles:\n${cssContent}` },
|
|
74
|
+
{ text: `Design Tokens:\n${jsonContent}` },
|
|
75
|
+
// Prompt
|
|
76
|
+
{ text: `
|
|
77
|
+
Analyze these design files and generate production-ready React components.
|
|
78
|
+
|
|
79
|
+
Requirements:
|
|
80
|
+
1. Match the visual design exactly
|
|
81
|
+
2. Use Tailwind CSS
|
|
82
|
+
3. TypeScript with proper types
|
|
83
|
+
4. Responsive and accessible
|
|
84
|
+
|
|
85
|
+
Output complete component code.
|
|
86
|
+
` }
|
|
87
|
+
];
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 3A. Apply Gemini's Output
|
|
91
|
+
|
|
92
|
+
1. Parse Gemini's code output
|
|
93
|
+
2. Extract component files
|
|
94
|
+
3. Compare with existing code
|
|
95
|
+
4. Show diff to user
|
|
96
|
+
5. Apply with confirmation
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Path B: Gemini NOT Available (Claude Fallback)
|
|
101
|
+
|
|
102
|
+
### 1B. Read ALL Design Files
|
|
103
|
+
|
|
104
|
+
**MANDATORY: Read every file in the design folder.**
|
|
25
105
|
|
|
26
106
|
| Format | What to Extract |
|
|
27
107
|
| ------ | --------------- |
|
|
@@ -42,7 +122,7 @@ Scan folder and read ALL supported files:
|
|
|
42
122
|
|
|
43
123
|
**CRITICAL:** Do NOT skip any file. Read images with the Read tool.
|
|
44
124
|
|
|
45
|
-
###
|
|
125
|
+
### 2B. Analyze Design Intent (Claude)
|
|
46
126
|
|
|
47
127
|
From all design files, extract:
|
|
48
128
|
|
|
@@ -63,12 +143,15 @@ From all design files, extract:
|
|
|
63
143
|
- Border radius, shadows
|
|
64
144
|
- Dark/light theme support
|
|
65
145
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
146
|
+
### 3B. Generate Code (Claude)
|
|
147
|
+
|
|
148
|
+
Claude generates the code directly based on analysis.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Common Steps (Both Paths)
|
|
70
153
|
|
|
71
|
-
###
|
|
154
|
+
### 4. Find Existing UI Code
|
|
72
155
|
|
|
73
156
|
Search for existing UI implementation:
|
|
74
157
|
|
|
@@ -87,7 +170,7 @@ Detect framework:
|
|
|
87
170
|
- Svelte: `*.svelte`
|
|
88
171
|
- Angular: `*.component.ts`
|
|
89
172
|
|
|
90
|
-
###
|
|
173
|
+
### 5. Compare Design vs Code
|
|
91
174
|
|
|
92
175
|
Create a diff report:
|
|
93
176
|
|
|
@@ -98,7 +181,7 @@ Create a diff report:
|
|
|
98
181
|
| Border radius | 12px | 8px | Update |
|
|
99
182
|
| Component X | Exists | Missing | Create |
|
|
100
183
|
|
|
101
|
-
###
|
|
184
|
+
### 6. Generate Update Plan
|
|
102
185
|
|
|
103
186
|
List all changes needed:
|
|
104
187
|
|
|
@@ -125,7 +208,7 @@ List all changes needed:
|
|
|
125
208
|
**New Components:**
|
|
126
209
|
- List components that exist in design but not in code
|
|
127
210
|
|
|
128
|
-
###
|
|
211
|
+
### 7. Execute Updates
|
|
129
212
|
|
|
130
213
|
**Ask user before making changes:**
|
|
131
214
|
|
|
@@ -147,7 +230,7 @@ Proceed with updates? [Y/n]
|
|
|
147
230
|
4. Create new components (if needed)
|
|
148
231
|
5. Update imports
|
|
149
232
|
|
|
150
|
-
###
|
|
233
|
+
### 8. Verify Changes
|
|
151
234
|
|
|
152
235
|
After updates:
|
|
153
236
|
|
|
@@ -159,6 +242,7 @@ After updates:
|
|
|
159
242
|
|
|
160
243
|
```
|
|
161
244
|
📂 Design Analysis: ./design/ui/
|
|
245
|
+
🤖 Mode: Gemini (or Claude fallback)
|
|
162
246
|
|
|
163
247
|
Files Read:
|
|
164
248
|
✓ homepage.html (structure)
|
|
@@ -190,34 +274,33 @@ Apply changes? [Y/n]
|
|
|
190
274
|
Next: Run `npm run build` to verify
|
|
191
275
|
```
|
|
192
276
|
|
|
193
|
-
## Example
|
|
277
|
+
## Example (with Gemini)
|
|
194
278
|
|
|
195
279
|
```
|
|
196
280
|
User: /vibe.utils --ui-sync ./design/dashboard/
|
|
197
281
|
|
|
198
|
-
Claude:
|
|
282
|
+
Claude: Checking Gemini status...
|
|
283
|
+
✅ Gemini: OAuth authenticated (user@example.com)
|
|
199
284
|
|
|
200
|
-
📂
|
|
285
|
+
📂 Collecting design files...
|
|
201
286
|
- dashboard.html
|
|
202
287
|
- dashboard-dark.png
|
|
203
288
|
- dashboard-light.png
|
|
204
289
|
- tokens.json
|
|
205
290
|
- components.md
|
|
206
291
|
|
|
207
|
-
|
|
208
|
-
[Extracts: header, sidebar, 3-column grid, card components]
|
|
292
|
+
🤖 Sending to Gemini for analysis...
|
|
209
293
|
|
|
210
|
-
|
|
211
|
-
|
|
294
|
+
Gemini Response:
|
|
295
|
+
Layout: Header + Sidebar + 3-column main
|
|
296
|
+
Theme: Dark (#0F172A bg, #F8FAFC text)
|
|
297
|
+
Components: Header, Sidebar, StatCard, DataTable
|
|
212
298
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
Layout: Header + Sidebar + 3-column main
|
|
219
|
-
Theme: Dark (#0F172A bg, #F8FAFC text)
|
|
220
|
-
Components: Header, Sidebar, StatCard, DataTable
|
|
299
|
+
Generated code:
|
|
300
|
+
- Header.tsx (64 lines)
|
|
301
|
+
- Sidebar.tsx (45 lines)
|
|
302
|
+
- StatCard.tsx (32 lines)
|
|
303
|
+
- DataTable.tsx (78 lines)
|
|
221
304
|
|
|
222
305
|
🔍 Comparing with existing code...
|
|
223
306
|
|
|
@@ -228,31 +311,48 @@ Missing: src/components/DataTable.tsx
|
|
|
228
311
|
Differences:
|
|
229
312
|
1. StatCard border-radius: design=16px, code=8px
|
|
230
313
|
2. Sidebar width: design=240px, code=200px
|
|
231
|
-
3.
|
|
232
|
-
4.
|
|
233
|
-
|
|
314
|
+
3. Primary color: design=#6366F1, code=#3B82F6
|
|
315
|
+
4. DataTable component: missing
|
|
316
|
+
|
|
317
|
+
Apply 3 updates + create 1 component? [Y/n]
|
|
318
|
+
```
|
|
234
319
|
|
|
235
|
-
|
|
320
|
+
## Example (Claude Fallback)
|
|
236
321
|
|
|
237
|
-
|
|
322
|
+
```
|
|
323
|
+
User: /vibe.utils --ui-sync ./design/dashboard/
|
|
238
324
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
- src/components/Sidebar.tsx (width)
|
|
242
|
-
- src/components/Header.tsx (height)
|
|
243
|
-
- tailwind.config.js (primary color)
|
|
244
|
-
- src/components/DataTable.tsx (created)
|
|
325
|
+
Claude: Checking Gemini status...
|
|
326
|
+
⚠️ Gemini not configured. Using Claude fallback.
|
|
245
327
|
|
|
246
|
-
|
|
328
|
+
📂 Reading design files...
|
|
329
|
+
|
|
330
|
+
Reading dashboard.html...
|
|
331
|
+
[Extracts: header, sidebar, 3-column grid]
|
|
332
|
+
|
|
333
|
+
Reading dashboard-dark.png...
|
|
334
|
+
[Analyzes: dark theme colors, spacing]
|
|
335
|
+
|
|
336
|
+
Reading tokens.json...
|
|
337
|
+
[Extracts: color palette, spacing scale]
|
|
338
|
+
|
|
339
|
+
📊 Design Analysis Complete (Claude):
|
|
340
|
+
|
|
341
|
+
Layout: Header + Sidebar + 3-column main
|
|
342
|
+
Theme: Dark (#0F172A bg, #F8FAFC text)
|
|
343
|
+
|
|
344
|
+
🔍 Comparing with existing code...
|
|
345
|
+
...
|
|
247
346
|
```
|
|
248
347
|
|
|
249
348
|
## Notes
|
|
250
349
|
|
|
350
|
+
- Always check Gemini status first
|
|
351
|
+
- Gemini handles image analysis better than Claude for visual matching
|
|
352
|
+
- Claude fallback works but may be less accurate for complex visuals
|
|
251
353
|
- Always read ALL files including images
|
|
252
354
|
- Ask for confirmation before making changes
|
|
253
355
|
- Preserve existing functionality while updating styles
|
|
254
|
-
- Create backup recommendation for large changes
|
|
255
|
-
- Support both CSS-in-JS and traditional CSS
|
|
256
356
|
|
|
257
357
|
---
|
|
258
358
|
|
package/dist/lib/gemini-api.js
CHANGED
|
@@ -433,35 +433,35 @@ export async function vibeGeminiOrchestrate(prompt, systemPrompt, options = {})
|
|
|
433
433
|
* Vibe Spec 파싱 (Vibe Spec → 실행 계획)
|
|
434
434
|
*/
|
|
435
435
|
export async function vibeGeminiParseSpec(spec) {
|
|
436
|
-
return vibeGeminiOrchestrate(spec, `You are a Vibe Spec parser. Parse the given specification and output a structured execution plan.
|
|
436
|
+
return vibeGeminiOrchestrate(spec, `You are a Vibe Spec parser. Parse the given specification and output a structured execution plan.
|
|
437
437
|
Output format: { "phases": [...], "files": [...], "dependencies": [...] }`);
|
|
438
438
|
}
|
|
439
439
|
/**
|
|
440
440
|
* Vibe 실행 계획 수립 (Task → Steps)
|
|
441
441
|
*/
|
|
442
442
|
export async function vibeGeminiPlanExecution(task, context) {
|
|
443
|
-
return vibeGeminiOrchestrate(`Task: ${task}\n\nContext:\n${context}`, `You are a Vibe execution planner. Given a task and context, create a step-by-step execution plan.
|
|
443
|
+
return vibeGeminiOrchestrate(`Task: ${task}\n\nContext:\n${context}`, `You are a Vibe execution planner. Given a task and context, create a step-by-step execution plan.
|
|
444
444
|
Output format: { "steps": [{ "id": 1, "action": "...", "target": "...", "expected": "..." }], "estimatedComplexity": "low|medium|high" }`);
|
|
445
445
|
}
|
|
446
446
|
/**
|
|
447
447
|
* Vibe 코드 분석 (빠른 구조 분석)
|
|
448
448
|
*/
|
|
449
449
|
export async function vibeGeminiAnalyze(code, question) {
|
|
450
|
-
return vibeGeminiOrchestrate(`Code:\n\`\`\`\n${code}\n\`\`\`\n\nQuestion: ${question}`, `You are a code analyzer. Answer the question about the given code concisely.
|
|
450
|
+
return vibeGeminiOrchestrate(`Code:\n\`\`\`\n${code}\n\`\`\`\n\nQuestion: ${question}`, `You are a code analyzer. Answer the question about the given code concisely.
|
|
451
451
|
Output format: { "answer": "...", "confidence": 0.0-1.0, "relatedSymbols": [...] }`);
|
|
452
452
|
}
|
|
453
453
|
/**
|
|
454
454
|
* Vibe 다음 액션 결정 (상태 기반)
|
|
455
455
|
*/
|
|
456
456
|
export async function vibeGeminiDecideNextAction(currentState, availableActions, goal) {
|
|
457
|
-
return vibeGeminiOrchestrate(`Current State:\n${currentState}\n\nAvailable Actions:\n${availableActions.join('\n')}\n\nGoal: ${goal}`, `You are an action decider. Based on the current state and goal, select the best next action.
|
|
457
|
+
return vibeGeminiOrchestrate(`Current State:\n${currentState}\n\nAvailable Actions:\n${availableActions.join('\n')}\n\nGoal: ${goal}`, `You are an action decider. Based on the current state and goal, select the best next action.
|
|
458
458
|
Output format: { "selectedAction": "...", "reason": "...", "parameters": {} }`);
|
|
459
459
|
}
|
|
460
460
|
/**
|
|
461
461
|
* Vibe UI/UX 분석 (검색 없이 내부 지식으로)
|
|
462
462
|
*/
|
|
463
463
|
export async function vibeGeminiAnalyzeUX(description) {
|
|
464
|
-
return vibeGeminiOrchestrate(description, `You are a UI/UX expert. Analyze the given design description and provide structured feedback.
|
|
464
|
+
return vibeGeminiOrchestrate(description, `You are a UI/UX expert. Analyze the given design description and provide structured feedback.
|
|
465
465
|
Output format: { "issues": [...], "suggestions": [...], "accessibility": { "score": 0-100, "concerns": [...] } }`, { jsonMode: true });
|
|
466
466
|
}
|
|
467
467
|
//# sourceMappingURL=gemini-api.js.map
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Gemini UI Code Generator
|
|
5
|
+
*
|
|
6
|
+
* 디자인 파일(이미지, HTML 등)을 분석해서 UI 코드를 생성합니다.
|
|
7
|
+
* 기존 gemini-api 인프라 사용.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node gemini-ui-gen.js --image ./design.png --framework react --output ./src/components
|
|
11
|
+
* node gemini-ui-gen.js --html ./mockup.html --framework vue --output ./src/components
|
|
12
|
+
* node gemini-ui-gen.js --design-folder ./design/ --framework react --output ./src
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import fs from 'fs';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
import { getLibBaseUrl } from './utils.js';
|
|
18
|
+
|
|
19
|
+
const LIB_URL = getLibBaseUrl();
|
|
20
|
+
|
|
21
|
+
// ============================================
|
|
22
|
+
// Gemini API (기존 인프라 사용)
|
|
23
|
+
// ============================================
|
|
24
|
+
|
|
25
|
+
let geminiApi = null;
|
|
26
|
+
|
|
27
|
+
async function getGeminiApi() {
|
|
28
|
+
if (!geminiApi) {
|
|
29
|
+
geminiApi = await import(`${LIB_URL}gemini-api.js`);
|
|
30
|
+
}
|
|
31
|
+
return geminiApi;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function getGeminiStatus() {
|
|
35
|
+
const api = await getGeminiApi();
|
|
36
|
+
return api.vibeGeminiStatus ? await api.vibeGeminiStatus() : null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function askGemini(prompt) {
|
|
40
|
+
const api = await getGeminiApi();
|
|
41
|
+
return api.ask(prompt, { model: 'gemini-3-flash', maxTokens: 8192, temperature: 0.3 });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ============================================
|
|
45
|
+
// UI Code Generation
|
|
46
|
+
// ============================================
|
|
47
|
+
|
|
48
|
+
function getFrameworkPrompt(framework) {
|
|
49
|
+
const prompts = {
|
|
50
|
+
react: `Generate React TypeScript components using:
|
|
51
|
+
- Functional components with hooks
|
|
52
|
+
- Tailwind CSS for styling
|
|
53
|
+
- Proper TypeScript types/interfaces
|
|
54
|
+
- Export as default`,
|
|
55
|
+
|
|
56
|
+
vue: `Generate Vue 3 components using:
|
|
57
|
+
- Composition API with <script setup>
|
|
58
|
+
- Tailwind CSS for styling
|
|
59
|
+
- TypeScript support
|
|
60
|
+
- Single File Component format`,
|
|
61
|
+
|
|
62
|
+
svelte: `Generate Svelte components using:
|
|
63
|
+
- Svelte 5 runes syntax
|
|
64
|
+
- Tailwind CSS for styling
|
|
65
|
+
- TypeScript support`,
|
|
66
|
+
|
|
67
|
+
html: `Generate semantic HTML5 with:
|
|
68
|
+
- Tailwind CSS classes
|
|
69
|
+
- Accessible markup
|
|
70
|
+
- Responsive design`,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
return prompts[framework] || prompts.react;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function generateUIFromImage(imagePath, framework) {
|
|
77
|
+
const imageBuffer = fs.readFileSync(imagePath);
|
|
78
|
+
const imageBase64 = imageBuffer.toString('base64');
|
|
79
|
+
|
|
80
|
+
const ext = path.extname(imagePath).toLowerCase();
|
|
81
|
+
const mimeTypes = {
|
|
82
|
+
'.png': 'image/png',
|
|
83
|
+
'.jpg': 'image/jpeg',
|
|
84
|
+
'.jpeg': 'image/jpeg',
|
|
85
|
+
'.webp': 'image/webp',
|
|
86
|
+
'.gif': 'image/gif',
|
|
87
|
+
};
|
|
88
|
+
const mimeType = mimeTypes[ext] || 'image/png';
|
|
89
|
+
|
|
90
|
+
const prompt = `[Image attached as base64: ${mimeType}]
|
|
91
|
+
data:${mimeType};base64,${imageBase64}
|
|
92
|
+
|
|
93
|
+
Analyze this UI design image and generate production-ready code.
|
|
94
|
+
|
|
95
|
+
${getFrameworkPrompt(framework)}
|
|
96
|
+
|
|
97
|
+
Requirements:
|
|
98
|
+
1. Match the visual design exactly (colors, spacing, typography, layout)
|
|
99
|
+
2. Extract exact colors as hex values
|
|
100
|
+
3. Use proper semantic HTML structure
|
|
101
|
+
4. Make it responsive (mobile-first)
|
|
102
|
+
5. Include hover/focus states where appropriate
|
|
103
|
+
6. Add appropriate accessibility attributes
|
|
104
|
+
|
|
105
|
+
Output format:
|
|
106
|
+
\`\`\`${framework === 'html' ? 'html' : 'tsx'}
|
|
107
|
+
// Component code here
|
|
108
|
+
\`\`\`
|
|
109
|
+
|
|
110
|
+
Also provide a summary of:
|
|
111
|
+
- Colors extracted
|
|
112
|
+
- Components identified
|
|
113
|
+
- Layout structure`;
|
|
114
|
+
|
|
115
|
+
return askGemini(prompt);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function generateUIFromHTML(htmlPath, framework) {
|
|
119
|
+
const htmlContent = fs.readFileSync(htmlPath, 'utf-8');
|
|
120
|
+
|
|
121
|
+
const prompt = `Convert this HTML mockup to production-ready ${framework} code.
|
|
122
|
+
|
|
123
|
+
HTML Mockup:
|
|
124
|
+
\`\`\`html
|
|
125
|
+
${htmlContent}
|
|
126
|
+
\`\`\`
|
|
127
|
+
|
|
128
|
+
${getFrameworkPrompt(framework)}
|
|
129
|
+
|
|
130
|
+
Requirements:
|
|
131
|
+
1. Preserve the exact visual appearance
|
|
132
|
+
2. Extract inline styles to Tailwind classes
|
|
133
|
+
3. Create reusable components where appropriate
|
|
134
|
+
4. Add proper TypeScript types
|
|
135
|
+
5. Make it responsive
|
|
136
|
+
|
|
137
|
+
Output the converted code in proper format.`;
|
|
138
|
+
|
|
139
|
+
return askGemini(prompt);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function analyzeDesignFolder(folderPath, framework) {
|
|
143
|
+
const files = fs.readdirSync(folderPath);
|
|
144
|
+
let combinedPrompt = `Analyze the following design files and generate production-ready ${framework} code.\n\n`;
|
|
145
|
+
|
|
146
|
+
for (const file of files) {
|
|
147
|
+
const filePath = path.join(folderPath, file);
|
|
148
|
+
const ext = path.extname(file).toLowerCase();
|
|
149
|
+
|
|
150
|
+
if (['.png', '.jpg', '.jpeg', '.webp', '.gif'].includes(ext)) {
|
|
151
|
+
const imageBuffer = fs.readFileSync(filePath);
|
|
152
|
+
const imageBase64 = imageBuffer.toString('base64');
|
|
153
|
+
const mimeType = ext === '.png' ? 'image/png' : ext === '.webp' ? 'image/webp' : 'image/jpeg';
|
|
154
|
+
combinedPrompt += `\n--- Image: ${file} ---\ndata:${mimeType};base64,${imageBase64}\n`;
|
|
155
|
+
console.log(`📷 ${file}`);
|
|
156
|
+
} else if (ext === '.html') {
|
|
157
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
158
|
+
combinedPrompt += `\n--- HTML: ${file} ---\n${content}\n`;
|
|
159
|
+
console.log(`📄 ${file}`);
|
|
160
|
+
} else if (ext === '.json') {
|
|
161
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
162
|
+
combinedPrompt += `\n--- Design Tokens: ${file} ---\n${content}\n`;
|
|
163
|
+
console.log(`📋 ${file}`);
|
|
164
|
+
} else if (['.css', '.scss'].includes(ext)) {
|
|
165
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
166
|
+
combinedPrompt += `\n--- Styles: ${file} ---\n${content}\n`;
|
|
167
|
+
console.log(`🎨 ${file}`);
|
|
168
|
+
} else if (ext === '.md') {
|
|
169
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
170
|
+
combinedPrompt += `\n--- Guide: ${file} ---\n${content}\n`;
|
|
171
|
+
console.log(`📝 ${file}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
combinedPrompt += `\n${getFrameworkPrompt(framework)}
|
|
176
|
+
|
|
177
|
+
Requirements:
|
|
178
|
+
1. Match the visual design exactly
|
|
179
|
+
2. Extract design tokens from JSON if provided
|
|
180
|
+
3. Use CSS variables from stylesheets if provided
|
|
181
|
+
4. Create separate component files for each major UI section
|
|
182
|
+
5. Make it responsive (mobile-first)
|
|
183
|
+
6. Include accessibility attributes
|
|
184
|
+
|
|
185
|
+
Output complete component code.`;
|
|
186
|
+
|
|
187
|
+
return askGemini(combinedPrompt);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ============================================
|
|
191
|
+
// CLI
|
|
192
|
+
// ============================================
|
|
193
|
+
|
|
194
|
+
async function main() {
|
|
195
|
+
const args = process.argv.slice(2);
|
|
196
|
+
|
|
197
|
+
const options = {
|
|
198
|
+
image: null,
|
|
199
|
+
html: null,
|
|
200
|
+
designFolder: null,
|
|
201
|
+
framework: 'react',
|
|
202
|
+
output: './generated',
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
for (let i = 0; i < args.length; i++) {
|
|
206
|
+
switch (args[i]) {
|
|
207
|
+
case '--image':
|
|
208
|
+
options.image = args[++i];
|
|
209
|
+
break;
|
|
210
|
+
case '--html':
|
|
211
|
+
options.html = args[++i];
|
|
212
|
+
break;
|
|
213
|
+
case '--design-folder':
|
|
214
|
+
case '--folder':
|
|
215
|
+
options.designFolder = args[++i];
|
|
216
|
+
break;
|
|
217
|
+
case '--framework':
|
|
218
|
+
case '-f':
|
|
219
|
+
options.framework = args[++i];
|
|
220
|
+
break;
|
|
221
|
+
case '--output':
|
|
222
|
+
case '-o':
|
|
223
|
+
options.output = args[++i];
|
|
224
|
+
break;
|
|
225
|
+
case '--help':
|
|
226
|
+
case '-h':
|
|
227
|
+
console.log(`
|
|
228
|
+
Gemini UI Code Generator
|
|
229
|
+
|
|
230
|
+
Usage:
|
|
231
|
+
node gemini-ui-gen.js --image ./design.png --framework react
|
|
232
|
+
node gemini-ui-gen.js --html ./mockup.html --framework vue
|
|
233
|
+
node gemini-ui-gen.js --design-folder ./design/ --framework react
|
|
234
|
+
|
|
235
|
+
Options:
|
|
236
|
+
--image <path> Image file to analyze
|
|
237
|
+
--html <path> HTML mockup to convert
|
|
238
|
+
--design-folder <path> Folder with design files
|
|
239
|
+
--framework <name> Target framework (react, vue, svelte, html)
|
|
240
|
+
--output <path> Output directory
|
|
241
|
+
--help Show this help
|
|
242
|
+
`);
|
|
243
|
+
process.exit(0);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Check Gemini status
|
|
248
|
+
const status = await getGeminiStatus();
|
|
249
|
+
if (!status) {
|
|
250
|
+
console.error('❌ Gemini credentials not found. Run: vibe gemini auth');
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
console.log(`🤖 Gemini UI Generator (${status.type}${status.email ? `: ${status.email}` : ''})`);
|
|
255
|
+
console.log(`📦 Framework: ${options.framework}`);
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
let result;
|
|
259
|
+
|
|
260
|
+
if (options.image) {
|
|
261
|
+
console.log(`\n📷 Analyzing: ${options.image}\n`);
|
|
262
|
+
result = await generateUIFromImage(options.image, options.framework);
|
|
263
|
+
} else if (options.html) {
|
|
264
|
+
console.log(`\n📄 Converting: ${options.html}\n`);
|
|
265
|
+
result = await generateUIFromHTML(options.html, options.framework);
|
|
266
|
+
} else if (options.designFolder) {
|
|
267
|
+
console.log(`\n📂 Analyzing folder: ${options.designFolder}\n`);
|
|
268
|
+
result = await analyzeDesignFolder(options.designFolder, options.framework);
|
|
269
|
+
} else {
|
|
270
|
+
console.error('❌ No input specified. Use --image, --html, or --design-folder');
|
|
271
|
+
process.exit(1);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
console.log('\n' + '='.repeat(60) + '\n');
|
|
275
|
+
console.log(result);
|
|
276
|
+
console.log('\n' + '='.repeat(60));
|
|
277
|
+
|
|
278
|
+
if (options.output && result) {
|
|
279
|
+
if (!fs.existsSync(options.output)) {
|
|
280
|
+
fs.mkdirSync(options.output, { recursive: true });
|
|
281
|
+
}
|
|
282
|
+
const outputFile = path.join(options.output, `generated-${Date.now()}.txt`);
|
|
283
|
+
fs.writeFileSync(outputFile, result);
|
|
284
|
+
console.log(`\n✅ Output saved to: ${outputFile}`);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
} catch (error) {
|
|
288
|
+
console.error(`\n❌ Error: ${error.message}`);
|
|
289
|
+
process.exit(1);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
main();
|