codeplay-common 4.8.3 → 4.8.5
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 +108 -26
- package/package.json +1 -1
|
@@ -174,7 +174,7 @@ const deviceProfiles = [
|
|
|
174
174
|
},
|
|
175
175
|
{
|
|
176
176
|
name: 'iphone-duo',
|
|
177
|
-
label: 'iPhone Duo inner-display App Store viewport
|
|
177
|
+
label: 'iPhone Duo inner-display App Store viewport',
|
|
178
178
|
outputDirectory: 'iPhone-Duo',
|
|
179
179
|
frameType: 'iphone',
|
|
180
180
|
showDynamicIsland: false,
|
|
@@ -927,21 +927,21 @@ async function getWebViewTarget(localPort) {
|
|
|
927
927
|
&& target.url !== 'about:blank'
|
|
928
928
|
));
|
|
929
929
|
|
|
930
|
-
const
|
|
930
|
+
for (const target of pageTargets) {
|
|
931
|
+
const client = new CdpClient(target.webSocketDebuggerUrl);
|
|
931
932
|
try {
|
|
932
|
-
|
|
933
|
+
await client.connect();
|
|
934
|
+
const details = await getPageDetails(client);
|
|
935
|
+
if (details.width > 0 && details.height > 0 && details.hasAppPage) {
|
|
936
|
+
return target;
|
|
937
|
+
}
|
|
933
938
|
} catch {
|
|
934
|
-
|
|
939
|
+
// A target may close while discovering the active app WebView.
|
|
940
|
+
} finally {
|
|
941
|
+
client.close();
|
|
935
942
|
}
|
|
936
|
-
});
|
|
937
|
-
|
|
938
|
-
const target = visibleTarget || pageTargets[0];
|
|
939
|
-
|
|
940
|
-
if (!target) {
|
|
941
|
-
throw new Error('No rendered page was found in the app WebView.');
|
|
942
943
|
}
|
|
943
|
-
|
|
944
|
-
return target;
|
|
944
|
+
throw new Error('No visible Framework7 app page was found. Open the app page and retry; ad WebViews cannot be captured.');
|
|
945
945
|
}
|
|
946
946
|
|
|
947
947
|
async function getPageDetails(client) {
|
|
@@ -952,6 +952,14 @@ async function getPageDetails(client) {
|
|
|
952
952
|
width: innerWidth,
|
|
953
953
|
height: innerHeight,
|
|
954
954
|
devicePixelRatio: devicePixelRatio,
|
|
955
|
+
hasAppPage: [...document.querySelectorAll('.view .page')].some((page) => {
|
|
956
|
+
const bounds = page.getBoundingClientRect();
|
|
957
|
+
const style = getComputedStyle(page);
|
|
958
|
+
return style.display !== 'none' && style.visibility !== 'hidden'
|
|
959
|
+
&& bounds.width > 0 && bounds.height > 0
|
|
960
|
+
&& bounds.bottom > 0 && bounds.right > 0
|
|
961
|
+
&& bounds.top < innerHeight && bounds.left < innerWidth;
|
|
962
|
+
}),
|
|
955
963
|
readyState: document.readyState
|
|
956
964
|
})`,
|
|
957
965
|
returnByValue: true,
|
|
@@ -1041,7 +1049,7 @@ async function capturePageSnapshot(client) {
|
|
|
1041
1049
|
? element.closest('label, .item-content, .input')
|
|
1042
1050
|
: element;
|
|
1043
1051
|
const preferredLabel = labelRoot?.querySelector(
|
|
1044
|
-
'.item-title, .button-label, strong, .dialog-button-text',
|
|
1052
|
+
'.item-title, .button-label, strong, .dialog-button-text, .homeboxtitle',
|
|
1045
1053
|
);
|
|
1046
1054
|
const textRoot = preferredLabel || labelRoot || element;
|
|
1047
1055
|
const clone = textRoot.cloneNode(true);
|
|
@@ -1065,7 +1073,7 @@ async function capturePageSnapshot(client) {
|
|
|
1065
1073
|
if (!root) return [];
|
|
1066
1074
|
const records = [];
|
|
1067
1075
|
const seen = new Set();
|
|
1068
|
-
const selector = 'button,
|
|
1076
|
+
const selector = 'button, .button, [role="button"], .list-button, .dialog-button';
|
|
1069
1077
|
for (const element of root.querySelectorAll(selector)) {
|
|
1070
1078
|
if (!isVisible(element)) continue;
|
|
1071
1079
|
const label = getElementLabel(element);
|
|
@@ -1137,6 +1145,9 @@ async function capturePageSnapshot(client) {
|
|
|
1137
1145
|
const activePage = visiblePages.find((page) => page.classList.contains('page-current'))
|
|
1138
1146
|
|| visiblePages[visiblePages.length - 1]
|
|
1139
1147
|
|| null;
|
|
1148
|
+
if (!activePage) {
|
|
1149
|
+
throw new Error('No visible app page was found; screenshot information was not captured.');
|
|
1150
|
+
}
|
|
1140
1151
|
const overlaySelector = [
|
|
1141
1152
|
'.dialog.modal-in',
|
|
1142
1153
|
'.popup.modal-in',
|
|
@@ -1161,12 +1172,12 @@ async function capturePageSnapshot(client) {
|
|
|
1161
1172
|
].join(',');
|
|
1162
1173
|
const pageHeadings = collectUiText(
|
|
1163
1174
|
activePage,
|
|
1164
|
-
'h1, h2, h3, .navbar .title, .t-section-heading, .headTitle',
|
|
1175
|
+
'h1, h2, h3, .navbar .title, .t-section-heading, .headTitle, .home-hero-title, .home-section-title',
|
|
1165
1176
|
10,
|
|
1166
1177
|
);
|
|
1167
1178
|
const pageControls = collectUiText(
|
|
1168
1179
|
activePage,
|
|
1169
|
-
'button, [role="button"], label, .item-title, .item-subtitle, .list-button, option:checked',
|
|
1180
|
+
'button, [role="button"], label, .item-title, .item-subtitle, .list-button, option:checked, .homeboxtitle',
|
|
1170
1181
|
30,
|
|
1171
1182
|
);
|
|
1172
1183
|
const overlayText = collectUiText(activeOverlay, textSelector, 35);
|
|
@@ -1184,6 +1195,8 @@ async function capturePageSnapshot(client) {
|
|
|
1184
1195
|
params: Object.keys(f7Route.params || {}),
|
|
1185
1196
|
query: Object.keys(f7Route.query || {}),
|
|
1186
1197
|
} : null;
|
|
1198
|
+
const themeAttribute = document.documentElement.getAttribute('data-theme')
|
|
1199
|
+
|| document.body.getAttribute('data-theme');
|
|
1187
1200
|
const screenInfo = {
|
|
1188
1201
|
documentTitle: normalizeUiText(document.title),
|
|
1189
1202
|
route: {
|
|
@@ -1195,16 +1208,16 @@ async function capturePageSnapshot(client) {
|
|
|
1195
1208
|
dataName: activePage.getAttribute('data-name') || '',
|
|
1196
1209
|
id: activePage.id || '',
|
|
1197
1210
|
classes: getStableClasses(activePage),
|
|
1198
|
-
navbarTitle: collectUiText(activePage, '.navbar .title', 1)[0] || '',
|
|
1211
|
+
navbarTitle: collectUiText(activePage, '.navbar .title, .navbar .home-brand-text', 1)[0] || '',
|
|
1199
1212
|
headings: pageHeadings,
|
|
1200
1213
|
sections: collectUiText(
|
|
1201
1214
|
activePage,
|
|
1202
|
-
'h1, h2, h3, legend, .t-section-heading, .t-output-choice-heading, .headTitle',
|
|
1215
|
+
'h1, h2, h3, legend, .t-section-heading, .t-output-choice-heading, .headTitle, .home-hero-title, .home-section-title',
|
|
1203
1216
|
20,
|
|
1204
1217
|
),
|
|
1205
1218
|
descriptions: collectUiText(
|
|
1206
1219
|
activePage,
|
|
1207
|
-
'p, small, .item-subtitle, .item-after, .t-settings-footnote',
|
|
1220
|
+
'p, small, .item-subtitle, .item-after, .t-settings-footnote, .home-hero-kicker, .home-hero-subtitle, .home-stat, .homebox-meta',
|
|
1208
1221
|
30,
|
|
1209
1222
|
),
|
|
1210
1223
|
controls: pageControls,
|
|
@@ -1232,10 +1245,12 @@ async function capturePageSnapshot(client) {
|
|
|
1232
1245
|
)),
|
|
1233
1246
|
inputs: overlayInputs,
|
|
1234
1247
|
} : null,
|
|
1235
|
-
theme:
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1248
|
+
theme: themeAttribute === 'dark' || themeAttribute === 'light'
|
|
1249
|
+
? themeAttribute
|
|
1250
|
+
: document.documentElement.classList.contains('theme-dark')
|
|
1251
|
+
|| document.body.classList.contains('theme-dark')
|
|
1252
|
+
? 'dark'
|
|
1253
|
+
: 'light',
|
|
1239
1254
|
viewport: {
|
|
1240
1255
|
width: innerWidth,
|
|
1241
1256
|
height: innerHeight,
|
|
@@ -2250,6 +2265,40 @@ function createDeviceFrameHtml(
|
|
|
2250
2265
|
border-radius: 999px;
|
|
2251
2266
|
background: #000;
|
|
2252
2267
|
}
|
|
2268
|
+
${profile.name === 'iphone-duo' ? `
|
|
2269
|
+
.device.iphone-duo {
|
|
2270
|
+
--duo-radius: clamp(24px, 4vw, 100px);
|
|
2271
|
+
--duo-bezel: clamp(5px, 0.65vw, 14px);
|
|
2272
|
+
--duo-edge: clamp(2px, 0.2vw, 5px);
|
|
2273
|
+
container-type: inline-size;
|
|
2274
|
+
padding: var(--duo-bezel);
|
|
2275
|
+
border-width: var(--duo-edge);
|
|
2276
|
+
border-radius: 8px var(--duo-radius) var(--duo-radius) 8px;
|
|
2277
|
+
}
|
|
2278
|
+
.device.iphone-duo .screen {
|
|
2279
|
+
border-radius: 2px calc(var(--duo-radius) - var(--duo-bezel) - var(--duo-edge)) calc(var(--duo-radius) - var(--duo-bezel) - var(--duo-edge)) 2px;
|
|
2280
|
+
}
|
|
2281
|
+
.iphone-duo .ios-statusbar { height: 4.5%; }
|
|
2282
|
+
.iphone-duo .status-time { margin-left: 4%; font-size: 2.4cqw; line-height: 1; }
|
|
2283
|
+
.iphone-duo .ios-statusbar svg { width: 17%; height: 54%; margin-right: 8%; }
|
|
2284
|
+
.duo-camera {
|
|
2285
|
+
position: absolute;
|
|
2286
|
+
z-index: 3;
|
|
2287
|
+
top: 2.25%;
|
|
2288
|
+
right: 3%;
|
|
2289
|
+
width: 1.8%;
|
|
2290
|
+
aspect-ratio: 1;
|
|
2291
|
+
transform: translateY(-50%);
|
|
2292
|
+
border-radius: 50%;
|
|
2293
|
+
background: #000;
|
|
2294
|
+
}
|
|
2295
|
+
@media (orientation: landscape) {
|
|
2296
|
+
.device.iphone-duo { border-radius: var(--duo-radius); }
|
|
2297
|
+
.device.iphone-duo .screen { border-radius: calc(var(--duo-radius) - var(--duo-bezel) - var(--duo-edge)); }
|
|
2298
|
+
.iphone-duo .status-time { margin-left: max(4%, calc(var(--duo-radius) * 0.6)); font-size: 1.8cqw; }
|
|
2299
|
+
.iphone-duo .ios-statusbar svg { width: 13%; margin-right: max(4%, calc(var(--duo-radius) * 0.6)); }
|
|
2300
|
+
.duo-camera { display: none; }
|
|
2301
|
+
}` : ''}
|
|
2253
2302
|
.android-statusbar { height: 4%; background: ${statusBarStyle?.background || 'transparent'}; color: ${statusBarStyle?.foreground || '#050505'}; font-family: Roboto, Arial, sans-serif; }
|
|
2254
2303
|
.android-notification { width: 3.4%; height: 46%; margin-left: 2%; }
|
|
2255
2304
|
.android-status-icons { width: 32%; height: 58%; margin-left: auto; margin-right: 4%; }
|
|
@@ -2567,8 +2616,8 @@ function createDeviceFrameHtml(
|
|
|
2567
2616
|
<body class="${phonePlatform ? 'phone-frame' : ''} ${screenshotCopy ? 'has-copy' : ''}">
|
|
2568
2617
|
${createPromotionalDecorations(palette)}
|
|
2569
2618
|
${createScreenshotCopyMarkup(screenshotCopy)}
|
|
2570
|
-
<main class="device ${profile.frameType}">
|
|
2571
|
-
<div class="screen">${phoneStatusBar}<img src="${screenshotDataUrl}" />${phoneNavigation}</div>
|
|
2619
|
+
<main class="device ${profile.frameType}${profile.name === 'iphone-duo' ? ' iphone-duo' : ''}">
|
|
2620
|
+
<div class="screen">${phoneStatusBar}<img src="${screenshotDataUrl}" />${phoneNavigation}${profile.name === 'iphone-duo' ? '<div class="duo-camera" aria-hidden="true"></div>' : ''}</div>
|
|
2572
2621
|
${getFrameAdornment(profile.frameType)}
|
|
2573
2622
|
</main>
|
|
2574
2623
|
</body>
|
|
@@ -3365,7 +3414,25 @@ async function captureFramedScreenshot(
|
|
|
3365
3414
|
await client.send('Runtime.evaluate', {
|
|
3366
3415
|
expression: `new Promise((resolve) => {
|
|
3367
3416
|
const image = document.querySelector('img');
|
|
3368
|
-
const ready = () => requestAnimationFrame(() =>
|
|
3417
|
+
const ready = () => requestAnimationFrame(() => {
|
|
3418
|
+
const copy = document.querySelector('.promo-copy');
|
|
3419
|
+
const device = document.querySelector('.device');
|
|
3420
|
+
if (copy && device && window.innerHeight > window.innerWidth) {
|
|
3421
|
+
const gap = Math.max(16, Math.min(40, window.innerHeight * 0.02));
|
|
3422
|
+
const deviceTop = copy.getBoundingClientRect().bottom + gap;
|
|
3423
|
+
const deviceBounds = device.getBoundingClientRect();
|
|
3424
|
+
const availableHeight = window.innerHeight * 0.97 - deviceTop;
|
|
3425
|
+
const availableWidth = window.innerWidth * 0.9;
|
|
3426
|
+
const scale = Math.min(1, availableHeight / deviceBounds.height, availableWidth / deviceBounds.width);
|
|
3427
|
+
if (scale > 0) {
|
|
3428
|
+
device.style.top = deviceTop + 'px';
|
|
3429
|
+
device.style.bottom = 'auto';
|
|
3430
|
+
device.style.transformOrigin = 'top center';
|
|
3431
|
+
device.style.transform = 'translateX(-50%) scale(' + scale + ')';
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
requestAnimationFrame(resolve);
|
|
3435
|
+
});
|
|
3369
3436
|
if (image.complete) ready();
|
|
3370
3437
|
else {
|
|
3371
3438
|
image.addEventListener('load', ready, { once: true });
|
|
@@ -3438,6 +3505,21 @@ async function captureProfile(client, snapshot, profile, screenshotFileName) {
|
|
|
3438
3505
|
frameId: frameTree.frameTree.frame.id,
|
|
3439
3506
|
html: snapshot.html,
|
|
3440
3507
|
});
|
|
3508
|
+
if (profile.name === 'iphone-duo') {
|
|
3509
|
+
// The Duo frame supplies its own system bars; do not capture Android insets again.
|
|
3510
|
+
await client.send('Runtime.evaluate', {
|
|
3511
|
+
expression: `(() => {
|
|
3512
|
+
const style = document.createElement('style');
|
|
3513
|
+
style.dataset.duoCaptureInsets = '';
|
|
3514
|
+
style.textContent = 'html, body, .safe-areas, .popup, .sheet-modal, .panel-left, .panel-right {'
|
|
3515
|
+
+ '--android-system-bar-top: 0px !important; --android-system-bar-bottom: 0px !important;'
|
|
3516
|
+
+ '--android-system-bar-left: 0px !important; --android-system-bar-right: 0px !important;'
|
|
3517
|
+
+ '--f7-safe-area-top: 0px !important; --f7-safe-area-bottom: 0px !important;'
|
|
3518
|
+
+ '--f7-safe-area-left: 0px !important; --f7-safe-area-right: 0px !important; }';
|
|
3519
|
+
document.head.append(style);
|
|
3520
|
+
})()`,
|
|
3521
|
+
});
|
|
3522
|
+
}
|
|
3441
3523
|
const replayLayout = await waitForReplayLayout(client, snapshot);
|
|
3442
3524
|
|
|
3443
3525
|
if (replayLayout.documentWidth > replayLayout.viewportWidth + 1) {
|