commit 0e03c396bf283e2d56a4c4f5afc966f68292a21b Author: Skkeye Date: Thu Oct 1 00:42:28 2026 -0400 Add the project skeleton and the approved design Cargo.toml with the release profile and lints, the toolchain pinned to Rust 1.90.0, a build script that records the git commit for `cumulus --version`, and rustfmt settings and cargo aliases (`cargo lint`, `cargo linux`) as in skyshift. DESIGN.md holds the design approved after the question rounds; README.md has build instructions and the planned keys. Co-Authored-By: Claude Opus 5.5 diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..802244d --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,15 @@ +# Windows (MSVC): link the C runtime statically, so cumulus.exe runs on any +# Windows 10/11 machine without installing the "Visual C++ Redistributable". +[target.'cfg(all(windows, target_env = "msvc"))'] +rustflags = ["-C", "target-feature=+crt-static"] + +[alias] +# `cargo lint`: the strict check to run before committing. +lint = "clippy --all-targets -- -D warnings" +# `cargo linux`: build a Linux binary from any OS. It's fully static, so the +# same file runs on Void (glibc or musl), Fedora and other distributions: +# target/x86_64-unknown-linux-musl/release/cumulus +linux = [ + "build", "--release", "--target", "x86_64-unknown-linux-musl", + "--config", "target.x86_64-unknown-linux-musl.linker = 'rust-lld'", +] diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6b7a837 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Keep LF line endings everywhere (Windows and Linux share this repo). +* text=auto eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..919eafc --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "cumulus" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..1cb123f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "cumulus" +version = "0.1.0" +edition = "2024" +# The version cumulus is built and tested with (pinned in rust-toolchain.toml). +rust-version = "1.90" +description = "Brainstorm in the terminal: idea graphs on a 2D canvas, with vim keys and the mouse." +publish = false + +[lints.rust] +unsafe_code = "forbid" + +[lints.clippy] +all = { level = "warn", priority = -1 } + +[profile.release] +# A smaller, faster program: optimize across all crates at once (lto, in one +# piece with codegen-units = 1), and leave out the debug symbols (strip). +lto = true +codegen-units = 1 +strip = true diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..1518f27 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,465 @@ +# 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, half the spacing), + Overview (colored dots). 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, + cross-links as 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