# 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. - Rapid entry is what happens after `o`, `O` or `a`: Enter starts the next sibling, Tab / Shift-Tab indent and outdent it, Enter on an empty line stops. Esc switches to vim motions inside the text; a second Esc leaves. - `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. **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