summaryrefslogtreecommitdiff
path: root/test/snapshots/ttyclick.golden
Commit message (Collapse)AuthorAge
* Config opens the startup file, DumpConfig the settings report, and the ↵Gabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | workspace tag reads Dump Themes Config Debug Exit Config (SPC f c) opens init, found as the settings report finds it (opts.startup_config_path: $XDG_CONFIG_HOME/pardes/init or ~/.config/pardes/init, and the macOS and Windows paths config.md gives). A pane that already holds it is gone to instead; a file on disk is looked at; with none, an empty pane is named for it, and Save makes its directory and any above it first (File.make_dirs, fs.makeDirs) before writing. Without a configuration directory it says so and opens nothing. The report Config used to open, the startup path and every live setting, is DumpConfig, in a +DumpConfig pane. No alias keeps the old meaning. The workspace tag's default words put Themes where NextColor was, NextColor staying a builtin, and Config beside it. A tag the user edited is kept as it was: a dump stores only an edited one (topbar_custom), and a restore puts back only that. theme.snap types NextColor onto the workspace tag to click it, and tagnav's walk back to Dump takes five long-word steps. The rest of the re-recorded goldens differ in the workspace words and their widths only. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* A tag that exactly fills its row stays one row, as acme's doesGabriel Schneider29 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]>
* A pane tag wraps onto rows and can collapse to one, as acme's doesGabriel Schneider29 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 terminal's Tty word names its shell, Tty+fish, and a click on it opens anotherGabriel Schneider29 hours
| | | | | | | | | | | | | | | A terminal's tag said Tty whatever shell it ran. The host's acknowledgement of the shell it forked now sets the word to Tty+<shell>, tinted like the tag's name. Word+arg is taken as Word arg, one word a tag can hold and a click can take whole (+ is a word character), so Tty+fish opens a terminal on fish, as Tty fish does; Tty takes the shell as its argument, which the host forks for that pane instead of the configured one. Every snapshot whose screen shows a terminal's tag changes: 69 goldens, re-recorded by name, whose 297 changed lines are all Tty becoming Tty+bash (the harness runs bash), the cursor columns and style spans that moved with it, or that text cut at a pane's width. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Exit quits and Kill stops commands, as in acmeGabriel Schneider29 hours
| | | | | | | | | | | | | | | | Kill quit the editor, which in acme is Exit; acme's Kill stops the commands it started. Exit now quits as acme's does (exec.c, rowclean): it refuses once, naming each pane with unsaved text, and quits when asked again with nothing edited since (a small scratch is not asked about). Kill, bare or with names, stops the commands pardes typed into a terminal (an exec, a middle click, a pty/run) while their shell's marks say they run, by SIGTERM to the terminal's foreground job, never to the shell (acme posts the kill note, which terminates). Both are session builtins; the topbar's Kill becomes Exit, same width, and every golden's topbar row changed by exactly that word (checked line by line); the builtins script scrolls one more row for the index's new line. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Make a pane's tag a Text edited with the body's own keysGabriel Schneider29 hours
| | | | | | | | | | | | | | The tag was a fixed buffer with its own one-line editor, a cursor measured across the path, and a hijack of the body's mode while it was typed into. It is now the pane's second Text: its own characters after the computed prefix (path, dirty marker, PDF page), which is never stored in it, so a rename or a dirty marker never moves its cursor. Normal and insert mode are the body's, undo included; `:` moves the keyboard between body and tag, the tag remembers its cursor and starts on Save, and executing a word from it hands the keyboard back. The mouse still sees the prefix: a sweep selects across it and a click on the path drafts a new name. Co-Authored-By: Claude Opus 5.5 <[email protected]>
* Notices become tagline bands at the top of the bodyGabriel Schneider29 hours
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | A message, a leader chord and a prompt used to share one row of body text at the bottom of a pane, wearing the tagline font and nothing else about a tagline. Now each one is a TagLayer of its own, emitted through the same renderHeaderLayer the pane and column tags go through, so it gets the tagline height, the small-font metrics, the band offset and the border for free -- none of which a body-grid row can have by changing its font role. The text is right aligned. The prompt stays on the canonical grid because it owns a cursor, and a cursor has to sit on a real cell. The body starts BELOW the bands rather than under them, the way tree-sitter context rows already worked. Pane.body_offset is how many rows they took and Pardes.bodyTop(pane, rect) is the one place that answers "where does the body begin" -- replacing fifteen copies of `if (tag_bottom) r.y else r.y + BOX_H` spread across the paint, hit-test, scroll, PDF and image paths, which is what let the bands and the text under them come adrift. Every notice is painted on the grid as well, because the grid is what a terminal client draws and a band it cannot see is a message it never gets; the GUI skips grid cells a tag layer covers, so nothing is drawn twice. Three bugs the bands exposed, fixed here: - a prompt band flush with the right edge put its caret one column past the pane, which the detached wire refuses -- so every frame was dropped for as long as the prompt was open. The band now reserves that column. - a click on a band mapped to Sel row 0, which is the TAG row: clicking chrome expanded a word out of the tagline and ran it as a builtin. - a watched file reloading under the editor changed the core without going through update, so needs_frame was never set and the reload was never drawn. Pardes.invalidate() is the name for that, and the file and theme reloads call it. A session can now drive its own 9P namespace instead of being refused one: ownMountSuffix answers what a path names inside this editors own tree and resolve, readLimit and write serve it from memory rather than making the syscall that never returns. The match is anchored to whole components under the registrys 9p/pardes/<name>, because a bare /pardes/<name> anywhere in a string would claim ~/src/pardes/<name>/README -- and, before write learned the same trick, write the trees bytes over the real file. readFileLimit and writeFile refuse instead, having no core to answer from. A toggle setting SETS when given `on` or `off` and only flips when it is bare, so the report LocationsConfig prints can be fed back as configuration and mean what it says. Snapshots: 97/98, from 0/98. The goldens were several commits stale and 17 scripts had stopped running; `config <line>` is a new script command that appends to the per-script startup config, so a script that clicks body coordinates pins `Verbose off` instead of counting the rows an announcement moves. nested-optout is left failing on purpose: two levels of nesting prepend vaxis F3 codepoints to typed lines, which is a real bug and is written down in docs/divergences.md with a repro. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
* trunk: resume before the Reload experimentGabriel Schneider2026-09-15
| | | | Empty marker on the last pre-Reload change. Keep the Reload experiment on reload (3801914), its first change on reload-start (200a1fc), and the unfinished performance investigation on reload-perf-wip.
* tests: a capture is a delta and a click names its word, so a tagline edit ↵Gabriel Schneider2026-08-25
| | | | stops rewriting the suite
* builtins + config: Save reaches every pane holding text of its own, and ↵Gabriel Schneider2026-08-25
| | | | takes a path argument
* host: the core owns the event loop; every platform becomes a vtable of ↵Gabriel Schneider2026-08-25
| | | | optional methods
* term_pane + builtins: terminal pane work, builtins/config additions, snapshotsGabriel Schneider2026-08-18
|
* 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.
* show New in every pane tag and enforce builtin-first Exec fallbackGabriel Schneider2026-08-10
|
* prioritize useful topbar commands and put Kill lastGabriel Schneider2026-08-10
|
* add New builtin for an empty temporary file in the calling columnGabriel Schneider2026-08-10
|
* the goldens catch up with the empty normal-mode boxGabriel Schneider2026-08-01
| | | | | | | | | | config.tag_normal became a space when the mode moved into the layout box, and 66 goldens still spelled the old bullet — the whole suite was red on one character. Regenerated serially, and every file's diff is that character and nothing else: replacing the bullets in the OLD goldens with spaces is byte-identical to the new ones, so nothing else moved under cover of the churn. The one bullet left in tutor.golden is the tutor's own sentence about the badge, which the app still draws.
* the mode is a character in the layout box, not a word in the taglineGabriel Schneider2026-08-01
|
* lsp: writer seam, ZLS introspection builtins, ctrl-click goto, SPC l groupGabriel Schneider2026-08-01
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | THE SEAM TAKES A WRITER. `lsp.query`'s `out` is a `*std.Io.Writer`, not a `*std.ArrayList(u8)`. The shell owns the buffer behind it (an `Io.Writer.Allocating`), so a backend never allocates the result, never frees it, and cannot get the allocator wrong — the invalid-free class of bug has nowhere left to live. It also deleted a parameter from five functions: they only ever took a `gpa` to allocate rows, and 0.16's unused-parameter error found every one. `lsp.row()` lost its allocator argument too. Since a Writer cannot rewind or be counted, `query` renders into a scratch Allocating first: the log wants an exact row count, and `explain` throws the rows away and prints narration in their place. INTROSPECTION. `SPC l i` (Lspinfo) and `SPC l w` (Lspwhy), in the `l` group that now holds every language command (see below). They exist because of the seam's own contract: a backend never fails loudly, which is right for an editor, but it makes a broken backend and a correct one that found nothing look identical from the outside. Every query now leaves a record — kind, file, offset, duration, row count, and THE ERROR `run` returned, which `catch {}` swallowed and which was visible nowhere. Lspinfo prints those, plus which ZLS is compiled in, which zig lib dir and whether it actually opens (the usual cause of "gd does nothing in std"), and what the backend answers versus refuses. It answers from ANY pane, including one with no file, because it is about the backend — which matters precisely when the pane you are sitting in is the problem; both shells now send status for a file-less pane. Lspwhy narrates the REAL resolution path. The trace is threaded through `goto` itself, so what it prints is the position context the analyser returned and the branch that actually stopped. A debug view that re-derives the logic beside it is one that can disagree with it. CTRL-CLICK IS gd. Mouse gained a `ctrl` field, set by both shells (SDL asked directly via GetModState rather than read off key-event bookkeeping, which a click with no prior keypress would miss). The flag rides the drag rather than firing on the press: a click does not place the modal cursor until RELEASE, so a query asked at press time would answer about wherever the cursor previously sat. A ctrl-DRAG still selects. The snapshot DSL gained a `ctrl-` button prefix (SGR bit 4, what a terminal sends and what vaxis decodes). test/snapshots/lspdebug.snap covers all three, including a PLAIN click in the same spot that must NOT jump — without it the test would pass on a bug that made every click a goto. Durations cannot live in a golden, so PARDES_LSP_NOTIME (set by the harness, like PARDES_DUMP) omits them. 58 snapshot scripts, hxdiff 360, hxparity 440, unit 46, gui build: all green. lspbench: 17/17, 0 false claims. THE WHOLE LANGUAGE GROUP LIVES UNDER SPC l. pardes keeps its own leader letters back. `SPC d` is Del again, `SPC k` is Kill, `SPC s d`/`SPC s r` are Dump/Restore and `SPC h t` is Tutor — exactly where they were before the language work touched them. The previous pass put the LSP commands on helix's bare `<space>` letters and moved pardes's builtins out of the way (Kill k->q, Del d->wc, Dump/Restore s?->f?, Tutor ht->T). That was the wrong trade. Those five are the most-pressed keys in the editor and predate the language work; an LSP command is something you reach for deliberately and can afford one keystroke more. So every LSP command keeps HELIX'S OWN LETTER and gains the `l` prefix: `<space>k` -> `SPC l k` (hover), `<space>d` -> `SPC l d` (diagnostics), r/a/h/s/S/D likewise. Nothing to re-learn but the prefix, and `Lspinfo`/ `Lspwhy` were already there. THE GOTOS ARE UNTOUCHED. `gd` `gD` `gy` `gi` `gr`, `]d`/`[d`, `]D`/`[D`, `=` and ctrl-click all stay exactly as helix has them — they never collided with anything, so there was never a reason to move them, and they are the ones you actually press mid-edit. leader.snap is restored to the pre-LSP script (its `key q` unmapped-key step works again now that Kill is back on `k`) plus one new step for `SPC l ?`. Its `SPC ?` root listing had to stop waiting on Restore: the full list grew to 33 rows and row 21 falls off the pane, so it watches an early row instead. DEPENDENCY IMPORTS NOW RESOLVE. `gd` on `@import("vaxis")` opens vaxis's root file; before, it silently did nothing while `std` worked perfectly. The asymmetry was not a wiring mistake. ZLS's uriFromImportStr answers exactly three ways: a relative `.zig`/`.zon` path from disk, `std` from `zig_lib_dir` (one directory, which we supply), and EVERY OTHER NAME only by running `zig build --build-runner` to discover the module graph. That last branch needs `zig_exe_path`, which this backend sets to null on purpose — so every dependency import returned `.none`. Confirmed twice over: in ZLS's source, and by `SPC l w` on the import string, which printed the STOP line naming exactly that branch. (The introspection builtin diagnosing its own backend on its first real outing is a decent argument for having built it.) We never needed a compiler for this: build.zig IS the module graph. It folds `root_mod.import_table` into a name -> root-source-file table at configure time and passes it as a build option; the backend consults it precisely where ZLS gave up. Correct by construction — a dependency added or renamed in build.zig cannot forget to update it — and it costs no subprocess, no build step and no runtime work. `SPC l i` now lists the table, since "is this name even importable" is the first question when a jump does nothing. Two limits, both stated in the code: a module whose root is a GENERATED file is skipped (it has no path until make() runs), and a file inside a dependency importing that dependency's OWN internal module name is still a miss — that would mean running its build.zig. TRAP: the table is folded out of root_mod.import_table, so `addOptions` had to move BELOW every `addImport` call. Attached where it was, the table is empty. TOPBAR GAINS `Help`, WHICH IS WHY `SPC ?` LOOKED BROKEN. A bare `pardes` boots straight into tty mode (main.zig: `args.len == 1`), where every printable key belongs to the shell — so SPC never reaches the leader, and `SPC ?`, the one thing that would tell you the leader exists, is exactly the thing you cannot press. Ctrl-b first and it all works; nothing was broken. But "the help is unreachable until you already know the escape hatch" is a bad answer, and there was no mouse route either: Help was the one builtin missing from the bar. Row 0 is not a pane, so a middle-click there is dispatched before any pane's mode is consulted — the word works in tty mode, which is the only reason it earns the width. APPENDED, not inserted, so every existing topbar word keeps its column and no golden's click coordinates move. test/snapshots/ttyhelp.snap pins it from a bare boot: click Help, get the list, shell still TTY at its prompt, then Ctrl-b + SPC ? for the keyboard route. All 58 goldens carry row 0, so all 58 moved. Verified mechanically that the only changes are the row-0 text and the row-0 style run (0-47 -> 0-52), plus: dump/restore record the topbar inside their .zon, and tagnav's `$`+Enter now executes `Help` rather than `Grep` because the bar's last word changed — still exactly what that step's comment claims it tests. THE DEPENDENCY FIX HAS A CEILING, NOW STATED. The module map is consulted from OUR goto handler, not from inside ZLS, so the analyser still cannot type the `vaxis` const: `gd` on `@import("vaxis")` opens the file, `gd` on `vaxis.init` finds nothing. That is now spelled out at the top of lsp_zls.zig and on moduleRoot rather than left implied, and `SPC l w` detects the case by name — if the left side of a failed field access is a known dependency it says so, instead of the generic "could not resolve". Lifting it means giving ZLS a real BuildConfig, either by letting it run the build runner (a subprocess, and with no cross-query cache that is once per keypress) or by synthesizing one into BuildFile.impl. Both are real work and neither is smuggled in. Also fixed while there: the field-access miss was only explained when ZLS returned null, but it returns an EMPTY SLICE when it typed the left side and found no such member. Both are "gd did nothing" from the outside; both are explained now.
* Builtins (Find, Grep) qol improvements and ui fixesGabriel Schneider2026-08-01
|
* trim the taglines: image renderer toggles and Colors/Crt become leader-onlyGabriel Schneider2026-08-01
| | | | | | | | | | | | | | | | | | | The image tag spelled out `Petscii <C64|Term> Ascii` and actOnSelection matched those words by hand before the builtin dispatch. They are three real Builtins now (SPC t p/l/a), so the by-word matching is gone, the tag is a plain `img <path> Del`, and with nothing live left in it the image early-returns in curTail, enterTagEdit and the tag-click guard go too — an image tag edits like every other pane's. Colors and Crt leave the topbar for SPC t c / SPC t r: set-once display switches should not spend width in a bar you read every frame. NextColor stays, it is the one you cycle repeatedly. Both remain full builtins. The snap scripts that clicked those words now press the leader instead, and images.snap finally exercises the palette and ascii toggles for real: its old clicks at x=57 landed on the neighbouring shell's tag, so that toggle had never been covered. The test PPM grew to 512x512 with LCG noise so the ASCII bitmaps actually win cells and dropping them visibly changes the art.
* steam deck nice versionGabriel Schneider2026-08-01
|
* clicks in a shell pane never change its mode: the body-press exitTagEdit no ↵Gabriel Schneider2026-08-01
longer force-drops to normal (it only restores the mode a tag edit hijacked, remembered in pane.tag_mode), and the left-release cursor pin keeps the terminal's mode (files still click into normal). New ttyclick.snap covers tty body click, tty tag round trip, insert body click.