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.
Files changed (179) hide show
  1. package/.ralph-events.json +151 -0
  2. package/.ralph-last-branch +1 -0
  3. package/.ralph-monitor-state.json +7 -0
  4. package/.ralph-monitor.pid +1 -0
  5. package/.ralph-timing.json +26 -0
  6. package/README.md +708 -0
  7. package/dist/index.d.ts +18 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +21 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/platforms/android.d.ts +94 -0
  12. package/dist/platforms/android.d.ts.map +1 -0
  13. package/dist/platforms/android.js +123 -0
  14. package/dist/platforms/android.js.map +1 -0
  15. package/dist/platforms/ios.d.ts +51 -0
  16. package/dist/platforms/ios.d.ts.map +1 -0
  17. package/dist/platforms/ios.js +149 -0
  18. package/dist/platforms/ios.js.map +1 -0
  19. package/dist/platforms/macos.d.ts +33 -0
  20. package/dist/platforms/macos.d.ts.map +1 -0
  21. package/dist/platforms/macos.js +50 -0
  22. package/dist/platforms/macos.js.map +1 -0
  23. package/dist/platforms/watchos.d.ts +36 -0
  24. package/dist/platforms/watchos.d.ts.map +1 -0
  25. package/dist/platforms/watchos.js +113 -0
  26. package/dist/platforms/watchos.js.map +1 -0
  27. package/dist/platforms/web.d.ts +64 -0
  28. package/dist/platforms/web.d.ts.map +1 -0
  29. package/dist/platforms/web.js +96 -0
  30. package/dist/platforms/web.js.map +1 -0
  31. package/dist/providers/gemini.d.ts +41 -0
  32. package/dist/providers/gemini.d.ts.map +1 -0
  33. package/dist/providers/gemini.js +177 -0
  34. package/dist/providers/gemini.js.map +1 -0
  35. package/dist/tools/analyze/compare.d.ts +12 -0
  36. package/dist/tools/analyze/compare.d.ts.map +1 -0
  37. package/dist/tools/analyze/compare.js +83 -0
  38. package/dist/tools/analyze/compare.js.map +1 -0
  39. package/dist/tools/analyze/mockup.d.ts +12 -0
  40. package/dist/tools/analyze/mockup.d.ts.map +1 -0
  41. package/dist/tools/analyze/mockup.js +88 -0
  42. package/dist/tools/analyze/mockup.js.map +1 -0
  43. package/dist/tools/analyze/screenshot.d.ts +12 -0
  44. package/dist/tools/analyze/screenshot.d.ts.map +1 -0
  45. package/dist/tools/analyze/screenshot.js +61 -0
  46. package/dist/tools/analyze/screenshot.js.map +1 -0
  47. package/dist/tools/app-assets/app-icon.d.ts +9 -0
  48. package/dist/tools/app-assets/app-icon.d.ts.map +1 -0
  49. package/dist/tools/app-assets/app-icon.js +133 -0
  50. package/dist/tools/app-assets/app-icon.js.map +1 -0
  51. package/dist/tools/app-assets/device-mockup.d.ts +9 -0
  52. package/dist/tools/app-assets/device-mockup.d.ts.map +1 -0
  53. package/dist/tools/app-assets/device-mockup.js +139 -0
  54. package/dist/tools/app-assets/device-mockup.js.map +1 -0
  55. package/dist/tools/app-assets/launch-images.d.ts +3 -0
  56. package/dist/tools/app-assets/launch-images.d.ts.map +1 -0
  57. package/dist/tools/app-assets/launch-images.js +171 -0
  58. package/dist/tools/app-assets/launch-images.js.map +1 -0
  59. package/dist/tools/app-assets/resize-devices.d.ts +14 -0
  60. package/dist/tools/app-assets/resize-devices.d.ts.map +1 -0
  61. package/dist/tools/app-assets/resize-devices.js +296 -0
  62. package/dist/tools/app-assets/resize-devices.js.map +1 -0
  63. package/dist/tools/app-assets/screenshots.d.ts +14 -0
  64. package/dist/tools/app-assets/screenshots.d.ts.map +1 -0
  65. package/dist/tools/app-assets/screenshots.js +186 -0
  66. package/dist/tools/app-assets/screenshots.js.map +1 -0
  67. package/dist/tools/core/edit-image.d.ts +12 -0
  68. package/dist/tools/core/edit-image.d.ts.map +1 -0
  69. package/dist/tools/core/edit-image.js +102 -0
  70. package/dist/tools/core/edit-image.js.map +1 -0
  71. package/dist/tools/core/generate-image.d.ts +12 -0
  72. package/dist/tools/core/generate-image.d.ts.map +1 -0
  73. package/dist/tools/core/generate-image.js +96 -0
  74. package/dist/tools/core/generate-image.js.map +1 -0
  75. package/dist/tools/core/restore-image.d.ts +12 -0
  76. package/dist/tools/core/restore-image.d.ts.map +1 -0
  77. package/dist/tools/core/restore-image.js +104 -0
  78. package/dist/tools/core/restore-image.js.map +1 -0
  79. package/dist/tools/design/mockup-to-code.d.ts +3 -0
  80. package/dist/tools/design/mockup-to-code.d.ts.map +1 -0
  81. package/dist/tools/design/mockup-to-code.js +311 -0
  82. package/dist/tools/design/mockup-to-code.js.map +1 -0
  83. package/dist/tools/design/sketch-to-code.d.ts +3 -0
  84. package/dist/tools/design/sketch-to-code.d.ts.map +1 -0
  85. package/dist/tools/design/sketch-to-code.js +325 -0
  86. package/dist/tools/design/sketch-to-code.js.map +1 -0
  87. package/dist/tools/docs/architecture-diagram.d.ts +12 -0
  88. package/dist/tools/docs/architecture-diagram.d.ts.map +1 -0
  89. package/dist/tools/docs/architecture-diagram.js +179 -0
  90. package/dist/tools/docs/architecture-diagram.js.map +1 -0
  91. package/dist/tools/docs/readme-banner.d.ts +6 -0
  92. package/dist/tools/docs/readme-banner.d.ts.map +1 -0
  93. package/dist/tools/docs/readme-banner.js +108 -0
  94. package/dist/tools/docs/readme-banner.js.map +1 -0
  95. package/dist/tools/docs/sequence-diagram.d.ts +12 -0
  96. package/dist/tools/docs/sequence-diagram.d.ts.map +1 -0
  97. package/dist/tools/docs/sequence-diagram.js +161 -0
  98. package/dist/tools/docs/sequence-diagram.js.map +1 -0
  99. package/dist/tools/docs/social-preview.d.ts +11 -0
  100. package/dist/tools/docs/social-preview.d.ts.map +1 -0
  101. package/dist/tools/docs/social-preview.js +111 -0
  102. package/dist/tools/docs/social-preview.js.map +1 -0
  103. package/dist/tools/video/extend-video.d.ts +14 -0
  104. package/dist/tools/video/extend-video.d.ts.map +1 -0
  105. package/dist/tools/video/extend-video.js +39 -0
  106. package/dist/tools/video/extend-video.js.map +1 -0
  107. package/dist/tools/video/generate-video.d.ts +14 -0
  108. package/dist/tools/video/generate-video.d.ts.map +1 -0
  109. package/dist/tools/video/generate-video.js +39 -0
  110. package/dist/tools/video/generate-video.js.map +1 -0
  111. package/dist/tools/video/image-to-video.d.ts +15 -0
  112. package/dist/tools/video/image-to-video.d.ts.map +1 -0
  113. package/dist/tools/video/image-to-video.js +42 -0
  114. package/dist/tools/video/image-to-video.js.map +1 -0
  115. package/dist/tools/video/storyboard-video.d.ts +91 -0
  116. package/dist/tools/video/storyboard-video.d.ts.map +1 -0
  117. package/dist/tools/video/storyboard-video.js +230 -0
  118. package/dist/tools/video/storyboard-video.js.map +1 -0
  119. package/dist/utils/ffmpeg.d.ts +30 -0
  120. package/dist/utils/ffmpeg.d.ts.map +1 -0
  121. package/dist/utils/ffmpeg.js +205 -0
  122. package/dist/utils/ffmpeg.js.map +1 -0
  123. package/dist/utils/file-handler.d.ts +7 -0
  124. package/dist/utils/file-handler.d.ts.map +1 -0
  125. package/dist/utils/file-handler.js +10 -0
  126. package/dist/utils/file-handler.js.map +1 -0
  127. package/dist/utils/image-processing.d.ts +7 -0
  128. package/dist/utils/image-processing.d.ts.map +1 -0
  129. package/dist/utils/image-processing.js +10 -0
  130. package/dist/utils/image-processing.js.map +1 -0
  131. package/docs/PLUGIN-VERIFICATION.md +182 -0
  132. package/logs/notifications.jsonl +46 -0
  133. package/package.json +61 -0
  134. package/prd.json +216 -0
  135. package/progress.txt +145 -0
  136. package/ralph-report.html +297 -0
  137. package/src/index.ts +23 -0
  138. package/src/platforms/android/.gitkeep +0 -0
  139. package/src/platforms/ios/.gitkeep +0 -0
  140. package/src/platforms/web/.gitkeep +0 -0
  141. package/src/providers/.gitkeep +0 -0
  142. package/src/providers/gemini.ts +288 -0
  143. package/src/tools/core/.gitkeep +0 -0
  144. package/src/tools/platform/.gitkeep +0 -0
  145. package/src/tools/video/extend-video.ts +71 -0
  146. package/src/tools/video/generate-video.ts +70 -0
  147. package/src/tools/video/image-to-video.ts +76 -0
  148. package/src/tools/video/storyboard-video.ts +325 -0
  149. package/src/utils/.gitkeep +0 -0
  150. package/src/utils/ffmpeg.ts +266 -0
  151. package/src/utils/file-handler.ts +10 -0
  152. package/src/utils/image-processing.ts +10 -0
  153. package/templates/.gitkeep +0 -0
  154. package/test-analyze-screenshot.ts +50 -0
  155. package/test-app-icons.ts +55 -0
  156. package/test-cat-sunset.ts +30 -0
  157. package/test-full-plugin.ts +88 -0
  158. package/test-icon-gen.ts +30 -0
  159. package/test-output/test-edit.png +0 -0
  160. package/test-output/test-generate.png +0 -0
  161. package/test-output/test-video.mp4 +0 -0
  162. package/test-plugin-load.js +45 -0
  163. package/test-princess-emma-continue.ts +35 -0
  164. package/test-princess-emma-full.ts +38 -0
  165. package/test-princess-emma-short.ts +32 -0
  166. package/test-princess-emma-with-reference.ts +34 -0
  167. package/test-princess-emma.ts +38 -0
  168. package/test-product-ad.ts +66 -0
  169. package/test-ralph-droid.ts +30 -0
  170. package/test-social-preview.ts +61 -0
  171. package/test-veo31-live.ts +187 -0
  172. package/test-video-gen.ts +40 -0
  173. package/test-video-veo.ts +73 -0
  174. package/test-zurich-video.ts +64 -0
  175. package/tests/.gitkeep +0 -0
  176. package/tests/providers/gemini.test.ts +388 -0
  177. package/tests/utils/ffmpeg.test.ts +328 -0
  178. package/tests/video/storyboard.test.ts +469 -0
  179. 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"}