appshot-cli 0.8.5 → 0.8.6

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
@@ -8,9 +8,9 @@
8
8
  [![Node.js Version](https://img.shields.io/node/v/appshot-cli.svg)](https://nodejs.org)
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
10
10
 
11
- 🆕 **Version 0.8.5** - **Watch Mode & Device Capture** - File system monitoring with auto-processing and direct iOS simulator capture!
11
+ 🆕 **Version 0.8.6** - **Background Images** - Replace gradients with custom static images, auto-detection, and dimension validation!
12
12
 
13
- > ⚠️ **NEW in v0.8.5**: `appshot watch` for automatic screenshot processing and `appshot device` for iOS simulator capture (macOS only). Includes duplicate detection and background service management.
13
+ > ⚠️ **NEW in v0.8.6**: Use custom background images instead of gradients! Auto-detects `background.png` in device folders, supports multiple fit modes, and validates dimensions against App Store specs.
14
14
 
15
15
  > ⚠️ **BREAKING CHANGE in v0.4.0**: Output structure now always uses language subdirectories.
16
16
  > Single language builds now output to `final/device/lang/` instead of `final/device/`.
@@ -267,6 +267,129 @@ appshot gradients --sample
267
267
  }
268
268
  ```
269
269
 
270
+ ### Background System (NEW in v0.8.6)
271
+
272
+ Replace gradients with custom static background images for a unique, branded look. Appshot supports automatic detection, multiple formats, and intelligent scaling.
273
+
274
+ #### Background Locations
275
+
276
+ Backgrounds are searched in priority order:
277
+
278
+ 1. **Device-specific**: `screenshots/<device>/background.png`
279
+ 2. **Global**: `screenshots/background.png`
280
+ 3. **Custom**: Path specified via config or CLI
281
+
282
+ #### Background Commands
283
+
284
+ ```bash
285
+ # Set background for a device
286
+ appshot backgrounds set iphone ./backgrounds/sunset.jpg
287
+
288
+ # Set global background for all devices
289
+ appshot backgrounds set --global ./backgrounds/brand-bg.png
290
+
291
+ # Validate dimensions against App Store specs
292
+ appshot backgrounds validate
293
+
294
+ # List all configured backgrounds
295
+ appshot backgrounds list
296
+
297
+ # Clear background configuration
298
+ appshot backgrounds clear iphone
299
+ ```
300
+
301
+ #### Build Options
302
+
303
+ ```bash
304
+ # Auto-detect background.png in device folders
305
+ appshot build --auto-background
306
+
307
+ # Use specific background image
308
+ appshot build --background ./assets/custom-bg.png
309
+
310
+ # Set background fit mode
311
+ appshot build --background-fit cover
312
+
313
+ # Disable backgrounds (transparent)
314
+ appshot build --no-background
315
+ ```
316
+
317
+ #### Fit Modes
318
+
319
+ - **`cover`** - Scale to cover entire area (may crop)
320
+ - **`contain`** - Scale to fit within area (may add letterbox bars)
321
+ - **`fill`** - Stretch to exact dimensions (may distort)
322
+ - **`scale-down`** - Only scale down if larger, never scale up
323
+
324
+ #### Creating Backgrounds with ImageMagick
325
+
326
+ ImageMagick is a powerful CLI tool for creating custom backgrounds:
327
+
328
+ ```bash
329
+ # Solid color background
330
+ magick -size 1290x2796 canvas:navy background.png
331
+
332
+ # Gradient background
333
+ magick -size 1290x2796 gradient:blue-purple background.png
334
+
335
+ # Radial gradient
336
+ magick -size 1290x2796 radial-gradient:white-darkblue background.png
337
+
338
+ # Plasma fractal pattern
339
+ magick -size 1290x2796 plasma:fractal background.png
340
+
341
+ # Blurred noise texture
342
+ magick -size 1290x2796 xc: +noise Random -blur 0x10 background.png
343
+
344
+ # Tiled pattern
345
+ magick -size 100x100 pattern:checkerboard -scale 1290x2796 background.png
346
+
347
+ # Multi-point color interpolation
348
+ magick -size 1290x2796 xc: -sparse-color barycentric \
349
+ '0,0 skyblue 1290,0 white 645,2796 lightblue' background.png
350
+ ```
351
+
352
+ #### Configuration
353
+
354
+ ```json
355
+ {
356
+ "background": {
357
+ "mode": "image",
358
+ "image": "./backgrounds/global.png",
359
+ "fit": "cover"
360
+ },
361
+ "devices": {
362
+ "iphone": {
363
+ "background": {
364
+ "image": "./backgrounds/iphone.png",
365
+ "fit": "contain"
366
+ }
367
+ }
368
+ }
369
+ }
370
+ ```
371
+
372
+ #### Mixed Configurations
373
+
374
+ You can mix backgrounds and gradients across devices:
375
+
376
+ - iPhone uses a custom background image
377
+ - iPad falls back to gradient
378
+ - Mac uses a different background
379
+ - Watch uses the global background
380
+
381
+ This flexibility allows you to optimize each device's appearance independently.
382
+
383
+ #### Dimension Validation
384
+
385
+ Appshot validates background dimensions and warns about:
386
+
387
+ - Images smaller than target resolution (will be upscaled)
388
+ - Aspect ratio mismatches (may cause cropping/distortion)
389
+ - Large file sizes (>10MB triggers optimization suggestion)
390
+
391
+ Use `appshot backgrounds validate` to check all backgrounds before building.
392
+
270
393
  ### Font System
271
394
 
272
395
  Version 0.4.0 introduces comprehensive font management with intelligent fallbacks.
@@ -2044,7 +2167,7 @@ For security vulnerabilities, please see [SECURITY.md](SECURITY.md).
2044
2167
  ### NPM Package
2045
2168
 
2046
2169
  - 📦 [appshot-cli on NPM](https://www.npmjs.com/package/appshot-cli)
2047
- - 🔄 Latest version: 0.8.5
2170
+ - 🔄 Latest version: 0.8.6
2048
2171
 
2049
2172
  ---
2050
2173
 
package/dist/cli.js CHANGED
@@ -12,6 +12,7 @@ import presetsCmd from './commands/presets.js';
12
12
  import validateCmd from './commands/validate.js';
13
13
  import styleCmd from './commands/style.js';
14
14
  import gradientsCmd from './commands/gradients.js';
15
+ import backgroundsCmd from './commands/backgrounds.js';
15
16
  import fontsCmd from './commands/fonts.js';
16
17
  import migrateCmd from './commands/migrate.js';
17
18
  import { createCleanCommand } from './commands/clean.js';
@@ -23,12 +24,12 @@ import watchStatusCmd from './commands/watch-status.js';
23
24
  const program = new Command();
24
25
  program
25
26
  .name('appshot')
26
- .description(`Generate App Store–ready screenshots with frames, gradients, and captions.
27
+ .description(`Generate App Store–ready screenshots with frames, backgrounds, and captions.
27
28
 
28
29
  ${pc.bold('Features:')}
29
30
  • Auto-detects portrait/landscape orientation
30
31
  • 8 embedded font families with italic & bold variants
31
- • 24+ gradient presets with visual preview
32
+ Custom background images or 24+ gradient presets
32
33
  • AI-powered translation to 25+ languages
33
34
  • Smart caption wrapping and positioning
34
35
  • All official App Store resolutions
@@ -44,6 +45,7 @@ ${pc.bold('Quick Start:')}
44
45
  ${pc.bold('Common Workflows:')}
45
46
  $ appshot fonts --set "Poppins Italic" # Set italic font
46
47
  $ appshot gradients select # Pick gradient
48
+ $ appshot backgrounds set iphone bg.jpg # Set background image
47
49
  $ appshot frame ./screenshots --recursive # Batch frame images
48
50
  $ appshot build --preset iphone-6-9,ipad-13 # App Store presets
49
51
  $ appshot localize --langs es,fr,de # Batch translate${platform() === 'darwin' ? `
@@ -51,7 +53,7 @@ ${pc.bold('Common Workflows:')}
51
53
  $ appshot watch start --process # Auto-process new screenshots` : ''}
52
54
 
53
55
  ${pc.dim('Docs: https://github.com/chrisvanbuskirk/appshot')}`)
54
- .version('0.8.5')
56
+ .version('0.8.6')
55
57
  .addHelpText('after', `\n${pc.bold('Environment Variables:')}
56
58
  OPENAI_API_KEY API key for translation features
57
59
  APPSHOT_DISABLE_FONT_SCAN Skip system font detection (CI optimization)
@@ -66,6 +68,7 @@ program.addCommand(initCmd());
66
68
  program.addCommand(captionCmd());
67
69
  program.addCommand(styleCmd());
68
70
  program.addCommand(gradientsCmd());
71
+ program.addCommand(backgroundsCmd());
69
72
  program.addCommand(fontsCmd());
70
73
  program.addCommand(localizeCmd());
71
74
  program.addCommand(buildCmd());
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export default function backgroundsCommand(): Command;
3
+ //# sourceMappingURL=backgrounds.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backgrounds.d.ts","sourceRoot":"","sources":["../../src/commands/backgrounds.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,MAAM,CAAC,OAAO,UAAU,kBAAkB,IAAI,OAAO,CAmWpD"}
@@ -0,0 +1,323 @@
1
+ import { Command } from 'commander';
2
+ import { promises as fs } from 'fs';
3
+ import path from 'path';
4
+ import pc from 'picocolors';
5
+ import { select, input } from '@inquirer/prompts';
6
+ import { validateBackgroundDimensions, detectBestFit } from '../core/background.js';
7
+ import { loadConfig, saveConfig } from '../core/files.js';
8
+ export default function backgroundsCommand() {
9
+ const cmd = new Command('backgrounds')
10
+ .description('Manage background images for screenshots')
11
+ .addHelpText('after', `
12
+ ${pc.bold('Examples:')}
13
+ $ appshot backgrounds set iphone ./backgrounds/sunset.jpg
14
+ $ appshot backgrounds validate
15
+ $ appshot backgrounds preview
16
+ $ appshot backgrounds clear iphone
17
+ $ appshot backgrounds list
18
+
19
+ ${pc.bold('Background Locations:')}
20
+ Device-specific: screenshots/<device>/background.png
21
+ Global: screenshots/background.png
22
+ Custom: Specified via config or command
23
+
24
+ ${pc.bold('Fit Modes:')}
25
+ cover Scale to cover entire area (may crop)
26
+ contain Scale to fit within area (may add bars)
27
+ fill Stretch to exact dimensions (may distort)
28
+ scale-down Only scale down if larger, never scale up
29
+ `);
30
+ // Set background for a device
31
+ cmd
32
+ .command('set')
33
+ .description('Set background image for a device')
34
+ .argument('[device]', 'Device type (iphone, ipad, mac, watch)')
35
+ .argument('[image]', 'Path to background image')
36
+ .option('-f, --fit <mode>', 'Fit mode: cover, contain, fill, scale-down', 'cover')
37
+ .option('--global', 'Set as global background for all devices')
38
+ .action(async (device, image, options) => {
39
+ try {
40
+ // Interactive mode if arguments not provided
41
+ if (!device && !options.global) {
42
+ device = await select({
43
+ message: 'Select device type:',
44
+ choices: [
45
+ { name: 'iPhone', value: 'iphone' },
46
+ { name: 'iPad', value: 'ipad' },
47
+ { name: 'Mac', value: 'mac' },
48
+ { name: 'Watch', value: 'watch' },
49
+ { name: 'All Devices (Global)', value: 'global' }
50
+ ]
51
+ });
52
+ if (device === 'global') {
53
+ options.global = true;
54
+ device = undefined;
55
+ }
56
+ }
57
+ if (!image) {
58
+ image = await input({
59
+ message: 'Enter path to background image:',
60
+ validate: async (value) => {
61
+ try {
62
+ await fs.access(value);
63
+ return true;
64
+ }
65
+ catch {
66
+ return 'File not found';
67
+ }
68
+ }
69
+ });
70
+ }
71
+ // Validate image exists
72
+ try {
73
+ await fs.access(image);
74
+ }
75
+ catch {
76
+ console.error(pc.red(`❌ Background image not found: ${image}`));
77
+ process.exit(1);
78
+ }
79
+ // Load config
80
+ const config = await loadConfig();
81
+ // Initialize background config if not exists
82
+ if (!config.background) {
83
+ config.background = {
84
+ mode: 'image',
85
+ warnOnMismatch: true
86
+ };
87
+ }
88
+ // Set background
89
+ if (options.global) {
90
+ config.background.image = image;
91
+ config.background.fit = options.fit;
92
+ console.log(pc.green(`✅ Set global background: ${image}`));
93
+ }
94
+ else {
95
+ // Device-specific background
96
+ if (!config.devices[device]) {
97
+ console.error(pc.red(`❌ Device '${device}' not found in config`));
98
+ console.log(pc.dim(`Available devices: ${Object.keys(config.devices).join(', ')}`));
99
+ process.exit(1);
100
+ }
101
+ if (!config.devices[device].background) {
102
+ config.devices[device].background = {};
103
+ }
104
+ config.devices[device].background.image = image;
105
+ config.devices[device].background.fit = options.fit;
106
+ console.log(pc.green(`✅ Set ${device} background: ${image}`));
107
+ }
108
+ // Save config
109
+ await saveConfig(config);
110
+ console.log(pc.dim('Configuration saved'));
111
+ }
112
+ catch (error) {
113
+ console.error(pc.red('Error setting background:'), error);
114
+ process.exit(1);
115
+ }
116
+ });
117
+ // Validate backgrounds
118
+ cmd
119
+ .command('validate')
120
+ .description('Validate background dimensions against App Store specs')
121
+ .option('-d, --device <type>', 'Validate specific device only')
122
+ .action(async (options) => {
123
+ try {
124
+ const config = await loadConfig();
125
+ let hasWarnings = false;
126
+ // Get devices to validate
127
+ const devices = options.device
128
+ ? [options.device]
129
+ : Object.keys(config.devices);
130
+ console.log(pc.bold('\n📐 Validating background dimensions...\n'));
131
+ for (const device of devices) {
132
+ const deviceConfig = config.devices[device];
133
+ if (!deviceConfig)
134
+ continue;
135
+ // Find background image
136
+ let backgroundPath = null;
137
+ if (deviceConfig.background?.image) {
138
+ backgroundPath = deviceConfig.background.image;
139
+ }
140
+ else if (config.background?.image) {
141
+ backgroundPath = config.background.image;
142
+ }
143
+ else {
144
+ // Check for auto-detected background
145
+ const candidates = [
146
+ path.join(deviceConfig.input, 'background.png'),
147
+ path.join(deviceConfig.input, 'background.jpg'),
148
+ path.join('screenshots', 'background.png'),
149
+ path.join('screenshots', 'background.jpg')
150
+ ];
151
+ for (const candidate of candidates) {
152
+ try {
153
+ await fs.access(candidate);
154
+ backgroundPath = candidate;
155
+ break;
156
+ }
157
+ catch {
158
+ // Continue checking
159
+ }
160
+ }
161
+ }
162
+ if (!backgroundPath) {
163
+ console.log(pc.dim(`${device}: No background image found`));
164
+ continue;
165
+ }
166
+ // Get target dimensions
167
+ const [width, height] = deviceConfig.resolution.split('x').map(Number);
168
+ // Validate
169
+ const validation = await validateBackgroundDimensions(backgroundPath, width, height);
170
+ // Display results
171
+ console.log(pc.cyan(`${device}:`));
172
+ console.log(pc.dim(` Background: ${backgroundPath}`));
173
+ console.log(pc.dim(` Source: ${validation.dimensions.source.width}x${validation.dimensions.source.height}`));
174
+ console.log(pc.dim(` Target: ${validation.dimensions.target.width}x${validation.dimensions.target.height}`));
175
+ if (validation.warnings.length > 0) {
176
+ hasWarnings = true;
177
+ validation.warnings.forEach(warning => {
178
+ console.log(pc.yellow(` ⚠️ ${warning}`));
179
+ });
180
+ // Suggest best fit mode
181
+ const bestFit = detectBestFit(validation.dimensions.source.width, validation.dimensions.source.height, validation.dimensions.target.width, validation.dimensions.target.height);
182
+ console.log(pc.cyan(` 💡 Suggested fit mode: ${bestFit}`));
183
+ }
184
+ else {
185
+ console.log(pc.green(' ✅ Dimensions OK'));
186
+ }
187
+ console.log();
188
+ }
189
+ if (hasWarnings) {
190
+ console.log(pc.yellow('⚠️ Some backgrounds have dimension warnings'));
191
+ console.log(pc.dim('Run "appshot backgrounds set" to adjust fit modes'));
192
+ }
193
+ else {
194
+ console.log(pc.green('✅ All backgrounds validated successfully'));
195
+ }
196
+ }
197
+ catch (error) {
198
+ console.error(pc.red('Error validating backgrounds:'), error);
199
+ process.exit(1);
200
+ }
201
+ });
202
+ // Preview backgrounds
203
+ cmd
204
+ .command('preview')
205
+ .description('Generate preview of screenshots with backgrounds')
206
+ .option('-d, --device <type>', 'Preview specific device only')
207
+ .option('-o, --output <dir>', 'Output directory', './preview')
208
+ .action(async (options) => {
209
+ try {
210
+ const config = await loadConfig();
211
+ const outputDir = options.output;
212
+ // Create output directory
213
+ await fs.mkdir(outputDir, { recursive: true });
214
+ console.log(pc.bold('\n🎨 Generating background previews...\n'));
215
+ // This would integrate with the compose system
216
+ // For now, just show what would be generated
217
+ const devices = options.device
218
+ ? [options.device]
219
+ : Object.keys(config.devices);
220
+ for (const device of devices) {
221
+ console.log(pc.cyan(`${device}:`));
222
+ console.log(pc.dim(` Would generate preview in ${outputDir}/${device}/`));
223
+ }
224
+ console.log(pc.dim('\nNote: Full preview generation requires running "appshot build --preview"'));
225
+ }
226
+ catch (error) {
227
+ console.error(pc.red('Error generating preview:'), error);
228
+ process.exit(1);
229
+ }
230
+ });
231
+ // Clear background
232
+ cmd
233
+ .command('clear')
234
+ .description('Remove background configuration')
235
+ .argument('[device]', 'Device type to clear (or "all" for global)')
236
+ .action(async (device) => {
237
+ try {
238
+ if (!device) {
239
+ device = await select({
240
+ message: 'Clear background for:',
241
+ choices: [
242
+ { name: 'iPhone', value: 'iphone' },
243
+ { name: 'iPad', value: 'ipad' },
244
+ { name: 'Mac', value: 'mac' },
245
+ { name: 'Watch', value: 'watch' },
246
+ { name: 'All Devices (Global)', value: 'all' }
247
+ ]
248
+ });
249
+ }
250
+ const config = await loadConfig();
251
+ if (device === 'all') {
252
+ // Clear global background
253
+ if (config.background) {
254
+ delete config.background.image;
255
+ console.log(pc.green('✅ Cleared global background'));
256
+ }
257
+ }
258
+ else {
259
+ // Clear device-specific background
260
+ if (config.devices[device]?.background) {
261
+ delete config.devices[device].background.image;
262
+ console.log(pc.green(`✅ Cleared ${device} background`));
263
+ }
264
+ }
265
+ await saveConfig(config);
266
+ console.log(pc.dim('Configuration saved'));
267
+ }
268
+ catch (error) {
269
+ console.error(pc.red('Error clearing background:'), error);
270
+ process.exit(1);
271
+ }
272
+ });
273
+ // List backgrounds
274
+ cmd
275
+ .command('list')
276
+ .description('List configured backgrounds')
277
+ .action(async () => {
278
+ try {
279
+ const config = await loadConfig();
280
+ console.log(pc.bold('\n📋 Configured Backgrounds:\n'));
281
+ // Global background
282
+ if (config.background?.image) {
283
+ console.log(pc.cyan('Global:'));
284
+ console.log(pc.dim(` Image: ${config.background.image}`));
285
+ console.log(pc.dim(` Fit: ${config.background.fit || 'cover'}`));
286
+ console.log();
287
+ }
288
+ // Device-specific backgrounds
289
+ for (const [device, deviceConfig] of Object.entries(config.devices)) {
290
+ if (deviceConfig.background?.image) {
291
+ console.log(pc.cyan(`${device}:`));
292
+ console.log(pc.dim(` Image: ${deviceConfig.background.image}`));
293
+ console.log(pc.dim(` Fit: ${deviceConfig.background.fit || 'cover'}`));
294
+ console.log();
295
+ }
296
+ }
297
+ // Auto-detected backgrounds
298
+ console.log(pc.bold('Auto-detected backgrounds:'));
299
+ for (const [device, deviceConfig] of Object.entries(config.devices)) {
300
+ const candidates = [
301
+ path.join(deviceConfig.input, 'background.png'),
302
+ path.join(deviceConfig.input, 'background.jpg')
303
+ ];
304
+ for (const candidate of candidates) {
305
+ try {
306
+ await fs.access(candidate);
307
+ console.log(pc.dim(` ${device}: ${candidate}`));
308
+ break;
309
+ }
310
+ catch {
311
+ // Not found
312
+ }
313
+ }
314
+ }
315
+ }
316
+ catch (error) {
317
+ console.error(pc.red('Error listing backgrounds:'), error);
318
+ process.exit(1);
319
+ }
320
+ });
321
+ return cmd;
322
+ }
323
+ //# sourceMappingURL=backgrounds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backgrounds.js","sourceRoot":"","sources":["../../src/commands/backgrounds.ts"],"names":[],"mappings":"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"}
@@ -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,YA4T/B"}
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,YA6V/B"}
@@ -19,6 +19,10 @@ 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('--background <image>', 'use specific background image')
23
+ .option('--no-background', 'disable background (transparent)')
24
+ .option('--background-fit <mode>', 'background fit mode: cover, contain, fill, scale-down', 'cover')
25
+ .option('--auto-background', 'auto-detect background.png in device folders')
22
26
  .option('--dry-run', 'show what would be rendered without generating images')
23
27
  .option('--verbose', 'show detailed rendering information')
24
28
  .addHelpText('after', `
@@ -41,6 +45,12 @@ ${pc.bold('Examples:')}
41
45
  ${pc.dim('# Frames only (no gradient/caption)')}
42
46
  $ appshot build --no-gradient --no-caption
43
47
 
48
+ ${pc.dim('# Use custom background image')}
49
+ $ appshot build --background ./assets/sunset.jpg
50
+
51
+ ${pc.dim('# Auto-detect background.png in device folders')}
52
+ $ appshot build --auto-background
53
+
44
54
  ${pc.dim('# Preview what would be built')}
45
55
  $ appshot build --dry-run
46
56
 
@@ -93,9 +103,10 @@ ${pc.bold('Language Detection:')}
93
103
  }
94
104
  // Create device output directory
95
105
  await fs.mkdir(outputDir, { recursive: true });
96
- // Get screenshots
106
+ // Get screenshots (excluding background images)
97
107
  const screenshots = (await fs.readdir(inputDir))
98
108
  .filter(f => f.match(/\.(png|jpg|jpeg)$/i))
109
+ .filter(f => !f.match(/^background\.(png|jpg|jpeg)$/i))
99
110
  .sort();
100
111
  if (screenshots.length === 0) {
101
112
  console.log(pc.yellow('⚠'), `No screenshots found in ${inputDir}`);
@@ -223,6 +234,27 @@ ${pc.bold('Language Detection:')}
223
234
  // Actual rendering
224
235
  let image;
225
236
  try {
237
+ // Configure background
238
+ let backgroundConfig = config.background;
239
+ // Override with command line options
240
+ if (opts.background) {
241
+ backgroundConfig = {
242
+ mode: 'image',
243
+ image: opts.background,
244
+ fit: opts.backgroundFit || 'cover',
245
+ warnOnMismatch: true
246
+ };
247
+ }
248
+ else if (opts.autoBackground) {
249
+ backgroundConfig = {
250
+ mode: 'auto',
251
+ fit: opts.backgroundFit || 'cover',
252
+ warnOnMismatch: true
253
+ };
254
+ }
255
+ else if (opts.noBackground) {
256
+ backgroundConfig = undefined;
257
+ }
226
258
  image = await composeAppStoreScreenshot({
227
259
  screenshot: screenshotBuffer,
228
260
  frame: frame,
@@ -237,7 +269,8 @@ ${pc.bold('Language Detection:')}
237
269
  } : undefined,
238
270
  caption: opts.caption !== false ? captionText : undefined,
239
271
  captionConfig: config.caption,
240
- gradientConfig: config.gradient,
272
+ gradientConfig: opts.gradient === false ? undefined : config.gradient,
273
+ backgroundConfig: backgroundConfig,
241
274
  deviceConfig: deviceConfig,
242
275
  outputWidth: outWidth,
243
276
  outputHeight: outHeight,