oxedyne/daimond/dev/shot_border.mjs
3.3 KiB, 1 run
created by r2519314175:147, 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 | // shot_border.mjs — what --border-strong actually looks like. |
| 2 | // |
| 3 | // The numbers say every control now clears 3:1 against the surface it sits on, and |
| 4 | // that a focused field reads 4.19-6.75 against its own unfocused pixels. Neither |
| 5 | // says whether the app still looks like one system: a boundary lifted to a floor is |
| 6 | // a boundary that got darker on a light palette and lighter on a dark one, and |
| 7 | // eleven of those could easily read as eleven different apps. |
| 8 | // |
| 9 | // Four palettes, one from each corner of the tone/ink grid, and for each: the chat |
| 10 | // composer (the field a person looks at most), the settings dialog (the densest run |
| 11 | // of fields in the app), and a field with the focus on it, shot at 3x so the 1px |
| 12 | // boundary and the 2px ring are both legible. |
| 13 | import { open } from './harness.mjs'; |
| 14 | import path from 'node:path'; |
| 15 | import fs from 'node:fs'; |
| 16 | |
| 17 | const OUT = path.join(process.cwd(), 'dev', 'shots'); |
| 18 | fs.mkdirSync(OUT, { recursive: true }); |
| 19 | |
| 20 | // light band / dark ink, mid band / light ink, dark band / light ink, and the |
| 21 | // candy one, which is the palette a boundary is easiest to lose in. |
| 22 | const PALETTES = ['light', 'dusk', 'dark', 'lollypop']; |
| 23 | |
| 24 | const s = await open({ name: 'border', connect: false }); |
| 25 | const p = s.page; |
| 26 | await p.setViewportSize({ width: 1400, height: 900 }); |
| 27 | await p.waitForTimeout(1500); |
| 28 | |
| 29 | for (const pal of PALETTES) { |
| 30 | await p.evaluate((name) => window.DaimondTheme.set(name), pal); |
| 31 | await p.waitForTimeout(400); |
| 32 | |
| 33 | // The settings dialog: the densest run of fields in the app, and the one place |
| 34 | // where a text input, a select and an outlined button sit within 20px of each |
| 35 | // other, so an inconsistency between them cannot hide. |
| 36 | // The Admin drawer, which is where this app keeps the densest run of controls |
| 37 | // that are not behind an account: rows, an outlined button, the model chip. |
| 38 | await p.evaluate(() => { |
| 39 | const u = document.querySelector('#user-row'); |
| 40 | if (u) u.click(); |
| 41 | }); |
| 42 | await p.waitForTimeout(600); |
| 43 | await p.screenshot({ path: path.join(OUT, `border_${pal}_app.png`) }); |
| 44 | await p.keyboard.press('Escape'); |
| 45 | await p.waitForTimeout(400); |
| 46 | |
| 47 | // A field with the focus on it. `:focus-visible` only arms under a real Tab -- |
| 48 | // Chromium reports `outline: none` for a scripted focus() -- so the focus is |
| 49 | // moved with the keyboard from wherever the page starts. |
| 50 | await p.keyboard.press('Tab'); |
| 51 | await p.waitForTimeout(300); |
| 52 | const focused = await p.evaluate(() => { |
| 53 | const el = document.activeElement; |
| 54 | if (!el || el === document.body) return null; |
| 55 | const r = el.getBoundingClientRect(); |
| 56 | const cs = getComputedStyle(el); |
| 57 | return { |
| 58 | sel: el.tagName.toLowerCase() + (el.id ? '#' + el.id : '') |
| 59 | + (el.className && typeof el.className === 'string' ? '.' + el.className.trim().split(/\s+/)[0] : ''), |
| 60 | outline: cs.outlineWidth + ' ' + cs.outlineStyle + ' ' + cs.outlineColor, |
| 61 | border: cs.borderTopColor, |
| 62 | box: { x: r.x, y: r.y, w: r.width, h: r.height }, |
| 63 | }; |
| 64 | }); |
| 65 | if (focused && focused.box.w) { |
| 66 | console.log(`${pal.padEnd(9)} focus on ${focused.sel} — outline ${focused.outline}, border ${focused.border}`); |
| 67 | await p.screenshot({ |
| 68 | path: path.join(OUT, `border_${pal}_focus.png`), |
| 69 | clip: { |
| 70 | x: Math.max(0, focused.box.x - 12), y: Math.max(0, focused.box.y - 12), |
| 71 | width: focused.box.w + 24, height: focused.box.h + 24, |
| 72 | }, |
| 73 | scale: 'device', |
| 74 | }); |
| 75 | } else { |
| 76 | console.log(`${pal.padEnd(9)} focus: nothing took it`); |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | await s.close(); |