codeplay-common 4.8.2 → 4.8.4

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
@@ -33,6 +33,21 @@ If `onesignal-cordova-plugin` is declared or installed, the pre-build script war
33
33
  runs `npm uninstall onesignal-cordova-plugin`. It stops if removal fails or active
34
34
  package imports remain. Legacy OneSignal patching and helper downloads are disabled.
35
35
 
36
+ ## Screenshot data
37
+
38
+ Shared screenshot helpers are installed into `scripts/screenshot/` and updated with
39
+ the package. App-specific data is not bundled with the package.
40
+ During an upgrade, cleanup removes only the two legacy helpers `frame-samples.js`
41
+ and `background-decorations.js` from `scripts-screenshot/screenshot/`, preserving
42
+ the folder, JSON data, and other app files.
43
+
44
+ `take-screen-image.js` creates `scripts-screenshot/screenshot/` and empty
45
+ `screenshot-copy.json` and `screenshot-info.json` files when needed. Existing files
46
+ are preserved during initialization, package installation, and cleanup. Capture
47
+ updates screenshot information; the AI text-generation workflow fills screenshot
48
+ copy. `--delete` removes matching records, and screenshot runs reconcile records
49
+ against the captured images (except dry runs and `--refresh-info`).
50
+
36
51
  ## Agent instructions
37
52
 
38
53
  Agent instructions are updated during `npm run build`, not during npm installation.
@@ -26,12 +26,12 @@ const path = require('node:path');
26
26
  const readline = require('node:readline/promises');
27
27
  const { spawn, spawnSync } = require('node:child_process');
28
28
  const sharp = require('sharp');
29
- const { frameStyles, createFrameSamples } = require('./scripts-screenshot/screenshot/frame-samples.js');
29
+ const { frameStyles, createFrameSamples } = require('./scripts/screenshot/frame-samples.js');
30
30
  const {
31
31
  getDecorationSeed,
32
32
  createPromotionalDecorations,
33
33
  getPromotionalDecorationStyles,
34
- } = require('./scripts-screenshot/screenshot/background-decorations.js');
34
+ } = require('./scripts/screenshot/background-decorations.js');
35
35
 
36
36
  const screenshotDirectory = path.join(__dirname, 'Auto-Screenshot', 'Store-image-Screenshot');
37
37
  const inFrameDirectory = path.join(__dirname, 'Auto-Screenshot', 'Store-image-Frame');
@@ -727,6 +727,20 @@ function deleteMatchingImages(input) {
727
727
  for (const filePath of deletedFiles) console.log(`Deleted: ${filePath}`);
728
728
  }
729
729
 
730
+ function initializeScreenshotData() {
731
+ for (const [filePath, version] of [
732
+ [screenshotCopyPath, 1],
733
+ [screenshotInfoPath, 2],
734
+ ]) {
735
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
736
+ try {
737
+ fs.writeFileSync(filePath, `${JSON.stringify({ version, screenshots: {} }, null, 2)}\n`, { flag: 'wx' });
738
+ } catch (error) {
739
+ if (error.code !== 'EEXIST') throw error;
740
+ }
741
+ }
742
+ }
743
+
730
744
  function readScreenshotRecordSource(filePath) {
731
745
  if (!fs.existsSync(filePath)) return { screenshots: {} };
732
746
 
@@ -913,21 +927,21 @@ async function getWebViewTarget(localPort) {
913
927
  && target.url !== 'about:blank'
914
928
  ));
915
929
 
916
- const visibleTarget = pageTargets.find((target) => {
930
+ for (const target of pageTargets) {
931
+ const client = new CdpClient(target.webSocketDebuggerUrl);
917
932
  try {
918
- return JSON.parse(target.description || '{}').visible !== false;
933
+ await client.connect();
934
+ const details = await getPageDetails(client);
935
+ if (details.width > 0 && details.height > 0 && details.hasAppPage) {
936
+ return target;
937
+ }
919
938
  } catch {
920
- return true;
939
+ // A target may close while discovering the active app WebView.
940
+ } finally {
941
+ client.close();
921
942
  }
922
- });
923
-
924
- const target = visibleTarget || pageTargets[0];
925
-
926
- if (!target) {
927
- throw new Error('No rendered page was found in the app WebView.');
928
943
  }
929
-
930
- return target;
944
+ throw new Error('No visible Framework7 app page was found. Open the app page and retry; ad WebViews cannot be captured.');
931
945
  }
932
946
 
933
947
  async function getPageDetails(client) {
@@ -938,6 +952,14 @@ async function getPageDetails(client) {
938
952
  width: innerWidth,
939
953
  height: innerHeight,
940
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
+ }),
941
963
  readyState: document.readyState
942
964
  })`,
943
965
  returnByValue: true,
@@ -1027,7 +1049,7 @@ async function capturePageSnapshot(client) {
1027
1049
  ? element.closest('label, .item-content, .input')
1028
1050
  : element;
1029
1051
  const preferredLabel = labelRoot?.querySelector(
1030
- '.item-title, .button-label, strong, .dialog-button-text',
1052
+ '.item-title, .button-label, strong, .dialog-button-text, .homeboxtitle',
1031
1053
  );
1032
1054
  const textRoot = preferredLabel || labelRoot || element;
1033
1055
  const clone = textRoot.cloneNode(true);
@@ -1051,7 +1073,7 @@ async function capturePageSnapshot(client) {
1051
1073
  if (!root) return [];
1052
1074
  const records = [];
1053
1075
  const seen = new Set();
1054
- const selector = 'button, a.button, [role="button"], .list-button, .dialog-button';
1076
+ const selector = 'button, .button, [role="button"], .list-button, .dialog-button';
1055
1077
  for (const element of root.querySelectorAll(selector)) {
1056
1078
  if (!isVisible(element)) continue;
1057
1079
  const label = getElementLabel(element);
@@ -1123,6 +1145,9 @@ async function capturePageSnapshot(client) {
1123
1145
  const activePage = visiblePages.find((page) => page.classList.contains('page-current'))
1124
1146
  || visiblePages[visiblePages.length - 1]
1125
1147
  || null;
1148
+ if (!activePage) {
1149
+ throw new Error('No visible app page was found; screenshot information was not captured.');
1150
+ }
1126
1151
  const overlaySelector = [
1127
1152
  '.dialog.modal-in',
1128
1153
  '.popup.modal-in',
@@ -1147,12 +1172,12 @@ async function capturePageSnapshot(client) {
1147
1172
  ].join(',');
1148
1173
  const pageHeadings = collectUiText(
1149
1174
  activePage,
1150
- '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',
1151
1176
  10,
1152
1177
  );
1153
1178
  const pageControls = collectUiText(
1154
1179
  activePage,
1155
- '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',
1156
1181
  30,
1157
1182
  );
1158
1183
  const overlayText = collectUiText(activeOverlay, textSelector, 35);
@@ -1170,6 +1195,8 @@ async function capturePageSnapshot(client) {
1170
1195
  params: Object.keys(f7Route.params || {}),
1171
1196
  query: Object.keys(f7Route.query || {}),
1172
1197
  } : null;
1198
+ const themeAttribute = document.documentElement.getAttribute('data-theme')
1199
+ || document.body.getAttribute('data-theme');
1173
1200
  const screenInfo = {
1174
1201
  documentTitle: normalizeUiText(document.title),
1175
1202
  route: {
@@ -1181,16 +1208,16 @@ async function capturePageSnapshot(client) {
1181
1208
  dataName: activePage.getAttribute('data-name') || '',
1182
1209
  id: activePage.id || '',
1183
1210
  classes: getStableClasses(activePage),
1184
- navbarTitle: collectUiText(activePage, '.navbar .title', 1)[0] || '',
1211
+ navbarTitle: collectUiText(activePage, '.navbar .title, .navbar .home-brand-text', 1)[0] || '',
1185
1212
  headings: pageHeadings,
1186
1213
  sections: collectUiText(
1187
1214
  activePage,
1188
- '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',
1189
1216
  20,
1190
1217
  ),
1191
1218
  descriptions: collectUiText(
1192
1219
  activePage,
1193
- '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',
1194
1221
  30,
1195
1222
  ),
1196
1223
  controls: pageControls,
@@ -1218,10 +1245,12 @@ async function capturePageSnapshot(client) {
1218
1245
  )),
1219
1246
  inputs: overlayInputs,
1220
1247
  } : null,
1221
- theme: document.documentElement.classList.contains('theme-dark')
1222
- || document.body.classList.contains('theme-dark')
1223
- ? 'dark'
1224
- : 'light',
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',
1225
1254
  viewport: {
1226
1255
  width: innerWidth,
1227
1256
  height: innerHeight,
@@ -3351,7 +3380,25 @@ async function captureFramedScreenshot(
3351
3380
  await client.send('Runtime.evaluate', {
3352
3381
  expression: `new Promise((resolve) => {
3353
3382
  const image = document.querySelector('img');
3354
- const ready = () => requestAnimationFrame(() => requestAnimationFrame(resolve));
3383
+ const ready = () => requestAnimationFrame(() => {
3384
+ const copy = document.querySelector('.promo-copy');
3385
+ const device = document.querySelector('.device');
3386
+ if (copy && device && window.innerHeight > window.innerWidth) {
3387
+ const gap = Math.max(16, Math.min(40, window.innerHeight * 0.02));
3388
+ const deviceTop = copy.getBoundingClientRect().bottom + gap;
3389
+ const deviceBounds = device.getBoundingClientRect();
3390
+ const availableHeight = window.innerHeight * 0.97 - deviceTop;
3391
+ const availableWidth = window.innerWidth * 0.9;
3392
+ const scale = Math.min(1, availableHeight / deviceBounds.height, availableWidth / deviceBounds.width);
3393
+ if (scale > 0) {
3394
+ device.style.top = deviceTop + 'px';
3395
+ device.style.bottom = 'auto';
3396
+ device.style.transformOrigin = 'top center';
3397
+ device.style.transform = 'translateX(-50%) scale(' + scale + ')';
3398
+ }
3399
+ }
3400
+ requestAnimationFrame(resolve);
3401
+ });
3355
3402
  if (image.complete) ready();
3356
3403
  else {
3357
3404
  image.addEventListener('load', ready, { once: true });
@@ -3793,6 +3840,10 @@ async function main() {
3793
3840
 
3794
3841
  options = await getInteractiveOptions(options);
3795
3842
 
3843
+ if (!options.dryRun) {
3844
+ initializeScreenshotData();
3845
+ }
3846
+
3796
3847
  if (!options.dryRun && !options.refreshInfo) {
3797
3848
  reconcileScreenshotRecords();
3798
3849
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeplay-common",
3
- "version": "4.8.2",
3
+ "version": "4.8.4",
4
4
  "description": "Common build scripts and files",
5
5
  "scripts": {
6
6
  "postinstall": "node scripts/sync-files.js",
@@ -25,6 +25,8 @@ function copyFolderSync(source, destination) {
25
25
 
26
26
  // Copy all files from `common-build-files/files/` to the project root
27
27
  fs.readdirSync(commonBuildPath).forEach(file => {
28
+ // Never distribute or overwrite app-owned screenshot data.
29
+ if (file === "scripts-screenshot") return;
28
30
  const sourcePath = path.join(commonBuildPath, file);
29
31
  const destPath = path.join(projectRoot, file);
30
32
 
@@ -70,7 +70,8 @@ filesInProject.forEach(file => {
70
70
  if (fs.existsSync(commonBuildPath)) {
71
71
  fs.readdirSync(commonBuildPath).forEach(file => {
72
72
  const destPath = path.join(projectRoot, file);
73
- if (file === "scripts" || isAgentInstructionAsset(file)) {
73
+ // Screenshot data belongs to the app, including folders from older releases.
74
+ if (file === "scripts" || file === "scripts-screenshot" || isAgentInstructionAsset(file)) {
74
75
  return;
75
76
  }
76
77
  if (fs.existsSync(destPath)) {
@@ -88,6 +89,19 @@ if (fs.existsSync(commonBuildPath)) {
88
89
  });
89
90
  }
90
91
 
92
+ for (const helperDirectory of [
93
+ path.join(projectRoot, "scripts", "screenshot"),
94
+ path.join(projectRoot, "scripts-screenshot", "screenshot")
95
+ ]) {
96
+ for (const helperName of ["frame-samples.js", "background-decorations.js"]) {
97
+ const helperPath = path.join(helperDirectory, helperName);
98
+ if (fs.existsSync(helperPath)) {
99
+ fs.unlinkSync(helperPath);
100
+ process.stdout.write(`🗑️ Removed file: ${helperPath}\n`);
101
+ }
102
+ }
103
+ }
104
+
91
105
  if (fs.existsSync(baselineProfileSetupScript)) {
92
106
  try {
93
107
  fs.unlinkSync(baselineProfileSetupScript);