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 <[email protected]>
466 lines
23 KiB
Markdown
466 lines
23 KiB
Markdown
# 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/<name>.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<ColorName>, border: Option<Border>, style: Option<StyleName> }
|
||
enum Border { Rounded, Square, Double, Thick, Dashed, Hidden }
|
||
|
||
// Cross-links. Branch lines use the same LineLook.
|
||
struct Link { from: NodeId, to: NodeId, label: Option<Label>, look: LineLook }
|
||
struct LineLook { arrows: Option<Arrows>, line: Option<Stroke>, color: Option<LineColor>, style: Option<StyleName> }
|
||
enum Arrows { Off, End, Start, Both }
|
||
enum Stroke { Solid, Dashed, Dotted, Thick, Double }
|
||
enum LineColor { Named(ColorName), FollowNode }
|
||
|
||
// Between documents.
|
||
struct Portal { path: LinkPath, uid: DocUid, title: Title } // title: kept in case the file goes missing
|
||
struct Backlink { from: LinkPath, uid: DocUid, node: NodeId }
|
||
|
||
// The document. Its fields are private, so only its methods can change it.
|
||
struct Document { uid, root: NodeId, entries: BTreeMap<NodeId, Entry>, loose: Vec<NodeId>,
|
||
links: BTreeMap<LinkKey, Link>, backlinks: Vec<Backlink>, next_id }
|
||
struct Entry { node: Node, place: Place, children: Vec<NodeId> }
|
||
```
|
||
|
||
The `Option` fields in `NodeLook` and `LineLook` are overrides. A look is built
|
||
up in layers: the theme default, then the tag style, then the named style, then
|
||
the node's own fields. Each layer only states what it changes.
|
||
|
||
**Rules the `Document` methods guarantee**
|
||
- There is exactly one root, and it can't be deleted or given a parent.
|
||
- Each node has at most one parent, and there are no cycles: a move checks
|
||
ancestors first.
|
||
- Children keep their order, and the file lists nodes in outline order.
|
||
- A link never joins a node to itself, both of its ends always exist, and
|
||
deleting a node removes its links.
|
||
|
||
**Undo.** Every change runs as a transaction:
|
||
`doc.edit(|e| { e.add(…)?; e.update(…)?; Ok(()) })`. The transaction records
|
||
the old value of everything it touches. If a step fails, it rolls back; if it
|
||
succeeds, that record becomes one undo step. Undo writes the old values back,
|
||
redo the new ones. No command needs its own "reverse" code, and the records are
|
||
plain data, ready for a persistent undo file later.
|
||
|
||
**Folding** is one pure function of the document, the cursor and the focus
|
||
toggle, so every view folds the same way.
|
||
|
||
**Portals.** A cache holds each target's root title, color and node count,
|
||
refreshed when files change. When a root's title changes, the next save renames
|
||
the file, then fixes the portal paths in every document listed in its
|
||
backlinks, and the backlink paths in every document it points to.
|
||
|
||
## Screen and views
|
||
|
||
```
|
||
cumulus Garden plans [+] │ Kitchen │ Reading list
|
||
┌ Outline ──────────────┐┌ Canvas ───────────────────────────────────────────┐
|
||
│ ▾ Garden plans ││ ┌──────┐ │
|
||
│ ├ Soil ││ ┌──┤ Soil │ │
|
||
│ ├ [Raised beds] ││ │ └──────┘ │
|
||
│ │ └ Cedar ││ ╭──────────────╮ │ ┏━━━━━━━━━━━━━┓ ┌───────┐ │
|
||
│ └ Watering ││ │ Garden plans ├──┼──┨ Raised beds ┠────┤ Cedar │ │
|
||
│ ◦ ask about compost ││ ╰──────────────╯ │ ┗━━━━━━━━━━━━━┛ └───────┘ │
|
||
│ ││ │ ┌──────────┐ │
|
||
│ ││ └──┤ Watering │ │
|
||
│ ││ └──────────┘ │
|
||
└───────────────────────┘└───────────────────────────────────────────────────┘
|
||
NORMAL │ Canvas │ Garden plans › Raised beds 2g
|
||
```
|
||
|
||
| View | What it's for |
|
||
|---|---|
|
||
| **Canvas** | The 2D map: free placement, 3 zoom levels, right-angle lines and the mouse. |
|
||
| **Outline** | Reorganizing and reading: an indented tree with fold markers, `+N` for hidden nodes and `»` for portals, with loose nodes listed at the end. |
|
||
| **Note** | Writing: one node's title, tags, color and body (with vim motions inside), plus its links in and out and its children. It follows the cursor. |
|
||
| **Board** (v1.1) | Sorting: columns by tag or color. Moving a card to another column changes its tag. |
|
||
|
||
- **Pickers:** Documents in v1, Search in v1.1.
|
||
- **Popups:** which-key (bottom right, as in skyshift), `?` help, the `K` card,
|
||
the right-click menu, confirmations, and the command line with its
|
||
completion list.
|
||
- **Top bar:** the open documents. **Bottom bar:** the mode, the view, the
|
||
breadcrumb (or a message), and the keys typed so far (`2g` above).
|
||
|
||
**Mouse** (on the canvas; other views get click, double-click and scroll):
|
||
click selects, double-click edits the title, dragging a node moves it with its
|
||
branch, dragging empty space pans, scrolling pans, Ctrl+drag selects a
|
||
rectangle, Ctrl+click adds or removes one node, right-click opens a menu built
|
||
from the same actions as the keys. Zoom is on the `+` and `-` keys.
|
||
|
||
## Modes and keys
|
||
|
||
```rust
|
||
enum Mode {
|
||
Normal,
|
||
Insert(TextEdit), // typing a title or body; rapid entry lives here
|
||
TextNormal(TextEdit), // vim motions inside that text
|
||
Visual(Visual), // anchor + Rectangle or Branches
|
||
Move(Grab), // a node or a branch being moved
|
||
Connect(Connect), // the source node and the link being styled
|
||
Command(CommandLine), // `:` with completion
|
||
}
|
||
```
|
||
|
||
Each mode has its own key tables. Which-key, `?` help and `keys.toml` all read
|
||
those same tables. The engine handles counts, registers, operator + target
|
||
(`d` + `ab`), and keys that take a character after them (`ma`, `fx` inside
|
||
text).
|
||
|
||
| Group | Keys |
|
||
|---|---|
|
||
| **Move** (every view) | `hjkl` or arrows · `gh gj gk gl` tree moves · `w` `b` next / previous in reading order · `gg` root · `G` last · `/` `n` `N` search · `Ctrl-o` `Ctrl-i` (Tab) jump back / forward · `m{a-z}` `'{a-z}` marks (`A–Z` across documents) · Enter or `gf` through a portal · `gd` follow a cross-link |
|
||
| **View** | `zz` center · `Ctrl-e` `Ctrl-y` `zh` `zl` pan · `Ctrl-d` `Ctrl-u` half a screen · `+` `-` zoom · `za zo zc zR zM` folds · `zf` Auto · `zi` focus · `K` body card |
|
||
| **Create and edit** | `o` `O` sibling below / above · `a` child · `i` `A` Enter edit the title · `I` edit from its start · `cc` `S` retype |
|
||
| **Operators** | `dd` or `dab` delete branch · `dib` delete children · `x` delete just the node · `yy` `yab` `yib` yank · `p` `P` paste after / before · `]p` paste as child · `>>` `<<` indent / outdent · `"a`–`"z` and `"+` registers · `.` repeat · `u` `Ctrl-r` undo / redo |
|
||
| **Modes** | `v` `V` visual · `M` move · `C` connect · `:` command · Esc back to normal |
|
||
| **Leader** `<Space>` | `c` color · `s` style · `B` border · `t` tags · `p` new portal · `i` inbox · `b` documents · `v` then `c`/`o`/`n` for canvas / outline / note · `w` which-key on/off · `?` help |
|
||
| **Panes, documents** | `Ctrl-w v` `Ctrl-w s` split · `Ctrl-w hjkl` focus · `Ctrl-w c` close · `Ctrl-w o` only · `Ctrl-w =` even sizes · `gt` `gT` `]b` `[b` next / previous document · `ZZ` close pane |
|
||
| **Typing** | `Ctrl-w` delete word · `Ctrl-u` delete line · arrows, Home, End · Enter (title: done or next sibling; body: new line) · Tab Shift-Tab (new node: child / back out) · Esc to vim motions |
|
||
| **Inside text** | `h j k l w b e 0 ^ $ f t x r J o O p P u Ctrl-r .` · `d` `c` `y` with motions and `iw aw i" a" i( a(` · `i a I A` back to typing |
|
||
| **Move mode** | `hjkl` 1 cell · `HJKL` 5 cells · `M` branch ↔ node · Enter or Esc done |
|
||
| **Connect mode** | move to the target as usual · Enter link · `d` remove · `>` attach as child · `<Space>` style menu (`s` named, `a` arrows, `l` line, `c` color, `t` label) · Esc cancel |
|
||
| **Visual** | `d y > < M C` and `<Space>c` `<Space>s` `<Space>t` act on the whole selection · `o` swaps ends |
|
||
|
||
## Files
|
||
|
||
**Documents** (`.cum`, RON). A `backlinks: [...]` list appears once another
|
||
document has a portal pointing here.
|
||
|
||
```ron
|
||
// cumulus document. Hand edits are fine: cumulus reloads it when it changes.
|
||
(
|
||
format: 1,
|
||
uid: "7f3c9a1e52b04d18",
|
||
root: 1,
|
||
nodes: [
|
||
(
|
||
id: 1,
|
||
title: "Garden plans",
|
||
pos: (0, 4),
|
||
color: "green",
|
||
),
|
||
(
|
||
id: 2,
|
||
parent: 1,
|
||
title: "Raised beds",
|
||
pos: (22, 4),
|
||
tags: "#todo",
|
||
fold: Auto,
|
||
body: "Cedar, three of them.
|
||
40 cm high.",
|
||
),
|
||
(
|
||
id: 3,
|
||
parent: 1,
|
||
portal: (
|
||
path: "kitchen.cum",
|
||
uid: "c01d5e7aa0f3b912",
|
||
title: "Kitchen",
|
||
),
|
||
pos: (22, 8),
|
||
),
|
||
(
|
||
id: 4,
|
||
title: "ask about compost",
|
||
pos: (0, 14),
|
||
),
|
||
],
|
||
links: [
|
||
(
|
||
from: 2,
|
||
to: 4,
|
||
label: "needs",
|
||
line: Dashed,
|
||
arrows: End,
|
||
),
|
||
],
|
||
)
|
||
```
|
||
|
||
**Config** (TOML) lives in `%APPDATA%\cumulus` (`~/.config/cumulus` on Linux);
|
||
`cumulus --paths` prints the folders. `:config` writes these files with a
|
||
comment on every option, and cumulus reloads them when they change.
|
||
|
||
```toml
|
||
# config.toml
|
||
theme = "default" # a file in the themes folder
|
||
default_document = "ideas" # what `cumulus` looks for at startup
|
||
autosave = true
|
||
mouse = true
|
||
which_key = true
|
||
which_key_delay = "300ms"
|
||
max_node_width = 30
|
||
glyphs = "unicode" # "ascii" for fonts without box-drawing characters
|
||
|
||
# keys.toml
|
||
leader = "<Space>"
|
||
[normal]
|
||
"<Space>x" = "delete-branch"
|
||
[canvas]
|
||
"H" = "previous-document"
|
||
|
||
# themes/default.toml
|
||
accent = "cyan"
|
||
[palette] # terminal names, 0-255, or "#rrggbb"
|
||
red = 167
|
||
orange = 208
|
||
green = 108 # … yellow, teal, blue, purple, pink, brown, gray
|
||
[modes]
|
||
insert = "green"
|
||
visual = "magenta"
|
||
[styles.node.question]
|
||
color = "yellow"
|
||
border = "dashed"
|
||
[styles.link.blocks]
|
||
color = "red"
|
||
line = "dashed"
|
||
arrows = "end"
|
||
label = "blocks"
|
||
[tags]
|
||
question = "question" # a #question node gets the "question" style
|
||
```
|
||
|
||
**Data folder:** where you left off in each document (keyed by `uid`), command
|
||
history, capital marks, recent documents, daily backups, and lock files. If a
|
||
second cumulus opens a document that's already open, it opens it read-only.
|
||
|
||
**Command line**
|
||
- `cumulus` opens `./ideas.cum`, or `~/ideas.cum` if the current folder doesn't
|
||
have one.
|
||
- `cumulus garden.cum` opens that file; `cumulus some/folder` opens that
|
||
folder's default document.
|
||
- `cumulus new` creates `./ideas.cum`, and `cumulus new "Garden plans"` creates
|
||
`./garden-plans.cum`. If the file already exists, cumulus opens it and says so.
|
||
- `cumulus --paths`, `--version` and `--help`.
|
||
|
||
## Module layout and flow
|
||
|
||
```
|
||
cumulus/
|
||
├─ Cargo.toml rust-toolchain.toml build.rs rustfmt.toml .cargo/config.toml
|
||
├─ README.md DESIGN.md .gitignore .gitattributes
|
||
├─ examples/ runnable checkpoints during the build (canvas.rs, …)
|
||
└─ src/
|
||
├─ main.rs command-line arguments, then app::run
|
||
├─ lib.rs the module list
|
||
├─ model/ ids, text, geometry, look, node, portal, link, document, edit, history, fold
|
||
├─ input/ key (vim notation), command (Command enum + names), bindings (default tables),
|
||
│ keymap (+ keys.toml), engine (counts, registers, operators), which_key, mouse
|
||
├─ command/ registry, parse, complete, history
|
||
├─ view/ mod.rs (the View trait), canvas/ (camera, boxes, lines, route, render, hit,
|
||
│ navigate, placement), outline, note, board
|
||
├─ ui/ text_editor (+ vim normal), which_key, command_line, status_bar, document_bar,
|
||
│ card, menu, picker, help, theme, glyphs
|
||
├─ config/ paths, settings, keys_file, theme_file (TOML)
|
||
├─ storage/ document_file (RON), atomic save, backups, lock, state, watch (change polling)
|
||
└─ app/ App, mode, action, buffers, panes (split tree), jumps, marks, registers,
|
||
portals (cache, backlinks, renames), update, render,
|
||
terminal (setup, teardown, panic hook)
|
||
```
|
||
|
||
**Flow:** a terminal event comes in → `input` turns the key or mouse event into
|
||
a `Command` → the focused view turns that command into `Action`s →
|
||
`App::update` applies them (the only place data changes) → the screen is drawn
|
||
from the state.
|
||
|
||
```rust
|
||
trait View { // expected shape; stage 5 settles it from what stages 2–4 needed
|
||
fn kind(&self) -> ViewKind;
|
||
fn keys(&self, mode: &Mode) -> &[KeyTable]; // its tables, most specific first
|
||
fn command(&mut self, command: Command, ctx: &Ctx) -> Vec<Action>;
|
||
fn mouse(&mut self, gesture: Gesture, ctx: &Ctx) -> Vec<Action>;
|
||
fn render(&self, frame: &mut Frame, area: Rect, ctx: &Ctx); // reads state, draws, changes nothing
|
||
}
|
||
```
|
||
|
||
## Build order
|
||
|
||
Hardest parts first, so they shape a simple interface that every view shares
|
||
and the core ends up thin. The model comes first because every hard part reads
|
||
it.
|
||
|
||
| # | Stage | What you can run |
|
||
|---|---|---|
|
||
| 0 | Skeleton: Cargo.toml, toolchain, `build.rs`, rustfmt, aliases, README, DESIGN.md | `cumulus --version` |
|
||
| 1 | Model: types, document rules, transactions and undo, folding, RON round trip | `cargo test` |
|
||
| 2 | Canvas engine: camera, boxes, a line layer with junctions, right-angle routing, arrows and labels, zoom levels, hit-testing, nearest-node moves, placement | `cargo run --example canvas` |
|
||
| 3 | Key engine: notation, tables, counts, registers, operator + target, keys that take a character, which-key, `keys.toml` | the example gets real keys and which-key |
|
||
| 4 | Command line: registry, parser, completion (popup + ghost text), history | `:` in the example |
|
||
| 5 | The shared `View` interface, `Action` and `Mode`, pulled out of stages 2–4 | the example runs through it |
|
||
| 6 | Text editor: typing, plus vim normal mode inside text | editing titles in the example |
|
||
| 7 | Outline and Note views; status bar, document bar, help, `K` card, right-click menu, Documents picker | the example with splits |
|
||
| 8 | App core: main loop, terminal setup and panic hook, buffers, splits, modes, jumps, marks, registers, files (load, autosave, reload, backups, locks, where you left off), config/theme/keys, portals, backlinks and renames, startup rules | `cumulus`: v1 |
|
||
| 9 | v1.1: Board, Search picker, `:tidy` and align, branch out, export/import | |
|
||
|
||
Each stage ends with `cargo build`, `cargo lint` and `cargo test` passing, and
|
||
one commit.
|
||
|
||
## Crates and tooling
|
||
|
||
- **Crates:** ratatui 0.30 (with the crossterm 0.29 it ships with) · serde, ron
|
||
0.12 and toml 1 · directories 6 · thiserror 2 and anyhow · arboard 3 (with
|
||
Wayland support) · unicode-width and unicode-segmentation. No testing crates.
|
||
Each crate is added in the stage that first needs it.
|
||
- **Toolchain:** `rust-toolchain.toml` pins 1.90.0 with clippy, rustfmt and the
|
||
musl target.
|
||
- **`build.rs`:** embeds the git commit for `cumulus --version`.
|
||
- **Windows first:** only key Press and Repeat events count; a panic hook
|
||
restores the terminal (raw mode, the screen, the mouse); paths come from
|
||
`directories`.
|
||
- **Same as skyshift:** edition 2024, rustfmt at 110 columns, `cargo lint` and
|
||
`cargo linux`, the static C runtime on Windows, LF line endings, `unsafe`
|
||
forbidden.
|
||
- **Doc comments** on public types and functions, written where they explain
|
||
something. (The `missing_docs` lint was dropped: it demands a comment on
|
||
every field and enum variant, which is the comment noise we don't want.)
|
||
|
||
## Tests
|
||
|
||
- **Keys:** notation, sequences, counts, registers, operator + target,
|
||
overrides, and no binding hiding another.
|
||
- **Command line:** parsing, abbreviations, completion ranking, history.
|
||
- **Model:** tree rules, undo/redo round trips, folding.
|
||
- **Canvas:** camera conversions, routing and junctions (small text snapshots),
|
||
hit-testing, nearest-node moves.
|
||
- **Files:** RON round trip, load errors, config parsing, and backlink and
|
||
rename fix-ups, in temporary folders.
|