oxedyne/daimond/dev/shot_guide.mjs
3.5 KiB, 1 run
created by r2519314175:155, 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_guide.mjs — regenerate the guide screenshots that show the rail. |
| 2 | // |
| 3 | // Only the shots that carry the renamed word need remaking: the two full-window ones, the |
| 4 | // rail crop of a fold in progress, and the Models blurb that names what a new chat starts |
| 5 | // on. The other guide shots are panel crops with no Diamond in them and are left alone. |
| 6 | // |
| 7 | // Needs dev/serve.mjs and dev/mockllm.mjs up. Writes straight into www/guide/shots/. |
| 8 | import { open, signInAs, connectMock, newChat, chat } from './harness.mjs'; |
| 9 | import path from 'node:path'; |
| 10 | |
| 11 | const OUT = path.join(process.cwd(), 'www', 'guide', 'shots'); |
| 12 | const s = await open({ name: 'Alex', connect: false, signIn: false }); |
| 13 | const p = s.page; |
| 14 | await p.waitForTimeout(1200); |
| 15 | await signInAs(s, 'Alex'); |
| 16 | await p.waitForTimeout(1500); |
| 17 | await connectMock(s); |
| 18 | await p.waitForTimeout(1200); |
| 19 | |
| 20 | const clipOf = async (sel) => p.evaluate((sel) => { |
| 21 | const el = document.querySelector(sel); |
| 22 | if (!el) return null; |
| 23 | const r = el.getBoundingClientRect(); |
| 24 | return { x: Math.round(r.x), y: Math.round(r.y), width: Math.round(r.width), height: Math.round(r.height) }; |
| 25 | }, sel); |
| 26 | |
| 27 | const grab = async (name, sel) => { |
| 28 | const clip = sel ? await clipOf(sel) : null; |
| 29 | if (sel && (!clip || !clip.width || !clip.height)) { |
| 30 | console.log(` SKIP ${name} — ${sel} is not on screen`); return; |
| 31 | } |
| 32 | await p.screenshot({ path: path.join(OUT, name), clip: clip || undefined, timeout: 10000 }); |
| 33 | console.log(` wrote ${name}${clip ? ` (${clip.width}x${clip.height})` : ' (full window)'}`); |
| 34 | }; |
| 35 | |
| 36 | // ── getting-started.png — the whole window, guide open at "The interface" ── |
| 37 | await p.evaluate(async () => { |
| 38 | const g = document.querySelector('#panel-web') ? null : null; |
| 39 | if (window.DaimondWeb && DaimondWeb.guide) DaimondWeb.guide('interface.html'); |
| 40 | await new Promise(r => setTimeout(r, 1500)); |
| 41 | }); |
| 42 | await p.waitForTimeout(1500); |
| 43 | await grab('getting-started.png', null); |
| 44 | |
| 45 | // ── models-form.png — the Models view, which names what a new chat starts on ── |
| 46 | await p.evaluate(async () => { |
| 47 | // The status row opens the thing it names; the gear only opens the panel. |
| 48 | const row = document.getElementById('astat-model'); |
| 49 | if (row) row.click(); |
| 50 | await new Promise(r => setTimeout(r, 900)); |
| 51 | }); |
| 52 | await p.waitForTimeout(900); |
| 53 | await grab('models-form.png', '#admin-models'); |
| 54 | |
| 55 | // ── diamond-fold.png — the rail, mid-fold, with the picker open ── |
| 56 | await p.evaluate(async () => { |
| 57 | const close = document.getElementById('admin-close'); |
| 58 | if (close) close.click(); |
| 59 | await new Promise(r => setTimeout(r, 400)); |
| 60 | }); |
| 61 | await newChat(s).catch(() => {}); |
| 62 | await p.waitForTimeout(1200); |
| 63 | // The picker refuses an empty chat, and an empty chat also reads "0 tok" on the tile. |
| 64 | // One real turn through the mock gives the shot something true to show. |
| 65 | await chat(s, 'How do I open a folder on my real disk?').catch(() => {}); |
| 66 | await p.waitForTimeout(1500); |
| 67 | await p.evaluate(async () => { |
| 68 | const fold = document.querySelector('.session-box .tile-fold'); |
| 69 | if (fold) fold.click(); |
| 70 | await new Promise(r => setTimeout(r, 800)); |
| 71 | }); |
| 72 | await p.waitForTimeout(800); |
| 73 | await grab('diamond-fold.png', '#panel-rail'); |
| 74 | |
| 75 | // ── accounts-switch.png — the whole window with the account list showing ── |
| 76 | await p.evaluate(async () => { |
| 77 | const btn = document.getElementById('settings-btn'); |
| 78 | if (btn) btn.click(); // the admin panel home, where accounts live |
| 79 | await new Promise(r => setTimeout(r, 1000)); |
| 80 | }); |
| 81 | await p.waitForTimeout(1000); |
| 82 | await grab('accounts-switch.png', null); |
| 83 | |
| 84 | await s.close(); |
| 85 | console.log('guide shots done'); |