feat(wezterm): dim unfocused windows - #25
Merged
Merged
Conversation
Register a window-focus-changed handler that applies a dimmed foreground_text_hsb and a lower window_background_opacity as per-window config overrides while a window is unfocused, and clears exactly those two keys on focus so the config's own defaults are restored. Unrelated per-window overrides are preserved across both transitions, and set_config_overrides() is only called when one of the two owned values actually changes, to avoid redundant config reloads.
window:get_config_overrides() returns a copy, so the stored foreground_text_hsb is never the same table as the constant we last wrote. The identity comparison in the redundant-write guard was therefore always false while unfocused, so every repeated window-focus-changed event issued another set_config_overrides() call and triggered a config reload. Compare the hue/saturation/brightness fields instead. Behavior is otherwise unchanged: the same two override keys are applied while unfocused and cleared on focus, and unrelated overrides are preserved.
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.
Summary
Dim WezTerm windows while they are unfocused, so the focused window is obvious at a glance when several are open side by side.
A
window-focus-changedhandler reads the new state withwindow:is_focused()and adjusts only per-window config overrides. While a window is unfocused it applies:foreground_text_hsb = { hue = 1.0, saturation = 0.25, brightness = 0.45 }window_background_opacity = 0.62When focus returns, those two keys are cleared (set to
nil) rather than rewritten with literal values, so the config's own defaults apply again. That matters because the focused appearance stays defined in exactly one place:config.window_background_opacity = 0.8at the top of the file. Change the default there and the focused look follows automatically, with no second copy of the value to keep in sync.The handler owns those two keys and nothing else. Any unrelated per-window override set elsewhere survives both transitions untouched.
Why the comparison is structural
The handler calls
set_config_overrides()only when one of the two values it owns actually needs to change, since every write triggers a config reload.Making that guard correct requires care:
window:get_config_overrides()returns a copy of the overrides, not the stored table itself. Comparing the returnedforeground_text_hsbagainst the module-level constant by table identity is therefore always false, which would make every repeated focus event write again and reload the config in a loop. The guard instead compareshue,saturation, andbrightnessfield by field, with anilbranch so the focused case (nothing expected) still reads as "already cleared".Scope
Confined to
home/.config/wezterm/wezterm.lua, plus a one-line README mention of the behavior. Every unrelated setting and the structure of the config file are unchanged. No platform-specific guards and no new user-facing settings were added.Testing
Validated headlessly against the real config in this branch, using an isolated
HOME, XDG directories, and socket with an explicit--config-file, so no running WezTerm instance was involved:get_config_overrides()returns a fresh deep copy on every call (asserting it never hands back an aliased table), matching the real API contract that would otherwise hide an identity-comparison bug.The harness covers: first unfocus from no overrides; repeated unfocus with no second write; focus clearing only the owned keys while an unrelated override survives; repeated focus with no write; unfocus again with the unrelated override still intact; repeat-unfocus after a full focus cycle; and a stored-but-different HSB producing exactly one corrective write. It asserts the exact HSB and opacity values, and that the base settings (0.8 opacity, rose-pine-moon, font size 15, blur 50, tab bar, decorations) are untouched.
A mutation check confirms the harness fails when the compared values or the comparison strategy are wrong, so the passes are meaningful rather than vacuous.
Evidence: WezTerm focus validation
No screenshot is included: the dimming is a live GPU-rendered effect, and capturing it would mean launching a WezTerm GUI against this config, which the headless checks above deliberately avoid.
Pipeline
Updates from git push no-mistakes
✅ intent - passed
✅ No issues found.
✅ Rebase - passed
✅ No issues found.
✅ Review - passed
✅ No issues found.
✅ Test - passed
✅ No issues found.
wezterm --config-file home/.config/wezterm/wezterm.lua show-keysReal WezTerm Lua harness covering focus transitions, exact values, override preservation, and deep-copy semantics✅ Document - passed
✅ No issues found.
✅ Lint - passed
✅ No issues found.
✅ Push - passed
✅ No issues found.