summaryrefslogtreecommitdiff
path: root/src/pardes.zig
Commit message (Collapse)AuthorAge
* Newcol shares a column's rows out again when its narrower tags would leave a ↵Gabriel Schneider45 hours
| | | | | | | | pane under its tag and two rows, or is refused A seeded sweep of placements (New, pane/new, Edit =, Tty, look, Delcol, deletes, refusals included) over screens 6 to 40 rows high found no refused placement that leaves a pane short since lyltlqnz and lrpzwyom; the pane a row short came from Newcol, which halves a column's width so long tags wrap onto more rows, raising those panes' minimums with no rows moved. Newcol now shares each such column's rows out again, and where a column has not the rows for every minimum there is no new column (Newcol: no space for a column: the panes' tags would not fit), as a size too small is refused. The sweep, 300 seeds, is the test; a user's drag or a tag grown by editing may still take a pane under, as before. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Only a write logs an err: a refused open or truncation, create or remove, or ↵Gabriel Schneider45 hours
| | | | | | | | a write to pane/new, is its error alone, and every refusal is said in words The one-failure rule is a write's. A refused remove, pane/new's open refused for want of a slot, and an OTRUNC open's refused truncation (data's after a failed addr) also logged an err, and a reply carrying only an errno reached the client as the C library's text (Operation not permitted). tree.handle now gives such a reply Plan 9's words, chosen so 9ns maps each back to its errno (EPERM's to EACCES, as Plan 9's does), and serveFs logs errs for writes only, not a write to pane/new, which is only read. docs/fs.md and the 9P skill say so, and that a look miss quotes what was written. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Writes held before the shell's clock is first seen no longer overflow its ↵Gabriel Schneider45 hours
| | | | | | | | first step A 9P write served before a detached host's first pump holds its batch stamped at time 0, so nextWake said to wake at 20 ms while advance, seeing the clock for the first time, set stepped_ns days later and counted due - stepped_ns back past zero: an integer-overflow panic. It was what the 64 KiB msize trial hit in fs-test (restore detached space, the connection closing at the first write's clunk), a race any msize can lose. A batch's wake is now never before the next frame. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A write to addr, limit or mark asks for no frame, so its close is not held ↵Gabriel Schneider45 hours
| | | | | | | | behind one Every write that changes a pane set needs_frame, an addr write too, so the editor drew a whole frame after each and the clunk that followed waited for the turn while it did: 0.5 ms (Debug) to 1.2 ms a clunk, what made addr and event write-back 4-10x acme's. An address, a limit and the mark flag are nothing drawn; their writes no longer ask for a frame. Clunk after an addr write: 0.518 -> 0.015 ms (Debug tty, fs.py client). Co-Authored-By: Claude Opus 5.5 <[email protected]>
* An open's writes in a row to one place go in as one edit: a 10 MB body write ↵Gabriel Schneider45 hours
| | | | | | | | is linear Each body or data write copied and hashed the whole buffer, so a write the mount cut in 8 KB pieces was quadratic: 10 MB took 50 s and a 1 MB insert into 10 MB 11 s, holding the editor's turn. An open's appends to body, or inserts going on at data's address, are now held and put in as one splice (one copy, one undo step, one line-starts pass) before any other request, the close, or the editor's step once the writes pause 20 ms. fs.py-driven: 2 MB 2.19 -> 0.16 s, 10 MB 50.17 -> 0.78 s, 1 MB data into 10 MB 11.49 -> 0.26 s. A body-2m case (2 MB in 256 KB writes on one open) joins the perf gate: 84023 -> 15785 us; baselines re-recorded. Co-Authored-By: Claude Opus 5.5 <[email protected]>
*-. Merge: 9P round 23 + perf fixes + input garbage fixes + Crt docGabriel Schneider45 hours
|\ \
| | * Function keys, Insert and the keypad reach a terminal pane as their terminal ↵Gabriel Schneider45 hours
| |/ | | | | | | sequences
* | Delcol on the last column leaves the window, empty but for its workspace ↵Gabriel Schneider45 hours
| | | | | | | | | | | | | | | | tag, as acme's does The last column's Delcol ended the session. The window now stays, with only the workspace tag; Newcol, and New (which makes the column it goes in), start it again. A word run with no pane at all runs with a stand-in that is no pane, the root ctl takes session words, and pane/new makes its scratch in the session's directory. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | Back at the oldest place says nothing further back, and Forward at the ↵Gabriel Schneider45 hours
| | | | | | | | | | | | | | | | newest nothing further on A step past either end of the jumplist did nothing, silently. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | Not-found failures say it once, in plain words: grep: pattern not found, ↵Gabriel Schneider45 hours
| | | | | | | | | | | | | | | | callers not found The ENOENT that 9ns reads from a failure's words came from a tacked-on suffix, grep: no match, not found, definition: nothing found, not found, no shell "x", not found. They now say it once: grep: pattern not found, find: name not found, Callers: callers not found, definition: not found, shell "x" not found (...). Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A new file, once saved, shows in /recent, openGabriel Schneider45 hours
| | | | | | | | | | | | A pane named for a file not yet on disk is not kept in Recent (round 22), and its Save did not add it, so the file stayed out of /recent while open. A Save that puts a file on disk now records it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A Tty started by a shell's path keeps the path in its Tty+ wordGabriel Schneider45 hours
|/ | | | | | The tag said Tty+<basename>, so a click on it looked the bare name up on the usual paths and found nothing, or another program. A shell given as a path now keeps the path in the word. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* The theme's chrome is worked out once, not per grapheme; still post passes ↵Gabriel Schneider45 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | let the GUI rest; a perf gate somtrmsz's contrast floors made ChromeTheme.fromTheme run its focus-tint and separator searches (pow calls each), and Output's RowDecoration.styleAt called it for every grapheme of every highlighted row: the 50k-line file's render went from 0.11 ms to 13 ms. Pardes.bodyChrome now keeps the theme's chrome, worked out again only when the theme differs; recolorSyntax asks for it once a decorated row, and a plain row (nearly every row of a file) never asks. ReleaseFast, medium fixture, median us, before -> after (main): render 13150 -> 76 (111), key-down 14658 -> 79 (110), wheel 13589 -> 76 (109), open 14891 -> 2095 (2055), edit-char 16703 -> 3006 (1251; the rest of that gap is editing and tree-sitter, not this). Post.animating asked for frames whenever the window had focus and any pass was ready, so a still pass kept the GUI drawing at the display's rate (Bloom: 49% of a core idle, in a hidden test window). A pass now says whether it moves on its own: the CRT (its hum and dither) and a Shadertoy file whose source reads iTime, iFrame or iDate (shader_build.readsTime; the flag rides the wire's post message); Bloom, Vignette and Grain are still. Bloom idle: 49% -> 1.2%, as with no pass. zig build perf-gate: the 50k-line file's gestures, each's fastest sample within 3x of the recorded baseline's (test/perf-baseline-<platform>- <optimize>.json, recorded from this build), run with every unit-test. On the regressed code it fails at 180x for render.
* A Jumplist entry is (closed) only while no pane has its file openGabriel Schneider45 hours
| | | | | | A closed file's entries stayed closed after the file was opened again another way (a look, Recent), so Jumplist said (closed) of an open file and a step onto it reopened it. They are now that pane's as soon as a pane shows the file. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* +Unsaved drops a pane once it has closedGabriel Schneider45 hours
| | | | | | The list kept a pane's Modified row after the pane was closed (discarded), so it named text there was no longer anything to lose of. A pane's closing now takes its row out. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* The workspace tag's Restore word names the last dump, and the dump a Restore ↵Gabriel Schneider45 hours
| | | | | | | | came from Once the workspace tag was edited, its own text kept the Restore <path> of the dump at that time, stale after the next Dump; and a Restore dropped the path altogether (the new session had no last dump). A new dump now swaps the old path in the tag's own text, and a Restore points the word at the dump it read. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Back or Forward over a closed file deleted since skips it and goes on, saying soGabriel Schneider45 hours
| | | | | | A jumplist entry for a closed file no longer on disk tried to reopen it, failed as a look, and left the step stuck there. Its entries now go, the message row says <path>: deleted, skipped, and the same step is taken over what is left. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A language server question that finds nothing, cannot be answered or has no ↵Gabriel Schneider45 hours
| | | | | | | | room fails, and so does a write to an image's or PDF's body Callers or Callees with nothing found, Hover or Lspwhy with no room for its pane, and a question asked of no file's pane said nothing, the 9P write succeeding; a body write to an image or PDF pane went nowhere. A write that asks a language server now waits for its answer (turn.lsp_answers), and what fails there -- nothing found, no answer, a backend's @none why, a placement with no room (ENOSPC) -- fails the write and logs its err. No diagnostics is said, not failed. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Recent goes on across a Restore, and is kept after itGabriel Schneider45 hours
| | | | | | A Restore makes a new session, which never loaded the kept list, and saveRecent gives up until it has: Recent was empty after a Restore and nothing opened afterwards was kept. The new session now takes the old one's list (a boot from a dump reads the kept file). Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A Dump into a DumpDir that is no directory says so; the doc says a missing ↵Gabriel Schneider45 hours
| | | | | | | | last directory is made DumpDir /dev/null said no such directory though it is there, and the doc claimed a missing DumpDir always fails, where its last component is made at the Dump. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Recent keeps only files on disk, and its cap never drops an open oneGabriel Schneider45 hours
| | | | | | A renamed scratch never saved and a /virtual/ pane were recorded as recent files, and at 200 the oldest entry went even when that file was open. Opens, renames and closes now record a path only when it is a file on this host, and the cap drops the oldest closed entry. A name with a newline is not written to the kept list, where it would read back as two. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A pty/ctl exec that cannot start its shell keeps the one runningGabriel Schneider45 hours
| | | | | | Each host closed the running shell before it forked the new one, so an exec whose shell failed left the pane with none and later runs answered error shell gone. A shell not there is now refused up front, and every host starts the new shell first, replacing the old only once the close-on-exec pipe says it ran; a failure there is only said (restartFailed). Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A script whose interpreter is not there is told apart from a missing shell, ↵Gabriel Schneider45 hours
| | | | | | | | and Tty refuses it up front Tty's up-front check found the script and let it through, so the pane was made, its exec failed ENOENT, and the log read new, msg shell: shell not found, del, then the err. The check now reads a script's #! line and refuses it, interpreter /no/such/interp not found, and an exec that fails ENOENT on a file that is there says the same. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* One reopen of a closed file is that file's pane in every jumplist entryGabriel Schneider45 hours
| | | | | | A Back that reopened a file updated only the entry it stepped onto; another entry for the same file stayed closed, and stepping onto it re-looked the file, found it already active and returned without moving, so Forward (or Back) did nothing. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A remembered place is clamped to the file as it is now, its selection's two ↵Gabriel Schneider45 hours
| | | | | | | | ends kept A Recent row or a jumplist entry for a line the file no longer has failed the look; now it opens at the last line. The selection's anchor is remembered too, so a reopen restores both ends where they fit, and Recent and Jumplist share the (closed) marker. Co-Authored-By: Claude Opus 5.5 <[email protected]>
*-. Merge: 9P rounds 18-20 + Recent + renames + render G11-G14, tty track, ↵Gabriel Schneider45 hours
|\ \ | | | | | | | | | cursor + faithful themes
| * | The focused cursor rides with a moving pane, and a focus change glides to ↵Gabriel Schneider45 hours
| |/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | the new one G3's springs now keep the cursor's place in its pane: a pane that moves in the layout carries them, and the quad is drawn through the pane's presented box (its transition track), so through a slide, zoom or drag the cursor rides with the text, and with no transition it lands with it. A focus change, to another pane or into a column's or the workspace's tag, glides from where the cursor was drawn to the new place instead of snapping, unclamped while it crosses. While panes move the cursor is drawn as the overlay quad instead of vanishing (golden 18-mid-transition re-recorded for that: the only change is the quad, gliding into the new column's tag). Lapis: a tag plaque starts flush against its grip's button, the scroll gap between them part of the plaque, so grip and plaque read as one piece, on pane and column rows alike; below the button's row the grip keeps its band. Themes without plaques are unchanged (their grip band and tag band are one colour, no sliver).
| * GUI goldens settle on the window's idle, not two captures that agreeGabriel Schneider45 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | test/gui_golden.py hashed a scene once two captures 400 ms apart agreed; on a loaded machine a slow frame agreed with itself before the scene's change landed and every later hash moved up a scene. A test window now writes a status file into its capture directory each loop step (gui.zig writeTestStatus: step, frames captured, idle — nothing owes a frame, nothing moves, nothing waits on the core's clock), and the harness waits for an idle step two past the one current when it asks: a 9P write has been served by then, so the frame is the scene's. Two things kept a window from ever going idle, both fixed: - Look-hover re-armed forever: resting on a word with nothing to open, the hover wait ran its two frames, found nothing, cancelled, and the next present's pointer refresh started it again: a frame every other frame for as long as the pointer rested there, live too (CPU). The place last refused is remembered (Pardes.look_hover_refused) until the pointer moves off it. - The virtual clock moved only on a wait that timed out, so a steady trickle of events held it still; it now moves to the core's wake too when an event cut the wait short and nothing owes a frame. Captures go to a directory of their own, outside the session's (a file written there every frame is a change in the directory its panes look at). All 17 goldens match the file as recorded at oooopqyv; eight runs, five alongside a ReleaseFast build and three with all 16 cores saturated (load 13-19), were byte-identical, 14 s a run unloaded (was ~30). Tests: the resting pointer lets the core go idle.
* | Recent lists the files opened lately, closed ones too, and a jump back to a ↵Gabriel Schneider45 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | closed file reopens it A file closed by accident, its path forgotten, could not be found again: the jumplist dropped closed panes. Every file opened (a look, a Save of a scratch, a Restore, a rename) goes first in a list of 200, kept once, its dot noted when it closes, kept across sessions in $XDG_STATE_HOME/pardes/ recent. `Recent` (SPC f r) shows it in a reused +Recent, each row `path:line:col open|closed` a look reopens; /recent reads `open|closed <path>`. The jumplist keeps a closed file's entries, +Jumps marks them `(closed)`, and Back to one opens the file at its place. acme has no such thing; its dump and Load are the nearest. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A column takes panes until their minimums fill it, not until none can be halvedGabriel Schneider45 hours
| | | | | | | | | | | | | | | | | | | | Placement halved the tallest pane or refused, so 11 panes filled 48 rows that hold 16 at their tag and two rows each: the refusal did not match the documented arithmetic. When no pane can be halved but the minimums with the new pane's fit, the column's rows are shared out again; and a refused pane's removal leaves no pane under its minimum. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | Errnos that fit: a Dump into a missing DumpDir and a shell that is not there ↵Gabriel Schneider45 hours
| | | | | | | | | | | | | | | | | | | | | | | | are ENOENT, a GUI-only setting EINVAL A Dump into a DumpDir not there failed EIO `file not found`; `no shell "x"` read as EIO through a mount; and a GUI-only setting on a terminal, EINVAL in the reply, had no word 9ns reads as EINVAL. Each says so in the words that give its errno: `no such directory`, `not found`, `invalid here`. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A shell that cannot start fails Tty and pty/ctl exec with why, before either ↵Gabriel Schneider45 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | answers A shell whose exec failed (a script's missing interpreter) was reported started: Tty and pty/ctl exec succeeded, then the pane died. The child now reports a failed chdir or exec through a close-on-exec pipe the parent reads before acknowledging the shell; the host's spawn fails with the reason (`shell not found`, ENOENT), which fails the waiting write. A Tty whose first shell never ran leaves no pane; a terminal restarted by pty/ctl exec keeps its pane. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A shell started again by pty/ctl exec is guarded against the rename flap as ↵Gabriel Schneider45 hours
| | | | | | | | | | | | | | | | | | | | | | a new one is The guard that keeps a shell from taking this process's directory before its chdir lasted only until the first shell spoke; a shell started again in the same pane (pty/ctl exec) was asked with the old shell's word still standing. A spawn resets it. fs.py's naming test restarts one too. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A Save into a directory that is not there says no such directory, ENOENTGabriel Schneider45 hours
| | | | | | | | | | | | | | | | It failed EIO with `file not found`, which named no file that was missing. It says `Save <path>: no such directory`, and a late failure saying `no such` answers ENOENT, as a builtin's does. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | pty/ctl exec fails the write when the shell cannot start; a directory ↵Gabriel Schneider45 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | removed under a shell leaves its name `exec` on pty/ctl restarted a shell in a directory that was gone, the write succeeding and the pane dead; and the pane was then renamed `<dir> (deleted)` from /proc, so a restart failed even once the directory was back. `exec` is now refused up front, ENOENT, where the directory is gone; a shell the host cannot start fails the waiting write (late failure, as a Save's); and a `(deleted)` cwd is no name to take. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A command from a missing directory fails ENOENT with no pane; one whose ↵Gabriel Schneider45 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | shell never started ends exit 127 Exec made a command pane in a directory that was not there, whose shell never started: it said running for ever, a follower waited for an exit that never came, and Kill found it. The line now fails up front, as Tty does, `exec: <dir>: no such directory`; and a command pane whose shell the host could not start ends `exit 127` at once. A shell that could not start in its directory says `directory missing`, not `file not found`. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A shell given a directory is not taken to be in this process's before it has ↵Gabriel Schneider45 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | spoken, so it logs no rename to the session's and back Each host read /proc/<pid>/cwd right after forking the shell and at every frame after; before the shell's chdir that says this process's directory, a rename to it, then the real one a frame later, a rename back. A shell given a directory is named by it already (newShell) and is no longer asked at fork; and until it has printed anything, an answer naming this process's directory is not taken. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A new terminal is named by its directory from the start, so its shell ↵Gabriel Schneider45 hours
| | | | | | | | | | | | | | | | | | | | arriving there is no rename Tty's terminal was `new N /` until its shell reported its directory, then `rename N <dir>`: a rename for no change of place. The pane owns the directory it is started in from the first; only a move elsewhere renames. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A new terminal's greeting `ls` goes at its shell's first prompt or neverGabriel Schneider45 hours
| | | | | | | | | | | | | | | | | | | | | | The greeting waited for any moment the cursor sat at a prompt's input, so where the first prompt was missed (a resize still pending, a line written before it), `ls` was typed later into whatever ran then. It is now sent only while nothing has run (pardes's marks saw no command start) and nothing is typed at the prompt; past that it is dropped, never typed late. A prompt drawn again for a resize is still the first. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | Tty in a missing directory fails and makes no pane; a shell that never ↵Gabriel Schneider45 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | started answers runs The host could not start the shell (its chdir failed), and the pane stayed with no pty: a run written to it waited for a prompt for ever. Tty now refuses a directory that is not there, `Tty: <dir>: no such directory`, before making a pane; and a terminal whose shell the host could not start (Pardes.shellFailed, from each host's spawn) answers any run with `error shell gone`. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A Save whose write fails changes nothing: not the name, not the dirty flagGabriel Schneider45 hours
| | | | | | | | | | | | | | | | | | A scratch took its new name before the host wrote it, so `Save /root/x.txt` failing with EACCES renamed it anyway; a failed write to another name marked a clean file dirty. A scratch is now named once its write is done (promoteSaved), and a failed write puts the saved revision back. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A Save refused for want of a name counts as a failure, so its announcement goesGabriel Schneider45 hours
|/ | | | | | | | A builtin that would open a prompt nobody can answer (Save with no name on a scratch, through a ctl write) failed the write without saying a failure, so the announcement rule missed it and `msg N Save` stayed. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Merge: 9P rounds 9-17 + render DPI, dark tints, G4-G10Gabriel Schneider45 hours
|\
| * G7: a moving pane casts a shadow, a retargeted move keeps its speed, a ↵Gabriel Schneider45 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | dissolve crossfades - Track.launch: a move retargeted mid-way (Presentation.sync) starts at the old move's speed along the new one's longest side (carriedLaunch, Motion.easeFrom/speedFrom), so a second change during a slide bends the motion instead of stopping it dead. The macOS track ABI grows the float (pardes.h, 48 bytes; its reserved byte is named motion, as in Zig). - A pane that slides, zooms or rises casts Lift's soft shadow at sin(pi x progress) of its strength, on the other panes' bodies and rails alone, under the same contrast ceiling, carried by its track. - The GUI's PanelDissolve is a crossfade: the new cell drawn first, the old one over it at 1 - progress, premultiplied, on the blending cell pipeline for such groups (new cells now emitted before old ones, as the old layers already came after the new). sRGB blend, not linear light (documented). The tty keeps its per-cell swap. Presentation.zig's and animation.zig's tests were not in any test root; pardes.zig's test block now takes them in. Tests: a retargeted move carries its speed and still lands exactly; a moving pane's shadow is clipped off every tag, grip and its own body, none at the start or the landing, most mid-way. GUI golden 18 changes by the shadow (frame 6 of a slide). Gates as G6. Feel review: .scratch/render/transitions/.
| * G6: a wheel's notch scrolls the core at once and the picture glides after itGabriel Schneider45 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | A mouse wheel's notch (SDL's whole-number step, notchOf) scrolls the core by its lines at once, so keys and clicks act where the text is, and the picture follows on the Motion flavour's spring at half again its pace, critically damped whatever the flavour (text never passes its mark), carried from its current speed (animation.Spring.carry), so notches in a row flow on. How far the rows moved is measured on the next frame against the last (Glide.measure: a wrapped line's rows, a terminal's output, with a couple of rows' slack for the cursor's line and its number); the rows that left are kept, nearest first, and drawn in the gap the lag opens; the lag is never more than the body's height. A touchpad's fractions move the picture under the finger as before; `Motion off` lands a notch at once; a PDF page scrolls as before. The glide runs on the core's continuous path: Pardes.shell_continuous keeps frames coming every loop while it moves and carries a virtual clock with it; idle, nothing. The test feed takes wheel events (ESC]777;mouse;wheel;<amount>;<x>;<y>BEL). Tests: notch vs fraction; a carried spring keeps its speed and never passes its mark; a notch over two-row lines lags two rows, keeps the rows that left, settles frame by frame and leaves the continuous path, and lands at once with Motion off. Gates as G5. Feel review: .scratch/render/scroll/glide-{smooth,crisp,bouncy,off}{,-quarter}.mp4 and strips (a notch down, three, then two up).
* | New from a column's exec makes its scratch in the session's directory, as ↵Gabriel Schneider45 hours
| | | | | | | | | | | | | | | | | | tagexec's does A word run from a header runs in the session's directory (execDir), but New named its scratch after the pane with the keyboard. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A builtin's Verbose announcement is logged only when it succeedsGabriel Schneider45 hours
| | | | | | | | | | | | | | | | | | | | | | A failure said while it ran (Exit's refusal, a Del refused) or later by the host (a Save's write, a Dump's) left `msg N Save` in the log as if it had worked. The announcement's record is remembered and taken back out on either, unless a follower read it already. A refused pane/new logs its err alone, and a failed Dump says its reason in words (`file not found`, not `FileNotFound`) so its msg goes and the err is the one record. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A terminal keeps its tag and two body rows through drags, squeezes and a ↵Gabriel Schneider45 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | smaller window A terminal of one body row loses its prompt's mark and reads busy for ever. Placement already refused to make one that short; now layout never leaves one there either: a grip drag stops at the floor, and a column squeezed by weights or by the window takes the rows from its other panes. Text panes keep acme's way and can still be dragged to their tag, since acme has no terminals to follow. The shrinktag, splitparent and cornerdrag goldens are re-recorded for it, shrinktag's re-grab moved to where the grip now is. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A pane from pane/new is the session's directory's scratch, not the last ↵Gabriel Schneider45 hours
| | | | | | | | | | | | | | | | | | | | active pane's No pane asked for it, so borrowing the directory of whichever pane last had the keyboard named it after an accident; acme's new window has acme's directory. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A pty/ctl winsize gives a terminal two rows at least, so it no longer reads ↵Gabriel Schneider45 hours
| | | | | | | | | | | | | | | | | | | | | | | | busy for ever A one-row pty loses the prompt's input mark as soon as the prompt wraps, and busy detection never sees the shell idle again. Placement already keeps a new terminal at tag + 2 rows; a winsize write now keeps the pty at two. A pane dragged down to one body row keeps a one-row pty, since a taller pty than the pane shows would put its cursor below the pane. Co-Authored-By: Claude Opus 5.5 <[email protected]>