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,171 @@
|
|
|
1
|
+
import { tool } from '@opencode-ai/plugin/tool';
|
|
2
|
+
import { GeminiProvider } from '../../providers/gemini.js';
|
|
3
|
+
import { loadImage, saveImage, ensureDirectory } from '../../utils/file-handler.js';
|
|
4
|
+
import { resize } from '../../utils/image-processing.js';
|
|
5
|
+
import { IOS_SCREENSHOT_SIZES } from '../../platforms/ios.js';
|
|
6
|
+
import { ANDROID_SCREENSHOT_SIZES } from '../../platforms/android.js';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import * as fs from 'fs/promises';
|
|
9
|
+
const args = {
|
|
10
|
+
design: tool.schema.string().describe('Text description of splash screen design OR path to existing image file'),
|
|
11
|
+
platforms: tool.schema.array(tool.schema.enum(['ios', 'android'])).optional().describe('Target platforms (default: both)'),
|
|
12
|
+
includeAllSizes: tool.schema.boolean().optional().describe('Generate for all device sizes (default: true)'),
|
|
13
|
+
outputDir: tool.schema.string().optional().describe('Custom output directory (default: generated-assets/launch-images)'),
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Determines if the design input is a file path or a text description
|
|
17
|
+
*/
|
|
18
|
+
function isFilePath(design) {
|
|
19
|
+
// Check if it's a valid path pattern (has extension or path separators)
|
|
20
|
+
return design.includes('/') || design.includes('\\') || /\.(png|jpg|jpeg|webp)$/i.test(design);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get all launch image sizes for specified platforms
|
|
24
|
+
*/
|
|
25
|
+
function getLaunchImageSizes(platforms) {
|
|
26
|
+
const sizes = [];
|
|
27
|
+
if (platforms.includes('ios')) {
|
|
28
|
+
// Use iOS screenshot sizes as launch image sizes (same dimensions)
|
|
29
|
+
IOS_SCREENSHOT_SIZES.forEach((size) => {
|
|
30
|
+
sizes.push({
|
|
31
|
+
width: size.width,
|
|
32
|
+
height: size.height,
|
|
33
|
+
name: size.device,
|
|
34
|
+
platform: 'ios',
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
if (platforms.includes('android')) {
|
|
39
|
+
// Use Android screenshot sizes as launch image sizes
|
|
40
|
+
ANDROID_SCREENSHOT_SIZES.forEach((size) => {
|
|
41
|
+
sizes.push({
|
|
42
|
+
width: size.width,
|
|
43
|
+
height: size.height,
|
|
44
|
+
name: size.device,
|
|
45
|
+
platform: 'android',
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return sizes;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Generate launch images from text description using Gemini
|
|
53
|
+
*/
|
|
54
|
+
async function generateFromPrompt(design, sizes, outputDir) {
|
|
55
|
+
const gemini = new GeminiProvider();
|
|
56
|
+
const generatedPaths = [];
|
|
57
|
+
// Group sizes by aspect ratio to minimize API calls
|
|
58
|
+
const aspectRatioGroups = new Map();
|
|
59
|
+
for (const size of sizes) {
|
|
60
|
+
const aspectRatio = size.width / size.height;
|
|
61
|
+
const key = aspectRatio < 1 ? '9:16' : '16:9'; // Portrait or landscape
|
|
62
|
+
if (!aspectRatioGroups.has(key)) {
|
|
63
|
+
aspectRatioGroups.set(key, []);
|
|
64
|
+
}
|
|
65
|
+
aspectRatioGroups.get(key).push(size);
|
|
66
|
+
}
|
|
67
|
+
// Generate one master image per aspect ratio, then resize
|
|
68
|
+
for (const [aspectRatio, groupSizes] of aspectRatioGroups) {
|
|
69
|
+
// Generate master splash screen
|
|
70
|
+
const prompt = `Create a professional mobile app splash screen / launch screen with this design: ${design}.
|
|
71
|
+
Aspect ratio: ${aspectRatio}.
|
|
72
|
+
The design should be clean, minimal, centered, and work well as an app loading screen.
|
|
73
|
+
Include any branding, logo, or visual elements described.
|
|
74
|
+
Ensure the design works for ${aspectRatio === '9:16' ? 'portrait' : 'landscape'} orientation.`;
|
|
75
|
+
const images = await gemini.generateImage(prompt, { aspectRatio: aspectRatio });
|
|
76
|
+
const masterImage = images[0];
|
|
77
|
+
if (!masterImage) {
|
|
78
|
+
throw new Error(`Failed to generate splash screen for aspect ratio ${aspectRatio}`);
|
|
79
|
+
}
|
|
80
|
+
// Resize to all sizes in this group
|
|
81
|
+
for (const size of groupSizes) {
|
|
82
|
+
const platformDir = path.join(outputDir, size.platform);
|
|
83
|
+
await ensureDirectory(platformDir);
|
|
84
|
+
const resized = await resize(masterImage, size.width, size.height, { fit: 'cover' });
|
|
85
|
+
const filename = `launch-${size.name}.png`;
|
|
86
|
+
const outputPath = await saveImage(resized, platformDir, filename, 0);
|
|
87
|
+
generatedPaths.push(outputPath);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return generatedPaths;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Generate launch images from existing image file
|
|
94
|
+
*/
|
|
95
|
+
async function generateFromImage(imagePath, sizes, outputDir) {
|
|
96
|
+
const generatedPaths = [];
|
|
97
|
+
// Load source image
|
|
98
|
+
const sourceImage = await loadImage(imagePath);
|
|
99
|
+
// Resize to all requested sizes
|
|
100
|
+
for (const size of sizes) {
|
|
101
|
+
const platformDir = path.join(outputDir, size.platform);
|
|
102
|
+
await ensureDirectory(platformDir);
|
|
103
|
+
// Use 'cover' fit to fill the screen without letterboxing
|
|
104
|
+
const resized = await resize(sourceImage, size.width, size.height, { fit: 'cover' });
|
|
105
|
+
const filename = `launch-${size.name}.png`;
|
|
106
|
+
const outputPath = await saveImage(resized, platformDir, filename, 0);
|
|
107
|
+
generatedPaths.push(outputPath);
|
|
108
|
+
}
|
|
109
|
+
return generatedPaths;
|
|
110
|
+
}
|
|
111
|
+
export const generate_launch_images = tool({
|
|
112
|
+
description: 'Generate splash screens / launch images for iOS and Android apps from text description or existing image',
|
|
113
|
+
args,
|
|
114
|
+
execute: async (args, _context) => {
|
|
115
|
+
try {
|
|
116
|
+
const { design, platforms = ['ios', 'android'], outputDir = path.join(process.cwd(), 'generated-assets', 'launch-images') } = args;
|
|
117
|
+
// Ensure output directory exists
|
|
118
|
+
await ensureDirectory(outputDir);
|
|
119
|
+
// Get all launch image sizes for specified platforms
|
|
120
|
+
const sizes = getLaunchImageSizes(platforms);
|
|
121
|
+
if (sizes.length === 0) {
|
|
122
|
+
return 'No devices found for the specified platforms. Please check your platform selection.';
|
|
123
|
+
}
|
|
124
|
+
// Determine if input is a file path or text description
|
|
125
|
+
const isImage = isFilePath(design);
|
|
126
|
+
let generatedPaths;
|
|
127
|
+
if (isImage) {
|
|
128
|
+
// Check if file exists
|
|
129
|
+
try {
|
|
130
|
+
await fs.access(design);
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return `Image file not found: ${design}`;
|
|
134
|
+
}
|
|
135
|
+
// Generate from existing image
|
|
136
|
+
generatedPaths = await generateFromImage(design, sizes, outputDir);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
// Generate from text description using Gemini
|
|
140
|
+
generatedPaths = await generateFromPrompt(design, sizes, outputDir);
|
|
141
|
+
}
|
|
142
|
+
// Group paths by platform for cleaner output message
|
|
143
|
+
const iosPaths = generatedPaths.filter(p => p.includes('/ios/'));
|
|
144
|
+
const androidPaths = generatedPaths.filter(p => p.includes('/android/'));
|
|
145
|
+
let message = `Successfully generated ${generatedPaths.length} launch images!\n\n`;
|
|
146
|
+
if (iosPaths.length > 0) {
|
|
147
|
+
const firstIosPath = iosPaths[0];
|
|
148
|
+
if (firstIosPath) {
|
|
149
|
+
message += `iOS Launch Images (${iosPaths.length}):\n`;
|
|
150
|
+
message += ` ${path.dirname(firstIosPath)}/\n`;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (androidPaths.length > 0) {
|
|
154
|
+
const firstAndroidPath = androidPaths[0];
|
|
155
|
+
if (firstAndroidPath) {
|
|
156
|
+
message += `Android Launch Images (${androidPaths.length}):\n`;
|
|
157
|
+
message += ` ${path.dirname(firstAndroidPath)}/\n`;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
message += `\nAll images saved to: ${outputDir}`;
|
|
161
|
+
return message;
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
if (error instanceof Error && error.message.includes('GEMINI_API_KEY')) {
|
|
165
|
+
return 'Gemini API key not configured. Please set GEMINI_API_KEY environment variable.';
|
|
166
|
+
}
|
|
167
|
+
return `Failed to generate launch images: ${error instanceof Error ? error.message : String(error)}`;
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
//# sourceMappingURL=launch-images.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launch-images.js","sourceRoot":"","sources":["../../../src/tools/app-assets/launch-images.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resize for Devices Tool
|
|
3
|
+
*
|
|
4
|
+
* Smart image resizing to device-specific dimensions with multiple crop modes:
|
|
5
|
+
* - fit: Letterbox if aspect ratio differs
|
|
6
|
+
* - fill: Crop to fill the screen
|
|
7
|
+
* - smart: Use Gemini to identify important content before cropping
|
|
8
|
+
*/
|
|
9
|
+
import { type ToolDefinition } from '@opencode-ai/plugin/tool';
|
|
10
|
+
/**
|
|
11
|
+
* Tool definition for resize_for_devices
|
|
12
|
+
*/
|
|
13
|
+
export declare const resizeForDevicesTool: ToolDefinition;
|
|
14
|
+
//# sourceMappingURL=resize-devices.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resize-devices.d.ts","sourceRoot":"","sources":["../../../src/tools/app-assets/resize-devices.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAuOrE;;GAEG;AACH,eAAO,MAAM,oBAAoB,EAAE,cAwHjC,CAAC"}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resize for Devices Tool
|
|
3
|
+
*
|
|
4
|
+
* Smart image resizing to device-specific dimensions with multiple crop modes:
|
|
5
|
+
* - fit: Letterbox if aspect ratio differs
|
|
6
|
+
* - fill: Crop to fill the screen
|
|
7
|
+
* - smart: Use Gemini to identify important content before cropping
|
|
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, crop, getMetadata } from '../../utils/image-processing.js';
|
|
13
|
+
import { IOS_SCREENSHOT_SIZES } from '../../platforms/ios.js';
|
|
14
|
+
import { ANDROID_SCREENSHOT_SIZES } from '../../platforms/android.js';
|
|
15
|
+
import path from 'node:path';
|
|
16
|
+
/**
|
|
17
|
+
* Tool args schema
|
|
18
|
+
*/
|
|
19
|
+
const resizeForDevicesArgs = {
|
|
20
|
+
imagePath: tool.schema.string().describe('Path to the source image to resize'),
|
|
21
|
+
platform: tool.schema.enum(['ios', 'android', 'both']).describe('Target platform(s). Defaults to "both"'),
|
|
22
|
+
screenshotType: tool.schema.enum(['phone', 'tablet', 'all']).optional().describe('Device type to target. Defaults to "all"'),
|
|
23
|
+
cropMode: tool.schema.enum(['fit', 'fill', 'smart']).optional().describe('Resize mode: "fit" letterboxes, "fill" crops to fill, "smart" uses AI to identify important content. Defaults to "fill"'),
|
|
24
|
+
outputDir: tool.schema.string().optional().describe('Custom output directory. Defaults to "./generated-assets/device-screenshots"')
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Convert iOS screenshot size to unified format
|
|
28
|
+
*/
|
|
29
|
+
function unifyIOSSize(size) {
|
|
30
|
+
return {
|
|
31
|
+
name: size.name,
|
|
32
|
+
width: size.width,
|
|
33
|
+
height: size.height,
|
|
34
|
+
device: size.device,
|
|
35
|
+
platform: 'ios'
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Convert Android screenshot size to unified format
|
|
40
|
+
*/
|
|
41
|
+
function unifyAndroidSize(size) {
|
|
42
|
+
return {
|
|
43
|
+
name: size.device.replace(/\s+/g, '-').toLowerCase(),
|
|
44
|
+
width: size.width,
|
|
45
|
+
height: size.height,
|
|
46
|
+
device: size.device,
|
|
47
|
+
platform: 'android'
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Filter unified sizes by platform and device type
|
|
52
|
+
*/
|
|
53
|
+
function filterSizes(platform, screenshotType) {
|
|
54
|
+
const sizes = [];
|
|
55
|
+
// Add iOS sizes
|
|
56
|
+
if (platform === 'ios' || platform === 'both') {
|
|
57
|
+
const iosSizes = IOS_SCREENSHOT_SIZES.map(unifyIOSSize);
|
|
58
|
+
if (screenshotType === 'phone') {
|
|
59
|
+
sizes.push(...iosSizes.filter(s => s.device.includes('iPhone')));
|
|
60
|
+
}
|
|
61
|
+
else if (screenshotType === 'tablet') {
|
|
62
|
+
sizes.push(...iosSizes.filter(s => s.device.includes('iPad')));
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
sizes.push(...iosSizes);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Add Android sizes
|
|
69
|
+
if (platform === 'android' || platform === 'both') {
|
|
70
|
+
const androidSizes = ANDROID_SCREENSHOT_SIZES.map(unifyAndroidSize);
|
|
71
|
+
if (screenshotType === 'phone') {
|
|
72
|
+
sizes.push(...androidSizes.filter(s => s.device.toLowerCase().includes('phone')));
|
|
73
|
+
}
|
|
74
|
+
else if (screenshotType === 'tablet') {
|
|
75
|
+
sizes.push(...androidSizes.filter(s => s.device.toLowerCase().includes('tablet')));
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
sizes.push(...androidSizes);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return sizes;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Calculate smart crop region using Gemini to identify important content
|
|
85
|
+
*/
|
|
86
|
+
async function calculateSmartCrop(imageBuffer, sourceWidth, sourceHeight, targetWidth, targetHeight) {
|
|
87
|
+
const provider = new GeminiProvider();
|
|
88
|
+
// Calculate target aspect ratio
|
|
89
|
+
const targetAspect = targetWidth / targetHeight;
|
|
90
|
+
const sourceAspect = sourceWidth / sourceHeight;
|
|
91
|
+
// If aspects match or source is smaller, no smart crop needed
|
|
92
|
+
if (Math.abs(targetAspect - sourceAspect) < 0.01 || sourceWidth <= targetWidth) {
|
|
93
|
+
return {
|
|
94
|
+
left: 0,
|
|
95
|
+
top: 0,
|
|
96
|
+
width: sourceWidth,
|
|
97
|
+
height: sourceHeight
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// Ask Gemini to identify the most important region
|
|
101
|
+
const prompt = `Analyze this image and identify the most important or interesting region that should be preserved when cropping.
|
|
102
|
+
|
|
103
|
+
The image is ${sourceWidth}x${sourceHeight} pixels.
|
|
104
|
+
We need to crop it for a ${targetWidth}x${targetHeight} display (aspect ratio ${targetAspect.toFixed(2)}).
|
|
105
|
+
|
|
106
|
+
Please respond with ONLY a JSON object in this exact format:
|
|
107
|
+
{"left": <number>, "top": <number>, "width": <number>, "height": <number>}
|
|
108
|
+
|
|
109
|
+
Where:
|
|
110
|
+
- left: x-coordinate of the top-left corner (0 to ${sourceWidth})
|
|
111
|
+
- top: y-coordinate of the top-left corner (0 to ${sourceHeight})
|
|
112
|
+
- width: width of the crop region (maintaining aspect ratio ${targetAspect.toFixed(2)})
|
|
113
|
+
- height: height of the crop region (maintaining aspect ratio ${targetAspect.toFixed(2)})
|
|
114
|
+
|
|
115
|
+
Focus on faces, text, or the main subject. Ensure the crop region:
|
|
116
|
+
1. Has aspect ratio close to ${targetAspect.toFixed(2)}
|
|
117
|
+
2. Fits within the image boundaries
|
|
118
|
+
3. Captures the most important content`;
|
|
119
|
+
const analysis = await provider.analyzeImage(imageBuffer, prompt);
|
|
120
|
+
// Parse JSON response
|
|
121
|
+
try {
|
|
122
|
+
// Extract JSON from response (might have markdown code blocks)
|
|
123
|
+
const jsonMatch = analysis.match(/\{[^}]+\}/);
|
|
124
|
+
if (!jsonMatch) {
|
|
125
|
+
throw new Error('No JSON found in response');
|
|
126
|
+
}
|
|
127
|
+
const region = JSON.parse(jsonMatch[0]);
|
|
128
|
+
// Validate and constrain the region
|
|
129
|
+
const constrainedRegion = {
|
|
130
|
+
left: Math.max(0, Math.min(region.left, sourceWidth - 1)),
|
|
131
|
+
top: Math.max(0, Math.min(region.top, sourceHeight - 1)),
|
|
132
|
+
width: Math.min(region.width, sourceWidth - region.left),
|
|
133
|
+
height: Math.min(region.height, sourceHeight - region.top)
|
|
134
|
+
};
|
|
135
|
+
// Ensure minimum size
|
|
136
|
+
if (constrainedRegion.width < 100 || constrainedRegion.height < 100) {
|
|
137
|
+
throw new Error('Crop region too small');
|
|
138
|
+
}
|
|
139
|
+
return constrainedRegion;
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
// Fallback to center crop if AI analysis fails
|
|
143
|
+
console.warn('Smart crop failed, falling back to center crop:', error);
|
|
144
|
+
// Calculate center crop with target aspect ratio
|
|
145
|
+
const cropHeight = targetAspect > sourceAspect
|
|
146
|
+
? sourceHeight
|
|
147
|
+
: sourceWidth / targetAspect;
|
|
148
|
+
const cropWidth = targetAspect > sourceAspect
|
|
149
|
+
? sourceHeight * targetAspect
|
|
150
|
+
: sourceWidth;
|
|
151
|
+
return {
|
|
152
|
+
left: Math.floor((sourceWidth - cropWidth) / 2),
|
|
153
|
+
top: Math.floor((sourceHeight - cropHeight) / 2),
|
|
154
|
+
width: Math.floor(cropWidth),
|
|
155
|
+
height: Math.floor(cropHeight)
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Resize image for a specific device size
|
|
161
|
+
*/
|
|
162
|
+
async function resizeForDevice(imageBuffer, size, cropMode) {
|
|
163
|
+
const metadata = await getMetadata(imageBuffer);
|
|
164
|
+
if (cropMode === 'fit') {
|
|
165
|
+
// Letterbox mode - contain the image with black bars if needed
|
|
166
|
+
return await resize(imageBuffer, size.width, size.height, {
|
|
167
|
+
fit: 'contain',
|
|
168
|
+
position: 'center',
|
|
169
|
+
background: '#000000'
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
else if (cropMode === 'fill') {
|
|
173
|
+
// Fill mode - crop to fill the screen
|
|
174
|
+
return await resize(imageBuffer, size.width, size.height, {
|
|
175
|
+
fit: 'cover',
|
|
176
|
+
position: 'center'
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
// Smart mode - use Gemini to identify important content
|
|
181
|
+
const cropRegion = await calculateSmartCrop(imageBuffer, metadata.width, metadata.height, size.width, size.height);
|
|
182
|
+
// First crop to the smart region
|
|
183
|
+
const croppedBuffer = await crop(imageBuffer, cropRegion);
|
|
184
|
+
// Then resize to exact dimensions
|
|
185
|
+
return await resize(croppedBuffer, size.width, size.height, {
|
|
186
|
+
fit: 'fill',
|
|
187
|
+
position: 'center'
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Tool definition for resize_for_devices
|
|
193
|
+
*/
|
|
194
|
+
export const resizeForDevicesTool = tool({
|
|
195
|
+
description: 'Smart image resizing to device-specific dimensions. Supports fit (letterbox), fill (crop), and smart (AI-guided crop) modes.',
|
|
196
|
+
args: resizeForDevicesArgs,
|
|
197
|
+
async execute(args, _context) {
|
|
198
|
+
try {
|
|
199
|
+
const { imagePath, platform = 'both', screenshotType = 'all', cropMode = 'fill', outputDir = './generated-assets/device-screenshots' } = args;
|
|
200
|
+
// Load source image
|
|
201
|
+
const imageBuffer = await loadImage(imagePath);
|
|
202
|
+
const metadata = await getMetadata(imageBuffer);
|
|
203
|
+
// Get target sizes
|
|
204
|
+
const targetSizes = filterSizes(platform, screenshotType);
|
|
205
|
+
if (targetSizes.length === 0) {
|
|
206
|
+
return [
|
|
207
|
+
'✗ Error: No matching device sizes found',
|
|
208
|
+
'',
|
|
209
|
+
`Platform: ${platform}`,
|
|
210
|
+
`Screenshot type: ${screenshotType}`,
|
|
211
|
+
'',
|
|
212
|
+
'Please check your filter settings.'
|
|
213
|
+
].join('\n');
|
|
214
|
+
}
|
|
215
|
+
// Ensure output directory exists
|
|
216
|
+
await ensureDirectory(outputDir);
|
|
217
|
+
const savedPaths = [];
|
|
218
|
+
let smartCropWarnings = 0;
|
|
219
|
+
// Resize for each device
|
|
220
|
+
for (const size of targetSizes) {
|
|
221
|
+
try {
|
|
222
|
+
// Create platform/device-specific directory
|
|
223
|
+
const deviceDir = path.join(outputDir, size.platform, size.name);
|
|
224
|
+
await ensureDirectory(deviceDir);
|
|
225
|
+
// Resize image
|
|
226
|
+
const resizedBuffer = await resizeForDevice(imageBuffer, size, cropMode);
|
|
227
|
+
// Save with descriptive filename
|
|
228
|
+
const filename = `${path.basename(imagePath, path.extname(imagePath))}-${size.name}.png`;
|
|
229
|
+
const filepath = await saveImage(resizedBuffer, deviceDir, filename.replace('.png', ''), 0);
|
|
230
|
+
savedPaths.push(filepath);
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
// Track smart crop failures but continue
|
|
234
|
+
if (cropMode === 'smart' && error instanceof Error) {
|
|
235
|
+
smartCropWarnings++;
|
|
236
|
+
console.warn(`Smart crop warning for ${size.name}:`, error.message);
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
throw error;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
const warnings = smartCropWarnings > 0
|
|
244
|
+
? `\n\n⚠️ ${smartCropWarnings} device(s) used fallback center crop (AI analysis unavailable)`
|
|
245
|
+
: '';
|
|
246
|
+
return [
|
|
247
|
+
`✓ Successfully resized image for ${savedPaths.length} device size(s)`,
|
|
248
|
+
'',
|
|
249
|
+
`Source: ${imagePath} (${metadata.width}x${metadata.height})`,
|
|
250
|
+
`Platform: ${platform}`,
|
|
251
|
+
`Device type: ${screenshotType}`,
|
|
252
|
+
`Crop mode: ${cropMode}`,
|
|
253
|
+
'',
|
|
254
|
+
'Generated sizes:',
|
|
255
|
+
...savedPaths.slice(0, 10).map((p, i) => ` ${i + 1}. ${p}`),
|
|
256
|
+
savedPaths.length > 10 ? ` ... and ${savedPaths.length - 10} more` : '',
|
|
257
|
+
'',
|
|
258
|
+
`Output directory: ${outputDir}`,
|
|
259
|
+
warnings
|
|
260
|
+
].filter(Boolean).join('\n');
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
// Handle errors with user-friendly messages
|
|
264
|
+
if (error instanceof Error) {
|
|
265
|
+
if (error.message.includes('GEMINI_API_KEY')) {
|
|
266
|
+
return [
|
|
267
|
+
'✗ Error: Gemini API key not found',
|
|
268
|
+
'',
|
|
269
|
+
'Smart crop mode requires GEMINI_API_KEY environment variable.',
|
|
270
|
+
'Get your API key at: https://makersuite.google.com/app/apikey',
|
|
271
|
+
'',
|
|
272
|
+
'Tip: Use cropMode "fit" or "fill" to resize without AI analysis.'
|
|
273
|
+
].join('\n');
|
|
274
|
+
}
|
|
275
|
+
if (error.message.includes('ENOENT') || error.message.includes('no such file')) {
|
|
276
|
+
return [
|
|
277
|
+
'✗ Error: Source image file not found',
|
|
278
|
+
'',
|
|
279
|
+
`Path: ${args.imagePath}`,
|
|
280
|
+
'',
|
|
281
|
+
'Please check the file path and try again.'
|
|
282
|
+
].join('\n');
|
|
283
|
+
}
|
|
284
|
+
return [
|
|
285
|
+
'✗ Device resize failed',
|
|
286
|
+
'',
|
|
287
|
+
`Error: ${error.message}`,
|
|
288
|
+
'',
|
|
289
|
+
'Please check your inputs and try again.'
|
|
290
|
+
].join('\n');
|
|
291
|
+
}
|
|
292
|
+
return '✗ Device resize failed with an unknown error';
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
//# sourceMappingURL=resize-devices.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resize-devices.js","sourceRoot":"","sources":["../../../src/tools/app-assets/resize-devices.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
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 { type ToolDefinition } from '@opencode-ai/plugin/tool';
|
|
10
|
+
/**
|
|
11
|
+
* Tool definition for generate_app_screenshots
|
|
12
|
+
*/
|
|
13
|
+
export declare const generateScreenshotsTool: ToolDefinition;
|
|
14
|
+
//# sourceMappingURL=screenshots.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screenshots.d.ts","sourceRoot":"","sources":["../../../src/tools/app-assets/screenshots.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAyCrE;;GAEG;AACH,eAAO,MAAM,uBAAuB,EAAE,cAuKpC,CAAC"}
|