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,12 @@
1
+ /**
2
+ * Analyze Mockup Tool
3
+ *
4
+ * Extracts design specifications from mockup images including colors, typography,
5
+ * spacing, and component structure. Useful for design-to-code workflows.
6
+ */
7
+ import { type ToolDefinition } from '@opencode-ai/plugin/tool';
8
+ /**
9
+ * Tool definition for analyze_mockup
10
+ */
11
+ export declare const analyzeMockupTool: ToolDefinition;
12
+ //# sourceMappingURL=mockup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mockup.d.ts","sourceRoot":"","sources":["../../../src/tools/analyze/mockup.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAkFrE;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,cA2C9B,CAAC"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Analyze Mockup Tool
3
+ *
4
+ * Extracts design specifications from mockup images including colors, typography,
5
+ * spacing, and component structure. Useful for design-to-code workflows.
6
+ */
7
+ import { tool } from '@opencode-ai/plugin/tool';
8
+ import { GeminiProvider } from '../../providers/gemini.js';
9
+ import { loadImage } from '../../utils/file-handler.js';
10
+ /**
11
+ * Tool args schema
12
+ */
13
+ const analyzeMockupArgs = {
14
+ imagePath: tool.schema.string()
15
+ .describe('Path to the design mockup image to analyze'),
16
+ extractColors: tool.schema.boolean()
17
+ .optional()
18
+ .default(true)
19
+ .describe('Whether to extract color palette from the design (default: true)'),
20
+ extractSpacing: tool.schema.boolean()
21
+ .optional()
22
+ .default(true)
23
+ .describe('Whether to extract spacing and layout measurements (default: true)')
24
+ };
25
+ /**
26
+ * Build comprehensive design analysis prompt
27
+ */
28
+ function buildDesignAnalysisPrompt(extractColors, extractSpacing) {
29
+ const sections = [
30
+ 'Analyze this design mockup and extract detailed design specifications.'
31
+ ];
32
+ // Always extract component structure and typography
33
+ sections.push('\n## Component Structure', '- Identify all UI components (buttons, inputs, cards, navigation, etc.)', '- Describe the visual hierarchy', '- Note component states (hover, active, disabled) if visible');
34
+ sections.push('\n## Typography', '- Font families used (or similar web-safe alternatives)', '- Font sizes for headings, body text, captions', '- Font weights and styles', '- Line heights and letter spacing');
35
+ // Conditional: color extraction
36
+ if (extractColors) {
37
+ sections.push('\n## Color Palette', '- Primary, secondary, and accent colors (provide hex codes)', '- Background and surface colors', '- Text colors (body, headings, muted)', '- Border and divider colors', '- Status colors (success, warning, error, info) if present');
38
+ }
39
+ // Conditional: spacing and measurements
40
+ if (extractSpacing) {
41
+ sections.push('\n## Spacing & Layout', '- Margin and padding values (estimate in px or rem)', '- Grid or layout system used', '- Border radius values', '- Shadow specifications', '- Component dimensions and aspect ratios');
42
+ }
43
+ sections.push('\n## Design System Notes', '- Overall design style (modern, minimal, material, iOS, etc.)', '- Notable design patterns or conventions', '- Responsive considerations if visible');
44
+ sections.push('\nProvide specific, actionable values that a developer can use to implement this design.');
45
+ return sections.join('\n');
46
+ }
47
+ /**
48
+ * Tool definition for analyze_mockup
49
+ */
50
+ export const analyzeMockupTool = tool({
51
+ description: 'Extract design specifications from mockup images. Identifies colors, typography, spacing, component structure, and design system details. Perfect for design-to-code workflows.',
52
+ args: analyzeMockupArgs,
53
+ async execute(args, _context) {
54
+ try {
55
+ const { imagePath, extractColors = true, extractSpacing = true } = args;
56
+ // Initialize Gemini provider
57
+ const provider = new GeminiProvider();
58
+ // Load the mockup image
59
+ const imageBuffer = await loadImage(imagePath);
60
+ // Build analysis prompt based on extraction options
61
+ const analysisPrompt = buildDesignAnalysisPrompt(extractColors, extractSpacing);
62
+ // Analyze the mockup using Gemini
63
+ const analysis = await provider.analyzeImage(imageBuffer, analysisPrompt);
64
+ // Build summary of what was extracted
65
+ const extractedFeatures = ['Component Structure', 'Typography'];
66
+ if (extractColors)
67
+ extractedFeatures.push('Color Palette');
68
+ if (extractSpacing)
69
+ extractedFeatures.push('Spacing & Layout');
70
+ return `✓ Design Mockup Analysis Complete
71
+
72
+ Path: ${imagePath}
73
+ Extracted: ${extractedFeatures.join(', ')}
74
+
75
+ ${analysis}`;
76
+ }
77
+ catch (error) {
78
+ if (error instanceof Error && error.message.includes('GEMINI_API_KEY')) {
79
+ return '✗ Error: GEMINI_API_KEY is not set. Please configure your API key in environment variables.';
80
+ }
81
+ if (error instanceof Error && (error.message.includes('ENOENT') || error.message.includes('no such file'))) {
82
+ return `✗ Error: Could not load mockup image at path: ${args.imagePath}. Please verify the file exists.`;
83
+ }
84
+ return `✗ Error analyzing mockup: ${error instanceof Error ? error.message : String(error)}`;
85
+ }
86
+ }
87
+ });
88
+ //# sourceMappingURL=mockup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mockup.js","sourceRoot":"","sources":["../../../src/tools/analyze/mockup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Analyze Screenshot Tool
3
+ *
4
+ * Provides detailed visual analysis of UI screenshots for debugging purposes.
5
+ * Identifies components, layout issues, accessibility concerns, and provides improvement suggestions.
6
+ */
7
+ import { type ToolDefinition } from '@opencode-ai/plugin/tool';
8
+ /**
9
+ * Tool definition for analyze_screenshot
10
+ */
11
+ export declare const analyzeScreenshotTool: ToolDefinition;
12
+ //# sourceMappingURL=screenshot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../../src/tools/analyze/screenshot.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAerE;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,cA4ClC,CAAC"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Analyze Screenshot Tool
3
+ *
4
+ * Provides detailed visual analysis of UI screenshots for debugging purposes.
5
+ * Identifies components, layout issues, accessibility concerns, and provides improvement suggestions.
6
+ */
7
+ import { tool } from '@opencode-ai/plugin/tool';
8
+ import { GeminiProvider } from '../../providers/gemini.js';
9
+ import { loadImage } from '../../utils/file-handler.js';
10
+ /**
11
+ * Tool args schema
12
+ */
13
+ const analyzeScreenshotArgs = {
14
+ imagePath: tool.schema.string()
15
+ .describe('Path to the screenshot image to analyze'),
16
+ question: tool.schema.string()
17
+ .optional()
18
+ .describe('Specific question or focus area for the analysis. If not provided, performs comprehensive UI analysis')
19
+ };
20
+ /**
21
+ * Tool definition for analyze_screenshot
22
+ */
23
+ export const analyzeScreenshotTool = tool({
24
+ description: 'Analyze UI screenshots for debugging. Identifies components, layout issues, accessibility concerns, and provides improvement suggestions. Optionally accepts a specific question to focus the analysis.',
25
+ args: analyzeScreenshotArgs,
26
+ async execute(args, _context) {
27
+ try {
28
+ const { imagePath, question } = args;
29
+ // Initialize Gemini provider
30
+ const provider = new GeminiProvider();
31
+ // Load the screenshot image
32
+ const imageBuffer = await loadImage(imagePath);
33
+ // Build analysis prompt
34
+ const analysisPrompt = question ||
35
+ 'Analyze this UI screenshot in detail. Identify:\n' +
36
+ '1. UI components and their purpose\n' +
37
+ '2. Layout and spacing issues\n' +
38
+ '3. Accessibility concerns (contrast, text size, touch targets)\n' +
39
+ '4. Visual inconsistencies or bugs\n' +
40
+ '5. Suggestions for improvement\n\n' +
41
+ 'Provide a clear, structured analysis.';
42
+ // Analyze the screenshot using Gemini
43
+ const analysis = await provider.analyzeImage(imageBuffer, analysisPrompt);
44
+ return `✓ Screenshot Analysis Complete
45
+
46
+ Path: ${imagePath}
47
+
48
+ ${analysis}`;
49
+ }
50
+ catch (error) {
51
+ if (error instanceof Error && error.message.includes('GEMINI_API_KEY')) {
52
+ return '✗ Error: GEMINI_API_KEY is not set. Please configure your API key in environment variables.';
53
+ }
54
+ if (error instanceof Error && (error.message.includes('ENOENT') || error.message.includes('no such file'))) {
55
+ return `✗ Error: Could not load screenshot at path: ${args.imagePath}. Please verify the file exists.`;
56
+ }
57
+ return `✗ Error analyzing screenshot: ${error instanceof Error ? error.message : String(error)}`;
58
+ }
59
+ }
60
+ });
61
+ //# sourceMappingURL=screenshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../../src/tools/analyze/screenshot.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAuB,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAExD;;GAEG;AACH,MAAM,qBAAqB,GAAG;IAC5B,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;SAC5B,QAAQ,CAAC,yCAAyC,CAAC;IACtD,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;SAC3B,QAAQ,EAAE;SACV,QAAQ,CAAC,uGAAuG,CAAC;CAC5G,CAAC;AAEX;;GAEG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAmB,IAAI,CAAC;IACxD,WAAW,EAAE,yMAAyM;IAEtN,IAAI,EAAE,qBAAqB;IAE3B,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ;QAC1B,IAAI,CAAC;YACH,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;YAErC,6BAA6B;YAC7B,MAAM,QAAQ,GAAG,IAAI,cAAc,EAAE,CAAC;YAEtC,4BAA4B;YAC5B,MAAM,WAAW,GAAG,MAAM,SAAS,CAAC,SAAS,CAAC,CAAC;YAE/C,wBAAwB;YACxB,MAAM,cAAc,GAAG,QAAQ;gBAC7B,mDAAmD;oBACnD,sCAAsC;oBACtC,gCAAgC;oBAChC,kEAAkE;oBAClE,qCAAqC;oBACrC,oCAAoC;oBACpC,uCAAuC,CAAC;YAE1C,sCAAsC;YACtC,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;YAE1E,OAAO;;QAEL,SAAS;;EAEf,QAAQ,EAAE,CAAC;QAET,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBACvE,OAAO,6FAA6F,CAAC;YACvG,CAAC;YACD,IAAI,KAAK,YAAY,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC;gBAC3G,OAAO,+CAA+C,IAAI,CAAC,SAAS,kCAAkC,CAAC;YACzG,CAAC;YACD,OAAO,iCAAiC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QACnG,CAAC;IACH,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Generate App Icon Tool
3
+ *
4
+ * Creates complete app icon sets for iOS and Android platforms.
5
+ * Generates 1024x1024 master icon via Gemini, then resizes to all required sizes.
6
+ */
7
+ import { type ToolDefinition } from '@opencode-ai/plugin/tool';
8
+ export declare const generateAppIcon: ToolDefinition;
9
+ //# sourceMappingURL=app-icon.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-icon.d.ts","sourceRoot":"","sources":["../../../src/tools/app-assets/app-icon.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AA6BrE,eAAO,MAAM,eAAe,EAAE,cAsE5B,CAAC"}
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Generate App Icon Tool
3
+ *
4
+ * Creates complete app icon sets for iOS and Android platforms.
5
+ * Generates 1024x1024 master icon via Gemini, then resizes to all required sizes.
6
+ */
7
+ import { tool } from '@opencode-ai/plugin/tool';
8
+ import { GeminiProvider } from '../../providers/gemini.js';
9
+ import { ensureDirectory, getOutputDir } from '../../utils/file-handler.js';
10
+ import { resize } from '../../utils/image-processing.js';
11
+ import { IOS_ICON_SIZES, WATCHOS_ICON_SIZES, CARPLAY_ICON_SIZES, generateContentsJson, } from '../../platforms/ios.js';
12
+ import { ANDROID_ICON_SIZES, generateAdaptiveIconXml, } from '../../platforms/android.js';
13
+ import * as fs from 'fs/promises';
14
+ import * as path from 'path';
15
+ const generateAppIconArgs = {
16
+ prompt: tool.schema.string().describe('Description of the app icon to generate'),
17
+ platforms: tool.schema
18
+ .array(tool.schema.string())
19
+ .optional()
20
+ .describe('Target platforms: ["ios", "android"] or ["ios"] or ["android"]. Defaults to both.'),
21
+ includeWatchOS: tool.schema.boolean().optional().describe('Include watchOS icon sizes. Defaults to false.'),
22
+ includeCarPlay: tool.schema.boolean().optional().describe('Include CarPlay icon sizes. Defaults to false.'),
23
+ outputDir: tool.schema.string().optional().describe('Custom output directory. Defaults to "./generated-assets/app-icons"'),
24
+ };
25
+ export const generateAppIcon = tool({
26
+ description: 'Generate complete app icon sets for iOS and Android from a text prompt. Creates 1024x1024 master icon and resizes to all required platform sizes.',
27
+ args: generateAppIconArgs,
28
+ async execute(args, _context) {
29
+ try {
30
+ // Initialize Gemini provider
31
+ const gemini = new GeminiProvider();
32
+ // Extract parameters with defaults
33
+ const { prompt, platforms = ['ios', 'android'], includeWatchOS = false, includeCarPlay = false, outputDir: customOutputDir } = args;
34
+ // Determine output directory
35
+ const baseOutputDir = customOutputDir || getOutputDir();
36
+ const iconOutputDir = path.join(baseOutputDir, 'app-icons');
37
+ await ensureDirectory(iconOutputDir);
38
+ // Generate 1024x1024 master icon
39
+ console.log('Generating 1024x1024 master icon...');
40
+ const masterIcons = await gemini.generateImage(`App icon: ${prompt}. Professional, simple, clean design suitable for a mobile app icon. Square format, centered composition.`, { aspectRatio: '1:1', count: 1 });
41
+ if (masterIcons.length === 0) {
42
+ return 'Failed to generate master icon';
43
+ }
44
+ const masterIcon = masterIcons[0];
45
+ if (!masterIcon) {
46
+ return 'Failed to generate master icon';
47
+ }
48
+ // Resize master to exactly 1024x1024 if needed
49
+ const master1024 = await resize(masterIcon, 1024, 1024, { fit: 'cover' });
50
+ const generatedPaths = [];
51
+ // Generate iOS icons
52
+ if (platforms.includes('ios')) {
53
+ const iosPaths = await generateIOSIcons(master1024, iconOutputDir, includeWatchOS, includeCarPlay);
54
+ generatedPaths.push(...iosPaths);
55
+ }
56
+ // Generate Android icons
57
+ if (platforms.includes('android')) {
58
+ const androidPaths = await generateAndroidIcons(master1024, iconOutputDir);
59
+ generatedPaths.push(...androidPaths);
60
+ }
61
+ return `✅ App icon generated successfully!\n\nGenerated ${generatedPaths.length} icon files:\n${generatedPaths.map(p => ` - ${p}`).join('\n')}\n\nMaster icon: 1024x1024`;
62
+ }
63
+ catch (error) {
64
+ if (error instanceof Error) {
65
+ return `❌ Failed to generate app icon: ${error.message}`;
66
+ }
67
+ return '❌ Failed to generate app icon: Unknown error';
68
+ }
69
+ },
70
+ });
71
+ /**
72
+ * Generate iOS icon set with Contents.json
73
+ */
74
+ async function generateIOSIcons(masterIcon, baseOutputDir, includeWatchOS, includeCarPlay) {
75
+ const generatedPaths = [];
76
+ // Create AppIcon.appiconset directory
77
+ const appiconsetDir = path.join(baseOutputDir, 'ios', 'AppIcon.appiconset');
78
+ await ensureDirectory(appiconsetDir);
79
+ // Collect all iOS icon sizes
80
+ let allIconSizes = [...IOS_ICON_SIZES];
81
+ if (includeWatchOS) {
82
+ allIconSizes = allIconSizes.concat(WATCHOS_ICON_SIZES);
83
+ }
84
+ if (includeCarPlay) {
85
+ allIconSizes = allIconSizes.concat(CARPLAY_ICON_SIZES);
86
+ }
87
+ // Generate each icon size
88
+ for (const iconSize of allIconSizes) {
89
+ const resizedIcon = await resize(masterIcon, iconSize.pixels, iconSize.pixels, {
90
+ fit: 'cover',
91
+ });
92
+ const iconPath = path.join(appiconsetDir, iconSize.filename);
93
+ await fs.writeFile(iconPath, resizedIcon);
94
+ generatedPaths.push(iconPath);
95
+ }
96
+ // Generate Contents.json
97
+ const contentsJson = generateContentsJson(allIconSizes);
98
+ const contentsPath = path.join(appiconsetDir, 'Contents.json');
99
+ await fs.writeFile(contentsPath, JSON.stringify(contentsJson, null, 2));
100
+ generatedPaths.push(contentsPath);
101
+ return generatedPaths;
102
+ }
103
+ /**
104
+ * Generate Android icon set with adaptive icon XML
105
+ */
106
+ async function generateAndroidIcons(masterIcon, baseOutputDir) {
107
+ const generatedPaths = [];
108
+ const androidDir = path.join(baseOutputDir, 'android');
109
+ await ensureDirectory(androidDir);
110
+ // Generate icons for each density
111
+ for (const androidIcon of ANDROID_ICON_SIZES) {
112
+ const densityDir = path.join(androidDir, androidIcon.directory);
113
+ await ensureDirectory(densityDir);
114
+ const resizedIcon = await resize(masterIcon, androidIcon.size, androidIcon.size, {
115
+ fit: 'cover',
116
+ });
117
+ const iconPath = path.join(densityDir, androidIcon.filename);
118
+ await fs.writeFile(iconPath, resizedIcon);
119
+ generatedPaths.push(iconPath);
120
+ }
121
+ // Generate adaptive icon XML
122
+ const adaptiveIconDir = path.join(androidDir, 'mipmap-anydpi-v26');
123
+ await ensureDirectory(adaptiveIconDir);
124
+ const adaptiveIconXml = generateAdaptiveIconXml();
125
+ const xmlPath = path.join(adaptiveIconDir, 'ic_launcher.xml');
126
+ await fs.writeFile(xmlPath, adaptiveIconXml);
127
+ generatedPaths.push(xmlPath);
128
+ // Note: For a complete adaptive icon setup, you would also generate separate
129
+ // foreground and background layers. For now, we use the standard icon as both.
130
+ // Users can customize by replacing ic_launcher_foreground and ic_launcher_background.
131
+ return generatedPaths;
132
+ }
133
+ //# sourceMappingURL=app-icon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-icon.js","sourceRoot":"","sources":["../../../src/tools/app-assets/app-icon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Device Mockup Tool
3
+ *
4
+ * Places screenshots in realistic device frames for App Store/marketing materials.
5
+ * Supports iPhone, iPad, and various device colors/orientations.
6
+ */
7
+ import { type ToolDefinition } from '@opencode-ai/plugin/tool';
8
+ export declare const generateDeviceMockup: ToolDefinition;
9
+ //# sourceMappingURL=device-mockup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-mockup.d.ts","sourceRoot":"","sources":["../../../src/tools/app-assets/device-mockup.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAkDrE,eAAO,MAAM,oBAAoB,EAAE,cAkFjC,CAAC"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Device Mockup Tool
3
+ *
4
+ * Places screenshots in realistic device frames for App Store/marketing materials.
5
+ * Supports iPhone, iPad, and various device colors/orientations.
6
+ */
7
+ import { tool } from '@opencode-ai/plugin/tool';
8
+ import { GeminiProvider } from '../../providers/gemini.js';
9
+ import { loadImage, saveImage, ensureDirectory, getOutputDir } from '../../utils/file-handler.js';
10
+ import { getMetadata } from '../../utils/image-processing.js';
11
+ import * as path from 'path';
12
+ // Device specifications
13
+ const DEVICE_SPECS = {
14
+ // iPhone 16 Series
15
+ 'iphone-16-pro-max': { displaySize: '6.9"', aspectRatio: '19.5:9', family: 'iPhone 16' },
16
+ 'iphone-16-pro': { displaySize: '6.3"', aspectRatio: '19.5:9', family: 'iPhone 16' },
17
+ 'iphone-16-plus': { displaySize: '6.7"', aspectRatio: '19.5:9', family: 'iPhone 16' },
18
+ 'iphone-16': { displaySize: '6.1"', aspectRatio: '19.5:9', family: 'iPhone 16' },
19
+ // iPhone 15 Series
20
+ 'iphone-15-pro-max': { displaySize: '6.7"', aspectRatio: '19.5:9', family: 'iPhone 15' },
21
+ 'iphone-15-pro': { displaySize: '6.1"', aspectRatio: '19.5:9', family: 'iPhone 15' },
22
+ 'iphone-15-plus': { displaySize: '6.7"', aspectRatio: '19.5:9', family: 'iPhone 15' },
23
+ 'iphone-15': { displaySize: '6.1"', aspectRatio: '19.5:9', family: 'iPhone 15' },
24
+ // iPad Pro Series
25
+ 'ipad-pro-12.9': { displaySize: '12.9"', aspectRatio: '4:3', family: 'iPad Pro' },
26
+ 'ipad-pro-11': { displaySize: '11"', aspectRatio: '4.3:3', family: 'iPad Pro' },
27
+ // iPad Air & mini
28
+ 'ipad-air': { displaySize: '10.9"', aspectRatio: '4.3:3', family: 'iPad Air' },
29
+ 'ipad-mini': { displaySize: '8.3"', aspectRatio: '4:3', family: 'iPad mini' },
30
+ };
31
+ // Device colors are validated softly - any color string is accepted for flexibility
32
+ // Common options: silver, black, gold, titanium, blue, pink, green, purple, white, space-gray
33
+ const generateDeviceMockupArgs = {
34
+ imagePath: tool.schema.string().describe('Path to the screenshot image to place in device frame'),
35
+ device: tool.schema.string().describe('Device model: iphone-16-pro-max, iphone-16-pro, iphone-16-plus, iphone-16, iphone-15-pro-max, iphone-15-pro, iphone-15-plus, iphone-15, ipad-pro-12.9, ipad-pro-11, ipad-air, ipad-mini'),
36
+ color: tool.schema.string().optional().describe('Device color: silver, black, gold, titanium, blue, pink, green, purple, white, space-gray. Defaults to black.'),
37
+ orientation: tool.schema.string().optional().describe('Device orientation: portrait or landscape. Defaults to portrait.'),
38
+ outputPath: tool.schema.string().optional().describe('Custom output path for the mockup. Defaults to "./generated-assets/mockups/{device}-mockup.png"'),
39
+ };
40
+ export const generateDeviceMockup = tool({
41
+ description: 'Generate a realistic device mockup by placing a screenshot in a device frame. Supports iPhone 15/16 series and iPad models with various colors and orientations.',
42
+ args: generateDeviceMockupArgs,
43
+ async execute(args, _context) {
44
+ try {
45
+ // Initialize Gemini provider
46
+ const gemini = new GeminiProvider();
47
+ // Extract and validate parameters
48
+ const { imagePath, device, color = 'black', orientation = 'portrait', outputPath: customOutputPath, } = args;
49
+ // Validate device
50
+ if (!(device in DEVICE_SPECS)) {
51
+ const availableDevices = Object.keys(DEVICE_SPECS).join(', ');
52
+ return `❌ Invalid device model. Available devices: ${availableDevices}`;
53
+ }
54
+ const deviceModel = device;
55
+ const deviceSpec = DEVICE_SPECS[deviceModel];
56
+ // Validate color (soft validation - allow any color for flexibility)
57
+ const normalizedColor = color.toLowerCase();
58
+ // Validate orientation
59
+ if (orientation !== 'portrait' && orientation !== 'landscape') {
60
+ return `❌ Invalid orientation. Must be 'portrait' or 'landscape'.`;
61
+ }
62
+ // Load the screenshot image
63
+ console.log(`Loading screenshot from ${imagePath}...`);
64
+ const screenshotBuffer = await loadImage(imagePath);
65
+ // Get screenshot metadata
66
+ const metadata = await getMetadata(screenshotBuffer);
67
+ console.log(`Screenshot loaded: ${metadata.width}x${metadata.height}`);
68
+ // Determine output path
69
+ const baseOutputDir = getOutputDir();
70
+ const mockupsDir = path.join(baseOutputDir, 'mockups');
71
+ await ensureDirectory(mockupsDir);
72
+ // Prepare device filename for output
73
+ const deviceFileName = `${deviceModel}-${normalizedColor}-${orientation}-mockup`;
74
+ // Create a detailed prompt for Gemini to generate the mockup
75
+ const mockupPrompt = buildMockupPrompt(deviceSpec, deviceModel, normalizedColor, orientation);
76
+ console.log('Generating device mockup with Gemini...');
77
+ // Use Gemini's image editing capability to composite the screenshot into a device frame
78
+ // Strategy: Ask Gemini to generate a device frame with the screenshot placed inside
79
+ const mockupBuffer = await gemini.editImage(screenshotBuffer, mockupPrompt);
80
+ // Save the mockup
81
+ const savedPath = customOutputPath
82
+ ? await saveImage(mockupBuffer, path.dirname(customOutputPath), path.basename(customOutputPath, path.extname(customOutputPath)), 0)
83
+ : await saveImage(mockupBuffer, mockupsDir, deviceFileName, 0);
84
+ return `✅ Device mockup generated successfully!\n\nDevice: ${deviceSpec.family} (${deviceSpec.displaySize})\nColor: ${normalizedColor}\nOrientation: ${orientation}\nOutput: ${savedPath}`;
85
+ }
86
+ catch (error) {
87
+ if (error instanceof Error) {
88
+ return `❌ Failed to generate device mockup: ${error.message}`;
89
+ }
90
+ return '❌ Failed to generate device mockup: Unknown error';
91
+ }
92
+ },
93
+ });
94
+ /**
95
+ * Build a detailed prompt for Gemini to generate realistic device mockup
96
+ */
97
+ function buildMockupPrompt(deviceSpec, deviceModel, color, orientation) {
98
+ const isIPad = deviceModel.includes('ipad');
99
+ const deviceType = isIPad ? 'iPad' : 'iPhone';
100
+ const deviceName = deviceSpec.family;
101
+ // Build comprehensive prompt
102
+ let prompt = `Place this screenshot inside a realistic ${deviceName} device mockup. `;
103
+ // Device specifications
104
+ prompt += `The device should be a ${deviceName} with a ${deviceSpec.displaySize} display. `;
105
+ prompt += `Device color/finish: ${color}. `;
106
+ prompt += `Orientation: ${orientation}. `;
107
+ // Frame details based on device type
108
+ if (deviceType === 'iPhone') {
109
+ if (deviceModel.includes('16-pro') || deviceModel.includes('15-pro')) {
110
+ prompt += `Include the distinctive titanium frame with rounded corners. `;
111
+ prompt += `Show the Dynamic Island at the top of the screen. `;
112
+ prompt += `Add subtle reflections on the titanium edges. `;
113
+ }
114
+ else {
115
+ prompt += `Include the aluminum frame with rounded corners. `;
116
+ prompt += `Show the notch at the top of the screen. `;
117
+ }
118
+ prompt += `Include the volume buttons on the left side and power button on the right. `;
119
+ }
120
+ else {
121
+ // iPad
122
+ prompt += `Include the thin aluminum bezel with rounded corners characteristic of modern iPads. `;
123
+ prompt += `Show the minimal bezels around the display. `;
124
+ if (deviceModel.includes('pro')) {
125
+ prompt += `Include the flat squared-off edges of the iPad Pro design. `;
126
+ }
127
+ }
128
+ // Composition and lighting
129
+ prompt += `The screenshot should be perfectly fitted within the device screen area with accurate proportions. `;
130
+ prompt += `Add realistic lighting and shadows to make the device appear three-dimensional. `;
131
+ prompt += `Include subtle screen glare and reflections for realism. `;
132
+ prompt += `The device should be centered on a clean white or light gray background. `;
133
+ prompt += `Add a soft drop shadow beneath the device for depth. `;
134
+ // Final quality requirements
135
+ prompt += `The result should look professional and suitable for App Store marketing materials. `;
136
+ prompt += `Ensure the screenshot content is clearly visible and not distorted.`;
137
+ return prompt;
138
+ }
139
+ //# sourceMappingURL=device-mockup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device-mockup.js","sourceRoot":"","sources":["../../../src/tools/app-assets/device-mockup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { type ToolDefinition } from '@opencode-ai/plugin/tool';
2
+ export declare const generate_launch_images: ToolDefinition;
3
+ //# sourceMappingURL=launch-images.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launch-images.d.ts","sourceRoot":"","sources":["../../../src/tools/app-assets/launch-images.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AA8IrE,eAAO,MAAM,sBAAsB,EAAE,cA0EnC,CAAC"}