A terminal-native note-taking system. Fuzzy-search the contents of every note,
follow [[wiki]]-style links between them, and view your whole notes graph in
the browser — all without leaving the terminal.
┌────────────────────────────────────────────────────────────────────────┐
│ > graph │
│ physics_notes.md │ 1: # Physics Notes │
│ graph_theory.md │ 2: │
│ cytoscape_layouts.md │ 3: See [[graph_theory]] for the │
│ ▌ notes_on_clustering.md │ 4: clustering algorithms used in │
│ ... │ 5: [cytoscape layouts](cytoscape_…). │
│ │ │
│ Ctrl-D delete │ Ctrl-C copy first match │ Ctrl-P graph view │
└────────────────────────────────────────────────────────────────────────┘
- Fast full-text fuzzy search across all notes via
fzf+ripgrep, backed by a local content index so it stays instant even when your notes live in iCloud / Dropbox / OneDrive (no per-launch cloud download). - Live preview of each note as you type, with your query highlighted inline in the full file content.
- Create-on-the-fly: type a query that doesn't match, press Enter, and
you're editing a new
.mdfile named after your query. - Inter-note links, two notations:
[[wiki-style]]/[[target|alias]]/[[subdir/target]][label](file.md)standard markdown Press<CR>orgfon a link in vim to jump to it. Broken links auto-create the missing note on follow.
- In-vim insert-mode link picker: in insert mode, type
[[and an fzf overlay pops up listing every note. Selecting one inserts the stem inside[[…]]and leaves your cursor past the closing brackets in insert mode. - Backlinks via
:ZnoteBacklinks— populates quickfix with every note that links to the current one. - Interactive graph view (Ctrl-P from the fzf list): launches a local HTTP server and opens a browser with a force-directed graph of your notes (Cytoscape.js + cola layout, organic Poisson-disk initial layout, click a node to read the rendered markdown in a modal). The server auto-shuts when the tab is closed.
- Delete + copy bindings built into the picker (Ctrl-D, Ctrl-C).
- Cloud-sync friendly: point
NOTES_DIRat any synced folder (iCloud Drive, Dropbox, Google Drive, …) and edit from any device.
git clone https://github.com/eladab/znote.git
cd znote
./install.shInstalls znote to /usr/local/bin (or ~/.local/bin if /usr/local/bin
isn't writable). Override the destination with PREFIX=…:
PREFIX=$HOME/.local ./install.shSkip the first-run prompt by pre-seeding the notes directory:
ZNOTE_NOTES_DIR=$HOME/Documents/Notes ./install.sh(ZNOTE_NOTES_DIR is a dedicated installer var so an exported runtime
NOTES_DIR in your shell doesn't accidentally clobber an existing config.)
cp znote /usr/local/bin/znote
chmod +x /usr/local/bin/znotefzf≥ 0.30 (uses--bind change:reload,start:reload)ripgrep(rg)vim8.2+ with+terminaland+popupwin(link picker)python3≥ 3.7 (graph server)gawk(GNU awk; the main loop usesgensub)
Install on macOS:
brew install fzf ripgrep gawk vim python3Debian/Ubuntu:
sudo apt install fzf ripgrep gawk vim python3Arch:
sudo pacman -S fzf ripgrep gawk vim python3On first run, znote asks for your notes directory and saves it to:
~/.config/znote/config # or $XDG_CONFIG_HOME/znote/config
To change it later, edit the file, delete it to re-prompt, or export
NOTES_DIR in your shell (the env var always wins):
export NOTES_DIR="$HOME/Documents/Notes"The config file is a sourceable shell snippet:
NOTES_DIR=/Users/you/Documents/NotesJust run:
znote| Key | Action |
|---|---|
Enter |
Open selected note in vim, jumping to first match if a query was typed |
Enter (query, no match) |
Create a new note named after the query (.md added, spaces → _) |
Ctrl-D |
Delete the selected file (with confirmation) |
Ctrl-C |
Copy the first matching line to clipboard |
Ctrl-P |
Open the graph view in your browser |
Esc |
Exit |
| Key | Action |
|---|---|
<CR> / gf (normal) |
Follow link under cursor ([[wiki]] or [label](file.md)) |
[[ (insert) |
Open an fzf overlay listing all notes; select to insert the link |
<C-o> |
Jump back (vim's built-in jumplist) |
:ZnoteBacklinks |
Populate quickfix with notes linking to the current note |
These bindings live in ~/.cache/znote/znote.vim, which znote regenerates on
every launch and sources via vim -S. They are only active when vim is
launched through znote (or znote --resolve triggered from within such a
session). Opening a note with plain vim foo.md won't enable them — by
design, znote stays out of your normal vim sessions.
If you want them everywhere, source the cached file from your ~/.vimrc:
if filereadable(expand('~/.cache/znote/znote.vim'))
source ~/.cache/znote/znote.vim
endif(Run znote at least once first to generate the file.)
Wiki style: see [[work_log]] for context.
Aliased: refer to [[work_log|yesterday's notes]].
Nested: subdir: [[projects/q3_plan]].
Markdown: see [my work log](work_log.md).
Nested: see [Q3 plan](projects/q3_plan.md).Broken [[wiki]] targets auto-create as $NOTES_DIR/<target>.md (spaces
become underscores). Bare basenames resolve against the index — if multiple
files share a basename, the first alphabetically wins with a warning.
Ctrl-P from the picker spawns a small Python HTTP server on a free port in
8765–8775 and opens http://127.0.0.1:<port>/ in your browser.
- Force-directed layout (cola), Poisson-disk initial placement → organic spread
- Click a node → rendered markdown in a modal; links inside the modal stay clickable
- Live search box, zoom controls, shuffle button (re-randomize layout)
- Server auto-shuts seconds after the tab closes (SSE-based detection), or after 30 min idle as a fallback
- Force-stop with
znote --graph-stop
znote keeps a tab-separated content index at
~/.cache/znote/index (relpath\tlineno\tcontent) which is rebuilt in the
background on every launch. All foreground operations (search, preview,
jump-to-line, backlinks) read from the index, so they never touch the source
files — important when your notes live in iCloud Drive with "Optimize
Storage" enabled and most files are dataless placeholders. The index is also
kept in sync incrementally: after every edit in vim, the touched file is
re-indexed; Ctrl-D removes the deleted file from the index immediately.
These are mostly used internally by znote but are documented for the curious:
| Command | Purpose |
|---|---|
znote |
Interactive picker (the main UI) |
znote --build |
Rebuild the content index |
znote --search <q> |
Print matching file paths (used by fzf) |
znote --preview <q> <file> |
Preview content with query highlighted |
znote --jump <q> <file> |
Print the first matching line number |
znote --resolve <target> |
Resolve a wiki-link target to an absolute path |
znote --backlinks <basename> |
Emit file:line:text for every link to <basename> |
znote --graph |
Start the graph server and open the browser |
znote --graph-stop |
Stop the graph server |
znote --index-update <file> |
Re-index a single file (used after vim exits) |
znote --index-remove <file> |
Drop a file from the index (used after delete) |
znote --write-vim |
Regenerate ~/.cache/znote/znote.vim |
MIT — see LICENSE.
