perf: faster startup and long pages

- open the window while the notes folder loads; rebuild the search index after the page list is sent
- load mermaid and katex on demand
- vault.list reads files in parallel batches
- resolveLink uses a per-page-list lookup table
- very long pages (>300 blocks) skip layout of off-screen blocks
- perf-large: add a long-page case, fix the tasks poll

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Your NameandClaude Sonnet 5.5 committed 2026-10-15 12:25:45 +02:00
1 parent fcad23afa9
commit d5d25eaf46
10 files changed
+147 -64

No files matched your search

+10 -1
View File
@@ -18,6 +18,8 @@ for (let i = 0; i < N; i++) {
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`);
// one very long page: headings, paragraphs, bullets, a table
fs.writeFileSync(path.join(vault, 'Long.md'), Array.from({ length: 1500 }, (_, i) => `## Section ${i}\n\n${sentence()} **bold** and a [[Note ${i}]] link. ${sentence()}\n\n- ${sentence()}\n- ${sentence()}\n`).join('\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();
@@ -49,6 +51,13 @@ app.whenReady().then(async () => {
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);
// a very long page
const L = Date.now();
await js(`document.querySelector('.tree .row[data-id="Long"]')?.click()`);
for (let i = 0; i < 300; i++) { if ((await js(`return document.querySelectorAll('.block').length`)) > 4000) break; await sleep(20); }
report('open a 4500-block page', Date.now() - L);
report('worst frame gap after that (1s)', await jank(1000));
report('scroll that page 20 steps', await js(`const sc = [...document.querySelectorAll('*')].find((e) => e.scrollHeight > e.clientHeight + 5000); if (!sc) return -1; const t = performance.now(); for (let i = 0; i < 20; i++) { sc.scrollTop += 1500; await new Promise((r) => requestAnimationFrame(r)); } return performance.now() - t`));
// search
const s = Date.now();
await js(`document.querySelector('.sidebar button[aria-label="Search"]').click()`);
@@ -69,7 +78,7 @@ app.whenReady().then(async () => {
// 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); }
for (let i = 0; i < 100; i++) { if (await js(`const p = document.querySelector('.tasks-panel'); return !!p && !/Loading/.test(p.textContent)`)) break; await sleep(20); }
report('tasks dialog', Date.now() - k);
} catch (err) {
console.log('harness', err.stack);