designkit-ai 1.1.0 → 1.1.2
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/bin/designkit.js +2 -0
- package/package.json +1 -1
- package/src/commands/imagine.js +93 -40
package/bin/designkit.js
CHANGED
|
@@ -76,6 +76,8 @@ program
|
|
|
76
76
|
.option('-o, --output <dir>', 'Output directory', '.')
|
|
77
77
|
.option('-n, --name <name>', 'Output filename (without extension)', 'image')
|
|
78
78
|
.option('-c, --count <n>', 'Number of images to generate (1–4)', '1')
|
|
79
|
+
.option('-m, --model <model>', 'Gemini model to use (run --list-models to see all)')
|
|
80
|
+
.option('--list-models', 'Show all available image models')
|
|
79
81
|
.option('--aspect <ratio>', 'Aspect ratio: 1:1, 16:9, 9:16, 4:3, 3:4', '1:1')
|
|
80
82
|
.option('--size <size>', 'DALL-E size: 1024x1024, 1792x1024, 1024x1792', '1024x1024')
|
|
81
83
|
.option('--quality <quality>', 'DALL-E quality: standard, hd', 'standard')
|
package/package.json
CHANGED
package/src/commands/imagine.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { GoogleGenAI } from '@google/genai'
|
|
2
2
|
import OpenAI from 'openai'
|
|
3
3
|
import { writeFileSync, existsSync, mkdirSync } from 'fs'
|
|
4
|
-
import { resolve
|
|
4
|
+
import { resolve } from 'path'
|
|
5
5
|
|
|
6
|
-
const IMAGEN_MODEL = 'imagen-3.0-generate-002'
|
|
7
6
|
const DALLE_MODEL = 'dall-e-3'
|
|
8
7
|
|
|
9
8
|
const PROVIDERS = {
|
|
10
|
-
gemini: { envKey: 'GEMINI_API_KEY', label: '
|
|
9
|
+
gemini: { envKey: 'GEMINI_API_KEY', label: 'Gemini (Google)' },
|
|
11
10
|
openai: { envKey: 'OPENAI_API_KEY', label: 'DALL-E 3 (OpenAI)' }
|
|
12
11
|
}
|
|
13
12
|
|
|
@@ -22,10 +21,35 @@ function checkApiKey(provider) {
|
|
|
22
21
|
return key
|
|
23
22
|
}
|
|
24
23
|
|
|
24
|
+
const GEMINI_MODELS = [
|
|
25
|
+
{ model: 'gemini-2.5-flash-image', type: 'gemini', note: 'Default — fast, good quality' },
|
|
26
|
+
{ model: 'gemini-3.1-flash-image-preview', type: 'gemini', note: 'Newer flash preview' },
|
|
27
|
+
{ model: 'gemini-3-pro-image-preview', type: 'gemini', note: 'Pro quality' },
|
|
28
|
+
{ model: 'imagen-4.0-fast-generate-001', type: 'imagen', note: 'Imagen 4 Fast' },
|
|
29
|
+
{ model: 'imagen-4.0-generate-001', type: 'imagen', note: 'Imagen 4 — high quality' },
|
|
30
|
+
{ model: 'imagen-4.0-ultra-generate-001', type: 'imagen', note: 'Imagen 4 Ultra — best quality' },
|
|
31
|
+
]
|
|
32
|
+
|
|
25
33
|
export async function imagineCommand(prompt, options) {
|
|
26
34
|
const provider = options.provider || 'gemini'
|
|
27
35
|
const validProviders = Object.keys(PROVIDERS)
|
|
28
36
|
|
|
37
|
+
// --list-models: show available models and exit
|
|
38
|
+
if (options.listModels) {
|
|
39
|
+
console.log('\nGemini image models:\n')
|
|
40
|
+
console.log(' ' + 'Model'.padEnd(42) + 'Notes')
|
|
41
|
+
console.log(' ' + '─'.repeat(60))
|
|
42
|
+
for (const m of GEMINI_MODELS) {
|
|
43
|
+
console.log(` ${m.model.padEnd(42)}${m.note}`)
|
|
44
|
+
}
|
|
45
|
+
console.log('\nOpenAI image models:\n')
|
|
46
|
+
console.log(' dall-e-3 Standard or HD quality')
|
|
47
|
+
console.log('\nUsage:')
|
|
48
|
+
console.log(' designkit imagine "prompt" --model imagen-4.0-ultra-generate-001')
|
|
49
|
+
console.log('')
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
29
53
|
if (!validProviders.includes(provider)) {
|
|
30
54
|
console.error(`Error: Unknown provider "${provider}". Available: ${validProviders.join(', ')}`)
|
|
31
55
|
process.exit(1)
|
|
@@ -37,19 +61,69 @@ export async function imagineCommand(prompt, options) {
|
|
|
37
61
|
|
|
38
62
|
if (!existsSync(outputDir)) mkdirSync(outputDir, { recursive: true })
|
|
39
63
|
|
|
40
|
-
console.error(`\nProvider: ${PROVIDERS[provider].label}`)
|
|
41
|
-
console.error(`Prompt: ${prompt}`)
|
|
42
|
-
console.error(`Count: ${count}`)
|
|
43
|
-
console.error(`Output: ${outputDir}\n`)
|
|
44
|
-
|
|
45
64
|
if (provider === 'gemini') {
|
|
46
|
-
|
|
65
|
+
const model = options.model || 'gemini-2.5-flash-image'
|
|
66
|
+
console.error(`\nModel: ${model}`)
|
|
67
|
+
console.error(`Prompt: ${prompt}`)
|
|
68
|
+
console.error(`Count: ${count}`)
|
|
69
|
+
console.error(`Output: ${outputDir}\n`)
|
|
70
|
+
|
|
71
|
+
// imagen-* models use generateImages API
|
|
72
|
+
// gemini-* models use generateContent with responseModalities
|
|
73
|
+
if (model.startsWith('imagen')) {
|
|
74
|
+
await generateWithImagen({ apiKey, model, prompt, count, outputDir, options })
|
|
75
|
+
} else {
|
|
76
|
+
await generateWithGeminiFlash({ apiKey, model, prompt, count, outputDir, options })
|
|
77
|
+
}
|
|
47
78
|
} else if (provider === 'openai') {
|
|
79
|
+
console.error(`\nModel: ${DALLE_MODEL}`)
|
|
80
|
+
console.error(`Prompt: ${prompt}`)
|
|
81
|
+
console.error(`Count: ${count}`)
|
|
82
|
+
console.error(`Output: ${outputDir}\n`)
|
|
48
83
|
await generateWithOpenAI({ apiKey, prompt, count, outputDir, options })
|
|
49
84
|
}
|
|
50
85
|
}
|
|
51
86
|
|
|
52
|
-
|
|
87
|
+
// gemini-2.5-flash, gemini-2.0-flash-exp-image-generation, etc.
|
|
88
|
+
async function generateWithGeminiFlash({ apiKey, model, prompt, count, outputDir, options }) {
|
|
89
|
+
const ai = new GoogleGenAI({ apiKey })
|
|
90
|
+
console.error(`Generating images...`)
|
|
91
|
+
|
|
92
|
+
const saved = []
|
|
93
|
+
for (let i = 0; i < count; i++) {
|
|
94
|
+
const response = await ai.models.generateContent({
|
|
95
|
+
model,
|
|
96
|
+
contents: prompt,
|
|
97
|
+
config: {
|
|
98
|
+
responseModalities: ['image', 'text'],
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
const parts = response.candidates?.[0]?.content?.parts || []
|
|
103
|
+
const imagePart = parts.find(p => p.inlineData?.mimeType?.startsWith('image/'))
|
|
104
|
+
|
|
105
|
+
if (!imagePart?.inlineData?.data) {
|
|
106
|
+
console.error(`Warning: No image in response ${i + 1}`)
|
|
107
|
+
continue
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const ext = imagePart.inlineData.mimeType === 'image/jpeg' ? 'jpg' : 'png'
|
|
111
|
+
const filename = count === 1
|
|
112
|
+
? `${options.name || 'image'}.${ext}`
|
|
113
|
+
: `${options.name || 'image'}-${i + 1}.${ext}`
|
|
114
|
+
|
|
115
|
+
const outputPath = resolve(outputDir, filename)
|
|
116
|
+
const buffer = Buffer.from(imagePart.inlineData.data, 'base64')
|
|
117
|
+
writeFileSync(outputPath, buffer)
|
|
118
|
+
saved.push(outputPath)
|
|
119
|
+
console.error(`Saved: ${outputPath}`)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
console.error(`\nDone — ${saved.length} image(s) generated`)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// imagen-3.0-generate-002
|
|
126
|
+
async function generateWithImagen({ apiKey, model, prompt, count, outputDir, options }) {
|
|
53
127
|
const ai = new GoogleGenAI({ apiKey })
|
|
54
128
|
|
|
55
129
|
const config = {
|
|
@@ -57,21 +131,13 @@ async function generateWithGemini({ apiKey, prompt, count, outputDir, options })
|
|
|
57
131
|
outputMimeType: 'image/png'
|
|
58
132
|
}
|
|
59
133
|
|
|
60
|
-
if (options.
|
|
61
|
-
config.aspectRatio = getAspectRatio(parseInt(options.width), parseInt(options.height))
|
|
62
|
-
} else if (options.aspect) {
|
|
63
|
-
config.aspectRatio = options.aspect
|
|
64
|
-
}
|
|
134
|
+
if (options.aspect) config.aspectRatio = options.aspect
|
|
65
135
|
|
|
66
|
-
console.error(`Generating
|
|
67
|
-
|
|
68
|
-
const response = await ai.models.generateImages({
|
|
69
|
-
model: IMAGEN_MODEL,
|
|
70
|
-
prompt,
|
|
71
|
-
config
|
|
72
|
-
})
|
|
136
|
+
console.error(`Generating images...`)
|
|
73
137
|
|
|
138
|
+
const response = await ai.models.generateImages({ model, prompt, config })
|
|
74
139
|
const images = response.generatedImages || []
|
|
140
|
+
|
|
75
141
|
if (!images.length) {
|
|
76
142
|
console.error('Error: No images generated')
|
|
77
143
|
process.exit(1)
|
|
@@ -83,12 +149,11 @@ async function generateWithGemini({ apiKey, prompt, count, outputDir, options })
|
|
|
83
149
|
if (!imageData) continue
|
|
84
150
|
|
|
85
151
|
const filename = count === 1
|
|
86
|
-
?
|
|
152
|
+
? `${options.name || 'image'}.png`
|
|
87
153
|
: `${options.name || 'image'}-${i + 1}.png`
|
|
88
154
|
|
|
89
155
|
const outputPath = resolve(outputDir, filename)
|
|
90
|
-
|
|
91
|
-
writeFileSync(outputPath, buffer)
|
|
156
|
+
writeFileSync(outputPath, Buffer.from(imageData, 'base64'))
|
|
92
157
|
saved.push(outputPath)
|
|
93
158
|
console.error(`Saved: ${outputPath}`)
|
|
94
159
|
}
|
|
@@ -98,13 +163,11 @@ async function generateWithGemini({ apiKey, prompt, count, outputDir, options })
|
|
|
98
163
|
|
|
99
164
|
async function generateWithOpenAI({ apiKey, prompt, count, outputDir, options }) {
|
|
100
165
|
const client = new OpenAI({ apiKey })
|
|
101
|
-
|
|
102
166
|
const size = options.size || '1024x1024'
|
|
103
167
|
const quality = options.quality || 'standard'
|
|
104
168
|
|
|
105
|
-
console.error(`Generating
|
|
169
|
+
console.error(`Generating images...`)
|
|
106
170
|
|
|
107
|
-
// DALL-E 3 only supports n=1 at a time
|
|
108
171
|
const saved = []
|
|
109
172
|
for (let i = 0; i < count; i++) {
|
|
110
173
|
const response = await client.images.generate({
|
|
@@ -120,24 +183,14 @@ async function generateWithOpenAI({ apiKey, prompt, count, outputDir, options })
|
|
|
120
183
|
if (!imageData) continue
|
|
121
184
|
|
|
122
185
|
const filename = count === 1
|
|
123
|
-
?
|
|
186
|
+
? `${options.name || 'image'}.png`
|
|
124
187
|
: `${options.name || 'image'}-${i + 1}.png`
|
|
125
188
|
|
|
126
189
|
const outputPath = resolve(outputDir, filename)
|
|
127
|
-
|
|
128
|
-
writeFileSync(outputPath, buffer)
|
|
190
|
+
writeFileSync(outputPath, Buffer.from(imageData, 'base64'))
|
|
129
191
|
saved.push(outputPath)
|
|
130
192
|
console.error(`Saved: ${outputPath}`)
|
|
131
193
|
}
|
|
132
194
|
|
|
133
195
|
console.error(`\nDone — ${saved.length} image(s) generated`)
|
|
134
196
|
}
|
|
135
|
-
|
|
136
|
-
function getAspectRatio(width, height) {
|
|
137
|
-
const ratio = width / height
|
|
138
|
-
if (ratio >= 1.7) return '16:9'
|
|
139
|
-
if (ratio >= 1.3) return '4:3'
|
|
140
|
-
if (ratio >= 0.95) return '1:1'
|
|
141
|
-
if (ratio >= 0.7) return '3:4'
|
|
142
|
-
return '9:16'
|
|
143
|
-
}
|