From 3b8ee10301e0b83013a38d93516a345622a93aaa Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Wed, 26 Aug 2026 12:00:20 -0300 Subject: Make the chrome fade a build option, and compile it out for the board A theme change moves the anchored chrome palette - taglines, boxes, line numbers, scroll bars - from the old colors to the new ones over ten display frames. On a screen that repaints in microseconds that is a short legible transition, and it is why the code exists: a palette that teleports reads as a glitch. On a 115200 serial line it is not a fade. Each of the ten steps recolors every anchored cell, so the diff finds the whole chrome dirty and spends a frame's worth of wire on it, ten times over, with nothing else on screen to look at. Measured on the die, one `NextColor`: fade on 12,593 bytes 1,097 ms of saturated wire fade off 2,425 bytes 215 ms A second of the editor talking to itself about a color, on the one transport where a second is noticeable, for a gradient nobody can watch arrive at 11.5 KB/s. ## Comptime, so the code is not there `ChromeAnimation` now selects between `animation.Transition` and a new `animation.Immediate` - the same interface with the animation taken out, a value that is only ever what it was last set to. That is what makes `ChromeTheme.interpolate` unreachable, and unreachable is what makes it absent: the flashed image drops 2,336 bytes, and the object 13,180. A bool tested at runtime would have kept every one of those bytes and still paid the branch. It also would have needed a second meaning bolted onto `animate_theme_changes`, whose job is the startup window and nothing else; that field is untouched here. The option is `-Dtheme-animation`, defaulting to off for `p4` and on everywhere else, and it is an option rather than a platform test because "is a frame expensive" is a property of the transport: a P4 driven over something faster than a UART would want the fade back, and `-Dtheme-animation=true` gives it to them. ## What was checked `Immediate` is new code with one contract worth pinning, and it is the one a caller could get wrong: it must arrive at the SAME palette a completed fade arrives at. An endpoint that differed by a rounding step would make the option a change of colors rather than a change of how long they take. Tested against a fully advanced `Transition` in `animation.zig`. Full suite: unit-test, snap 95/95, hxdiff 481/0, hxparity 561/0, image-harness, pdf-harness, mupdf-check. Builds: tty, p4, gui, and tty/gui with the fade forced off. On the die the canonical verifier reports the screen IDENTICAL across both arms - the workload contains no theme change, so this is the check that ordinary rendering was not perturbed - and `p4-bench --check` stays 4/4. --- src/pardes.zig | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'src/pardes.zig') diff --git a/src/pardes.zig b/src/pardes.zig index 691ee345..9f20e0a3 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -68,6 +68,17 @@ pub const frameMark = tracy.frameMark; pub const Platform = enum { tty, gui, web, macos, p4 }; pub const platform: Platform = @field(Platform, @tagName(@import("pardes_config").platform)); +/// Whether a theme change FADES the anchored chrome palette or replaces it. Ten display frames +/// either way (`animation.transition_steps`), and on every screen but one that is a short legible +/// transition rather than a glitch. +/// +/// The exception is a screen reached through a UART. Each of the ten steps recolors every anchored +/// cell, so the diff finds the whole chrome dirty and spends a frame's worth of wire on it, ten times +/// over, for a fade nobody can see arrive gradually anyway. Off by default for `p4` and settable +/// either way from the build, because the thing that makes it wrong is the transport rather than the +/// target - see `build.zig`. +pub const theme_animation = @import("pardes_config").theme_animation; + /// A build with no host but its display: the embedded source filesystem, the /// in-process clipboard, silent ptys. Comptime, and its own option module /// rather than a `pardes_config` field, because it is the one setting that @@ -2436,7 +2447,13 @@ pub const ChromeTheme = struct { } }; -const ChromeAnimation = animation.Transition(ChromeTheme); +/// The fade, or its absence, decided at comptime so that `-Dtheme-animation=false` leaves +/// `ChromeTheme.interpolate` unreachable and therefore out of the binary entirely. Every call site +/// below is written against the shared interface and needs no condition of its own. +const ChromeAnimation = if (theme_animation) + animation.Transition(ChromeTheme) +else + animation.Immediate(ChromeTheme); const initial_chrome = ChromeTheme.fromTheme(&themes[0]); // ---- the boundary types ---- @@ -5548,6 +5565,10 @@ pub const Pardes = struct { chrome_animation: ChromeAnimation = ChromeAnimation.init(initial_chrome), /// False only while startup configuration or a dump restore is selecting /// its first theme. No frame is rendered in that interval. + /// + /// Nothing to do with `-Dtheme-animation`: that is decided by the type of + /// `chrome_animation`, so a build without the fade does not carry a flag + /// saying so. animate_theme_changes: bool = false, /// Native pixel attachments supported by the shell (Kitty graphics in a /// terminal, GPU textures in SDL). Image panes dynamically fall back to -- cgit v1.3