appshot-cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +381 -0
  3. package/bin/appshot.js +5 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +31 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/commands/build.d.ts +3 -0
  9. package/dist/commands/build.d.ts.map +1 -0
  10. package/dist/commands/build.js +200 -0
  11. package/dist/commands/build.js.map +1 -0
  12. package/dist/commands/caption.d.ts +3 -0
  13. package/dist/commands/caption.d.ts.map +1 -0
  14. package/dist/commands/caption.js +118 -0
  15. package/dist/commands/caption.js.map +1 -0
  16. package/dist/commands/check.d.ts +3 -0
  17. package/dist/commands/check.d.ts.map +1 -0
  18. package/dist/commands/check.js +121 -0
  19. package/dist/commands/check.js.map +1 -0
  20. package/dist/commands/clean.d.ts +3 -0
  21. package/dist/commands/clean.d.ts.map +1 -0
  22. package/dist/commands/clean.js +133 -0
  23. package/dist/commands/clean.js.map +1 -0
  24. package/dist/commands/init.d.ts +3 -0
  25. package/dist/commands/init.d.ts.map +1 -0
  26. package/dist/commands/init.js +84 -0
  27. package/dist/commands/init.js.map +1 -0
  28. package/dist/commands/localize.d.ts +3 -0
  29. package/dist/commands/localize.d.ts.map +1 -0
  30. package/dist/commands/localize.js +31 -0
  31. package/dist/commands/localize.js.map +1 -0
  32. package/dist/commands/presets.d.ts +3 -0
  33. package/dist/commands/presets.d.ts.map +1 -0
  34. package/dist/commands/presets.js +135 -0
  35. package/dist/commands/presets.js.map +1 -0
  36. package/dist/commands/specs.d.ts +3 -0
  37. package/dist/commands/specs.d.ts.map +1 -0
  38. package/dist/commands/specs.js +69 -0
  39. package/dist/commands/specs.js.map +1 -0
  40. package/dist/commands/validate.d.ts +3 -0
  41. package/dist/commands/validate.d.ts.map +1 -0
  42. package/dist/commands/validate.js +183 -0
  43. package/dist/commands/validate.js.map +1 -0
  44. package/dist/core/app-store-specs.d.ts +55 -0
  45. package/dist/core/app-store-specs.d.ts.map +1 -0
  46. package/dist/core/app-store-specs.js +457 -0
  47. package/dist/core/app-store-specs.js.map +1 -0
  48. package/dist/core/compose.d.ts +30 -0
  49. package/dist/core/compose.d.ts.map +1 -0
  50. package/dist/core/compose.js +420 -0
  51. package/dist/core/compose.js.map +1 -0
  52. package/dist/core/devices.d.ts +50 -0
  53. package/dist/core/devices.d.ts.map +1 -0
  54. package/dist/core/devices.js +404 -0
  55. package/dist/core/devices.js.map +1 -0
  56. package/dist/core/files.d.ts +5 -0
  57. package/dist/core/files.d.ts.map +1 -0
  58. package/dist/core/files.js +35 -0
  59. package/dist/core/files.js.map +1 -0
  60. package/dist/core/frames-analyzer.d.ts +24 -0
  61. package/dist/core/frames-analyzer.d.ts.map +1 -0
  62. package/dist/core/frames-analyzer.js +97 -0
  63. package/dist/core/frames-analyzer.js.map +1 -0
  64. package/dist/core/frames-loader.d.ts +113 -0
  65. package/dist/core/frames-loader.d.ts.map +1 -0
  66. package/dist/core/frames-loader.js +440 -0
  67. package/dist/core/frames-loader.js.map +1 -0
  68. package/dist/core/mask-generator.d.ts +10 -0
  69. package/dist/core/mask-generator.d.ts.map +1 -0
  70. package/dist/core/mask-generator.js +99 -0
  71. package/dist/core/mask-generator.js.map +1 -0
  72. package/dist/core/render.d.ts +10 -0
  73. package/dist/core/render.d.ts.map +1 -0
  74. package/dist/core/render.js +92 -0
  75. package/dist/core/render.js.map +1 -0
  76. package/dist/core/text-renderer.d.ts +18 -0
  77. package/dist/core/text-renderer.d.ts.map +1 -0
  78. package/dist/core/text-renderer.js +148 -0
  79. package/dist/core/text-renderer.js.map +1 -0
  80. package/dist/types.d.ts +57 -0
  81. package/dist/types.d.ts.map +1 -0
  82. package/dist/types.js +2 -0
  83. package/dist/types.js.map +1 -0
  84. package/dist/utils/caption-history.d.ts +35 -0
  85. package/dist/utils/caption-history.d.ts.map +1 -0
  86. package/dist/utils/caption-history.js +233 -0
  87. package/dist/utils/caption-history.js.map +1 -0
  88. package/frames/Frames.json +308 -0
  89. package/frames/MacBook Air 2020.png +0 -0
  90. package/frames/MacBook Air 2022.png +0 -0
  91. package/frames/MacBook Pro 13.png +0 -0
  92. package/frames/MacBook Pro 2021 14.png +0 -0
  93. package/frames/MacBook Pro 2021 16.png +0 -0
  94. package/frames/Watch Series 10 42.png +0 -0
  95. package/frames/Watch Series 10 42_mask.png +0 -0
  96. package/frames/Watch Series 10 46.png +0 -0
  97. package/frames/Watch Series 10 46_mask.png +0 -0
  98. package/frames/Watch Series 4 40.png +0 -0
  99. package/frames/Watch Series 4 44.png +0 -0
  100. package/frames/Watch Series 7 41.png +0 -0
  101. package/frames/Watch Series 7 45.png +0 -0
  102. package/frames/Watch Ultra 2024.png +0 -0
  103. package/frames/iMac 2021.png +0 -0
  104. package/frames/iPad 2021 Landscape.png +0 -0
  105. package/frames/iPad 2021 Portrait.png +0 -0
  106. package/frames/iPad Air 2020 Landscape.png +0 -0
  107. package/frames/iPad Air 2020 Portrait.png +0 -0
  108. package/frames/iPad Pro 2018-2021 11 Landscape.png +0 -0
  109. package/frames/iPad Pro 2018-2021 11 Portrait.png +0 -0
  110. package/frames/iPad Pro 2018-2021 Landscape.png +0 -0
  111. package/frames/iPad Pro 2018-2021 Portrait.png +0 -0
  112. package/frames/iPad Pro 2024 11 Landscape.png +0 -0
  113. package/frames/iPad Pro 2024 11 Portrait.png +0 -0
  114. package/frames/iPad Pro 2024 13 Landscape.png +0 -0
  115. package/frames/iPad Pro 2024 13 Portrait.png +0 -0
  116. package/frames/iPad mini 2021 Landscape.png +0 -0
  117. package/frames/iPad mini 2021 Portrait.png +0 -0
  118. package/frames/iPhone 11 Landscape.png +0 -0
  119. package/frames/iPhone 11 Portrait.png +0 -0
  120. package/frames/iPhone 11 Pro Max Landscape.png +0 -0
  121. package/frames/iPhone 11 Pro Max Portrait.png +0 -0
  122. package/frames/iPhone 11 Pro Portrait.png +0 -0
  123. package/frames/iPhone 12-13 Pro Landscape.png +0 -0
  124. package/frames/iPhone 12-13 Pro Max Landscape.png +0 -0
  125. package/frames/iPhone 12-13 Pro Max Landscape_mask.png +0 -0
  126. package/frames/iPhone 12-13 Pro Max Portrait.png +0 -0
  127. package/frames/iPhone 12-13 Pro Max Portrait_mask.png +0 -0
  128. package/frames/iPhone 12-13 Pro Portrait.png +0 -0
  129. package/frames/iPhone 12-13 mini Landscape.png +0 -0
  130. package/frames/iPhone 12-13 mini Portrait.png +0 -0
  131. package/frames/iPhone 16 Landscape.png +0 -0
  132. package/frames/iPhone 16 Landscape_frame_no_island.png +0 -0
  133. package/frames/iPhone 16 Landscape_mask.png +0 -0
  134. package/frames/iPhone 16 Plus Landscape.png +0 -0
  135. package/frames/iPhone 16 Plus Landscape_frame_no_island.png +0 -0
  136. package/frames/iPhone 16 Plus Landscape_mask.png +0 -0
  137. package/frames/iPhone 16 Plus Portrait.png +0 -0
  138. package/frames/iPhone 16 Plus Portrait_frame_no_island.png +0 -0
  139. package/frames/iPhone 16 Plus Portrait_mask.png +0 -0
  140. package/frames/iPhone 16 Portrait.png +0 -0
  141. package/frames/iPhone 16 Portrait_frame_no_island.png +0 -0
  142. package/frames/iPhone 16 Portrait_mask.png +0 -0
  143. package/frames/iPhone 16 Pro Landscape.png +0 -0
  144. package/frames/iPhone 16 Pro Landscape_frame_no_island.png +0 -0
  145. package/frames/iPhone 16 Pro Landscape_mask.png +0 -0
  146. package/frames/iPhone 16 Pro Max Landscape.png +0 -0
  147. package/frames/iPhone 16 Pro Max Landscape_frame_no_island.png +0 -0
  148. package/frames/iPhone 16 Pro Max Landscape_mask.png +0 -0
  149. package/frames/iPhone 16 Pro Max Portrait.png +0 -0
  150. package/frames/iPhone 16 Pro Max Portrait_frame_no_island.png +0 -0
  151. package/frames/iPhone 16 Pro Max Portrait_mask.png +0 -0
  152. package/frames/iPhone 16 Pro Portrait.png +0 -0
  153. package/frames/iPhone 16 Pro Portrait_frame_no_island.png +0 -0
  154. package/frames/iPhone 16 Pro Portrait_mask.png +0 -0
  155. package/frames/iPhone 8 Plus Landscape.png +0 -0
  156. package/frames/iPhone 8 Plus Portrait.png +0 -0
  157. 2020 SE.png +0 -0
  158. package/frames/version.txt +1 -0
  159. package/package.json +61 -0
@@ -0,0 +1,135 @@
1
+ import { Command } from 'commander';
2
+ import { promises as fs } from 'fs';
3
+ import pc from 'picocolors';
4
+ import { ALL_PRESETS, getRequiredPresets, getPresetById } from '../core/app-store-specs.js';
5
+ export default function presetsCmd() {
6
+ const cmd = new Command('presets')
7
+ .description('Manage App Store screenshot presets')
8
+ .option('--list', 'list all available presets')
9
+ .option('--required', 'list only required presets for App Store')
10
+ .option('--generate <ids>', 'generate config for specific preset IDs (comma-separated)')
11
+ .option('--category <type>', 'filter by category (iphone, ipad, mac, appletv, visionpro, watch)')
12
+ .option('--output <file>', 'output file for generated config', 'appshot-presets.json')
13
+ .action(async (opts) => {
14
+ try {
15
+ if (opts.list || opts.required) {
16
+ listPresets(opts);
17
+ }
18
+ else if (opts.generate) {
19
+ await generateConfig(opts.generate, opts.output);
20
+ }
21
+ else {
22
+ // Default: show required presets
23
+ listPresets({ required: true });
24
+ }
25
+ }
26
+ catch (error) {
27
+ console.error(pc.red('Error:'), error instanceof Error ? error.message : String(error));
28
+ process.exit(1);
29
+ }
30
+ });
31
+ return cmd;
32
+ }
33
+ function listPresets(opts) {
34
+ console.log(pc.bold('\n📱 App Store Screenshot Presets\n'));
35
+ const presets = opts.required ? getRequiredPresets() : ALL_PRESETS;
36
+ const categories = opts.category ? [opts.category] : Object.keys(presets);
37
+ for (const category of categories) {
38
+ const categoryPresets = presets[category];
39
+ if (!categoryPresets || categoryPresets.length === 0)
40
+ continue;
41
+ console.log(pc.cyan(`\n${category.toUpperCase()}`));
42
+ console.log(pc.dim('─'.repeat(50)));
43
+ for (const preset of categoryPresets) {
44
+ const required = preset.required ? pc.red(' [REQUIRED]') : '';
45
+ console.log(`\n ${pc.bold(preset.id)}${required}`);
46
+ console.log(` ${preset.name} (${preset.displaySize})`);
47
+ if (preset.resolutions.portrait) {
48
+ console.log(` Portrait: ${pc.green(preset.resolutions.portrait)}`);
49
+ }
50
+ if (preset.resolutions.landscape) {
51
+ console.log(` Landscape: ${pc.green(preset.resolutions.landscape)}`);
52
+ }
53
+ if (preset.devices && preset.devices.length > 0) {
54
+ console.log(` Devices: ${pc.dim(preset.devices.slice(0, 3).join(', '))}${preset.devices.length > 3 ? '...' : ''}`);
55
+ }
56
+ if (preset.notes) {
57
+ console.log(` Note: ${pc.yellow(preset.notes)}`);
58
+ }
59
+ }
60
+ }
61
+ console.log(pc.dim('\n─'.repeat(50)));
62
+ console.log(pc.dim('\nUse --generate <preset-ids> to create a configuration file'));
63
+ console.log(pc.dim('Example: appshot presets --generate iphone-6-9,ipad-13,mac-2880'));
64
+ }
65
+ async function generateConfig(presetIds, outputFile) {
66
+ const ids = presetIds.split(',').map(id => id.trim());
67
+ const config = {
68
+ output: './app-store-screenshots',
69
+ frames: './frames',
70
+ gradient: {
71
+ colors: ['#667eea', '#764ba2'],
72
+ direction: 'diagonal'
73
+ },
74
+ caption: {
75
+ font: 'SF Pro Display',
76
+ fontsize: 72,
77
+ color: '#FFFFFF',
78
+ align: 'center',
79
+ paddingTop: 120,
80
+ paddingBottom: 80,
81
+ position: 'above'
82
+ },
83
+ devices: {}
84
+ };
85
+ const foundPresets = [];
86
+ const notFound = [];
87
+ for (const id of ids) {
88
+ const preset = getPresetById(id);
89
+ if (!preset) {
90
+ notFound.push(id);
91
+ continue;
92
+ }
93
+ foundPresets.push(id);
94
+ // Determine device type from preset ID
95
+ const deviceType = id.split('-')[0];
96
+ // Add both portrait and landscape configurations if available
97
+ if (preset.resolutions.portrait) {
98
+ const configId = `${id}-portrait`;
99
+ config.devices[configId] = {
100
+ input: `./screenshots/${deviceType}/${preset.displaySize.replace(/[^a-z0-9]/gi, '')}/portrait`,
101
+ resolution: preset.resolutions.portrait,
102
+ autoFrame: true,
103
+ partialFrame: deviceType === 'iphone' || deviceType === 'ipad'
104
+ };
105
+ }
106
+ if (preset.resolutions.landscape) {
107
+ const configId = `${id}-landscape`;
108
+ config.devices[configId] = {
109
+ input: `./screenshots/${deviceType}/${preset.displaySize.replace(/[^a-z0-9]/gi, '')}/landscape`,
110
+ resolution: preset.resolutions.landscape,
111
+ autoFrame: true,
112
+ partialFrame: deviceType === 'iphone' || deviceType === 'ipad'
113
+ };
114
+ }
115
+ }
116
+ if (notFound.length > 0) {
117
+ console.log(pc.yellow('⚠ Warning: Preset IDs not found:'), notFound.join(', '));
118
+ }
119
+ if (foundPresets.length === 0) {
120
+ console.log(pc.red('✗ No valid presets found'));
121
+ return;
122
+ }
123
+ // Write config file
124
+ await fs.writeFile(outputFile, JSON.stringify(config, null, 2), 'utf-8');
125
+ console.log(pc.green('✓'), `Generated config for ${foundPresets.length} presets in ${outputFile}`);
126
+ console.log(pc.dim('\nPresets included:'));
127
+ for (const id of foundPresets) {
128
+ const preset = getPresetById(id);
129
+ console.log(` - ${preset?.name} (${id})`);
130
+ }
131
+ console.log(pc.dim('\nNext steps:'));
132
+ console.log(' 1. Add your screenshots to the appropriate directories');
133
+ console.log(' 2. Run: appshot build --config', outputFile);
134
+ }
135
+ //# sourceMappingURL=presets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presets.js","sourceRoot":"","sources":["../../src/commands/presets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export default function specsCmd(): Command;
3
+ //# sourceMappingURL=specs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"specs.d.ts","sourceRoot":"","sources":["../../src/commands/specs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmCpC,MAAM,CAAC,OAAO,UAAU,QAAQ,YAuC/B"}
@@ -0,0 +1,69 @@
1
+ import { Command } from 'commander';
2
+ import pc from 'picocolors';
3
+ const deviceSpecs = {
4
+ iphone: {
5
+ '15-pro-max': { resolution: '1290x2796', displayName: 'iPhone 15 Pro Max' },
6
+ '15-pro': { resolution: '1179x2556', displayName: 'iPhone 15 Pro' },
7
+ '15': { resolution: '1179x2556', displayName: 'iPhone 15' },
8
+ '14-pro-max': { resolution: '1290x2796', displayName: 'iPhone 14 Pro Max' },
9
+ '14-pro': { resolution: '1179x2556', displayName: 'iPhone 14 Pro' },
10
+ 'se-3rd': { resolution: '750x1334', displayName: 'iPhone SE (3rd gen)' }
11
+ },
12
+ ipad: {
13
+ 'pro-12.9': { resolution: '2048x2732', displayName: 'iPad Pro 12.9"' },
14
+ 'pro-11': { resolution: '1668x2388', displayName: 'iPad Pro 11"' },
15
+ 'air-5th': { resolution: '1640x2360', displayName: 'iPad Air (5th gen)' },
16
+ 'mini-6th': { resolution: '1488x2266', displayName: 'iPad mini (6th gen)' },
17
+ '10th': { resolution: '1640x2360', displayName: 'iPad (10th gen)' }
18
+ },
19
+ mac: {
20
+ 'macbook-pro-16': { resolution: '3456x2234', displayName: 'MacBook Pro 16"' },
21
+ 'macbook-pro-14': { resolution: '3024x1964', displayName: 'MacBook Pro 14"' },
22
+ 'macbook-air-15': { resolution: '2880x1864', displayName: 'MacBook Air 15"' },
23
+ 'macbook-air-13': { resolution: '2560x1664', displayName: 'MacBook Air 13"' },
24
+ 'imac-24': { resolution: '4480x2520', displayName: 'iMac 24"' }
25
+ },
26
+ watch: {
27
+ 'ultra-2': { resolution: '410x502', displayName: 'Apple Watch Ultra 2' },
28
+ 'series-9-45mm': { resolution: '396x484', displayName: 'Apple Watch Series 9 (45mm)' },
29
+ 'series-9-41mm': { resolution: '352x430', displayName: 'Apple Watch Series 9 (41mm)' },
30
+ 'se-44mm': { resolution: '368x448', displayName: 'Apple Watch SE (44mm)' },
31
+ 'se-40mm': { resolution: '324x394', displayName: 'Apple Watch SE (40mm)' }
32
+ }
33
+ };
34
+ export default function specsCmd() {
35
+ return new Command('specs')
36
+ .description('Show device specifications and resolutions')
37
+ .option('--device <name>', 'filter by device type (iphone|ipad|mac|watch)')
38
+ .option('--json', 'output as JSON')
39
+ .action((opts) => {
40
+ try {
41
+ if (opts.json) {
42
+ const output = opts.device ? { [opts.device]: deviceSpecs[opts.device] } : deviceSpecs;
43
+ console.log(JSON.stringify(output, null, 2));
44
+ return;
45
+ }
46
+ console.log(pc.bold('\nDevice Specifications\n'));
47
+ const devices = opts.device
48
+ ? [opts.device]
49
+ : Object.keys(deviceSpecs);
50
+ for (const device of devices) {
51
+ if (!deviceSpecs[device]) {
52
+ console.log(pc.red(`Unknown device: ${device}`));
53
+ continue;
54
+ }
55
+ console.log(pc.cyan(device.toUpperCase()));
56
+ for (const [_model, spec] of Object.entries(deviceSpecs[device])) {
57
+ console.log(` ${spec.displayName.padEnd(30)} ${pc.dim(spec.resolution)}`);
58
+ }
59
+ console.log();
60
+ }
61
+ console.log(pc.dim('Use these resolutions in your appshot.json configuration'));
62
+ }
63
+ catch (error) {
64
+ console.error(pc.red('Error:'), error instanceof Error ? error.message : String(error));
65
+ process.exit(1);
66
+ }
67
+ });
68
+ }
69
+ //# sourceMappingURL=specs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"specs.js","sourceRoot":"","sources":["../../src/commands/specs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export default function validateCmd(): Command;
3
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,MAAM,CAAC,OAAO,UAAU,WAAW,YAqFlC"}
@@ -0,0 +1,183 @@
1
+ import { Command } from 'commander';
2
+ import { promises as fs } from 'fs';
3
+ import path from 'path';
4
+ import pc from 'picocolors';
5
+ import sharp from 'sharp';
6
+ import { validateResolution, recommendPreset, getRequiredPresets } from '../core/app-store-specs.js';
7
+ import { loadConfig } from '../core/files.js';
8
+ export default function validateCmd() {
9
+ const cmd = new Command('validate')
10
+ .description('Validate screenshots against App Store requirements')
11
+ .option('--strict', 'validate against required presets only')
12
+ .option('--fix', 'suggest fixes for invalid screenshots')
13
+ .action(async (opts) => {
14
+ try {
15
+ console.log(pc.bold('🔍 Validating App Store Screenshots...\n'));
16
+ const config = await loadConfig();
17
+ const results = [];
18
+ // Check each device configuration
19
+ for (const [deviceKey, deviceConfig] of Object.entries(config.devices)) {
20
+ const deviceType = deviceKey.split('-')[0].split('_')[0]; // Extract base device type
21
+ const inputDir = path.resolve(deviceConfig.input);
22
+ // Check if input directory exists
23
+ try {
24
+ await fs.access(inputDir);
25
+ }
26
+ catch {
27
+ results.push({
28
+ device: deviceKey,
29
+ status: 'error',
30
+ message: `Input directory not found: ${inputDir}`
31
+ });
32
+ continue;
33
+ }
34
+ // Parse configured resolution
35
+ const [configWidth, configHeight] = deviceConfig.resolution.split('x').map(Number);
36
+ // Validate resolution against App Store specs
37
+ const isValid = validateResolution(configWidth, configHeight, deviceType);
38
+ const recommended = recommendPreset(configWidth, configHeight, deviceType);
39
+ if (isValid) {
40
+ results.push({
41
+ device: deviceKey,
42
+ status: 'valid',
43
+ message: `✓ ${deviceConfig.resolution} - Matches ${recommended?.name || 'App Store specs'}`
44
+ });
45
+ }
46
+ else {
47
+ results.push({
48
+ device: deviceKey,
49
+ status: 'invalid',
50
+ message: `✗ ${deviceConfig.resolution} - Not a valid App Store resolution`,
51
+ fix: opts.fix ? await suggestFix(configWidth, configHeight, deviceType) : undefined
52
+ });
53
+ }
54
+ // Check actual screenshots
55
+ const screenshots = (await fs.readdir(inputDir))
56
+ .filter(f => f.match(/\.(png|jpg|jpeg)$/i));
57
+ for (const screenshot of screenshots) {
58
+ const screenshotPath = path.join(inputDir, screenshot);
59
+ const metadata = await sharp(screenshotPath).metadata();
60
+ if (metadata.width !== configWidth || metadata.height !== configHeight) {
61
+ results.push({
62
+ device: deviceKey,
63
+ status: 'warning',
64
+ message: `⚠ ${screenshot}: ${metadata.width}x${metadata.height} doesn't match config ${configWidth}x${configHeight}`
65
+ });
66
+ }
67
+ }
68
+ }
69
+ // Display results
70
+ displayResults(results);
71
+ // Check for required presets if strict mode
72
+ if (opts.strict) {
73
+ console.log(pc.bold('\n📋 Required Presets Check:\n'));
74
+ checkRequiredPresets(config);
75
+ }
76
+ }
77
+ catch (error) {
78
+ console.error(pc.red('Error:'), error instanceof Error ? error.message : String(error));
79
+ process.exit(1);
80
+ }
81
+ });
82
+ return cmd;
83
+ }
84
+ function displayResults(results) {
85
+ const valid = results.filter(r => r.status === 'valid');
86
+ const invalid = results.filter(r => r.status === 'invalid');
87
+ const warnings = results.filter(r => r.status === 'warning');
88
+ const errors = results.filter(r => r.status === 'error');
89
+ if (valid.length > 0) {
90
+ console.log(pc.green('Valid Configurations:'));
91
+ for (const result of valid) {
92
+ console.log(` ${result.message}`);
93
+ }
94
+ }
95
+ if (warnings.length > 0) {
96
+ console.log(pc.yellow('\nWarnings:'));
97
+ for (const result of warnings) {
98
+ console.log(` ${result.message}`);
99
+ }
100
+ }
101
+ if (invalid.length > 0) {
102
+ console.log(pc.red('\nInvalid Configurations:'));
103
+ for (const result of invalid) {
104
+ console.log(` ${result.message}`);
105
+ if (result.fix) {
106
+ console.log(pc.dim(` Suggestion: ${result.fix}`));
107
+ }
108
+ }
109
+ }
110
+ if (errors.length > 0) {
111
+ console.log(pc.red('\nErrors:'));
112
+ for (const result of errors) {
113
+ console.log(` ${result.message}`);
114
+ }
115
+ }
116
+ // Summary
117
+ console.log(pc.bold('\n📊 Summary:'));
118
+ console.log(` Valid: ${pc.green(valid.length.toString())}`);
119
+ console.log(` Invalid: ${pc.red(invalid.length.toString())}`);
120
+ console.log(` Warnings: ${pc.yellow(warnings.length.toString())}`);
121
+ console.log(` Errors: ${pc.red(errors.length.toString())}`);
122
+ if (invalid.length > 0 || errors.length > 0) {
123
+ console.log(pc.dim('\nRun with --fix flag for suggestions'));
124
+ process.exit(1);
125
+ }
126
+ }
127
+ async function suggestFix(width, height, deviceType) {
128
+ const aspectRatio = width / height;
129
+ const orientation = width > height ? 'landscape' : 'portrait';
130
+ // Find closest valid resolution
131
+ const { ALL_PRESETS } = await import('../core/app-store-specs.js');
132
+ const presets = ALL_PRESETS[deviceType] || [];
133
+ let closestPreset = null;
134
+ let minDiff = Infinity;
135
+ for (const preset of presets) {
136
+ const resolution = preset.resolutions[orientation];
137
+ if (!resolution)
138
+ continue;
139
+ const [presetWidth, presetHeight] = resolution.split('x').map(Number);
140
+ const presetRatio = presetWidth / presetHeight;
141
+ const ratioDiff = Math.abs(aspectRatio - presetRatio);
142
+ if (ratioDiff < minDiff) {
143
+ minDiff = ratioDiff;
144
+ closestPreset = preset;
145
+ }
146
+ }
147
+ if (closestPreset) {
148
+ const resolution = closestPreset.resolutions[orientation];
149
+ return `Use ${resolution} (${closestPreset.name})`;
150
+ }
151
+ return 'No matching App Store resolution found';
152
+ }
153
+ function checkRequiredPresets(config) {
154
+ const required = getRequiredPresets();
155
+ const configured = new Set();
156
+ // Extract device types from config
157
+ for (const deviceKey of Object.keys(config.devices)) {
158
+ const deviceType = deviceKey.split('-')[0].split('_')[0];
159
+ const resolution = config.devices[deviceKey].resolution;
160
+ configured.add(`${deviceType}-${resolution}`);
161
+ }
162
+ // Check each required preset
163
+ for (const [category, presets] of Object.entries(required)) {
164
+ if (presets.length === 0)
165
+ continue;
166
+ console.log(pc.cyan(`${category.toUpperCase()}:`));
167
+ for (const preset of presets) {
168
+ const hasPortrait = preset.resolutions.portrait &&
169
+ configured.has(`${category}-${preset.resolutions.portrait}`);
170
+ const hasLandscape = preset.resolutions.landscape &&
171
+ configured.has(`${category}-${preset.resolutions.landscape}`);
172
+ const status = (hasPortrait || hasLandscape) ? pc.green('✓') : pc.red('✗');
173
+ console.log(` ${status} ${preset.name} (${preset.displaySize})`);
174
+ if (!hasPortrait && preset.resolutions.portrait) {
175
+ console.log(pc.dim(` Missing portrait: ${preset.resolutions.portrait}`));
176
+ }
177
+ if (!hasLandscape && preset.resolutions.landscape) {
178
+ console.log(pc.dim(` Missing landscape: ${preset.resolutions.landscape}`));
179
+ }
180
+ }
181
+ }
182
+ }
183
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Official App Store Screenshot Specifications
3
+ * Source: https://developer.apple.com/help/app-store-connect/reference/screenshot-specifications
4
+ */
5
+ export interface DevicePreset {
6
+ id: string;
7
+ name: string;
8
+ displaySize: string;
9
+ devices: string[];
10
+ resolutions: {
11
+ portrait?: string;
12
+ landscape?: string;
13
+ };
14
+ required?: boolean;
15
+ fallback?: string;
16
+ notes?: string;
17
+ }
18
+ export declare const IPHONE_PRESETS: DevicePreset[];
19
+ export declare const IPAD_PRESETS: DevicePreset[];
20
+ export declare const MAC_PRESETS: DevicePreset[];
21
+ export declare const APPLE_TV_PRESETS: DevicePreset[];
22
+ export declare const VISION_PRO_PRESETS: DevicePreset[];
23
+ export declare const APPLE_WATCH_PRESETS: DevicePreset[];
24
+ export declare const ALL_PRESETS: {
25
+ iphone: DevicePreset[];
26
+ ipad: DevicePreset[];
27
+ mac: DevicePreset[];
28
+ appletv: DevicePreset[];
29
+ visionpro: DevicePreset[];
30
+ watch: DevicePreset[];
31
+ };
32
+ /**
33
+ * Get required presets for App Store submission
34
+ */
35
+ export declare function getRequiredPresets(): {
36
+ iphone: DevicePreset[];
37
+ ipad: DevicePreset[];
38
+ mac: DevicePreset[];
39
+ appletv: DevicePreset[];
40
+ visionpro: DevicePreset[];
41
+ watch: DevicePreset[];
42
+ };
43
+ /**
44
+ * Get preset by ID
45
+ */
46
+ export declare function getPresetById(id: string): DevicePreset | undefined;
47
+ /**
48
+ * Validate if resolution matches App Store requirements
49
+ */
50
+ export declare function validateResolution(width: number, height: number, deviceType: string): boolean;
51
+ /**
52
+ * Get recommended preset for given resolution
53
+ */
54
+ export declare function recommendPreset(width: number, height: number, deviceType: string): DevicePreset | undefined;
55
+ //# sourceMappingURL=app-store-specs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-store-specs.d.ts","sourceRoot":"","sources":["../../src/core/app-store-specs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE;QACX,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,cAAc,EAAE,YAAY,EA8IxC,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,YAAY,EA4GtC,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,YAAY,EA2CrC,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,YAAY,EAqB1C,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,YAAY,EAW5C,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,YAAY,EAuD7C,CAAC;AAEF,eAAO,MAAM,WAAW;;;;;;;CAOvB,CAAC;AAEF;;GAEG;AACH,wBAAgB,kBAAkB;;;;;;;EASjC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAMlE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAa7F;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAa3G"}