Skip to content

feat(wezterm): dim unfocused windows - #25

Merged
kunchenguid merged 3 commits into
mainfrom
fm/dotfiles-wezterm-unfocused-window-r1
Jul 30, 2026
Merged

feat(wezterm): dim unfocused windows#25
kunchenguid merged 3 commits into
mainfrom
fm/dotfiles-wezterm-unfocused-window-r1

Conversation

@kunchenguid

@kunchenguid kunchenguid commented Jul 30, 2026

Copy link
Copy Markdown
Owner

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-changed handler reads the new state with window: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.62

When 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.8 at 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 returned foreground_text_hsb against 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 compares hue, saturation, and brightness field by field, with a nil branch 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:

  1. The config parses cleanly under the installed WezTerm CLI.
  2. A deterministic harness loads this exact config inside WezTerm's Lua interpreter, captures the registered handler, and drives it against a fake window whose 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
direct_config_parse_exit=0
target_harness_exit=0

PASS: real public config loaded and base settings preserved
PASS: unfocus applies exact HSB/opacity and repeated unfocus is idempotent
PASS: focus clears only owned keys, restores defaults, and repeated focus is idempotent
PASS: unrelated overrides survive both transitions and a full focus cycle
PASS: deep-copy semantics detect and correct a differing HSB exactly once

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-keys
  • Real 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.

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.
@kunchenguid
kunchenguid merged commit 7857db1 into main Jul 30, 2026
@kunchenguid
kunchenguid deleted the fm/dotfiles-wezterm-unfocused-window-r1 branch July 30, 2026 20:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant