Oregami
Repositories/oxedyne/daimond

oxedyne/daimond/dev/shot_moderow.mjs

3.9 KiB, 1 run

created by r2519314175:165, 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_moderow.mjs — the Workspace mode row, with the root controls it grew.
2//
3// Three states worth seeing, because each used to be wrong in a different way:
4//
5// machine — the agent working in a real folder. The row now carries "Change folder…", which
6// is the ONE control that opens a picker; the chip itself states the root's scope.
7// scope — that statement, as the active chip says it, with "Forget this folder" beside it.
8// remembered — back on the browser sandbox, with the last folder still named on the Machine
9// chip. This state did not exist: switching to the sandbox deleted the handle, so
10// the chip read a bare "Machine" and going back cost a native dialog.
11//
12// A picked folder is stood in for by an OPFS subdirectory, which is the same handle type and
13// answers queryPermission with 'granted' -- the trick verify_fsa rests on, since Playwright cannot
14// answer a directory picker.
15import { open } from './harness.mjs';
16import path from 'node:path';
17import fs from 'node:fs';
18
19const OUT = path.join(process.cwd(), 'dev', 'shots');
20fs.mkdirSync(OUT, { recursive: true });
21const FOLDER = 'Projects';
22
23const s = await open({ name: 'moderow', connect: false });
24const p = s.page;
25await p.waitForTimeout(1500);
26
27await p.evaluate(() => { try { DaimondPanels.show('work'); } catch (e) {} });
28await p.waitForTimeout(700);
29
30// Seed the stored handle where FsaDB looks, then drive the app's own reconnect: that is
31// activateFolder, so `rootHandle` is set exactly as it is in ordinary use.
32await p.evaluate(async (folder) => {
33 const root = await navigator.storage.getDirectory();
34 const dir = await root.getDirectoryHandle(folder, { create: true });
35 const db = await new Promise((res, rej) => {
36 const q = indexedDB.open('daimond-fsa', 1);
37 q.onupgradeneeded = () => q.result.createObjectStore('handles');
38 q.onsuccess = () => res(q.result);
39 q.onerror = () => rej(q.error);
40 });
41 await new Promise((res, rej) => {
42 const t = db.transaction('handles', 'readwrite');
43 t.objectStore('handles').put(dir, 'workspace');
44 t.oncomplete = res;
45 t.onerror = () => rej(t.error);
46 });
47 db.close();
48}, FOLDER);
49
50await p.reload({ waitUntil: 'domcontentloaded' });
51const { signInAs } = await import('./harness.mjs');
52await signInAs(s, 'moderow');
53await p.waitForTimeout(2500);
54await p.evaluate(() => { try { DaimondPanels.show('work'); } catch (e) {} });
55await p.waitForTimeout(900);
56
57const clip = async () => p.evaluate(() => {
58 const row = document.querySelector('.files-mode');
59 if (!row) return null;
60 const r = row.getBoundingClientRect();
61 return { x: Math.max(0, r.x - 8), y: Math.max(0, r.y - 8), width: r.width + 16, height: r.height + 16 };
62});
63const shoot = async (label) => {
64 const file = path.join(OUT, `moderow-${label}.png`);
65 let err = '';
66 await p.screenshot({ path: file, clip: (await clip()) || undefined, timeout: 8000 })
67 .catch((e) => { err = String(e && e.message ? e.message : e).split('\n')[0]; });
68 console.log(' →', file, err ? 'FAILED: ' + err : '');
69};
70const clickChip = (pat) => p.evaluate((pat) => {
71 const c = [...document.querySelectorAll('.files-mode-chip')]
72 .find(x => new RegExp(pat, 'i').test(x.textContent));
73 if (c) c.click();
74 return !!c;
75}, pat);
76
77const row = () => p.evaluate(() => {
78 const r = document.querySelector('.files-mode');
79 if (!r) return null;
80 return {
81 chips: [...r.querySelectorAll('.files-mode-chip')].map(c => c.textContent.trim()),
82 btns: [...r.querySelectorAll('.files-mode-btn')].map(b => b.textContent.trim()),
83 msg: (r.querySelector('.files-mode-msg') || {}).textContent || '',
84 };
85});
86
87console.log('machine:', JSON.stringify(await row()));
88await shoot('machine');
89
90await clickChip('Machine|' + FOLDER);
91await p.waitForTimeout(500);
92console.log('scope: ', JSON.stringify(await row()));
93await shoot('scope');
94
95await clickChip('Browser');
96await p.waitForTimeout(900);
97console.log('remembered:', JSON.stringify(await row()));
98await shoot('remembered');
99
100await s.close();