From 91bd678535af91a870785aed77794af61ddbc5a5 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Nov 2025 05:18:07 +0000 Subject: [PATCH] perf(obsidian): optimize folder tree for large vaults The previous implementation read ALL file contents to get frontmatter when browsing notes, which caused massive slowdown for large vaults (13k+ files = 13k+ HTTP requests). Changes: - Add listAllNotePaths() function that only gets file paths (fast) - Build folder tree from paths only, no content reading - Lazy load file content only when user clicks on a file - Change message from "Loading all notes" to "Loading file list" - Add logging to show file count being displayed Performance improvement: - Before: O(n) HTTP requests where n = number of files - After: O(1) - just the recursive directory scan - File content loaded on-demand (1 request per clicked file) This makes the folder tree instant even for vaults with 10k+ files. --- modules/io/obsidian-bridge.js | 24 ++++++++++++++++++++++-- modules/ui/obsidian-notes-editor.js | 12 +++++++++--- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/modules/io/obsidian-bridge.js b/modules/io/obsidian-bridge.js index d81b5598..2a7278fe 100644 --- a/modules/io/obsidian-bridge.js +++ b/modules/io/obsidian-bridge.js @@ -463,7 +463,7 @@ Add your lore here... return results; } - // List all notes with basic info + // List all notes with basic info (reads frontmatter - slow for large vaults) async function listAllNotes() { const allFiles = await getVaultFiles(); const notes = []; @@ -495,6 +495,25 @@ Add your lore here... return notes; } + // List just file paths (fast - no content reading) + async function listAllNotePaths() { + const allFiles = await getVaultFiles(); + + INFO && console.log(`listAllNotePaths: Found ${allFiles.length} files`); + + // Convert to note objects with just path and name + const notes = allFiles.map(filePath => ({ + path: filePath, + name: filePath.split("/").pop().replace(".md", ""), + folder: filePath.includes("/") ? filePath.substring(0, filePath.lastIndexOf("/")) : "" + })); + + // Sort by path + notes.sort((a, b) => a.path.localeCompare(b.path)); + + return notes; + } + return { init, config, @@ -509,7 +528,8 @@ Add your lore here... findNoteByFmgId, generateNoteTemplate, searchNotes, - listAllNotes + listAllNotes, + listAllNotePaths }; })(); diff --git a/modules/ui/obsidian-notes-editor.js b/modules/ui/obsidian-notes-editor.js index 126b179d..0c52e96b 100644 --- a/modules/ui/obsidian-notes-editor.js +++ b/modules/ui/obsidian-notes-editor.js @@ -300,16 +300,19 @@ async function promptCreateNewNote(elementId, elementType, coordinates) { }; const showBrowse = async () => { - resultsDiv.innerHTML = "

Loading all notes...

"; + resultsDiv.innerHTML = "

Loading file list...

"; try { - const allNotes = await ObsidianBridge.listAllNotes(); + // Use fast path-only listing (doesn't read file contents) + const allNotes = await ObsidianBridge.listAllNotePaths(); if (allNotes.length === 0) { resultsDiv.innerHTML = "

No notes in vault

"; return; } + INFO && console.log(`Displaying ${allNotes.length} notes in folder tree`); + // Build folder tree const tree = buildFolderTree(allNotes); resultsDiv.innerHTML = renderFolderTree(tree, allNotes); @@ -321,12 +324,15 @@ async function promptCreateNewNote(elementId, elementType, coordinates) { $("#alert").dialog("close"); try { const note = allNotes[index]; + // Read the file content only when clicked const content = await ObsidianBridge.getNote(note.path); + const {frontmatter} = ObsidianBridge.parseFrontmatter(content); + resolve({ path: note.path, name: note.name, content, - frontmatter: note.frontmatter + frontmatter }); } catch (error) { reject(error);