create-threejs-game 1.0.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/README.md +97 -0
- package/bin/cli.js +370 -0
- package/package.json +29 -0
- package/template/.claude/skills/threejs-animation/SKILL.md +552 -0
- package/template/.claude/skills/threejs-fundamentals/SKILL.md +488 -0
- package/template/.claude/skills/threejs-geometry/SKILL.md +548 -0
- package/template/.claude/skills/threejs-interaction/SKILL.md +660 -0
- package/template/.claude/skills/threejs-lighting/SKILL.md +481 -0
- package/template/.claude/skills/threejs-loaders/SKILL.md +623 -0
- package/template/.claude/skills/threejs-materials/SKILL.md +520 -0
- package/template/.claude/skills/threejs-postprocessing/SKILL.md +602 -0
- package/template/.claude/skills/threejs-shaders/SKILL.md +642 -0
- package/template/.claude/skills/threejs-textures/SKILL.md +628 -0
- package/template/.codex/skills/threejs-animation/SKILL.md +552 -0
- package/template/.codex/skills/threejs-fundamentals/SKILL.md +488 -0
- package/template/.codex/skills/threejs-geometry/SKILL.md +548 -0
- package/template/.codex/skills/threejs-interaction/SKILL.md +660 -0
- package/template/.codex/skills/threejs-lighting/SKILL.md +481 -0
- package/template/.codex/skills/threejs-loaders/SKILL.md +623 -0
- package/template/.codex/skills/threejs-materials/SKILL.md +520 -0
- package/template/.codex/skills/threejs-postprocessing/SKILL.md +602 -0
- package/template/.codex/skills/threejs-shaders/SKILL.md +642 -0
- package/template/.codex/skills/threejs-textures/SKILL.md +628 -0
- package/template/README.md +352 -0
- package/template/docs/.gitkeep +0 -0
- package/template/plans/.gitkeep +0 -0
- package/template/prompts/01-mockup-generation.md +44 -0
- package/template/prompts/02-prd-generation.md +119 -0
- package/template/prompts/03-tdd-generation.md +127 -0
- package/template/prompts/04-execution-plan.md +89 -0
- package/template/prompts/05-implementation.md +61 -0
- package/template/public/assets/.gitkeep +0 -0
- package/template/scripts/config.example.json +12 -0
- package/template/scripts/generate-assets-json.js +149 -0
- package/template/scripts/generate-mockup.js +197 -0
- package/template/scripts/generate-plan.js +295 -0
- package/template/scripts/generate-prd.js +297 -0
- package/template/scripts/generate-tdd.js +283 -0
- package/template/scripts/pipeline.js +229 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Execution Plan Generation Prompt
|
|
2
|
+
|
|
3
|
+
## Tool
|
|
4
|
+
LLM with Three.js skills enabled (Claude Code, Cursor, etc.)
|
|
5
|
+
|
|
6
|
+
**IMPORTANT:** Start a NEW CHAT with clear context
|
|
7
|
+
|
|
8
|
+
## Files to Attach
|
|
9
|
+
- `docs/prd.md` - Product requirements
|
|
10
|
+
- `docs/tdd.md` - Technical design
|
|
11
|
+
- `public/assets/{game_name}/assets.json` - Asset index
|
|
12
|
+
|
|
13
|
+
## Prompt Template
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
Implement the game defined in docs/prd.md adhering to the technical design
|
|
17
|
+
in docs/tdd.md.
|
|
18
|
+
|
|
19
|
+
Note the assets index in public/assets/{game_name}/assets.json.
|
|
20
|
+
|
|
21
|
+
Use the Three.js skills for implementation patterns.
|
|
22
|
+
|
|
23
|
+
Create an execution plan that:
|
|
24
|
+
|
|
25
|
+
## Overview
|
|
26
|
+
- Target file(s) to create
|
|
27
|
+
- Key references to PRD and TDD sections
|
|
28
|
+
- Asset path format
|
|
29
|
+
|
|
30
|
+
## Implementation Phases
|
|
31
|
+
For each phase, specify:
|
|
32
|
+
- Phase name and priority (Critical/Important/Polish)
|
|
33
|
+
- What to implement
|
|
34
|
+
- Key code sections from TDD to use
|
|
35
|
+
- Verification steps
|
|
36
|
+
|
|
37
|
+
Suggested phases:
|
|
38
|
+
1. Core Engine (Critical) - Scene, camera, lighting, ground
|
|
39
|
+
2. Asset Loading (Critical) - GLTF loader, fallbacks
|
|
40
|
+
3. ECS Architecture (Critical) - Entities, components, manager
|
|
41
|
+
4. Selection System (Critical) - Click/box select, visuals
|
|
42
|
+
5. Command System (Critical) - Move, attack commands
|
|
43
|
+
6. Movement System (Critical) - Unit movement, collision
|
|
44
|
+
7. Combat System (Critical) - Damage, death
|
|
45
|
+
8. Economy/Resource System (Important) - If applicable
|
|
46
|
+
9. AI System (Important) - Enemy behavior
|
|
47
|
+
10. UI & Game States (Important) - Menus, HUD
|
|
48
|
+
11. Effects (Polish) - Juice, particles
|
|
49
|
+
12. Mobile/Polish (Polish) - Touch controls, onboarding
|
|
50
|
+
|
|
51
|
+
## HTML File Structure
|
|
52
|
+
Show the expected structure:
|
|
53
|
+
- DOCTYPE, head, meta tags
|
|
54
|
+
- Style block organization
|
|
55
|
+
- Import map for Three.js
|
|
56
|
+
- Script module organization
|
|
57
|
+
|
|
58
|
+
## Map Setup
|
|
59
|
+
- Initial entity positions
|
|
60
|
+
- Resource placement
|
|
61
|
+
- Obstacle placement
|
|
62
|
+
|
|
63
|
+
## Verification Checklist
|
|
64
|
+
From PRD success criteria, list what must work:
|
|
65
|
+
- [ ] Game loads without errors
|
|
66
|
+
- [ ] [Other criteria...]
|
|
67
|
+
|
|
68
|
+
## Estimated Scope
|
|
69
|
+
- Approximate lines of code
|
|
70
|
+
- Expected complexity
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Output
|
|
74
|
+
Save to: `plans/{descriptive-name}.md`
|
|
75
|
+
|
|
76
|
+
**Naming convention:** Use a memorable name like:
|
|
77
|
+
- `plans/initial-implementation.md`
|
|
78
|
+
- `plans/core-gameplay.md`
|
|
79
|
+
- Or use a random word generator for unique names
|
|
80
|
+
|
|
81
|
+
## Next Step
|
|
82
|
+
After saving the plan, proceed to implementation:
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
Please proceed with implementing the game based on the plan in
|
|
86
|
+
plans/{plan-name}.md
|
|
87
|
+
|
|
88
|
+
Use the Three.js skills for reference.
|
|
89
|
+
```
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Implementation Prompt
|
|
2
|
+
|
|
3
|
+
## Tool
|
|
4
|
+
Claude Code / Cursor with Three.js skills enabled
|
|
5
|
+
|
|
6
|
+
## Files to Attach
|
|
7
|
+
- `plans/{plan-name}.md` - The execution plan
|
|
8
|
+
|
|
9
|
+
## Prompt Template
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
Please proceed with implementing the game based on the plan in
|
|
13
|
+
plans/{plan-name}.md
|
|
14
|
+
|
|
15
|
+
Use the Three.js skills for reference.
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## What to Expect
|
|
19
|
+
The AI will:
|
|
20
|
+
1. Read the plan and understand the phased approach
|
|
21
|
+
2. Create the HTML file structure
|
|
22
|
+
3. Implement each phase sequentially
|
|
23
|
+
4. Test and verify as it goes
|
|
24
|
+
|
|
25
|
+
## During Implementation
|
|
26
|
+
You may need to:
|
|
27
|
+
- Answer clarifying questions
|
|
28
|
+
- Approve file creations
|
|
29
|
+
- Test the game in browser and report issues
|
|
30
|
+
- Request adjustments to specific features
|
|
31
|
+
|
|
32
|
+
## Common Follow-up Prompts
|
|
33
|
+
|
|
34
|
+
**If something doesn't work:**
|
|
35
|
+
```
|
|
36
|
+
The [feature] isn't working correctly. When I [action], it [actual behavior]
|
|
37
|
+
instead of [expected behavior]. Can you fix this?
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**To add a feature:**
|
|
41
|
+
```
|
|
42
|
+
Can you add [feature] to the game? It should [description of behavior].
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**To improve performance:**
|
|
46
|
+
```
|
|
47
|
+
The game is running slowly. Can you optimize [specific system/area]?
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**To adjust visuals:**
|
|
51
|
+
```
|
|
52
|
+
Can you adjust the [visual element] to be more [description]?
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**To test specific functionality:**
|
|
56
|
+
```
|
|
57
|
+
Can you verify that [specific feature] works by [test steps]?
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Verification
|
|
61
|
+
After implementation, test all items in the plan's verification checklist.
|
|
File without changes
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"google_ai_studio": {
|
|
3
|
+
"api_key": "YOUR_GOOGLE_AI_STUDIO_API_KEY"
|
|
4
|
+
},
|
|
5
|
+
"anthropic": {
|
|
6
|
+
"api_key": "YOUR_ANTHROPIC_API_KEY"
|
|
7
|
+
},
|
|
8
|
+
"game": {
|
|
9
|
+
"name": "your_game_name",
|
|
10
|
+
"description": "A 3D real-time strategy game set in a medieval fantasy world where players gather resources, build bases, and destroy the enemy."
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Asset Index Generator
|
|
5
|
+
*
|
|
6
|
+
* Generates an assets.json file that indexes all assets in a game's asset folder.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node generate-assets-json.js <game_name>
|
|
10
|
+
*
|
|
11
|
+
* Example:
|
|
12
|
+
* node generate-assets-json.js medieval
|
|
13
|
+
*
|
|
14
|
+
* This will scan public/assets/medieval/ and create public/assets/medieval/assets.json
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
|
|
20
|
+
// Get game name from command line argument
|
|
21
|
+
const gameName = process.argv[2];
|
|
22
|
+
|
|
23
|
+
if (!gameName) {
|
|
24
|
+
console.error('Usage: node generate-assets-json.js <game_name>');
|
|
25
|
+
console.error('Example: node generate-assets-json.js medieval');
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Paths
|
|
30
|
+
const scriptDir = __dirname;
|
|
31
|
+
const projectRoot = path.join(scriptDir, '..');
|
|
32
|
+
const assetsDir = path.join(projectRoot, 'public', 'assets', gameName);
|
|
33
|
+
const outputPath = path.join(assetsDir, 'assets.json');
|
|
34
|
+
|
|
35
|
+
// Check if assets directory exists
|
|
36
|
+
if (!fs.existsSync(assetsDir)) {
|
|
37
|
+
console.error(`Error: Assets directory not found: ${assetsDir}`);
|
|
38
|
+
console.error(`Please create the directory and add your assets first.`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// File extensions to index
|
|
43
|
+
const SUPPORTED_EXTENSIONS = {
|
|
44
|
+
'3d': ['.gltf', '.glb', '.obj', '.fbx'],
|
|
45
|
+
'images': ['.png', '.jpg', '.jpeg', '.webp', '.gif'],
|
|
46
|
+
'audio': ['.mp3', '.wav', '.ogg'],
|
|
47
|
+
'other': ['.json', '.txt']
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Get category from extension
|
|
51
|
+
function getCategory(ext) {
|
|
52
|
+
ext = ext.toLowerCase();
|
|
53
|
+
|
|
54
|
+
if (SUPPORTED_EXTENSIONS['3d'].includes(ext)) {
|
|
55
|
+
return ext === '.gltf' || ext === '.glb' ? 'glTF' : '3D';
|
|
56
|
+
}
|
|
57
|
+
if (SUPPORTED_EXTENSIONS.images.includes(ext)) {
|
|
58
|
+
return 'PNG'; // Keep as PNG for consistency with existing format
|
|
59
|
+
}
|
|
60
|
+
if (SUPPORTED_EXTENSIONS.audio.includes(ext)) {
|
|
61
|
+
return 'Audio';
|
|
62
|
+
}
|
|
63
|
+
return 'Other';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Recursively scan directory for assets
|
|
67
|
+
function scanDirectory(dir, relativeTo) {
|
|
68
|
+
const assets = [];
|
|
69
|
+
|
|
70
|
+
if (!fs.existsSync(dir)) {
|
|
71
|
+
return assets;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const items = fs.readdirSync(dir, { withFileTypes: true });
|
|
75
|
+
|
|
76
|
+
for (const item of items) {
|
|
77
|
+
const fullPath = path.join(dir, item.name);
|
|
78
|
+
|
|
79
|
+
if (item.isDirectory()) {
|
|
80
|
+
// Recursively scan subdirectories
|
|
81
|
+
assets.push(...scanDirectory(fullPath, relativeTo));
|
|
82
|
+
} else if (item.isFile()) {
|
|
83
|
+
const ext = path.extname(item.name).toLowerCase();
|
|
84
|
+
|
|
85
|
+
// Skip if not a supported extension or is the output file itself
|
|
86
|
+
if (item.name === 'assets.json') continue;
|
|
87
|
+
|
|
88
|
+
const allExtensions = [
|
|
89
|
+
...SUPPORTED_EXTENSIONS['3d'],
|
|
90
|
+
...SUPPORTED_EXTENSIONS.images,
|
|
91
|
+
...SUPPORTED_EXTENSIONS.audio
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
if (!allExtensions.includes(ext)) continue;
|
|
95
|
+
|
|
96
|
+
const relativePath = path.relative(relativeTo, fullPath);
|
|
97
|
+
const category = getCategory(ext);
|
|
98
|
+
|
|
99
|
+
assets.push({
|
|
100
|
+
name: item.name,
|
|
101
|
+
path: `public/assets/${gameName}/${relativePath.replace(/\\/g, '/')}`,
|
|
102
|
+
relativePath: relativePath.replace(/\\/g, '/'),
|
|
103
|
+
category: category,
|
|
104
|
+
extension: ext,
|
|
105
|
+
focusGlTF: ext === '.gltf' || ext === '.glb'
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return assets;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Main execution
|
|
114
|
+
console.log(`Scanning assets in: ${assetsDir}`);
|
|
115
|
+
|
|
116
|
+
const assets = scanDirectory(assetsDir, assetsDir);
|
|
117
|
+
|
|
118
|
+
// Sort assets by name
|
|
119
|
+
assets.sort((a, b) => a.name.localeCompare(b.name));
|
|
120
|
+
|
|
121
|
+
// Count by category
|
|
122
|
+
const categoryCounts = {};
|
|
123
|
+
for (const asset of assets) {
|
|
124
|
+
categoryCounts[asset.category] = (categoryCounts[asset.category] || 0) + 1;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Count glTF assets
|
|
128
|
+
const glTFCount = assets.filter(a => a.focusGlTF).length;
|
|
129
|
+
|
|
130
|
+
// Build output
|
|
131
|
+
const output = {
|
|
132
|
+
metadata: {
|
|
133
|
+
generatedAt: new Date().toISOString(),
|
|
134
|
+
root: `public/assets/${gameName}`,
|
|
135
|
+
totalAssets: assets.length,
|
|
136
|
+
glTFAssetCount: glTFCount,
|
|
137
|
+
categories: categoryCounts
|
|
138
|
+
},
|
|
139
|
+
assets: assets
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// Write output
|
|
143
|
+
fs.writeFileSync(outputPath, JSON.stringify(output, null, 2));
|
|
144
|
+
|
|
145
|
+
console.log(`\nGenerated: ${outputPath}`);
|
|
146
|
+
console.log(`\nSummary:`);
|
|
147
|
+
console.log(` Total assets: ${assets.length}`);
|
|
148
|
+
console.log(` glTF/GLB models: ${glTFCount}`);
|
|
149
|
+
console.log(` Categories:`, categoryCounts);
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Mockup Generation Script
|
|
5
|
+
*
|
|
6
|
+
* Uses Google AI Studio (Gemini) to generate a game concept mockup
|
|
7
|
+
* based on the asset preview and description.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node generate-mockup.js
|
|
11
|
+
*
|
|
12
|
+
* Requires:
|
|
13
|
+
* - config.json with google_ai_studio.api_key and game settings
|
|
14
|
+
* - public/assets/{game_name}/Preview.jpg
|
|
15
|
+
* - public/assets/{game_name}/assets.json
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const https = require('https');
|
|
21
|
+
|
|
22
|
+
// Load config
|
|
23
|
+
const scriptDir = __dirname;
|
|
24
|
+
const projectRoot = path.join(scriptDir, '..');
|
|
25
|
+
const configPath = path.join(scriptDir, 'config.json');
|
|
26
|
+
|
|
27
|
+
if (!fs.existsSync(configPath)) {
|
|
28
|
+
console.error('Error: config.json not found');
|
|
29
|
+
console.error('Copy config.example.json to config.json and add your API keys');
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
34
|
+
const { google_ai_studio, game } = config;
|
|
35
|
+
|
|
36
|
+
// Check config first, then env vars
|
|
37
|
+
const apiKey = (google_ai_studio?.api_key && !google_ai_studio.api_key.includes('YOUR_'))
|
|
38
|
+
? google_ai_studio.api_key
|
|
39
|
+
: process.env.GOOGLE_API_KEY || process.env.GOOGLE_AI_STUDIO_API_KEY;
|
|
40
|
+
|
|
41
|
+
if (!apiKey) {
|
|
42
|
+
console.error('Error: Google AI Studio API key not found');
|
|
43
|
+
console.error('Set GOOGLE_API_KEY env var or configure scripts/config.json');
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Use resolved key
|
|
48
|
+
google_ai_studio.api_key = apiKey;
|
|
49
|
+
|
|
50
|
+
// Paths
|
|
51
|
+
const assetsDir = path.join(projectRoot, 'public', 'assets', game.name);
|
|
52
|
+
const previewPath = path.join(assetsDir, 'Preview.jpg');
|
|
53
|
+
const assetsJsonPath = path.join(assetsDir, 'assets.json');
|
|
54
|
+
const outputDir = path.join(projectRoot, 'public', game.name);
|
|
55
|
+
const outputPath = path.join(outputDir, 'concept.jpg');
|
|
56
|
+
|
|
57
|
+
// Check required files
|
|
58
|
+
if (!fs.existsSync(previewPath)) {
|
|
59
|
+
console.error(`Error: Preview image not found: ${previewPath}`);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!fs.existsSync(assetsJsonPath)) {
|
|
64
|
+
console.error(`Error: assets.json not found: ${assetsJsonPath}`);
|
|
65
|
+
console.error('Run: node generate-assets-json.js ' + game.name);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Read files
|
|
70
|
+
const previewImage = fs.readFileSync(previewPath);
|
|
71
|
+
const previewBase64 = previewImage.toString('base64');
|
|
72
|
+
const assetsJson = JSON.parse(fs.readFileSync(assetsJsonPath, 'utf-8'));
|
|
73
|
+
|
|
74
|
+
// Build asset summary (just glTF names for brevity)
|
|
75
|
+
const assetNames = assetsJson.assets
|
|
76
|
+
.filter(a => a.focusGlTF)
|
|
77
|
+
.map(a => a.name.replace('.gltf', ''))
|
|
78
|
+
.join(', ');
|
|
79
|
+
|
|
80
|
+
// Build prompt
|
|
81
|
+
const prompt = `You are a game concept artist. Based on the attached preview image showing available 3D assets and the following game description, create a detailed mockup image showing how this game would look during gameplay.
|
|
82
|
+
|
|
83
|
+
GAME DESCRIPTION:
|
|
84
|
+
${game.description}
|
|
85
|
+
|
|
86
|
+
AVAILABLE ASSETS (3D models):
|
|
87
|
+
${assetNames}
|
|
88
|
+
|
|
89
|
+
The mockup should show:
|
|
90
|
+
1. An actual gameplay scene (not a title screen)
|
|
91
|
+
2. Multiple game elements arranged naturally (buildings, units, terrain, resources)
|
|
92
|
+
3. Appropriate UI elements for this game type (health bars, resource counters, minimap, etc.)
|
|
93
|
+
4. The camera perspective that best suits this game type
|
|
94
|
+
5. The visual style matching the provided assets
|
|
95
|
+
|
|
96
|
+
Create a polished, professional game screenshot mockup that shows the core gameplay loop in action.`;
|
|
97
|
+
|
|
98
|
+
console.log('Generating mockup with Google AI Studio...');
|
|
99
|
+
console.log('Game:', game.name);
|
|
100
|
+
console.log('Description:', game.description);
|
|
101
|
+
|
|
102
|
+
// Gemini API request
|
|
103
|
+
const requestBody = JSON.stringify({
|
|
104
|
+
contents: [{
|
|
105
|
+
parts: [
|
|
106
|
+
{
|
|
107
|
+
text: prompt
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
inline_data: {
|
|
111
|
+
mime_type: 'image/jpeg',
|
|
112
|
+
data: previewBase64
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
}],
|
|
117
|
+
generationConfig: {
|
|
118
|
+
responseModalities: ['image', 'text'],
|
|
119
|
+
responseMimeType: 'image/jpeg'
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const options = {
|
|
124
|
+
hostname: 'generativelanguage.googleapis.com',
|
|
125
|
+
port: 443,
|
|
126
|
+
path: `/v1beta/models/gemini-2.0-flash-exp:generateContent?key=${google_ai_studio.api_key}`,
|
|
127
|
+
method: 'POST',
|
|
128
|
+
headers: {
|
|
129
|
+
'Content-Type': 'application/json',
|
|
130
|
+
'Content-Length': Buffer.byteLength(requestBody)
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const req = https.request(options, (res) => {
|
|
135
|
+
let data = '';
|
|
136
|
+
|
|
137
|
+
res.on('data', (chunk) => {
|
|
138
|
+
data += chunk;
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
res.on('end', () => {
|
|
142
|
+
try {
|
|
143
|
+
const response = JSON.parse(data);
|
|
144
|
+
|
|
145
|
+
if (response.error) {
|
|
146
|
+
console.error('API Error:', response.error.message);
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Find image in response
|
|
151
|
+
const candidates = response.candidates || [];
|
|
152
|
+
let imageData = null;
|
|
153
|
+
|
|
154
|
+
for (const candidate of candidates) {
|
|
155
|
+
const parts = candidate.content?.parts || [];
|
|
156
|
+
for (const part of parts) {
|
|
157
|
+
if (part.inline_data?.mime_type?.startsWith('image/')) {
|
|
158
|
+
imageData = part.inline_data.data;
|
|
159
|
+
break;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (imageData) break;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (!imageData) {
|
|
166
|
+
console.error('No image generated in response');
|
|
167
|
+
console.error('Response:', JSON.stringify(response, null, 2));
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Ensure output directory exists
|
|
172
|
+
if (!fs.existsSync(outputDir)) {
|
|
173
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Save image
|
|
177
|
+
const imageBuffer = Buffer.from(imageData, 'base64');
|
|
178
|
+
fs.writeFileSync(outputPath, imageBuffer);
|
|
179
|
+
|
|
180
|
+
console.log('\nMockup generated successfully!');
|
|
181
|
+
console.log('Output:', outputPath);
|
|
182
|
+
|
|
183
|
+
} catch (err) {
|
|
184
|
+
console.error('Error parsing response:', err.message);
|
|
185
|
+
console.error('Raw response:', data.substring(0, 500));
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
req.on('error', (err) => {
|
|
192
|
+
console.error('Request error:', err.message);
|
|
193
|
+
process.exit(1);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
req.write(requestBody);
|
|
197
|
+
req.end();
|