# 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. - The editor handles bodies (several lines) too, but they're edited in the Note view, in stage 7. **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