Summary
Add support for a .zenignore file at the vault root that lets users exclude
directories and files from indexing, search, and the folder tree — using
gitignore-compatible syntax.
Motivation
Some vaults contain more than notes. When a vault directory also holds
programming artifacts (e.g. node_modules, __pycache__, build output), every
one of those files is currently walked, read for metadata, and surfaced in the
sidebar. This inflates load times and clutters the tree with content that isn't
a note.
Concretely, a single project folder in my vault indexes 2385 items, of which
2209 come from node_modules — so ~93% of the indexing cost for that folder
is pure noise.
Current behavior
Vault content is walked in several independent places, all sharing the same
skip rules (resolveDirDescent + entry.name.startsWith('.')):
listNotes() — the note-metadata index (main load-time cost)
listFolders() — the sidebar folder tree
collectBuiltinSearchCandidates() — JS full-text search fallback
collectRipgrepSearchCandidates() — ripgrep full-text search
VaultWatcher — the chokidar ignored predicate for live updates
There is already precedent for path exclusion: VaultWatcher hardcodes
node_modules in its ignore predicate. That skip is not honored by the
load-time walks, and it isn't user-configurable. .zenignore generalizes it.
Proposed behavior
- On scan, read
<vault-root>/.zenignore (if present) and build a
gitignore-compatible matcher.
- Apply the matcher in each walk so ignored directories are skipped before
descending into them — that's where the load-time win comes from.
- Honor it in the watcher and both search backends so ignored paths don't
reappear via live updates or search results.
- Keep the existing built-in
node_modules default so current behavior
doesn't regress; .zenignore adds to it rather than replacing it.
Open questions
- Scope: root-only
.zenignore vs. nested per-directory files (like git).
Root-only is much simpler and covers the primary use case; suggest starting
there.
- Syntax source: adopt exact gitignore semantics (least surprising) via a
small dependency rather than hand-rolling glob matching?
Notes on implementation
The startsWith('.') skip + descent check is currently duplicated across the
walks. Extracting that shared skip logic into one helper first would let the
ignore matcher be threaded through a single place, keeping the feature small and
the diff easy to review.
Happy to implement this if the approach sounds good — I'd wait for agreement on scope (root-only vs. nested) before starting.
Summary
Add support for a
.zenignorefile at the vault root that lets users excludedirectories and files from indexing, search, and the folder tree — using
gitignore-compatible syntax.
Motivation
Some vaults contain more than notes. When a vault directory also holds
programming artifacts (e.g.
node_modules,__pycache__, build output), everyone of those files is currently walked, read for metadata, and surfaced in the
sidebar. This inflates load times and clutters the tree with content that isn't
a note.
Concretely, a single project folder in my vault indexes 2385 items, of which
2209 come from
node_modules— so ~93% of the indexing cost for that folderis pure noise.
Current behavior
Vault content is walked in several independent places, all sharing the same
skip rules (
resolveDirDescent+entry.name.startsWith('.')):listNotes()— the note-metadata index (main load-time cost)listFolders()— the sidebar folder treecollectBuiltinSearchCandidates()— JS full-text search fallbackcollectRipgrepSearchCandidates()— ripgrep full-text searchVaultWatcher— the chokidarignoredpredicate for live updatesThere is already precedent for path exclusion:
VaultWatcherhardcodesnode_modulesin its ignore predicate. That skip is not honored by theload-time walks, and it isn't user-configurable.
.zenignoregeneralizes it.Proposed behavior
<vault-root>/.zenignore(if present) and build agitignore-compatible matcher.
descending into them — that's where the load-time win comes from.
reappear via live updates or search results.
node_modulesdefault so current behaviordoesn't regress;
.zenignoreadds to it rather than replacing it.Open questions
.zenignorevs. nested per-directory files (like git).Root-only is much simpler and covers the primary use case; suggest starting
there.
small dependency rather than hand-rolling glob matching?
Notes on implementation
The
startsWith('.')skip + descent check is currently duplicated across thewalks. Extracting that shared skip logic into one helper first would let the
ignore matcher be threaded through a single place, keeping the feature small and
the diff easy to review.