diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-25 09:25:32 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-25 09:25:32 -0500 |
| commit | 56da3d940b26a51102bce39b3b82dfbbc2b391fd (patch) | |
| tree | 54ce4e57ef954835ccc0fb7ebdb51a5c7c68f59b | |
| parent | a522e5537ab9c94a45656b28e94a73b98f47d4b8 (diff) | |
| download | dotemacs-56da3d940b26a51102bce39b3b82dfbbc2b391fd.tar.gz dotemacs-56da3d940b26a51102bce39b3b82dfbbc2b391fd.zip | |
feat(auto-dim): dim non-selected windows via auto-dim-other-buffers
I added auto-dim-config, a module that loads my local auto-dim-other-buffers fork and dims windows that don't have focus so the selected window stands out. A non-selected window drops to a pure-black background with faded gray text. The dimmed faces live in the dupre theme (themes/dupre-faces.el) so they track theme switches, and the module remaps default, the font-lock faces, and org-block onto them so syntax-highlighted code fades too rather than staying lit. Fringe is left out because dimming it forces a full-frame refresh that flickers on this non-pgtk build.
dim-on-focus-out is nil, so tabbing to a browser or terminal on Hyprland doesn't dim the whole frame. vterm and agent windows don't dim either, because the terminal paints its own per-cell colors past the face remap. I'm keeping that, since the agent's output stays readable while I work in code on the other side.
The module loads after the theme, carries a load-graph header, joins the header-contract allowlist, and the inventory moves to 103 of 103 classified.
| -rw-r--r-- | docs/design/module-inventory.org | 9 | ||||
| -rw-r--r-- | init.el | 1 | ||||
| -rw-r--r-- | modules/auto-dim-config.el | 61 | ||||
| -rw-r--r-- | tests/test-auto-dim-config.el | 33 | ||||
| -rw-r--r-- | tests/test-init-module-headers.el | 1 | ||||
| -rw-r--r-- | themes/dupre-faces.el | 10 |
6 files changed, 111 insertions, 4 deletions
diff --git a/docs/design/module-inventory.org b/docs/design/module-inventory.org index a6a065da..ffef323a 100644 --- a/docs/design/module-inventory.org +++ b/docs/design/module-inventory.org @@ -10,7 +10,7 @@ each module is inspected and classified. A module moves from [[*Pending classification][Pending classification]] into [[*Classified modules][Classified modules]] once its source has been read and its load-graph header written. -Phase 1 exit criterion: every module required by =init.el= (102 total) is +Phase 1 exit criterion: every module required by =init.el= (103 total) is represented here with a category and target load shape, every eager survivor has a documented reason, and top-level timer/process/network side effects are identified. Classification proceeds in batches; the header-validation test @@ -26,13 +26,13 @@ This inventory is independent from the helper inventory owned by - Batch 1 (Foundation, Layer 1): classified. 7 modules. - Batch 2 (Text/editing command modules, Layer 2): classified. 9 modules. - Batch 3 (Core libraries and command modules): classified. 7 modules. -- Batch 4 (UI / core-UX modules, Layer 2): classified. 10 modules. +- Batch 4 (UI / core-UX modules, Layer 2): classified. 11 modules. - Batch 5 (Dev entry-points, diff, help, lint, VC, Layer 2): classified. 9 modules. - Batch 6 (Programming modules, Layer 2-4): classified. 10 modules. - Batch 7 (Org modules, Layer 3-4): classified. 13 modules. - Batch 8 (Domain / integration / optional modules, Layer 2-4): classified. 18 modules. - Batch 9 (Remaining domain / integration / optional modules, Layer 2-4): classified. 19 modules. -- 102 of 102 modules classified. Phase 1 classification complete. +- 103 of 103 modules classified. Phase 1 classification complete. - No load-order changes have been made; =init.el= keeps its current eager order. * Legend @@ -112,6 +112,7 @@ eager reason and stay eager. | Module | Layer | Cat | Current | Target | Runtime requires | Top-level side effects | Direct load | |--------+-------+-----+---------+--------+------------------+------------------------+-------------| +| =auto-dim-config= | 2 | C/O | eager | eager | none (loads ~/code auto-dim fork) | enables global minor mode, edits affected-faces | conditional | | =ui-config= | 2 | C/S | eager | eager | user-constants | UI defaults, post-command hook, display-buffer-alist | yes | | =ui-theme= | 2 | C | eager | eager | none | theme load path, theme key | yes | | =ui-navigation= | 2 | C/P | eager | eager | none | nav keymap, 5 global keys, package config | yes | @@ -285,4 +286,4 @@ found and fixed; the =Phase 2 fix:= notes below describe the change applied. * Pending classification -- None. Every module required by =init.el= is classified (102 of 102). +- None. Every module required by =init.el= is classified (103 of 103). @@ -57,6 +57,7 @@ (require 'ui-config) ;; transparency, cursor color, icons, &c. (require 'ui-theme) ;; themes and theme persistency (cj/load-theme-from-file) +(require 'auto-dim-config) ;; dim non-selected windows (faces live in the theme) (require 'ui-navigation) ;; the movement and navigation of windows (require 'font-config) ;; font and emoji configuration (require 'nerd-icons-config) ;; nerd-icons + completion/ibuffer integration + tint diff --git a/modules/auto-dim-config.el b/modules/auto-dim-config.el new file mode 100644 index 00000000..83c5b17c --- /dev/null +++ b/modules/auto-dim-config.el @@ -0,0 +1,61 @@ +;;; auto-dim-config.el --- Dim non-selected windows -*- lexical-binding: t; coding: utf-8; -*- +;; author Craig Jennings <c@cjennings.net> +;; +;;; Commentary: +;; +;; Layer: 2 (Core UX). +;; Category: C/O. +;; Load shape: eager. +;; Eager reason: global UI minor mode; the dimming should be visible in the +;; first frame. +;; Top-level side effects: enables auto-dim-other-buffers-mode and edits +;; auto-dim-other-buffers-affected-faces. +;; Runtime requires: none (loads the auto-dim-other-buffers fork via use-package). +;; Direct test load: conditional (needs the ~/code fork on the load-path). +;; +;; Dims windows that do not have focus so the selected window stands out, +;; using a local fork of auto-dim-other-buffers (the fork adds a focus-change +;; debounce). The dimmed faces (auto-dim-other-buffers and +;; auto-dim-other-buffers-hide) live in the active theme +;; (themes/dupre-faces.el) so they track theme switches. + +;;; Code: + +(use-package auto-dim-other-buffers + :load-path "~/code/auto-dim-other-buffers.el" + :ensure nil + ;; :vc (:url "git@cjennings.net:auto-dim-other-buffers.git" :rev :newest) + :custom + ;; Dim only non-selected windows within Emacs, not the whole frame when + ;; Emacs loses focus -- on Hyprland focus moves to other apps constantly, + ;; and the ai-vterm agents live in their own windows. + (auto-dim-other-buffers-dim-on-focus-out nil) + (auto-dim-other-buffers-dim-on-switch-to-minibuffer t) + :config + ;; Remap these faces to auto-dim-other-buffers (pure-black background + + ;; faded gray foreground, defined in the theme) in non-selected windows. + ;; The font-lock faces are included so code text fades to "disabled" + ;; rather than staying lit -- remapping default alone would leave + ;; syntax-highlighted text at full colour. Fringe is left out because + ;; dimming it forces a full-frame refresh that flickers on this non-pgtk + ;; build; org-hide uses the -hide face so hidden text stays hidden. + (setq auto-dim-other-buffers-affected-faces + '((default . (auto-dim-other-buffers . nil)) + (org-block . (auto-dim-other-buffers . nil)) + (org-hide . (auto-dim-other-buffers-hide . nil)) + (font-lock-keyword-face . (auto-dim-other-buffers . nil)) + (font-lock-string-face . (auto-dim-other-buffers . nil)) + (font-lock-comment-face . (auto-dim-other-buffers . nil)) + (font-lock-comment-delimiter-face . (auto-dim-other-buffers . nil)) + (font-lock-doc-face . (auto-dim-other-buffers . nil)) + (font-lock-function-name-face . (auto-dim-other-buffers . nil)) + (font-lock-variable-name-face . (auto-dim-other-buffers . nil)) + (font-lock-type-face . (auto-dim-other-buffers . nil)) + (font-lock-constant-face . (auto-dim-other-buffers . nil)) + (font-lock-builtin-face . (auto-dim-other-buffers . nil)) + (font-lock-preprocessor-face . (auto-dim-other-buffers . nil)) + (font-lock-warning-face . (auto-dim-other-buffers . nil)))) + (auto-dim-other-buffers-mode 1)) + +(provide 'auto-dim-config) +;;; auto-dim-config.el ends here diff --git a/tests/test-auto-dim-config.el b/tests/test-auto-dim-config.el new file mode 100644 index 00000000..45e1db5f --- /dev/null +++ b/tests/test-auto-dim-config.el @@ -0,0 +1,33 @@ +;;; test-auto-dim-config.el --- Tests for the auto-dim-other-buffers config -*- lexical-binding: t; -*- + +;;; Commentary: +;; auto-dim-config configures the local auto-dim-other-buffers fork: dim only +;; non-selected windows within Emacs (not the whole frame on focus-out), drop +;; fringe from the dimmed faces to avoid flicker on this non-pgtk build, and +;; enable the global mode. Guarded with `skip-unless' because the fork lives +;; in ~/code and may be absent on a clean checkout. + +;;; Code: + +(require 'ert) +(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) + +(defconst test-auto-dim--fork + (expand-file-name "~/code/auto-dim-other-buffers.el") + "Local fork directory the module loads via `:load-path'.") + +(ert-deftest test-auto-dim-config-applies-settings () + "Normal: loading the module enables the mode with the chosen settings." + (skip-unless (file-directory-p test-auto-dim--fork)) + (require 'auto-dim-config) + (unwind-protect + (progn + (should (bound-and-true-p auto-dim-other-buffers-mode)) + (should (null auto-dim-other-buffers-dim-on-focus-out)) + (should (eq t auto-dim-other-buffers-dim-on-switch-to-minibuffer)) + (should-not (assq 'fringe auto-dim-other-buffers-affected-faces))) + (when (fboundp 'auto-dim-other-buffers-mode) + (auto-dim-other-buffers-mode -1)))) + +(provide 'test-auto-dim-config) +;;; test-auto-dim-config.el ends here diff --git a/tests/test-init-module-headers.el b/tests/test-init-module-headers.el index af4f9ec9..ef5a7132 100644 --- a/tests/test-init-module-headers.el +++ b/tests/test-init-module-headers.el @@ -48,6 +48,7 @@ "text-config" "undead-buffers" ;; Batch 4 — UI / core-UX modules (Layer 2) + "auto-dim-config" "ui-config" "ui-theme" "ui-navigation" diff --git a/themes/dupre-faces.el b/themes/dupre-faces.el index fdd61a55..648fded3 100644 --- a/themes/dupre-faces.el +++ b/themes/dupre-faces.el @@ -832,6 +832,16 @@ `(shr-code ((t (:foreground ,green :background ,bg+1)))) `(shr-mark ((t (:background ,yellow-2)))) +;;;;; auto-dim-other-buffers + ;; Non-selected windows recede to a pure-black background with faded + ;; gray text, so an inactive window reads as "disabled". This face is + ;; remapped onto default, the font-lock faces, and org-block (see + ;; auto-dim-config.el), so code text fades too rather than staying lit. + ;; The -hide face keeps org hidden text invisible in dimmed windows (its + ;; foreground must match the dimmed background). + `(auto-dim-other-buffers ((t (:foreground ,gray-1 :background "#000000")))) + `(auto-dim-other-buffers-hide ((t (:foreground "#000000" :background "#000000")))) + ))) (provide 'dupre-faces) |
