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. |
| 8 | import fs from 'node:fs'; |
| 9 | import path from 'node:path'; |
| 10 | import { fileURLToPath } from 'node:url'; |
| 11 | import { openCell, defaultCells, VIEWS, cellLabel, enginesAvailable, resetView } from './matrix.mjs'; |
| 12 | |
| 13 | const HERE = path.dirname(fileURLToPath(import.meta.url)); |
| 14 | const OUT = path.join(HERE, 'shots', 'matrix'); |
| 15 | fs.mkdirSync(OUT, { recursive: true }); |
| 16 | // Start clean so a stale image never masquerades as this run's. |
| 17 | for (const f of fs.readdirSync(OUT)) { if (f.endsWith('.png')) fs.rmSync(path.join(OUT, f)); } |
| 18 | |
| 19 | const avail = await enginesAvailable(); |
| 20 | console.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. |
| 24 | if (!process.env.DAIMOND_DEVICES) process.env.DAIMOND_DEVICES = 'desktop,iphone,pixel'; |
| 25 | if (!process.env.DAIMOND_THEMES) process.env.DAIMOND_THEMES = 'dark,light'; |
| 26 | |
| 27 | const cells = defaultCells().filter(c => avail[c.engine]); |
| 28 | const index = []; |
| 29 | |
| 30 | for (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 | |
| 55 | fs.writeFileSync(path.join(OUT, 'INDEX.txt'), index.sort().join('\n') + '\n'); |
| 56 | console.log(`\n${index.length} frames in ${OUT}`); |
| 57 | console.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.'); |