codeplay-common 4.3.4 → 4.3.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.
|
@@ -3,9 +3,12 @@ const path = require('node:path');
|
|
|
3
3
|
const readline = require('node:readline/promises');
|
|
4
4
|
const { spawn, spawnSync } = require('node:child_process');
|
|
5
5
|
|
|
6
|
-
const screenshotDirectory = path.join(__dirname, 'Auto-Screenshot', '
|
|
7
|
-
const inFrameDirectory = path.join(__dirname, 'Auto-Screenshot', '
|
|
8
|
-
const
|
|
6
|
+
const screenshotDirectory = path.join(__dirname, 'Auto-Screenshot', 'Store-image-Screenshot');
|
|
7
|
+
const inFrameDirectory = path.join(__dirname, 'Auto-Screenshot', 'Store-image-Frame');
|
|
8
|
+
const amazonAssetsDirectory = path.join(__dirname, 'Auto-Screenshot', 'Amazon-assets');
|
|
9
|
+
const samsungAssetsDirectory = path.join(__dirname, 'Auto-Screenshot', 'Samsung-assets');
|
|
10
|
+
const capacitorConfigPath = path.join(__dirname, 'capacitor.config.json');
|
|
11
|
+
const amazonIconPath = path.join(__dirname, 'resources', 'icon-only.png');
|
|
9
12
|
const temporaryDirectory = path.join(__dirname, 'agent-temp');
|
|
10
13
|
const browserExecutablePaths = [
|
|
11
14
|
'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe',
|
|
@@ -25,18 +28,34 @@ const deviceProfiles = [
|
|
|
25
28
|
deviceScaleFactor: 3,
|
|
26
29
|
orientationSensitive: true,
|
|
27
30
|
mobile: true,
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
name: 'android-tablet',
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'android-tablet-7-inch',
|
|
34
|
+
aliases: ['android-tablet'],
|
|
35
|
+
label: 'Android 7-inch Play Store tablet',
|
|
36
|
+
outputDirectory: 'Android-Tab-7inch',
|
|
37
|
+
frameType: 'tablet',
|
|
38
|
+
width: 675,
|
|
39
|
+
height: 1200,
|
|
36
40
|
deviceScaleFactor: 2,
|
|
41
|
+
framedWidth: 1350,
|
|
42
|
+
framedHeight: 2400,
|
|
37
43
|
orientationSensitive: true,
|
|
38
44
|
mobile: true,
|
|
39
|
-
},
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: 'android-tablet-10-inch',
|
|
48
|
+
label: 'Android 10-inch Play Store tablet',
|
|
49
|
+
outputDirectory: 'Android-Tab-10inch',
|
|
50
|
+
frameType: 'tablet',
|
|
51
|
+
width: 900,
|
|
52
|
+
height: 1600,
|
|
53
|
+
deviceScaleFactor: 2,
|
|
54
|
+
framedWidth: 1800,
|
|
55
|
+
framedHeight: 3200,
|
|
56
|
+
orientationSensitive: true,
|
|
57
|
+
mobile: true,
|
|
58
|
+
},
|
|
40
59
|
{
|
|
41
60
|
name: 'iphone',
|
|
42
61
|
label: 'iPhone 6.9-inch App Store viewport',
|
|
@@ -141,16 +160,18 @@ const deviceProfiles = [
|
|
|
141
160
|
deviceScaleFactor: 1,
|
|
142
161
|
mobile: false,
|
|
143
162
|
},
|
|
144
|
-
{
|
|
145
|
-
name: 'chromebook',
|
|
146
|
-
label: 'Chromebook
|
|
147
|
-
outputDirectory: 'Chromebook',
|
|
148
|
-
frameType: 'laptop',
|
|
149
|
-
width:
|
|
150
|
-
height:
|
|
151
|
-
deviceScaleFactor:
|
|
152
|
-
|
|
153
|
-
|
|
163
|
+
{
|
|
164
|
+
name: 'chromebook',
|
|
165
|
+
label: 'Chromebook 4K Play Store viewport',
|
|
166
|
+
outputDirectory: 'Chromebook',
|
|
167
|
+
frameType: 'laptop',
|
|
168
|
+
width: 1920,
|
|
169
|
+
height: 1080,
|
|
170
|
+
deviceScaleFactor: 2,
|
|
171
|
+
framedWidth: 3840,
|
|
172
|
+
framedHeight: 2160,
|
|
173
|
+
mobile: false,
|
|
174
|
+
},
|
|
154
175
|
{
|
|
155
176
|
name: 'android-xr',
|
|
156
177
|
label: 'Android XR 2D panel approximation',
|
|
@@ -339,7 +360,9 @@ function getScreenshotFileName(fileNameInput, selectedProfiles) {
|
|
|
339
360
|
|
|
340
361
|
function parseArguments() {
|
|
341
362
|
const args = process.argv.slice(2);
|
|
342
|
-
const options = {
|
|
363
|
+
const options = {
|
|
364
|
+
amazonAssets: false,
|
|
365
|
+
samsungAssets: false,
|
|
343
366
|
dryRun: false,
|
|
344
367
|
frameExisting: false,
|
|
345
368
|
help: false,
|
|
@@ -351,7 +374,11 @@ function parseArguments() {
|
|
|
351
374
|
for (let index = 0; index < args.length; index += 1) {
|
|
352
375
|
const argument = args[index];
|
|
353
376
|
|
|
354
|
-
if (argument === '--
|
|
377
|
+
if (argument === '--amazon-assets') {
|
|
378
|
+
options.amazonAssets = true;
|
|
379
|
+
} else if (argument === '--samsung-assets') {
|
|
380
|
+
options.samsungAssets = true;
|
|
381
|
+
} else if (argument === '--dry-run') {
|
|
355
382
|
options.dryRun = true;
|
|
356
383
|
} else if (argument === '--frame-existing') {
|
|
357
384
|
options.frameExisting = true;
|
|
@@ -395,12 +422,14 @@ function printHelp() {
|
|
|
395
422
|
console.log('Capture the current live Android WebView at multiple responsive viewport sizes.');
|
|
396
423
|
console.log('');
|
|
397
424
|
console.log('Usage:');
|
|
398
|
-
console.log(' node take-
|
|
399
|
-
console.log(' node take-
|
|
400
|
-
console.log(' node take-
|
|
401
|
-
console.log(' node take-
|
|
402
|
-
console.log(' node take-
|
|
403
|
-
console.log(' node take-
|
|
425
|
+
console.log(' node take-screen-image.js');
|
|
426
|
+
console.log(' node take-screen-image.js --profiles iphone,ipad');
|
|
427
|
+
console.log(' node take-screen-image.js --prefix home --profiles android-phone,ipad');
|
|
428
|
+
console.log(' node take-screen-image.js --frame-existing');
|
|
429
|
+
console.log(' node take-screen-image.js --amazon-assets');
|
|
430
|
+
console.log(' node take-screen-image.js --samsung-assets');
|
|
431
|
+
console.log(' node take-screen-image.js --dry-run');
|
|
432
|
+
console.log(' node take-screen-image.js --list');
|
|
404
433
|
}
|
|
405
434
|
|
|
406
435
|
function selectProfiles(input) {
|
|
@@ -415,7 +444,9 @@ function selectProfiles(input) {
|
|
|
415
444
|
}
|
|
416
445
|
|
|
417
446
|
const selectedProfiles = names.map((name) => {
|
|
418
|
-
const profile = deviceProfiles.find((candidate) =>
|
|
447
|
+
const profile = deviceProfiles.find((candidate) => (
|
|
448
|
+
candidate.name === name || candidate.aliases?.includes(name)
|
|
449
|
+
));
|
|
419
450
|
|
|
420
451
|
if (!profile) {
|
|
421
452
|
throw new Error(`Unknown profile: ${name}. Run with --list to see valid profiles.`);
|
|
@@ -876,7 +907,7 @@ function getFrameAdornment(frameType) {
|
|
|
876
907
|
return '<div class="xr-status"></div>';
|
|
877
908
|
}
|
|
878
909
|
|
|
879
|
-
function createDeviceFrameHtml(profile, screenshotBuffer) {
|
|
910
|
+
function createDeviceFrameHtml(profile, screenshotBuffer) {
|
|
880
911
|
const screenshotDataUrl = `data:image/png;base64,${screenshotBuffer.toString('base64')}`;
|
|
881
912
|
const aspectRatio = `${profile.width} / ${profile.height}`;
|
|
882
913
|
|
|
@@ -1114,9 +1145,522 @@ function createDeviceFrameHtml(profile, screenshotBuffer) {
|
|
|
1114
1145
|
${getFrameAdornment(profile.frameType)}
|
|
1115
1146
|
</main>
|
|
1116
1147
|
</body>
|
|
1117
|
-
</html>`;
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1148
|
+
</html>`;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
function escapeHtml(value) {
|
|
1152
|
+
return String(value)
|
|
1153
|
+
.replace(/&/g, '&')
|
|
1154
|
+
.replace(/</g, '<')
|
|
1155
|
+
.replace(/>/g, '>')
|
|
1156
|
+
.replace(/"/g, '"')
|
|
1157
|
+
.replace(/'/g, ''');
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
function cleanAssetCopy(value, maximumLength, fallback = '') {
|
|
1161
|
+
const cleaned = String(value || '')
|
|
1162
|
+
.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, ' ')
|
|
1163
|
+
.replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, ' ')
|
|
1164
|
+
.replace(/<[^>]+>/g, ' ')
|
|
1165
|
+
.replace(/&[a-zA-Z#0-9]+;/g, ' ')
|
|
1166
|
+
.replace(/[_|]+/g, ' ')
|
|
1167
|
+
.replace(/\s+/g, ' ')
|
|
1168
|
+
.trim();
|
|
1169
|
+
if (!cleaned) return fallback;
|
|
1170
|
+
if (cleaned.length <= maximumLength) return cleaned;
|
|
1171
|
+
const shortened = cleaned.slice(0, maximumLength + 1);
|
|
1172
|
+
const lastSpace = shortened.lastIndexOf(' ');
|
|
1173
|
+
return shortened.slice(0, lastSpace > maximumLength * 0.55 ? lastSpace : maximumLength).trim();
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
function readJsonAssetSource(filePath) {
|
|
1177
|
+
try {
|
|
1178
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
1179
|
+
} catch {
|
|
1180
|
+
return {};
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
function collectProjectUiPhrases() {
|
|
1185
|
+
const phrases = [];
|
|
1186
|
+
const pageDirectory = path.join(__dirname, 'src', 'pages');
|
|
1187
|
+
if (fs.existsSync(pageDirectory)) {
|
|
1188
|
+
const pageFiles = fs.readdirSync(pageDirectory)
|
|
1189
|
+
.filter((fileName) => /\.(?:f7|html)$/i.test(fileName))
|
|
1190
|
+
.sort()
|
|
1191
|
+
.slice(0, 40);
|
|
1192
|
+
const visibleElementPattern = /<(?:h[1-4]|button|label|option|a|li)\b[^>]*>([\s\S]*?)<\/(?:h[1-4]|button|label|option|a|li)>/gi;
|
|
1193
|
+
for (const fileName of pageFiles) {
|
|
1194
|
+
const source = fs.readFileSync(path.join(pageDirectory, fileName), 'utf8');
|
|
1195
|
+
let match;
|
|
1196
|
+
while ((match = visibleElementPattern.exec(source)) && phrases.length < 120) {
|
|
1197
|
+
const phrase = cleanAssetCopy(match[1], 52);
|
|
1198
|
+
if (phrase) phrases.push(phrase);
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
return phrases;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
function getStoreAssetCopy(config) {
|
|
1206
|
+
const configuredAppName = cleanAssetCopy(config.appName, 40, 'App');
|
|
1207
|
+
const appUniqueId = String(config.android?.APP_UNIQUE_ID ?? '').trim();
|
|
1208
|
+
const promoCopyPath = appUniqueId
|
|
1209
|
+
? path.join(
|
|
1210
|
+
__dirname,
|
|
1211
|
+
'Auto-AI-Video',
|
|
1212
|
+
`${appUniqueId}. ${sanitizeName(configuredAppName, 'App')}-promo-copy.json`,
|
|
1213
|
+
)
|
|
1214
|
+
: '';
|
|
1215
|
+
const promoCopy = promoCopyPath && fs.existsSync(promoCopyPath)
|
|
1216
|
+
? readJsonAssetSource(promoCopyPath).copy || {}
|
|
1217
|
+
: {};
|
|
1218
|
+
const scenes = Array.isArray(promoCopy.scenes) ? promoCopy.scenes : [];
|
|
1219
|
+
const footerKeywords = Array.isArray(promoCopy.footer_keywords)
|
|
1220
|
+
? promoCopy.footer_keywords
|
|
1221
|
+
: [];
|
|
1222
|
+
const manifestCandidates = [
|
|
1223
|
+
path.join(__dirname, 'src', 'manifest.webmanifest'),
|
|
1224
|
+
path.join(__dirname, 'public', 'manifest.webmanifest'),
|
|
1225
|
+
path.join(__dirname, 'manifest.webmanifest'),
|
|
1226
|
+
];
|
|
1227
|
+
const manifestPath = manifestCandidates.find((candidate) => fs.existsSync(candidate));
|
|
1228
|
+
const manifest = manifestPath ? readJsonAssetSource(manifestPath) : {};
|
|
1229
|
+
const candidates = [
|
|
1230
|
+
...footerKeywords,
|
|
1231
|
+
...scenes.flatMap((scene) => [
|
|
1232
|
+
`${scene.title_line_1 || ''} ${scene.title_line_2 || ''}`,
|
|
1233
|
+
scene.kicker,
|
|
1234
|
+
scene.body,
|
|
1235
|
+
]),
|
|
1236
|
+
...(Array.isArray(manifest.categories) ? manifest.categories : []),
|
|
1237
|
+
manifest.description,
|
|
1238
|
+
...collectProjectUiPhrases(),
|
|
1239
|
+
];
|
|
1240
|
+
const uniquePhrases = [];
|
|
1241
|
+
const seen = new Set([configuredAppName.toLowerCase()]);
|
|
1242
|
+
for (const candidate of candidates) {
|
|
1243
|
+
const phrase = cleanAssetCopy(candidate, 52);
|
|
1244
|
+
const key = phrase.toLowerCase();
|
|
1245
|
+
if (phrase.length < 3 || seen.has(key)) continue;
|
|
1246
|
+
seen.add(key);
|
|
1247
|
+
uniquePhrases.push(phrase);
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
const featureLabels = uniquePhrases
|
|
1251
|
+
.map((phrase) => cleanAssetCopy(phrase, 18))
|
|
1252
|
+
.filter((phrase, index, values) => (
|
|
1253
|
+
phrase.length >= 3
|
|
1254
|
+
&& values.findIndex((value) => value.toLowerCase() === phrase.toLowerCase()) === index
|
|
1255
|
+
))
|
|
1256
|
+
.slice(0, 9);
|
|
1257
|
+
while (featureLabels.length < 9) {
|
|
1258
|
+
featureLabels.push(`Feature ${String(featureLabels.length + 1).padStart(2, '0')}`);
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
const firstScene = scenes[0] || {};
|
|
1262
|
+
const firstSceneTitle = cleanAssetCopy(
|
|
1263
|
+
`${firstScene.title_line_1 || ''} ${firstScene.title_line_2 || ''}`,
|
|
1264
|
+
34,
|
|
1265
|
+
);
|
|
1266
|
+
const primaryPhrase = footerKeywords[0] || featureLabels[0];
|
|
1267
|
+
return {
|
|
1268
|
+
amazonEyebrow: cleanAssetCopy(primaryPhrase, 32, `Explore ${configuredAppName}`),
|
|
1269
|
+
amazonCallout: cleanAssetCopy(
|
|
1270
|
+
firstScene.body || manifest.description || uniquePhrases[0],
|
|
1271
|
+
52,
|
|
1272
|
+
`Explore ${configuredAppName}`,
|
|
1273
|
+
),
|
|
1274
|
+
featureLabels,
|
|
1275
|
+
samsungKicker: cleanAssetCopy(firstScene.kicker || primaryPhrase, 28, 'App features'),
|
|
1276
|
+
samsungAction: firstSceneTitle || cleanAssetCopy(primaryPhrase, 34, `Open ${configuredAppName}`),
|
|
1277
|
+
samsungBody: cleanAssetCopy(
|
|
1278
|
+
firstScene.body || manifest.description || uniquePhrases[1],
|
|
1279
|
+
52,
|
|
1280
|
+
'Explore the available features',
|
|
1281
|
+
),
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
function createAmazonAssetHtml(asset, appName, iconBuffer, storeCopy) {
|
|
1286
|
+
const iconDataUrl = `data:image/png;base64,${iconBuffer.toString('base64')}`;
|
|
1287
|
+
const safeAppName = escapeHtml(appName);
|
|
1288
|
+
const safeEyebrow = escapeHtml(storeCopy.amazonEyebrow);
|
|
1289
|
+
const safeCallout = escapeHtml(storeCopy.amazonCallout);
|
|
1290
|
+
const featureMarkup = storeCopy.featureLabels.slice(0, 6)
|
|
1291
|
+
.map((label) => `<span>${escapeHtml(label)}</span>`)
|
|
1292
|
+
.join('');
|
|
1293
|
+
const logoMarkup = asset.type === 'featured-logo'
|
|
1294
|
+
? `<main class="featured-logo"><img src="${iconDataUrl}" /><div><small>${safeEyebrow}</small><strong>${safeAppName}</strong></div></main>`
|
|
1295
|
+
: '';
|
|
1296
|
+
const iconMarkup = asset.type === 'icon'
|
|
1297
|
+
? `<main class="icon-card"><img src="${iconDataUrl}" /><div><small>${safeCallout}</small><strong>${safeAppName}</strong></div></main>`
|
|
1298
|
+
: '';
|
|
1299
|
+
const backgroundMarkup = asset.type.includes('background')
|
|
1300
|
+
? `<main class="background-art"><div class="pulse pulse-one"></div><div class="pulse pulse-two"></div><div class="feature-grid">${featureMarkup}</div></main>`
|
|
1301
|
+
: '';
|
|
1302
|
+
|
|
1303
|
+
return `<!doctype html>
|
|
1304
|
+
<html>
|
|
1305
|
+
<head>
|
|
1306
|
+
<meta charset="utf-8" />
|
|
1307
|
+
<style>
|
|
1308
|
+
* { box-sizing: border-box; }
|
|
1309
|
+
html, body { width: 100%; height: 100%; margin: 0; overflow: hidden; }
|
|
1310
|
+
body {
|
|
1311
|
+
position: relative;
|
|
1312
|
+
font-family: Arial, Helvetica, sans-serif;
|
|
1313
|
+
color: #f8fbff;
|
|
1314
|
+
background: transparent;
|
|
1315
|
+
}
|
|
1316
|
+
body.icon,
|
|
1317
|
+
body.background-image,
|
|
1318
|
+
body.featured-background {
|
|
1319
|
+
background:
|
|
1320
|
+
radial-gradient(circle at 16% 18%, rgba(52, 211, 235, 0.32), transparent 28%),
|
|
1321
|
+
radial-gradient(circle at 82% 82%, rgba(201, 255, 65, 0.24), transparent 30%),
|
|
1322
|
+
linear-gradient(135deg, #24113c 0%, #15152d 48%, #071725 100%);
|
|
1323
|
+
}
|
|
1324
|
+
body.icon::before,
|
|
1325
|
+
body.background-image::before,
|
|
1326
|
+
body.featured-background::before {
|
|
1327
|
+
content: '';
|
|
1328
|
+
position: absolute;
|
|
1329
|
+
inset: 0;
|
|
1330
|
+
opacity: 0.22;
|
|
1331
|
+
background-image:
|
|
1332
|
+
linear-gradient(rgba(255,255,255,0.07) 1px, transparent 1px),
|
|
1333
|
+
linear-gradient(90deg, rgba(255,255,255,0.07) 1px, transparent 1px);
|
|
1334
|
+
background-size: 72px 72px;
|
|
1335
|
+
}
|
|
1336
|
+
.icon-card {
|
|
1337
|
+
position: relative;
|
|
1338
|
+
z-index: 2;
|
|
1339
|
+
width: 100%;
|
|
1340
|
+
height: 100%;
|
|
1341
|
+
display: flex;
|
|
1342
|
+
align-items: center;
|
|
1343
|
+
justify-content: center;
|
|
1344
|
+
gap: 72px;
|
|
1345
|
+
padding: 72px 110px;
|
|
1346
|
+
}
|
|
1347
|
+
.icon-card img {
|
|
1348
|
+
width: 500px;
|
|
1349
|
+
height: 500px;
|
|
1350
|
+
object-fit: contain;
|
|
1351
|
+
border-radius: 104px;
|
|
1352
|
+
box-shadow: 0 38px 80px rgba(0,0,0,0.48), 0 0 70px rgba(52,211,235,0.2);
|
|
1353
|
+
}
|
|
1354
|
+
.icon-card div { display: flex; flex-direction: column; gap: 18px; }
|
|
1355
|
+
.icon-card small,
|
|
1356
|
+
.featured-logo small {
|
|
1357
|
+
color: #35d4eb;
|
|
1358
|
+
font-size: 24px;
|
|
1359
|
+
font-weight: 700;
|
|
1360
|
+
letter-spacing: 7px;
|
|
1361
|
+
}
|
|
1362
|
+
.icon-card strong { font-size: 86px; line-height: 0.96; max-width: 500px; }
|
|
1363
|
+
.featured-logo {
|
|
1364
|
+
width: 100%;
|
|
1365
|
+
height: 100%;
|
|
1366
|
+
display: flex;
|
|
1367
|
+
align-items: center;
|
|
1368
|
+
gap: 24px;
|
|
1369
|
+
padding: 20px 26px;
|
|
1370
|
+
color: #111827;
|
|
1371
|
+
}
|
|
1372
|
+
.featured-logo img {
|
|
1373
|
+
width: 210px;
|
|
1374
|
+
height: 210px;
|
|
1375
|
+
object-fit: contain;
|
|
1376
|
+
border-radius: 44px;
|
|
1377
|
+
}
|
|
1378
|
+
.featured-logo div { display: flex; min-width: 0; flex-direction: column; gap: 8px; }
|
|
1379
|
+
.featured-logo small { font-size: 14px; letter-spacing: 3px; }
|
|
1380
|
+
.featured-logo strong {
|
|
1381
|
+
max-width: 360px;
|
|
1382
|
+
overflow: hidden;
|
|
1383
|
+
color: #ffffff;
|
|
1384
|
+
font-size: 56px;
|
|
1385
|
+
line-height: 1;
|
|
1386
|
+
text-overflow: ellipsis;
|
|
1387
|
+
white-space: nowrap;
|
|
1388
|
+
text-shadow: 0 3px 12px rgba(0,0,0,0.75);
|
|
1389
|
+
}
|
|
1390
|
+
.background-art { position: absolute; inset: 0; }
|
|
1391
|
+
.pulse {
|
|
1392
|
+
position: absolute;
|
|
1393
|
+
border: 3px solid rgba(53,212,235,0.28);
|
|
1394
|
+
border-radius: 50%;
|
|
1395
|
+
box-shadow: 0 0 80px rgba(53,212,235,0.15);
|
|
1396
|
+
}
|
|
1397
|
+
.pulse-one { width: 520px; height: 520px; top: -180px; left: -110px; }
|
|
1398
|
+
.pulse-two { width: 620px; height: 620px; right: -180px; bottom: -310px; border-color: rgba(201,255,65,0.22); }
|
|
1399
|
+
.feature-grid {
|
|
1400
|
+
position: absolute;
|
|
1401
|
+
left: 8%;
|
|
1402
|
+
right: 8%;
|
|
1403
|
+
top: 50%;
|
|
1404
|
+
display: grid;
|
|
1405
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
1406
|
+
gap: 24px;
|
|
1407
|
+
transform: translateY(-50%);
|
|
1408
|
+
}
|
|
1409
|
+
.feature-grid span {
|
|
1410
|
+
display: flex;
|
|
1411
|
+
min-height: 108px;
|
|
1412
|
+
align-items: center;
|
|
1413
|
+
justify-content: center;
|
|
1414
|
+
padding: 20px;
|
|
1415
|
+
color: #f8fbff;
|
|
1416
|
+
border: 2px solid rgba(255,255,255,0.17);
|
|
1417
|
+
border-radius: 22px;
|
|
1418
|
+
background: linear-gradient(145deg, rgba(42,55,75,0.96), rgba(11,18,29,0.96));
|
|
1419
|
+
box-shadow: 0 20px 28px rgba(0,0,0,0.28);
|
|
1420
|
+
font-size: clamp(20px, 2vw, 34px);
|
|
1421
|
+
font-weight: 700;
|
|
1422
|
+
text-align: center;
|
|
1423
|
+
}
|
|
1424
|
+
.feature-grid span:nth-child(3n+1) { border-top: 7px solid #35d4eb; }
|
|
1425
|
+
.feature-grid span:nth-child(3n+2) { border-top: 7px solid #ff9f43; }
|
|
1426
|
+
.feature-grid span:nth-child(3n) { border-top: 7px solid #c9ff41; }
|
|
1427
|
+
body.featured-background .feature-grid { left: 5%; right: 5%; gap: 18px; }
|
|
1428
|
+
</style>
|
|
1429
|
+
</head>
|
|
1430
|
+
<body class="${asset.type}">
|
|
1431
|
+
${iconMarkup}
|
|
1432
|
+
${logoMarkup}
|
|
1433
|
+
${backgroundMarkup}
|
|
1434
|
+
</body>
|
|
1435
|
+
</html>`;
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
async function createAmazonAssets(client) {
|
|
1439
|
+
if (!fs.existsSync(amazonIconPath)) {
|
|
1440
|
+
throw new Error(`Amazon asset source icon was not found: ${amazonIconPath}`);
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
const config = JSON.parse(fs.readFileSync(capacitorConfigPath, 'utf8'));
|
|
1444
|
+
const appName = config.appName || 'App';
|
|
1445
|
+
const storeCopy = getStoreAssetCopy(config);
|
|
1446
|
+
const iconBuffer = fs.readFileSync(amazonIconPath);
|
|
1447
|
+
const assets = [
|
|
1448
|
+
{ fileName: 'icon-1280x720.png', type: 'icon', width: 1280, height: 720 },
|
|
1449
|
+
{ fileName: 'background-image-1920x1080.png', type: 'background-image', width: 1920, height: 1080 },
|
|
1450
|
+
{ fileName: 'featured-content-logo-640x260.png', type: 'featured-logo', width: 640, height: 260, transparent: true },
|
|
1451
|
+
{ fileName: 'featured-content-background-1920x720.png', type: 'featured-background', width: 1920, height: 720 },
|
|
1452
|
+
];
|
|
1453
|
+
|
|
1454
|
+
fs.mkdirSync(amazonAssetsDirectory, { recursive: true });
|
|
1455
|
+
|
|
1456
|
+
for (const asset of assets) {
|
|
1457
|
+
await client.send('Emulation.setDeviceMetricsOverride', {
|
|
1458
|
+
width: asset.width,
|
|
1459
|
+
height: asset.height,
|
|
1460
|
+
deviceScaleFactor: 1,
|
|
1461
|
+
mobile: false,
|
|
1462
|
+
screenWidth: asset.width,
|
|
1463
|
+
screenHeight: asset.height,
|
|
1464
|
+
positionX: 0,
|
|
1465
|
+
positionY: 0,
|
|
1466
|
+
});
|
|
1467
|
+
|
|
1468
|
+
const frameTree = await client.send('Page.getFrameTree');
|
|
1469
|
+
await client.send('Page.setDocumentContent', {
|
|
1470
|
+
frameId: frameTree.frameTree.frame.id,
|
|
1471
|
+
html: createAmazonAssetHtml(asset, appName, iconBuffer, storeCopy),
|
|
1472
|
+
});
|
|
1473
|
+
await client.send('Runtime.evaluate', {
|
|
1474
|
+
expression: `Promise.all([...document.images].map((image) => image.complete
|
|
1475
|
+
? Promise.resolve()
|
|
1476
|
+
: new Promise((resolve) => {
|
|
1477
|
+
image.addEventListener('load', resolve, { once: true });
|
|
1478
|
+
image.addEventListener('error', resolve, { once: true });
|
|
1479
|
+
}))).then(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))))`,
|
|
1480
|
+
awaitPromise: true,
|
|
1481
|
+
returnByValue: true,
|
|
1482
|
+
});
|
|
1483
|
+
|
|
1484
|
+
const screenshot = await client.send('Page.captureScreenshot', {
|
|
1485
|
+
format: 'png',
|
|
1486
|
+
fromSurface: true,
|
|
1487
|
+
captureBeyondViewport: false,
|
|
1488
|
+
omitBackground: Boolean(asset.transparent),
|
|
1489
|
+
});
|
|
1490
|
+
const outputPath = path.join(amazonAssetsDirectory, asset.fileName);
|
|
1491
|
+
fs.writeFileSync(outputPath, Buffer.from(screenshot.data, 'base64'));
|
|
1492
|
+
console.log(`Saved Amazon asset ${asset.width}x${asset.height}: ${outputPath}`);
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
function createSamsungAssetHtml(asset, appName, iconBuffer, storeCopy) {
|
|
1497
|
+
const iconDataUrl = `data:image/png;base64,${iconBuffer.toString('base64')}`;
|
|
1498
|
+
const safeAppName = escapeHtml(appName);
|
|
1499
|
+
const featureButtons = storeCopy.featureLabels.map((label, index) => (
|
|
1500
|
+
`<button><i>0${index + 1}</i><span>${escapeHtml(label.toUpperCase())}</span></button>`
|
|
1501
|
+
)).join('');
|
|
1502
|
+
const safeKicker = escapeHtml(storeCopy.samsungKicker.toUpperCase());
|
|
1503
|
+
const safeAction = escapeHtml(storeCopy.samsungAction.toUpperCase());
|
|
1504
|
+
const safeBody = escapeHtml(storeCopy.samsungBody);
|
|
1505
|
+
const content = asset.type === 'edge-screen'
|
|
1506
|
+
? `<main class="edge-panel"><header><img src="${iconDataUrl}" /><strong>${safeAppName}</strong></header><section>${featureButtons}</section></main>`
|
|
1507
|
+
: `<main class="single-panel"><header><img src="${iconDataUrl}" /><div><small>${safeKicker}</small><strong>${safeAppName}</strong></div></header><div class="single-action"><span>+</span><strong>${safeAction}</strong><small>${safeBody}</small></div><div class="accent-bars"><i></i><i></i><i></i><i></i><i></i></div></main>`;
|
|
1508
|
+
|
|
1509
|
+
return `<!doctype html>
|
|
1510
|
+
<html>
|
|
1511
|
+
<head>
|
|
1512
|
+
<meta charset="utf-8" />
|
|
1513
|
+
<style>
|
|
1514
|
+
* { box-sizing: border-box; }
|
|
1515
|
+
html, body { width: 100%; height: 100%; margin: 0; overflow: hidden; }
|
|
1516
|
+
body {
|
|
1517
|
+
position: relative;
|
|
1518
|
+
color: #f7fbff;
|
|
1519
|
+
background:
|
|
1520
|
+
radial-gradient(circle at 50% 8%, rgba(53,212,235,0.26), transparent 24%),
|
|
1521
|
+
radial-gradient(circle at 50% 88%, rgba(201,255,65,0.16), transparent 25%),
|
|
1522
|
+
linear-gradient(180deg, #231039, #111729 48%, #071522);
|
|
1523
|
+
font-family: Arial, Helvetica, sans-serif;
|
|
1524
|
+
}
|
|
1525
|
+
body::before {
|
|
1526
|
+
content: '';
|
|
1527
|
+
position: absolute;
|
|
1528
|
+
inset: 0;
|
|
1529
|
+
opacity: 0.18;
|
|
1530
|
+
background: repeating-linear-gradient(0deg, transparent 0 79px, rgba(255,255,255,0.08) 80px);
|
|
1531
|
+
}
|
|
1532
|
+
main { position: relative; z-index: 1; width: 100%; height: 100%; }
|
|
1533
|
+
.edge-panel { display: flex; flex-direction: column; padding: 28px 12px; }
|
|
1534
|
+
.edge-panel header { display: flex; flex-direction: column; align-items: center; gap: 12px; min-height: 230px; }
|
|
1535
|
+
.edge-panel header img { width: 112px; height: 112px; border-radius: 25px; object-fit: contain; }
|
|
1536
|
+
.edge-panel header strong { max-width: 136px; overflow: hidden; font-size: 18px; text-overflow: ellipsis; white-space: nowrap; }
|
|
1537
|
+
.edge-panel section { display: grid; flex: 1; grid-template-rows: repeat(9, 1fr); gap: 13px; }
|
|
1538
|
+
.edge-panel button {
|
|
1539
|
+
display: flex;
|
|
1540
|
+
width: 100%;
|
|
1541
|
+
min-height: 0;
|
|
1542
|
+
flex-direction: column;
|
|
1543
|
+
align-items: center;
|
|
1544
|
+
justify-content: center;
|
|
1545
|
+
gap: 8px;
|
|
1546
|
+
color: #f7fbff;
|
|
1547
|
+
border: 1px solid rgba(255,255,255,0.16);
|
|
1548
|
+
border-top: 5px solid #35d4eb;
|
|
1549
|
+
border-radius: 18px;
|
|
1550
|
+
background: linear-gradient(145deg, rgba(41,55,76,0.98), rgba(10,17,29,0.98));
|
|
1551
|
+
box-shadow: 0 10px 18px rgba(0,0,0,0.3);
|
|
1552
|
+
}
|
|
1553
|
+
.edge-panel button:nth-child(3n+2) { border-top-color: #ff9f43; }
|
|
1554
|
+
.edge-panel button:nth-child(3n) { border-top-color: #c9ff41; }
|
|
1555
|
+
.edge-panel button i { color: #35d4eb; font-size: 14px; font-style: normal; font-weight: 700; }
|
|
1556
|
+
.edge-panel button span { font-size: 12px; font-weight: 700; letter-spacing: 1px; }
|
|
1557
|
+
.single-panel { display: flex; flex-direction: column; align-items: center; padding: 100px 44px; }
|
|
1558
|
+
.single-panel header { display: flex; width: 100%; align-items: center; gap: 24px; }
|
|
1559
|
+
.single-panel header img { width: 160px; height: 160px; border-radius: 36px; object-fit: contain; }
|
|
1560
|
+
.single-panel header div { display: flex; min-width: 0; flex-direction: column; gap: 9px; }
|
|
1561
|
+
.single-panel header small { color: #35d4eb; font-size: 17px; font-weight: 700; letter-spacing: 3px; }
|
|
1562
|
+
.single-panel header strong { overflow: hidden; font-size: 48px; text-overflow: ellipsis; white-space: nowrap; }
|
|
1563
|
+
.single-action {
|
|
1564
|
+
display: flex;
|
|
1565
|
+
width: 100%;
|
|
1566
|
+
height: 880px;
|
|
1567
|
+
margin-top: 380px;
|
|
1568
|
+
flex-direction: column;
|
|
1569
|
+
align-items: center;
|
|
1570
|
+
justify-content: center;
|
|
1571
|
+
border: 3px solid rgba(53,212,235,0.5);
|
|
1572
|
+
border-radius: 90px;
|
|
1573
|
+
background: linear-gradient(145deg, rgba(39,53,75,0.96), rgba(8,16,28,0.98));
|
|
1574
|
+
box-shadow: 0 50px 90px rgba(0,0,0,0.42), 0 0 75px rgba(53,212,235,0.16);
|
|
1575
|
+
}
|
|
1576
|
+
.single-action span {
|
|
1577
|
+
display: grid;
|
|
1578
|
+
width: 260px;
|
|
1579
|
+
height: 260px;
|
|
1580
|
+
place-items: center;
|
|
1581
|
+
color: #0d1721;
|
|
1582
|
+
border-radius: 50%;
|
|
1583
|
+
background: linear-gradient(145deg, #35d4eb, #c9ff41);
|
|
1584
|
+
box-shadow: 0 25px 55px rgba(53,212,235,0.3);
|
|
1585
|
+
font-size: 170px;
|
|
1586
|
+
font-weight: 200;
|
|
1587
|
+
line-height: 1;
|
|
1588
|
+
}
|
|
1589
|
+
.single-action strong { margin-top: 65px; font-size: 42px; letter-spacing: 5px; }
|
|
1590
|
+
.single-action small { margin-top: 18px; color: #a9b7c9; font-size: 24px; }
|
|
1591
|
+
.accent-bars { display: flex; align-items: center; gap: 18px; height: 300px; margin-top: auto; }
|
|
1592
|
+
.accent-bars i { width: 14px; height: 70px; border-radius: 999px; background: #35d4eb; }
|
|
1593
|
+
.accent-bars i:nth-child(2), .accent-bars i:nth-child(4) { height: 140px; background: #ff5964; }
|
|
1594
|
+
.accent-bars i:nth-child(3) { height: 210px; background: #c9ff41; }
|
|
1595
|
+
</style>
|
|
1596
|
+
</head>
|
|
1597
|
+
<body>${content}</body>
|
|
1598
|
+
</html>`;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
async function createSamsungAssets(client) {
|
|
1602
|
+
if (!fs.existsSync(amazonIconPath)) {
|
|
1603
|
+
throw new Error(`Samsung asset source icon was not found: ${amazonIconPath}`);
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
const config = JSON.parse(fs.readFileSync(capacitorConfigPath, 'utf8'));
|
|
1607
|
+
const appName = config.appName || 'App';
|
|
1608
|
+
const storeCopy = getStoreAssetCopy(config);
|
|
1609
|
+
const iconBuffer = fs.readFileSync(amazonIconPath);
|
|
1610
|
+
const assets = [
|
|
1611
|
+
{ fileName: 'edge-screen-160x2560.png', type: 'edge-screen', width: 160, height: 2560 },
|
|
1612
|
+
{ fileName: 'edge-screen-single-plus-550x2560.png', type: 'edge-screen-single-plus', width: 550, height: 2560 },
|
|
1613
|
+
];
|
|
1614
|
+
const maximumFileSize = 1024 * 1024;
|
|
1615
|
+
|
|
1616
|
+
fs.mkdirSync(samsungAssetsDirectory, { recursive: true });
|
|
1617
|
+
|
|
1618
|
+
for (const asset of assets) {
|
|
1619
|
+
await client.send('Emulation.setDeviceMetricsOverride', {
|
|
1620
|
+
width: asset.width,
|
|
1621
|
+
height: asset.height,
|
|
1622
|
+
deviceScaleFactor: 1,
|
|
1623
|
+
mobile: false,
|
|
1624
|
+
screenWidth: asset.width,
|
|
1625
|
+
screenHeight: asset.height,
|
|
1626
|
+
positionX: 0,
|
|
1627
|
+
positionY: 0,
|
|
1628
|
+
});
|
|
1629
|
+
const frameTree = await client.send('Page.getFrameTree');
|
|
1630
|
+
await client.send('Page.setDocumentContent', {
|
|
1631
|
+
frameId: frameTree.frameTree.frame.id,
|
|
1632
|
+
html: createSamsungAssetHtml(asset, appName, iconBuffer, storeCopy),
|
|
1633
|
+
});
|
|
1634
|
+
await client.send('Runtime.evaluate', {
|
|
1635
|
+
expression: `Promise.all([...document.images].map((image) => image.complete
|
|
1636
|
+
? Promise.resolve()
|
|
1637
|
+
: new Promise((resolve) => image.addEventListener('load', resolve, { once: true }))))
|
|
1638
|
+
.then(() => new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve))))`,
|
|
1639
|
+
awaitPromise: true,
|
|
1640
|
+
returnByValue: true,
|
|
1641
|
+
});
|
|
1642
|
+
const screenshot = await client.send('Page.captureScreenshot', {
|
|
1643
|
+
format: 'png',
|
|
1644
|
+
fromSurface: true,
|
|
1645
|
+
captureBeyondViewport: false,
|
|
1646
|
+
});
|
|
1647
|
+
const outputPath = path.join(samsungAssetsDirectory, asset.fileName);
|
|
1648
|
+
const outputBuffer = Buffer.from(screenshot.data, 'base64');
|
|
1649
|
+
|
|
1650
|
+
if (outputBuffer.length >= maximumFileSize) {
|
|
1651
|
+
throw new Error(
|
|
1652
|
+
`Samsung asset exceeds 1024 KB (${Math.ceil(outputBuffer.length / 1024)} KB): ${asset.fileName}`,
|
|
1653
|
+
);
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
fs.writeFileSync(outputPath, outputBuffer);
|
|
1657
|
+
console.log(
|
|
1658
|
+
`Saved Samsung asset ${asset.width}x${asset.height} `
|
|
1659
|
+
+ `(${Math.ceil(outputBuffer.length / 1024)} KB): ${outputPath}`,
|
|
1660
|
+
);
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1120
1664
|
async function captureFramedScreenshot(
|
|
1121
1665
|
client,
|
|
1122
1666
|
screenshotBuffer,
|
|
@@ -1332,8 +1876,13 @@ async function runInteractiveCaptureLoop(
|
|
|
1332
1876
|
console.log('\nResponsive screenshot tool closed.');
|
|
1333
1877
|
}
|
|
1334
1878
|
|
|
1335
|
-
async function getInteractiveOptions(options) {
|
|
1336
|
-
if (
|
|
1879
|
+
async function getInteractiveOptions(options) {
|
|
1880
|
+
if (
|
|
1881
|
+
!process.stdin.isTTY
|
|
1882
|
+
|| options.dryRun
|
|
1883
|
+
|| options.amazonAssets
|
|
1884
|
+
|| options.samsungAssets
|
|
1885
|
+
) {
|
|
1337
1886
|
return options;
|
|
1338
1887
|
}
|
|
1339
1888
|
|
|
@@ -1370,9 +1919,43 @@ async function main() {
|
|
|
1370
1919
|
return;
|
|
1371
1920
|
}
|
|
1372
1921
|
|
|
1373
|
-
options = await getInteractiveOptions(options);
|
|
1374
|
-
|
|
1375
|
-
|
|
1922
|
+
options = await getInteractiveOptions(options);
|
|
1923
|
+
|
|
1924
|
+
if (options.amazonAssets) {
|
|
1925
|
+
let amazonBrowser;
|
|
1926
|
+
let amazonClient;
|
|
1927
|
+
|
|
1928
|
+
try {
|
|
1929
|
+
amazonBrowser = await launchReplayBrowser();
|
|
1930
|
+
amazonClient = await createReplayPage(amazonBrowser);
|
|
1931
|
+
await createAmazonAssets(amazonClient);
|
|
1932
|
+
} finally {
|
|
1933
|
+
if (amazonClient) amazonClient.close();
|
|
1934
|
+
if (amazonBrowser) await closeReplayBrowser(amazonBrowser);
|
|
1935
|
+
}
|
|
1936
|
+
|
|
1937
|
+
console.log(`Amazon asset creation completed: ${amazonAssetsDirectory}`);
|
|
1938
|
+
return;
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
if (options.samsungAssets) {
|
|
1942
|
+
let samsungBrowser;
|
|
1943
|
+
let samsungClient;
|
|
1944
|
+
|
|
1945
|
+
try {
|
|
1946
|
+
samsungBrowser = await launchReplayBrowser();
|
|
1947
|
+
samsungClient = await createReplayPage(samsungBrowser);
|
|
1948
|
+
await createSamsungAssets(samsungClient);
|
|
1949
|
+
} finally {
|
|
1950
|
+
if (samsungClient) samsungClient.close();
|
|
1951
|
+
if (samsungBrowser) await closeReplayBrowser(samsungBrowser);
|
|
1952
|
+
}
|
|
1953
|
+
|
|
1954
|
+
console.log(`Samsung asset creation completed: ${samsungAssetsDirectory}`);
|
|
1955
|
+
return;
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
const selectedProfiles = selectProfiles(options.profiles);
|
|
1376
1959
|
|
|
1377
1960
|
if (options.frameExisting) {
|
|
1378
1961
|
await frameExistingScreenshots(selectedProfiles);
|