appshot-cli 0.2.2 β†’ 0.3.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/README.md CHANGED
@@ -13,13 +13,14 @@ Generate beautiful, App Store-ready screenshots with device frames, gradients, a
13
13
  - πŸ–ΌοΈ **Smart Frames** - Automatically detects portrait/landscape and selects appropriate device frame
14
14
  - 🎨 **Gradients** - Beautiful gradient backgrounds with customizable colors
15
15
  - ✏️ **Captions** - Add marketing text with full typography control (above or overlay)
16
- - 🌍 **Localization** - Multi-language support for international app stores
16
+ - 🌍 **Localization** - Multi-language support with AI-powered translations for international app stores
17
17
  - πŸ“± **Multi-Device** - Support for iPhone, iPad, Mac, Apple TV, Vision Pro, and Apple Watch
18
18
  - πŸ“ **App Store Specs** - Built-in support for all official App Store screenshot resolutions
19
19
  - βœ… **Validation** - Verify screenshots meet App Store requirements
20
20
  - πŸ”„ **Orientation Detection** - Intelligently handles both portrait and landscape screenshots
21
21
  - ⚑ **Fast** - Parallel processing with configurable concurrency
22
- - πŸ› οΈ **CLI** - Simple command-line interface
22
+ - πŸ€– **AI Translation** - Real-time and batch translation using OpenAI models (GPT-4o, GPT-5, o1, o3)
23
+ - πŸ› οΈ **CLI** - Simple command-line interface designed for agents and automation
23
24
 
24
25
  ## Quick Start
25
26
 
@@ -200,7 +201,7 @@ Options:
200
201
  - `--force` - Overwrite existing files
201
202
 
202
203
  ### `appshot caption`
203
- Interactively add or edit captions for screenshots with intelligent autocomplete.
204
+ Interactively add or edit captions for screenshots with intelligent autocomplete and AI-powered translation.
204
205
 
205
206
  Features:
206
207
  - **Autocomplete suggestions** - Shows previous captions as you type
@@ -208,10 +209,23 @@ Features:
208
209
  - **Usage tracking** - Frequently used captions appear first
209
210
  - **Learning system** - Improves suggestions over time
210
211
  - **Device-specific** - Prioritizes captions used for the same device
212
+ - **Real-time translation** - Instantly translate captions as you type (requires OpenAI API key)
211
213
 
212
214
  Options:
213
215
  - `--device <name>` - Device name (required)
214
- - `--lang <code>` - Language code (default: en)
216
+ - `--lang <code>` - Primary language code (default: en)
217
+ - `--translate` - Enable AI-powered real-time translation
218
+ - `--langs <codes>` - Target languages for translation (comma-separated)
219
+ - `--model <name>` - OpenAI model to use (default: gpt-4o-mini)
220
+
221
+ Example with translation:
222
+ ```bash
223
+ # Add captions with instant translation to Spanish and French
224
+ appshot caption --device iphone --translate --langs es,fr
225
+
226
+ # Use a specific model for translation
227
+ appshot caption --device iphone --translate --langs zh-CN,ja --model gpt-5
228
+ ```
215
229
 
216
230
  Keyboard shortcuts:
217
231
  - **Tab** - Autocomplete the top suggestion
@@ -369,24 +383,206 @@ Options:
369
383
  - `--strict` - Validate against required presets only
370
384
  - `--fix` - Suggest fixes for invalid screenshots
371
385
 
372
- ### `appshot localize` (Coming Soon)
373
- Generate translations for captions using AI providers.
386
+ ### `appshot localize`
387
+ Generate translations for all existing captions using OpenAI's powerful language models.
374
388
 
375
389
  Options:
376
- - `--langs <codes>` - Target languages
377
- - `--device <name>` - Specific device or all
378
- - `--provider <name>` - Translation provider
390
+ - `--langs <codes>` - Target languages (comma-separated, required)
391
+ - `--device <name>` - Specific device or all devices
392
+ - `--model <name>` - OpenAI model to use (default: gpt-4o-mini)
393
+ - `--source <lang>` - Source language (default: en)
394
+ - `--review` - Review translations before saving
395
+ - `--overwrite` - Overwrite existing translations
396
+
397
+ Supported models:
398
+ - **GPT-4o models**: gpt-4o, gpt-4o-mini (using max_tokens)
399
+ - **GPT-5 models**: gpt-5, gpt-5-mini, gpt-5-nano (using max_completion_tokens)
400
+ - **o1 models**: o1, o1-mini (reasoning models)
401
+ - **o3 models**: o3, o3-mini (latest reasoning models)
402
+
403
+ Example usage:
404
+ ```bash
405
+ # Translate all captions to Spanish, French, and German
406
+ appshot localize --langs es,fr,de
407
+
408
+ # Translate iPhone captions only, with review
409
+ appshot localize --device iphone --langs ja,ko --review
410
+
411
+ # Use GPT-5 for higher quality translations
412
+ appshot localize --langs zh-CN,zh-TW --model gpt-5
413
+
414
+ # Overwrite existing translations with new ones
415
+ appshot localize --langs es,fr --overwrite
416
+ ```
417
+
418
+ Setup:
419
+ ```bash
420
+ # Set your OpenAI API key
421
+ export OPENAI_API_KEY="your-api-key-here"
422
+ ```
423
+
424
+ ## AI-Powered Translation πŸ€–
425
+
426
+ Appshot includes powerful AI translation capabilities using OpenAI's latest models, including GPT-4o, GPT-5, and reasoning models (o1, o3).
427
+
428
+ ### Setup
429
+
430
+ First, set your OpenAI API key:
431
+ ```bash
432
+ export OPENAI_API_KEY="your-api-key-here"
433
+ ```
434
+
435
+ ### Real-Time Translation
436
+
437
+ Translate captions instantly as you type them:
438
+
439
+ ```bash
440
+ # Add captions with automatic translation to Spanish and French
441
+ appshot caption --device iphone --translate --langs es,fr
442
+
443
+ # Use a specific model for higher quality
444
+ appshot caption --device iphone --translate --langs zh-CN,ja --model gpt-5
445
+ ```
446
+
447
+ When you enter a caption, translations appear immediately below:
448
+ ```
449
+ ? home.png: Welcome to the future
450
+ es: Bienvenido al futuro
451
+ fr: Bienvenue dans le futur
452
+ ```
453
+
454
+ ### Batch Translation
455
+
456
+ Translate all existing captions at once:
457
+
458
+ ```bash
459
+ # Translate all captions to multiple languages
460
+ appshot localize --langs es,fr,de,ja,zh-CN
461
+
462
+ # Translate specific device only
463
+ appshot localize --device iphone --langs es,fr
464
+
465
+ # Review translations before saving
466
+ appshot localize --langs es,fr --review
467
+
468
+ # Use GPT-5 for best quality
469
+ appshot localize --langs ja,ko --model gpt-5
470
+ ```
471
+
472
+ ### Supported Models
473
+
474
+ Appshot intelligently handles different OpenAI models and their specific requirements:
475
+
476
+ #### GPT-4o Series (General Purpose)
477
+ - **Models**: `gpt-4o`, `gpt-4o-mini`, `gpt-4-turbo`
478
+ - **Best for**: Fast, cost-effective translations
479
+ - **Parameter**: Uses `max_tokens`
480
+ - **Temperature**: Configurable (default 0.3)
481
+
482
+ #### GPT-5 Series (Advanced)
483
+ - **Models**: `gpt-5`, `gpt-5-mini`, `gpt-5-nano`
484
+ - **Best for**: High-quality, nuanced translations
485
+ - **Parameter**: Uses `max_completion_tokens`
486
+ - **Temperature**: Fixed at 1.0 (reasoning model requirement)
487
+
488
+ #### o1 Series (Deep Reasoning)
489
+ - **Models**: `o1`, `o1-mini`
490
+ - **Best for**: Complex marketing copy with cultural adaptation
491
+ - **Parameter**: Uses `max_completion_tokens`
492
+ - **Temperature**: Fixed at 1.0
493
+
494
+ #### o3 Series (Latest Reasoning)
495
+ - **Models**: `o3`, `o3-mini`
496
+ - **Best for**: State-of-the-art translation quality
497
+ - **Parameter**: Uses `max_completion_tokens`
498
+ - **Temperature**: Fixed at 1.0
499
+
500
+ ### Language Support
501
+
502
+ Built-in support for 25+ languages:
503
+ - **European**: Spanish (es), French (fr), German (de), Italian (it), Portuguese (pt), Dutch (nl), Swedish (sv), Norwegian (no), Danish (da), Finnish (fi), Polish (pl), Russian (ru)
504
+ - **Asian**: Japanese (ja), Korean (ko), Simplified Chinese (zh-CN), Traditional Chinese (zh-TW), Hindi (hi), Thai (th), Vietnamese (vi), Indonesian (id), Malay (ms)
505
+ - **Middle Eastern**: Arabic (ar), Hebrew (he), Turkish (tr)
506
+ - **Portuguese Variants**: Brazilian Portuguese (pt-BR)
507
+
508
+ ### Translation Features
509
+
510
+ #### Smart Caching
511
+ Translations are cached to avoid duplicate API calls. If you translate the same caption again, it returns instantly without using API credits.
512
+
513
+ #### Marketing-Optimized Prompts
514
+ The system uses specialized prompts designed for app marketing text:
515
+ - Maintains marketing tone and impact
516
+ - Keeps translations concise
517
+ - Ensures cultural appropriateness
518
+ - Preserves call-to-action strength
519
+
520
+ #### Error Handling
521
+ - Graceful fallback if API is unavailable
522
+ - Clear error messages for rate limits
523
+ - Automatic retry with delays for batch operations
524
+ - Continues with other translations if one fails
525
+
526
+ ### Advanced Configuration
527
+
528
+ Create `.appshot/ai-config.json` for custom settings:
529
+
530
+ ```json
531
+ {
532
+ "defaultModel": "gpt-5",
533
+ "temperature": 0.3,
534
+ "cache": true,
535
+ "systemPrompt": "You are translating premium app marketing text. Keep it impactful and concise."
536
+ }
537
+ ```
538
+
539
+ ### Cost Optimization Tips
540
+
541
+ 1. **Use `gpt-4o-mini`** for draft translations (very cost-effective)
542
+ 2. **Enable caching** to avoid retranslating identical text
543
+ 3. **Batch operations** are more efficient than real-time for large projects
544
+ 4. **Review mode** lets you verify before committing translations
545
+
546
+ ### Example Workflow
547
+
548
+ ```bash
549
+ # 1. Initialize project
550
+ appshot init
551
+
552
+ # 2. Add screenshots
553
+ cp ~/Desktop/screenshots/*.png screenshots/iphone/
554
+
555
+ # 3. Add captions with instant translation
556
+ appshot caption --device iphone --translate --langs es,fr,de
557
+
558
+ # 4. Build localized screenshots
559
+ appshot build --langs en,es,fr,de
560
+
561
+ # Output structure:
562
+ # final/
563
+ # iphone/
564
+ # en/
565
+ # home.png
566
+ # es/
567
+ # home.png
568
+ # fr/
569
+ # home.png
570
+ # de/
571
+ # home.png
572
+ ```
379
573
 
380
574
  ## Multi-Language Support
381
575
 
382
- Captions support multiple languages:
576
+ Captions are stored with all translations in JSON format:
383
577
 
384
578
  ```json
385
579
  {
386
580
  "home.png": {
387
581
  "en": "Organize your life",
388
582
  "fr": "Organisez votre vie",
389
- "es": "Organiza tu vida"
583
+ "es": "Organiza tu vida",
584
+ "de": "Organisieren Sie Ihr Leben",
585
+ "ja": "δΊΊη”Ÿγ‚’ζ•΄η†γ™γ‚‹"
390
586
  }
391
587
  }
392
588
  ```
@@ -798,7 +994,7 @@ def generate_app_store_screenshots():
798
994
  - [x] Intelligent caption autocomplete
799
995
  - [x] Apple Watch optimizations
800
996
  - [x] Gradient presets system (24+ gradients)
801
- - [ ] **AI-Powered Translations** - Translate captions using OpenAI/Anthropic/local LLMs
997
+ - [x] **AI-Powered Translations** - Translate captions using OpenAI models (GPT-4o, GPT-5, o1, o3)
802
998
  - [ ] **MCP Integration Guide** - Documentation for screenshot tool integration
803
999
  - [ ] **Agent API Mode** - Structured JSON input/output for all commands
804
1000
  - [ ] **Android Device Support** - Google Play Store specifications
package/dist/cli.js CHANGED
@@ -15,7 +15,7 @@ const program = new Command();
15
15
  program
16
16
  .name('appshot')
17
17
  .description('Generate App Store–ready screenshots with frames, gradients, and captions.')
18
- .version('0.1.0');
18
+ .version('0.3.0');
19
19
  program.addCommand(initCmd());
20
20
  program.addCommand(captionCmd());
21
21
  program.addCommand(styleCmd());
@@ -1 +1 @@
1
- {"version":3,"file":"caption.d.ts","sourceRoot":"","sources":["../../src/commands/caption.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC,MAAM,CAAC,OAAO,UAAU,UAAU,YA6HjC"}
1
+ {"version":3,"file":"caption.d.ts","sourceRoot":"","sources":["../../src/commands/caption.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmBpC,MAAM,CAAC,OAAO,UAAU,UAAU,YA0MjC"}
@@ -1,16 +1,21 @@
1
1
  import { Command } from 'commander';
2
2
  import autocomplete from 'inquirer-autocomplete-standalone';
3
+ import { select } from '@inquirer/prompts';
3
4
  import fuzzy from 'fuzzy';
4
5
  import { promises as fs } from 'fs';
5
6
  import path from 'path';
6
7
  import pc from 'picocolors';
8
+ import { translationService } from '../services/translation.js';
7
9
  import { loadCaptionHistory, saveCaptionHistory, updateFrequency, addToSuggestions, getSuggestions, learnFromExistingCaptions } from '../utils/caption-history.js';
8
10
  export default function captionCmd() {
9
11
  const cmd = new Command('caption')
10
12
  .description('Interactively add/edit captions in a device folder')
11
13
  .requiredOption('--device <name>', 'device name (iphone|ipad|mac|watch)')
12
- .option('--lang <code>', 'language code', 'en')
13
- .action(async ({ device, lang }) => {
14
+ .option('--lang <code>', 'primary language code', 'en')
15
+ .option('--translate', 'enable AI-powered translation')
16
+ .option('--langs <codes>', 'target languages for translation (comma-separated)')
17
+ .option('--model <name>', 'OpenAI model to use', 'gpt-4o-mini')
18
+ .action(async ({ device, lang, translate, langs, model }) => {
14
19
  try {
15
20
  const dir = path.join(process.cwd(), 'screenshots', device);
16
21
  const captionsFile = path.join(process.cwd(), '.appshot', 'captions', `${device}.json`);
@@ -44,6 +49,49 @@ export default function captionCmd() {
44
49
  // Load caption history for autocomplete
45
50
  const history = await loadCaptionHistory();
46
51
  await learnFromExistingCaptions(history);
52
+ // Check translation setup
53
+ let targetLanguages = [];
54
+ let selectedModel = model;
55
+ if (translate) {
56
+ if (!translationService.hasApiKey()) {
57
+ console.error(pc.red('Error:'), 'OpenAI API key not found');
58
+ console.log(pc.dim('Set the OPENAI_API_KEY environment variable to enable translations'));
59
+ process.exit(1);
60
+ }
61
+ // Parse target languages
62
+ if (langs) {
63
+ targetLanguages = langs.split(',').map((l) => l.trim());
64
+ }
65
+ else {
66
+ // Ask for target languages if not provided
67
+ console.log(pc.cyan('\nSelect target languages for translation:'));
68
+ console.log(pc.dim('Common options: es, fr, de, it, pt, ja, ko, zh-CN'));
69
+ const langsInput = await autocomplete({
70
+ message: 'Target languages (comma-separated):',
71
+ default: 'es,fr,de',
72
+ source: async () => []
73
+ });
74
+ targetLanguages = langsInput.split(',').map((l) => l.trim());
75
+ }
76
+ // Validate model selection
77
+ const availableModels = translationService.getAvailableModels();
78
+ if (!availableModels.includes(selectedModel)) {
79
+ console.log(pc.yellow('\nSelect AI model for translation:'));
80
+ selectedModel = await select({
81
+ message: 'Choose model:',
82
+ choices: availableModels.map(m => {
83
+ const info = translationService.getModelInfo(m);
84
+ return {
85
+ value: m,
86
+ description: info ? `Context: ${info.contextWindow}, Max output: ${info.maxTokens}` : ''
87
+ };
88
+ })
89
+ });
90
+ }
91
+ await translationService.loadConfig();
92
+ console.log(pc.green('βœ“'), `Translation enabled: ${lang} β†’ ${targetLanguages.join(', ')}`);
93
+ console.log(pc.dim(`Using model: ${selectedModel}\n`));
94
+ }
47
95
  console.log(pc.bold(`\nAdding captions for ${device} (${lang}):`));
48
96
  console.log(pc.dim('Type to search suggestions, use arrow keys to navigate'));
49
97
  console.log(pc.dim('Press Tab to autocomplete, Enter to confirm\n'));
@@ -100,6 +148,30 @@ export default function captionCmd() {
100
148
  captions[file] = {};
101
149
  }
102
150
  captions[file][lang] = text;
151
+ // Translate if enabled
152
+ if (translate && text && targetLanguages.length > 0) {
153
+ process.stdout.write(pc.dim(' Translating...'));
154
+ try {
155
+ const translations = await translationService.translate({
156
+ text,
157
+ targetLanguages,
158
+ model: selectedModel
159
+ });
160
+ // Clear the "Translating..." message
161
+ process.stdout.write('\r\x1b[K');
162
+ // Store translations
163
+ for (const [langCode, translation] of Object.entries(translations)) {
164
+ captions[file][langCode] = translation;
165
+ console.log(pc.dim(` ${langCode}: ${translation}`));
166
+ }
167
+ }
168
+ catch (error) {
169
+ // Clear the "Translating..." message
170
+ process.stdout.write('\r\x1b[K');
171
+ console.error(pc.yellow(' Translation failed:'), error instanceof Error ? error.message : String(error));
172
+ // Continue without translations
173
+ }
174
+ }
103
175
  }
104
176
  // Save updated captions
105
177
  await fs.writeFile(captionsFile, JSON.stringify(captions, null, 2), 'utf8');
@@ -1 +1 @@
1
- {"version":3,"file":"caption.js","sourceRoot":"","sources":["../../src/commands/caption.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"caption.js","sourceRoot":"","sources":["../../src/commands/caption.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"localize.d.ts","sourceRoot":"","sources":["../../src/commands/localize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,MAAM,CAAC,OAAO,UAAU,WAAW,YA0BlC"}
1
+ {"version":3,"file":"localize.d.ts","sourceRoot":"","sources":["../../src/commands/localize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,MAAM,CAAC,OAAO,UAAU,WAAW,YAyLlC"}
@@ -1,26 +1,162 @@
1
1
  import { Command } from 'commander';
2
+ import { select, confirm } from '@inquirer/prompts';
3
+ import { promises as fs } from 'fs';
4
+ import path from 'path';
2
5
  import pc from 'picocolors';
6
+ import { translationService } from '../services/translation.js';
3
7
  export default function localizeCmd() {
4
8
  return new Command('localize')
5
- .description('Generate translations into specified languages (requires API key)')
9
+ .description('Generate translations for all captions using AI')
6
10
  .requiredOption('--langs <codes>', 'comma-separated language codes (e.g., fr,de,es)')
7
11
  .option('--device <name>', 'specific device (iphone|ipad|mac|watch), or all devices')
8
- .option('--provider <name>', 'translation provider (openai|deepl)', 'openai')
12
+ .option('--model <name>', 'OpenAI model to use', 'gpt-4o-mini')
13
+ .option('--source <lang>', 'source language', 'en')
9
14
  .option('--review', 'review translations before saving')
15
+ .option('--overwrite', 'overwrite existing translations')
10
16
  .action(async (opts) => {
11
17
  try {
12
- console.log(pc.bold('Localization (Coming Soon)'));
13
- console.log('\nThis feature will:');
14
- console.log(' β€’ Read existing English captions from captions.json');
15
- console.log(' β€’ Translate them using your chosen provider');
16
- console.log(' β€’ Save translations back to captions.json');
17
- console.log('\nConfiguration:');
18
- console.log(' Languages:', pc.cyan(opts.langs));
19
- console.log(' Device:', pc.cyan(opts.device || 'all'));
20
- console.log(' Provider:', pc.cyan(opts.provider));
21
- console.log('\n' + pc.dim('Set API keys via environment variables:'));
22
- console.log(pc.dim(' β€’ OPENAI_API_KEY for OpenAI'));
23
- console.log(pc.dim(' β€’ DEEPL_API_KEY for DeepL'));
18
+ // Check for API key
19
+ if (!translationService.hasApiKey()) {
20
+ console.error(pc.red('Error:'), 'OpenAI API key not found');
21
+ console.log(pc.dim('Set the OPENAI_API_KEY environment variable:'));
22
+ console.log(pc.dim(' export OPENAI_API_KEY="your-api-key"'));
23
+ process.exit(1);
24
+ }
25
+ // Parse languages
26
+ const targetLanguages = opts.langs.split(',').map((l) => l.trim());
27
+ const sourceLang = opts.source;
28
+ // Select model
29
+ let selectedModel = opts.model;
30
+ const availableModels = translationService.getAvailableModels();
31
+ if (!availableModels.includes(selectedModel)) {
32
+ console.log(pc.yellow('\nSelect AI model for translation:'));
33
+ selectedModel = await select({
34
+ message: 'Choose model:',
35
+ choices: availableModels.map(m => {
36
+ const info = translationService.getModelInfo(m);
37
+ return {
38
+ value: m,
39
+ name: m,
40
+ description: info ? `Max output: ${info.maxTokens} tokens` : ''
41
+ };
42
+ })
43
+ });
44
+ }
45
+ await translationService.loadConfig();
46
+ console.log(pc.bold('\nπŸ“ Batch Translation'));
47
+ console.log('Source language:', pc.cyan(sourceLang));
48
+ console.log('Target languages:', pc.cyan(targetLanguages.join(', ')));
49
+ console.log('Model:', pc.cyan(selectedModel));
50
+ console.log();
51
+ // Determine which devices to process
52
+ const captionsDir = path.join(process.cwd(), '.appshot', 'captions');
53
+ let devices = [];
54
+ if (opts.device && opts.device !== 'all') {
55
+ devices = [opts.device];
56
+ }
57
+ else {
58
+ // Get all device caption files
59
+ try {
60
+ const files = await fs.readdir(captionsDir);
61
+ devices = files
62
+ .filter(f => f.endsWith('.json'))
63
+ .map(f => f.replace('.json', ''));
64
+ }
65
+ catch {
66
+ console.error(pc.red('Error:'), 'No caption files found');
67
+ console.log(pc.dim('Run'), pc.cyan('appshot caption'), pc.dim('to create captions first'));
68
+ process.exit(1);
69
+ }
70
+ }
71
+ // Process each device
72
+ let totalTranslated = 0;
73
+ let totalSkipped = 0;
74
+ for (const device of devices) {
75
+ const captionsFile = path.join(captionsDir, `${device}.json`);
76
+ try {
77
+ const content = await fs.readFile(captionsFile, 'utf8');
78
+ const captions = JSON.parse(content);
79
+ console.log(pc.cyan(`\n${device.toUpperCase()}`));
80
+ console.log(pc.dim('─'.repeat(40)));
81
+ // Collect unique captions to translate
82
+ const textsToTranslate = new Set();
83
+ for (const [_filename, caption] of Object.entries(captions)) {
84
+ if (typeof caption === 'string') {
85
+ textsToTranslate.add(caption);
86
+ }
87
+ else if (caption && typeof caption === 'object') {
88
+ const sourceText = caption[sourceLang];
89
+ if (sourceText) {
90
+ // Check if we should translate (no existing translations or overwrite enabled)
91
+ const hasExistingTranslations = targetLanguages.some((lang) => caption[lang]);
92
+ if (!hasExistingTranslations || opts.overwrite) {
93
+ textsToTranslate.add(sourceText);
94
+ }
95
+ }
96
+ }
97
+ }
98
+ if (textsToTranslate.size === 0) {
99
+ console.log(pc.yellow(' No captions to translate'));
100
+ totalSkipped++;
101
+ continue;
102
+ }
103
+ console.log(` Found ${textsToTranslate.size} unique caption(s) to translate`);
104
+ // Batch translate
105
+ const translations = await translationService.translateBatch(Array.from(textsToTranslate), targetLanguages, selectedModel, (current, total) => {
106
+ process.stdout.write(`\r Translating: ${current}/${total}`);
107
+ });
108
+ process.stdout.write('\r' + ' '.repeat(50) + '\r'); // Clear progress line
109
+ // Review translations if requested
110
+ if (opts.review) {
111
+ console.log(pc.yellow('\n Review translations:'));
112
+ for (const [text, trans] of translations.entries()) {
113
+ console.log(`\n Original: ${pc.white(text)}`);
114
+ for (const [lang, translation] of Object.entries(trans)) {
115
+ console.log(` ${lang}: ${pc.green(translation)}`);
116
+ }
117
+ const proceed = await confirm({
118
+ message: ' Accept these translations?',
119
+ default: true
120
+ });
121
+ if (!proceed) {
122
+ translations.delete(text);
123
+ }
124
+ }
125
+ }
126
+ // Apply translations to captions
127
+ for (const [filename, caption] of Object.entries(captions)) {
128
+ let sourceText;
129
+ if (typeof caption === 'string') {
130
+ sourceText = caption;
131
+ // Convert to object format
132
+ captions[filename] = { [sourceLang]: caption };
133
+ }
134
+ else if (caption && typeof caption === 'object') {
135
+ sourceText = caption[sourceLang];
136
+ }
137
+ if (sourceText && translations.has(sourceText)) {
138
+ const trans = translations.get(sourceText);
139
+ for (const [lang, translation] of Object.entries(trans)) {
140
+ captions[filename][lang] = translation;
141
+ }
142
+ }
143
+ }
144
+ // Save updated captions
145
+ await fs.writeFile(captionsFile, JSON.stringify(captions, null, 2), 'utf8');
146
+ console.log(pc.green(' βœ“'), `Translated ${translations.size} caption(s)`);
147
+ totalTranslated += translations.size;
148
+ }
149
+ catch (error) {
150
+ console.error(pc.red(` Error processing ${device}:`), error instanceof Error ? error.message : String(error));
151
+ }
152
+ }
153
+ // Summary
154
+ console.log(pc.bold('\nπŸ“Š Summary'));
155
+ console.log(pc.green('βœ“'), `Translated ${totalTranslated} unique caption(s)`);
156
+ if (totalSkipped > 0) {
157
+ console.log(pc.yellow('⚠'), `Skipped ${totalSkipped} device(s) with no captions`);
158
+ }
159
+ console.log(pc.dim('\nRun'), pc.cyan('appshot build --langs ' + targetLanguages.join(',')), pc.dim('to generate localized screenshots'));
24
160
  }
25
161
  catch (error) {
26
162
  console.error(pc.red('Error:'), error instanceof Error ? error.message : String(error));
@@ -1 +1 @@
1
- {"version":3,"file":"localize.js","sourceRoot":"","sources":["../../src/commands/localize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,YAAY,CAAC;AAE5B,MAAM,CAAC,OAAO,UAAU,WAAW;IACjC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC;SAC3B,WAAW,CAAC,mEAAmE,CAAC;SAChF,cAAc,CAAC,iBAAiB,EAAE,iDAAiD,CAAC;SACpF,MAAM,CAAC,iBAAiB,EAAE,yDAAyD,CAAC;SACpF,MAAM,CAAC,mBAAmB,EAAE,qCAAqC,EAAE,QAAQ,CAAC;SAC5E,MAAM,CAAC,UAAU,EAAE,mCAAmC,CAAC;SACvD,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QACrB,IAAI,CAAC;YACH,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC,CAAC;YACnD,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;YACpC,OAAO,CAAC,GAAG,CAAC,uDAAuD,CAAC,CAAC;YACrE,OAAO,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC;YAC7D,OAAO,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAC;YAC3D,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;YAChC,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;YACjD,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC;YACxD,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YACnD,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAC,CAAC;YACtE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC,CAAC;YACrD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAC,CAAC;QACrD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YACxF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
1
+ {"version":3,"file":"localize.js","sourceRoot":"","sources":["../../src/commands/localize.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { OpenAIModel, ModelConfig, TranslationOptions, TranslationResult, AIConfig } from '../types/ai.js';
2
+ export declare class TranslationService {
3
+ private client;
4
+ private config;
5
+ constructor();
6
+ private initialize;
7
+ hasApiKey(): boolean;
8
+ loadConfig(): Promise<void>;
9
+ saveConfig(config: Partial<AIConfig>): Promise<void>;
10
+ private getCacheKey;
11
+ translate(options: TranslationOptions): Promise<TranslationResult>;
12
+ translateBatch(captions: string[], targetLanguages: string[], model?: OpenAIModel, onProgress?: (current: number, total: number) => void): Promise<Map<string, TranslationResult>>;
13
+ getAvailableModels(): OpenAIModel[];
14
+ getModelInfo(model: OpenAIModel): ModelConfig | undefined;
15
+ clearCache(): void;
16
+ }
17
+ export declare const translationService: TranslationService;
18
+ //# sourceMappingURL=translation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translation.d.ts","sourceRoot":"","sources":["../../src/services/translation.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,iBAAiB,EACjB,QAAQ,EACT,MAAM,gBAAgB,CAAC;AAMxB,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,MAAM,CAAW;;IAYzB,OAAO,CAAC,UAAU;IAQX,SAAS,IAAI,OAAO;IAId,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAW3B,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAQjE,OAAO,CAAC,WAAW;IAIN,SAAS,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA8HlE,cAAc,CACzB,QAAQ,EAAE,MAAM,EAAE,EAClB,eAAe,EAAE,MAAM,EAAE,EACzB,KAAK,CAAC,EAAE,WAAW,EACnB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,GACpD,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IA+BnC,kBAAkB,IAAI,WAAW,EAAE;IAInC,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,WAAW,GAAG,SAAS;IAIzD,UAAU,IAAI,IAAI;CAG1B;AAGD,eAAO,MAAM,kBAAkB,oBAA2B,CAAC"}
@@ -0,0 +1,201 @@
1
+ import OpenAI from 'openai';
2
+ import { promises as fs } from 'fs';
3
+ import path from 'path';
4
+ import pc from 'picocolors';
5
+ import { MODEL_CONFIGS } from '../types/ai.js';
6
+ // Cache for translations to avoid duplicate API calls
7
+ const translationCache = new Map();
8
+ export class TranslationService {
9
+ client = null;
10
+ config;
11
+ constructor() {
12
+ this.config = {
13
+ defaultModel: 'gpt-4o-mini',
14
+ temperature: 0.3,
15
+ cache: true
16
+ };
17
+ this.initialize();
18
+ }
19
+ initialize() {
20
+ const apiKey = process.env.OPENAI_API_KEY;
21
+ if (apiKey) {
22
+ this.client = new OpenAI({ apiKey });
23
+ }
24
+ }
25
+ hasApiKey() {
26
+ return !!process.env.OPENAI_API_KEY;
27
+ }
28
+ async loadConfig() {
29
+ try {
30
+ const configPath = path.join(process.cwd(), '.appshot', 'ai-config.json');
31
+ const content = await fs.readFile(configPath, 'utf8');
32
+ const loadedConfig = JSON.parse(content);
33
+ this.config = { ...this.config, ...loadedConfig };
34
+ }
35
+ catch {
36
+ // Config file doesn't exist, use defaults
37
+ }
38
+ }
39
+ async saveConfig(config) {
40
+ const configPath = path.join(process.cwd(), '.appshot', 'ai-config.json');
41
+ this.config = { ...this.config, ...config };
42
+ await fs.mkdir(path.dirname(configPath), { recursive: true });
43
+ await fs.writeFile(configPath, JSON.stringify(this.config, null, 2), 'utf8');
44
+ }
45
+ getCacheKey(text, languages, model) {
46
+ return `${model}:${languages.sort().join(',')}:${text}`;
47
+ }
48
+ async translate(options) {
49
+ if (!this.client) {
50
+ throw new Error('OpenAI API key not found. Set OPENAI_API_KEY environment variable.');
51
+ }
52
+ const model = options.model || this.config.defaultModel;
53
+ const modelConfig = MODEL_CONFIGS[model];
54
+ if (!modelConfig) {
55
+ throw new Error(`Unknown model: ${model}`);
56
+ }
57
+ // Check cache
58
+ if (this.config.cache) {
59
+ const cacheKey = this.getCacheKey(options.text, options.targetLanguages, model);
60
+ const cached = translationCache.get(cacheKey);
61
+ if (cached) {
62
+ return cached;
63
+ }
64
+ }
65
+ // Build language map for the prompt
66
+ const languageNames = {
67
+ 'es': 'Spanish',
68
+ 'fr': 'French',
69
+ 'de': 'German',
70
+ 'it': 'Italian',
71
+ 'pt': 'Portuguese',
72
+ 'pt-BR': 'Brazilian Portuguese',
73
+ 'ru': 'Russian',
74
+ 'ja': 'Japanese',
75
+ 'ko': 'Korean',
76
+ 'zh-CN': 'Simplified Chinese',
77
+ 'zh-TW': 'Traditional Chinese',
78
+ 'ar': 'Arabic',
79
+ 'hi': 'Hindi',
80
+ 'nl': 'Dutch',
81
+ 'sv': 'Swedish',
82
+ 'no': 'Norwegian',
83
+ 'da': 'Danish',
84
+ 'fi': 'Finnish',
85
+ 'pl': 'Polish',
86
+ 'tr': 'Turkish',
87
+ 'th': 'Thai',
88
+ 'vi': 'Vietnamese',
89
+ 'id': 'Indonesian',
90
+ 'ms': 'Malay',
91
+ 'he': 'Hebrew'
92
+ };
93
+ const targetLangs = options.targetLanguages
94
+ .map(code => `${code}: ${languageNames[code] || code}`)
95
+ .join(', ');
96
+ const systemPrompt = options.systemPrompt || this.config.systemPrompt ||
97
+ `You are a professional app localization expert. Translate the given app screenshot caption into the requested languages.
98
+ The captions are marketing text for mobile app screenshots.
99
+ Keep translations concise, impactful, and culturally appropriate.
100
+ Maintain the marketing tone and appeal of the original text.`;
101
+ const userPrompt = `Translate this app screenshot caption into the following languages: ${targetLangs}
102
+
103
+ Caption: "${options.text}"
104
+
105
+ Return ONLY a JSON object with language codes as keys and translations as values. Example:
106
+ {
107
+ "es": "Spanish translation here",
108
+ "fr": "French translation here"
109
+ }`;
110
+ try {
111
+ // Build the request parameters based on model type
112
+ const params = {
113
+ model: modelConfig.model,
114
+ messages: [
115
+ { role: 'system', content: systemPrompt },
116
+ { role: 'user', content: userPrompt }
117
+ ],
118
+ temperature: modelConfig.temperature,
119
+ response_format: { type: 'json_object' }
120
+ };
121
+ // Use the appropriate max tokens parameter based on model
122
+ if (modelConfig.maxTokensParam === 'max_completion_tokens') {
123
+ params.max_completion_tokens = Math.min(modelConfig.maxTokens, 2000); // Limit for translations
124
+ }
125
+ else {
126
+ params.max_tokens = Math.min(modelConfig.maxTokens, 2000);
127
+ }
128
+ const response = await this.client.chat.completions.create(params);
129
+ const content = response.choices[0]?.message?.content;
130
+ if (!content) {
131
+ throw new Error('No response from OpenAI');
132
+ }
133
+ const translations = JSON.parse(content);
134
+ // Validate that we got all requested languages
135
+ for (const lang of options.targetLanguages) {
136
+ if (!translations[lang]) {
137
+ console.warn(pc.yellow(`Warning: Translation for ${lang} not returned by API`));
138
+ }
139
+ }
140
+ // Cache the result
141
+ if (this.config.cache) {
142
+ const cacheKey = this.getCacheKey(options.text, options.targetLanguages, model);
143
+ translationCache.set(cacheKey, translations);
144
+ }
145
+ return translations;
146
+ }
147
+ catch (error) {
148
+ if (error instanceof OpenAI.APIError) {
149
+ if (error.status === 401) {
150
+ throw new Error('Invalid OpenAI API key');
151
+ }
152
+ else if (error.status === 429) {
153
+ throw new Error('OpenAI API rate limit exceeded. Please try again later.');
154
+ }
155
+ else if (error.status === 404) {
156
+ throw new Error(`Model ${model} not found. You may not have access to this model.`);
157
+ }
158
+ }
159
+ throw error;
160
+ }
161
+ }
162
+ async translateBatch(captions, targetLanguages, model, onProgress) {
163
+ const results = new Map();
164
+ const total = captions.length;
165
+ for (let i = 0; i < captions.length; i++) {
166
+ if (onProgress) {
167
+ onProgress(i + 1, total);
168
+ }
169
+ try {
170
+ const translations = await this.translate({
171
+ text: captions[i],
172
+ targetLanguages,
173
+ model
174
+ });
175
+ results.set(captions[i], translations);
176
+ }
177
+ catch (error) {
178
+ console.error(pc.red(`Failed to translate: "${captions[i]}"`));
179
+ console.error(pc.dim(error instanceof Error ? error.message : String(error)));
180
+ // Continue with other translations
181
+ }
182
+ // Add a small delay to avoid rate limits
183
+ if (i < captions.length - 1) {
184
+ await new Promise(resolve => setTimeout(resolve, 200));
185
+ }
186
+ }
187
+ return results;
188
+ }
189
+ getAvailableModels() {
190
+ return Object.keys(MODEL_CONFIGS);
191
+ }
192
+ getModelInfo(model) {
193
+ return MODEL_CONFIGS[model];
194
+ }
195
+ clearCache() {
196
+ translationCache.clear();
197
+ }
198
+ }
199
+ // Singleton instance
200
+ export const translationService = new TranslationService();
201
+ //# sourceMappingURL=translation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translation.js","sourceRoot":"","sources":["../../src/services/translation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ export type OpenAIModel = 'gpt-4o' | 'gpt-4o-mini' | 'gpt-4-turbo' | 'gpt-5' | 'gpt-5-mini' | 'gpt-5-nano' | 'o1' | 'o1-mini' | 'o3' | 'o3-mini';
2
+ export interface ModelConfig {
3
+ model: OpenAIModel;
4
+ maxTokensParam: 'max_tokens' | 'max_completion_tokens';
5
+ maxTokens: number;
6
+ temperature?: number;
7
+ contextWindow: number;
8
+ }
9
+ export declare const MODEL_CONFIGS: Record<OpenAIModel, ModelConfig>;
10
+ export interface TranslationOptions {
11
+ text: string;
12
+ targetLanguages: string[];
13
+ model?: OpenAIModel;
14
+ systemPrompt?: string;
15
+ }
16
+ export interface TranslationResult {
17
+ [lang: string]: string;
18
+ }
19
+ export interface AIConfig {
20
+ defaultModel: OpenAIModel;
21
+ temperature?: number;
22
+ systemPrompt?: string;
23
+ cache?: boolean;
24
+ }
25
+ //# sourceMappingURL=ai.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai.d.ts","sourceRoot":"","sources":["../../src/types/ai.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GACnB,QAAQ,GACR,aAAa,GACb,aAAa,GACb,OAAO,GACP,YAAY,GACZ,YAAY,GACZ,IAAI,GACJ,SAAS,GACT,IAAI,GACJ,SAAS,CAAC;AAEd,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,WAAW,CAAC;IACnB,cAAc,EAAE,YAAY,GAAG,uBAAuB,CAAC;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,WAAW,EAAE,WAAW,CAuE1D,CAAC;AAEF,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,QAAQ;IACvB,YAAY,EAAE,WAAW,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB"}
@@ -0,0 +1,73 @@
1
+ export const MODEL_CONFIGS = {
2
+ 'gpt-4o': {
3
+ model: 'gpt-4o',
4
+ maxTokensParam: 'max_tokens',
5
+ maxTokens: 16384,
6
+ temperature: 0.3,
7
+ contextWindow: 128000
8
+ },
9
+ 'gpt-4o-mini': {
10
+ model: 'gpt-4o-mini',
11
+ maxTokensParam: 'max_tokens',
12
+ maxTokens: 16384,
13
+ temperature: 0.3,
14
+ contextWindow: 128000
15
+ },
16
+ 'gpt-4-turbo': {
17
+ model: 'gpt-4-turbo',
18
+ maxTokensParam: 'max_tokens',
19
+ maxTokens: 4096,
20
+ temperature: 0.3,
21
+ contextWindow: 128000
22
+ },
23
+ 'gpt-5': {
24
+ model: 'gpt-5',
25
+ maxTokensParam: 'max_completion_tokens',
26
+ maxTokens: 16384,
27
+ temperature: 1, // GPT-5 models have fixed temperature
28
+ contextWindow: 200000
29
+ },
30
+ 'gpt-5-mini': {
31
+ model: 'gpt-5-mini',
32
+ maxTokensParam: 'max_completion_tokens',
33
+ maxTokens: 8192,
34
+ temperature: 1, // GPT-5 models have fixed temperature
35
+ contextWindow: 128000
36
+ },
37
+ 'gpt-5-nano': {
38
+ model: 'gpt-5-nano',
39
+ maxTokensParam: 'max_completion_tokens',
40
+ maxTokens: 4096,
41
+ temperature: 1, // GPT-5 models have fixed temperature
42
+ contextWindow: 64000
43
+ },
44
+ 'o1': {
45
+ model: 'o1',
46
+ maxTokensParam: 'max_completion_tokens',
47
+ maxTokens: 100000,
48
+ temperature: 1, // o1 models have fixed temperature
49
+ contextWindow: 200000
50
+ },
51
+ 'o1-mini': {
52
+ model: 'o1-mini',
53
+ maxTokensParam: 'max_completion_tokens',
54
+ maxTokens: 65536,
55
+ temperature: 1,
56
+ contextWindow: 128000
57
+ },
58
+ 'o3': {
59
+ model: 'o3',
60
+ maxTokensParam: 'max_completion_tokens',
61
+ maxTokens: 100000,
62
+ temperature: 1,
63
+ contextWindow: 200000
64
+ },
65
+ 'o3-mini': {
66
+ model: 'o3-mini',
67
+ maxTokensParam: 'max_completion_tokens',
68
+ maxTokens: 65536,
69
+ temperature: 1,
70
+ contextWindow: 128000
71
+ }
72
+ };
73
+ //# sourceMappingURL=ai.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai.js","sourceRoot":"","sources":["../../src/types/ai.ts"],"names":[],"mappings":"AAoBA,MAAM,CAAC,MAAM,aAAa,GAAqC;IAC7D,QAAQ,EAAE;QACR,KAAK,EAAE,QAAQ;QACf,cAAc,EAAE,YAAY;QAC5B,SAAS,EAAE,KAAK;QAChB,WAAW,EAAE,GAAG;QAChB,aAAa,EAAE,MAAM;KACtB;IACD,aAAa,EAAE;QACb,KAAK,EAAE,aAAa;QACpB,cAAc,EAAE,YAAY;QAC5B,SAAS,EAAE,KAAK;QAChB,WAAW,EAAE,GAAG;QAChB,aAAa,EAAE,MAAM;KACtB;IACD,aAAa,EAAE;QACb,KAAK,EAAE,aAAa;QACpB,cAAc,EAAE,YAAY;QAC5B,SAAS,EAAE,IAAI;QACf,WAAW,EAAE,GAAG;QAChB,aAAa,EAAE,MAAM;KACtB;IACD,OAAO,EAAE;QACP,KAAK,EAAE,OAAO;QACd,cAAc,EAAE,uBAAuB;QACvC,SAAS,EAAE,KAAK;QAChB,WAAW,EAAE,CAAC,EAAE,sCAAsC;QACtD,aAAa,EAAE,MAAM;KACtB;IACD,YAAY,EAAE;QACZ,KAAK,EAAE,YAAY;QACnB,cAAc,EAAE,uBAAuB;QACvC,SAAS,EAAE,IAAI;QACf,WAAW,EAAE,CAAC,EAAE,sCAAsC;QACtD,aAAa,EAAE,MAAM;KACtB;IACD,YAAY,EAAE;QACZ,KAAK,EAAE,YAAY;QACnB,cAAc,EAAE,uBAAuB;QACvC,SAAS,EAAE,IAAI;QACf,WAAW,EAAE,CAAC,EAAE,sCAAsC;QACtD,aAAa,EAAE,KAAK;KACrB;IACD,IAAI,EAAE;QACJ,KAAK,EAAE,IAAI;QACX,cAAc,EAAE,uBAAuB;QACvC,SAAS,EAAE,MAAM;QACjB,WAAW,EAAE,CAAC,EAAE,mCAAmC;QACnD,aAAa,EAAE,MAAM;KACtB;IACD,SAAS,EAAE;QACT,KAAK,EAAE,SAAS;QAChB,cAAc,EAAE,uBAAuB;QACvC,SAAS,EAAE,KAAK;QAChB,WAAW,EAAE,CAAC;QACd,aAAa,EAAE,MAAM;KACtB;IACD,IAAI,EAAE;QACJ,KAAK,EAAE,IAAI;QACX,cAAc,EAAE,uBAAuB;QACvC,SAAS,EAAE,MAAM;QACjB,WAAW,EAAE,CAAC;QACd,aAAa,EAAE,MAAM;KACtB;IACD,SAAS,EAAE;QACT,KAAK,EAAE,SAAS;QAChB,cAAc,EAAE,uBAAuB;QACvC,SAAS,EAAE,KAAK;QAChB,WAAW,EAAE,CAAC;QACd,aAAa,EAAE,MAAM;KACtB;CACF,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appshot-cli",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "Generate App Store–ready screenshots with frames, gradients, and captions.",
5
5
  "bin": {
6
6
  "appshot": "bin/appshot.js"
@@ -18,10 +18,12 @@
18
18
  "clean:all": "rm -rf final/ dist/ .appshot/"
19
19
  },
20
20
  "dependencies": {
21
+ "@inquirer/prompts": "^7.8.3",
21
22
  "commander": "^12.0.0",
22
23
  "fuzzy": "^0.1.3",
23
24
  "inquirer": "^12.0.0",
24
25
  "inquirer-autocomplete-standalone": "^0.8.1",
26
+ "openai": "^5.13.1",
25
27
  "picocolors": "^1.0.0",
26
28
  "sharp": "^0.33.5"
27
29
  },