summaryrefslogtreecommitdiff
path: root/src/tty/tty.zig
Commit message (Collapse)AuthorAge
* acmefs: pardes --fs serves acme's control filesystem over raw Linux FUSEGabriel Schneider2026-08-25
|
* host: the core owns the event loop; every platform becomes a vtable of ↵Gabriel Schneider2026-08-25
| | | | optional methods
* file_watch + builtins + config: richer watch semantics, new builtins, config ↵Gabriel Schneider2026-08-18
| | | | docs
* big slow change: prebuilt shaders (SPIR-V/Metal), core gui reflow, docs, web ↵Gabriel Schneider2026-08-18
| | | | + snapshot refresh
* look: richer path/range parsing, pdf rendering, corner-drag and stepgrain ↵Gabriel Schneider2026-08-15
| | | | snapshots
* clipboard, n/N and the tty prompt: three things that were half-wiredGabriel Schneider2026-08-12
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Three changes that all turned out to be the same shape -- a feature that worked in one direction, or for one pane kind, and quietly did not in the others. CLIPBOARD. Every register write emitted set_clipboard, so deleting one character threw away whatever the desktop was holding; multi-cursor yank took the join's early return and emitted nothing at all, so the same key reached the clipboard on one cursor and not on two. Nothing could READ the clipboard: the SDL shell had no SDL_GetClipboardText anywhere in it, and the tty shell never asked for OSC 52, so `p` from another application was dead in both. Now it is helix's split. y/d/c/p/P/R and the acme chords are the DEFAULT REGISTER and nothing else; the system clipboard is five words on helix's own letters -- SPC y, SPC Y, SPC p, SPC P, SPC R -- spelled as builtins so they land in Help and are executable like every other verb. The one exception is the tag `y` chord, which still mirrors out because a tag is always insert, so SPC cannot be pressed there, and copying the path out is the whole point of the chord. Reading is a new read_clipboard effect answered by an ordinary Event.paste, so the round trip is honest about being one: SDL and NSPasteboard answer inside the same drain, the browser answers a promise, and a terminal answers over OSC 52 or -- far more often -- refuses. A refused read is a paste that does not happen, and the request dies at the next keystroke rather than landing minutes late in whatever pane is focused by then. The tty shell also enables BRACKETED PASTE now and coalesces paste_start..paste_end into one event. Before this a paste arrived as a flood of individual key presses: plausible in insert mode, and in normal mode every pasted character ran as a command. n/N. They stepped the armed results buffer and immediately Looked each row, so you could not walk past a hit without opening it. They are a MOTION now: select the next look-able text, open nothing, and let Enter decide. What they step is the largest whitespace-delimited run look.resolve can act on (look.lookableSpan, wrapper punctuation peeled), over a RING of panes -- every pane that has performed a look, most recent first, then the output buffers that have not, newest first, and only if both are empty the pane in front of you. N is the exact inverse of n, computed rather than remembered: both directions ask the same question about the same spans and compare against the column the walk parks on, so x presses one way and x back land exactly where you started, pane boundaries and the ring's seam included. A ring rather than a list with two ends because a shell's cursor sits at the prompt, below everything it has printed, so a walk that could not come round would have nowhere to go on the very first press -- which is the case n/N were written for. One motion everywhere, no pane-kind or buffer-kind special case. The only thing a buffer may change is the GRAIN of what a step selects, and it does it with one flag rather than a branch: output_pane.Traits.commands (renamed from `executes`, which named one reader's behaviour rather than the fact) makes a row select WHOLE, because a ThemeSel line is a word to run and has no path inside it to pick out. `]d`/`[d` are not n/N -- they are helix's diagnostic motions, their job is to ARRIVE, and they still reach searchStep. THE TTY PROMPT. Leaving raw tty blanked the prompt row, and the command you had typed at that prompt shares the row, so it went too -- a shell out of tty read as output only. OSC 133 marks the row CELL by cell, so the two are separable: config.tty_blank = .prompt cuts the prompt's own columns and leaves the command, left-hugged at column 0 in line with the output under it rather than in a bay of blanks. .prompt_and_input is the old behaviour, kept. Because the row is now something you can put a cursor in, enterTty adds the hidden prompt width back before asking ghostty to walk the shell's own cursor to it -- the modal column on a cut row is short by exactly that much. Verified: unit-test 186/186 (nine new), snap 87/87 (new ttyprompt.snap), hxdiff 481 and hxparity 561 with 0 mismatches, tty and gui both build. And against the real binaries rather than the harness: in a pty, SPC y emits OSC 52 carrying exactly the selection while plain y emits nothing, SPC p issues the read and pastes the reply, and a bracketed paste of "dd..." inserts text instead of deleting two lines. In a real SDL window, SPC y then SPC p round trips through the system clipboard while the default register holds different text. Setting tty_blank back to .prompt_and_input reproduces all 86 old goldens byte for byte.
* replace ArrayLists with bounded storageGabriel Schneider2026-08-10
|
* review pass: fix the eaten Tab, drop the duplicated code, cover the gapsGabriel Schneider2026-08-10
|
* a pardes launched inside pardes hands its file to the outer oneGabriel Schneider2026-08-10
|
* add a Shell builtin, default fish, with per-family prompt integrationGabriel Schneider2026-08-10
|
* add the transient message rowGabriel Schneider2026-08-10
|
* animate anchored theme colors on native and tty backendsGabriel Schneider2026-08-10
|
* pipe selections through shell commandsGabriel Schneider2026-08-10
|
* render PDFs as a continuous vertical page stripGabriel Schneider2026-08-10
|
* fix PDF mouse-wheel scrolling jumping multiple pagesGabriel Schneider2026-08-10
|
* add New builtin for an empty temporary file in the calling columnGabriel Schneider2026-08-10
|
* tty/image: big harness + golden coverage passGabriel Schneider2026-08-10
|
* gui: draw image surfaces as GPU quads via glsl shadersGabriel Schneider2026-08-10
|
* open files follow the disk, and undo is the merge strategyGabriel Schneider2026-08-01
| | | | | | | | | | | | | | | | | | | | | | | | | | | An external update is pushed onto the undo stack exactly like an edit the user typed, so unsaved work is one `u` away and pardes never has to merge anything. That is the design, not an implementation detail: the whole feature is pushUndo() then setContent(). One inotify instance in the tty shell, blocking in readVec through std.Io on a concurrent task started beside the pty readers — after loop.start(), so the forkpty ordering is untouched. It watches the containing DIRECTORY, because an editor rewrites by rename-over and a watch on the file would follow the dead inode, and it listens for CLOSE_WRITE rather than MODIFY, which is one event per finished writer and most of the debounce for free. Our own Save does not reach the undo stack: each watch keeps a hash of the bytes last seen on disk and save_file restamps it. A hash rather than mtime and size because the reload has to read the file anyway. The core stays sans-IO — one watch effect out, one file_changed event in, and a shell that cannot watch simply never sends the event, which is what the gui and the web platform do. Linux only; fanotify is what the build system uses and is rejected in a comment: it exists for thousands of directories across mounts, and sixteen panes of inotify is a third of the code with no kernel floor. New golden filewatch: edit without saving, overwrite from a shell in another column, watch it reload, undo, get the unsaved edit back. None moved.
* the capability handshake cannot lose a race it never runsGabriel Schneider2026-08-01
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | queryTerminal(2ms) blocks on a futex until DA1 comes back, and the number has to beat one terminal round trip: local answers in microseconds, ssh localhost under a millisecond, any real link never. Measured against sshd with the replies delayed to model the wire, 2ms already loses at 5ms RTT. Losing it is worse than never probing, because vaxis splits detect from enable and only detect respects the deadline. The flag flips the moment the futex times out, so the two replies gated on it — explicit width and scaled text, both spelled as a cursor-position report — stop being read as probe replies and arrive at the app as shift-F3 and alt-F3 keypresses, while the ungated ones keep mutating caps from the reader thread long after enable already declined to switch those modes on. Over ssh the terminal sat in its default modes while caps claimed otherwise: kitty keyboard was never actually pushed, ever. So send the probes and resolve them on the loop. DA1 is last and terminals answer in order, so when the reader flips the flag every earlier reply is applied — no window to miss at any latency. Verified over real ssh at 5 through 500ms RTT: 7/7 caps and kitty keyboard actually enabled at every one, where before it was 5/7 and never. Startup is 2ms faster, no golden moves (nothing answers in the harness, and with no caps enable writes no bytes). Honest scope: I could not reproduce the reported stale characters, only the handshake bug behind them. The width half of the theory is inert — vaxis's Cell.width defaults to 1 and pardes writes one codepoint per cell with an explicit spacer, so gwidth, the only consumer of caps.unicode, is never called. That is written down so nobody re-derives it. If the dirty screen survives, the next suspect is vaxis's own carry-over for an escape sequence split across a read boundary (Loop.zig:174-190, wrong length and an off-by-one): four wheel events sent whole scroll four notches, the same four split at a `;` with a 60ms gap scroll zero. Network framing is exactly what makes those gaps. It is an input bug in a vendored dep and wants its own change.
* structure: backends into src/{tty,gui,lsp}, pane kinds and builtins into ↵Gabriel Schneider2026-08-01
their own files The core now lies FLAT at src/ and every subdirectory is one backend, so a file being in no directory at all is what says it is core. Pane-kind bodies leave pardes.zig for term_pane.zig / file_pane.zig / output_pane.zig, leaving it the layout, the event/effect machine and the generic render loop. Builtins are one struct each in builtins.zig, and the enum is folded out of the file's own declaration list at comptime — a zig file IS a struct, so the list of builtins and the builtins themselves are the same text. Adding one is writing a struct. Key paths deliberately stay one table for the config pass. Pure refactor: no golden moved.