From 0e03c396bf283e2d56a4c4f5afc966f68292a21b Mon Sep 17 00:00:00 2001 From: Skkeye Date: Thu, 1 Oct 2026 00:42:28 -0400 Subject: [PATCH] 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 --- .cargo/config.toml | 15 ++ .gitattributes | 2 + .gitignore | 1 + Cargo.lock | 7 + Cargo.toml | 21 ++ DESIGN.md | 465 ++++++++++++++++++++++++++++++++++++++++++++ README.md | 56 ++++++ build.rs | 32 +++ rust-toolchain.toml | 8 + rustfmt.toml | 4 + src/lib.rs | 11 ++ src/main.rs | 37 ++++ 12 files changed, 659 insertions(+) create mode 100644 .cargo/config.toml create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 DESIGN.md create mode 100644 README.md create mode 100644 build.rs create mode 100644 rust-toolchain.toml create mode 100644 rustfmt.toml create mode 100644 src/lib.rs create mode 100644 src/main.rs 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