summaryrefslogtreecommitdiff
path: root/docs
Commit message (Collapse)AuthorAge
...
* | A name write is one name, refused with its reason otherwise; a log record is ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | one line of UTF-8 A name took a second line, DEL, a C1 control or bytes that are not UTF-8, which then went into /index and /log as they were and split or garbled a reader's lines. `name` strips one trailing newline and refuses the rest, saying which (`bad character in file name: not UTF-8`); and the log turns DEL and C1 into spaces, as it did control characters, and escapes bytes that are not UTF-8 as `\xNN`. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A rename alone does not make a pane dirtyGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | Any rename marked the pane dirty (saved revision one back), while the guards ask only whether its text was ever edited: /index said dirty for a pane Exit let go without asking. The name is no edit now, so /index, the guards and Restore agree; Save writes under the new name all the same. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | The root exec's refusals before a line runs name the pane, and say invalid, ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | so a mount reads EINVAL A line over 1024 bytes or with a control character was refused with its errno EINVAL but words 9ns reads as EIO (no `invalid` in them), and logged `err - exec`. They now say `invalid command line: ...`, and the root's err record names the pane the line would have run at. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | Tty in a missing directory fails and makes no pane; a shell that never ↵Gabriel Schneider29 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 Schneider29 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]>
* Merge: 9P rounds 9-17 + render DPI, dark tints, G4-G10Gabriel Schneider29 hours
|\
| * G10: the theme's chrome fades through OKLab, easing in and outGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | ChromeTheme.interpolate mixes each colour in OKLab (colors.mixOklab), so a fade between hues keeps its lightness instead of going dark and muddy mid way; animation.Fade eases it in and out (smooth), and one retargeted mid fade eases out from where it shows, so it never stalls. Still 10 frames (160 ms). The page, text and syntax colours change at once as before. Tests: OKLab round-trips its ends exactly and keeps a red-to-green mid-way brighter than the sRGB mix. Gates as G9 (no golden moved). Feel review: .scratch/render/crossfade/.
| * G9: SelectionGlow, a soft halo round the body's selectionGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | SelectionGlow on|off (off; a pixel shell's, Chrome.selection_glow): each block of selected rows in a pane body casts Lift's soft kind in the selection's own colour, the block its caster (never touched) and a cursor beside it joined to the caster; clipped to the bodies (never a tag or a grip); at most 22% (§8.2), less where the text round it would drop below min(its contrast, 4.5); fading in over 100 ms with an ease-out, on the core's continuous path (shell_continuous); none while panes move. Blocks come from the cells in the theme's selection colour (documented). Tests: the halo's colour, its clip to the body, nothing at its first frame and up to its cap after 100 ms, fading done. Gates as G8. Feel review: .scratch/render/selection/.
| * G8: a notice leaves quicker than it arrivesGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | The notice chip's motion was mostly there (a drop easing out on the Motion flavour, a dissolve easing in, Lift's shadow in tier 4 at its fade). Its dissolve took 300 ms against a 180 ms fall, the wrong way round for §8.1 (leaving quicker than arriving, 100 to 160 ms): MessageDissolve now defaults to 150. The field comments had the two easings swapped; the stack test pins a longer dissolve for the case it checks. docs/config.md and docs/effects.md say so. Tests: both timings in their bands, leaving quicker. Gates as G7 (no golden moved). Feel review: .scratch/render/notices/.
| * G7: a moving pane casts a shadow, a retargeted move keeps its speed, a ↵Gabriel Schneider29 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 Schneider29 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).
| * G5: lapis, 0x4200.cafe's manuscript, as a theme with its ornamentGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | A native theme `lapis` (src/themes/lapis.zig) from web/theme/lapis.css: a lapis-deep page, lapis tags with gold names, vellum text, vermilion rubrics, gold rules and a vermilion tag rule, gold selection in lapis ink. Its ornament is the theme's new `decor` (colors.Decor), drawn by a pixel shell inside the chrome it already has (decision 1), all of it off every focus indicator: - page_dots: the site's gold dot grid (#e8c46a22, 22 px), drawn by the cell shader on page-coloured cells alone (flag 0x10000000), so it lies under the text and never under a selection, a tag or the cursor; fixed to the window, a page the panes lie on. - rail_checker: the dither frame's gold and lapis checker in the scroll track (a decor kind, flag 0x08000000), anchored to its rail; the thumb stays solid gold. - tag plaques (tag_border, tag_shadow, tag_stripe): every tag a raised plaque inside its own band, the site's trail: a 2px gold frame and a hard 5px vermilion shadow down and right, both within the band's rows and columns (down, what the slack under the text leaves), so no shadow reaches a body, a grip or the next pane. The focused pane's plaque is the code window's title bar, gold and lapis stripes of 2px, its words on plates of the focused ground; unfocused ones are plain; column and workspace tags take the object-label language (1px frame, 2px shadow toward the page). Drawn as decor under the cells (a group's under range), with blank tag cells see-through and the cells blended premultiplied (pipeline_over) on such frames; opaque windows only. - title_shadow: the titles' vermilion offset shadow under a file name's letters, sampled in the cell shader inside the letters' own cells (flag 0x08000000), none on a cursor. Chrome carries the decor (decor_*, name_ink, active_name_ink); a theme file folds a nested decor literal field by field. Left out, with why, in docs/effects.md: the fonts, the ornaments, a decor crossfade, and in the tty anything but the colours (a tag row has no spare cell for a shadow). An empty column is the page. The effect id is masked to 27 bits (ui.vert/ui.frag) for the fifth flag. Tests: the cell flags land only on page ground and file-name cells, never a cursor, nothing without decor; lapis's plaques stay inside their tag's band, clear of every grip, only the focused one striped; its checker is in the track; no other theme draws any; the separator and focus tests cover lapis. Goldens: theme and themesel (the ring gains lapis after acme). Gates as G4. Feel review: .scratch/render/lapis/ (lapis.png, lapis-2x.png, tags-focused-vs-unfocused-2x.png, a PanelSlide at 60 fps and a quarter).
| * G4: bloom, vignette and grain in the post chainGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Three bundled Shadertoy passes beside Crt, each `Bloom|Vignette|Grain 0..3` (off by default, nothing drawn while off), in shaders/post/: - Bloom: what is brighter than the page (a channel's linear peak over 0.75/0.7/0.6 and over the page's own + 0.15, so a light page does not bloom) glows, 2/3.5/5% (§8.2's budget). The glow is a dual Kawase blur in RGBA16F textures at half size and down, 3/4/5 steps by level (shaders/bloom-down, bloom-up; Post.bloomChain), composited in linear light and dithered. - Vignette: round, from halfway to a corner, 10/18/28% at the far corners, in linear light, dithered. - Grain: a still, triangular-spread grain of 1.5/3/5 of 255 on page-coloured pixels only, a logical pixel big; still, so it asks for no frames. Focus rule: the shell passes every bundled pass the rectangles of the tags with their grips, the column and workspace tags, the notices and the cursor (pardesSpare, at most 32, neighbours on one row merged), and a bundled pass leaves them as they are; and a per-theme ceiling (pardesCap, Post.capsOf) keeps the page's text and the selection's text at min(their contrast, 4.5) under each pass at its strongest. User files get no ceiling and no spared rectangles. Fix found on the way: SDL GPU binds at most 4 KiB of a uniform block, so every Shadertoy colour after iPalette (iBackgroundColor, iForegroundColor, the cursor and selection colours, iPalette[>=4]) read zero. The prefix's block is cut in three (Globals, Palette, Colors) with ghostty's names and order, each pushed as a slice of the one struct; pardes's own block moves to binding 3. Tests: the caps hold over every native theme; each uniform slice fits 4 KiB; the prefix's members in ghostty's order across its blocks. Gates: tty/gui unit-test and core-test pass; snap fails only nested-optout; GUI goldens 01-08 byte-identical (09-18 wait on wkzlsqvy, as in somtrmsz); web builds. Feel review material: .scratch/render/{bloom,vignette,grain}/ (levels on forge, dusk, acme, crops, a levels mp4).
| * Rules and chrome scale with the display's density; dark pages get visible ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | separators and focus tints Density: dp() in gui.zig draws every size given in pixels in logical pixels (SDL's window display scale, rounded, at least one), as acme scales Border, ButtonBorder and Scrollgap: the rules (rule_px), the tag rule, the grip's ring, its dirty gap and mode bars, the rail's gap to the text and its minimum, the thumb's inset, the rim, a bar cursor's minimum width, underline and strikethrough thickness (the atlas is dropped on a scale change), the rule-grab margin, and the Crt's scanline and mask periods (pardesScale in the post prefix). Cell- and tagline-measured sizes stay: the rail's width, Lift's blur and offsets, every motion; iResolution, fragCoord and iMouse stay in the display's pixels, as ghostty's. PARDES_TEST_SCALE forces a density in a harness (a test window is 1x otherwise, whatever display it lands on) and sizes the test font with it. Colours: a separator that stands off its page by less than 1.5:1 is lifted toward the text to 1.6:1 (helix, atelier, paper; a theme with no page, dark, is judged against #121212), and empty_col follows it. forge's own rule already stands at 3.83:1; what hid it was density: 2 device pixels on a 3x screen, now 6 (dp). A focused tag's tint closer than 1.5:1 to the plain tag on a dark page (1.25:1 on a light one) is pushed further the way it leans, as far as its text keeps its own contrast or 4.5: forge 1.23 -> 1.51 (#2d3440 -> #374253), and likewise dusk, orchard, ink, lagoon, clay, harvest, spectrum, solarium, forge_soft; daybreak, atelier, paper to 1.25. dark and helix gain a tint of their own (dark: #484566 under #c8c8c8, 1.54:1; helix: ramp 237 under 250, 1.65:1). Tag caret: past a full row's end, the G3 glide box sits on the tag's right edge, where the layer's caret and the pixel shell's are. Tests: every native theme's separator >= 1.5 and every dark one has a tint >= 1.25 with readable text; forge keeps its own border; the tag caret glide box at a full row; at 2x the spine, tag rule, ring, rail start and gap, and bar cursor double. Legacy-ThemeFile test strips helix's new pair. Goldens: snapshot goldens change in colour rows only, the tints (orchard #283a2f -> #314738 at boot, forge, helix and dark in theme/themesel). GUI goldens: the harness's single Del of the renamed wide pane is refused since ysqnkyoy (a rename counted as unsaved; fixed on the 9P line in wkzlsqvy), so scenes 09-17 were recorded with that pane open and the file was stale since the merge (08 and 18 also moved with ykvnltvs's full-row tag wrap). The goldens here were recorded with that pane closed (as wkzlsqvy will close it); against the parent's binary under the same conditions they differ only in the tints: 01-13 forge's (#2d3440 -> #374253, and the glyph edges on it), 14 ink's; 15-18 are byte-identical. Until wkzlsqvy is merged, a run here fails 09-17. Each commit reproduces its own recorded goldens on this machine (checked at kvtmqkyn). A test window is 1x unless PARDES_TEST_SCALE says otherwise, whatever display it opens on. Prebuilt shaders refreshed from their sources. Captures: .scratch/render/dark-borders/{before,after,compare,scale2,scale3}.
* | The docs: a log stat is the whole ring, changed's three forms, terminal ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | +Search rows' column range, /commands in the skill, read -t through FUSE Dogfood round 17 found these false or missing: the skill said a stat of log was one record's length; fs.md's changed covered only unsaved edits; a terminal's +Search row is `@p<serial>:<line>:<cols>`, not a line alone; the skill's /commands line predated `both`, setting values and the description; bash's `read -t` never times out on a followed file through FUSE. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A name written with a blank at either end is refused, not quietly trimmedGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | `name` trimmed blanks off both ends, so `trailing.zig ` named another file than the one written without a word. acme refuses any blank in a name, `bad character in file name` (xfid.c:650); pardes takes one inside (it names files with spaces) and now refuses one at an end, and a control character, in acme's words. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A root exec or look that fails logs its err against the pane it ran atGabriel Schneider29 hours
| | | | | | | | | | | | | | | | `err - exec: ...` said nothing of where the line ran; the root's exec and look run at the keyboard's pane, and their err now names it, as a click there would. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A detached session no frontend has sized is 160x50, not 80x24Gabriel Schneider29 hours
| | | | | | | | | | | | | | At 80x24 a script's third or fourth pane/new was refused for want of rows. `size` still sets any other size, and a frontend attaching its own. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | Mount dials its peer at once and fails if none answers; a peer out of reach ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | is said as `dial failed: <reason>` Mount took any well-formed dial and nothing was found out until the first look through it, which then said `look: look: dial`. Mount now probes the peer (version, attach, its root) with the turn out, and fails its write, mounting nothing, when none answers; the dial errors read as words, `no answer`, `timed out`, `hung up`, and a look through a peer gone since names its path: `look: /n/peer/f: dial failed: no answer`. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | New from a column's exec makes its scratch in the session's directory, as ↵Gabriel Schneider29 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]>
* | An event record never carries flag 2, as no expansion record follows; flag 1 ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | reads the first word In acme flag 2 promises a second record with the expanded range, and acme's libraries read one. F records set it always and a click with no range known set it, with none following: a library then took the next event for it. Records here carry the word already, so 2 is never set. Flag 1 now reads the first word as acme's lookup does, so `Msg hi` is 1. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | `size` is refused only when the panes' own minima do not fit, so it is monotonicGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | The check read how the weights shared the rows after the resize, not whether the rows could hold every pane's tag and two rows: a pane the proportional squeeze took short made `size` refuse, growing included, and refuse sizes it took a moment before. It now asks layout.minRows, the minimum placement asks, per column; a size they fit is taken, and a pane the resize took under its minimum gets its rows back. An out of range size says `invalid size`, which 9ns reads as EINVAL, as the doc had. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A look line takes `?re?` after its colon, as addr doesGabriel Schneider29 hours
| | | | | | | | | | | | | | | | `file:?foo?`, `@p5:?foo?` and `:?foo?` were not taken for addresses (the first character had to be one of `0-9+-/$.#,;`), so they went the path way and found nothing. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | `@p<terminal>:<address>` takes any address over the terminal's logical linesGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | Only a line number reached a terminal (the line way); any other address went to lookAddress, which wanted a file and said the terminal was no text. The body is now the lines a +Search of it lists, addressed from the cursor, and the match selected as a +Search row's range is. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A refusal over unsaved panes logs each as `unsaved <serial> <name>`, and its ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | err never cuts a list Exit's refusal joined every name into one err, cut at the record's cap in mid-name, so a script could not tell which panes held it up. Each pane is now an `unsaved` record before the err, and the err names the one pane or counts them: `4 unsaved panes: Modified (Exit again to discard)`. Restore, Del and Delcol refuse the same way. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A builtin's Verbose announcement is logged only when it succeedsGabriel Schneider29 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 Schneider29 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]>
* | The docs: Kill leaves bash and fish to run the rest of a line, index rows ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | split from the right, busy names its program, chord follow-ups, /commands rows, Exit's second refusal Dogfood round 16 took these literally and found them false: bash ran the echo after Kill as fish does; a name with spaces broke split(maxsplit=3) since the row ends with the column serial; pty/run's busy line names the program; /commands rows end in a description; a second Exit refusal names only panes edited since. 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 Schneider29 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 Schneider29 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]>
* | A new pane with no room under the last goes last all the sameGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | A pane from pane/new or New takes the bottom half of its column's last pane, as acme's coladd does (cols.c:62-65); when that pane is too short to give it a tag and 2 rows, the fallback halved the tallest pane and put the new one under it, mid-column (the 7 9 8 found). It now goes last all the same, with half the tallest pane's rows (its tag measured at the column's width). Tty from a pane's ctl already follows acme's makenewwindow (the active column first, util.c:456-467); fs.md now says so rather than beside that pane. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | @p<serial>:<address> takes any sam address, as file:<address> doesGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | @p<serial> took a line spot but an address after it (/re/, #n, $, 0/re/, a range) was dropped, the name failing the file check, and an unknown serial fell through to a search. The pane is now addressed like a file; a miss and a serial no pane has each log an err, and look reads back empty. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | One rule for failing builtins: the write fails, one err, no msgGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | A builtin that failed through a ctl failed the write and logged its err, but through look, exec, tagexec or a column's exec it only said so on the message row, logged as a msg, and the write succeeded. Now every click write runs its builtin as a ctl line does (ctl.captured): a failure fails the write, with its words (EINVAL for malformed input, else EIO or what the words name), logs one err and no msg or announcement. get's Modified and a look miss are shown but logged once, as their err. A control character's refusal names its reason, a failed click reads back nothing, and look never reads back a pane closed since. The special case for a cut-short Edit through exec goes (the rule covers it); fs.md's table of exceptions becomes the one rule, as does the skill. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | The docs: which writes fail, mark for one undo step, directory looks, the ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | skill's log line Round 15's doc findings: one table of which files fail a write and which only say so; mark to make a loop's data writes one undo step; the 100-byte scratch exemption and the chord write-back in the skill; the skill's log line with ask, answer, changed, newcol, delcol and the restored records; a directory looked at opens a terminal that runs ls, and why; a plain word in a terminal is listed in +Search, its rows @p<serial>. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A prompt a 9P request opens is an ask, answered with answer, never stackedGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | Save through exec on a terminal (or a scratch) opened its path prompt on screen, where no script could see or answer it, and a second Save stacked another. Opened while a 9P request is served, it now logs ask <serial> save path; answer <path> on the pane's ctl saves there and closes it, answer - closes it; asked again, the open one stands. A pane closed with it open logs answer <serial> -. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | Newcol at the column cap says so, and a desktop session holds 16 columnsGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | Newcol with every column slot taken, or a column too narrow to split, did nothing and said nothing. It now fails (a ctl write with it) with no space for a column: 16 max (ENOSPC through 9ns), or that the column is too narrow. MAX_COLS is 16 on a desktop, 6 on the board; each column costs about 8 KB of core state (the Pardes struct grows from 1.47 MB to 1.56 MB), most of it its tag's text. fs.md and the skill give the cap. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A chorded record written back with its two follow-ups runs once, with its ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | argument Writing a chorded exec's three records back as read ran the argument and its location as shell commands. The first record (flag 8) now takes the next record as its argument when it comes in the same write, else the one kept from the click (or, with none kept, waits for it); its two follow-up records, however they are written, are consumed as its, never run. Tested written back together and one per write. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | The docs: the raw paste's second Enter, event flags and write-back, a new ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | Tty's ls Round 14's doc findings: a raw pty/data paste of more than one line needs the second \r unless it ends in a newline (not only an indented last line); the event flag bits (1 builtin, 2 expansion, 4 filename, 8 chorded, with its two records) and what writing back each record kind does; why a new Tty types ls; and the sam box's N+1 for text without a final newline (out of range). Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | follow new: a log follower that starts at the endGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | A follower read the ring's whole history before anything new, and there was no way to skip it. Writing follow new instead of follow skips what the open froze and waits for what comes after (tail -n0 -f); fs.md and the skill say that follow replays first. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | /commands says what each word does, and that the root's ctl takes EditGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | A /commands line gave a word, its argument and root or pane, but not what it does, and called Edit a pane's word though the root's ctl takes it too. Each line now ends with -- and the first sentence of the word's doc comment (read from builtins.zig itself, so there is one copy), and Edit is both. fs.md says a pane's word such as Undo or Msg is refused at the root. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | An a, c or i text with no . line fails the Edit, as an open { doesGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | sam takes the end of input as a text block's end, so a block cut short (a write that lost its tail, an open closed early) inserted half of itself. An Edit whose a, c or i text never meets its . line now fails with "a, c or i text not ended by a . line" and changes nothing, as an unclosed { does; at a close, logged as an err. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A pane has a tagexec: a word run as a click in its tagGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | The workspace had /tagexec and a column its exec, but a pane's own tag could be clicked only by an event record. pane/<n>/tagexec runs each line as a click in that pane's tag (never redirected to its event reader, as a header's is not) and reads back what it touched. The README, fs.md and the skill list it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A looked word selects its next place in its own pane, as acme's look3Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | A plain word looked at (a look write, a right click, an ML record written back) opened another identically named +Search pane each time, until the panes ran out, and never wrapped. As acme's look3 searches the text for it (look.c:210), it now selects the word's next place in that pane after the dot, wrapping at the end, and opens nothing. LookWord list, a new root setting, keeps the +Search listing (search is the default); the rsearch snapshot and an output test that walk the listing say LookWord list. (The leader-path check over every builtin gets a comptime branch quota, as one more builtin took the GUI build past the default.) Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | The docs: sam gotchas, Restore's log order and paths, stream reads, layout's ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | current and active Round 13's doc findings: a sam gotchas box in the skill ($-1 with and without a final newline, N+1 at the end, ^/$ at the end, 2,1, y's leading empty piece, c's literal &, columns from 1); the skill's Restore log order made the true one (new, restore, restored, restoredcol); a relative Restore path is looked for in DumpDir then the session's directory, and a bare one takes the last dump; a follower sees each (xN) as a new line; reading data or xdata moves addr; a second read of look/exec on one fd is EOF (re-open or seek 0); /layout's current (the keyboard's column) and active (acme's activecol) defined; no room, not no rows left; why a scratch's dirty blocks nothing under 100 bytes; the changed record listed. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A file changed on disk never reloads over unsaved editsGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | A watched file rewritten on disk was loaded over a buffer's unsaved edits and its dirty flag cleared, so Exit and Del no longer asked about them. A clean buffer still reloads; one with edits keeps its text and stays dirty, says <file> changed on disk (get reloads it, Save overwrites it), logs changed <serial>, and its Save warns once before overwriting the newer file, as acme's Put refuses a file modified since read (exec.c:577); get asks as ever (winclean, exec.c:513). The restored-watch test's dirty case now keeps the snapshot, and the filewatch snapshot script (re-recorded by name) checks both halves: the edited pane keeps its edit and says so, the clean one reloads. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | The docs say what Restore renumbers and re-runs, what Tty takes, and how a ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | range reaches a REPL Round 12's doc findings: the README's ctl line gains Restore, Placement and size (still 45 lines, detail in fs.md); Tty's argument is a shell and Tty on a pane's ctl opens a new pane; a .py pane's range goes to its REPL as an MX<q0> <q1> event write-back; after a Restore panes are renumbered (columns count from 1 again, so often keep theirs), a command pane shows exit ? and does not re-run, and REPL bindings are gone. acme's p adds no newline to what it prints (ecmd.c:744, pdisplay), so pardes's p does not either. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | A command run from a column's tag gets its pane in that columnGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | A command from a column's tag ran in a command pane at the foot of the last column, however far from the tag, and could reuse a finished one anywhere. It now goes to the tag's column, under both placements, and reuses only a finished command pane in that column. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | Repl's unknown-language refusal names a few whole and points to the restGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | The refusal listed every language and was clipped to fit, ending mid-word. It now names six, those that start as the word did first (pithon: python), says how many more, and points at docs/tags.md, which lists them all. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | size says its limits, keeps every pane its rows, and ptys follow their panesGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | size refused an out-of-range line without saying the range, took a size too small for the panes (leaving bodies of no rows), and a terminal squeezed to its tag kept a pty of its old size. The refusal now names 20x6 to 4096x4096 (a bare size is EINVAL like any malformed line), a size that would leave any pane less than its tag and 2 rows is refused and undone, and a terminal's pty is resized with its pane down to one row (only a folded or unplaced one keeps its grid). Co-Authored-By: Claude Opus 5.5 <[email protected]>
* | Tty and Shell refuse a directory: not a shellGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | A directory passes access(X_OK), so Tty /etc made a pane whose shell exited 127 and Shell /etc was taken. The shell lookup now refuses a directory, and both say "not a shell: /etc is a directory" (Tty and Shell share one refusal, host_io.Shell.refusal). shellset's golden takes the shared wording (re-recorded by name). Co-Authored-By: Claude Opus 5.5 <[email protected]>