appshot-cli 0.4.0 → 0.6.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 +159 -17
- package/dist/cli.js +36 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +144 -61
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/caption.d.ts.map +1 -1
- package/dist/commands/caption.js +28 -1
- package/dist/commands/caption.js.map +1 -1
- package/dist/commands/doctor.d.ts +3 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +67 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/fonts.d.ts.map +1 -1
- package/dist/commands/fonts.js +89 -6
- package/dist/commands/fonts.js.map +1 -1
- package/dist/commands/gradients.d.ts.map +1 -1
- package/dist/commands/gradients.js +42 -2
- package/dist/commands/gradients.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +20 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/localize.d.ts.map +1 -1
- package/dist/commands/localize.js +33 -1
- package/dist/commands/localize.js.map +1 -1
- package/dist/commands/specs.d.ts.map +1 -1
- package/dist/commands/specs.js +61 -46
- package/dist/commands/specs.js.map +1 -1
- package/dist/commands/style.d.ts.map +1 -1
- package/dist/commands/style.js +35 -2
- package/dist/commands/style.js.map +1 -1
- package/dist/core/app-store-specs.d.ts +2 -0
- package/dist/core/app-store-specs.d.ts.map +1 -1
- package/dist/core/app-store-specs.js +2 -0
- package/dist/core/app-store-specs.js.map +1 -1
- package/dist/core/compose.d.ts +6 -0
- package/dist/core/compose.d.ts.map +1 -1
- package/dist/core/compose.js +104 -4
- package/dist/core/compose.js.map +1 -1
- package/dist/core/devices.d.ts +1 -1
- package/dist/core/devices.d.ts.map +1 -1
- package/dist/core/devices.js +5 -1
- package/dist/core/devices.js.map +1 -1
- package/dist/services/doctor.d.ts +35 -0
- package/dist/services/doctor.d.ts.map +1 -0
- package/dist/services/doctor.js +439 -0
- package/dist/services/doctor.js.map +1 -0
- package/dist/services/fonts.d.ts +37 -2
- package/dist/services/fonts.d.ts.map +1 -1
- package/dist/services/fonts.js +223 -0
- package/dist/services/fonts.js.map +1 -1
- package/dist/types/exec.d.ts +5 -0
- package/dist/types/exec.d.ts.map +1 -0
- package/dist/types/exec.js +2 -0
- package/dist/types/exec.js.map +1 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/fonts/DMSans/DMSans-LICENSE.txt +93 -0
- package/fonts/DMSans/DMSans-Regular.ttf +0 -0
- package/fonts/Inter/InterVariable-Italic.ttf +0 -0
- package/fonts/Inter/InterVariable.ttf +0 -0
- package/fonts/Inter/LICENSE.txt +92 -0
- package/fonts/Lato/Lato-Bold.ttf +0 -0
- package/fonts/Lato/Lato-BoldItalic.ttf +0 -0
- package/fonts/Lato/Lato-Italic.ttf +0 -0
- package/fonts/Lato/Lato-LICENSE.txt +93 -0
- package/fonts/Lato/Lato-Regular.ttf +0 -0
- package/fonts/Montserrat/Montserrat-Bold.ttf +2070 -0
- package/fonts/Montserrat/Montserrat-BoldItalic.ttf +2070 -0
- package/fonts/Montserrat/Montserrat-Italic.ttf +2070 -0
- package/fonts/Montserrat/Montserrat-LICENSE.txt +93 -0
- package/fonts/Montserrat/Montserrat-Regular.ttf +2070 -0
- package/fonts/OpenSans/OpenSans-LICENSE.txt +92 -0
- package/fonts/OpenSans/OpenSans-Regular.ttf +0 -0
- package/fonts/Poppins/Poppins-Bold.ttf +0 -0
- package/fonts/Poppins/Poppins-BoldItalic.ttf +0 -0
- package/fonts/Poppins/Poppins-Italic.ttf +0 -0
- package/fonts/Poppins/Poppins-LICENSE.txt +93 -0
- package/fonts/Poppins/Poppins-Regular.ttf +0 -0
- package/fonts/Roboto/Roboto-Bold.ttf +2070 -0
- package/fonts/Roboto/Roboto-BoldItalic.ttf +2070 -0
- package/fonts/Roboto/Roboto-Italic.ttf +2070 -0
- package/fonts/Roboto/Roboto-LICENSE.txt +2070 -0
- package/fonts/Roboto/Roboto-Regular.ttf +2070 -0
- package/fonts/WorkSans/WorkSans-LICENSE.txt +93 -0
- package/fonts/WorkSans/WorkSans-Regular.ttf +0 -0
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
[](https://www.npmjs.com/package/appshot-cli)
|
|
7
7
|
[](https://opensource.org/licenses/MIT)
|
|
8
8
|
|
|
9
|
-
🆕 **Version 0.
|
|
9
|
+
🆕 **Version 0.6.0** - 8 embedded OSS fonts with variants, dry-run mode, verbose debugging, and enhanced CLI UX!
|
|
10
10
|
|
|
11
11
|
> ⚠️ **BREAKING CHANGE in v0.4.0**: Output structure now always uses language subdirectories.
|
|
12
12
|
> Single language builds now output to `final/device/lang/` instead of `final/device/`.
|
|
@@ -55,6 +55,7 @@ Appshot is the only **agent-first CLI tool** designed for automated App Store sc
|
|
|
55
55
|
- 🖼️ **Smart Frames** - Automatically detects portrait/landscape and selects appropriate device frame
|
|
56
56
|
- 🎨 **Gradient Presets** - 24+ beautiful gradients with visual preview and easy application
|
|
57
57
|
- 🔤 **Font System** - 50+ font mappings, direct font setting, interactive selection, and system detection
|
|
58
|
+
- 📦 **Embedded Fonts** - 8 high-quality open source fonts bundled for consistent rendering everywhere
|
|
58
59
|
- ✏️ **Dynamic Captions** - Smart text wrapping, auto-sizing, and multi-line support
|
|
59
60
|
- 🌍 **AI Translation** - Real-time and batch translation using OpenAI's latest models
|
|
60
61
|
- 📱 **Multi-Device** - iPhone, iPad, Mac, Apple TV, Vision Pro, and Apple Watch support
|
|
@@ -62,6 +63,8 @@ Appshot is the only **agent-first CLI tool** designed for automated App Store sc
|
|
|
62
63
|
- 🔄 **Orientation Detection** - Intelligently handles both portrait and landscape
|
|
63
64
|
- ⚡ **Parallel Processing** - Configurable concurrency for large batches
|
|
64
65
|
- 🔍 **Caption Autocomplete** - Intelligent suggestions with fuzzy search and learning
|
|
66
|
+
- 🔬 **Dry-Run Mode** - Preview what would be built without generating images
|
|
67
|
+
- 🐛 **Verbose Debugging** - Detailed rendering metrics for troubleshooting
|
|
65
68
|
|
|
66
69
|
## 🚀 Quick Start
|
|
67
70
|
|
|
@@ -524,6 +527,8 @@ appshot build [options]
|
|
|
524
527
|
- `--langs <list>` - Build for specific languages (if not specified, auto-detects)
|
|
525
528
|
- `--preview` - Generate low-res previews
|
|
526
529
|
- `--concurrency <n>` - Parallel processing limit (default: 5)
|
|
530
|
+
- `--dry-run` - Show what would be rendered without generating images
|
|
531
|
+
- `--verbose` - Show detailed rendering information
|
|
527
532
|
- `--no-frame` - Skip device frames
|
|
528
533
|
- `--no-gradient` - Skip gradient backgrounds
|
|
529
534
|
- `--no-caption` - Skip captions
|
|
@@ -548,6 +553,12 @@ appshot build --preset iphone-6-9-portrait,ipad-13-landscape
|
|
|
548
553
|
|
|
549
554
|
# Preview mode
|
|
550
555
|
appshot build --preview --devices iphone
|
|
556
|
+
|
|
557
|
+
# Dry-run to see what would be built
|
|
558
|
+
appshot build --dry-run
|
|
559
|
+
|
|
560
|
+
# Verbose mode for debugging
|
|
561
|
+
appshot build --verbose --devices iphone
|
|
551
562
|
```
|
|
552
563
|
|
|
553
564
|
**Exit Codes:**
|
|
@@ -627,9 +638,65 @@ appshot clean --all --yes
|
|
|
627
638
|
appshot clean --history
|
|
628
639
|
```
|
|
629
640
|
|
|
641
|
+
### `appshot doctor`
|
|
642
|
+
|
|
643
|
+
Run comprehensive system diagnostics to verify appshot installation and dependencies.
|
|
644
|
+
|
|
645
|
+
```bash
|
|
646
|
+
appshot doctor [options]
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
**Options:**
|
|
650
|
+
- `--json` - Output results as JSON for CI/automation
|
|
651
|
+
- `--verbose` - Show detailed diagnostic information
|
|
652
|
+
- `--category <categories>` - Run specific checks (comma-separated: system,dependencies,fonts,filesystem,frames)
|
|
653
|
+
|
|
654
|
+
**Checks:**
|
|
655
|
+
- **System Requirements** - Node.js version (≥18), npm availability, platform detection
|
|
656
|
+
- **Dependencies** - Sharp module installation, native bindings, image processing test, OpenAI API key
|
|
657
|
+
- **Font System** - Font detection commands, system font loading, common font availability
|
|
658
|
+
- **File System** - Write permissions (current/temp directories), .appshot directory, configuration validity
|
|
659
|
+
- **Frame Assets** - Frame directory presence, Frames.json validation, device frame counts, missing files
|
|
660
|
+
|
|
661
|
+
**Examples:**
|
|
662
|
+
```bash
|
|
663
|
+
# Run all diagnostics
|
|
664
|
+
appshot doctor
|
|
665
|
+
|
|
666
|
+
# Check specific categories
|
|
667
|
+
appshot doctor --category system,dependencies
|
|
668
|
+
|
|
669
|
+
# JSON output for CI
|
|
670
|
+
appshot doctor --json
|
|
671
|
+
|
|
672
|
+
# Verbose mode for debugging
|
|
673
|
+
appshot doctor --verbose
|
|
674
|
+
```
|
|
675
|
+
|
|
676
|
+
**Output Example:**
|
|
677
|
+
```
|
|
678
|
+
🏥 Appshot Doctor - System Diagnostics
|
|
679
|
+
|
|
680
|
+
System Requirements:
|
|
681
|
+
✓ Node.js v20.5.0 (minimum: v18.0.0)
|
|
682
|
+
✓ npm v9.8.0
|
|
683
|
+
✓ darwin (macOS)
|
|
684
|
+
|
|
685
|
+
Dependencies:
|
|
686
|
+
✓ Sharp v0.33.5 installed
|
|
687
|
+
✓ libvips v8.15.3 loaded
|
|
688
|
+
✓ Sharp image processing test passed
|
|
689
|
+
⚠ OpenAI API key not found (translation features disabled)
|
|
690
|
+
|
|
691
|
+
Summary: 20 passed, 1 warning, 0 errors
|
|
692
|
+
|
|
693
|
+
Suggestions:
|
|
694
|
+
• Set OPENAI_API_KEY environment variable to enable translation features
|
|
695
|
+
```
|
|
696
|
+
|
|
630
697
|
### `appshot fonts`
|
|
631
698
|
|
|
632
|
-
Browse, validate, and set fonts for captions.
|
|
699
|
+
Browse, validate, and set fonts for captions. Includes 8 high-quality embedded fonts for consistent rendering across all platforms.
|
|
633
700
|
|
|
634
701
|
```bash
|
|
635
702
|
appshot fonts [options]
|
|
@@ -637,29 +704,47 @@ appshot fonts [options]
|
|
|
637
704
|
|
|
638
705
|
**Options:**
|
|
639
706
|
- `--all` - List all system fonts
|
|
707
|
+
- `--embedded` - Show embedded fonts bundled with appshot
|
|
640
708
|
- `--recommended` - Show recommended fonts only (default)
|
|
641
|
-
- `--validate <name>` - Check if font is available
|
|
709
|
+
- `--validate <name>` - Check if font is available (embedded or system)
|
|
642
710
|
- `--set <name>` - Set the caption font
|
|
643
711
|
- `--select` - Interactive font selection
|
|
644
712
|
- `--device <name>` - Target specific device (with --set or --select)
|
|
645
713
|
- `--json` - Output as JSON
|
|
646
714
|
|
|
715
|
+
**Embedded Fonts (Always Available):**
|
|
716
|
+
- **Modern UI**: Inter, Poppins, Montserrat, DM Sans
|
|
717
|
+
- **Popular Web**: Roboto, Open Sans, Lato, Work Sans
|
|
718
|
+
- **Variants**: Regular, Italic, Bold, and Bold Italic styles
|
|
719
|
+
|
|
720
|
+
All embedded fonts use open source licenses (OFL or Apache 2.0) and provide consistent rendering without requiring system installation. Font variants are automatically detected and properly rendered with correct styles.
|
|
721
|
+
|
|
647
722
|
**Examples:**
|
|
648
723
|
```bash
|
|
649
724
|
# Browse recommended fonts
|
|
650
725
|
appshot fonts
|
|
651
726
|
|
|
652
|
-
#
|
|
653
|
-
appshot fonts --
|
|
727
|
+
# Show embedded fonts
|
|
728
|
+
appshot fonts --embedded
|
|
729
|
+
|
|
730
|
+
# Set global font directly (embedded font)
|
|
731
|
+
appshot fonts --set "Inter"
|
|
732
|
+
|
|
733
|
+
# Set font variant (italic style)
|
|
734
|
+
appshot fonts --set "Poppins Italic"
|
|
735
|
+
|
|
736
|
+
# Set bold variant
|
|
737
|
+
appshot fonts --set "Montserrat Bold"
|
|
654
738
|
|
|
655
739
|
# Interactive font selection
|
|
656
740
|
appshot fonts --select
|
|
657
741
|
|
|
658
|
-
# Set device-specific font
|
|
659
|
-
appshot fonts --set "
|
|
742
|
+
# Set device-specific font variant
|
|
743
|
+
appshot fonts --set "Poppins Bold Italic" --device iphone
|
|
660
744
|
|
|
661
|
-
# Validate
|
|
662
|
-
appshot fonts --validate "
|
|
745
|
+
# Validate font availability
|
|
746
|
+
appshot fonts --validate "Inter" # Shows: embedded
|
|
747
|
+
appshot fonts --validate "Arial" # Shows: system installed
|
|
663
748
|
|
|
664
749
|
# List all system fonts
|
|
665
750
|
appshot fonts --all
|
|
@@ -798,21 +883,50 @@ appshot presets --generate iphone-6-9,ipad-13
|
|
|
798
883
|
|
|
799
884
|
### `appshot specs`
|
|
800
885
|
|
|
801
|
-
Display
|
|
886
|
+
Display complete Apple App Store screenshot specifications.
|
|
802
887
|
|
|
803
888
|
```bash
|
|
804
889
|
appshot specs [options]
|
|
805
890
|
```
|
|
806
891
|
|
|
807
892
|
**Options:**
|
|
808
|
-
- `--device <name>` - Filter by device
|
|
809
|
-
- `--json` - Output as JSON
|
|
893
|
+
- `--device <name>` - Filter by device type (iphone|ipad|mac|watch|appletv|visionpro)
|
|
894
|
+
- `--json` - Output as JSON (exact Apple specifications for diffing)
|
|
895
|
+
- `--required` - Show only required presets
|
|
810
896
|
|
|
811
897
|
**Shows:**
|
|
812
|
-
-
|
|
813
|
-
-
|
|
814
|
-
-
|
|
815
|
-
-
|
|
898
|
+
- Complete Apple specifications with exact resolutions
|
|
899
|
+
- Display sizes and device compatibility lists
|
|
900
|
+
- Required vs optional indicators
|
|
901
|
+
- Fallback notes and requirements
|
|
902
|
+
|
|
903
|
+
**JSON Output for Change Tracking:**
|
|
904
|
+
The `--json` flag outputs the complete Apple App Store specifications data, perfect for tracking changes over time:
|
|
905
|
+
|
|
906
|
+
```bash
|
|
907
|
+
# Save current specifications
|
|
908
|
+
appshot specs --json > apple-specs-2024-08.json
|
|
909
|
+
|
|
910
|
+
# After Apple updates (typically September)
|
|
911
|
+
appshot specs --json > apple-specs-2024-09.json
|
|
912
|
+
|
|
913
|
+
# See what changed
|
|
914
|
+
diff apple-specs-2024-08.json apple-specs-2024-09.json
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
**Data Source:**
|
|
918
|
+
The specifications mirror [Apple's official screenshot requirements](https://developer.apple.com/help/app-store-connect/reference/screenshot-specifications)
|
|
919
|
+
and are maintained in sync with Apple's updates. The JSON output preserves all metadata including:
|
|
920
|
+
- Exact resolutions (e.g., `1290x2796` for iPhone 6.9")
|
|
921
|
+
- Device groupings (which devices share requirements)
|
|
922
|
+
- Requirement status (mandatory vs optional)
|
|
923
|
+
- Fallback rules and special notes
|
|
924
|
+
|
|
925
|
+
This is particularly useful for:
|
|
926
|
+
- Tracking when Apple adds new device requirements
|
|
927
|
+
- Validating screenshot compliance before submission
|
|
928
|
+
- Automating screenshot generation pipelines
|
|
929
|
+
- Planning resource allocation for new devices
|
|
816
930
|
|
|
817
931
|
### `appshot style`
|
|
818
932
|
|
|
@@ -1369,6 +1483,34 @@ appshot build --devices iphone
|
|
|
1369
1483
|
appshot build --devices ipad
|
|
1370
1484
|
```
|
|
1371
1485
|
|
|
1486
|
+
### Debugging with Verbose Mode
|
|
1487
|
+
|
|
1488
|
+
Use `--verbose` flag to diagnose rendering issues:
|
|
1489
|
+
|
|
1490
|
+
```bash
|
|
1491
|
+
# See detailed caption metrics
|
|
1492
|
+
appshot build --verbose --devices iphone
|
|
1493
|
+
|
|
1494
|
+
# Output includes:
|
|
1495
|
+
# - Caption wrap width and line count
|
|
1496
|
+
# - Font stack with fallbacks
|
|
1497
|
+
# - Device frame scaling factors
|
|
1498
|
+
# - Position calculations
|
|
1499
|
+
```
|
|
1500
|
+
|
|
1501
|
+
Use `--dry-run` to validate configuration without processing:
|
|
1502
|
+
|
|
1503
|
+
```bash
|
|
1504
|
+
# Check what would be generated
|
|
1505
|
+
appshot build --dry-run
|
|
1506
|
+
|
|
1507
|
+
# Verify frame selection
|
|
1508
|
+
appshot build --dry-run --devices iphone
|
|
1509
|
+
|
|
1510
|
+
# Check multi-language output
|
|
1511
|
+
appshot build --dry-run --langs en,es,fr
|
|
1512
|
+
```
|
|
1513
|
+
|
|
1372
1514
|
### Performance Tips
|
|
1373
1515
|
|
|
1374
1516
|
1. **Use appropriate concurrency**
|
|
@@ -1547,7 +1689,7 @@ For security vulnerabilities, please see [SECURITY.md](SECURITY.md).
|
|
|
1547
1689
|
### NPM Package
|
|
1548
1690
|
|
|
1549
1691
|
- 📦 [appshot-cli on NPM](https://www.npmjs.com/package/appshot-cli)
|
|
1550
|
-
- 🔄 Latest version: 0.
|
|
1692
|
+
- 🔄 Latest version: 0.6.0
|
|
1551
1693
|
- ⬇️ Weekly downloads: 
|
|
1552
1694
|
|
|
1553
1695
|
---
|
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,7 @@ import localizeCmd from './commands/localize.js';
|
|
|
6
6
|
import buildCmd from './commands/build.js';
|
|
7
7
|
import specsCmd from './commands/specs.js';
|
|
8
8
|
import checkCmd from './commands/check.js';
|
|
9
|
+
import doctorCmd from './commands/doctor.js';
|
|
9
10
|
import presetsCmd from './commands/presets.js';
|
|
10
11
|
import validateCmd from './commands/validate.js';
|
|
11
12
|
import styleCmd from './commands/style.js';
|
|
@@ -16,8 +17,40 @@ import { createCleanCommand } from './commands/clean.js';
|
|
|
16
17
|
const program = new Command();
|
|
17
18
|
program
|
|
18
19
|
.name('appshot')
|
|
19
|
-
.description(
|
|
20
|
-
|
|
20
|
+
.description(`Generate App Store–ready screenshots with frames, gradients, and captions.
|
|
21
|
+
|
|
22
|
+
${pc.bold('Features:')}
|
|
23
|
+
• Auto-detects portrait/landscape orientation
|
|
24
|
+
• 8 embedded font families with italic & bold variants
|
|
25
|
+
• 24+ gradient presets with visual preview
|
|
26
|
+
• AI-powered translation to 25+ languages
|
|
27
|
+
• Smart caption wrapping and positioning
|
|
28
|
+
• All official App Store resolutions
|
|
29
|
+
• Parallel processing for large batches
|
|
30
|
+
|
|
31
|
+
${pc.bold('Quick Start:')}
|
|
32
|
+
$ appshot init # Initialize project
|
|
33
|
+
$ appshot caption --device iphone # Add captions
|
|
34
|
+
$ appshot build # Generate screenshots
|
|
35
|
+
|
|
36
|
+
${pc.bold('Common Workflows:')}
|
|
37
|
+
$ appshot fonts --set "Poppins Italic" # Set italic font
|
|
38
|
+
$ appshot gradients select # Pick gradient
|
|
39
|
+
$ appshot build --preset iphone-6-9,ipad-13 # App Store presets
|
|
40
|
+
$ appshot localize --langs es,fr,de # Batch translate
|
|
41
|
+
|
|
42
|
+
${pc.dim('Docs: https://github.com/chrisvanbuskirk/appshot')}`)
|
|
43
|
+
.version('0.6.0')
|
|
44
|
+
.addHelpText('after', `\n${pc.bold('Environment Variables:')}
|
|
45
|
+
OPENAI_API_KEY API key for translation features
|
|
46
|
+
APPSHOT_DISABLE_FONT_SCAN Skip system font detection (CI optimization)
|
|
47
|
+
|
|
48
|
+
${pc.bold('Configuration Files:')}
|
|
49
|
+
.appshot/config.json Main configuration
|
|
50
|
+
.appshot/captions/*.json Per-device captions
|
|
51
|
+
.appshot/caption-history.json Autocomplete history
|
|
52
|
+
|
|
53
|
+
${pc.dim('Run \'appshot <command> --help\' for command details.')}`);
|
|
21
54
|
program.addCommand(initCmd());
|
|
22
55
|
program.addCommand(captionCmd());
|
|
23
56
|
program.addCommand(styleCmd());
|
|
@@ -27,6 +60,7 @@ program.addCommand(localizeCmd());
|
|
|
27
60
|
program.addCommand(buildCmd());
|
|
28
61
|
program.addCommand(specsCmd());
|
|
29
62
|
program.addCommand(checkCmd());
|
|
63
|
+
program.addCommand(doctorCmd());
|
|
30
64
|
program.addCommand(presetsCmd());
|
|
31
65
|
program.addCommand(validateCmd());
|
|
32
66
|
program.addCommand(migrateCmd());
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,OAAO,MAAM,oBAAoB,CAAC;AACzC,OAAO,UAAU,MAAM,uBAAuB,CAAC;AAC/C,OAAO,WAAW,MAAM,wBAAwB,CAAC;AACjD,OAAO,QAAQ,MAAM,qBAAqB,CAAC;AAC3C,OAAO,QAAQ,MAAM,qBAAqB,CAAC;AAC3C,OAAO,QAAQ,MAAM,qBAAqB,CAAC;AAC3C,OAAO,UAAU,MAAM,uBAAuB,CAAC;AAC/C,OAAO,WAAW,MAAM,wBAAwB,CAAC;AACjD,OAAO,QAAQ,MAAM,qBAAqB,CAAC;AAC3C,OAAO,YAAY,MAAM,yBAAyB,CAAC;AACnD,OAAO,QAAQ,MAAM,qBAAqB,CAAC;AAC3C,OAAO,UAAU,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,SAAS,CAAC;KACf,WAAW,CAAC,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,OAAO,MAAM,oBAAoB,CAAC;AACzC,OAAO,UAAU,MAAM,uBAAuB,CAAC;AAC/C,OAAO,WAAW,MAAM,wBAAwB,CAAC;AACjD,OAAO,QAAQ,MAAM,qBAAqB,CAAC;AAC3C,OAAO,QAAQ,MAAM,qBAAqB,CAAC;AAC3C,OAAO,QAAQ,MAAM,qBAAqB,CAAC;AAC3C,OAAO,SAAS,MAAM,sBAAsB,CAAC;AAC7C,OAAO,UAAU,MAAM,uBAAuB,CAAC;AAC/C,OAAO,WAAW,MAAM,wBAAwB,CAAC;AACjD,OAAO,QAAQ,MAAM,qBAAqB,CAAC;AAC3C,OAAO,YAAY,MAAM,yBAAyB,CAAC;AACnD,OAAO,QAAQ,MAAM,qBAAqB,CAAC;AAC3C,OAAO,UAAU,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,SAAS,CAAC;KACf,WAAW,CAAC;;EAEb,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC;;;;;;;;;EASpB,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC;;;;;EAKvB,EAAE,CAAC,IAAI,CAAC,mBAAmB,CAAC;;;;;;EAM5B,EAAE,CAAC,GAAG,CAAC,kDAAkD,CAAC,EAAE,CAAC;KAC5D,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,wBAAwB,CAAC;;;;EAI5D,EAAE,CAAC,IAAI,CAAC,sBAAsB,CAAC;;;;;EAK/B,EAAE,CAAC,GAAG,CAAC,uDAAuD,CAAC,EAAE,CAAC,CAAC;AAErE,OAAO,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC;AAC9B,OAAO,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC;AAC/B,OAAO,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC;AAC/B,OAAO,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC;AAC/B,OAAO,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC;AAC/B,OAAO,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC;AAC/B,OAAO,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,kBAAkB,EAAE,CAAC,CAAC;AAEzC,OAAO,CAAC,kBAAkB,CAAC,EAAE,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC,CAAC;AAE9D,OAAO,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACjC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IAC7C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,MAAM,CAAC,OAAO,UAAU,QAAQ,
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,MAAM,CAAC,OAAO,UAAU,QAAQ,YA4T/B"}
|
package/dist/commands/build.js
CHANGED
|
@@ -9,7 +9,7 @@ import { composeAppStoreScreenshot } from '../core/compose.js';
|
|
|
9
9
|
import { resolveLanguages, normalizeLanguageCode } from '../utils/language.js';
|
|
10
10
|
export default function buildCmd() {
|
|
11
11
|
const cmd = new Command('build')
|
|
12
|
-
.description('
|
|
12
|
+
.description('Generate final screenshots with frames, gradients, and captions')
|
|
13
13
|
.option('--devices <list>', 'comma-separated device list (e.g., iphone,ipad)', 'iphone,ipad,mac,watch')
|
|
14
14
|
.option('--preset <ids>', 'use specific App Store presets (e.g., iphone-6-9,ipad-13)')
|
|
15
15
|
.option('--config <file>', 'use specific config file', 'appshot.json')
|
|
@@ -19,9 +19,51 @@ export default function buildCmd() {
|
|
|
19
19
|
.option('--no-frame', 'skip device frames')
|
|
20
20
|
.option('--no-gradient', 'skip gradient backgrounds')
|
|
21
21
|
.option('--no-caption', 'skip captions')
|
|
22
|
+
.option('--dry-run', 'show what would be rendered without generating images')
|
|
23
|
+
.option('--verbose', 'show detailed rendering information')
|
|
24
|
+
.addHelpText('after', `
|
|
25
|
+
${pc.bold('Examples:')}
|
|
26
|
+
${pc.dim('# Build all devices')}
|
|
27
|
+
$ appshot build
|
|
28
|
+
|
|
29
|
+
${pc.dim('# Build specific devices')}
|
|
30
|
+
$ appshot build --devices iphone,ipad
|
|
31
|
+
|
|
32
|
+
${pc.dim('# Build for multiple languages')}
|
|
33
|
+
$ appshot build --langs en,es,fr,de
|
|
34
|
+
|
|
35
|
+
${pc.dim('# Use App Store presets')}
|
|
36
|
+
$ appshot build --preset iphone-6-9,ipad-13
|
|
37
|
+
|
|
38
|
+
${pc.dim('# Fast preview mode')}
|
|
39
|
+
$ appshot build --preview --concurrency 8
|
|
40
|
+
|
|
41
|
+
${pc.dim('# Frames only (no gradient/caption)')}
|
|
42
|
+
$ appshot build --no-gradient --no-caption
|
|
43
|
+
|
|
44
|
+
${pc.dim('# Preview what would be built')}
|
|
45
|
+
$ appshot build --dry-run
|
|
46
|
+
|
|
47
|
+
${pc.dim('# Show detailed rendering info')}
|
|
48
|
+
$ appshot build --verbose
|
|
49
|
+
|
|
50
|
+
${pc.bold('Output:')}
|
|
51
|
+
Screenshots are saved to: ${pc.cyan('final/[device]/[language]/')}
|
|
52
|
+
|
|
53
|
+
${pc.bold('Language Detection:')}
|
|
54
|
+
1. --langs parameter (if provided)
|
|
55
|
+
2. Languages in caption files
|
|
56
|
+
3. defaultLanguage in config.json
|
|
57
|
+
4. System locale
|
|
58
|
+
5. Fallback to 'en'`)
|
|
22
59
|
.action(async (opts) => {
|
|
23
60
|
try {
|
|
24
|
-
|
|
61
|
+
if (opts.dryRun) {
|
|
62
|
+
console.log(pc.bold('Dry run mode - no images will be generated\n'));
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
console.log(pc.bold('Building screenshots...'));
|
|
66
|
+
}
|
|
25
67
|
// Load configuration
|
|
26
68
|
const config = await loadConfig();
|
|
27
69
|
const devices = opts.devices.split(',').map((d) => d.trim());
|
|
@@ -65,7 +107,7 @@ export default function buildCmd() {
|
|
|
65
107
|
// Resolve languages for this device
|
|
66
108
|
const cliLangs = opts.langs ? opts.langs.split(',').map((l) => normalizeLanguageCode(l.trim())) : undefined;
|
|
67
109
|
const { languages, source } = resolveLanguages(cliLangs, captions, config);
|
|
68
|
-
console.log(pc.cyan(`\n${device}:`),
|
|
110
|
+
console.log(pc.cyan(`\n${device}:`), `${opts.dryRun ? 'Would process' : 'Processing'} ${screenshots.length} screenshots`);
|
|
69
111
|
if (!cliLangs) {
|
|
70
112
|
console.log(pc.dim(` Using language: ${languages.join(', ')} (from ${source})`));
|
|
71
113
|
}
|
|
@@ -73,7 +115,9 @@ export default function buildCmd() {
|
|
|
73
115
|
for (const lang of languages) {
|
|
74
116
|
// Always use language subdirectory
|
|
75
117
|
const langDir = path.join(outputDir, lang);
|
|
76
|
-
|
|
118
|
+
if (!opts.dryRun) {
|
|
119
|
+
await fs.mkdir(langDir, { recursive: true });
|
|
120
|
+
}
|
|
77
121
|
// Process screenshots in batches
|
|
78
122
|
for (let i = 0; i < screenshots.length; i += concurrency) {
|
|
79
123
|
const batch = screenshots.slice(i, i + concurrency);
|
|
@@ -91,20 +135,22 @@ export default function buildCmd() {
|
|
|
91
135
|
captionText = captionData[lang] || '';
|
|
92
136
|
}
|
|
93
137
|
// Get screenshot dimensions and orientation
|
|
94
|
-
const { orientation } = await getImageDimensions(inputPath);
|
|
95
|
-
//
|
|
138
|
+
const { width: srcWidth, height: srcHeight, orientation } = await getImageDimensions(inputPath);
|
|
139
|
+
// In dry-run mode, skip loading the screenshot buffer
|
|
96
140
|
let screenshotBuffer;
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
141
|
+
if (!opts.dryRun) {
|
|
142
|
+
try {
|
|
143
|
+
// First verify the file exists and is readable
|
|
144
|
+
await fs.access(inputPath, fs.constants.R_OK);
|
|
145
|
+
// Load the screenshot into a buffer
|
|
146
|
+
screenshotBuffer = await sharp(inputPath)
|
|
147
|
+
.png() // Ensure output is PNG
|
|
148
|
+
.toBuffer();
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
console.error(pc.red(` ✗ ${path.basename(inputPath)}`), `Failed to load screenshot: ${error instanceof Error ? error.message : String(error)}`);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
108
154
|
}
|
|
109
155
|
// Parse resolution for output dimensions
|
|
110
156
|
const [configWidth, configHeight] = deviceConfig.resolution.split('x').map(Number);
|
|
@@ -134,54 +180,85 @@ export default function buildCmd() {
|
|
|
134
180
|
if (opts.frame !== false) {
|
|
135
181
|
// If autoFrame is disabled but preferredFrame is set, use the preferred frame
|
|
136
182
|
// Otherwise, auto-select a frame
|
|
137
|
-
const result = await autoSelectFrame(inputPath, path.resolve(config.frames), device, deviceConfig.preferredFrame
|
|
183
|
+
const result = await autoSelectFrame(inputPath, path.resolve(config.frames), device, deviceConfig.preferredFrame, opts.dryRun // Pass dry-run flag
|
|
184
|
+
);
|
|
138
185
|
frame = result.frame;
|
|
139
186
|
frameMetadata = result.metadata;
|
|
140
|
-
if (
|
|
187
|
+
if (frameMetadata) {
|
|
141
188
|
frameUsed = true;
|
|
142
|
-
|
|
189
|
+
if (opts.verbose || opts.dryRun) {
|
|
190
|
+
console.log(pc.dim(` ${opts.dryRun ? 'Would use' : 'Using'} ${frameMetadata.displayName} ${orientation} frame`));
|
|
191
|
+
}
|
|
143
192
|
}
|
|
144
|
-
else if (frameMetadata && !frame) {
|
|
193
|
+
else if (!opts.dryRun && frameMetadata && !frame) {
|
|
145
194
|
console.error(pc.red(' ERROR: Frame metadata found but image failed to load!'));
|
|
146
195
|
}
|
|
147
196
|
}
|
|
148
|
-
//
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
deviceConfig
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
197
|
+
// Handle dry-run vs actual rendering
|
|
198
|
+
if (opts.dryRun) {
|
|
199
|
+
// Dry-run output
|
|
200
|
+
console.log(pc.cyan(` ${screenshot}`) + pc.dim(` → ${lang}`));
|
|
201
|
+
// Show source dimensions
|
|
202
|
+
console.log(pc.dim(` Source: ${srcWidth}x${srcHeight} (${orientation})`));
|
|
203
|
+
// Show frame info
|
|
204
|
+
if (frameMetadata) {
|
|
205
|
+
console.log(pc.dim(` Frame: ${frameMetadata.displayName} (${frameMetadata.frameWidth}x${frameMetadata.frameHeight})`));
|
|
206
|
+
}
|
|
207
|
+
else if (opts.frame !== false) {
|
|
208
|
+
console.log(pc.dim(' Frame: No matching frame found'));
|
|
209
|
+
}
|
|
210
|
+
// Show caption info
|
|
211
|
+
if (captionText && opts.caption !== false) {
|
|
212
|
+
const lines = captionText.split('\n').length;
|
|
213
|
+
console.log(pc.dim(` Caption: "${captionText.substring(0, 50)}${captionText.length > 50 ? '...' : ''}" (${captionText.length} chars, ${lines} line${lines > 1 ? 's' : ''})`));
|
|
214
|
+
// Show font info
|
|
215
|
+
const fontName = deviceConfig.captionFont || config.caption.font;
|
|
216
|
+
console.log(pc.dim(` Font: ${fontName}`));
|
|
217
|
+
}
|
|
218
|
+
// Show output info
|
|
219
|
+
console.log(pc.dim(` Output: ${outWidth}x${outHeight} → ${outputPath}`));
|
|
220
|
+
totalProcessed++;
|
|
170
221
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
222
|
+
else {
|
|
223
|
+
// Actual rendering
|
|
224
|
+
let image;
|
|
225
|
+
try {
|
|
226
|
+
image = await composeAppStoreScreenshot({
|
|
227
|
+
screenshot: screenshotBuffer,
|
|
228
|
+
frame: frame,
|
|
229
|
+
frameMetadata: frameMetadata ? {
|
|
230
|
+
frameWidth: frameMetadata.frameWidth,
|
|
231
|
+
frameHeight: frameMetadata.frameHeight,
|
|
232
|
+
screenRect: frameMetadata.screenRect,
|
|
233
|
+
maskPath: frameMetadata.maskPath,
|
|
234
|
+
deviceType: frameMetadata.deviceType,
|
|
235
|
+
displayName: frameMetadata.displayName,
|
|
236
|
+
name: frameMetadata.name
|
|
237
|
+
} : undefined,
|
|
238
|
+
caption: opts.caption !== false ? captionText : undefined,
|
|
239
|
+
captionConfig: config.caption,
|
|
240
|
+
gradientConfig: config.gradient,
|
|
241
|
+
deviceConfig: deviceConfig,
|
|
242
|
+
outputWidth: outWidth,
|
|
243
|
+
outputHeight: outHeight,
|
|
244
|
+
verbose: opts.verbose
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
console.error(pc.red(` ✗ ${path.basename(inputPath)}`), error instanceof Error ? error.message : String(error));
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
// Save final image
|
|
252
|
+
await sharp(image)
|
|
253
|
+
.resize(opts.preview ? 800 : undefined, undefined, {
|
|
254
|
+
fit: 'inside',
|
|
255
|
+
withoutEnlargement: true
|
|
256
|
+
})
|
|
257
|
+
.png() // Ensure output is PNG
|
|
258
|
+
.toFile(outputPath);
|
|
259
|
+
console.log(pc.green(' ✓'), path.basename(outputPath), pc.dim(`[${orientation}${frameUsed ? ', framed' : ''}${captionText ? ', captioned' : ''}]`));
|
|
260
|
+
totalProcessed++;
|
|
174
261
|
}
|
|
175
|
-
// Save final image
|
|
176
|
-
await sharp(image)
|
|
177
|
-
.resize(opts.preview ? 800 : undefined, undefined, {
|
|
178
|
-
fit: 'inside',
|
|
179
|
-
withoutEnlargement: true
|
|
180
|
-
})
|
|
181
|
-
.png() // Ensure output is PNG
|
|
182
|
-
.toFile(outputPath);
|
|
183
|
-
console.log(pc.green(' ✓'), path.basename(outputPath), pc.dim(`[${orientation}${frameUsed ? ', framed' : ''}${captionText ? ', captioned' : ''}]`));
|
|
184
|
-
totalProcessed++;
|
|
185
262
|
}
|
|
186
263
|
catch (error) {
|
|
187
264
|
console.log(pc.red(' ✗'), screenshot, pc.dim(error instanceof Error ? error.message : String(error)));
|
|
@@ -193,12 +270,18 @@ export default function buildCmd() {
|
|
|
193
270
|
}
|
|
194
271
|
}
|
|
195
272
|
// Summary
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
273
|
+
if (opts.dryRun) {
|
|
274
|
+
console.log('\n' + pc.bold('Dry run complete!'));
|
|
275
|
+
console.log(pc.cyan(`→ ${totalProcessed} screenshots would be generated`));
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
console.log('\n' + pc.bold('Build complete!'));
|
|
279
|
+
console.log(pc.green(`✓ ${totalProcessed} screenshots processed`));
|
|
280
|
+
if (totalErrors > 0) {
|
|
281
|
+
console.log(pc.red(`✗ ${totalErrors} errors`));
|
|
282
|
+
}
|
|
283
|
+
console.log(pc.dim(`Output directory: ${config.output}`));
|
|
200
284
|
}
|
|
201
|
-
console.log(pc.dim(`Output directory: ${config.output}`));
|
|
202
285
|
}
|
|
203
286
|
catch (error) {
|
|
204
287
|
console.error(pc.red('Error:'), error instanceof Error ? error.message : String(error));
|