fix(src): detect light/dark theme inside sessions via OSC 11#92
Open
kylecarbs wants to merge 1 commit into
Open
fix(src): detect light/dark theme inside sessions via OSC 11#92kylecarbs wants to merge 1 commit into
kylecarbs wants to merge 1 commit into
Conversation
Programs like nvim and helix could not detect the terminal's light/dark theme inside a boo session. On startup they enter the alternate screen and, in the same burst, query the background color with OSC 11 (ESC ]11;? BEL). That query never reaches the real terminal: a `boo attach` strips it along with the alt-screen tail it rides behind, and a `boo ui` view renders into a client-side terminal that never answers it. The embedded ghostty-vt also treats OSC color queries as no-ops, so the daemon answered nothing and apps fell back to a default (usually dark). Make the daemon the terminal authority for the background color: - A client probes its real terminal's background once at startup (OSC 11) and reports the RGB to the daemon via a new `bg_color` protocol message. Both `boo attach` and `boo ui` send it right after `attach`. - The daemon caches the color per window, strips OSC 11 queries from child output (so the real terminal can't also answer and double-reply), and answers each stripped query itself. A query that arrives before any color is known is deferred and answered as soon as a client reports one. - The color-scheme DSR (CSI ?996n) is answered from the background's luminance (Rec. 601), classifying light vs dark. The probe sends `attach` first and yields to pending signals so a slow or unanswered probe never delays a kill or the initial window size. Mixed daemon/client versions degrade gracefully: an older peer ignores the unknown `bg_color` message and behavior is unchanged. Fixes #91.
|
Confirming that this fixes the system theme issues. I do have a question for my understanding though (and please excuse me if I totally misunderstand this):
Why is the query stripped and discarded, and why can't the client just pass it on to the real terminal? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #91.
Problem
Programs like nvim and helix can't detect the terminal's light/dark theme inside a boo session (reported by @ldb). On startup they enter the alternate screen and, in the same burst, query the background color with OSC 11 (
ESC ]11;? BEL). That query never reaches the real terminal:boo attachstrips it along with the alt-screen tail it rides behind (the alt-screen filter discards everything after the screen toggle).boo uirenders into a client-side terminal that never answers it.The embedded ghostty-vt also treats OSC color queries as no-ops, so the daemon answered nothing and apps fell back to a default (usually dark).
Fix
Make the daemon the terminal authority for the background color:
client.zig,ui.zig): a client probes its real terminal's background once at startup (OSC 11), parses the reply, and reports the RGB to the daemon via a newbg_colorprotocol message, sent right afterattach.daemon.zig,window.zig,oscquery.zig): the daemon caches the color per window, strips OSC 11 queries from child output so the real terminal can't also answer (no double reply), and answers each stripped query itself. A query that arrives before any color is known is deferred and answered as soon as a client reports one.window.zig):CSI ?996nis now answered from the background's luminance (Rec. 601), classifying light vs dark.The probe sends
attachfirst and yields to pending signals, so a slow or unanswered probe never delays a kill or the initial window size.Compatibility
Mixed daemon/client versions degrade gracefully: an older peer ignores the unknown
bg_colormessage and behavior is unchanged.Known tradeoff
The daemon always strips OSC 11 queries and answers from the probed background. If the real terminal answers the startup probe slower than the 150 ms timeout, the late reply leaks into the session as input (harmless for alt-screen apps, which await it; minor stray bytes for a bare shell). This is a narrow, high-latency edge case; full main-loop interception would remove it at the cost of complexity and is deferred.
Testing
zig build test— 151 unit tests pass (oscquery filter, OSC 11 reply parser with BEL/ST and channel-width scaling, window answer/defer + color-scheme luminance, RGB payload roundtrip).zig build test-integration— PTY integration tests pass.zig fmt --check src/*.zig— clean.boo attachandboo ui, including the cold-start deferral path (session queries before the ui reports a color).Plan and decision log
Root cause (confirmed empirically with a PTY harness)
CSI ?1049h) and then immediately query the background with OSC 11 (ESC ]11;? BEL) in one burst.altscreen.zig) discards everything after the screen toggle and feeds it viaWindow.feedDiscarded, so the query never reaches the real terminal..querycase incolorOperationis a no-op) and thecolor_schemeeffect was null, so the daemon answered nothing. The app times out and falls back to dark.boo attach(raw passthrough) andboo ui(compositor) are affected.Design (daemon is the terminal authority)
attach+ui) probe the real terminal once at startup with OSC 11, parse the reply, and report the background RGB to the daemon via a newbg_colorprotocol message.?queries from child output (newoscquery.zigfilter), answering exactly once with the stored color so the real terminal cannot also answer (no double reply);CSI ?996n) via the now-implementedcolor_schemeeffect, derived from background luminance.Files
src/protocol.zig:bg_colormessage +RgbPayload(encode/decode, luminance, isDark).src/oscquery.zig: new stateful filter that strips OSC 11 queries and reports them (BEL/ST terminators, sequences split across feeds, near-misses pass through).src/window.zig: store bg,color_schemeeffect, answer + defer logic, run the OSC filter.src/daemon.zig: handlebg_color; run the OSC filter inserviceWindowbefore passthrough.src/client.zig: probe + OSC 11 reply parser, wired intoattach(attach first, then probe).src/ui.zig: probe once at startup, pass bg to eachView.src/main.zig: registeroscquery.zigin the unit-test aggregator.Generated by Coder Agents on behalf of @kylecarbs.