genmix 1.0.4 → 1.0.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 CHANGED
@@ -17,6 +17,13 @@ AI-powered image generator using Google Gemini API. Supports image generation fr
17
17
  npm install genmix
18
18
  ```
19
19
 
20
+ ### AI Skill
21
+ You can also add GenMix as a skill for AI agentic development:
22
+
23
+ ```bash
24
+ npx skills add https://github.com/clasen/GenMix --skill genmix
25
+ ```
26
+
20
27
  ## Setup
21
28
 
22
29
  Create a `.env` file in your project root:
@@ -27,10 +34,47 @@ GEMINI_API_KEY=your_api_key_here
27
34
 
28
35
  ## Basic Usage
29
36
 
37
+ ### The Power of GenMix: Multiple References & Chainable API
38
+
39
+ GenMix shines when combining multiple images with specific instructions using its intuitive chainable API:
40
+
41
+ ```javascript
42
+ import { GeminiGenerator } from 'genmix';
43
+ const generator = new GeminiGenerator();
44
+
45
+ const result = await generator
46
+ .pro() // Use the Pro model for best results
47
+ .addReference('./person.jpg', 'Use this person as the main subject')
48
+ .addReference('./background.jpg', 'Use this as the background setting')
49
+ .generate('A photo of the person standing in the background setting, cinematic lighting');
50
+
51
+ await generator.save({ filename: 'composite-result' });
52
+ ```
53
+
54
+ ### Model Selection
55
+
56
+ You can easily switch between the Pro and Flash models using chainable methods:
57
+
58
+ ```javascript
59
+ import { GeminiGenerator } from 'genmix';
60
+
61
+ const generator = new GeminiGenerator();
62
+
63
+ // Use the Pro model
64
+ await generator
65
+ .pro()
66
+ .generate('A highly detailed portrait of a cat');
67
+
68
+ // Switch to the Flash model
69
+ await generator
70
+ .flash()
71
+ .generate('A quick sketch of a dog');
72
+ ```
73
+
30
74
  ### Simple Image Generation
31
75
 
32
76
  ```javascript
33
- const { GeminiGenerator } = require('genmix');
77
+ import { GeminiGenerator } from 'genmix';
34
78
 
35
79
  const generator = new GeminiGenerator();
36
80
 
@@ -90,6 +134,23 @@ new GeminiGenerator({
90
134
  })
91
135
  ```
92
136
 
137
+ ### Model Selection Methods
138
+
139
+ ```javascript
140
+ generator.pro() // Switches to the gemini-3-pro-image-preview model
141
+ generator.flash() // Switches to the gemini-3.1-flash-image-preview model
142
+ ```
143
+ Both methods are chainable and return the generator instance.
144
+
145
+ ### Reference Methods
146
+
147
+ You can also use chainable methods to add one or multiple reference images before calling `generate()`:
148
+
149
+ ```javascript
150
+ generator.addReference(image, description) // Adds a reference image (path, URL, Buffer)
151
+ generator.clearReferences() // Removes all queued reference images
152
+ ```
153
+
93
154
  ### generate() Method
94
155
 
95
156
  ```javascript
@@ -190,7 +251,7 @@ const result = await generator.generate(
190
251
  ### Using Buffers
191
252
 
192
253
  ```javascript
193
- const fs = require('fs');
254
+ import fs from 'fs';
194
255
  const imageBuffer = fs.readFileSync('./image.png');
195
256
 
196
257
  const result = await generator.generate(
@@ -1,15 +1,14 @@
1
- process.loadEnvFile(); // Native Node.js .env loading (Node 20.12+)
2
- const { GeminiGenerator } = require('../index');
3
- const path = require('path');
4
- const fs = require('fs');
1
+ process.loadEnvFile();
2
+ import { GeminiGenerator } from '../index.js';
3
+ import path from 'path';
4
+ import fs from 'fs';
5
5
 
6
6
  async function main() {
7
7
  try {
8
8
  const generator = new GeminiGenerator();
9
9
 
10
- // === Multiple variations of the same image ===
11
- const localImagePath = path.join(__dirname, 'camera_4126.jpg');
12
-
10
+ const localImagePath = path.join(import.meta.dirname, 'camera_4126.jpg');
11
+
13
12
  if (fs.existsSync(localImagePath)) {
14
13
  console.log('🎨 Generating multiple variations...\n');
15
14
 
@@ -23,7 +22,7 @@ async function main() {
23
22
  );
24
23
 
25
24
  if (result.images && result.images.length > 0) {
26
- const saved = await generator.save({ directory: __dirname });
25
+ const saved = await generator.save({ directory: import.meta.dirname });
27
26
  console.log(`✅ ${saved.length} variations saved:`);
28
27
  saved.forEach(p => console.log(` - ${p}`));
29
28
  console.log();
@@ -46,4 +45,3 @@ async function main() {
46
45
  }
47
46
 
48
47
  main();
49
-
@@ -0,0 +1,51 @@
1
+ try { process.loadEnvFile(); } catch (error) { }
2
+ import { GeminiGenerator } from '../index.js';
3
+ import path from 'path';
4
+
5
+
6
+ try {
7
+ const generator = new GeminiGenerator();
8
+
9
+ const background = path.join(import.meta.dirname, 'background.jpg');
10
+ const subject = path.join(import.meta.dirname, 'selfie.jpg');
11
+
12
+ console.log('🖼️ Adding references...');
13
+ generator
14
+ .addReference(background, 'use as background landscape')
15
+ .addReference(subject, 'use as the main subject');
16
+
17
+ console.log('🎨 Generating composite image...\n');
18
+
19
+ const result = await generator.generate(
20
+ 'Place the subject in front of the background, cinematic lighting, golden hour',
21
+ { quality: '2K' }
22
+ );
23
+
24
+ if (result.text) {
25
+ console.log('📝 Model notes:', result.text);
26
+ }
27
+
28
+ if (result.images && result.images.length > 0) {
29
+ const saved = await generator.save({
30
+ directory: import.meta.dirname,
31
+ filename: 'composite-result'
32
+ });
33
+ console.log(`✅ Saved: ${saved[0]}\n`);
34
+ } else {
35
+ console.log('⚠️ No images generated.\n');
36
+ }
37
+
38
+ console.log('🎉 Example completed!\n');
39
+
40
+ } catch (error) {
41
+ console.error('❌ Error:', error.message);
42
+
43
+ if (error.message.includes('API Key')) {
44
+ console.error('\n💡 Tip: Make sure you have GEMINI_API_KEY in your .env file');
45
+ }
46
+
47
+ if (error.message.includes('reference image')) {
48
+ console.error('\n💡 Tip: Place background.jpg and subject.jpg in the demo/ folder');
49
+ }
50
+ }
51
+
@@ -1,14 +1,13 @@
1
- process.loadEnvFile(); // Native Node.js .env loading (Node 20.12+)
2
- const { GeminiGenerator } = require('../index');
3
- const path = require('path');
4
- const fs = require('fs');
1
+ process.loadEnvFile();
2
+ import { GeminiGenerator } from '../index.js';
3
+ import path from 'path';
4
+ import fs from 'fs';
5
5
 
6
6
  async function main() {
7
7
  try {
8
8
  const generator = new GeminiGenerator();
9
9
 
10
- // === Option 1: Use a local image ===
11
- const localImagePath = path.join(__dirname, 'camera_4126.jpg');
10
+ const localImagePath = path.join(import.meta.dirname, 'camera_4126.jpg');
12
11
 
13
12
  if (fs.existsSync(localImagePath)) {
14
13
  console.log('📸 Processing local image...\n');
@@ -23,21 +22,17 @@ async function main() {
23
22
  );
24
23
 
25
24
  if (result1.images && result1.images.length > 0) {
26
- // Create pt directory if it doesn't exist
27
- const ptDir = path.join(__dirname, 'pt');
25
+ const ptDir = path.join(import.meta.dirname, 'pt');
28
26
  if (!fs.existsSync(ptDir)) {
29
27
  fs.mkdirSync(ptDir, { recursive: true });
30
28
  }
31
-
32
- // Use the same filename as the original
29
+
33
30
  const originalName = path.basename(localImagePath, path.extname(localImagePath));
34
-
35
- // Get reference metadata to match format
31
+
36
32
  const refMetadata = generator.getReferenceMetadata();
37
33
  console.log('📊 Reference format:', refMetadata);
38
-
39
- // Save with same format as reference
40
- const saved = await generator.save({
34
+
35
+ const saved = await generator.save({
41
36
  directory: ptDir,
42
37
  filename: originalName,
43
38
  formatOptions: refMetadata
@@ -64,4 +59,3 @@ async function main() {
64
59
  }
65
60
 
66
61
  main();
67
-
package/demo/example.js CHANGED
@@ -1,6 +1,5 @@
1
- process.loadEnvFile(); // Native Node.js .env loading (Node 20.12+)
2
- const { GeminiGenerator } = require('../index');
3
-
1
+ process.loadEnvFile();
2
+ import { GeminiGenerator } from '../index.js';
4
3
 
5
4
  async function exampleBasicGeneration() {
6
5
  console.log('\n=== Example 1: Basic Image Generation ===\n');
@@ -12,7 +11,7 @@ async function exampleBasicGeneration() {
12
11
  console.log('Generating image...');
13
12
  const result = await generator.generate(prompt, {
14
13
  numberOfImages: 2,
15
- quality: '1K', // Options: 1K, 2K, 4K
14
+ quality: '1K',
16
15
  aspectRatio: '1:1'
17
16
  });
18
17
 
@@ -25,20 +24,16 @@ async function exampleBasicGeneration() {
25
24
  if (result.images && result.images.length > 0) {
26
25
  console.log(`Found ${result.images.length} images.`);
27
26
 
28
- const savedPaths = await generator.save({ directory: __dirname });
27
+ const savedPaths = await generator.save({ directory: import.meta.dirname });
29
28
  savedPaths.forEach(p => console.log(`Saved image to ${p}`));
30
29
  } else {
31
30
  console.log('No images generated.');
32
31
  }
33
32
  }
34
33
 
35
-
36
-
37
34
  async function main() {
38
35
  try {
39
- // Example 1: Basic generation
40
36
  await exampleBasicGeneration();
41
-
42
37
  console.log('\n=== All examples completed! ===\n');
43
38
  } catch (error) {
44
39
  console.error('Error:', error.message);
@@ -0,0 +1,12 @@
1
+ {
2
+ "name": "demo",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "license": "ISC",
6
+ "author": "",
7
+ "type": "module",
8
+ "main": "index.js",
9
+ "scripts": {
10
+ "test": "echo \"Error: no test specified\" && exit 1"
11
+ }
12
+ }
@@ -4,10 +4,15 @@ const fs = require('fs');
4
4
  const path = require('path');
5
5
 
6
6
  class GeminiGenerator extends BaseGenerator {
7
+ static MODELS = {
8
+ PRO: 'gemini-3-pro-image-preview',
9
+ FLASH: 'gemini-3.1-flash-image-preview',
10
+ };
11
+
7
12
  /**
8
13
  * @param {Object} [config]
9
14
  * @param {string} [config.apiKey]
10
- * @param {string} [config.modelId]
15
+ * @param {string} [config.modelId] - Model ID or use GeminiGenerator.MODELS constants
11
16
  */
12
17
  constructor(config = {}) {
13
18
  super(config);
@@ -16,20 +21,56 @@ class GeminiGenerator extends BaseGenerator {
16
21
  if (!this.apiKey) {
17
22
  throw new Error('API Key is required. Provide it in the constructor or set GEMINI_API_KEY environment variable.');
18
23
  }
19
- // Use the specific model for image generation or default to pro-vision if needed
20
- // However, for standard image generation via prompt, specific models like 'gemini-1.5-pro' or 'imagen-3.0-generate-001' are often used.
21
- // Keeping the user's modelId preference.
22
- this.modelId = config.modelId || 'gemini-3-pro-image-preview';
23
- // NOTE: For simple generation, we might not need streamGenerateContent unless we want text streaming.
24
- // But let's stick to what worked for text, and adjust the body.
24
+ this.modelId = config.modelId || GeminiGenerator.MODELS.FLASH;
25
25
  this.apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/${this.modelId}:streamGenerateContent`;
26
26
  this.referenceMetadata = null;
27
+ this.references = [];
28
+ }
29
+
30
+ /**
31
+ * Add a reference image to be included in the next generate() call.
32
+ * @param {string|Buffer} image - Path to image file, URL, base64 data URI, or Buffer
33
+ * @param {string} [description] - How the model should use this image (e.g. 'use as background')
34
+ * @returns {GeminiGenerator} this instance for chaining
35
+ */
36
+ addReference(image, description = '') {
37
+ this.references.push({ image, description });
38
+ return this;
39
+ }
40
+
41
+ /**
42
+ * Remove all queued reference images.
43
+ * @returns {GeminiGenerator} this instance for chaining
44
+ */
45
+ clearReferences() {
46
+ this.references = [];
47
+ return this;
48
+ }
49
+
50
+ /**
51
+ * Switch to the Pro model
52
+ * @returns {GeminiGenerator} this instance for chaining
53
+ */
54
+ pro() {
55
+ this.modelId = GeminiGenerator.MODELS.PRO;
56
+ this.apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/${this.modelId}:streamGenerateContent`;
57
+ return this;
58
+ }
59
+
60
+ /**
61
+ * Switch to the Flash model
62
+ * @returns {GeminiGenerator} this instance for chaining
63
+ */
64
+ flash() {
65
+ this.modelId = GeminiGenerator.MODELS.FLASH;
66
+ this.apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/${this.modelId}:streamGenerateContent`;
67
+ return this;
27
68
  }
28
69
 
29
70
  /**
30
71
  * @param {string} prompt
31
72
  * @param {Object} [options]
32
- * @param {string|Buffer} [options.referenceImage] - Path to image file, base64 data URI, or Buffer
73
+ * @param {string|Buffer} [options.referenceImage] - Path to image file, base64 data URI, or Buffer (legacy single-image API)
33
74
  * @param {string} [options.numberOfImages] - Number of images to generate
34
75
  * @param {string} [options.quality] - Image quality: '1K', '2K', '4K'
35
76
  * @param {string} [options.aspectRatio] - Aspect ratio like '1:1', '16:9', etc.
@@ -56,6 +97,8 @@ class GeminiGenerator extends BaseGenerator {
56
97
  raw: result.raw
57
98
  };
58
99
 
100
+ this.references = [];
101
+
59
102
  return result;
60
103
  }
61
104
 
@@ -225,27 +268,42 @@ class GeminiGenerator extends BaseGenerator {
225
268
  }
226
269
  }
227
270
 
228
- async _generateSingleRequest(prompt, options) {
229
- const url = `${this.apiUrl}?key=${this.apiKey}`;
230
-
231
- // Build the parts array
271
+ /**
272
+ * Builds the API parts array from queued references + prompt.
273
+ * Each reference becomes [inlineData, text description (if any)], followed by the main prompt.
274
+ * @param {string} prompt
275
+ * @returns {Promise<Object[]>}
276
+ * @private
277
+ */
278
+ async _buildReferenceParts(prompt) {
232
279
  const parts = [];
233
280
 
234
- // Add reference image if provided
235
- if (options.referenceImage) {
236
- const imageData = await this._processReferenceImage(options.referenceImage);
281
+ for (const ref of this.references) {
282
+ const imageData = await this._processReferenceImage(ref.image);
237
283
  parts.push({
238
284
  inlineData: {
239
285
  mimeType: imageData.mimeType,
240
286
  data: imageData.data
241
287
  }
242
288
  });
289
+ if (ref.description) {
290
+ parts.push({ text: `Reference: ${ref.description}` });
291
+ }
292
+ }
293
+
294
+ parts.push({ text: prompt });
295
+ return parts;
296
+ }
297
+
298
+ async _generateSingleRequest(prompt, options) {
299
+ const url = `${this.apiUrl}?key=${this.apiKey}`;
300
+
301
+ // Legacy single-image API: promote to references for unified code path
302
+ if (options.referenceImage && this.references.length === 0) {
303
+ this.addReference(options.referenceImage);
243
304
  }
244
305
 
245
- // Add text prompt
246
- parts.push({
247
- text: prompt,
248
- });
306
+ const parts = await this._buildReferenceParts(prompt);
249
307
 
250
308
  // Simplified data structure to minimize conflicts
251
309
  const data = {
package/index.js CHANGED
@@ -2,5 +2,6 @@ const GeminiGenerator = require('./generators/GeminiGenerator');
2
2
 
3
3
  module.exports = {
4
4
  GeminiGenerator,
5
+ MODELS: GeminiGenerator.MODELS,
5
6
  };
6
7
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genmix",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "AI-powered image generator using Google Gemini API. Supports image generation from text prompts and image modification with reference images.",
5
5
  "license": "MIT",
6
6
  "author": "Martin Clasen",
@@ -8,6 +8,7 @@
8
8
  "nano",
9
9
  "banana",
10
10
  "pro",
11
+ "flash",
11
12
  "image-generator",
12
13
  "google-gemini",
13
14
  "gemini-api",
@@ -24,7 +25,7 @@
24
25
  "test": "echo \"Error: no test specified\" && exit 1"
25
26
  },
26
27
  "dependencies": {
27
- "axios": "^1.13.2",
28
+ "axios": "^1.13.6",
28
29
  "hash-factory": "^1.1.2",
29
30
  "sharp": "^0.33.5"
30
31
  }
@@ -0,0 +1,51 @@
1
+ ---
2
+ name: genmix
3
+ description: AI-powered image generator using Google Gemini API. Use this skill when the user asks to generate an image from text, modify an existing image with a reference, apply style transfer, or create an image based on a prompt.
4
+ ---
5
+
6
+ # GenMix Skill
7
+
8
+ # Instructions
9
+
10
+ ### Step 1: Install GenMix
11
+ If GenMix is not already installed in the project, install it:
12
+ ```bash
13
+ npm install genmix
14
+ ```
15
+ Ensure `GEMINI_API_KEY` is set in the `.env` file.
16
+
17
+ ### Step 2: Write the Generation Script
18
+ Write a Node.js script to use GenMix based on the user's request.
19
+
20
+ Example for generating a new image:
21
+ ```javascript
22
+ import { GeminiGenerator } from 'genmix';
23
+
24
+ const generator = new GeminiGenerator();
25
+ await generator.generate('Your prompt here', { quality: '2K' });
26
+ await generator.save({ directory: './output' });
27
+ ```
28
+
29
+ Example for modifying an image with a reference:
30
+ ```javascript
31
+ import { GeminiGenerator } from 'genmix';
32
+
33
+ const generator = new GeminiGenerator();
34
+ await generator
35
+ .addReference('./path/to/reference.jpg', 'Description of reference')
36
+ .generate('Your modification prompt here', { quality: '2K' });
37
+ await generator.save({ directory: './output' });
38
+ ```
39
+
40
+ ### Step 3: Execute the script
41
+ Run the script using Node.js to generate the image.
42
+
43
+ ## Examples
44
+
45
+ **Example 1: Generate a futuristic city**
46
+ User says: "Generate an image of a futuristic city"
47
+ Actions: Create a script using GenMix to generate the image with the prompt "A futuristic city with flying cars, cyberpunk style", run the script, and save the output.
48
+
49
+ **Example 2: Modify an image**
50
+ User says: "Make this portrait look like a watercolor painting"
51
+ Actions: Create a script using GenMix, add the portrait as a reference image, use the prompt "Transform this photo into a watercolor painting", run the script, and save the output.