diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-29 19:25:00 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:17 -0300 |
| commit | 57b30ba3e38153a4446626449b0fed5120da954c (patch) | |
| tree | 7b9381327a05791181a855bd8d4ba3cfb4df5301 /docs/config.md | |
| parent | 0fd908eea63d04886b269438aa7529d3dd422256 (diff) | |
| download | pardes-57b30ba3e38153a4446626449b0fed5120da954c.tar.gz pardes-57b30ba3e38153a4446626449b0fed5120da954c.zip | |
The docs and the 9P skill say each fact once, in the file that owns it, and say only what a live session does
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'docs/config.md')
| -rw-r--r-- | docs/config.md | 790 |
1 files changed, 141 insertions, 649 deletions
diff --git a/docs/config.md b/docs/config.md index 349814a6..f25c8746 100644 --- a/docs/config.md +++ b/docs/config.md @@ -1,79 +1,20 @@ -# Startup configuration +# Configuration -Native pardes builds use a per-user `pardes` configuration directory. Its -main command file is named `init`: +## The startup file -- Unix: `$XDG_CONFIG_HOME/pardes/init`, falling back to - `~/.config/pardes/init`. -- macOS: `$XDG_CONFIG_HOME/pardes/init` when that variable is set, otherwise - `~/Library/Application Support/pardes/init`. -- Windows: `%LOCALAPPDATA%\pardes\init`, with - `%USERPROFILE%\AppData\Local\pardes\init` as the fallback. - -On the two unixes `XDG_CONFIG_HOME` counts only when it is ABSOLUTE, as the -XDG base-directory specification requires; an empty or relative value falls -back to the home-directory form (`config.User.path`, and the test beside -it). Windows never consults it. An `init` that does not fit the `max_bytes` -read limit — 1 MiB, `config.User` in `src/config.zig` — or that cannot be read at all is -treated as no file: `load` takes the `readFileAlloc` error and keeps going. The -path still resolves, because "nothing is there yet" is the answer `Config` -exists to give. There is one case with no path at all: a native launch with no -`HOME` set (or, on Windows, neither `%LOCALAPPDATA%` nor `%USERPROFILE%`), -which `Config` reports as `no per-user config path`. - -`Config` (`SPC f c`, or the word executed anywhere) opens one refreshable -`+Config` pane. It reports the startup path and every live config-like value: -theme, colors, focus tint, syntax weight, wrapping, tag position, debug mode, the requested shell and the -executable actually resolved at the last spawn, requested/effective GUI font -and size, tagline scale, window opacity, ligatures (SDL GUI only), panel transition, -scene effects, hover delay, platform, native-image support, and (on SDL) whether -the executable uses live-built shaders or the paired prebuilt shader snapshot. -Platform-dependent rows say `unsupported` instead of looking like an off or -empty supported setting. The fields of `config.Runtime.Capabilities` gate -them and are stated once as plain data in `builtins.capabilities`: -`font_picker` is the SDL GUI and -native macOS only, `scene_shaders` the same two, `panel_transitions` every -hosted shell, `window_opacity` the SDL GUI and macOS, `window_blur` macOS -only, `ligatures` the SDL GUI only, and `tagline_font_size` -everything but the TTY and the board. `ligatures` is the exception to -`unsupported`: where it is off, `Ligatures` is not a builtin at all and -`Config` has no row for it. So -the TTY reports Font, TaglineSize and the scene shaders as unsupported; the -browser reports Font, panel transitions and the scene shaders as unsupported, -and its TaglineSize row reads `82 (build-time only)` — tagline font size is -its own capability precisely because GUI font SELECTION is native-only while -the browser still applies the compiled percentage to its DOM glyphs. -The startup path is printed whether or not a file exists — that is usually when -it is most useful — and is ordinary selectable text, so a right click on it -opens the file. Shell follows the same requested/effective/pending model as -Font. `Default shell` is the one used while no `Shell` is set: `$SHELL`, the -user's login shell, else `/bin/sh` (also when `$SHELL` names nothing -executable); an explicit `Shell` overrides it. `Shell <name or path>` is -refused unless it names an executable file (`Shell: shell "x" not found (...)`, or -`Shell: not a shell: /etc is a directory`; a bare name is looked for in the -usual bin directories), as `Tty <shell>` is, and a -bare `Shell` goes back to the default. The root ctl reads `Shell <the one the -next terminal runs>`. `Shell -effective (last spawn)` is the executable the native host really chose after -installation lookup and fallback. A changed request remains pending until a -terminal is spawned, because the core does not resolve native executables. +Native builds read one command file, `init`, from the per-user `pardes` +configuration directory: -The mutable global values live together in the plain `config.Runtime` record. -One plain capability record gates the setting registry, leader table, -`EffectCode`, and report; the compile-time setting table generates both setter -builtins and their `Config` rows. Exhaustive checks require every table-backed -toggle, transition, and scene-effect switch to occur exactly once, so those -generated setting builtins cannot quietly lose their query row or leave a -renderer switch unnamed. Manual pane-local actions remain with their payload (for -example an image tag reports its renderer choices); they are not global -configuration. +- Unix: `$XDG_CONFIG_HOME/pardes/init` (only an absolute `XDG_CONFIG_HOME` + counts), else `~/.config/pardes/init`. +- macOS: `$XDG_CONFIG_HOME/pardes/init` when set, else + `~/Library/Application Support/pardes/init`. +- Windows: `%LOCALAPPDATA%\pardes\init`, else + `%USERPROFILE%\AppData\Local\pardes\init`. -The browser build has no local user-config path and does not load this file. -(Nor does it have `Font`, the effect builtins or `EffectCode`, a language -backend, or ptys of its own — see `docs/web.md`.) +The browser build has none. A file over 1 MiB or unreadable counts as absent. -The format is one existing builtin command per line, using the same spelling -and argument parsing as commands executed inside pardes: +Each line is one builtin, spelled as it would be executed in pardes: ```text Theme orchard @@ -83,620 +24,171 @@ Shell zsh Wrap ``` -A line matches a builtin whose name takes NO argument only as that whole word: -`Kill` runs, `Kill something` does not. A builtin that takes one -(`takes_arg` in `src/builtins.zig`, or a `settings` row whose action is -`shell`, `theme`, `font`, `tagline_size` or `window_opacity` in `config.Runtime.settings`) takes -everything after the name as the argument. On a native build that is `Theme`, -`ThemeFile`, `Font`, `TaglineSize`, `Shell`, `Save`, `Restore`, `Attach`, -`Mount`, `Unmount`, `Find`, `Grep`, `Rename`, `WsSymbols`, `Look`, `Exec`, -`Msg` and `EffectCode`. -The SDL GUI also has `WindowOpacity`, which takes one argument. -(`Peek`, `Poke`, `Hexdump` and `Gpio` take one too, but they exist only where -`builtins.Board.enabled` holds, and that build has no config file.) - -`Theme <name>` wants one of the names in the compiled ring. Do not derive the -spelling — read it off `Themes` (`SPC t t`), which lists every one as the -exact `Theme <name>` line that selects it. `slug` in `tools/gen_themes.zig` -lowercases, folds punctuation runs to a single `_` and then TRIMS leading and -trailing ones (`penumbra+.toml` is `penumbra`, not `penumbra_`), and every -variant read out of a zed `.json` gets `_zed` on the end so it cannot collide -with a helix theme of the same name — zed's "Ayu Mirage" is `ayu_mirage_zed` -and `ayu_mirage` is helix's `ayu_mirage.toml`. The suffix goes on all of them -rather than only the eight that clash today, so a name cannot move when either -project gains or loses a file. A name that is not in the ring is ignored. - -The fifteen [native Pardes themes](themes.md) lead the ring: `orchard` (the -default), `dusk`, `ink`, `paper`, `daybreak`, `atelier`, `forge`, `lagoon`, -`solarium`, `spectrum`, `harvest`, `clay`, `forge_black`, `forge_soft`, and -`orchard_black`, then `acme` and `lapis`. `Themes` lists them first under -Pardes themes. They add coordinated -focus, search, diagnostic and terminal colors; `ink` and `daybreak` are high -contrast dark and light options. After them come the -[faithful ports](themes.md#faithful-ports) of well-known themes, a section per -family, then the legacy `helix` and `dark`, then everything imported. Where a -port takes an imported theme's name (`dracula`), the imported one gains -`_helix` (`dracula_helix`), as zed's carry `_zed`. `NextColor` walks the same -ring in the same order. - -`FocusTint` toggles the focused pane and column tag tints; it is on by default. -Workspace, column and pane command text can be edited directly; see -[editable tags](tags.md) for naming and saved-workspace behavior. -The column command row is always shown; an old `ColumnTags` init line is -ignored with a message. -`Placement acme` (the default) puts new panes where acme would, in the active -column; `Placement pardes` brings back pardes's own rules, which open a first -document in a column of its own. See [where new panes go](tags.md#where-new-panes-go). -`BootShell keep` (the default) leaves a shell alone when a document is dragged -into the left column beside it, whatever it holds; `BootShell replace` closes -such a shell there when it is the column's only one and nobody has typed -into it (no scrollback, cursor still on the first prompt line), the boot's -placeholder giving its rows to the document. Bare `BootShell` flips it, as -bare `Placement` does: a setting that chooses among words (`on`/`off` -switches, `Placement`, `BootShell`, `Crt`, `Bloom`, `Vignette`, `Grain`, `Lift`, `Motion`, -`ShaderAnimation`) steps to its -next value when written bare, as its word clicked in a tag does, and a value -it does not take is refused with the values it takes, which `/commands` also -lists. -`Verbose` toggles the message-row announcement every builtin makes of its own -name before it runs; it is on by default, and the builtins that own the message -row themselves (`Msg`) never announce. Turning it off leaves the row to the -messages a builtin chooses to write. -`MessageAnimation` toggles how a message comes and goes; it is on by default. -A message eases down into its row, fast at first and settling at the end, its -colour whole by half way (graphical frontends slide it out from under the -tagline as it fades up; a terminal only fades it in), stays until the next key or click as it -always has, then lingers for `MessageLinger` milliseconds (default 800) before -it dissolves into the page. `MessageFall` (default 180) and `MessageDissolve` -(default 150: leaving is quicker than arriving) set how long the fall and the dissolve take, also in -milliseconds; each timing is at most 60000, and `Config` reports all three. `MessageLinger 0` with -`MessageAnimation off` restores the old behaviour, a message cleared by the -very input that follows it. An updated line on a row already showing one swaps -its text in place rather than falling again. -`SyntaxBold` toggles bold syntax keywords; it is off by default. These -settings are shared by GUI and TTY, and `Config` reports their current -states. Like `Colors` and `Wrap`, these commands take no argument and invert -the current value. A `FocusTint` line in a fresh startup configuration disables -the tint; a `SyntaxBold` line enables the stronger keyword weight. Those two -appearance controls leave focus, selections and editing behavior unchanged. - -`Ligatures` (SDL GUI only) toggles a font's programming ligatures, such as -Maple Mono's `->` and `!=` drawn across their cells; it is on by default. -Off, every cell draws its own glyph, exactly as a font without ligatures does. -`Ligatures on` and `Ligatures off` set it explicitly. The native macOS shell -draws CoreText ligatures of its own, which this setting does not reach. - -`TreeContext` toggles sticky declaration headers for the current source pane. -It is off by default and appears in the default pane tag when a tree-sitter -grammar supports that file. `TreeContext on` and `TreeContext off` set it -explicitly. Scrolling inside a function, type or module keeps its enclosing -declarations above the body, with source line numbers, syntax colors and a -subtle background tint. Clicking a header moves the cursor there without -scrolling; scrolling upward reveals that source line as the headers recede. -`Dump` and `Restore` preserve the setting per pane. Customized tags retain -their text; the command can still be executed from any source pane. - -`TreeContextTagStyle` toggles the experimental tagline treatment for those -headers and is on by default. In graphical frontends it uses the tagline font, -line height and thin border. A matching separator marks gaps between the -headers' source lines. Turning it off restores body-sized context rows. -`TreeContext` itself still defaults off; this appearance option does not enable -it. `Config` reports the appearance option, and dumps preserve it. - -Search, Grep and LSP location-result panes include `LocationsConfig` in their -default tags. Custom tags keep their edits. - -Location highlighting is enabled by the command that produces a location list. -Plain reports, including `LocationsConfig`, keep ordinary text colors even -when their text resembles a location. `Mini` keeps its own syntax colors. -LSP references and goto results highlight the exact symbol span supplied by -the server, using the search-result colors. Source indentation is retained so -those byte ranges stay aligned; context and descriptive labels remain unmarked. - -`LocationsConfig` prints the current settings for Search, Grep and LSP location -results in an output pane. Execute the printed line to apply it again, or -supply just the fields to change: - -```text -LocationsConfig context:5 tscontext:on tslocations:off layout:stacked -``` - -- `context` is the number of source lines above and below each match (default - `0`). Overlapping context is shown once. These neighboring lines omit their - locations and keep their code aligned with the matching result. -- `tscontext` includes enclosing tree-sitter declaration headers in source - order (default `off`). Preceding neighboring context stops at the outermost - enclosing declaration, keeping unrelated lines above it out of the result. -- `tslocations` shows a location on the first line of each declaration header - (default `on`); continuation lines keep the same alignment without repeating - the location. Declaration headers use the same muted color as locations, with or - without their locations visible. -- `layout:stacked` (default) puts each location on its own line, followed by its - source preview. Hidden context locations do not add an empty address line. - Use `layout:inline` to put locations beside the source instead. +A builtin that takes no argument matches only as the whole line (`Kill` runs, +`Kill something` does not); one that takes an argument takes the rest of the +line. Blank, unknown, malformed or failing lines are ignored silently and do +not stop later ones. Text that is no builtin is not run as a shell command +(`Exec ...` still is). Key bindings are compile-time choices in +`src/config.zig`; `init` does not remap them. -In inline layout, result locations share padding in groups of eight matches, so a long path only -widens its own group. Context does not count toward the eight; at group boundaries, -it aligns with the nearer match (ties stay with the preceding group). Source -indentation is preserved. All visible locations start flush left; `n` and `N` -still stop on matches. -With `tscontext` enabled, asterisks after a match location show its declaration -depth (for example, `main.zig:42 ***`). Ordinary neighboring source lines retain -syntax colors; declaration headers do not. -A declaration context line ends with ` ...` when source lines are omitted -before the next displayed row from that file. -Source analysis is reused across result queries while the source bytes stay the -same. The cache retains at most 64 files and 64 MiB; it checks current buffer or -filesystem contents on each refresh. -Open file buffers supply context from their current edits. Missing files still -leave the original result available. These settings apply to subsequent result -generation and survive `Dump`/`Restore`. Invalid fields reject the entire -update; if a field appears twice, its last value wins. +`Config` (`SPC f c`) opens a `+Config` pane with the startup path (a right +click opens it) and every live setting; a setting the frontend cannot show +reads `unsupported`. The root `ctl` file reads the settings back in the +words a write takes ([fs.md](fs.md#the-root-ctl)). -`WindowOpacity <percent>` controls the opacity of everything in the SDL window -except text and the cursor, which stay fully opaque. Use `WindowOpacity 85` -to see the desktop through the editor, or `WindowOpacity 100` to restore full -opacity (the default). The argument must be a whole number from -0 through 100; missing or invalid values -leave the setting unchanged. At zero only text and the cursor remain visible. -Add the command to `init` to persist it. +## Settings -The same opacity applies to editor and embedded terminal backgrounds, UI -chrome, borders, scrollbars, gutters, and images. Overlapping non-text drawing -does not make those areas more opaque. Regular text, syntax colors, tagline -text, terminal glyphs, and the cursor keep their normal opacity. This does -not blend foreground colors into their cell backgrounds. The TTY -and other non-SDL hosts do not emulate this effect; their `Config` report says -`WindowOpacity unsupported`. +A setting that chooses among words (`on`/`off` switches, `Placement`, +`BootShell`, `Crt`, `Bloom`, `Vignette`, `Grain`, `Lift`, `Motion`, +`ShaderAnimation`) steps to its next value when given bare, as its word +clicked in a tag does; a value it does not take is refused, naming those it +does. `/commands` lists every builtin and its values. -On native Wayland, Pardes uses an alpha-capable transparent surface. It does -not use whole-window opacity protocols such as `wp_alpha_modifier_v1`, because -those would also fade the text. Presentation uses the existing GPU offscreen -renderer followed by a readback and SDL renderer upload per presented frame, -which adds rendering cost. Other SDL drivers can report background transparency -as unsupported unless their rendering configuration is alpha-capable. -If the rendering backend cannot apply the request, Pardes reports the error -and keeps the last successfully applied opacity. `Config` reports the -percentage and marks a request as pending until the SDL host handles it. - -## Crash records - -A panic appends to `crashes` in that same directory, beside `init`, and only -then prints to stderr (`src/crash.zig`, wired into the panic handlers in -`main.zig` and — because the macOS build roots there — `macos.zig`). stderr is -the one place this program cannot keep a trace: in the TTY shell stderr IS the -screen, so the trace lands on a grid the terminal is being reset out of; the SDL -and AppKit shells have no terminal at all; and a `--detach` session's stderr -goes wherever its launcher left it. The file is appended, never rewritten, and -each record is two lines — build metadata, then the panic message: - -```text -pardes 0.0.2 (a1b2c3d) 2026-09-03T11:20:44Z linux-x86_64 pid 48812 -panic: index out of bounds: index 4, len 4 -``` - -NO STACK TRACE, and that is a measured decision rather than an omission. The -frames stay on stderr, where `std.debug.defaultPanic` prints them. Collecting -them here instead HANGS the process: `writeCurrentStackTrace` called from a -panic handler before `defaultPanic` has run wedges at 0% CPU, and -`captureCurrentStackTrace` — which looks like the safe half — takes `SelfInfo`'s -rwlock exclusively on its first call, so a panic inside the walk leaves that -lock held and `defaultPanic` then waits on it forever. What makes `defaultPanic` -survive the same hazard is its own private `panic_stage`, which nothing outside -`std.debug` can reach. A crash that becomes a hang is worse than the crash, so -this file keeps only what it can gather without asking the process any -questions: which build, when, where, and what it said. - -Everything about it is best effort and silent: no config directory (a launch -with no `HOME`) means no file, and a directory that cannot be created or opened -leaves the panic exactly as it was before — stderr alone. The directory itself -is created if it does not exist, because the user who never wrote an `init` is -as likely as any other to hit a bug. One record at a time: two threads panicking -at once would otherwise interleave into one buffer, so the second falls straight -through to stderr. Only panics come here; a SIGSEGV is caught one level lower -(`main.zig`'s `debug.handleSegfault`) and unwinding one needs the signal's saved -CPU context. - -## Runtime theme files - -`ThemeFile <path>` loads one complete theme from a `.zon` file. An absolute -path is used as written; a relative path is resolved from the `pardes` -configuration directory, not from the process working directory. A typical -layout is: - -```text -~/.config/pardes/ -├── init -└── themes/ - └── mine.zon -``` - -and the corresponding init line is: - -```text -ThemeFile themes/mine.zon -``` - -After a successful load, hosts with document live reload watch the path with -the same parent-directory mechanism, so in-place writes and editor-style -rename-over saves reload the theme live. A malformed or incomplete save does -not replace the last valid theme; fixing and saving the file applies the next -valid snapshot. Selecting a compiled theme with `Theme <name>` or `NextColor` -stops the custom-file watch. - -Execute `DumpThemes` to write every theme compiled into the executable to: - -```text -<config directory>/themes/builtin/<name>.zon -``` - -The command replaces those generated reference files but leaves unrelated -files alone. Copy one into `themes/`, rename it, change its `.name`, and use it -as the starting point for a custom theme. The dumped file is also the complete -format, and it is `pardes.Theme` serialised by `std.zon.stringify`: the theme -name, thirteen required RGB roles (`sel_bg`, `sel_fg`, `tag_bg`, `tag_fg`, -`box`, `box_dim`, `kw`, `str`, `num`, `comment`, `lineno`, `scroll_track`, -`scroll_thumb`), nullable `bg`/`fg`, and either a 16-color RGB `palette` or -`null`. Optional nullable RGB roles extend this format: -`tag_active_bg`, `tag_active_fg`, `tag_name_fg`, `tag_active_name_fg`, `border`, `empty_col`, `lineno_active`, `search_bg`, `search_fg`, -`diagnostic_error`, `diagnostic_warning`, `diagnostic_info`, -`diagnostic_hint`, `tag_sel_bg`, `tag_rule`, `box_border` and `box_dirty`; -`sweep_bg` and `sweep_fg` take three triples, and `rule_px` and `rail_px` a -pixel count. Missing new roles use backward-compatible defaults, so -previously exported files remain valid. [Theme customization](themes.md) -describes each role and its fallback. RGB values are three-byte arrays, and -hex literals are accepted. The original fields remain required; there is no -inheritance or partial override layer. - -`tag_name_fg` gives the filename at the end of a pane's path a separate -foreground. `tag_active_name_fg` can adjust that tint for active tags; it falls -back to `tag_name_fg`. When both are omitted or `null`, the filename uses -the corresponding tag foreground. All canonical themes use a different hue -at similar perceived brightness to the surrounding text in both states. -Directory text and tag commands retain -their regular colors; selecting filename text uses the selection colors. -Terminal tags use this color for `Tty`, which comes immediately after the path. - -`Filter` in a terminal's tag projects that pane's ANSI colors through the -active theme, and does it in two stages. The default foreground and background -roles are mapped FIRST, because ghostty-vt generates the whole 256-color -projection from that pair; every other color follows, by reducing it to its -nearest canonical xterm key and reading the key back out of the projection. - -That reduction compares RGB triples, so it knows about hue and nothing about -the page — and the projection's cube corners ARE the two anchors, which is how -a foreground used to end up painted the exact color of the paper behind it -(`\x1b[38;2;255;255;255m` on acme's `#ffffea`, and the ANSI black a shell -writes with `\x1b[30m` on either dark theme). So a foreground additionally has -to keep `tty_filter_min_contrast` — a WCAG ratio, `1.5` by default — against -the mapped background. One that cannot is not mapped: it takes whichever of -the theme's own two anchors is still visible on that background. Backgrounds -are exempt, since a background is the page the floor is measured against. Set -the constant to `1.0` to accept every projected color, collapses included. - -`Shell <name>` sets the binary that the NEXT terminal pane execs; panes -already open keep the shell they are running. A bare name is resolved against -the handful of directories a shell actually lives in, not `$PATH`. - -On Linux, `Tty9p` (`SPC n 9`) starts that shell with a private kernel 9P mount, -asking sudo inside the new terminal. `$PARDES_MOUNT` names the mountpoint. -The installed `pardes-v9fs` helper lives beside the editor; development builds -can set `PARDES_V9FS_HELPER` to its absolute path. See [v9fs.md](v9fs.md). - -Ctrl-B switches between raw TTY and editor mode. Plain Escape at a detected -shell prompt hops back to the previous pane. Other keys, including Ctrl-O, -Ctrl-W and modified Escape, belong to the child. Use `Mode` in the pane tag to -return to editor mode in place. Desktop paste events still feed the terminal. - -The paste chords are the exception the window keeps. Ctrl-V types the yank -register at the program, and Ctrl-Shift-V asks the desktop for its clipboard -and types that; both go through the program's bracketed paste when it has -asked for one. Neither reaches the child as a keystroke, so an application -that would otherwise answer Ctrl-V by reading the system clipboard itself -never gets the chance to read the wrong thing. - -`Font` and `Fonts` exist ONLY in the SDL GUI and native macOS builds — a -terminal's font belongs to its emulator and a browser's to the page — so a -`Font` line is one of the silently-ignored ones everywhere else. Both builds -resolve the name by walking the font directories on every lookup, so a face -installed a moment ago is findable. - -Use `Font <name>:<size>` to change face and size together, for example -`Font MartianMono-NrRg:18` in the startup file or an editable tag. Fractional -sizes such as `:18.5` are supported; the accepted range is 8–72 (pixels in SDL, -points on macOS). `Font <name>` without a suffix preserves the current size. -Invalid sizes or unknown faces leave the current font unchanged. - -The SDL GUI also has a tiny optional workspace-tag companion: `Pet cat`, -`Pet frog`, or `Pet off` (the default). Its original pixel sprite walks and -idles in the trailing blank area of the global tag only. It hides while that -tag is being edited, when the tag is full, or when the font leaves too little -room; it never replaces text, receives clicks, or appears in pane/column tags. -Animation runs at ten steps per second, uses theme ink, and requires no image -assets or shaders. Put `Pet cat` in the startup configuration to keep it; -`Pet off` disables the companion and its animation. Other hosts ignore the -startup command. `Config` reports the current choice in SDL. +| setting | default | | +|---|---|---| +| `Theme <name>` | `orchard` | names as `Themes` (`SPC t t`) lists them ([themes.md](themes.md)); `NextColor` walks the ring | +| `ThemeFile <path>` | | a `.zon` theme, relative to the config directory; reloads live when saved | +| `FocusTint` | on | tint the focused pane's and column's tags | +| `SyntaxBold` | off | bold syntax keywords | +| `Verbose` | on | a builtin announces its name on the message row | +| `MessageAnimation` | on | messages ease in and dissolve | +| `MessageLinger`, `MessageFall`, `MessageDissolve` | 800, 180, 150 | milliseconds, at most 60000 | +| `Placement acme\|pardes` | `acme` | where new panes go ([tags.md](tags.md#where-new-panes-go)) | +| `BootShell keep\|replace` | `keep` | `replace` closes the untouched lone shell a dragged document lands beside | +| `LookWord search\|list` | `search` | a looked-at word selects its next place, or lists all in `+Search` | +| `Shell <name or path>` | `$SHELL`, else the login shell, else `/bin/sh` | the shell the next terminal runs; a bare name is looked for in the usual bin directories, not `$PATH`; bare `Shell` returns to the default | +| `DumpDir <dir>` | `$XDG_DATA_HOME/pardes`, else `~/.local/share/pardes` | where `Dump` writes; `~/` is home; bare returns to the default | +| `TreeContext` | off | sticky declaration headers in a source pane (per pane, dumped) | +| `TreeContextTagStyle` | on | draw those headers in the tagline style | +| `LocationsConfig ...` | | Search, Grep and LSP result layout (below) | +| `Wrap`, `Colors`, `Tagbottom`, `Debug` | | toggles | +| `Font <name>[:<size>]`, `Fonts` | | SDL and macOS only; size 8-72 (pixels in SDL, points on macOS) | +| `TaglineSize <1-100>` | 82 | tagline face, percent; SDL and macOS | +| `WindowOpacity <0-100>` | 100 | SDL only: everything but text and the cursor | +| `Ligatures` | on | SDL only; macOS draws CoreText's own | +| `Pet cat\|frog\|off` | off | SDL only: a sprite in the workspace tag's blank space | -`Font` is asynchronous at the renderer boundary. `Config` therefore keeps -requested name/path/size, pending state, and the effective face/point-or-pixel size -as separate facts; a failed request never gets reported as the face on screen. -Taglines use a distinct face size in both native GUI renderers. Execute -`TaglineSize <percent>` to change it live, for example `TaglineSize 70`; the -accepted range is 1 through 100 and the default comes from -`gui_tagline_font_percent` in `src/config.zig` (82). The native renderer -remeasures both the glyph and its visible tag band while retaining body-grid -pane geometry. Graphical tag text uses the smaller face's measured monospace -advance and fills the available pixel width, including workspace and column -tags. Text selection and scrolling use that same capacity; drag grips retain -their physical body-grid width and position. The 100% ceiling is -deliberate: a tagline remains exactly one logical grid row, so a larger face or -band would overlap its pane body or a neighbour instead of leaving the body -grid stable. `Config` reports the active percentage. The browser applies the -same compiled percentage to its DOM glyphs but has no runtime setter. +`Tty9p` (`SPC n 9`) is described in [v9fs.md](v9fs.md); the +`PARDES_V9FS_HELPER` variable points development builds at the helper. -Both native GUIs separate the reduced-height global and pane tagline bands with -a `gui_topbar_pane_border_px` physical-pixel rule. Set it to zero to leave it out. -`gui_topbar_pane_border_rgb` can pin an RGB color; its default `null` follows -the theme's `border` role in SDL (falling back to `scroll_track` for older -themes), and `scroll_track` in the macOS shell. Every band is centred in its -row, so the workspace, column and pane tag text share one baseline offset and -the anchors get the same margin above, below and to the left. With `Tagbottom` -enabled, a tagline on the final grid row is bottom-aligned so the unused -half-band does not show beneath it. The rule lives in the core -(`pardes.taglineBandOffset`), and the macOS shell reaches it over the C ABI -rather than keeping its own copy. When the window is not a whole number of cells, -bands and rules at the right and bottom edges run on through the leftover pixels. +### Terminals -What happens to a codepoint the chosen face has no glyph for differs by shell. -The SDL GUI falls back through a chain it builds itself: embedded Adwaita -Mono, then installed `NotoSansMono-Regular`, `DejaVuSansMono`, -`SymbolsNerdFont-Regular`, `NotoSansSymbols2-Regular`, -`NotoSansSymbols-Regular`, and `DejaVuSans`, in that order, missing entries -skipped; the rasterized glyphs are retained in its GPU atlas. The macOS shell -has none of that and needs none. It embeds no font. Its default face is -`NSFont.monospacedSystemFont`, resolved through the descriptor rather than by -name, and `Menlo` only if that face turns out not to be fixed-pitch — a -proportional face in a fixed grid is a broken screen, not a cosmetic problem -(`PardesView.defaultFace`). A `Font <name>` arrives as a PATH the core already -resolved by walking the font directories, and a file CoreText cannot measure -leaves the face already on screen rather than substituting one, because the -alternative is a terminal with no way back out (`PardesView.fromFile`). -Per-codepoint fallback is CoreText's own cascade at draw time. What the shell -caches is `CGGlyph` ids, not pixels. +Ctrl-B switches a terminal between raw input and editor mode. Plain Escape +at a detected shell prompt hops back to the previous pane; other keys, +Ctrl-O, Ctrl-W and modified Escape included, go to the program. `Mode` in the +tag returns to editor mode in place. Ctrl-V types the yank register and +Ctrl-Shift-V the desktop clipboard, through bracketed paste when the program +asked for it; neither reaches the program as a keystroke. -Blank, unknown, malformed, or unsuccessful lines are ignored silently, and a -bad line does not prevent later lines from running. Top-level text that is not -a builtin is not sent to a shell. (`Exec ...` remains an ordinary builtin and -therefore keeps its normal behavior.) Key bindings remain compile-time choices -in `src/config.zig`; this startup file does not remap them. +`Filter` in a terminal's tag maps its ANSI colours through the theme, +keeping each foreground at least `tty_filter_min_contrast` (WCAG 1.5, in +`src/config.zig`) against its background. -## Panel and scene effects +### Location results -Exactly one panel transition is selected at a time. Executing its builtin a -second time turns it off; selecting another replaces it: +`LocationsConfig` with no argument prints the current settings as a line +that can be run again; with fields it changes only those: ```text -PanelSlide -PanelZoom -PanelDissolve -PanelAscii -PanelVertical -PanelEdges -PanelFall -PanelWave -PanelCurtain -PanelScramble -PanelType +LocationsConfig context:5 tscontext:on tslocations:off layout:stacked ``` -All panel transitions start off. To disable one, execute its builtin again: -`PanelDissolve` turns off an active dissolve, and `PanelAscii` turns off an -active ASCII transition. `Config` shows the active command under -`Panel transition`. Remove that command from your startup configuration to -keep it off after restarting. Executing a different transition enables that -one instead; these commands are toggles, not an unconditional animation-off command. +- `context` (0): source lines shown above and below each match. +- `tscontext` (off): include the enclosing tree-sitter declaration headers. +- `tslocations` (on): show a location on each declaration header. +- `layout` (`stacked`): the location on its own line; `inline` puts it + beside the source, padded in groups of eight matches. -Slide uses cubic ease-out and zoom uses an -overshooting ease-out-back. Dissolve and ASCII compare the last successfully -presented grid with the new one. Dissolve switches visually changed cells at -stable noise thresholds. -ASCII walks every changed single-byte printable glyph -from its old `u8` value to its new one, spending that byte distance as the -frames of the walk. The walk is eased in and out: a glyph creeps at both ends -and crosses the middle of its distance in a few large skips, inside the same -number of frames a constant one-value-per-frame walk would have taken. -Glyph-stable style changes -and non-ASCII graphemes become canonical immediately. A walk is capped at -twelve movement frames, and each pane lasts only as long as its longest walk. -The core computes and composes that semantic diff once for every backend; -pixel attachments, which have no character value, pass through unchanged. +An invalid field rejects the whole line; a repeated field's last value wins. +The settings survive Dump and Restore. Source analysis is cached for 64 +files and 64 MiB. -Six further transitions are character *motion* over the same frozen/new grid -pair, and are composed in the core the same way: +### Effects -- `PanelEdges` — whole rows slide in from alternating screen edges. -- `PanelFall` — columns rain down into place, each with its own head start. -- `PanelWave` — a vertical ripple travels across the pane and decays. -- `PanelCurtain` — a wipe, left to right, behind a soft edge two cells wide. -- `PanelScramble` — every cell churns through printable ASCII and locks onto - its final glyph at its own stable noise threshold. -- `PanelType` — reading-order reveal with a caret sitting on the write head. +Panel transitions, one at a time; running the active one again turns it +off: `PanelSlide`, `PanelZoom`, `PanelDissolve`, `PanelAscii`, +`PanelVertical`, `PanelEdges`, `PanelFall`, `PanelWave`, `PanelCurtain`, +`PanelScramble`, `PanelType`. All start off; the web shell has none. -Unlike dissolve and ASCII these carry *every* glyph in the pane, changed or -not: text flying in from a screen edge has to bring its unchanged glyphs with -it. A cell whose glyph has not arrived shows the frozen old cell rather than a -blank or a blend, so every intermediate frame is made of real characters. No -cell is a valid input target until its own glyph has settled. +Scene passes (SDL GUI, and a GUI attached to a detached session), each at a +level 0-3 (`on` is 2), all off by default: `Crt`, `Bloom`, `Vignette`, +`Grain`. `Shader <file.glsl>` adds a Shadertoy file written for ghostty to +the chain (`Shader off` removes it; it recompiles when saved); +`ShaderAnimation off|on|always` says when the chain animates by itself. -Vertical is a pane-lifecycle effect: a newly added -pane rises from below inside its own fixed box, and a deleted pane's frozen -content drops back down; surviving panes are never animated. The TTY -implementation performs its remaining geometry/dissolve operations directly -on a copy of the core-composed presentation grid. -The SDL and native macOS GUI implementations pass plain panel tracks to their -GPU shaders, including native image/PDF pixels; layout itself commits -immediately and remains the one authoritative geometry. DOM web intentionally -does not expose these builtins: its renderer is selectable HTML/CSS and has no -canvas or shader stage. +The focused pane can stand off the page: `Lift shadow|rim|auto|off`, +`InactiveDim <percent>`, `Motion off|crisp|smooth|bouncy|playful` +(default `smooth`), `SelectionGlow`, `HoverGlow`, `Occlusion`, `Parallax`, +`JumpTrail`, `ChipShadow`, `ThumbFlash`, `CursorBlink`, `GripWidth <50-300>`. +Most are GUI-only; `InactiveDim` works everywhere, and `JumpTrail`, +`ChipShadow` and `ThumbFlash` are the terminal's. No effect may lower the +contrast of text, the selection or a focus indicator +([effects.md](effects.md)). -The scene effect is `Crt`, at a level from 0 (off) to 3; `on` is 2: +`EffectCode <effect>` lists that effect's sources under `/virtual` when the +build embeds them (`-Dembed-sources=true`). `zig build shaders` refreshes +the committed SPIR-V with its GLSL. -```text -Crt -Crt 3 -``` +### Look preview -The SDL GUI runs it as the bundled pass of its post chain, which also takes -Shadertoy files written for ghostty (`Shader ~/crt.glsl`, `Shader off`; -a file compiles again when it is saved, and a save that fails to compile -keeps the last good one and says why), and -`ShaderAnimation off|on|always` says when the chain animates on its own. A -GUI attached to a detached session runs the session's chain the same way. With -the chain empty the pass is bypassed. CRT works in linear light with restrained -scanlines, mask, bloom and vignette, and no curvature, so clicks land where -they are drawn. Its slow hum and dither move on their own, so with Crt on the -GUI keeps drawing while idle (under `ShaderAnimation on`, while focused); -the other bundled passes are still and let it rest. +Resting the pointer on text for about 32 ms (`look_preview_delay_frames`, 2 +frames) tints what a right click would look at, with no other effect. Set +`look_preview_delay_frames` to `null` in `src/config.zig` to turn it off. -Three more bundled passes take the same levels, each off by default and -each costing nothing while off: +## Themes from files -```text -Bloom 2 the brightest ink glows a little (only what is brighter than - the page; a dual Kawase blur at half size and down) -Vignette 2 the window's corners fall a little into shade -Grain 2 the page's own ground takes a fine, still grain, like paper -``` +`ThemeFile themes/mine.zon` loads a complete theme; a malformed save keeps +the last good one, and `Theme <name>` stops the watch. `DumpThemes` writes +every compiled theme to `<config dir>/themes/builtin/<name>.zon`: copy one, +change its `.name`, and edit. The format is `pardes.Theme` as +`std.zon.stringify` writes it; [themes.md](themes.md) describes each role. -None of them touches a tag, a grip, a notice or the cursor, and each is -capped per theme so text and the selection keep their own contrast or 4.5 -(docs/effects.md). - -The focused pane can stand off the page (SDL GUI, off by default): - -```text -Lift shadow a soft drop shadow, on the other panes' bodies only -Lift rim a hairline just above the focused tag (light, or shade on a light page) -Lift auto a shadow on a light page; on a dark one the others recede -Lift off -InactiveDim 30 the unfocused panes' text fades 30% toward its ground -Motion smooth off, crisp, smooth (the default), bouncy or playful -SelectionGlow on a soft halo of the selection's colour round it in the body, - fading in over 100 ms, never over a tag, a grip or the cursor -HoverGlow on a soft glow under the word a look-hover would open, fading - in over 80 ms -Occlusion on pane bodies darken faintly toward their edges (2%), never - over the cursor -Parallax on a theme's page pattern (lapis's dots) moves with the text - at a quarter of its speed -JumpTrail on in a terminal, a jump of the cursor of three cells or more - leaves a trail of a few cells that fades in 120 ms - (truecolor terminals; a pixel shell glides instead) -ChipShadow on in a terminal, a notice chip casts a shadow a cell right and - down: half blocks on blank cells, a darker ground on text -ThumbFlash on in a terminal, a pane's scroll thumb brightens as it scrolls - and fades back over 250 ms -CursorBlink on the cursor blinks, solid while typing and half a second - after, eased at each edge, solid after 10 idle seconds -GripWidth 150 the grip's button and the scrollbar under it, percent of the - theme's rail_px (12px at a 17px tagline; 50 to 300) -``` +## Dumps -`InactiveDim` works everywhere (a grid shows it at once) and never takes a -pair below its own contrast or 4.5. Under `Lift auto` on a dark page it is 30 -while unset. `Motion` sets how every such effect moves: see docs/effects.md. +`Dump` writes `pardes-<date>-<time>.zon` (UTC) in `DumpDir`, creating the +last directory if it is missing (a missing parent fails `no such +directory`); `$PARDES_DUMP` overrides the file. `Restore <path>` looks for a +relative path in `DumpDir`, then in the directory pardes started in; bare +`Restore` takes the last dump this session wrote. `pardes -l <dump.zon>` +starts from one. -`EffectCode PanelAscii` or `EffectCode Crt` lists the current backend's -build-embedded source paths under `/virtual`. Look opens each full file; -no checkout is needed, but the build must carry them (`-Dembed-sources=true`, -off by default); otherwise the command reports them unavailable. TTY exposes grid transitions, native GUI builds also -expose scene shaders, and web has neither. Shared implementations share paths. -SDL reports whether GLSL was compiled during this build or came from the -`-Dprebuilt-shaders` snapshot paired with the committed SPIR-V. -`zig build shaders` refreshes both files of every pair together, -so editing live GLSL without that explicit refresh changes neither half of a -prebuilt executable. +A dump keeps panes, columns and their tags, each text pane's selection, the +theme, and the settings that differ from a fresh session's; the font stays +the frontend's. A terminal comes back with its last MiB of output as +history, a `── restored history ──` line, and a new shell in its old +directory; a command pane comes back finished (`exit ?` if it was running). +Undo history and REPL bindings are not kept. A dump holds at most 6 columns. -## Delayed Look preview - -The preview is enabled by default. Moving the pointer onto selectable text and -leaving it still for `look_preview_delay_frames` — 2 animation ticks, and -`animation.frame_ms` is 16, so about 32 ms — paints a subtle theme-derived -preview of the exact operand a right-click Look would receive. -Repeated motion reports in the same semantic grid cell do not restart the -delay. The preview uses the same side-effect-free word/path expansion as Look; -it does not focus a pane, move a cursor, install a selection, activate a PDF -page, or execute anything. Motion to another operand, pointer leave, input, -pane teardown, and relevant content changes cancel it. - -Set this compile-time option in `src/config.zig` to disable the feature: +## Crash records -```zig -pub const look_preview_delay_frames: ?u16 = null; -``` +A panic appends two lines to `crashes` beside `init` (build, time, platform, +pid; then the panic message) before printing to stderr. There is no stack +trace in it: collecting one from a panic handler can hang the process. The +trace stays on stderr. -## Build-time configuration +## Build options -Runtime settings live in `src/config.zig`. Build options are listed below; -`zig build --help` lists the options available for the selected platform, -including the standard `-Dtarget` and `-Doptimize` options. +`zig build --help` lists the options for the selected platform. | option | values | default | |---|---|---| -| `-Dplatform` | `tty`, `gui`, `web`, `macos`, `esp32p4` | absent builds the tty cli and the SDL gui together | +| `-Dplatform` | `tty`, `gui`, `web`, `macos`, `esp32p4` | absent: the tty and SDL shells together, installed into `~/.local` | | `-Dstatic` | bool | `false` | -| `-Dquic` | bool; 9P over QUIC using system OpenSSL 3.6+ | `false` | -| `-Dmupdf` | bool | on for a native target, off for web and esp32p4 | -| `-Djpx` | bool | `true` — JPEG 2000, and with it scanned PDFs | +| `-Dquic` | bool; 9P over QUIC with system OpenSSL 3.6+ | `false` | +| `-Dmupdf` | bool | on natively, off for web and esp32p4 | +| `-Djpx` | bool; JPEG 2000, and with it scanned PDFs | `true` | | `-Dtree-sitter` | `disabled`, `zig`, `minimal`, `full` | `full` natively, `zig` for web, `disabled` for esp32p4 | -| `-Dtheme-animation` | bool | on everywhere except `-Dplatform=esp32p4` | -| `-Dworkspace-tag` | bool; draw the workspace tag row — the macOS shell hands its commands to the native menu bar instead | on except on `-Dplatform=macos` | -| `-Dprebuilt-shaders` | bool | on for a bare `zig build`, off when `-Dplatform` names a shell | -| `-Dtracy` | path to a Tracy source checkout | off | +| `-Dembed-sources` | bool; serve the sources under `/src` | `false` | +| `-Dstamp-commit` | bool; the git commit in `--version` and crash records | on for release builds and the `~/.local` install | +| `-Dtheme-animation` | bool | on except for esp32p4 | +| `-Dworkspace-tag` | bool; draw the workspace tag row | on except for macOS, whose menu bar carries it | +| `-Dprebuilt-shaders` | bool; embed the committed SPIR-V | on for a bare `zig build`, off with `-Dplatform` | +| `-Dtracy` | path to a Tracy checkout | off | | `-Dmacos-identity` | codesigning identity for `pardes.app` | `-` (ad-hoc) | | `-Ddump` | a `dump.zon` to embed in the web shell | none | -| `-Dtest-filter` | substring; run only tests whose name contains it | none | -| `-Dtest-rebuild` | bool; force fresh Zig test compilation, retaining cached C dependencies | `false` | -| `-Dhelix-harness` | native reference executable for live differential tests | `HX_HARNESS`, otherwise `hx-harness` on PATH | -| `-Desp32p4-cols` | u16, the board's grid width in cells | `56` | -| `-Desp32p4-rows` | u16, the board's grid height in cells | `14` | - -The local board build emits an object. Firmware clock, serial port and profiling -options belong to the sibling `05-zig-p4` toolchain's build. - -Two build inputs reach the running binary as ordinary values rather than as -behaviour. `build.zig` reads `.version` from `build.zig.zon` through an untyped -`@import("build.zig.zon")` — one place to bump — and `gitCommit(b)` reads the -revision at configure time. Both land in `pardes_config` and are re-exported as -`pardes.version` and `pardes.commit`, and `pardes --version` prints -`pardes <version> (<commit>)`, or `pardes <version>` alone when there is no -commit: a tarball, a container with no `git`, or a checkout outside version -control all yield null, and the flag has to work anyway. Neither is a question -asked at runtime — a binary that shelled out to `git` would describe whatever -tree it was standing in rather than the one it came from. - -`Restore a.dump` first looks for the relative path in the dump directory: -`DumpDir <path>` when set (a leading `~/` is your home; bare `DumpDir` returns -to the default), else `$XDG_DATA_HOME/pardes` or `~/.local/share/pardes`. -`Config` reports the directory in effect as `DumpDir <path>`, so the line can -be fed back as configuration. If it is absent, Restore -uses the argument as a path as before. Absolute paths and argument-free Restore -retain their existing behavior. `Dump` still honors `$PARDES_DUMP` and otherwise -writes a timestamped file in the default directory. - -A dump keeps each text pane's dot (its selection, or the caret), as acme's -keeps a window's q0 and q1, and a Restore puts it back where the kept view -shows it. It keeps the settings that differ from a fresh session's (`Placement -pardes`, `Verbose off`, a shader), as the root ctl reads them, and a Restore -sets them again; the theme is kept with the layout, and the font stays the -frontend's. REPL bindings are not kept. +| `-Dtest-filter` | run only tests whose name contains it | none | +| `-Dtest-rebuild` | bool; fresh Zig test compilation | `false` | +| `-Dhelix-harness` | reference executable for live differential tests | `HX_HARNESS`, else `hx-harness` on PATH | +| `-Desp32p4-cols`, `-Desp32p4-rows` | the board's grid | 56, 14 | -A restored terminal comes back live: its recorded output (the last MiB of it) -is replayed as history, a dim `── restored history ──` line marks where it -ends, and a new shell starts below it in the directory the old one was in, -the same shell it ran (a `Tty bash` pane comes back running bash). A -view left scrolled back stays where it was. Only the shell is new; nothing the -old one was running is restarted. A command pane comes back finished, with -what it printed and its `exit N`, and is not run again (a command still -running when dumped comes back as `exit ?`). The web shell, which has no ptys, still -shows the history alone. +The version comes from `build.zig.zon`'s `.version`. |
