chore: project scaffold, build config, scripts and README
Bun + SvelteKit (static adapter) + Electron project files, icon resources, dev/e2e/bench scripts and the README documenting the app. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
437887207c
commit
0b5f8bfa8b
27 files changed
+2050
No files matched your search
@@ -0,0 +1,79 @@
|
||||
// How the app feels on a big vault. Not part of `bun run e2e` (it is slow): electron scripts/perf-large.cjs [notes=10000]
|
||||
const { app, BrowserWindow } = require('electron');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
const fs = require('fs');
|
||||
|
||||
const N = Number(process.argv.find((a) => /^\d+$/.test(a))) || 10000;
|
||||
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'ink-perf-'));
|
||||
const vault = path.join(tmp, 'Vault');
|
||||
const words = 'alpha beta gamma delta project roadmap meeting notes idea review plan budget design draft summary weekly sync'.split(' ');
|
||||
const rnd = (n) => Math.floor(Math.random() * n);
|
||||
const sentence = () => Array.from({ length: 8 + rnd(10) }, () => words[rnd(words.length)]).join(' ');
|
||||
for (let i = 0; i < N; i++) {
|
||||
const folder = path.join(vault, `Area ${i % 40}`);
|
||||
fs.mkdirSync(folder, { recursive: true });
|
||||
let body = `# Note ${i}\n\n`;
|
||||
for (let p = 0; p < 6; p++) body += sentence() + (rnd(3) === 0 ? ` [[Note ${rnd(N)}]]` : '') + '.\n\n';
|
||||
fs.writeFileSync(path.join(folder, `Note ${i}.md`), `---\ntags: [t${i % 50}]\n---\n\n${body}`);
|
||||
}
|
||||
for (let a = 0; a < 40; a++) fs.writeFileSync(path.join(vault, `Area ${a}.md`), `Area ${a}\n`);
|
||||
app.setPath('userData', tmp);
|
||||
fs.writeFileSync(path.join(tmp, 'settings.json'), JSON.stringify({ vault, lastPage: 'Area 1', theme: 'dark', bounds: { width: 1300, height: 850 } }));
|
||||
const started = Date.now();
|
||||
require('../electron/main.cjs');
|
||||
|
||||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
app.whenReady().then(async () => {
|
||||
let win;
|
||||
for (let i = 0; i < 200 && !(win = BrowserWindow.getAllWindows()[0]); i++) await sleep(50);
|
||||
const wc = win.webContents;
|
||||
if (wc.isLoading()) await new Promise((r) => wc.once('did-finish-load', r));
|
||||
const js = (code) => wc.executeJavaScript(`(async () => { ${code} })()`);
|
||||
const report = (label, ms, extra = '') => console.log(`${label.padEnd(40)} ${String(Math.round(ms)).padStart(6)} ms ${extra}`);
|
||||
// frames the page could not draw: longest gap between animation frames over a period
|
||||
const jank = (ms) => js(`let last = performance.now(), worst = 0; const end = last + ${ms}; await new Promise((res) => { const f = (t) => { worst = Math.max(worst, t - last); last = t; t < end ? requestAnimationFrame(f) : res(); }; requestAnimationFrame(f); }); return worst`);
|
||||
const timeIn = (code) => js(`const t = performance.now(); ${code}; return performance.now() - t`);
|
||||
try {
|
||||
for (let i = 0; i < 300; i++) { if (await js(`return !!document.querySelector('.tree .row')`)) break; await sleep(100); }
|
||||
report('launch to sidebar shown', Date.now() - started);
|
||||
report('sidebar rows (folded)', 0, String(await js(`return document.querySelectorAll('.tree .row').length`)));
|
||||
// open every top-level folder
|
||||
const t0 = Date.now();
|
||||
await js(`for (const r of document.querySelectorAll('.tree > .node > .row')) { r.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true })); }`);
|
||||
await sleep(200);
|
||||
report('open all 40 folders', Date.now() - t0, 'rows now: ' + (await js(`return document.querySelectorAll('.tree .row').length`)));
|
||||
report('worst frame gap after that (1s)', await jank(1000));
|
||||
// open a page that has many backlinks / mentions
|
||||
const o = Date.now();
|
||||
await js(`document.querySelector('.tree .row[data-id="Area 2/Note 2"]')?.click()`);
|
||||
for (let i = 0; i < 100; i++) { if ((await js(`return document.querySelector('.title')?.textContent`)) === 'Note 2') break; await sleep(20); }
|
||||
report('open a page', Date.now() - o);
|
||||
// search
|
||||
const s = Date.now();
|
||||
await js(`document.querySelector('.sidebar button[aria-label="Search"]').click()`);
|
||||
await sleep(100);
|
||||
await js(`const i = document.querySelector('.pal-top input'); i.value = 'roadmap budget'; i.dispatchEvent(new Event('input', { bubbles: true }))`);
|
||||
for (let i = 0; i < 100; i++) { if (await js(`return document.querySelectorAll('.pal-list .pi').length > 3`)) break; await sleep(20); }
|
||||
report('search shows results', Date.now() - s);
|
||||
await js(`document.querySelector('.overlay')?.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }))`);
|
||||
await sleep(400);
|
||||
// graph
|
||||
const g = Date.now();
|
||||
await js(`document.querySelector('.sidebar button[aria-label="Graph view"]').click()`);
|
||||
for (let i = 0; i < 200; i++) { if (await js(`return !!document.querySelector('canvas, .graph svg, .gv')`)) break; await sleep(25); }
|
||||
report('graph view appears', Date.now() - g);
|
||||
report('worst frame gap while it settles (3s)', await jank(3000));
|
||||
await js(`document.querySelector('.sidebar button[aria-label="Graph view"]').click()`);
|
||||
await sleep(500);
|
||||
// tasks list
|
||||
const k = Date.now();
|
||||
await js(`document.querySelector('.sidebar button[aria-label="Tasks"]').click()`);
|
||||
for (let i = 0; i < 100; i++) { if (await js(`return !document.querySelector('.tasks-panel .trash-empty') || false`)) break; await sleep(20); }
|
||||
report('tasks dialog', Date.now() - k);
|
||||
} catch (err) {
|
||||
console.log('harness', err.stack);
|
||||
}
|
||||
fs.rmSync(tmp, { recursive: true, force: true });
|
||||
app.exit(0);
|
||||
});
|
||||
Reference in new issue
Block a user