Oregami
Repositories/oxedyne/daimond

oxedyne/daimond/dev/shots_matrix.mjs

2.8 KiB, 1 run

created by r2519314175:209, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1// shots_matrix.mjs — capture every view across the cell matrix, for the vision
2// review that no assertion can do: spacing rhythm, alignment, truncation, theme
3// coherence, "does this read as one system". Writes PNGs to dev/shots/matrix/,
4// named <view>.<device>.<engine>.<theme>.png, and prints an index to read back.
5//
6// Pair with verify_style.mjs: that catches the mechanical faults (overflow,
7// contrast, tap size); these images catch the ones only an eye sees.
8import fs from 'node:fs';
9import path from 'node:path';
10import { fileURLToPath } from 'node:url';
11import { openCell, defaultCells, VIEWS, cellLabel, enginesAvailable, resetView } from './matrix.mjs';
12
13const HERE = path.dirname(fileURLToPath(import.meta.url));
14const OUT = path.join(HERE, 'shots', 'matrix');
15fs.mkdirSync(OUT, { recursive: true });
16// Start clean so a stale image never masquerades as this run's.
17for (const f of fs.readdirSync(OUT)) { if (f.endsWith('.png')) fs.rmSync(path.join(OUT, f)); }
18
19const avail = await enginesAvailable();
20console.log('engines: ' + Object.entries(avail).map(([k, v]) => `${k}=${v ? 'yes' : 'no'}`).join(' '));
21
22// A tighter default than the audit: enough to see coherence across form factors
23// and themes without hundreds of frames. Widen with DAIMOND_DEVICES / DAIMOND_THEMES.
24if (!process.env.DAIMOND_DEVICES) process.env.DAIMOND_DEVICES = 'desktop,iphone,pixel';
25if (!process.env.DAIMOND_THEMES) process.env.DAIMOND_THEMES = 'dark,light';
26
27const cells = defaultCells().filter(c => avail[c.engine]);
28const index = [];
29
30for (const cell of cells) {
31 const label = cellLabel(cell);
32 // The locked screen needs no auth; everything else does. Do one signed-out
33 // pass for `locked`, then a signed-in cell for the rest.
34 for (const phase of ['locked', 'auth']) {
35 const views = VIEWS.filter(v => (phase === 'locked' ? v.name === 'locked' : v.needsAuth));
36 if (!views.length) continue;
37 let s;
38 try { s = await openCell(cell, { signIn: phase === 'auth', connect: phase === 'auth' }); }
39 catch (e) { console.log(` skip ${label} (${phase}) — ${e.message}`); continue; }
40 try {
41 for (const view of views) {
42 await resetView(s.page);
43 await view.setup(s.page).catch(() => {});
44 const file = `${view.name}.${label}.png`;
45 await s.page.screenshot({ path: path.join(OUT, file), fullPage: false, timeout: 8000 }).catch(() => {});
46 index.push(file);
47 console.log(' shot ' + file);
48 }
49 } finally {
50 await s.close();
51 }
52 }
53}
54
55fs.writeFileSync(path.join(OUT, 'INDEX.txt'), index.sort().join('\n') + '\n');
56console.log(`\n${index.length} frames in ${OUT}`);
57console.log('Review them for: alignment & spacing rhythm, truncation/clipping, theme coherence (colours from the palette, both themes legible), trust-band/chip consistency, and whether every surface reads as one system.');