Add connect mode, portals, backlinks and renames (stage 8d, v1)

Part 8d, the last of stage 8, and with it everything on DESIGN.md's v1
list.

Connect mode (C): the cursor goes to another node, a dashed arrow in the
visual color follows it, and Enter links the two; then the cursor goes
back to where C was typed (the user's pick). d removes the line between
them (a branch is cut, and the child comes loose), > puts the node under
the other one, and <Space> a, l, c, s and t style the line as you watch,
making it when it isn't there yet. Everything connect mode changes is one
undo group, and Esc takes it back with History::cancel, which leaves
nothing to redo. From visual mode every selected node connects, and the
line pickers go back to connect mode when they close.

Portals show their document live, from a cache of what each document
file holds (open documents from their buffers, the others from their
files, looked at every second): the root's title, its color and how many
nodes it has, or ? when the document isn't there. Enter or gf goes
through, as a jump, and the pane's breadcrumb starts with the documents
it came through. <Space>p types :portal (the user's pick): a path as :e
takes it, or the title of a new document. Backlinks are put right
whenever a document is opened or saved; the root shows how many, the
Note view lists them, gu goes up to them (the user's pick) and
:backlinks lists them.

File names follow the root's title: when it changed, the next save
renames the file (-2 when the name is taken; a folder's default document
keeps its name), and the portals pointing at it, its backlinks in the
documents it leads to, the recent documents, the capital marks and the
jump lists follow. A portal whose file was moved by hand finds its
document in that folder by uid. Portals in a register keep full paths,
so a pasted portal leads to the same document from any folder.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
skkeyeandClaude Opus 5.5 committed 2026-10-02 08:24:43 -04:00
1 parent bd819409f4
commit ebe8a5988e
45 files changed
+2329 -148

No files matched your search

+65 -10
View File
@@ -197,6 +197,58 @@ lines.
beside the node. `:marks`, `:jumps` and `:registers` list them; Enter goes
to the mark or the place, or pastes the register.
**Connect mode and portals** (settled in part 8d)
- `C` starts connect mode on the cursor's node: the cursor goes to another
node as usual (`hjkl`, `gh`…, `n` after a search, a click), the node
being connected is drawn thick in the visual color, and a dashed arrow in
that color follows the cursor. Enter links them (a line already there
stays) and connect mode is done, with the cursor back on the node `C`
started from (the user's pick). `d` removes the line between them: a link
goes; a branch (aimed at the node's parent, or at a child) is cut, and the
child comes loose with its own branch. `>` puts the node under the one
aimed at, as its last child, where it is on the canvas (a link between the
two goes: the branch takes its place).
- The `<Space>` keys style the line as you watch: `a` and `l` go through the
arrowheads (off, end, start, both) and the kinds of line (solid, dashed,
dotted, thick, double); `c` and `s` pick a color (or the node's own) and a
named line style; `t` a link's label, where typing a label that isn't
listed makes it. A line that isn't there yet is made, as a link. Everything
connect mode changes is one undo step, and Esc takes it all back.
- From visual mode, `C` connects every selected node (with `V`, each
branch's top) to the one the cursor goes to. Connect mode works in every
view, and stays in its document.
- A portal shows its document live: the root's title, its color (when the
portal has none of its own) and how many nodes it has, in the box's corner
(`?` and faded when the document isn't there). The portal remembers the
title, in case the file goes. Enter or `gf` goes through it, to the root:
a jump, so `Ctrl-o` comes back. The breadcrumb then starts with the
documents the pane came through (`Garden plans » Kitchen › Herbs`);
going back up one of them takes the trail back there.
- `<Space>p` opens the command line with `:portal ` typed (the user's
pick): a path as `:e` takes it, relative to the document's folder, with
the same completion. An existing document gets a portal under the
cursor's node, where `a` adds a child; when nothing's there, what was
typed is a new document's title, made next to this one as `:new` makes
it. A portal to its own document, or to a file that isn't a document, is
refused.
- Backlinks are put right whenever a document is opened or saved: each
document its portals lead to gets exactly one backlink per portal (an
open one in its buffer, written at once unless it has changes of its own
to save with them; a closed one in its file, unless another cumulus has
it). The root shows how many (`« 2`), the Note view lists them on the
root, and `gu` goes up to them (the user's pick): one straight there, on
the portal, several in a list, the document the pane came from first.
`:backlinks` always lists them.
- File names follow the root's title: when the title changed since the
file was read or written, the next save renames it (`-2`, `-3`… when the
name is taken; a folder's default document keeps its name). The portals
pointing at it take the new path (in the documents its backlinks name,
and any open one), as do the backlinks in the documents it leads to, the
recent documents, the capital marks and the jump lists.
- A portal whose file isn't where it says looks in that folder for its
document by uid (a file moved or renamed by hand), and keeps the path it
finds.
**Views, panes and popups** (settled in stage 7)
- The Note view is made of parts: the title, the tags, the body, each link
(out, then in) and each child. `j` and `k` step through them, with a bar
@@ -406,7 +458,8 @@ enum Mode {
// two can't disagree. Rapid entry lives here too.
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
Connect(Connect), // the nodes being connected, where the cursor goes
// back, and the undo group Esc takes back
Command(CommandLine), // `:` with completion
Popup(Popup), // the help, the menu, a picker or the K card (which
// holds its own TextEdit while the body is edited)
@@ -420,12 +473,12 @@ 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 |
| **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 · `gu` up to the portals that lead here · `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` paste as children · `P` paste as siblings below · `>>` `<<` 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 |
| **Leader** `<Space>` | `c` color · `s` style · `B` border · `t` tags · `p` new portal (`: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 w` the next pane · `Ctrl-w c` close · `Ctrl-w o` only · `Ctrl-w =` even sizes · `gt` `gT` `]b` `[b` next / previous document · `ZZ` close pane (the last one quits) |
| **Outline** | `j` `k` rows · `h` `l` parent / first child · `zb` every body · `M` then `j` `k` (`J` `K`: 5) swap with siblings, `h` out, `l` under the one above |
| **Note view** | `j` `k` parts · Enter `i` `A` edit the part (`I` from its start, `cc` `S` anew), or go to that link or child · `h` `l` the sibling before / after |
@@ -434,7 +487,7 @@ text).
| **Typing** | `Backspace` `Delete` · `Ctrl-w` delete word · `Ctrl-u` delete line · arrows, Home, End · Enter (title: done, or after `o O a` the next idea; body: new line) · Tab Shift-Tab (a new idea: under the one above / out a level) · Esc to vim keys |
| **Inside text** | `h j k l w b e 0 ^ $ gg G` (and arrows, Home, End) · `f t F T ; ,` · `d` `c` `y` with any motion and `iw aw i" a" i( a(` · `x X s S D C Y` · `r ~ J p P` · `u Ctrl-r .` · `i a I A` back to typing · `o O` (title: a new sibling; body: a new line) · Esc done |
| **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 |
| **Connect mode** | move to the target as usual (or click it) · Enter link · `d` remove (a branch: cut) · `>` attach as child · `<Space>` style menu (`s` named, `a` arrows, `l` line, `c` color, `t` label) · Esc cancel (takes it all back) |
| **Visual** | `d` `x` `y` `>` `<` `M` and `<Space>c` `<Space>s` `<Space>B` `<Space>t` act on the whole selection · `o` swaps ends · `v` `V` switch, or stop · Ctrl+click, Ctrl+drag pick with the mouse |
## Files
@@ -616,9 +669,9 @@ cumulus/
│ command_line, theme, glyphs
├─ config/ paths (the config and data folders), settings (config.toml, `:set`), keys_file
│ (keys.toml's template), theme_file (themes laid over the built-in one)
├─ storage/ document_file (RON), write_atomically, names (file names from titles), backup,
│ lock, stamp (noticing changes made outside), memory (memory.ron), markdown
│ (nodes as Markdown, for the clipboard)
├─ storage/ document_file (RON), write_atomically, names (file names from titles), link_paths
│ (portal paths: relative, with /), backup, lock, stamp (noticing changes made
│ outside), memory (memory.ron), markdown (nodes as Markdown, for the clipboard)
└─ app/ mod.rs (App), input (keys and the mouse, routed), update (carrying actions out),
render, terminal (the main loop, setup, teardown, panic hook), startup (what
`cumulus` opens), documents (the open documents and their files), files
@@ -628,12 +681,14 @@ cumulus/
edited, rapid entry), registers (text or nodes), clipboard (the system's, for
"+), yank (nodes into registers and back), visual (selections), repeat (`.`),
search (`/`), jumps (each pane's jump list), marks (capital marks, `:marks`),
panes (the split tree), popup (help, menu, pickers, card, questions); later
portals (cache, backlinks, renames)
connect (connect mode), portals (what portals show, going through them,
`:portal`, backlinks, renames), panes (the split tree), popup (help, menu,
pickers, card, questions)
```
The end-to-end tests are in `app/tests.rs` (with `app/tests/files.rs`,
`app/tests/config.rs` and `app/tests/vim.rs`): they copy the sample
`app/tests/config.rs`, `app/tests/vim.rs`, `app/tests/connect.rs` and
`app/tests/portals.rs`): they copy the sample
documents to a temporary folder, type keys and click on a test terminal, and
check the screen, the documents and the files.