opencode-nanobanana 0.1.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/.ralph-events.json +151 -0
- package/.ralph-last-branch +1 -0
- package/.ralph-monitor-state.json +7 -0
- package/.ralph-monitor.pid +1 -0
- package/.ralph-timing.json +26 -0
- package/README.md +708 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/platforms/android.d.ts +94 -0
- package/dist/platforms/android.d.ts.map +1 -0
- package/dist/platforms/android.js +123 -0
- package/dist/platforms/android.js.map +1 -0
- package/dist/platforms/ios.d.ts +51 -0
- package/dist/platforms/ios.d.ts.map +1 -0
- package/dist/platforms/ios.js +149 -0
- package/dist/platforms/ios.js.map +1 -0
- package/dist/platforms/macos.d.ts +33 -0
- package/dist/platforms/macos.d.ts.map +1 -0
- package/dist/platforms/macos.js +50 -0
- package/dist/platforms/macos.js.map +1 -0
- package/dist/platforms/watchos.d.ts +36 -0
- package/dist/platforms/watchos.d.ts.map +1 -0
- package/dist/platforms/watchos.js +113 -0
- package/dist/platforms/watchos.js.map +1 -0
- package/dist/platforms/web.d.ts +64 -0
- package/dist/platforms/web.d.ts.map +1 -0
- package/dist/platforms/web.js +96 -0
- package/dist/platforms/web.js.map +1 -0
- package/dist/providers/gemini.d.ts +41 -0
- package/dist/providers/gemini.d.ts.map +1 -0
- package/dist/providers/gemini.js +177 -0
- package/dist/providers/gemini.js.map +1 -0
- package/dist/tools/analyze/compare.d.ts +12 -0
- package/dist/tools/analyze/compare.d.ts.map +1 -0
- package/dist/tools/analyze/compare.js +83 -0
- package/dist/tools/analyze/compare.js.map +1 -0
- package/dist/tools/analyze/mockup.d.ts +12 -0
- package/dist/tools/analyze/mockup.d.ts.map +1 -0
- package/dist/tools/analyze/mockup.js +88 -0
- package/dist/tools/analyze/mockup.js.map +1 -0
- package/dist/tools/analyze/screenshot.d.ts +12 -0
- package/dist/tools/analyze/screenshot.d.ts.map +1 -0
- package/dist/tools/analyze/screenshot.js +61 -0
- package/dist/tools/analyze/screenshot.js.map +1 -0
- package/dist/tools/app-assets/app-icon.d.ts +9 -0
- package/dist/tools/app-assets/app-icon.d.ts.map +1 -0
- package/dist/tools/app-assets/app-icon.js +133 -0
- package/dist/tools/app-assets/app-icon.js.map +1 -0
- package/dist/tools/app-assets/device-mockup.d.ts +9 -0
- package/dist/tools/app-assets/device-mockup.d.ts.map +1 -0
- package/dist/tools/app-assets/device-mockup.js +139 -0
- package/dist/tools/app-assets/device-mockup.js.map +1 -0
- package/dist/tools/app-assets/launch-images.d.ts +3 -0
- package/dist/tools/app-assets/launch-images.d.ts.map +1 -0
- package/dist/tools/app-assets/launch-images.js +171 -0
- package/dist/tools/app-assets/launch-images.js.map +1 -0
- package/dist/tools/app-assets/resize-devices.d.ts +14 -0
- package/dist/tools/app-assets/resize-devices.d.ts.map +1 -0
- package/dist/tools/app-assets/resize-devices.js +296 -0
- package/dist/tools/app-assets/resize-devices.js.map +1 -0
- package/dist/tools/app-assets/screenshots.d.ts +14 -0
- package/dist/tools/app-assets/screenshots.d.ts.map +1 -0
- package/dist/tools/app-assets/screenshots.js +186 -0
- package/dist/tools/app-assets/screenshots.js.map +1 -0
- package/dist/tools/core/edit-image.d.ts +12 -0
- package/dist/tools/core/edit-image.d.ts.map +1 -0
- package/dist/tools/core/edit-image.js +102 -0
- package/dist/tools/core/edit-image.js.map +1 -0
- package/dist/tools/core/generate-image.d.ts +12 -0
- package/dist/tools/core/generate-image.d.ts.map +1 -0
- package/dist/tools/core/generate-image.js +96 -0
- package/dist/tools/core/generate-image.js.map +1 -0
- package/dist/tools/core/restore-image.d.ts +12 -0
- package/dist/tools/core/restore-image.d.ts.map +1 -0
- package/dist/tools/core/restore-image.js +104 -0
- package/dist/tools/core/restore-image.js.map +1 -0
- package/dist/tools/design/mockup-to-code.d.ts +3 -0
- package/dist/tools/design/mockup-to-code.d.ts.map +1 -0
- package/dist/tools/design/mockup-to-code.js +311 -0
- package/dist/tools/design/mockup-to-code.js.map +1 -0
- package/dist/tools/design/sketch-to-code.d.ts +3 -0
- package/dist/tools/design/sketch-to-code.d.ts.map +1 -0
- package/dist/tools/design/sketch-to-code.js +325 -0
- package/dist/tools/design/sketch-to-code.js.map +1 -0
- package/dist/tools/docs/architecture-diagram.d.ts +12 -0
- package/dist/tools/docs/architecture-diagram.d.ts.map +1 -0
- package/dist/tools/docs/architecture-diagram.js +179 -0
- package/dist/tools/docs/architecture-diagram.js.map +1 -0
- package/dist/tools/docs/readme-banner.d.ts +6 -0
- package/dist/tools/docs/readme-banner.d.ts.map +1 -0
- package/dist/tools/docs/readme-banner.js +108 -0
- package/dist/tools/docs/readme-banner.js.map +1 -0
- package/dist/tools/docs/sequence-diagram.d.ts +12 -0
- package/dist/tools/docs/sequence-diagram.d.ts.map +1 -0
- package/dist/tools/docs/sequence-diagram.js +161 -0
- package/dist/tools/docs/sequence-diagram.js.map +1 -0
- package/dist/tools/docs/social-preview.d.ts +11 -0
- package/dist/tools/docs/social-preview.d.ts.map +1 -0
- package/dist/tools/docs/social-preview.js +111 -0
- package/dist/tools/docs/social-preview.js.map +1 -0
- package/dist/tools/video/extend-video.d.ts +14 -0
- package/dist/tools/video/extend-video.d.ts.map +1 -0
- package/dist/tools/video/extend-video.js +39 -0
- package/dist/tools/video/extend-video.js.map +1 -0
- package/dist/tools/video/generate-video.d.ts +14 -0
- package/dist/tools/video/generate-video.d.ts.map +1 -0
- package/dist/tools/video/generate-video.js +39 -0
- package/dist/tools/video/generate-video.js.map +1 -0
- package/dist/tools/video/image-to-video.d.ts +15 -0
- package/dist/tools/video/image-to-video.d.ts.map +1 -0
- package/dist/tools/video/image-to-video.js +42 -0
- package/dist/tools/video/image-to-video.js.map +1 -0
- package/dist/tools/video/storyboard-video.d.ts +91 -0
- package/dist/tools/video/storyboard-video.d.ts.map +1 -0
- package/dist/tools/video/storyboard-video.js +230 -0
- package/dist/tools/video/storyboard-video.js.map +1 -0
- package/dist/utils/ffmpeg.d.ts +30 -0
- package/dist/utils/ffmpeg.d.ts.map +1 -0
- package/dist/utils/ffmpeg.js +205 -0
- package/dist/utils/ffmpeg.js.map +1 -0
- package/dist/utils/file-handler.d.ts +7 -0
- package/dist/utils/file-handler.d.ts.map +1 -0
- package/dist/utils/file-handler.js +10 -0
- package/dist/utils/file-handler.js.map +1 -0
- package/dist/utils/image-processing.d.ts +7 -0
- package/dist/utils/image-processing.d.ts.map +1 -0
- package/dist/utils/image-processing.js +10 -0
- package/dist/utils/image-processing.js.map +1 -0
- package/docs/PLUGIN-VERIFICATION.md +182 -0
- package/logs/notifications.jsonl +46 -0
- package/package.json +61 -0
- package/prd.json +216 -0
- package/progress.txt +145 -0
- package/ralph-report.html +297 -0
- package/src/index.ts +23 -0
- package/src/platforms/android/.gitkeep +0 -0
- package/src/platforms/ios/.gitkeep +0 -0
- package/src/platforms/web/.gitkeep +0 -0
- package/src/providers/.gitkeep +0 -0
- package/src/providers/gemini.ts +288 -0
- package/src/tools/core/.gitkeep +0 -0
- package/src/tools/platform/.gitkeep +0 -0
- package/src/tools/video/extend-video.ts +71 -0
- package/src/tools/video/generate-video.ts +70 -0
- package/src/tools/video/image-to-video.ts +76 -0
- package/src/tools/video/storyboard-video.ts +325 -0
- package/src/utils/.gitkeep +0 -0
- package/src/utils/ffmpeg.ts +266 -0
- package/src/utils/file-handler.ts +10 -0
- package/src/utils/image-processing.ts +10 -0
- package/templates/.gitkeep +0 -0
- package/test-analyze-screenshot.ts +50 -0
- package/test-app-icons.ts +55 -0
- package/test-cat-sunset.ts +30 -0
- package/test-full-plugin.ts +88 -0
- package/test-icon-gen.ts +30 -0
- package/test-output/test-edit.png +0 -0
- package/test-output/test-generate.png +0 -0
- package/test-output/test-video.mp4 +0 -0
- package/test-plugin-load.js +45 -0
- package/test-princess-emma-continue.ts +35 -0
- package/test-princess-emma-full.ts +38 -0
- package/test-princess-emma-short.ts +32 -0
- package/test-princess-emma-with-reference.ts +34 -0
- package/test-princess-emma.ts +38 -0
- package/test-product-ad.ts +66 -0
- package/test-ralph-droid.ts +30 -0
- package/test-social-preview.ts +61 -0
- package/test-veo31-live.ts +187 -0
- package/test-video-gen.ts +40 -0
- package/test-video-veo.ts +73 -0
- package/test-zurich-video.ts +64 -0
- package/tests/.gitkeep +0 -0
- package/tests/providers/gemini.test.ts +388 -0
- package/tests/utils/ffmpeg.test.ts +328 -0
- package/tests/video/storyboard.test.ts +469 -0
- package/tsconfig.json +25 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate App Screenshots Tool
|
|
3
|
+
*
|
|
4
|
+
* Creates App Store screenshots for iOS and iPad devices.
|
|
5
|
+
* Supports two modes:
|
|
6
|
+
* - 'image': Resizes existing image to all device dimensions
|
|
7
|
+
* - 'code': Generates visuals from code/design description via Gemini
|
|
8
|
+
*/
|
|
9
|
+
import { tool } from '@opencode-ai/plugin/tool';
|
|
10
|
+
import { GeminiProvider } from '../../providers/gemini.js';
|
|
11
|
+
import { loadImage, saveImage, ensureDirectory } from '../../utils/file-handler.js';
|
|
12
|
+
import { resize } from '../../utils/image-processing.js';
|
|
13
|
+
import { IOS_SCREENSHOT_SIZES } from '../../platforms/ios.js';
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
/**
|
|
16
|
+
* Tool args schema
|
|
17
|
+
*/
|
|
18
|
+
const generateScreenshotsArgs = {
|
|
19
|
+
source: tool.schema.string().describe('Source content: image path for "image" mode, or description for "code" mode'),
|
|
20
|
+
sourceType: tool.schema.enum(['image', 'code']).describe('Type of source: "image" to resize existing image, or "code" to generate from description'),
|
|
21
|
+
platforms: tool.schema.array(tool.schema.enum(['ios', 'ipad'])).optional().describe('Target platforms. Defaults to ["ios", "ipad"]'),
|
|
22
|
+
devices: tool.schema.array(tool.schema.string()).optional().describe('Specific devices to target (e.g., ["6.9-inch", "iPad-12.9"]). Defaults to all devices'),
|
|
23
|
+
addDeviceFrame: tool.schema.boolean().optional().describe('Add device frame to screenshots (future feature). Defaults to false'),
|
|
24
|
+
outputDir: tool.schema.string().optional().describe('Custom output directory. Defaults to "./generated-assets/screenshots"')
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Filter screenshot sizes by platform
|
|
28
|
+
*/
|
|
29
|
+
function filterByPlatform(sizes, platforms) {
|
|
30
|
+
return sizes.filter(size => {
|
|
31
|
+
const isPhone = size.device.includes('iPhone');
|
|
32
|
+
const isTablet = size.device.includes('iPad');
|
|
33
|
+
if (platforms.includes('ios') && isPhone)
|
|
34
|
+
return true;
|
|
35
|
+
if (platforms.includes('ipad') && isTablet)
|
|
36
|
+
return true;
|
|
37
|
+
return false;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Filter screenshot sizes by device names
|
|
42
|
+
*/
|
|
43
|
+
function filterByDevices(sizes, deviceNames) {
|
|
44
|
+
return sizes.filter(size => deviceNames.includes(size.name));
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Tool definition for generate_app_screenshots
|
|
48
|
+
*/
|
|
49
|
+
export const generateScreenshotsTool = tool({
|
|
50
|
+
description: 'Generate App Store screenshots for iOS and iPad devices. Supports resizing existing images or generating from code/design descriptions.',
|
|
51
|
+
args: generateScreenshotsArgs,
|
|
52
|
+
async execute(args, _context) {
|
|
53
|
+
try {
|
|
54
|
+
const { source, sourceType, platforms = ['ios', 'ipad'], devices, addDeviceFrame = false, outputDir = './generated-assets/screenshots' } = args;
|
|
55
|
+
// Ensure output directory exists
|
|
56
|
+
await ensureDirectory(outputDir);
|
|
57
|
+
// Filter screenshot sizes based on platforms and devices
|
|
58
|
+
let targetSizes = IOS_SCREENSHOT_SIZES;
|
|
59
|
+
if (platforms.length > 0) {
|
|
60
|
+
targetSizes = filterByPlatform(targetSizes, platforms);
|
|
61
|
+
}
|
|
62
|
+
if (devices && devices.length > 0) {
|
|
63
|
+
targetSizes = filterByDevices(targetSizes, devices);
|
|
64
|
+
}
|
|
65
|
+
if (targetSizes.length === 0) {
|
|
66
|
+
return [
|
|
67
|
+
'✗ Error: No matching devices found',
|
|
68
|
+
'',
|
|
69
|
+
`Platforms: ${platforms.join(', ')}`,
|
|
70
|
+
devices ? `Devices: ${devices.join(', ')}` : '',
|
|
71
|
+
'',
|
|
72
|
+
'Available devices:',
|
|
73
|
+
...IOS_SCREENSHOT_SIZES.map(s => ` - ${s.name} (${s.device})`)
|
|
74
|
+
].join('\n');
|
|
75
|
+
}
|
|
76
|
+
const savedPaths = [];
|
|
77
|
+
if (sourceType === 'image') {
|
|
78
|
+
// Mode 1: Resize existing image to all device dimensions
|
|
79
|
+
const sourceBuffer = await loadImage(source);
|
|
80
|
+
for (const size of targetSizes) {
|
|
81
|
+
// Create device-specific directory
|
|
82
|
+
const deviceDir = path.join(outputDir, size.name);
|
|
83
|
+
await ensureDirectory(deviceDir);
|
|
84
|
+
// Resize image to device dimensions
|
|
85
|
+
// Using 'cover' fit mode to fill the screen without letterboxing
|
|
86
|
+
const resizedBuffer = await resize(sourceBuffer, size.width, size.height, {
|
|
87
|
+
fit: 'cover',
|
|
88
|
+
position: 'center'
|
|
89
|
+
});
|
|
90
|
+
// Save screenshot
|
|
91
|
+
const filename = `screenshot-${size.name}.png`;
|
|
92
|
+
const filepath = path.join(deviceDir, filename);
|
|
93
|
+
// Save using our file handler (it returns the path)
|
|
94
|
+
await saveImage(resizedBuffer, deviceDir, `screenshot-${size.name}`, 0);
|
|
95
|
+
savedPaths.push(filepath);
|
|
96
|
+
}
|
|
97
|
+
return [
|
|
98
|
+
`✓ Successfully resized image to ${targetSizes.length} device screenshot(s)`,
|
|
99
|
+
'',
|
|
100
|
+
`Source: ${source}`,
|
|
101
|
+
`Mode: Resize from image`,
|
|
102
|
+
'',
|
|
103
|
+
'Generated screenshots:',
|
|
104
|
+
...savedPaths.map((p, i) => ` ${i + 1}. ${p}`),
|
|
105
|
+
'',
|
|
106
|
+
`Output directory: ${outputDir}`
|
|
107
|
+
].join('\n');
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
// Mode 2: Generate visuals from code/design description via Gemini
|
|
111
|
+
const provider = new GeminiProvider();
|
|
112
|
+
for (const size of targetSizes) {
|
|
113
|
+
// Create device-specific directory
|
|
114
|
+
const deviceDir = path.join(outputDir, size.name);
|
|
115
|
+
await ensureDirectory(deviceDir);
|
|
116
|
+
// Generate screenshot for this specific device size
|
|
117
|
+
const prompt = [
|
|
118
|
+
source,
|
|
119
|
+
'',
|
|
120
|
+
`Generate an App Store screenshot for ${size.device} (${size.width}x${size.height}px).`,
|
|
121
|
+
'The screenshot should be visually polished and ready for App Store submission.',
|
|
122
|
+
addDeviceFrame ? 'Include the device frame in the screenshot.' : ''
|
|
123
|
+
].filter(Boolean).join('\n');
|
|
124
|
+
const imageBuffers = await provider.generateImage(prompt, {
|
|
125
|
+
aspectRatio: size.width > size.height ? '9:16' : '16:9',
|
|
126
|
+
count: 1
|
|
127
|
+
});
|
|
128
|
+
const buffer = imageBuffers[0];
|
|
129
|
+
if (!buffer) {
|
|
130
|
+
throw new Error(`Failed to generate screenshot for ${size.device}`);
|
|
131
|
+
}
|
|
132
|
+
// Resize to exact device dimensions
|
|
133
|
+
const resizedBuffer = await resize(buffer, size.width, size.height, {
|
|
134
|
+
fit: 'cover',
|
|
135
|
+
position: 'center'
|
|
136
|
+
});
|
|
137
|
+
// Save screenshot
|
|
138
|
+
const filepath = await saveImage(resizedBuffer, deviceDir, `screenshot-${size.name}`, 0);
|
|
139
|
+
savedPaths.push(filepath);
|
|
140
|
+
}
|
|
141
|
+
return [
|
|
142
|
+
`✓ Successfully generated ${targetSizes.length} screenshot(s) from description`,
|
|
143
|
+
'',
|
|
144
|
+
`Description: "${source}"`,
|
|
145
|
+
`Mode: Generated via Gemini`,
|
|
146
|
+
'',
|
|
147
|
+
'Generated screenshots:',
|
|
148
|
+
...savedPaths.map((p, i) => ` ${i + 1}. ${p}`),
|
|
149
|
+
'',
|
|
150
|
+
`Output directory: ${outputDir}`
|
|
151
|
+
].join('\n');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
// Handle errors with user-friendly messages
|
|
156
|
+
if (error instanceof Error) {
|
|
157
|
+
if (error.message.includes('GEMINI_API_KEY')) {
|
|
158
|
+
return [
|
|
159
|
+
'✗ Error: Gemini API key not found',
|
|
160
|
+
'',
|
|
161
|
+
'Please set your GEMINI_API_KEY environment variable.',
|
|
162
|
+
'Get your API key at: https://makersuite.google.com/app/apikey'
|
|
163
|
+
].join('\n');
|
|
164
|
+
}
|
|
165
|
+
if (error.message.includes('ENOENT') || error.message.includes('no such file')) {
|
|
166
|
+
return [
|
|
167
|
+
'✗ Error: Source image file not found',
|
|
168
|
+
'',
|
|
169
|
+
`Path: ${args.source}`,
|
|
170
|
+
'',
|
|
171
|
+
'Please check the file path and try again.'
|
|
172
|
+
].join('\n');
|
|
173
|
+
}
|
|
174
|
+
return [
|
|
175
|
+
'✗ Screenshot generation failed',
|
|
176
|
+
'',
|
|
177
|
+
`Error: ${error.message}`,
|
|
178
|
+
'',
|
|
179
|
+
'Please check your inputs and try again.'
|
|
180
|
+
].join('\n');
|
|
181
|
+
}
|
|
182
|
+
return '✗ Screenshot generation failed with an unknown error';
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
//# sourceMappingURL=screenshots.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screenshots.js","sourceRoot":"","sources":["../../../src/tools/app-assets/screenshots.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edit Image Tool
|
|
3
|
+
*
|
|
4
|
+
* Modifies existing images using natural language instructions with Google Gemini's vision and generation capabilities.
|
|
5
|
+
* Loads a source image, sends it with edit instructions to Gemini, and saves the result.
|
|
6
|
+
*/
|
|
7
|
+
import { type ToolDefinition } from '@opencode-ai/plugin/tool';
|
|
8
|
+
/**
|
|
9
|
+
* Tool definition for edit_image
|
|
10
|
+
*/
|
|
11
|
+
export declare const editImageTool: ToolDefinition;
|
|
12
|
+
//# sourceMappingURL=edit-image.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit-image.d.ts","sourceRoot":"","sources":["../../../src/tools/core/edit-image.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAgBrE;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,cA4F1B,CAAC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edit Image Tool
|
|
3
|
+
*
|
|
4
|
+
* Modifies existing images using natural language instructions with Google Gemini's vision and generation capabilities.
|
|
5
|
+
* Loads a source image, sends it with edit instructions to Gemini, and saves the result.
|
|
6
|
+
*/
|
|
7
|
+
import { tool } from '@opencode-ai/plugin/tool';
|
|
8
|
+
import { GeminiProvider } from '../../providers/gemini.js';
|
|
9
|
+
import { loadImage, saveImage, getOutputDir } from '../../utils/file-handler.js';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
/**
|
|
12
|
+
* Tool args schema
|
|
13
|
+
*/
|
|
14
|
+
const editImageArgs = {
|
|
15
|
+
imagePath: tool.schema.string().describe('Path to the source image file to edit'),
|
|
16
|
+
editPrompt: tool.schema.string().describe('Natural language instructions for how to edit the image'),
|
|
17
|
+
outputPath: tool.schema.string()
|
|
18
|
+
.optional()
|
|
19
|
+
.describe('Custom output directory path. Defaults to "./generated-assets"')
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Tool definition for edit_image
|
|
23
|
+
*/
|
|
24
|
+
export const editImageTool = tool({
|
|
25
|
+
description: 'Edit an existing image using natural language instructions. Loads a source image, applies edits via Gemini vision AI, and saves the result with an "_edited" suffix.',
|
|
26
|
+
args: editImageArgs,
|
|
27
|
+
async execute(args, _context) {
|
|
28
|
+
try {
|
|
29
|
+
// Initialize Gemini provider
|
|
30
|
+
const provider = new GeminiProvider();
|
|
31
|
+
// Extract parameters
|
|
32
|
+
const { imagePath, editPrompt, outputPath } = args;
|
|
33
|
+
// Determine output directory
|
|
34
|
+
const outputDir = outputPath || getOutputDir();
|
|
35
|
+
// Load source image
|
|
36
|
+
let sourceBuffer;
|
|
37
|
+
try {
|
|
38
|
+
sourceBuffer = await loadImage(imagePath);
|
|
39
|
+
}
|
|
40
|
+
catch (loadError) {
|
|
41
|
+
return [
|
|
42
|
+
'✗ Failed to load source image',
|
|
43
|
+
'',
|
|
44
|
+
`Error: ${loadError instanceof Error ? loadError.message : 'Unknown error'}`,
|
|
45
|
+
'',
|
|
46
|
+
`Image path: ${imagePath}`,
|
|
47
|
+
'Please check that the file exists and is accessible.'
|
|
48
|
+
].join('\n');
|
|
49
|
+
}
|
|
50
|
+
// Edit image using Gemini
|
|
51
|
+
const editedBuffer = await provider.editImage(sourceBuffer, editPrompt);
|
|
52
|
+
// Generate output filename with '_edited' suffix
|
|
53
|
+
const parsedPath = path.parse(imagePath);
|
|
54
|
+
const baseFilename = parsedPath.name;
|
|
55
|
+
const editedPrompt = `${baseFilename}_edited`;
|
|
56
|
+
// Save edited image
|
|
57
|
+
const savedPath = await saveImage(editedBuffer, outputDir, editedPrompt, 0);
|
|
58
|
+
// Build success message
|
|
59
|
+
return [
|
|
60
|
+
`✓ Successfully edited image`,
|
|
61
|
+
``,
|
|
62
|
+
`Source: ${imagePath}`,
|
|
63
|
+
`Edit instruction: "${editPrompt}"`,
|
|
64
|
+
``,
|
|
65
|
+
`Saved to: ${savedPath}`,
|
|
66
|
+
`Output directory: ${outputDir}`
|
|
67
|
+
].join('\n');
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
// Handle errors with user-friendly messages
|
|
71
|
+
if (error instanceof Error) {
|
|
72
|
+
if (error.message.includes('GEMINI_API_KEY')) {
|
|
73
|
+
return [
|
|
74
|
+
'✗ Error: Gemini API key not found',
|
|
75
|
+
'',
|
|
76
|
+
'Please set your GEMINI_API_KEY environment variable.',
|
|
77
|
+
'Get your API key at: https://makersuite.google.com/app/apikey'
|
|
78
|
+
].join('\n');
|
|
79
|
+
}
|
|
80
|
+
if (error.message.includes('No edited image was returned')) {
|
|
81
|
+
return [
|
|
82
|
+
'✗ Image editing failed',
|
|
83
|
+
'',
|
|
84
|
+
'Gemini did not return an edited image.',
|
|
85
|
+
'This may happen if the edit prompt is unclear or not supported.',
|
|
86
|
+
'',
|
|
87
|
+
'Try rephrasing your edit instruction with more specific details.'
|
|
88
|
+
].join('\n');
|
|
89
|
+
}
|
|
90
|
+
return [
|
|
91
|
+
'✗ Image editing failed',
|
|
92
|
+
'',
|
|
93
|
+
`Error: ${error.message}`,
|
|
94
|
+
'',
|
|
95
|
+
'Please check your image path and edit prompt, then try again.'
|
|
96
|
+
].join('\n');
|
|
97
|
+
}
|
|
98
|
+
return '✗ Image editing failed with an unknown error';
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
//# sourceMappingURL=edit-image.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit-image.js","sourceRoot":"","sources":["../../../src/tools/core/edit-image.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate Image Tool
|
|
3
|
+
*
|
|
4
|
+
* Creates images from text prompts using Google Gemini's Imagen model.
|
|
5
|
+
* Supports batch generation, aspect ratio control, and custom output paths.
|
|
6
|
+
*/
|
|
7
|
+
import { type ToolDefinition } from '@opencode-ai/plugin/tool';
|
|
8
|
+
/**
|
|
9
|
+
* Tool definition for generate_image
|
|
10
|
+
*/
|
|
11
|
+
export declare const generateImageTool: ToolDefinition;
|
|
12
|
+
//# sourceMappingURL=generate-image.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-image.d.ts","sourceRoot":"","sources":["../../../src/tools/core/generate-image.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAuBrE;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,cAiF9B,CAAC"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate Image Tool
|
|
3
|
+
*
|
|
4
|
+
* Creates images from text prompts using Google Gemini's Imagen model.
|
|
5
|
+
* Supports batch generation, aspect ratio control, and custom output paths.
|
|
6
|
+
*/
|
|
7
|
+
import { tool } from '@opencode-ai/plugin/tool';
|
|
8
|
+
import { GeminiProvider } from '../../providers/gemini.js';
|
|
9
|
+
import { saveImage, getOutputDir } from '../../utils/file-handler.js';
|
|
10
|
+
/**
|
|
11
|
+
* Tool args schema
|
|
12
|
+
*/
|
|
13
|
+
const generateImageArgs = {
|
|
14
|
+
prompt: tool.schema.string().describe('Text description of the image to generate'),
|
|
15
|
+
aspectRatio: tool.schema.string()
|
|
16
|
+
.optional()
|
|
17
|
+
.describe('Aspect ratio for the image (e.g., "1:1", "16:9", "9:16"). Defaults to "1:1"'),
|
|
18
|
+
outputPath: tool.schema.string()
|
|
19
|
+
.optional()
|
|
20
|
+
.describe('Custom output directory path. Defaults to "./generated-assets"'),
|
|
21
|
+
count: tool.schema.number()
|
|
22
|
+
.int()
|
|
23
|
+
.min(1)
|
|
24
|
+
.max(8)
|
|
25
|
+
.optional()
|
|
26
|
+
.describe('Number of images to generate (1-8). Defaults to 1')
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Tool definition for generate_image
|
|
30
|
+
*/
|
|
31
|
+
export const generateImageTool = tool({
|
|
32
|
+
description: 'Generate images from text prompts using Gemini Imagen. Supports batch generation (1-8 images), aspect ratio control, and custom output paths.',
|
|
33
|
+
args: generateImageArgs,
|
|
34
|
+
async execute(args, _context) {
|
|
35
|
+
try {
|
|
36
|
+
// Initialize Gemini provider
|
|
37
|
+
const provider = new GeminiProvider();
|
|
38
|
+
// Extract parameters with defaults
|
|
39
|
+
const { prompt, aspectRatio = '1:1', count = 1, outputPath } = args;
|
|
40
|
+
// Determine output directory
|
|
41
|
+
const outputDir = outputPath || getOutputDir();
|
|
42
|
+
// Generate images using Gemini
|
|
43
|
+
const imageBuffers = await provider.generateImage(prompt, {
|
|
44
|
+
aspectRatio,
|
|
45
|
+
count
|
|
46
|
+
});
|
|
47
|
+
// Save all generated images
|
|
48
|
+
const savedPaths = [];
|
|
49
|
+
for (let i = 0; i < imageBuffers.length; i++) {
|
|
50
|
+
const buffer = imageBuffers[i];
|
|
51
|
+
if (!buffer) {
|
|
52
|
+
throw new Error(`Failed to generate image ${i + 1}`);
|
|
53
|
+
}
|
|
54
|
+
const filepath = await saveImage(buffer, outputDir, prompt, i);
|
|
55
|
+
savedPaths.push(filepath);
|
|
56
|
+
}
|
|
57
|
+
// Build success message
|
|
58
|
+
const imageWord = count === 1 ? 'image' : 'images';
|
|
59
|
+
const pathList = savedPaths.map((p, i) => ` ${i + 1}. ${p}`).join('\n');
|
|
60
|
+
return [
|
|
61
|
+
`✓ Successfully generated ${count} ${imageWord} from prompt: "${prompt}"`,
|
|
62
|
+
``,
|
|
63
|
+
`Saved to:`,
|
|
64
|
+
pathList,
|
|
65
|
+
``,
|
|
66
|
+
`Aspect ratio: ${aspectRatio}`,
|
|
67
|
+
`Output directory: ${outputDir}`
|
|
68
|
+
].join('\n');
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
// Handle errors with user-friendly messages
|
|
72
|
+
if (error instanceof Error) {
|
|
73
|
+
if (error.message.includes('GEMINI_API_KEY')) {
|
|
74
|
+
return [
|
|
75
|
+
'✗ Error: Gemini API key not found',
|
|
76
|
+
'',
|
|
77
|
+
'Please set your GEMINI_API_KEY environment variable.',
|
|
78
|
+
'Get your API key at: https://makersuite.google.com/app/apikey'
|
|
79
|
+
].join('\n');
|
|
80
|
+
}
|
|
81
|
+
if (error.message.includes('Count must be between')) {
|
|
82
|
+
return `✗ Error: ${error.message}`;
|
|
83
|
+
}
|
|
84
|
+
return [
|
|
85
|
+
'✗ Image generation failed',
|
|
86
|
+
'',
|
|
87
|
+
`Error: ${error.message}`,
|
|
88
|
+
'',
|
|
89
|
+
'Please check your prompt and try again.'
|
|
90
|
+
].join('\n');
|
|
91
|
+
}
|
|
92
|
+
return '✗ Image generation failed with an unknown error';
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
//# sourceMappingURL=generate-image.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-image.js","sourceRoot":"","sources":["../../../src/tools/core/generate-image.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Restore Image Tool
|
|
3
|
+
*
|
|
4
|
+
* Enhances or repairs damaged images using Google Gemini's vision and generation capabilities.
|
|
5
|
+
* Loads a source image, applies restoration/enhancement, and saves the result.
|
|
6
|
+
*/
|
|
7
|
+
import { type ToolDefinition } from '@opencode-ai/plugin/tool';
|
|
8
|
+
/**
|
|
9
|
+
* Tool definition for restore_image
|
|
10
|
+
*/
|
|
11
|
+
export declare const restoreImageTool: ToolDefinition;
|
|
12
|
+
//# sourceMappingURL=restore-image.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restore-image.d.ts","sourceRoot":"","sources":["../../../src/tools/core/restore-image.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAkBrE;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,cA4F7B,CAAC"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Restore Image Tool
|
|
3
|
+
*
|
|
4
|
+
* Enhances or repairs damaged images using Google Gemini's vision and generation capabilities.
|
|
5
|
+
* Loads a source image, applies restoration/enhancement, and saves the result.
|
|
6
|
+
*/
|
|
7
|
+
import { tool } from '@opencode-ai/plugin/tool';
|
|
8
|
+
import { GeminiProvider } from '../../providers/gemini.js';
|
|
9
|
+
import { loadImage, saveImage, getOutputDir } from '../../utils/file-handler.js';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
/**
|
|
12
|
+
* Tool args schema
|
|
13
|
+
*/
|
|
14
|
+
const restoreImageArgs = {
|
|
15
|
+
imagePath: tool.schema.string().describe('Path to the source image file to restore'),
|
|
16
|
+
instructions: tool.schema.string()
|
|
17
|
+
.optional()
|
|
18
|
+
.describe('Optional custom restoration instructions. Defaults to "restore and enhance this image"'),
|
|
19
|
+
outputPath: tool.schema.string()
|
|
20
|
+
.optional()
|
|
21
|
+
.describe('Custom output directory path. Defaults to "./generated-assets"')
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Tool definition for restore_image
|
|
25
|
+
*/
|
|
26
|
+
export const restoreImageTool = tool({
|
|
27
|
+
description: 'Restore and enhance an image to improve quality, remove damage, or repair artifacts. Uses Gemini vision AI to intelligently restore images and saves the result with a "_restored" suffix.',
|
|
28
|
+
args: restoreImageArgs,
|
|
29
|
+
async execute(args, _context) {
|
|
30
|
+
try {
|
|
31
|
+
// Initialize Gemini provider
|
|
32
|
+
const provider = new GeminiProvider();
|
|
33
|
+
// Extract parameters
|
|
34
|
+
const { imagePath, instructions = 'restore and enhance this image', outputPath } = args;
|
|
35
|
+
// Determine output directory
|
|
36
|
+
const outputDir = outputPath || getOutputDir();
|
|
37
|
+
// Load source image
|
|
38
|
+
let sourceBuffer;
|
|
39
|
+
try {
|
|
40
|
+
sourceBuffer = await loadImage(imagePath);
|
|
41
|
+
}
|
|
42
|
+
catch (loadError) {
|
|
43
|
+
return [
|
|
44
|
+
'✗ Failed to load source image',
|
|
45
|
+
'',
|
|
46
|
+
`Error: ${loadError instanceof Error ? loadError.message : 'Unknown error'}`,
|
|
47
|
+
'',
|
|
48
|
+
`Image path: ${imagePath}`,
|
|
49
|
+
'Please check that the file exists and is accessible.'
|
|
50
|
+
].join('\n');
|
|
51
|
+
}
|
|
52
|
+
// Restore image using Gemini editImage method
|
|
53
|
+
const restoredBuffer = await provider.editImage(sourceBuffer, instructions);
|
|
54
|
+
// Generate output filename with '_restored' suffix
|
|
55
|
+
const parsedPath = path.parse(imagePath);
|
|
56
|
+
const baseFilename = parsedPath.name;
|
|
57
|
+
const restoredPrompt = `${baseFilename}_restored`;
|
|
58
|
+
// Save restored image
|
|
59
|
+
const savedPath = await saveImage(restoredBuffer, outputDir, restoredPrompt, 0);
|
|
60
|
+
// Build success message
|
|
61
|
+
return [
|
|
62
|
+
`✓ Successfully restored image`,
|
|
63
|
+
``,
|
|
64
|
+
`Source: ${imagePath}`,
|
|
65
|
+
`Restoration instruction: "${instructions}"`,
|
|
66
|
+
``,
|
|
67
|
+
`Saved to: ${savedPath}`,
|
|
68
|
+
`Output directory: ${outputDir}`
|
|
69
|
+
].join('\n');
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
// Handle errors with user-friendly messages
|
|
73
|
+
if (error instanceof Error) {
|
|
74
|
+
if (error.message.includes('GEMINI_API_KEY')) {
|
|
75
|
+
return [
|
|
76
|
+
'✗ Error: Gemini API key not found',
|
|
77
|
+
'',
|
|
78
|
+
'Please set your GEMINI_API_KEY environment variable.',
|
|
79
|
+
'Get your API key at: https://makersuite.google.com/app/apikey'
|
|
80
|
+
].join('\n');
|
|
81
|
+
}
|
|
82
|
+
if (error.message.includes('No edited image was returned')) {
|
|
83
|
+
return [
|
|
84
|
+
'✗ Image restoration failed',
|
|
85
|
+
'',
|
|
86
|
+
'Gemini did not return a restored image.',
|
|
87
|
+
'This may happen if the image cannot be processed or the format is unsupported.',
|
|
88
|
+
'',
|
|
89
|
+
'Try using a different image format (PNG, JPEG) or check if the image is corrupted.'
|
|
90
|
+
].join('\n');
|
|
91
|
+
}
|
|
92
|
+
return [
|
|
93
|
+
'✗ Image restoration failed',
|
|
94
|
+
'',
|
|
95
|
+
`Error: ${error.message}`,
|
|
96
|
+
'',
|
|
97
|
+
'Please check your image path and try again.'
|
|
98
|
+
].join('\n');
|
|
99
|
+
}
|
|
100
|
+
return '✗ Image restoration failed with an unknown error';
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
//# sourceMappingURL=restore-image.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restore-image.js","sourceRoot":"","sources":["../../../src/tools/core/restore-image.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mockup-to-code.d.ts","sourceRoot":"","sources":["../../../src/tools/design/mockup-to-code.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAIrE,eAAO,MAAM,gBAAgB,EAAE,cA4C7B,CAAC"}
|