# Startup configuration Native pardes builds use a per-user `pardes` configuration directory. Its main command file is named `init`: - 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. Windows never consults it. An `init` of 1 MiB or more, or one that cannot be read, is treated as no file at all — 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, which `Config` reports as such. `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, 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, 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: TTY reports Font and scene shaders as unsupported; web reports Font, panel transitions, and scene shaders as unsupported. Tagline font size is its own row and capability — GUI font selection is native-only, while the browser still applies the compiled tagline percentage to its DOM. 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. `Compiled default shell` is the command built into the binary; `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. The mutable global values live together in the plain `runtime_config.State` 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. 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 format is one existing builtin command per line, using the same spelling and argument parsing as commands executed inside pardes: ```text Theme acme Font DejaVuSansMono-Regular TaglineSize 82 Shell zsh Wrap ``` A line matches a builtin whose name takes NO argument only as that whole word: `Kill` runs, `Kill something` does not. Builtins that take one (`Theme`, `ThemeFile`, `Font`, `TaglineSize`, `Shell`, `Restore`, `Find`, `Grep`, `Rename`, `WsSymbols`, `Look`, `Exec`, `EffectCode`) take everything after the name as the argument. `Theme ` wants one of the 228 names in the ring. Do not derive the spelling — read it off `ThemeSel` (`SPC t t`), which lists every one as the exact `Theme ` line that selects it. The generator lowercases, folds punctuation runs to a single `_` and then TRIMS leading and trailing ones (`penumbra+.toml` is `penumbra`, not `penumbra_`), and it suffixes every theme that came from zed with `_zed` so it cannot collide with a helix theme of the same name (zed's "Ayu Mirage" is `ayu_mirage_zed`; `ayu_mirage` is helix's). A name that is not in the ring is ignored. ## Runtime theme files `ThemeFile ` 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 ` or `NextColor` stops the custom-file watch. Execute `DumpThemes` to write every theme compiled into the executable to: ```text /themes/builtin/.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: one ZON struct with the theme name, the fifteen RGB roles, nullable `bg`/`fg`, and either a 16-color RGB `palette` or `null`. RGB values are three-byte arrays, and hex literals are accepted. There is no inheritance or partial override layer. `Shell ` 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`. `Font` and `FontSel` 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. `Font` is asynchronous at the renderer boundary. `Config` therefore keeps requested name/path, 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 ` 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. The SDL GUI uses the smaller face's measured monospace advance as a real per-pane tagline grid, including pointer hit testing; it does not pad each smaller glyph back out to a body-width cell. 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. The SDL GUI joins the reduced-height global and pane tagline bands with `gui_topbar_pane_border_px` physical pixels. Set it to zero for a direct join. `gui_topbar_pane_border_rgb` can pin an RGB color; its default `null` follows the active theme's scrollbar-track color. With `Tagbottom` enabled, a tagline on the final grid row is bottom-aligned so the same unused half-band does not show beneath it. 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, substitutes the system monospaced face (else Menlo) when the NAME you asked for will not load, and leaves per-codepoint fallback to CoreText's own cascade when it draws. What it caches is glyph ids, not pixels. 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. ## Panel and scene effects Exactly one panel transition is selected at a time. Executing its builtin a second time turns it off; selecting another replaces it: ```text PanelSlide PanelZoom PanelDissolve PanelAscii PanelVertical PanelEdges PanelFall PanelWave PanelCurtain PanelScramble PanelType ``` All panel transitions start off. 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. Six further transitions are character *motion* over the same frozen/new grid pair, and are composed in the core the same way: - `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 curtain of glyphs marches column by column, left to right. - `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. 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. 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 scene effects are independent switches and can be combined: ```text Crt Ripple Glitch ``` They share one full-scene shader pass in SDL and macOS. With all three off the pass is bypassed. CRT works in linear light with restrained scanlines, mask, bloom, curvature, and noise rather than remapping the theme to a strong fixed palette; Ripple and Glitch primarily perturb sample coordinates. `EffectCode ` opens the build-embedded effect math, host paint/submission path, and backend shader/grid sources, for example `EffectCode PanelAscii` or, in a GUI build, `EffectCode Crt`. TTY exposes it for its grid transitions; native GUI builds expose it for transitions and scene shaders. It is absent on web, where no effect argument could succeed. Shared passes are shown as shared source segments rather than manufactured per-effect copies. The command works from an installed binary and does not need the source checkout beside it. SDL output also labels its shader provenance. An ordinary build prints the live GLSL that `glslc` compiled for that executable; `-Dprebuilt-shaders` prints the tracked GLSL snapshot paired with the committed SPIR-V instead and labels those segments with their `shaders/prebuilt/` paths. `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. ## 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, roughly 33 ms at the 60 Hz animation cadence) 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: ```zig pub const look_preview_delay_frames: ?u16 = null; ```