The first small part of stage 9 (v1.1). <Space>= then m, c or g (the
user's pick: leader menus), or :tidy map|chart|grid, lays out the
cursor's branch while its top node stays put: a mind map puts each node
in the middle of its children (the user's pick), an org chart puts them
side by side below, and a grid makes a block of cards beside the node
(the user's pick), a card's own branch going along as a mind map. Every
node of the branch gets a place, folded away or not (the user's pick),
as one undo step, and the message says when the branch now covers nodes
outside it.
The lines follow the layouts: a parent whose children are all below it
sends its branches down to a shared bend, and a new child of it goes
beside the last one; a grid card behind another isn't joined by a line,
which would look like it joins the two cards.
In visual mode, <Space>a then h, l, k, j or c lines the selection up on
the box around it, each node bringing its branch along, and : opens the
command line on the selection: :align and :tidy take it, and so do
:color, :border, :style, :tag and :fold, as one undo step. The layouts
are pure functions in view::canvas::arrange, applied as one
EditOp::Reposition. Layout and Side moved from the parser to the model
so keys can name them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The rest of vim, on nodes. Visual mode takes in what lies between where
it started and the cursor: on the canvas, every node the rectangle from
one end's box to the other's touches; in the outline and the Note view,
the nodes in between in reading order. V takes whole branches, Ctrl+click
and Ctrl+drag pick with the mouse, and the operators take exactly what's
selected: d, y, > and <, M, and the pickers on every node at once.
Registers now hold text or nodes, so yy, dd and x keep what they take and
p, P and ]p paste it, layout and links included. "+ is the system
clipboard through arboard: nodes go there as Markdown in the user's
format (## headings, a --- tag list, the body), and come back whole while
the clipboard still holds them. clipboard = "unnamedplus" makes plain
yanks use it, as in vim.
. repeats the last change made with keys, with the text typed after it.
/ opens a Telescope-style search: fuzzy matches over titles, tags and
bodies, best first, with the note of the one you're on beside them; n
and N go on with it, and :find searches straight away. Each pane keeps a
jump list for Ctrl-o and Ctrl-i that crosses documents and reopens closed
ones. Capital marks lead into any document and, like the small ones, are
kept in memory.ron. gd follows a cross-link, and :marks, :jumps and
:registers list what they hold.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The playground becomes app::App, and `cumulus` runs it: the main loop
with terminal setup and a panic hook, and the startup rules (./ideas.cum,
else ~/ideas.cum, made the first time; files and folders by name;
`cumulus new`; --paths).
The documents' files:
- autosave a second after the latest change (`:set noautosave` for
vim-style :w), [+] in the document bar for unsaved changes; quitting
saves first; without autosave :q refuses, :q! drops, ZZ saves first;
- a file changed outside cumulus is read again as an undo step; with
unsaved changes here too, cumulus asks which to keep; a file with a
mistake in it holds autosave until it's fixed or :w saves over it; a
deleted one waits for :w; nothing is reloaded under an edit;
- the day's first save backs the file up to the data folder (30 days);
- a document open in another cumulus is read-only ([RO]) until that one
lets go: OS file locks in the data folder, so a crash leaves none;
- where you left off (cursor, zoom, scroll), the recent documents (now
in the documents picker) and the command history, in memory.ron;
- :e (alone: read it again; ! drops changes), :new, :bd, :w, :wa, :x,
:qa, <Space>i for :in, and :set for every setting. glyphs=ascii now
reaches the frames and bars too, not just the canvas and outline.
Stage 8 comes in four parts with a commit each (the user's choice); 8b
is the settings files. The example is now a playground running the app
on copies of the sample documents in a temporary folder, and the
end-to-end tests moved to src/app/tests.rs, with tests for the files.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The canvas example did everything itself; now it runs the way the app
will. The key engine turns keys into commands, the focused view answers
with actions, and the commands every view shares (undo, marks, adding,
deleting, folds, tree moves, modes) answer the rest. A view can still
give a key its own meaning, since it gets the first say. `:` lines become
actions the same way (app::calls).
- View trait: kind, contexts (its key tables), layout, render, command
and mouse. layout comes before render because a view keeps the frame
it drew, to match clicks against, and follows the cursor with its
camera; drawing changes nothing. CanvasView owns the camera, the last
scene and the mouse drag; it reads the rest from a ViewContext.
- Edits travel as data (EditOp), so views never touch the document.
Buffer::apply is the one place a document changes: the edit, its undo
step, where the cursor goes, marks and `''`.
- Mode holds each mode's own state: move mode carries its Grab (whole
branch or not), the command line lives in Mode::Command.
- Undo groups: a whole move-mode session or mouse drag is one undo step,
built up as it goes. After an undo the cursor lands on a node the
change added or removed, before the others it touched.
- Actions run in order, and a problem stops the rest.
Small improvements on the way: zz centers the middle of the node, :find
says which match it's on, :unlink with no links says so, folding a node
without children says why nothing happened, and a captured idea is
placed next to a new Inbox even on the first :in.
The example has end-to-end tests now: they type key sequences and mouse
gestures into it on a test terminal, and `cargo test` runs them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`:` opens a vim-style command line. Every command is described once, in
a typed table (name, the short form that still works, aliases, a `!`,
its arguments and what it does), so parsing, completion and the help can
never disagree. Short forms work as in vim (`:e`, `:vs`, `:q!`, `:x`),
and a test checks that no typed name can mean two commands.
Parsing turns a line into a typed Call, checking every argument against
what the app offers (open documents, nodes, colors, styles, settings),
or says what's wrong in words: the usage when an argument is missing,
the choices when a word isn't one, "did you mean :fold?" for a typo.
Documents and nodes match fuzzily (`:b gpl` finds Garden plans).
While typing, a popup above the word lists what can finish it (command
names by prefix, choices, files from the folders on disk, documents and
nodes fuzzily), with Tab and Shift-Tab putting them in the line. Ghost
text after the cursor comes from the history first, like fish's, then
from the best candidate; → or Ctrl-e takes it. Up and Down browse the
lines that start with what was typed. The line itself is a small input
that steps over whole characters, accents and emoji included.
The canvas example opens it with `:` and runs a handful of commands:
:fold, :color, :border, :tag, :focus, :rename, :in (to an Inbox),
:link, :unlink, :find, :version and :q.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Keys now work the way vim reads them. A key press becomes a Key (with vim
notation for writing keys: `<C-w>v`, `<leader>c`, `<<`), and the engine
collects counts (`12j`), registers (`"+p`), sequences (`gg`), operators
with their targets (`dd`, `dab`, `2d3d` multiplies to 6) and keys that take
a character (`ma`, `'A`), then hands over a typed Command. Every command
has a name and a help text in one table, and the default keys live in one
table per place they work (motions, normal, canvas, visual, move, connect,
lists).
keys.toml changes keys per section, with the leader configurable and
"nothing" to unbind. Each mistake is skipped and reported in words, with a
suggestion for mistyped command names, and a key that would hide a longer
one is reported too. The which-key popup reads the same tables: it shows
what can follow the keys typed so far, in skyshift's look and place, with
the leader menu instant and other prefixes after 300 ms. The mouse reader
turns button events into presses, double-clicks, drags and scrolls.
`Direction` moved from the glyphs to the model, since input needs it too.
The canvas example now runs on all of this, with a two-line keys.toml of
its own that makes `q` quit.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The canvas draws a frame in three steps. Scene::build works out what's
shown and where: folding decides which nodes show, boxes are sized from
their wrapped titles (with tags and a +N badge in the bottom border), the
camera turns canvas positions into screen cells, and every branch and
cross-link gets a right-angle route. The line layer records which arms
meet in each cell, so borders and lines join with the right box-drawing
character (├ ┼ ┨ ╭), in any mix of light, thick, double, dashed and dotted.
Rendering then writes lines, arrowheads and labels, and the nodes on top.
Branches leave sideways and bend near their parent, so siblings share one
bus; cross-links take the wider gap and carry their label on the longest
level run. Far zoom keeps the full width and halves the height (one line
per node), Overview shows dots. hjkl picks the nearest node within 45° of
the direction, and new nodes find a free spot next to their parent or
sibling. The theme works out looks in layers (default, tag style, named
style, the node's own), with a mid-tone palette for dark and light
terminals, and an ASCII fallback for fonts without box drawing.
`cargo run --example canvas` opens a sample document to move around,
zoom, fold, edit and drag with the mouse. The tests compare rendered text
against the DESIGN.md mockup.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The model holds the rules: a document is a tree under one root, with
loose nodes beside it and cross-links between any two nodes. Every change
runs as a transaction that rolls back on error and returns an undo step
(the old and new values of whatever it touched), so undo and redo need no
code of their own per command. Text from outside (titles, tags, names) is
checked once, when it becomes a typed value. Folding (Open, Auto, Closed,
plus focus) is one function that every view will share.
storage::document_file reads and writes `.cum` files in RON, with
multi-line bodies kept readable, `\n` line endings on every OS, and errors
that name the node they're about. The tests cover the tree rules, 500
random edits undone and redone step by step, folding, and the file format
(the example from DESIGN.md is written back character for character).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
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]>