# cumulus: design This is the design approved on 2026-10-01, after six rounds of questions. It's the reference while cumulus is being built: when a decision changes, this file changes with it. ## What cumulus is A keyboard-driven brainstorming app for the terminal, in Rust with ratatui. The aim is "the idea node graph to end them all": quick idea dumps, mind maps, whiteboard-style planning and linked notes, in one tool. It runs on Windows and Linux, with vim keys first and full mouse support. ## Scope **v1** - The canvas: nodes, branches, cross-links with every style, folding and focus, zoom, the `K` card, the mouse. - The Outline and Note views, splits and the document bar. - The key engine: medium vim, which-key, help and status-bar hints. - Visual, move and connect modes; text editing with vim normal mode. - The command line, with completion and history. - Portals, with the jump list, backlinks and breadcrumb. - Rapid entry and the Inbox. - RON documents with autosave, reload, backups and undo; file names that follow the root's title. - The startup rules and `cumulus new`. - TOML config, key and theme files, with named styles and styles set by tags. **v1.1**: the Board view · the Search picker across documents · `:tidy` and align · branch out · export (Markdown, plain text) and import (indented lists). **Later**: peeking into portals and pulling a document back in · branches that keep themselves tidy · the Links map · backlinks inside a document · picking single nodes in visual mode · `Alt+hjkl` · opening a body in `$EDITOR` · persistent undo · sessions · operator + motion and macros · a tutorial document · checkbox, link and date fields · Graphviz and Mermaid export · a Settings view · vim tab pages. **Not planned unless asked**: `f` hint jumps · a minimap · straight (braille) lines. ## Decisions **Documents and structure** - Each document has one main root, and the document's name is the root's title. Loose top-level nodes float around it. - A tree plus cross-links: each node has at most one parent, and cross-links join any two nodes, at most one link per pair. - A node holds a title (one line, never empty), a body, tags and a color. A new node left empty disappears. - A portal node shows another document's root, live (its title, color and node count), and leads there. Portals always land on the root. - Backlinks are stored in the target document and synced on save, so they travel with the files. - Files live anywhere: there's no workspace folder. Portal paths are relative, with `/` on every OS. - Each file carries a random `uid`, so "where you left off" survives renames and a portal can tell when its file was replaced by another document. - File names follow the root's title, and a rename fixes the portals pointing at the file. A folder's default document keeps its name (`ideas.cum`). **Canvas** - Free placement: a new node appears next to its parent, and nothing moves unless you move it. Moving a node brings its branch along. - Folding: each branch is Open, Auto (open while the cursor is inside) or Closed (a jump inside opens it until you leave). New branches start Open; a focus toggle makes Open branches act like Auto. - Zoom levels: Normal (boxes), Far (one line per node: the same width, half the height) and Overview (colored dots: a quarter of the width, a third of the height). Far keeps the width because one-line titles collided when distances were halved both ways (changed during stage 2). Body text never shows on the canvas: `K` pops it up, and the Note view shows all of it. - Right-angle lines with proper junctions (`├ ┬ ┼`); lines pass under nodes. - A node's color goes on its border. Each node can pick its border, and the theme's named styles and tag rules can set color and border together. - Connections can set arrowheads (off, end, start, both), a line (solid, dashed, dotted, thick, double), a color (a palette color or the node's own) and a label, or use a named style. Branches start solid without arrowheads, in their parent's color, so a node's color flows down the lines to its children (dim when the parent has no color). Cross-links start as dim dashed arrows. **Keys** - Medium vim: counts, operators with node targets, registers (with `"+` for the system clipboard), marks (capital marks across documents), `.` to repeat. - Tab and `Ctrl-i` jump forward, as in vim, so `a` adds a child. - `dd` deletes a node with its branch; `x` deletes just the node, and its children move up. - A count on `dd`, `yy`, `>>` or `<<` takes in the next siblings: `3dd` deletes this node and the next two, with their branches, as vim's `3dd` takes three lines. Which-key says how many as you type (`2d3`: 6 nodes). - Rapid entry is what happens after `o`, `O` or `a`: Enter starts the next sibling, Tab / Shift-Tab indent and outdent the idea being typed, and Enter on an empty idea stops (the empty idea goes away). Esc switches to vim keys inside the text; a second Esc leaves. Inside a title, `o` and `O` start a new sibling too. - `>>` puts the node under the one above it, as its last child; `<<` takes it out a level, right after its old parent. Both place the node below its new neighbors, and keep its branch with it. - `C` (connect mode) edits the connection to another node. Aimed at your own parent, it styles or cuts the branch line; cutting makes the node loose. - `M` (move mode) moves the branch; `M` again switches to just the node. - `gh gj gk gl` move through the tree in every view; plain `hjkl` follow each view's layout (on the canvas: the nearest node that way). - Folds: `za zo zc zR zM`, plus `zf` (Auto) and `zi` (focus). - `q` stays free for macros; quit with `:q`, `:qa` or `ZZ`. - Which-key works like skyshift's; the leader menu pops up at once. The status bar shows key hints, and `?` opens searchable help. - Keys can be changed in `keys.toml`, in vim notation. **Editing text** (settled in stage 6) - A title is typed right in its box on the canvas (zoomed to Normal while it's edited). The box grows with the text, the terminal's cursor sits in it, and the border takes the insert color while you type. - The cursor's shape follows the mode, as in vim: a bar while typing (and in the command line), a block on the letter with vim keys, and the terminal's own shape elsewhere. - The document follows the text as you type, so every view stays current. The whole edit is one undo step (in rapid entry, one per idea); inside the text, `u` and `Ctrl-r` undo and redo the edit's own changes. - A title left empty comes back as it was, with a note saying so; a new idea left empty goes away. - Registers are shared by the whole app, as in vim: text yanked in one title pastes into another. `"+` stays inside cumulus until stage 8 connects it to the system clipboard. - `.` inside the text repeats the last change there, typing included. `.` on nodes comes with node yank and paste, in stage 8. - As in vim: `cw` changes to the end of the word, `dw` stops at the end of the line, `j` and `k` move by lines of text rather than wrapped rows, Esc steps back onto the last letter typed. - Tab and Shift-Tab only move a new idea; on an existing title they say that `>>` and `<<` do it. - The mouse while editing: a click in the text moves the text cursor; a click anywhere else keeps the text, finishes the edit and does what a click does; a double-click on a title starts editing at that letter. - A portal's title is its document's name, so it isn't edited in place: rename that document instead. - Bodies are edited in the Note view and in the `K` card, with the same editor: Enter starts a new line, Tab types four spaces. `cc`, `C` and `s` start typing on an empty line too, as in vim (bodies are often empty). **Views, panes and popups** (settled in stage 7) - The Note view is made of parts: the title, the tags, the body, each link (out, then in) and each child. `j` and `k` step through them, with a bar marking the one you're on; Enter, `i` or `A` edits it (`I` from its start, `cc` `S` anew), or goes to that link (a jump: `''` comes back) or child. `h` and `l` turn the page: the sibling before or after. Node keys (`dd`, `za`, `o`…) act on the note's node. The note follows the cursor, starting at the title of each node it comes to. - The outline has a row per node: its fold marker (`▾` `▸`), tree lines in the parent's color (as branches are on the canvas), its tags, `¶` when it has a body, `+N` for what a fold hides. `zb` shows every body under its title, where its children hang, to read the document like a page. `j` and `k` go row by row, `h` and `l` to the parent and the first child; a click on a marker folds. Titles are edited in their row, rapid entry included. Rows wider than the view scroll sideways (`zh` `zl`, the wheel sideways), and the cursor's title comes back into view when the cursor moves. - Move mode in the outline and the Note view moves the node through the tree: `j` and `k` swap it with its siblings (`J` and `K` five places), `h` takes it out a level, `l` puts it under the one above. - The status bar has two lines: the mode, the view, the breadcrumb (or a message in its place) and the keys typed so far; then the key hints for what you're doing. `w` hides the hint line along with which-key. - Help (`?`, `?`, `:help [topic]`) lists every key of the focused view and every `:` command, under which-key's headings. `/` searches (rows that start with what's typed first, then fuzzy matches), and Enter runs the row: a key that waits for more (`m`) is typed for you to finish, and a `:` command that needs an argument opens the command line with it typed. - The `K` card opens beside the node (in the middle when the node isn't on screen). `j` `k` `Ctrl-d` `Ctrl-u` scroll it; `i`, `A` or Enter edits the body right there (`I` from its start), and when that's done the card stays; `K`, `q` or Esc closes it. - Right-click opens a menu of what you can do with the node, each with the keys that do the same; on the empty canvas: a new idea right there (a loose node), zoom in and out. `c` `B` `s` `t` open the color, border, style and tags pickers, which start on what the node has now. In the tags picker Enter puts a tag on or off and the picker stays open; a search that matches no tag, then Enter, adds it as a new one. - Every pane has a box with its view and document in the top border, the focused one in the accent color. A new pane comes first (left or above) and takes the focus, as in vim, and closing the last pane quits (`q`, `ZZ`, `:q`). A click focuses the pane under it; a text edited in the old one is done first. - The document bar is always at the top, the focused pane's document standing out; a click on a name shows that document in the pane. Each pane can show any open document. - Popups (the help, the menu, the pickers, the card) are a mode of their own: keys and clicks go to them, a click outside a list closes it, and they stay above the status bar. **Files and saving** - Documents are RON files ending in `.cum`. Configs are TOML: `config.toml`, `keys.toml` and `themes/.toml`. - Autosave about a second after a change (`autosave = false` for vim-style `:w`). A file changed outside cumulus is reloaded; if you also had unsaved changes, cumulus asks which to keep. Daily backups go to the data folder. - Undo per document, while cumulus runs: one step per command, per text-editing session, and per node in rapid entry. - Each document reopens where you left it (cursor, zoom, scroll). **Screen** - Vim-style splits; panes on the same document share one cursor. - Open documents are buffers, listed in a bar at the top; the status bar is at the bottom. **Project** - A library plus a thin `main.rs`, so build stages can run as examples. - Rust pinned to 1.90.0; nothing needs nightly. ## Data model (`model/`: pure data, no UI, no files) ```rust // Ids and units: newtypes, so they can't be mixed up. NodeId(u32) // unique inside a document, never reused while it's open BufferId(u32) // an open document, like vim's buffer numbers (:ls, :b 3) PaneId(u32) // a split DocUid(u64) // stored in the file, survives renames CanvasPos { x: i32, y: i32 } // cells at Normal zoom, may be negative ScreenPos { col: u16, row: u16 } // a terminal cell Camera { origin: CanvasPos, zoom: Zoom } // converts between the two enum Zoom { Normal, Far, Overview } // Text, checked once when it comes in. Title // one line, never empty Body // any text Tag // lowercase, no spaces, stored without '#' Label // a connection label: one short line ColorName, StyleName // names looked up in the theme // A node and its place in the tree. struct Node { content: Content, tags: Tags, look: NodeLook, pos: CanvasPos, fold: Fold } enum Content { Idea(Idea), Portal(Portal) } enum Fold { Open, Auto, Closed } enum Place { Root, Loose, Child { parent: NodeId, branch: LineLook } } struct NodeLook { color: Option, border: Option, style: Option } enum Border { Rounded, Square, Double, Thick, Dashed, Hidden } // Cross-links. Branch lines use the same LineLook. struct Link { from: NodeId, to: NodeId, label: Option