summaryrefslogtreecommitdiff
path: root/src
Commit message (Collapse)AuthorAge
...
* | Columns are named by stable serials over 9P, as panes areGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | /col/<n> named a column by its index, which shifts when a column left of it closes: exactly what scripts trip on. A column is now /col/<serial>, its serial kept while it lives and never reused; /layout lines give serial and index (`<serial> <index> ...`), active names the serial, /index's last field is the column's serial, and the log says newcol and delcol with serials. Docs, the skill and the tree's README say so. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A pane's last row of text is no resize handle; the grip is, and the GUI's ruleGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | A press on a pane's last row began a seam drag, so the row could not be clicked or swept like the rest of the text (rsearch dragged the handle out of the way first). Now no row of text is a handle: in the GUI a press on the 2 px rule between two panes drags their seam, and anywhere the grip does, as acme's button moves a window -- dragged up or down its own column it moves the pane's top, either pane down to its tag alone (acme's coldragwin), dragged down inside itself too. The hover hint over a last row is gone; column edges resize as they did. Scripts that dragged a seam drag a grip instead and match their goldens; six goldens lost the hint or moved a cursor to the clicked text, re-recorded by name. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | The tree serves the layout: /layout, /tag and /col/<n>/tagGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | A script could not see where the panes sit or edit the tags a person clicks in. /layout lists each column, its index, x and width in cells, current or not, empty or full, and its panes' serials, then the active column; /tag is the workspace tag and /col/<n>/tag a column's, read and written as a pane tag is (one line: a newline written in is a space, and a truncating write drops the one ending it); /index's lines end with the pane's column; the log says newcol <n> and delcol <n>. acme serves no column files, and the docs say so. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A dump keeps the settings changed from a fresh session's, and Restore sets themGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | A session running with Placement pardes came back from Restore as acme: a dump kept the layout and the theme but no setting. It now keeps every setting line that differs from a fresh session's, as the root ctl reads it, and a Restore applies them; the theme stays where it was kept, the font the frontend's, and REPL bindings are still dropped. The dump golden gains the three the snapshot harness sets. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A GUI-only setting says so on a terminal, and DumpDir reads back its directoryGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | `Lift on` over ctl on a terminal answered "unknown control message" while Config listed Lift as unsupported, and the ctl's DumpDir line read bare while Config showed the path in effect. A setting word this frontend lacks is now refused as `Lift is GUI-only` (GripWidth too), and DumpDir reads back the directory in effect, as Config does. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A write to a closed pane's file says it is gone, and a terminal says why it ↵Gabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | takes no name `echo Del > pane/6/exec` just after pane 6 closed failed EACCES: the shell's lookup failed, it tried to create the file, and the engine refused every Tcreate as a permission error. pardes now takes Tcreate itself: refused as before (acme's fsyscreate refuses it too), ENOENT in a pane that has closed. Renaming a terminal was refused with only EPERM; it now says a terminal is named by its shell's directory, worded so a mount still maps it to EPERM. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A send to Python's REPL runs once, the Enter after the paste, not in itGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | A one-line send (print(1)) to Python 3.14 stopped at ... and needed another Enter: its REPL takes a bracketed paste, and an Enter read in the same read as the paste is part of the paste, one line or not (checked in a real pty). Into a REPL that asked for bracketed paste, the Enters (two for an open block) now wait for its next output, its answer to the paste, or 250 ms with nothing. fs-test runs python3 in a real pty and checks a one-line exec prints at once; the docs' pty/data recipe says to send the Enter apart. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A truncating write to tag drops the one newline that ends itGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | `echo X > tag` stored X and a trailing newline, which drew an empty tag row. After a truncation the newline ending each write is held back until more text follows or the open is released, so the one ending the whole of it is dropped and any other stays; `>>` keeps what it is given. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | Del and Delcol refuse unsaved text once, as acme's doGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | acme's Del asks winclean first and Delcol colclean (exec.c del, delcol): unsaved text is warned about, and the same word again closes. pardes closed at once. Both now refuse once through the one-warning-per-word mechanism Exit uses, from keys, tags, exec and ctl alike. A pane dirty only by a rename, or an empty one that never held a file, has no text to lose and is not asked about, and a scratch or a command's output under 100 bytes is not either, as acme's winclean passes a small unnamed window. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | Exit and Restore name a modified +New scratch too, in one wordingGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | Their refusal named only file panes, so text typed into a +New scratch went unasked about and was lost. A scratch with text counts as unsaved as a file does (acme's winclean asks about an unnamed window too). The refusal a ctl write gets now reads as the message row does, `<names>: Modified (Exit again to discard)`, instead of a second "discard them all" wording. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A failed Dump or a Restore of no dump fails its write, before any warningGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | A Dump into a DumpDir it could not write, or a Restore of a file that is not a dump, answered a ctl write with rc 0 and only a message, and the Restore warned about unsaved panes before it ever looked at the file. The hosts' dump write now fails the waiting 9P write with EIO (the late failure a Save uses), naming the path and the reason in the log, and Restore parses the file before its unsaved-panes refusal, failing with `not a pardes dump`. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A choice setting's refusal names what it takes, and /commands lists themGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | Bare Placement and BootShell flip, which is pardes's convention for a word that chooses (Crt does too), but nothing said so, and "bad value in control message" never said what would do. A refused value now says `takes acme, pardes` (every switch and choice setting), /commands lists each such setting's values in a fourth field, and config.md and fs.md say that bare steps to the next. Motion and ShaderAnimation stepped nowhere bare and now step like the rest. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | An answer the question does not offer is refused, and the question standsGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | `answer z` to a question offering a and b succeeded, sent nothing and dropped the question; so did Del's `answer x`. The ctl now takes only an offered choice or -, refuses anything else with the choices it takes (`answer takes k j or -`), and leaves the question asked. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A tag that exactly fills its row stays one row, as acme's doesGabriel Schneider30 hours
|/ | | | | | | | | | | | | | | Tags wrapped one cell early to keep a cell for the caret past a full row, so a tag that exactly filled its column broke as De / l. acme's frame wraps a box only when it is wider than what the row has left, and frptofchar clamps the tick past a full line's end to the frame's edge. Tags now wrap at their full width; the caret past a full row sits at the tag layer's edge, and on the pane's last cell in a terminal grid, which has none past it. Two tiny-screen tests collapse their tags, as the others do, and the 41 goldens that moved were re-recorded by name after the same check: rows shift back up under the shorter tags and nothing else changes. chordcut's file-body clicks moved back with them. Co-Authored-By: Claude Opus 5.5 <[email protected]>
*-. Merge: 9P round 6 + empty columns + render (acme theme, grips, Lift, Motion)Gabriel Schneider30 hours
|\ \
| | * Motion flavours differ by design, and drive notice drops and pane movesGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The flavours looked alike because Lift, the only motion that read them, moves a shadow a few pixels: overshoot and wind-up were sub-pixel. By design now: crisp leaves at full speed and never passes its mark; smooth (the default) eases in and out in twice crisp's time, a slide arriving in 208 ms (inside §8.1's 220); bouncy passes its mark by ~30% and settles twice; playful winds up, passes by ~40%, and stretches and squashes, text included (the user's choice). Small motions are exaggerated by the flavour's gain. Motion gains launch, gain, span and norm, and ease(u)/speed(u) for moves of fixed length. The notice drop follows ease() over its fall (alpha never behind its position) and stays inside its pane's body past its row. Panel tracks carry the flavour in Track.motion (the old padding byte; macOS header name follows); opening and moving slides, zooms and rises follow it over span x their frames; landed, a pane is exactly its target; closing keeps its own exit. A pane past its mark never takes a neighbour's click; a rising one stays in its box. Tests: each flavour's lift trajectory distinct; flavour character; exact landing bit for bit; a stretched pane's drawn cells map back; the overshoot hit rule; the vertical clip at peak; legacy tests pinned to crisp. GUI golden 18-mid-transition changes (the retuned smooth slide at frame 6). Shared files: src/animation.zig, src/Presentation.zig, src/Messages.zig, src/macos.zig, src/config.zig, src/pardes.zig tests, src/ninep/screen.zig test, docs/effects.md, docs/config.md.
| | * acme looks like acme; every theme gets acme's button, scroll column and 2px ↵Gabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | rules acme takes plan9port's own colours (acme.c iconinit, draw.h): selections #eeee9e in the body and #9eeeee in the tag, black ink; exec and look sweeps #aa0000 and #006600, white ink; a #99994c scrollbar under a #ffffea thumb; black rules; a #8888cc tag/body rule and column button. Every theme: the grip is acme's button, its tag row's full height, in one scroll column with the scrollbar (rail_px, 12 at a 17px tagline, scaled, times GripWidth, a percent, default 150; same x0/x1 to the pixel): focused clean = solid box; focused dirty = box_dirty filled to a box ring (acme's modbutton); unfocused clean = a box_border ring round the tag's ground; unfocused dirty = box_dirty a pixel inside that ring. The grid, which has no ring, marks dirty with a bold * on box_dirty in the grip's second cell and fills an unfocused grip halfway to its ring. Regions carry a dirty flag. Column buttons are the same shape, solid. 2px rules between columns and stacked panes (inside the tag band's slack), none at the window edge; a 1px tag/body rule halfway between tag and page. Theme data: tag_sel_bg, sweep_bg/fg, tag_rule, rule_px, rail_px, box_border, box_dirty. Chrome: rule_px, tag_rule, rail_px, grip_border, grip_focus_ring; a comptime guard on Chrome's layout forces a wire bump once v8 ships. Structural rules are exempt from the focus-indicator rule (docs/effects.md). Tests: acme widths; stacked rule in the slack at two tagline sizes; every grip-column pixel is band, button, ring or rule, and the button shares the scrollbar's x0/x1 (4 metrics x top/bottom x focus x dirty); the four grip states distinct in every native theme with a non-colour dirty cue in the grid; wire round-trip of the new fields. Shared files: src/colors.zig, src/surface.zig, src/body_layer.zig, src/tagline.zig, src/detached/wire.zig, docs/themes.md, docs/config.md, docs/effects.md, 21 snapshot goldens (the grip cells only), test/gui-goldens.txt. Motion flavours differ by design, and drive notice drops and pane moves
| | * A multi-row tag shows its grip on the first row only, as acme's buttonGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Rows under the first are the tag band's fill (the focused tint when the pane is focused), for pane tags and column tags alike, on every shell, since each draws the grid's cells. The grip region still spans every tag row, so a press there grabs the pane, as a press anywhere in acme's button column does (acme.c: t->scrollr is the tag's full height). GUI goldens 12-18 change only in the grip rows under the first.
| | * Motion flavours, and Lift is shadow, rim or auto: auto on a dark page ↵Gabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | recedes the other panes (InactiveDim) Motion off|crisp|smooth|bouncy|playful is one parameter set (animation.Motion) every fx animation reads. One focus spring per pane drives both the lift and the dim, at the flavour's pace; a grid snaps. Lift drops glow and surface. A shadow falls on pane bodies and rails only, capped so text and the selection keep min(contrast, 4.5); rim is a hairline along the focused tag's top. auto is a shadow on a light page; on a dark page the core recedes the unfocused panes' text (InactiveDim, ported from 2e4d97f, default 30 under auto) with the same floor. Tests: focused text contrast is never below unfocused in any style, dim or theme; the focused pane's cells and tag are untouched by a dim. Shared files: src/config.zig, src/builtins.zig, src/ninep/ctl.zig, src/surface.zig, src/detached/wire.zig, docs/config.md, test/snapshots/builtins.snap (one more wheel for the longer help).
| | * Lift takes a style: shadow, surface, rim, glow or autoGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The user wants Lift to read on dark themes, where a shadow has nothing to darken. `Lift shadow|surface|rim|glow|auto` (`on` is auto, bare flips auto and off): surface lightens the lifted part's ground about 5.5% (Material-dark), rim is a thin soft light off its top and left edges, glow a faint halo in the theme's accent (Chrome.accent, the focused box colour), auto a shadow on a light page and surface with half a rim on a dark one (by the page's luminance). Rim and glow are the soft kernel in a light colour (no pre-warp: coloured light is laid on as is, within §8.2's budget); each style is capped per theme so text keeps min(its contrast, 4.5) (tests: darkening and lightening over the native themes). The style travels in the palette (Chrome.lift_style, one byte on the wire). Config reports and ctl reads `Lift <style>`. Shared files touched: config.zig, builtins.zig, ninep/ctl.zig, detached/wire.zig (palette byte), gui.zig. Not touched: pardes.zig, Messages.zig, mouse.zig.
| | * Lift moves at the display's rate: render-time animationGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Review (and the coordinator): Lift stepped with the core's 16 ms ticks, so a 144 Hz display held each shadow for 2-3 refreshes. The lift springs are now sampled at each frame's own time (p.now_ns), and Pardes.continuous() reports a render-time animation in motion: while it is, the pump draws every loop (a 1 ms wait takes only the input there is) and the present's vsync paces it at the display's rate. They left nextWake; the stepped, frame-indexed animations (transitions, message fades, the theme fade) and every golden are unchanged. With PARDES_TEST_CLOCK, each loop of a moving render-time animation moves the virtual clock by a display frame at PARDES_TEST_HZ (default 60), so a 144 Hz series is deterministic: a focus switch is 51 distinct frames at 144 Hz. Test: renders 6.9 ms apart give rising lifts; settled, no frames. The tty never lifts (GUI capability). Shared files touched: pardes.zig (continuous, pump), gui.zig.
| | * G1: Lift, soft elevation shadows (off by default)Gabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | `Lift` (a GUI toggle, capability lift) lifts the focused pane on a critically damped spring (animation.Spring: closed form, about 240 ms to settle, velocity kept across retargets, settled = no frames) and floats notices. The core puts the lift on the regions (Region.lift, on the wire); the GUI casts from it: a soft shadow is decor, an erf rectangle on one quad grown by 3 sigma (decor.frag, flag 0x10000000), in the quad's own space so a track's transform carries it; darken-only, the caster's rect left alone, alpha pre-warped for linear light and IGN-dithered. Cast last in its group, so a neighbour's rails and rules darken with its text; a notice's before its rule. Strength is capped per theme so text over the darkest shadow keeps min(its contrast, 4.5) (test over the native themes). Opt-in until the focus lift/dim default is decided. Feel review material: docs/effects.md. Test-mode PARDES_TEST_SERIES keeps every captured frame. Shared files touched: pardes.zig (two fields, one nextWake line), detached/wire.zig (lift on the region), config.zig, builtins.zig (capability), gui.zig. Not touched: Messages.zig, mouse.zig, tty, host_io.
| * | A pane whose whole text fits in its rows never scrolls to keep a marginGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | The scroll margin reveals text; with none off screen there is nothing to reveal. ensureCursorVisible and a centred look both leave such a pane at its top. A longer file keeps helix's clamp (hxdiff and hxparity unchanged). Co-Authored-By: Claude Opus 5.5 <[email protected]>
| * | A document dragged beside the boot shell leaves it; BootShell replace closes ↵Gabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | it as before Placement and BootShell are one kind of setting now, a word from an enum, set, flipped bare, and reported by one arm. Co-Authored-By: Claude Opus 5.5 <[email protected]>
| * | An empty column shows its theme's empty_col: white in the acme theme, as ↵Gabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | acme paints it Co-Authored-By: Claude Opus 5.5 <[email protected]>
| * | A search pattern with a short \x escape is refused, where mvzr panicked on itGabriel Schneider30 hours
| | | | | | | | | | | | Co-Authored-By: Claude Opus 5.5 <[email protected]>
| * | A column can be empty, as acme's can; new panes go where acme's ↵Gabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | makenewwindow puts them Newcol makes an empty column; closing a column's last pane leaves it empty with the keyboard on its tag; Delcol and Joincol alone take a column away; the session's last pane closing quits. The +New stand-in and replaceStillborn / stillborn_joiner are gone. Every new pane goes through exec.placeNew, which follows acme's makenewwindow (active column, empty column whole, blank space, halving the biggest) or, with Placement pardes, the old rules. ColumnTags is gone: column tags are always shown. A grip drag shows acme's box cursor. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | Kill stops a command pane's whole line again, now it runs with job controlGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | With the line run under job control (the change before), the job running has a group of its own, and Kill's SIGTERM to the tty's foreground group stopped only that job: of `sleep 30; touch x` the touch still ran. For a command pane the hosts now signal the shell's group as well, so the whole line stops, as it did; a line typed at a prompt still loses only its foreground job. The test forks a real pty for both halves: a background job outliving its command and the pty's hangup, and a killed line not running on. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A Restore logs restored <old> <new> for each paneGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | A script holding serials from before a Restore could not find its panes: the replacement numbers them afresh. The dump now keeps each pane's serial (0 in older dumps), and the replacement's log says `restored <old> <new>` for each after its `restore <path>`. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A restored command pane comes back finished, not as a live shellGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | After Restore a dogfood agent's command panes were fish shells under a stale `(make) exit 0` tag. The dump's terminal record now keeps a command pane's line and status, and Restore brings it back done: what it printed, its exit, no shell and no rerun. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A pane's question is logged as ask, and answer on its ctl answers itGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | A script could not answer the several-REPLs question, which only a key could. Every question a pane asks on its notice band (which REPL takes an exec, Del's side from the keyboard) is now logged as `ask <serial> <what> <choices>`, and `answer <choice>` on the pane's ctl answers it as that key would, `answer -` taking it back as Esc does. One function answers for a key and for the ctl. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A Python block sent to a REPL gets the second Enter that ends itGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | A dogfood agent's `def` sent to python3 sat open at the continuation prompt: a block whose last line is indented is not over at one Enter, pasted into 3.13's REPL or typed into the old one (checked against python 3.14 in a real pty, both ways). A send to a Python REPL now adds that second Enter, and the pty/data recipe in the docs and the 9P skill says to. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | Restore brings a Tty bash pane back running bashGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | A dogfood agent's `Tty bash` pane came back from Restore running fish: the dump kept its directory but not the shell it was opened on. The dump's terminal record now carries that shell (empty for the configured one, as older dumps read), and Restore starts it again. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A command pane's background job outlives its commandGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | A dogfood agent's background jobs died when the command that started them finished, even under nohup. The command's shell leads its terminal's session, and on its exit the kernel hangs up the terminal's foreground group, which with job control off is the shell's and every job's. The line now runs with job control on (bash, sh, dash, zsh, ksh -m; fish status job-control full), so a job has a group of its own and lives on, printing below exit N, and survives the pane closing. While a job holds the pty the pane is not reused, so the directory's next command gets a second pane (cmdexit's golden, re-recorded by name). Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A shell that exits under a run answers exit N, and the log says exit before delGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | A dogfood agent ran `exit 3` through pty/run: the reader got ENOENT, and the log went straight to del. The hosts now read a shell's exit status at its pty's end as they do a command's; a run waiting on the line answers `exit 3` with what it printed, the log says `exit <serial> 3` before the pane's `del`, and an open run still stats after its pane is gone (cat fstats its input). Pins cloud9 f35b7ed, whose stat of an open fid names its open. Writes to a gone pane's pty/data, held open or not, fail ENOENT. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | pty/status reads the pty's size as winsize set it, on a line of its ownGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | A dogfood agent wrote winsize 100 30 to pty/ctl and pty/status went on reporting the pane's grid, and the status line had no newline to end it. The size winsize sets is kept until the pane resizes and gives the pty its grid again; ctl's winsize read back and status both report it, and status ends with a newline. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A pane tag wraps onto rows and can collapse to one, as acme's doesGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | acme's frame wraps a tag at its right edge and the window grows to show every row (wind.c wintaglines); Tagup/Tagdown shrink it to one row and back. Pane tags used to be one row, scrolled sideways to the caret. Now a tag's lines wrap at its width (one cell kept for the caret past a full row) and it starts expanded; insert-mode Up on the first row collapses it, Down on the last row expands it, and Alt-Up/Alt-Down do so in either mode. A word the wrap breaks across rows is still one word to a click and a selection. Column and workspace tags keep scrolling sideways. Tests read body rows relative to the body's first row; the 41 snapshot goldens that moved were re-recorded by name after a programmatic check that each differs only by body rows shifting under the taller tags, and twelve scripts whose clicks targeted fixed rows now click the same text. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A look with a blank in its pattern addresses, file:N selects the line, and a ↵Gabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | miss is said, logged and reads back empty Round 6: look calc.py:/return a/ was a word search, since a blank ended the address; calc.py:99 past the end focused the file and said nothing; and /look still read back the pane before a miss. A pattern after the colon may now hold blanks; file:N selects the line, newline included, as acme's does; a line past the end opens and focuses nothing; and every miss (no match, a bad address, a line past the end) says so, logs err <serial> look: why, keeps the selection, and leaves look reading back empty. Documented, with file:0/re/ for the first match, since acme evaluates the address from the file's dot. Five goldens change: a look at file:N now selects the line. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A key between two clicks makes them two clicks, not a double-clickGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | A double-click is two clicks with nothing between them; a click, some typing and a click at the same cell within half a second selected a word, and snapshot scripts that did that came out by timing. A key now forgets the last click. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A tag no longer marks an unsaved file with *: the grip shows itGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | The grip button now shows a pane's unsaved text in every state, as acme's modbutton does, and acme's tag has no asterisk. The computed prefix is the name alone, so tag reads, event offsets that count the path and the tag's text lose the " *". dirty, index's flag and the refuse-once guards are unchanged; a dump from before still restores its tags past the old mark. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A B1 sweep held past a pane's edge scrolls the body, as acme's frselect doesGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | Dragging a selection beyond the top or bottom of a pane stopped at the last visible row. Now, while B1 is held past the body's top or bottom (at a pane that reaches the screen's bottom, its last row, since the pointer cannot leave the screen), the body scrolls a line a tick; the sweep's start keeps its place in the text, and a sweep that scrolled ends as the text's own selection. Also here: a ctrl-click is not half a double-click (lspdebug's golden stays as it was), and a test that clicks many points in a row resets the double-click between them. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | Everything a B1 chord does while B1 is down is one undo stepGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | B1-B2 then B1-B3 cut and pasted as two undo steps. acme marks the file once per B1 hold (text.c:881, textselect), so the pair is a copy. The chord now takes one undo point when it begins and its edits share it until B1 comes up. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A double-click selects as acme's does: a word, a line, a bracketed or quoted ↵Gabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | text A second B1 click at the same cell selected nothing more. Now, as acme's textdoubleclick (plan9port text.c:1407), it selects up to the match of a bracket just before or after the point, the whole line at a line's start or end, the quoted text inside ' " `, or else the word, in bodies, tags and terminals in normal mode. A plain click still puts the caret on its cell. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | Column and workspace tags are one line again, as acme'sGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | acme gives a column tag one font height (cols.c:244) and its row tag one line; pardes let them grow a row per line. A newline typed, pasted, written or restored into a column or workspace tag now becomes a space, the caret and selection kept on the same characters, and the bars are one row each. A dump holding a multi-line header tag comes back with its lines joined by spaces. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A write of an Edit block reaches Edit whole, on a pane's ctl, the root's and ↵Gabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | exec Every write was split into lines, so a { group or an a/c/i text block written to ctl or exec became lines of their own: i/.../ and } ran as shell command panes that exited 127. An Edit line now takes the lines after it while its group is open or its text block waits for its . line (sam_edit.needsMore, asking sam's parser), on all three; the root ctl takes Edit at the active pane. s/re/text/N counts as sN/re/text/ does. The docs and the skill show a block written by heredoc or env printf, since bash's printf writes line by line, and take round 5's doc list: get refusing once, the (xN) on a repeated err, limit bounding only a forward search's end, line:col's clamping, and $NINE_MOUNT under 9ns --unix. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | An Edit with an unclosed { is refused, not runGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | Edit ,x/area_of/{ answered ok and did nothing: the group ran out of text and ran what it had. sam reads on for the }; an Edit has nothing more to read, so it now fails with unmatched `{', in the words sam uses for the other side. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A replace through data or body, truncate then write, is one undo stepGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | A shell's printf x > data truncates the addressed range and then writes, and each pushed its own undo point, so one replace took two Undos. A truncation of data, xdata or body now lets the write that follows it at once join its step, as acme's replace is one. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | Writes made under mark 0 are one undo step, and no history is lostGabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | mark 0 skipped the undo point of each write, and mark 1 then pushed the text as it had become, so the text from before the batch was in no step: with no history before it, no Undo brought it back. Turning mark off now keeps the text as it is, and the writes after it are one step back to it, as acme's nomark joins them. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | ^ in a pattern with \n matches at every line start, and a ^ or $ that cannot ↵Gabriel Schneider30 hours
| | | | | | | | | | | | | | | | | | | | | | | | work is refused A pattern naming a newline runs over the whole text, where mvzr's ^ is only the text's start: /^def .*\n/ from #0 failed, and Edit ,x/^def .*\n/.../ silently did nothing. A leading ^ is now tried at each line start from the search's own; $ just before \n is dropped, as it changes nothing; any other ^ or $ in such a pattern is refused with why, since mvzr would read it as the text's ends. ^ inside an alternation holds only where a search starts, an mvzr limit, documented. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | | A mouse sweep selects a stream, as acme's does, not a rectangleGabriel Schneider30 hours
|/ / | | | | | | | | | | A B1, B2 or B3 drag over several lines took the same columns from every row, a block. acme's sweep is a stream (libframe/frselect.c:115-140, frselectpaint; acme text.c:881, textselect): the first row from the press to its end, the rows between whole, the last from its start to the release. edit.sweepCols says which columns of a row a sweep takes; the captured spans, the text a sweep yields (copy, chords, B2 and B3 operands, a terminal's automatic yank) and the highlight all follow it, in bodies, multi-row tags, terminals and text-rendered PDFs; a row the sweep runs through is highlighted to the edge, and rows after the first leave out the line numbers. Native PDF selection was already MuPDF's stream. No pane keeps a rectangle. exec-bigdrag's golden changes: its middle sweep now runs whole lines. Co-Authored-By: Claude Opus 5.5 <[email protected]>