codeplay-common 4.10.7 → 4.10.8
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/files/take-screen-image.js +66 -18
- package/package.json +1 -1
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
//node take-screen-image.js --profiles vivo-store //only taking screenshot for vivo-store - Not only vivo, we can do this to all of our store images.
|
|
4
4
|
//node take-screen-image.js --vivo-existing //Used if we already took the screenshot and those vivo screenshots are not there then execute this to resize the screenshot for vivo store.
|
|
5
5
|
//node take-screen-image.js --oppo-existing //Used if we already took the screenshot and those oppo screenshots are not there then execute this to resize the screenshot for Oppo store.
|
|
6
|
-
//
|
|
6
|
+
//iPhone, iPhone Duo and iPad use plain screenshots without device frames or synthetic system bars.
|
|
7
|
+
//Reframing recovers missing iPhone/iPad images from existing captures without cropping.
|
|
7
8
|
//node take-screen-image.js --apple-source "C:\captures\iphone" --profiles iphone
|
|
8
9
|
//Add --apple-promotional to reuse screenshot-copy.json captions around the original capture.
|
|
9
10
|
|
|
@@ -179,7 +180,7 @@ const deviceProfiles = [
|
|
|
179
180
|
name: 'iphone',
|
|
180
181
|
label: 'iPhone Dynamic Island medium-display App Store viewport',
|
|
181
182
|
outputDirectory: 'iPhone',
|
|
182
|
-
frameType: '
|
|
183
|
+
frameType: 'native-apple',
|
|
183
184
|
width: 402,
|
|
184
185
|
height: 874,
|
|
185
186
|
deviceScaleFactor: 3,
|
|
@@ -192,7 +193,7 @@ const deviceProfiles = [
|
|
|
192
193
|
name: 'iphone-duo',
|
|
193
194
|
label: 'iPhone Duo inner-display App Store viewport',
|
|
194
195
|
outputDirectory: 'iPhone-Duo',
|
|
195
|
-
frameType: '
|
|
196
|
+
frameType: 'native-apple',
|
|
196
197
|
showDynamicIsland: false,
|
|
197
198
|
width: 669,
|
|
198
199
|
height: 951,
|
|
@@ -206,7 +207,7 @@ const deviceProfiles = [
|
|
|
206
207
|
name: 'ipad',
|
|
207
208
|
label: 'iPad 13-inch App Store viewport',
|
|
208
209
|
outputDirectory: 'iPad',
|
|
209
|
-
frameType: '
|
|
210
|
+
frameType: 'native-apple',
|
|
210
211
|
width: 1032,
|
|
211
212
|
height: 1376,
|
|
212
213
|
deviceScaleFactor: 2,
|
|
@@ -219,7 +220,7 @@ const deviceProfiles = [
|
|
|
219
220
|
name: 'ipad-store',
|
|
220
221
|
label: 'iPad App Store screenshot set',
|
|
221
222
|
outputDirectory: 'iPad-Store',
|
|
222
|
-
frameType: '
|
|
223
|
+
frameType: 'native-apple',
|
|
223
224
|
width: 1024,
|
|
224
225
|
height: 1366,
|
|
225
226
|
deviceScaleFactor: 2,
|
|
@@ -230,7 +231,7 @@ const deviceProfiles = [
|
|
|
230
231
|
name: 'iphone-store',
|
|
231
232
|
label: 'iPhone Dynamic Island medium-display App Store screenshot set',
|
|
232
233
|
outputDirectory: 'iPhone-Store',
|
|
233
|
-
frameType: '
|
|
234
|
+
frameType: 'native-apple',
|
|
234
235
|
width: 402,
|
|
235
236
|
height: 874,
|
|
236
237
|
deviceScaleFactor: 3,
|
|
@@ -617,7 +618,7 @@ function printProfiles() {
|
|
|
617
618
|
+ `(folder ${profile.outputDirectory}, viewport ${profile.width}x${profile.height}, `
|
|
618
619
|
+ `${(profile.outputFormat || 'png').toUpperCase()} ${outputWidth}x${outputHeight}${framedSize})`,
|
|
619
620
|
);
|
|
620
|
-
if (isAppleProfile(profile)) {
|
|
621
|
+
if (isAppleProfile(profile) && profile.frameType !== 'native-apple') {
|
|
621
622
|
console.log(' Native Apple capture required; Android replay/reframing is disabled.');
|
|
622
623
|
}
|
|
623
624
|
if (profile.storeRequirements) {
|
|
@@ -655,7 +656,9 @@ function printHelp() {
|
|
|
655
656
|
console.log('');
|
|
656
657
|
console.log('A normal capture automatically creates any missing Amazon, Samsung, and Play Store assets.');
|
|
657
658
|
console.log('Frame default: Android graphite (A). Apple imports do not add a device frame.');
|
|
658
|
-
console.log('iPhone/iPad profiles
|
|
659
|
+
console.log('iPhone/iPad profiles capture and reframe plain screenshots without device frames or synthetic system bars.');
|
|
660
|
+
console.log('Reframing recovers missing iPhone/iPad screenshots from existing captures, preserving content with padding.');
|
|
661
|
+
console.log('Use --apple-source with one profile to import original iOS/iPadOS captures instead.');
|
|
659
662
|
console.log('Name captures 1.png, 2.png, etc. to reuse existing promotional copy.');
|
|
660
663
|
console.log('Apple originals and optional promotions are saved separately under Store-upload/Apple-Native.');
|
|
661
664
|
console.log('Imports preserve source dimensions and system bars; the tool cannot verify capture provenance.');
|
|
@@ -693,7 +696,7 @@ async function chooseFrameStyles(options) {
|
|
|
693
696
|
if (terminal) terminal.close();
|
|
694
697
|
}
|
|
695
698
|
|
|
696
|
-
console.log(`Frame selection: Android ${choices.android}.
|
|
699
|
+
console.log(`Frame selection: Android ${choices.android}. iPhone/iPad screenshots have no device frame.`);
|
|
697
700
|
return choices;
|
|
698
701
|
}
|
|
699
702
|
|
|
@@ -3407,8 +3410,6 @@ async function captureFramedScreenshot(
|
|
|
3407
3410
|
skipExisting = false,
|
|
3408
3411
|
outputRootDirectory = inFrameDirectory,
|
|
3409
3412
|
) {
|
|
3410
|
-
if (profile.screenshotOnly) return false;
|
|
3411
|
-
|
|
3412
3413
|
const outputExtension = profile.outputExtension || path.extname(screenshotFileName).slice(1) || 'png';
|
|
3413
3414
|
const outputFileName = `${path.parse(screenshotFileName).name}.${outputExtension}`;
|
|
3414
3415
|
const framedProfileDirectory = path.join(outputRootDirectory, profile.outputDirectory);
|
|
@@ -3419,6 +3420,14 @@ async function captureFramedScreenshot(
|
|
|
3419
3420
|
return false;
|
|
3420
3421
|
}
|
|
3421
3422
|
|
|
3423
|
+
if (profile.screenshotOnly) {
|
|
3424
|
+
if (profile.frameType !== 'native-apple') return false;
|
|
3425
|
+
fs.mkdirSync(framedProfileDirectory, { recursive: true });
|
|
3426
|
+
fs.writeFileSync(framedScreenshotPath, screenshotBuffer, { flag: skipExisting ? 'wx' : 'w' });
|
|
3427
|
+
console.log(`Saved plain ${profile.label}: ${framedScreenshotPath}`);
|
|
3428
|
+
return true;
|
|
3429
|
+
}
|
|
3430
|
+
|
|
3422
3431
|
const originalImageMetadata = await sharp(screenshotBuffer).metadata();
|
|
3423
3432
|
const hasCustomFrameSize = Boolean(profile.framedWidth && profile.framedHeight);
|
|
3424
3433
|
const frameWidth = profile.framedWidth || profile.width;
|
|
@@ -3570,8 +3579,8 @@ async function captureProfile(client, snapshot, profile, screenshotFileName) {
|
|
|
3570
3579
|
frameId: frameTree.frameTree.frame.id,
|
|
3571
3580
|
html: snapshot.html,
|
|
3572
3581
|
});
|
|
3573
|
-
if (profile.
|
|
3574
|
-
//
|
|
3582
|
+
if (profile.frameType === 'native-apple') {
|
|
3583
|
+
// Plain iPhone/iPad captures do not include Android system-bar spacing.
|
|
3575
3584
|
await client.send('Runtime.evaluate', {
|
|
3576
3585
|
expression: `(() => {
|
|
3577
3586
|
const style = document.createElement('style');
|
|
@@ -3659,6 +3668,10 @@ async function frameExistingScreenshots(
|
|
|
3659
3668
|
);
|
|
3660
3669
|
const foundScreenshotNames = new Set();
|
|
3661
3670
|
|
|
3671
|
+
for (const profile of selectedProfiles.filter((item) => item.frameType === 'native-apple')) {
|
|
3672
|
+
await recoverMissingAppleScreenshots(profile, selectedScreenshotNames);
|
|
3673
|
+
}
|
|
3674
|
+
|
|
3662
3675
|
try {
|
|
3663
3676
|
replayBrowser = await launchReplayBrowser();
|
|
3664
3677
|
replayClient = await createReplayPage(replayBrowser);
|
|
@@ -3901,6 +3914,40 @@ async function runInteractiveCaptureLoop(
|
|
|
3901
3914
|
console.log('\nResponsive screenshot tool closed.');
|
|
3902
3915
|
}
|
|
3903
3916
|
|
|
3917
|
+
async function recoverMissingAppleScreenshots(profile, selectedNames) {
|
|
3918
|
+
const sourceFolders = profile.name.startsWith('ipad')
|
|
3919
|
+
? ['Amazon-Tab', 'Android-Tab-10inch', 'Android-Tab-7inch', 'Android']
|
|
3920
|
+
: ['Android', 'Amazon-Phone'];
|
|
3921
|
+
const sources = new Map();
|
|
3922
|
+
for (const folder of sourceFolders) {
|
|
3923
|
+
const directory = path.join(screenshotDirectory, folder);
|
|
3924
|
+
if (!fs.existsSync(directory)) continue;
|
|
3925
|
+
for (const fileName of fs.readdirSync(directory).sort()) {
|
|
3926
|
+
if (!/\.png$/i.test(fileName)) continue;
|
|
3927
|
+
const name = path.parse(fileName).name;
|
|
3928
|
+
if (selectedNames.size && !selectedNames.has(name)) continue;
|
|
3929
|
+
if (!sources.has(name)) sources.set(name, path.join(directory, fileName));
|
|
3930
|
+
}
|
|
3931
|
+
}
|
|
3932
|
+
for (const [name, sourcePath] of sources) {
|
|
3933
|
+
const outputPath = path.join(screenshotDirectory, profile.outputDirectory, `${name}.png`);
|
|
3934
|
+
if (fs.existsSync(outputPath)) continue;
|
|
3935
|
+
const image = sharp(sourcePath);
|
|
3936
|
+
const { width, height } = await image.metadata();
|
|
3937
|
+
const { data } = await image.clone().resize(1, 1, { fit: 'fill' }).toColourspace('srgb').removeAlpha().raw().toBuffer({ resolveWithObject: true });
|
|
3938
|
+
const landscape = width > height;
|
|
3939
|
+
const targetWidth = Math.min(profile.width, profile.height) * profile.deviceScaleFactor;
|
|
3940
|
+
const targetHeight = Math.max(profile.width, profile.height) * profile.deviceScaleFactor;
|
|
3941
|
+
const buffer = await image.resize(landscape ? targetHeight : targetWidth, landscape ? targetWidth : targetHeight, {
|
|
3942
|
+
fit: 'contain',
|
|
3943
|
+
background: { r: data[0], g: data[1], b: data[2], alpha: 1 },
|
|
3944
|
+
}).removeAlpha().png().toBuffer();
|
|
3945
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
3946
|
+
fs.writeFileSync(outputPath, buffer, { flag: 'wx' });
|
|
3947
|
+
console.log(`Recovered plain screenshot from ${sourcePath}: ${outputPath}`);
|
|
3948
|
+
}
|
|
3949
|
+
}
|
|
3950
|
+
|
|
3904
3951
|
function isAppleProfile(profile) {
|
|
3905
3952
|
return /^(iphone|ipad|apple)(-|$)/.test(profile.name) || profile.name === 'mac';
|
|
3906
3953
|
}
|
|
@@ -4113,15 +4160,16 @@ async function main() {
|
|
|
4113
4160
|
if (!options.amazonAssets && !options.samsungAssets && !options.vivoExisting
|
|
4114
4161
|
&& !options.oppoExisting && !options.refreshInfo) {
|
|
4115
4162
|
const selected = selectProfiles(options.profiles);
|
|
4116
|
-
const
|
|
4163
|
+
const requiresNativeCapture = (profile) => isAppleProfile(profile) && profile.frameType !== 'native-apple';
|
|
4164
|
+
const appleProfiles = selected.filter(requiresNativeCapture);
|
|
4117
4165
|
if (appleProfiles.length) {
|
|
4118
4166
|
const explicitSelection = options.profiles.split(',').some((name) => name.trim() && name.trim().toLowerCase() !== 'all');
|
|
4119
4167
|
if (explicitSelection) {
|
|
4120
|
-
throw new Error('
|
|
4168
|
+
throw new Error('Watch, Vision Pro, TV and Mac profiles require native captures. iPhone, iPhone Duo and iPad support plain screenshot capture and reframing.');
|
|
4121
4169
|
}
|
|
4122
|
-
options.profiles = selected.filter((profile) => !
|
|
4170
|
+
options.profiles = selected.filter((profile) => !requiresNativeCapture(profile))
|
|
4123
4171
|
.map((profile) => profile.name).join(',');
|
|
4124
|
-
console.log('Skipping
|
|
4172
|
+
console.log('Skipping Watch, Vision Pro, TV and Mac profiles. iPhone/iPad plain screenshots are enabled.');
|
|
4125
4173
|
}
|
|
4126
4174
|
}
|
|
4127
4175
|
|
|
@@ -4258,7 +4306,7 @@ async function main() {
|
|
|
4258
4306
|
replayClient = await createReplayPage(replayBrowser);
|
|
4259
4307
|
|
|
4260
4308
|
console.log(
|
|
4261
|
-
'
|
|
4309
|
+
'Profiles are viewport approximations of the live Android WebView. iPhone/iPad outputs use plain screenshots without device frames.',
|
|
4262
4310
|
);
|
|
4263
4311
|
|
|
4264
4312
|
if (interactiveMode) {
|