summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/config.md17
-rw-r--r--docs/design.typ2
-rw-r--r--docs/effects.md11
-rw-r--r--docs/fs.md117
-rw-r--r--docs/mvzr-group-backtrack.md64
-rw-r--r--docs/tags.md33
6 files changed, 205 insertions, 39 deletions
diff --git a/docs/config.md b/docs/config.md
index a9db1467..40d0fb24 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -120,7 +120,13 @@ document in a column of its own. See [where new panes go](tags.md#where-new-pane
into the left column beside it, whatever it holds; `BootShell replace` closes
such a shell there when it is the column's only one and nobody has typed
into it (no scrollback, cursor still on the first prompt line), the boot's
-placeholder giving its rows to the document. Bare `BootShell` flips it.
+placeholder giving its rows to the document. Bare `BootShell` flips it, as
+bare `Placement` does: a setting that chooses among words (`on`/`off`
+switches, `Placement`, `BootShell`, `Crt`, `Lift`, `Motion`,
+`ShaderAnimation`) steps to its
+next value when written bare, as its word clicked in a tag does, and a value
+it does not take is refused with the values it takes, which `/commands` also
+lists.
`Verbose` toggles the message-row announcement every builtin makes of its own
name before it runs; it is on by default, and the builtins that own the message
row themselves (`Msg`) never announce. Turning it off leaves the row to the
@@ -545,6 +551,8 @@ Lift auto a shadow on a light page; on a dark one the others recede
Lift off
InactiveDim 30 the unfocused panes' text fades 30% toward its ground
Motion smooth off, crisp, smooth (the default), bouncy or playful
+CursorBlink on the cursor blinks, solid while typing and half a second
+ after, eased at each edge, solid after 10 idle seconds
GripWidth 150 the grip's button and the scrollbar under it, percent of the
theme's rail_px (12px at a 17px tagline; 50 to 300)
```
@@ -631,6 +639,13 @@ uses the argument as a path as before. Absolute paths and argument-free Restore
retain their existing behavior. `Dump` still honors `$PARDES_DUMP` and otherwise
writes a timestamped file in the default directory.
+A dump keeps each text pane's dot (its selection, or the caret), as acme's
+keeps a window's q0 and q1, and a Restore puts it back where the kept view
+shows it. It keeps the settings that differ from a fresh session's (`Placement
+pardes`, `Verbose off`, a shader), as the root ctl reads them, and a Restore
+sets them again; the theme is kept with the layout, and the font stays the
+frontend's. REPL bindings are not kept.
+
A restored terminal comes back live: its recorded output (the last MiB of it)
is replayed as history, a dim `── restored history ──` line marks where it
ends, and a new shell starts below it in the directory the old one was in,
diff --git a/docs/design.typ b/docs/design.typ
index a6bf56a3..639884a9 100644
--- a/docs/design.typ
+++ b/docs/design.typ
@@ -1960,7 +1960,7 @@ p/l/a`). Topbar:
`Newcol Joincol Find Grep Help Changelog Tutor Dump NextColor Debug Kill` —
editable by left click, with keyboard command navigation and Exec/Look gestures.
An additional editable tag in each column supplies local New, Tty, Find,
-Grep and Joincol commands. ColumnTags hides that row when space is tight;
+Grep and Joincol commands; it is always shown, as acme's column tag is.
Colors and Crt left it for their leader paths.
*Panes.* Terminal: ghostty-vt, 16 MiB scrollback, OSC 133 prompt semantics,
diff --git a/docs/effects.md b/docs/effects.md
index 3f375b7d..08fd3d88 100644
--- a/docs/effects.md
+++ b/docs/effects.md
@@ -23,6 +23,7 @@ why. The verdict is the user's.
| Crt (bundled post pass) | `Crt 0..3` | kept, rewritten (no barrel) | fx-compare stills, live window | kept at the user's live look |
| Ripple, Glitch | — | removed | live window | dropped by the user after a live look |
| G1 lift | `Lift shadow\|rim\|auto` (off), `InactiveDim <percent>` | opt-in until the focus-lift default is decided | lift-shots-2 (off, shadow, rim, auto on acme, dusk, forge); frame series + mp4 per Motion flavour | shadow on acme: keep as is. Round 1 dropped glow and surface (surface lowered the focused text's contrast) and made rim a hairline just above the focused tag (light on a dark page, shade on a light one). auto on a dark page recedes the others (InactiveDim 30) |
+| G3 cursor | follows `Motion`; `CursorBlink on\|off` (on) | glide on by default through the default flavour (smooth); blink on (decision 6) | .scratch/render/cursor/cursor-flavours-grid.mp4 (and quarter speed), raw/<flavour>/<motion>/ | pending |
## Motion flavours
@@ -64,9 +65,13 @@ Which principles each motion uses:
spring over the move's length; anticipation (playful). A pane past its mark
never takes a neighbour's click, a rising one stays in its box, and a notice
past its row stays in its pane's body.
-- **Cursor** (G3, next): designed around the same set: glide on the spring,
- stretch along the path, an arc on long jumps, the trailing corners as the
- secondary action.
+- **Cursor** (G3): timing (the flavour's spring, 2.4x its pace: it follows
+ input), follow-through (bouncy and playful pass the target), squash and
+ stretch and secondary action (the trailing corners lag by the flavour's
+ follow, so a jump smears and collapses), arcs (a jump over 8 cells bows by
+ the flavour's arc), anticipation (playful's wind-up). A step of a cell or
+ less, or any move in insert mode, lands at once, so typing never trails;
+ a scroll carries the cursor with the text. Only the focused cursor moves.
Notes on G1: the lift and the dim run on one focus spring per pane, at the
diff --git a/docs/fs.md b/docs/fs.md
index 27a36248..d2f35505 100644
--- a/docs/fs.md
+++ b/docs/fs.md
@@ -92,7 +92,7 @@ Existing Plan9port/v9fs clients need a userspace bridge for QUIC.
```
/README this guide, also src/fs-help.txt
-/index one line per pane: serial, kind (text|term|pdf|image), dirty flag, name
+/index one line per pane: serial, kind (text|term|pdf|image), dirty flag, name, column serial
/status pid, version and pane count
/look write a line: a right click on it at the active pane; read: the serials the last
look, exec or ctl write touched (made, else acted at)
@@ -102,16 +102,44 @@ Existing Plan9port/v9fs clients need a userspace bridge for QUIC.
write follow to that open to wait for more
/screen rendered screen JSON; frozen per open handle
/listeners the session's dial addresses
-/focus the serial of the pane with the keyboard; write a serial to give it the keyboard
+/focus the serial of the pane with the keyboard; write a serial to give it the keyboard,
+ which makes its column the active one as a click there would
/ctl the settings, one a line as a write takes them; write a setting or a session builtin
-/commands every builtin: word, `arg` if it takes one, and `root` or `pane`, the ctl that takes it
-/pane/new open it to make a pane; the read answers that pane's serial
+/commands every builtin: word, `arg` if it takes one, `root` or `pane` (the ctl that takes it), a setting's values
+/layout one line per column, left to right: serial index x width current|notcurrent
+ empty|full pane-serials...; then active <serial>: the active column, where
+ pane/new and a look place a pane next (- when there is none)
+/tag the workspace tag; > replaces it, >> appends, one line
+/col/<n>/tag the tag of the column with serial n, the same way
+/col/<n>/ctl write Delcol, Joincol, New or Tty: each acts on that column, as from its tag
+/col/<n>/exec write a word: a middle click on it in that column's tag; rmdir col/<n> closes
+ an empty column (a column with panes is refused, ENOTEMPTY)
+/pane/new open it to make a pane; the read answers that pane's serial. A session holds
+ 64 panes (16 on the board); at that, every route that would open one -- this
+ open, look, exec, New, Tty -- fails with `no pane slots: 64 max` and an err
+ record, and look reads back empty
/pane/<n>/ name body tag ctl addr dot limit data xdata sel dirty mark scroll
errors event look exec, plus pty/{ctl,status,data} on terminals
/os/ the host filesystem
/src/ the editor's embedded sources, only when built with -Dembed-sources=true
```
+`/layout`, `/tag` and `/col` go past acme, which serves no column files --
+its columns are only where a window sits. They are here so a script can see
+where the panes are (`/index`'s last word is each one's column serial) and
+edit the tags a person clicks in: a column tag is one line, a newline
+written into it a space, and a truncating write (`echo Make > col/3/tag`)
+clears it and drops the newline that ends it, as a pane tag's does. A
+column is named by its serial, as a pane is: it stays while the column
+lives, whatever opens or closes beside it, and is never reused; /layout
+gives each column's serial and its index left to right, and the log says
+`newcol <serial>` and `delcol <serial>` as columns come and go, and after a
+Restore `restoredcol <old> <new>` for each column as `restored` does for
+panes. Joincol folds a column into the one on its right, which keeps its own
+serial and tag; the joined one's serial is gone (`delcol`). The root ctl
+takes no column word: its `Delcol` is refused, pointing at
+`col/<serial>/ctl`.
+
Control messages are split by what they act on, as acme keeps window verbs
on a window's ctl and webfs and upas/fs keep session settings on a root ctl.
Each builtin declares its scope in src/builtins.zig (`scope = .session`;
@@ -119,9 +147,11 @@ every setting is one, the rest act on a pane). `/ctl` takes the session's
builtins, one a line, at whichever pane has the keyboard as each runs --
`Newcol`, `Dump`, `Mount name dial`, `Theme ink`, `Verbose off`; `Exit`,
which quits the editor as acme's does (it refuses once, naming each pane with
-unsaved text, `<name>: Modified (Exit again to discard)`, and a second
-`Exit` with nothing edited since quits, throwing that text away; a scratch
-under 100 bytes is not asked about; `Restore`, which replaces every pane,
+unsaved text, a `+New` scratch of 100 bytes or more too, in one line,
+`<name>, <name>: Modified (Exit again to discard)`, and a second `Exit`
+with nothing edited since quits, throwing that text away; a scratch or a
+command's output under 100 bytes is not asked about, as acme's winclean
+asks about no small unnamed window; `Restore`, which replaces every pane,
asks the same first -- `Dump` writes `pardes-<date>-<time>.zon` (UTC) in
`DumpDir` (default `$XDG_DATA_HOME/pardes`, else `~/.local/share/pardes`)
and logs `dump <path>`, and `Restore` with no path takes the last one; a
@@ -151,14 +181,18 @@ in a shell running without job control (`set +m`) the job shares the
shell's group, so there is none to signal: Kill says `Kill: no job to
signal`, and a write of it to `ctl` fails with that; with nothing running
it says `Kill: nothing running`; and, of a typed line, only the
-foreground job, so of `sleep 30; echo done` typed at a prompt the `echo`
-still runs once the `sleep` is stopped --
+foreground job, after which what the rest of the line does is the shell's
+affair: of `sleep 30; echo done` typed at a prompt, fish goes on and runs
+the `echo`, and bash abandons the line, as each does when a job it waits
+on is killed --
and reads every setting there is, one a line, in the words a write of it
-takes (`Verbose on`, `WindowOpacity 70`, `PanelSlide off`, `DumpDir` bare
-for the default directory, `LocationsConfig ...`), so writing what it reads
-back changes nothing; platform and startup facts are `/status`'s and the
+takes (`Verbose on`, `WindowOpacity 70`, `PanelSlide off`, `DumpDir
+<the directory in effect>`, `LocationsConfig ...`), so writing what it reads
+back changes nothing; a setting the frontend cannot show (`Lift`,
+`GripWidth` on a terminal) is refused as `Lift is GUI-only`; platform and startup facts are `/status`'s and the
Config window's, not settings. A pane's `ctl` takes the builtins that act on
-a pane (`Del`, `Save f`, `Collapse`, which folds that pane, `Undo` and
+a pane (`Del`, or `Del k`/`Del j` to give its rows to the pane above or
+below, `Save f`, `Collapse`, which folds that pane, `Undo` and
`Redo`, which step its body through its edits, `Find pat`)
beside acme's `get`, `lock` and `unlock`. The column words are pane words
too, acting on the column that pane is in: `Delcol`, `DelAbove`, `DelBelow`
@@ -193,8 +227,13 @@ performed what it asked for (a save written, a shell started). A click on
the same word, or the word written to `exec`, still opens its prompt.
`/commands` lists every builtin the registry holds, in registry order, one
-a line: its word, `arg` when it takes one, and `root` or `pane` for the ctl
-that takes it, e.g. `Newcol root`, `Save arg pane`, `Verbose arg root`. It is
+a line: its word, `arg` when it takes one, `root` or `pane` for the ctl
+that takes it, and for a setting that chooses among words those words,
+comma-joined, e.g. `Newcol root`, `Save arg pane`, `Verbose arg root on,off`,
+`Placement arg root acme,pardes`. Such a setting written bare steps to its
+next value, so a two-valued one flips (`Crt`, `Placement`, `BootShell`), as
+its word clicked in a tag does; a value it does not take is refused with
+`bad value in control message; takes ...` naming those it does. It is
generated from the registry, so it is always this build's own list.
`/focus` reads the serial of the pane with the keyboard, and a serial written
@@ -307,13 +346,17 @@ terminal's while a program other than its shell holds the tty -- after
Ctrl-D the shell would run the text, so nothing is sent and the pane says
so. Still commands, whatever is bound: a word in a tag (so
the tag is how to run `make` from that file), `Exec <text>` run by name
-(typed, a 2-1 chord onto `Exec`, a `ctl` line) and a command word @`cmd` in
+(typed, a 2-1 chord onto `Exec`, a `ctl` line) -- in a `.py` pane bound to
+a REPL, `Exec print(1)` runs `print(1)` as a command, never in the REPL --
+and a command word @`cmd` in
the text, looked at or clicked (`# @`pytest -x`` in a script). A 9P `exec`
is no gesture and is never sent: a script writes to the REPL pane's
-`pty/data`, multi-line code as a bracketed paste (`\e[200~<code>\e[201~`
-then `\r`, and a second `\r` when its last line is indented: one Enter
-leaves a block open, pasted or typed; a middle click sends that second one
-itself), since line by line a blank line ends a Python block and Python
+`pty/data`, multi-line code as a bracketed paste (`\e[200~<code>\e[201~`,
+then `\r` in a write of its own once the REPL has echoed the paste --
+Python 3.13's REPL takes an Enter read with the paste as part of it, even a
+one-line one -- and a second `\r` when its last line is indented: one Enter
+leaves a block open, pasted or typed; a middle click does all of this
+itself, holding the Enter until the REPL answers the paste), since line by line a blank line ends a Python block and Python
3.14's REPL auto-indents each line typed into it. The REPL gets the text
wherever it is -- at a `pdb` or `input()` prompt too. Bindings are not dumped, so a Restore leaves none.
@@ -344,7 +387,11 @@ selection, and leaves `look` reading back empty.
`/pane/<n>/name` reads the pane's file name (a terminal's directory) and
writing it renames the buffer; a relative name resolves against the pane's
-directory. `body` appends on write and replaces on truncating open. `sel`
+directory. `body` appends on write and replaces on truncating open. A
+terminal's `body` is its history as plain text, frozen per open, in logical
+lines: a row the terminal wrapped is joined back to the row before it (the
+wrap is ghostty's, as `pty/run`'s output unwraps), and the last line ends
+with a newline. `sel`
reads the selected text and writing it replaces the selection. `errors`
appends to the directory's `+Errors` pane. Holding `event` open redirects the
pane's Look and Exec clicks to that client, and so does a line written to the
@@ -372,7 +419,9 @@ for the question the pane asks on its notice band, which the log names as
`ask <serial> <what> <choices>` -- `ask 4 del k j` for Del's side from the
keyboard (`k` the pane above takes the rows, `j` the one below), `ask 4
repl a b` for which bound REPL takes an exec (a REPL's letter) -- `answer -`
-taking it back as Esc does (with no question standing, `answer` is refused),
+taking it back as Esc does (a choice the question does not offer is refused,
+naming those it does, and the question stands; with no question standing,
+`answer` is refused),
and acme's `lock` and `unlock` (editors/acme/xfid.c:603-611), for an
edit of several writes to `addr` and `data` that another client must not
land in the middle of. As in acme the lock binds only the clients that take
@@ -496,9 +545,13 @@ then the text you may edit. A write appends to that text,
newlines included, and a tag with more than one line takes a row per line on
screen; truncating `tag` clears it, as acme's `cleartag` does -- the default
words (`Del`, `Put` and the rest) with it, since they are that text until
-you edit it, so `echo Make > tag` leaves only `Make`; append with `>>` to
-keep them, with `printf ' Make' >> tag`: `echo` ends its word with a
-newline, which starts a new line of the tag. The clearing is an edit of the tag like a typed one and its undo
+you edit it, so `echo Make > tag` leaves only `Make` -- a truncating write
+drops the one newline that ends what it wrote, which would draw an empty
+row, and keeps any other; append with `>>` to
+keep them, with `printf ' Make' >> tag`. The leading blank is needed: the
+tag reads back with no blank after its last word, so `printf Make >> tag`
+glues `Make` onto it; and `echo ' Make' >> tag` ends with a newline, which
+starts a new line of the tag. The clearing is an edit of the tag like a typed one and its undo
history is kept: `u` in the tag brings back the text it cleared, words
included.
@@ -515,16 +568,21 @@ entries carry no sizes, and neither does `/screen`, which has no length until
an open renders its frame; stat the entry.
`/log` is one ring (64 KiB) that records whether or not anyone reads it:
-`new`, `del`, `rename` (a terminal's too, as its shell changes directory,
+`new`, `del`, `rename` (a terminal's too, as its shell changes directory --
+a new terminal is `new N /` until its shell says where it is, then renamed;
+it runs `ls` once at its first prompt, a greeting that shows the directory --
since a terminal is named by its directory), `exit <serial> <N>` before
the `del` of a terminal whose shell exited by itself, `ask <serial> <what>
<choices>` when a pane asks a question (answered by `answer` on its ctl),
and `save <serial> <name>`,
`dump <path>` when a Dump is written and `restore <path>` in a Restore's
new log after its panes' `new`s, then `restored <old> <new>` for each pane,
-mapping the serial it had to the one it has now, and `msg <serial|-> <text>`
+mapping the serial it had to the one it has now, and `restoredcol <old>
+<new>` for each column, and `msg <serial|-> <text>`
for every line the editor says, repeats included (with `verbose` on, that
-includes each builtin announcing itself as it runs), and `err <serial|->
+includes each builtin announcing itself as it runs, on purpose: the log says
+which ran; its serial is the pane it ran at, `-` when the keyboard was on a
+column or workspace tag), and `err <serial|->
<file>: <why>` for every write or truncation the tree refused or that
failed -- through a mount a shell sees only the errno its kernel mapped the
reply to, usually `Invalid argument`, and this is the reason (`err 3 addr:
@@ -547,7 +605,8 @@ Control characters in a record become spaces, so a record is one line.
(An `event` record is not: acme's `<origin><action><q0> <q1> <flag> <n>
<text>\n`, whose text may hold newlines. The origin is `E` (a 9P write to body or tag), `F` (other
files, the editor's own lines), `K` (the keyboard) or `M` (the mouse); the
-action's case says where: `x`/`l` a click executed or looked at in the tag,
+action's case says where: `x`/`l` a click executed or looked at in the tag
+(its offsets count the tag's whole text, path included, as `tag` reads),
`X`/`L` in the body, `I`/`D` text put in or taken out of the body, `i`/`d`
of the tag. Read `n` bytes of the text, not up to a newline -- bytes here, where acme counts runes. Every offset and
count pardes serves is in bytes, `#n` and `q0`/`q1` too; the event count
diff --git a/docs/mvzr-group-backtrack.md b/docs/mvzr-group-backtrack.md
new file mode 100644
index 00000000..d074af62
--- /dev/null
+++ b/docs/mvzr-group-backtrack.md
@@ -0,0 +1,64 @@
+# mvzr: a repeat inside a group never gives back to what follows the group
+
+A report for upstream (https://github.com/mnemnion/mvzr), against trunk
+3efcfe3 (0.3.9). pardes patches its fetched copy at build time (build.zig)
+until it is fixed there.
+
+## What happens
+
+```zig
+const mvzr = @import("mvzr");
+// a+ab matches aab; the same with the repeat in a group does not
+std.debug.assert(mvzr.compile("a+ab").?.match("aab") != null);
+std.debug.assert(mvzr.compile("(a+)ab").?.match("aab") == null); // expected a match
+```
+
+The same for `(a*)ab` on `aab`, `(.+)_area` and `([a-z_]+)_area` on
+`the_total_area`. `\w+_area` and `(\w)+_area` match, as does
+`([a-z]+)_area` (whose class cannot eat the `_`, so no giving back is
+needed).
+
+## Why
+
+`hasAlt` returns true when its scan reaches the group's own closing
+`.right` at depth 0:
+
+```zig
+.right => {
+ if (pump == 0)
+ return true // the group's own `)`, not an alternative
+ else
+ pump -= 1;
+},
+```
+
+so every group with more pattern after it goes down `matchGroup`'s
+alternatives branch. That branch takes each alternative's single match (a
+greedy repeat's longest) and, when the pattern after the group fails from
+there, moves on to the next alternative; it never tries a shorter match of
+the same alternative. The alt-free branch has the same shape.
+
+## A fix
+
+Where the pattern after the group fails from an alternative's match, try
+that alternative's shorter matches, longest first, before the next
+alternative. pardes's patch, in `matchGroup`'s loop:
+
+```zig
+} else { // Try our next pattern, if any.
+ var e = m1.i;
+ while (e > i) {
+ e -= 1;
+ const shorter = matchAlt(remaining_patt, sets, haystack[0..e], i) orelse continue;
+ if (shorter.i != e) continue;
+ if (matchPattern(next_patt, sets, haystack, e)) |whole| return whole;
+ }
+ remaining_patt = m1.j;
+}
+```
+
+Matching the alternative against `haystack[0..e]` makes `$` and `\b` see a
+string end at `e`, so a group ending in either can accept a cut the full
+string would not; a continuation-passing matcher (each repeat trying the
+rest of the whole pattern) would be the full fix. `hasAlt` returning false
+at the group's own `)` is worth fixing too.
diff --git a/docs/tags.md b/docs/tags.md
index aa680c48..bfca4f0e 100644
--- a/docs/tags.md
+++ b/docs/tags.md
@@ -30,6 +30,15 @@ edge will land, beside the rule as a border drag's rail does. Only columns
between the old and new positions shift. Each column keeps its width, panes and
command text.
+Two panes in a column are resized as acme's windows are, by the grip: drag
+a pane's grip up or down its own column and its top follows, the pane above
+taking or giving the rows, either one down to its tag alone (acme's
+coldragwin keeps a window its tag line); drop it in another column and the
+pane moves there. In the GUI the 2 px rule between two panes also drags
+their seam. No row of text is a handle: a pane's last row takes a click like
+any other, and a terminal, which draws no rule, resizes by the grip. A
+column's right edge still drags its width.
+
When Look opens the first document and its source column is too narrow for
a new column, it splits below the originating pane, just like `Tty`, instead
of inserting at the top of the leftmost column. The originating terminal is
@@ -53,14 +62,23 @@ if every command ran; an error says why in acme's words and changes
nothing. `p` and `=` print to the directory's `+Errors`. Left out: the file
commands `b B D e r w f X Y`, the pipes `< | >`, and `\1`-`\9` in `s`,
since mvzr keeps no submatches; acme applies changes that come out of
-sequence with a warning, pardes refuses the Edit.
+sequence with a warning, pardes refuses the Edit. In `s`, `&` in the
+replacement is the matched text (`\&` a plain `&`); in `c`, `a` and `i` an
+`&` is only an `&`, as in sam. A pattern that finds nothing says so with the
+pattern (`no match for regexp /nomatch/`), and an `s` on an empty dot says
+`no substitution: dot is empty`.
`Undo` and `Redo` are acme's: typed or clicked in a pane's tag, or written
to its `ctl`, they step the body back and forward through its edits, as the
`u` and `U` keys do. They are not in the default tags.
`Del` closes a pane and gives its rows to one neighbor; the rest of the
-column keeps its heights. `Del k` (or `DelAbove`) gives them to the nearest
+column keeps its heights. A pane with unsaved text is refused once, as
+acme's Del warns (exec.c del, wind.c winclean): `<name>: Modified (Del again
+to discard)`, and the same `Del` again, nothing edited since, closes it and
+throws the text away; `Delcol` refuses a column holding such a pane the same
+way (exec.c delcol). Each word warns on its own, from a key, a tag, `exec`
+or a ctl, where the refusal fails the write with EIO. `Del k` (or `DelAbove`) gives them to the nearest
expanded pane above, `Del j` (or `DelBelow`) to the one below, each falling
back to the other side. Bare `Del` from the keyboard (`SPC d`, or Enter on
the word) on a pane with expanded panes both above and below asks on the
@@ -74,8 +92,10 @@ A tag is a text like a pane's body: pane tags, column tags and the
workspace tag all edit with the body's own normal and insert modes, undo
included. A pane's tag may hold more than one line, and a line wider than
the pane wraps onto more rows, as acme's tag does: it takes a row per shown
-line, up to eight, and leaves its body at least one row. A word the wrap
-breaks across rows is still one word to a click. A collapsed pane shows
+line, up to eight, and leaves its body at least one row. A line exactly as
+wide as the pane stays one row, its caret at the tag's edge past the last
+character, as acme's tick is (in a terminal, on the last cell). A word the
+wrap breaks across rows is still one word to a click. A collapsed pane shows
only the first row.
A pane tag starts expanded, showing all its rows, and can be collapsed to
@@ -183,7 +203,10 @@ the shared bold, underline, and strikethrough attributes, including diagnostic
underlines and the optional `SyntaxBold` keyword weight.
Unsaved text shows on the pane's grip button, as acme's modbutton does,
-not in the tag: the tag names the file and nothing more. The `dirty` file
+not in the tag: the tag names the file and nothing more. In a terminal the
+grip is two cells: the second is `*` while the pane holds unsaved text, and
+the first is the pane's mode: blank in normal mode, `^` in insert, `$` when
+the keyboard goes to a terminal's program (tty mode). The `dirty` file
and `index`'s flag say the same to a script.
## Empty columns