Oregami
Repositories/oxedyne/daimond

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/.
8import { open, signInAs, connectMock, newChat, chat } from './harness.mjs';
9import path from 'node:path';
10
11const OUT = path.join(process.cwd(), 'www', 'guide', 'shots');
12const s = await open({ name: 'Alex', connect: false, signIn: false });
13const p = s.page;
14await p.waitForTimeout(1200);
15await signInAs(s, 'Alex');
16await p.waitForTimeout(1500);
17await connectMock(s);
18await p.waitForTimeout(1200);
19
20const 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
27const 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" ──
37await 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});
42await p.waitForTimeout(1500);
43await grab('getting-started.png', null);
44
45// ── models-form.png — the Models view, which names what a new chat starts on ──
46await 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});
52await p.waitForTimeout(900);
53await grab('models-form.png', '#admin-models');
54
55// ── diamond-fold.png — the rail, mid-fold, with the picker open ──
56await p.evaluate(async () => {
57 const close = document.getElementById('admin-close');
58 if (close) close.click();
59 await new Promise(r => setTimeout(r, 400));
60});
61await newChat(s).catch(() => {});
62await 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.
65await chat(s, 'How do I open a folder on my real disk?').catch(() => {});
66await p.waitForTimeout(1500);
67await 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});
72await p.waitForTimeout(800);
73await grab('diamond-fold.png', '#panel-rail');
74
75// ── accounts-switch.png — the whole window with the account list showing ──
76await 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});
81await p.waitForTimeout(1000);
82await grab('accounts-switch.png', null);
83
84await s.close();
85console.log('guide shots done');