oxedyne/daimond/dev/shot_frame.mjs
4.0 KiB, 1 run
created by r2519314175:151, 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_frame.mjs — the frame, for the eye rather than the assertion: the top |
| 2 | // bar, the phone's footer strip at each end of its scroll, the sync row in the |
| 3 | // rail, and the chat header's one labelled chip. |
| 4 | // |
| 5 | // `dev/verify_frame.mjs` asserts that nothing in the bar moves. This says what |
| 6 | // the thing that does not move looks like, which is the half a number cannot |
| 7 | // carry — a scrolling strip with no sign that it scrolls reads as a truncated |
| 8 | // list, and only a picture says whether the fade at its end reads as one. |
| 9 | import { open } from './harness.mjs'; |
| 10 | import { writeFileSync, mkdirSync } from 'node:fs'; |
| 11 | const sleep = ms => new Promise(r => setTimeout(r, ms)); |
| 12 | |
| 13 | mkdirSync('shots', { recursive: true }); |
| 14 | const s = await open({ signIn: true, connect: true, name: 'frameshots' }); |
| 15 | const { page } = s; |
| 16 | await page.route('**/*.{woff,woff2,ttf,otf}', r => r.abort()); |
| 17 | await page.addStyleTag({ content: '*,*::before,*::after{transition:none!important;animation:none!important}' }); |
| 18 | await page.evaluate(() => { try { window.DaimondAdmin.closeModal(); } catch (e) {} const m = document.getElementById('settings-modal'); if (m) m.style.display = 'none'; }); |
| 19 | await sleep(400); |
| 20 | |
| 21 | // Playwright's screenshot waits on document.fonts.ready, which never settles |
| 22 | // with the webfonts aborted; CDP's captureScreenshot does not. |
| 23 | const cdp = await s.browser.newCDPSession(page); |
| 24 | const shot = async (name, sel, scale = 2) => { |
| 25 | try { |
| 26 | const clip = sel ? await page.evaluate((q) => { |
| 27 | const e = document.querySelector(q); if (!e) return null; |
| 28 | const r = e.getBoundingClientRect(); |
| 29 | return { x: Math.max(0, Math.round(r.x) - 4), y: Math.max(0, Math.round(r.y) - 4), |
| 30 | width: Math.round(r.width) + 8, height: Math.round(r.height) + 8 }; |
| 31 | }, sel) : null; |
| 32 | if (sel && !clip) { console.log('SKIP ' + name + ' — no ' + sel); return; } |
| 33 | const { data } = await cdp.send('Page.captureScreenshot', clip ? { format: 'png', clip: { ...clip, scale } } : { format: 'png' }); |
| 34 | writeFileSync('shots/' + name + '.png', Buffer.from(data, 'base64')); |
| 35 | console.log('shot shots/' + name + '.png'); |
| 36 | } catch (e) { console.log('SKIP ' + name + ' — ' + e.message.split('\n')[0]); } |
| 37 | }; |
| 38 | |
| 39 | // ── The desktop bar, at rest and with a round in flight ──────────── |
| 40 | await page.setViewportSize({ width: 1440, height: 900 }); |
| 41 | await sleep(600); |
| 42 | await shot('frame-topbar', '.topbar'); |
| 43 | await page.evaluate(() => { |
| 44 | window.DaimondGateway.state = () => ({ authed: true }); |
| 45 | window.DaimondGateway.clientApi = () => 1; |
| 46 | window.DaimondGateway.gwFetch = () => new Promise(r => setTimeout( |
| 47 | () => r({ status: 200, json: async () => ({ present: false }) }), 6000)); |
| 48 | }); |
| 49 | page.evaluate(() => window.DaimondSync.pull()).catch(() => {}); |
| 50 | await sleep(700); |
| 51 | await shot('frame-topbar-syncing', '.topbar'); |
| 52 | await shot('frame-rail-syncing', '#admin-status', 3); |
| 53 | // A count on a chip, which used to widen the chip it was on. |
| 54 | await page.evaluate(() => window.dispatchEvent(new CustomEvent('daimond:mail-arrived', { detail: { count: 3 } }))); |
| 55 | await sleep(400); |
| 56 | await shot('frame-topbar-badge', '.topbar'); |
| 57 | await shot('frame-chathead', '.panel.ai .chead', 3); |
| 58 | |
| 59 | // ── The phone: the footer strip ──────────────────────────────────── |
| 60 | await page.setViewportSize({ width: 390, height: 844 }); |
| 61 | await sleep(800); |
| 62 | await shot('frame-phone-screen'); |
| 63 | const scrollTo = async (px) => { |
| 64 | await page.evaluate((x) => { const r = document.getElementById('panel-tags'); if (r) { r.scrollLeft = x; r.dispatchEvent(new Event('scroll')); } }, px); |
| 65 | await sleep(250); |
| 66 | }; |
| 67 | await scrollTo(0); |
| 68 | await shot('frame-phone-strip-start', '#mnav', 3); |
| 69 | await scrollTo(300); |
| 70 | await shot('frame-phone-strip-middle', '#mnav', 3); |
| 71 | await scrollTo(9999); |
| 72 | await shot('frame-phone-strip-end', '#mnav', 3); |
| 73 | await shot('frame-phone-topbar', '.topbar', 3); |
| 74 | await page.evaluate(() => { const b = document.getElementById('drawer-btn'); if (b) b.click(); }); |
| 75 | await sleep(500); |
| 76 | await shot('frame-phone-rail', '#admin-status', 3); |
| 77 | |
| 78 | await s.close(); |
| 79 | console.log('done'); |