Bound document cache size to prevent long-session UI slowdown

This commit is contained in:
Jacob Schmidt 2026-02-28 16:26:10 -06:00
parent cde37b814d
commit c9c61a279e

View File

@ -58,6 +58,7 @@
busy: boolean; busy: boolean;
error: string; error: string;
}; };
const MAX_CACHED_DOCUMENTS = 5;
const initialEntry = getDefaultEntry(get(entriesStore)); const initialEntry = getDefaultEntry(get(entriesStore));
@ -73,6 +74,7 @@
"# Daily Notes\n\nStart writing today's entry...", "# Daily Notes\n\nStart writing today's entry...",
}; };
let savedDocumentContent: Record<string, string> = { ...openDocuments }; let savedDocumentContent: Record<string, string> = { ...openDocuments };
let documentAccessOrder: string[] = Object.keys(openDocuments);
let modalOpen = false; let modalOpen = false;
let modalTitle = ""; let modalTitle = "";
let modalMessage = ""; let modalMessage = "";
@ -165,6 +167,41 @@
return fileName.replace(/\.template\.md$/i, ""); return fileName.replace(/\.template\.md$/i, "");
} }
function touchDocument(id: string) {
documentAccessOrder = [
...documentAccessOrder.filter((item) => item !== id),
id,
];
}
function pruneDocumentCache(preserveIds: string[] = []) {
const preserve = new Set(
preserveIds.filter(Boolean).concat(activeDocumentId ? [activeDocumentId] : []),
);
const cachedIds = Object.keys(openDocuments);
if (cachedIds.length <= MAX_CACHED_DOCUMENTS) return;
let nextOpen = { ...openDocuments };
let nextSaved = { ...savedDocumentContent };
let nextOrder = [...documentAccessOrder];
let currentSize = cachedIds.length;
for (const candidate of documentAccessOrder) {
if (currentSize <= MAX_CACHED_DOCUMENTS) break;
if (preserve.has(candidate)) continue;
if (!(candidate in nextOpen)) continue;
delete nextOpen[candidate];
delete nextSaved[candidate];
nextOrder = nextOrder.filter((item) => item !== candidate);
currentSize -= 1;
}
openDocuments = nextOpen;
savedDocumentContent = nextSaved;
documentAccessOrder = nextOrder;
}
function showModal(options: { function showModal(options: {
action: action:
| "logout-confirm" | "logout-confirm"
@ -361,6 +398,9 @@
openDocuments = rest; openDocuments = rest;
const { [activeDocumentId]: __, ...savedRest } = savedDocumentContent; const { [activeDocumentId]: __, ...savedRest } = savedDocumentContent;
savedDocumentContent = savedRest; savedDocumentContent = savedRest;
documentAccessOrder = documentAccessOrder.filter(
(item) => item !== activeDocumentId,
);
activeDocumentId = ""; activeDocumentId = "";
activeDocumentLabel = ""; activeDocumentLabel = "";
editMode = false; editMode = false;
@ -398,6 +438,7 @@
...savedRest, ...savedRest,
[saved.id]: saved.initialContent, [saved.id]: saved.initialContent,
}; };
touchDocument(saved.id);
activeDocumentId = saved.id; activeDocumentId = saved.id;
activeDocumentLabel = saved.label; activeDocumentLabel = saved.label;
} else { } else {
@ -405,6 +446,7 @@
...savedDocumentContent, ...savedDocumentContent,
[activeDocumentId]: content, [activeDocumentId]: content,
}; };
touchDocument(activeDocumentId);
} }
} else if ( } else if (
selectedSection === "lists" && selectedSection === "lists" &&
@ -416,7 +458,9 @@
...savedDocumentContent, ...savedDocumentContent,
[activeDocumentId]: content, [activeDocumentId]: content,
}; };
touchDocument(activeDocumentId);
} }
pruneDocumentCache([activeDocumentId]);
} catch { } catch {
// best-effort save // best-effort save
} }
@ -518,11 +562,13 @@
[resolvedDoc.id]: resolvedDoc.initialContent, [resolvedDoc.id]: resolvedDoc.initialContent,
}; };
} }
touchDocument(resolvedDoc.id);
savedDocumentContent = { savedDocumentContent = {
...savedDocumentContent, ...savedDocumentContent,
[resolvedDoc.id]: [resolvedDoc.id]:
openDocuments[resolvedDoc.id] ?? resolvedDoc.initialContent, openDocuments[resolvedDoc.id] ?? resolvedDoc.initialContent,
}; };
pruneDocumentCache([resolvedDoc.id]);
activeDocumentId = resolvedDoc.id; activeDocumentId = resolvedDoc.id;
activeDocumentLabel = resolvedDoc.label; activeDocumentLabel = resolvedDoc.label;
} }
@ -540,7 +586,10 @@
} }
function handleDocumentContentChange(content: string) { function handleDocumentContentChange(content: string) {
if (!activeDocumentId) return;
openDocuments = { ...openDocuments, [activeDocumentId]: content }; openDocuments = { ...openDocuments, [activeDocumentId]: content };
touchDocument(activeDocumentId);
pruneDocumentCache([activeDocumentId]);
} }
async function handleForceSave() { async function handleForceSave() {
@ -552,6 +601,7 @@
openDocuments = remaining; openDocuments = remaining;
const { [id]: __, ...savedRemaining } = savedDocumentContent; const { [id]: __, ...savedRemaining } = savedDocumentContent;
savedDocumentContent = savedRemaining; savedDocumentContent = savedRemaining;
documentAccessOrder = documentAccessOrder.filter((item) => item !== id);
} }
async function performDelete(id: string) { async function performDelete(id: string) {