A keyboard-first mindmap desktop app for linux. The map is stored as a plain .md file (nested bullet lists) or a .org file (outline headings), you can edit it in the app UI or open the same file in any text editor. Fully mouseless: modal (vim-like) editing, marks/visual selection, operators, fuzzy search, command palette, quick file switching.
- Keyboard-first modal editor: normal / insert / visual modes
- Two on-disk formats, auto-detected by extension:
- Markdown: nested bullets,
- [ ]/- [x]task checkboxes - Org:
*headings,TODO/DONEtasks
- Markdown: nested bullets,
- Vim-style editing: count prefixes (
3j,3dd),mmarks,vvisual ranges,d/y/coperators,dd/yy/cc,p/Ppaste,o/Onew sibling - Reordering:
J/Kmove node,L/H(or>/<) indent / outdent - Jumping:
gg/Groot / last,j/k/h/l(+ arrow keys) - Search:
/fuzzy node search,n/Nrepeat,*/#find under cursor - Command palette:
:orctrl+poralt+x - Quick switcher:
alt+q(orctrl+q) to fuzzy-open any map or create a new one (name must end in.mdor.org) - Clipboard: cut / yank / paste whole subtrees — cut is never a permanent delete (
precovers it) - Undo / redo via document snapshots (
u/ctrl+r) - Subtree collapse / expand with
+Nbadges (z) - Task checkboxes with completion progress in the status bar (
t) - Autosave (debounced) — no save button ever; external edits reload live (external wins)
- Camera: auto-centers on the cursor; manual pan/zoom is remembered per file (
ctrl+wheelzoom-at-cursor, wheel /space~+drag pan, ~ctrl+=/ctrl+-/ctrl+0) - Node text wraps at 50 chars — boxes grow vertically instead of clipping
- Status bar: NORMAL / INSERT / VISUAL (+ count + pending operator), file path, breadcrumb, task + search progress
- Custom dark palette (amber focus / teal matches / green tasks / purple accents)
- Desktop shell: Tauri 2 (Rust backend)
- Frontend: Vanilla TypeScript + Vite (no UI framework)
- Rendering: HTML5 Canvas 2D
- Rust crates: notify (file watching), tauri-plugin-dialog, tauri-plugin-store, log
- Tests: vitest (unit tests for markdown, org, layout, state)
Each mindmap node is a list item; children are deeper-indented bullets.
# My Map - Setup - Install deps - Configure - Feature 1 - [ ] Keyboard nav - [x] Autosave
- Optional leading
# Titlesets the root label (else root = filename) - [ ]/- [x]map to task checkboxes- Writing canonicalizes to 2-space indent
Each mindmap node is a headline; children are deeper stars.
* My Map ** Setup *** Install deps *** Configure ** Feature 1 *** TODO Keyboard nav *** DONE Autosave
- The top-level headline becomes the root label
TODO/DONEkeywords map to task checkboxes- Heading levels are normalized to start at the root
./run.sh # installs deps on first run, then launches
npm install npm run tauri dev
Run the test suite:
npm test
i/a— edit text at start / end; the node becomes an inline inputEnterorEsc— commit the edit and return to NORMAL (no new node)Tab— commit and create a child, then edit ito/O— create a new sibling below / above and edit it
d/dd/dj— cut current node / line / node+next (recoverable viap)y/yy— copy current node (or subtree) to the clipboardc/cc— cut then start editing in placep/P— paste after / before the cursorJ/K— move the node down / up among its siblingsL/>— indent (child of the previous sibling);H/<— outdent
m— mark / unmark the current node (multi-select)v— visual mode: move withj/ketc. to grow the range, thend/y/c- An operator (
d/y/c) with no selection waits for a motion (j,G,gg, …) — this is “operator-pending” Escclears marks / visual mode / pending counts
/— fuzzy-search node text;Enterjumps to the matchn/N— next / previous match*/#— find next / previous node containing the current node’s text
:orctrl+poralt+x— command palette (new map, edit, cut/paste, move, search, undo/redo, …)alt+q/ctrl+q— quick switcher: fuzzy-open a map, or type a.md/.orgname to create it?— keymap help (planned)
Current version: 0.3.0. See ~/magnus/mouseless-mindmap/logs/ for app logs.
Implemented and tested (118 unit tests): model + markdown round-trip, org round-trip, LTR canvas renderer with wrapping, modal editor with the full vim-style suite (ops/motions/counts/clip/search as a tested Editor controller), autosave + live file watcher, vault + quick switcher, command palette, node search, status bar, camera (auto-center + per-file persistence), custom palette.
| Key | Action |
|---|---|
| j / k (↓ / ↑) | move down / up (count-prefixed) |
| h / l (← / →) | parent / expand-or-first-child |
| enter | create sibling below (edit) |
| o / O | new sibling below / above (edit) |
| tab / shift-tab | create child (edit) / outdent |
| i / a | edit node (start / end) |
| m | mark / unmark node (multi-select) |
| v | enter visual mode (range selection) |
| d / dd / d + motion | cut node(s) / line / range |
| y / yy / y + motion | yank node(s) / line / range |
| c / cc / c + motion | change node(s) / line / range |
| p / P | paste after / before |
| J / K | move node down / up |
| L / > | indent |
| H / < | outdent |
| z / t | collapse-toggle / task toggle |
| gg / G | jump root / last |
| u / ctrl+r | undo / redo |
| * / # | find next / prev under cursor |
| n / N | repeat search next / prev |
| / | fuzzy node search |
| : / ctrl+p / alt+x | command palette |
| alt+q / ctrl+q | quick switcher (open/create) |
| 0-9 | count prefix (e.g. 3j, 2dd, d3j) |
| esc | clear count / pending operator / visual |
| Key | Action |
|---|---|
| enter / esc | commit text, back to NORMAL (no new node) |
| tab | commit + create child |
| Key | Action |
|---|---|
| ctrl+wheel | zoom at cursor |
| ctrl+= / ctrl+- | zoom in / out |
| ctrl+0 | reset camera |
| wheel | pan vertically |
| shift+wheel | pan horizontally |
| space + drag | pan |
| shift+arrows | pan |
mouseless-mindmap/
src-tauri/ Rust backend
src/lib.rs commands (read/write/list/create/watch/log), app setup
src/watcher.rs notify file watcher -> fs-changed events
src/logger.rs file logger -> ~/magnus/.../logs/
src/main.rs tauri entrypoint
Cargo.toml crates: notify, serde_json, dialog, store, log
tauri.conf.json window/app config
capabilities/ Tauri permission capabilities
icons/ app icons
src/ TypeScript frontend
app.ts bootstrap, key dispatch, camera/autosave/vault wiring, status bar, boot
editor.ts modal-edit controller: operators, counts, clipboard, search (unit tested)
model.ts Node/document types
markdown.ts md parse + write
org.ts org parse + write (TODO/DONE, star depth)
state.ts app state, selections, visual mode, cut/yank/paste/move/indent + undo/redo
keymap.ts modal keybindings + action types
layout.ts LTR tidy tree layout, 50-char word wrap
canvas.ts render loop, camera, selection/match highlights
input.ts inline HTML text input overlay
overlays.ts generic fuzzy overlay (palette + search)
search.ts fuzzy matcher
vault.ts vault index, recents, quick switcher
backend.ts Tauri invoke wrappers + settings store
util.ts shared helpers (basename, isOrgPath)
styles.css theme (custom palette) + UI chrome
*.test.ts vitest unit tests (markdown, org, layout, state, editor, search, keymap, model, vault)
index.html app shell
vite.config.ts Vite/Tauri dev server config
run.sh Launcher (npm install + tauri dev)
plans-and-todos.org Milestone plan + todos
bugs.org Bug tracker
changelog.org Version log
LICENCE GPL 3.0
README.org This file
.gitignore Ignore build artifacts & logs