appshot-cli 0.5.0 → 0.7.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 +185 -14
- package/dist/cli.js +34 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +145 -62
- 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/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/style.d.ts.map +1 -1
- package/dist/commands/style.js +156 -2
- package/dist/commands/style.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 +433 -47
- 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/core/files.d.ts +1 -1
- package/dist/core/files.d.ts.map +1 -1
- package/dist/core/files.js +15 -3
- package/dist/core/files.js.map +1 -1
- package/dist/services/doctor.js +1 -1
- package/dist/services/fonts.d.ts +37 -2
- package/dist/services/fonts.d.ts.map +1 -1
- package/dist/services/fonts.js +225 -0
- package/dist/services/fonts.js.map +1 -1
- package/dist/types.d.ts +15 -2
- 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/FiraCode/FiraCode-Bold.ttf +0 -0
- package/fonts/FiraCode/FiraCode-LICENSE.txt +93 -0
- package/fonts/FiraCode/FiraCode-Light.ttf +0 -0
- package/fonts/FiraCode/FiraCode-Regular.ttf +0 -0
- package/fonts/FiraCode/FiraCode-SemiBold.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/JetBrainsMono/JetBrainsMono-Bold.ttf +0 -0
- package/fonts/JetBrainsMono/JetBrainsMono-BoldItalic.ttf +0 -0
- package/fonts/JetBrainsMono/JetBrainsMono-Italic.ttf +0 -0
- package/fonts/JetBrainsMono/JetBrainsMono-LICENSE.txt +93 -0
- package/fonts/JetBrainsMono/JetBrainsMono-Regular.ttf +0 -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,9 @@
|
|
|
6
6
|
[](https://www.npmjs.com/package/appshot-cli)
|
|
7
7
|
[](https://opensource.org/licenses/MIT)
|
|
8
8
|
|
|
9
|
-
🆕 **Version 0.
|
|
9
|
+
🆕 **Version 0.7.0** - **Enhanced caption styling**, flexible positioning (above/below/overlay), customizable backgrounds and borders for professional App Store screenshots!
|
|
10
|
+
|
|
11
|
+
> ⚠️ **NEW in v0.7.0**: Complete caption styling system with backgrounds, borders, and flexible positioning. Create professional captions with customizable colors, opacity, padding, and rounded corners.
|
|
10
12
|
|
|
11
13
|
> ⚠️ **BREAKING CHANGE in v0.4.0**: Output structure now always uses language subdirectories.
|
|
12
14
|
> Single language builds now output to `final/device/lang/` instead of `final/device/`.
|
|
@@ -55,6 +57,7 @@ Appshot is the only **agent-first CLI tool** designed for automated App Store sc
|
|
|
55
57
|
- 🖼️ **Smart Frames** - Automatically detects portrait/landscape and selects appropriate device frame
|
|
56
58
|
- 🎨 **Gradient Presets** - 24+ beautiful gradients with visual preview and easy application
|
|
57
59
|
- 🔤 **Font System** - 50+ font mappings, direct font setting, interactive selection, and system detection
|
|
60
|
+
- 📦 **Embedded Fonts** - 10 high-quality open source fonts bundled for consistent rendering everywhere
|
|
58
61
|
- ✏️ **Dynamic Captions** - Smart text wrapping, auto-sizing, and multi-line support
|
|
59
62
|
- 🌍 **AI Translation** - Real-time and batch translation using OpenAI's latest models
|
|
60
63
|
- 📱 **Multi-Device** - iPhone, iPad, Mac, Apple TV, Vision Pro, and Apple Watch support
|
|
@@ -62,6 +65,8 @@ Appshot is the only **agent-first CLI tool** designed for automated App Store sc
|
|
|
62
65
|
- 🔄 **Orientation Detection** - Intelligently handles both portrait and landscape
|
|
63
66
|
- ⚡ **Parallel Processing** - Configurable concurrency for large batches
|
|
64
67
|
- 🔍 **Caption Autocomplete** - Intelligent suggestions with fuzzy search and learning
|
|
68
|
+
- 🔬 **Dry-Run Mode** - Preview what would be built without generating images
|
|
69
|
+
- 🐛 **Verbose Debugging** - Detailed rendering metrics for troubleshooting
|
|
65
70
|
|
|
66
71
|
## 🚀 Quick Start
|
|
67
72
|
|
|
@@ -342,7 +347,7 @@ Intelligent caption rendering that adapts to content:
|
|
|
342
347
|
```json
|
|
343
348
|
{
|
|
344
349
|
"caption": {
|
|
345
|
-
"position": "above", // above or overlay
|
|
350
|
+
"position": "above", // above, below, or overlay
|
|
346
351
|
"box": {
|
|
347
352
|
"autoSize": true, // Dynamic height
|
|
348
353
|
"maxLines": 3, // Line limit
|
|
@@ -354,6 +359,97 @@ Intelligent caption rendering that adapts to content:
|
|
|
354
359
|
}
|
|
355
360
|
```
|
|
356
361
|
|
|
362
|
+
#### Caption Positioning Options
|
|
363
|
+
|
|
364
|
+
- **`above`** (default): Caption appears above the device frame
|
|
365
|
+
- **`below`** (new in v0.7.0): Caption appears below the device frame
|
|
366
|
+
- **`overlay`**: Caption overlays on the gradient background
|
|
367
|
+
|
|
368
|
+
```bash
|
|
369
|
+
# Configure caption below device
|
|
370
|
+
appshot style --device iphone
|
|
371
|
+
# → Select "Below device frame" option
|
|
372
|
+
|
|
373
|
+
# Or set directly in config
|
|
374
|
+
{
|
|
375
|
+
"devices": {
|
|
376
|
+
"iphone": {
|
|
377
|
+
"captionPosition": "below"
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
#### Caption Styling (New in v0.7.0)
|
|
384
|
+
|
|
385
|
+
Create professional captions with customizable backgrounds and borders:
|
|
386
|
+
|
|
387
|
+
```json
|
|
388
|
+
{
|
|
389
|
+
"caption": {
|
|
390
|
+
"color": "#FFFFFF", // Text color (hex)
|
|
391
|
+
"background": {
|
|
392
|
+
"color": "#000000", // Background color (hex)
|
|
393
|
+
"opacity": 0.8, // Transparency (0-1)
|
|
394
|
+
"padding": 20 // Padding around text
|
|
395
|
+
},
|
|
396
|
+
"border": {
|
|
397
|
+
"color": "#FFFFFF", // Border color (hex)
|
|
398
|
+
"width": 2, // Border thickness (1-10)
|
|
399
|
+
"radius": 12 // Rounded corners (0-30)
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
**Key Features:**
|
|
406
|
+
- **Full-width styling** - Backgrounds and borders span device width for uniformity
|
|
407
|
+
- **Flexible positioning** - Works with above, below, and overlay positions
|
|
408
|
+
- **Device-specific overrides** - Customize styling per device type
|
|
409
|
+
- **Professional appearance** - Rounded corners and padding for polished look
|
|
410
|
+
|
|
411
|
+
**Interactive Configuration:**
|
|
412
|
+
```bash
|
|
413
|
+
# Configure caption styling interactively
|
|
414
|
+
appshot style --device iphone
|
|
415
|
+
# → Choose caption position (above/below/overlay)
|
|
416
|
+
# → Configure background color and opacity
|
|
417
|
+
# → Set border color, width, and radius
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
**Examples:**
|
|
421
|
+
|
|
422
|
+
```json
|
|
423
|
+
// Dark background with white border
|
|
424
|
+
{
|
|
425
|
+
"caption": {
|
|
426
|
+
"color": "#FFFFFF",
|
|
427
|
+
"background": {
|
|
428
|
+
"color": "#000000",
|
|
429
|
+
"opacity": 0.9,
|
|
430
|
+
"padding": 30
|
|
431
|
+
},
|
|
432
|
+
"border": {
|
|
433
|
+
"color": "#FFFFFF",
|
|
434
|
+
"width": 3,
|
|
435
|
+
"radius": 16
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Subtle gradient-matched styling
|
|
441
|
+
{
|
|
442
|
+
"caption": {
|
|
443
|
+
"color": "#FFFFFF",
|
|
444
|
+
"background": {
|
|
445
|
+
"color": "#FF5733",
|
|
446
|
+
"opacity": 0.6,
|
|
447
|
+
"padding": 25
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
357
453
|
#### Caption Autocomplete
|
|
358
454
|
|
|
359
455
|
The caption command includes intelligent autocomplete:
|
|
@@ -524,6 +620,8 @@ appshot build [options]
|
|
|
524
620
|
- `--langs <list>` - Build for specific languages (if not specified, auto-detects)
|
|
525
621
|
- `--preview` - Generate low-res previews
|
|
526
622
|
- `--concurrency <n>` - Parallel processing limit (default: 5)
|
|
623
|
+
- `--dry-run` - Show what would be rendered without generating images
|
|
624
|
+
- `--verbose` - Show detailed rendering information
|
|
527
625
|
- `--no-frame` - Skip device frames
|
|
528
626
|
- `--no-gradient` - Skip gradient backgrounds
|
|
529
627
|
- `--no-caption` - Skip captions
|
|
@@ -548,6 +646,12 @@ appshot build --preset iphone-6-9-portrait,ipad-13-landscape
|
|
|
548
646
|
|
|
549
647
|
# Preview mode
|
|
550
648
|
appshot build --preview --devices iphone
|
|
649
|
+
|
|
650
|
+
# Dry-run to see what would be built
|
|
651
|
+
appshot build --dry-run
|
|
652
|
+
|
|
653
|
+
# Verbose mode for debugging
|
|
654
|
+
appshot build --verbose --devices iphone
|
|
551
655
|
```
|
|
552
656
|
|
|
553
657
|
**Exit Codes:**
|
|
@@ -685,7 +789,7 @@ Suggestions:
|
|
|
685
789
|
|
|
686
790
|
### `appshot fonts`
|
|
687
791
|
|
|
688
|
-
Browse, validate, and set fonts for captions.
|
|
792
|
+
Browse, validate, and set fonts for captions. Includes 8 high-quality embedded fonts for consistent rendering across all platforms.
|
|
689
793
|
|
|
690
794
|
```bash
|
|
691
795
|
appshot fonts [options]
|
|
@@ -693,29 +797,48 @@ appshot fonts [options]
|
|
|
693
797
|
|
|
694
798
|
**Options:**
|
|
695
799
|
- `--all` - List all system fonts
|
|
800
|
+
- `--embedded` - Show embedded fonts bundled with appshot
|
|
696
801
|
- `--recommended` - Show recommended fonts only (default)
|
|
697
|
-
- `--validate <name>` - Check if font is available
|
|
802
|
+
- `--validate <name>` - Check if font is available (embedded or system)
|
|
698
803
|
- `--set <name>` - Set the caption font
|
|
699
804
|
- `--select` - Interactive font selection
|
|
700
805
|
- `--device <name>` - Target specific device (with --set or --select)
|
|
701
806
|
- `--json` - Output as JSON
|
|
702
807
|
|
|
808
|
+
**Embedded Fonts (Always Available):**
|
|
809
|
+
- **Modern UI**: Inter, Poppins, Montserrat, DM Sans
|
|
810
|
+
- **Popular Web**: Roboto, Open Sans, Lato, Work Sans
|
|
811
|
+
- **Monospace**: JetBrains Mono, Fira Code
|
|
812
|
+
- **Variants**: Regular, Italic, Bold, and Bold Italic styles
|
|
813
|
+
|
|
814
|
+
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.
|
|
815
|
+
|
|
703
816
|
**Examples:**
|
|
704
817
|
```bash
|
|
705
818
|
# Browse recommended fonts
|
|
706
819
|
appshot fonts
|
|
707
820
|
|
|
708
|
-
#
|
|
709
|
-
appshot fonts --
|
|
821
|
+
# Show embedded fonts
|
|
822
|
+
appshot fonts --embedded
|
|
823
|
+
|
|
824
|
+
# Set global font directly (embedded font)
|
|
825
|
+
appshot fonts --set "Inter"
|
|
826
|
+
|
|
827
|
+
# Set font variant (italic style)
|
|
828
|
+
appshot fonts --set "Poppins Italic"
|
|
829
|
+
|
|
830
|
+
# Set bold variant
|
|
831
|
+
appshot fonts --set "Montserrat Bold"
|
|
710
832
|
|
|
711
833
|
# Interactive font selection
|
|
712
834
|
appshot fonts --select
|
|
713
835
|
|
|
714
|
-
# Set device-specific font
|
|
715
|
-
appshot fonts --set "
|
|
836
|
+
# Set device-specific font variant
|
|
837
|
+
appshot fonts --set "Poppins Bold Italic" --device iphone
|
|
716
838
|
|
|
717
|
-
# Validate
|
|
718
|
-
appshot fonts --validate "
|
|
839
|
+
# Validate font availability
|
|
840
|
+
appshot fonts --validate "Inter" # Shows: embedded
|
|
841
|
+
appshot fonts --validate "Arial" # Shows: system installed
|
|
719
842
|
|
|
720
843
|
# List all system fonts
|
|
721
844
|
appshot fonts --all
|
|
@@ -962,11 +1085,21 @@ appshot validate [options]
|
|
|
962
1085
|
"caption": {
|
|
963
1086
|
"font": "Font Name",
|
|
964
1087
|
"fontsize": 64, // Pixels
|
|
965
|
-
"color": "#FFFFFF",
|
|
1088
|
+
"color": "#FFFFFF", // Text color (hex)
|
|
966
1089
|
"align": "center", // left, center, right
|
|
967
|
-
"position": "above", // above, overlay
|
|
1090
|
+
"position": "above", // above, below, overlay
|
|
968
1091
|
"paddingTop": 100,
|
|
969
1092
|
"paddingBottom": 60,
|
|
1093
|
+
"background": { // Optional background styling
|
|
1094
|
+
"color": "#000000", // Background color (hex)
|
|
1095
|
+
"opacity": 0.8, // Transparency (0-1)
|
|
1096
|
+
"padding": 20 // Padding around text
|
|
1097
|
+
},
|
|
1098
|
+
"border": { // Optional border styling
|
|
1099
|
+
"color": "#FFFFFF", // Border color (hex)
|
|
1100
|
+
"width": 2, // Border thickness (1-10)
|
|
1101
|
+
"radius": 12 // Rounded corners (0-30)
|
|
1102
|
+
},
|
|
970
1103
|
"box": {
|
|
971
1104
|
"autoSize": true, // Dynamic height
|
|
972
1105
|
"maxLines": 3,
|
|
@@ -1022,6 +1155,16 @@ Each device can override global settings:
|
|
|
1022
1155
|
"captionFont": "SF Pro",
|
|
1023
1156
|
"captionSize": 64,
|
|
1024
1157
|
"captionPosition": "above",
|
|
1158
|
+
"captionBackground": { // Device-specific background
|
|
1159
|
+
"color": "#FF5733",
|
|
1160
|
+
"opacity": 0.7,
|
|
1161
|
+
"padding": 25
|
|
1162
|
+
},
|
|
1163
|
+
"captionBorder": { // Device-specific border
|
|
1164
|
+
"color": "#FFFFFF",
|
|
1165
|
+
"width": 3,
|
|
1166
|
+
"radius": 16
|
|
1167
|
+
},
|
|
1025
1168
|
"captionBox": {
|
|
1026
1169
|
"autoSize": false,
|
|
1027
1170
|
"minHeight": 320,
|
|
@@ -1454,6 +1597,34 @@ appshot build --devices iphone
|
|
|
1454
1597
|
appshot build --devices ipad
|
|
1455
1598
|
```
|
|
1456
1599
|
|
|
1600
|
+
### Debugging with Verbose Mode
|
|
1601
|
+
|
|
1602
|
+
Use `--verbose` flag to diagnose rendering issues:
|
|
1603
|
+
|
|
1604
|
+
```bash
|
|
1605
|
+
# See detailed caption metrics
|
|
1606
|
+
appshot build --verbose --devices iphone
|
|
1607
|
+
|
|
1608
|
+
# Output includes:
|
|
1609
|
+
# - Caption wrap width and line count
|
|
1610
|
+
# - Font stack with fallbacks
|
|
1611
|
+
# - Device frame scaling factors
|
|
1612
|
+
# - Position calculations
|
|
1613
|
+
```
|
|
1614
|
+
|
|
1615
|
+
Use `--dry-run` to validate configuration without processing:
|
|
1616
|
+
|
|
1617
|
+
```bash
|
|
1618
|
+
# Check what would be generated
|
|
1619
|
+
appshot build --dry-run
|
|
1620
|
+
|
|
1621
|
+
# Verify frame selection
|
|
1622
|
+
appshot build --dry-run --devices iphone
|
|
1623
|
+
|
|
1624
|
+
# Check multi-language output
|
|
1625
|
+
appshot build --dry-run --langs en,es,fr
|
|
1626
|
+
```
|
|
1627
|
+
|
|
1457
1628
|
### Performance Tips
|
|
1458
1629
|
|
|
1459
1630
|
1. **Use appropriate concurrency**
|
|
@@ -1590,7 +1761,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
|
|
|
1590
1761
|
|
|
1591
1762
|
### Completed ✅
|
|
1592
1763
|
- [x] Official App Store specifications
|
|
1593
|
-
- [x] Caption positioning (above/overlay)
|
|
1764
|
+
- [x] Caption positioning (above/below/overlay)
|
|
1594
1765
|
- [x] Partial frame support
|
|
1595
1766
|
- [x] Intelligent caption autocomplete
|
|
1596
1767
|
- [x] Apple Watch optimizations
|
|
@@ -1632,7 +1803,7 @@ For security vulnerabilities, please see [SECURITY.md](SECURITY.md).
|
|
|
1632
1803
|
### NPM Package
|
|
1633
1804
|
|
|
1634
1805
|
- 📦 [appshot-cli on NPM](https://www.npmjs.com/package/appshot-cli)
|
|
1635
|
-
- 🔄 Latest version: 0.
|
|
1806
|
+
- 🔄 Latest version: 0.6.0
|
|
1636
1807
|
- ⬇️ Weekly downloads: 
|
|
1637
1808
|
|
|
1638
1809
|
---
|
package/dist/cli.js
CHANGED
|
@@ -17,8 +17,40 @@ import { createCleanCommand } from './commands/clean.js';
|
|
|
17
17
|
const program = new Command();
|
|
18
18
|
program
|
|
19
19
|
.name('appshot')
|
|
20
|
-
.description(
|
|
21
|
-
|
|
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.7.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.')}`);
|
|
22
54
|
program.addCommand(initCmd());
|
|
23
55
|
program.addCommand(captionCmd());
|
|
24
56
|
program.addCommand(styleCmd());
|
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,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,
|
|
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,11 +19,53 @@ 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
|
-
const config = await loadConfig();
|
|
68
|
+
const config = await loadConfig(opts.config);
|
|
27
69
|
const devices = opts.devices.split(',').map((d) => d.trim());
|
|
28
70
|
const concurrency = parseInt(opts.concurrency, 10);
|
|
29
71
|
// Initialize frame registry from Frames.json if available
|
|
@@ -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));
|