aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-06-15 23:29:35 -0500
committerCraig Jennings <c@cjennings.net>2026-06-15 23:29:35 -0500
commitc0c6e72684387f990fda1096e842f1f100ddb3e5 (patch)
treec5a310db35ffb543df2773d3c239642b15bfe3d2
parent07738d4f58f463c20ab70b5f336b2911f5f9d521 (diff)
downloaddotemacs-c0c6e72684387f990fda1096e842f1f100ddb3e5.tar.gz
dotemacs-c0c6e72684387f990fda1096e842f1f100ddb3e5.zip
feat(system-utils): tint the *scratch* background a shade lighter
A buffer-local face remap lightens the *scratch* default background by cj/scratch-background-lighten percent (default 5) so it reads as the scratch buffer, applied on emacs-startup-hook. The colour math is display-dependent (verified live); the pure helper's guard contract is unit-tested.
-rw-r--r--modules/system-utils.el31
-rw-r--r--tests/test-system-utils-scratch-background.el30
-rw-r--r--todo.org4
3 files changed, 62 insertions, 3 deletions
diff --git a/modules/system-utils.el b/modules/system-utils.el
index 7cf958674..b3e038ef0 100644
--- a/modules/system-utils.el
+++ b/modules/system-utils.el
@@ -157,12 +157,39 @@ detached from Emacs."
;; Set scratch buffer to org-mode
(setopt initial-major-mode 'org-mode)
-;; Move cursor to end of scratch buffer on startup
+;; Tint the *scratch* background a shade lighter than the default so it reads
+;; as the scratch buffer at a glance. Buffer-local face remap, recomputed from
+;; whatever theme is loaded.
+(require 'color)
+
+(defcustom cj/scratch-background-lighten 5
+ "Percent to lighten the *scratch* background above the default background.
+Aesthetic; tune to taste."
+ :type 'integer
+ :group 'convenience)
+
+(defun cj/--scratch-lightened-background (bg)
+ "Return BG lightened by `cj/scratch-background-lighten' percent.
+Return nil when BG is not a usable color string (e.g. `unspecified')."
+ (when (and (stringp bg) (color-name-to-rgb bg))
+ (color-lighten-name bg cj/scratch-background-lighten)))
+
+(defun cj/scratch-apply-background ()
+ "Remap the *scratch* buffer background a shade lighter than the default."
+ (when (get-buffer "*scratch*")
+ (with-current-buffer "*scratch*"
+ (let ((lighter (cj/--scratch-lightened-background
+ (face-attribute 'default :background nil t))))
+ (when lighter
+ (face-remap-add-relative 'default :background lighter))))))
+
+;; Move cursor to end of scratch buffer on startup, and tint its background
(add-hook 'emacs-startup-hook
(lambda ()
(when (get-buffer "*scratch*")
(with-current-buffer "*scratch*"
- (goto-char (point-max))))))
+ (goto-char (point-max))))
+ (cj/scratch-apply-background)))
;;; --------------------------------- Dictionary --------------------------------
diff --git a/tests/test-system-utils-scratch-background.el b/tests/test-system-utils-scratch-background.el
new file mode 100644
index 000000000..422590f4b
--- /dev/null
+++ b/tests/test-system-utils-scratch-background.el
@@ -0,0 +1,30 @@
+;;; test-system-utils-scratch-background.el --- Tests for the scratch tint -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; cj/--scratch-lightened-background lightens the default background by a
+;; tunable percent for the *scratch* buffer's buffer-local face remap. The
+;; colour arithmetic (color-lighten-name -> color-name-to-rgb) is
+;; display-dependent and returns zeros under --batch, so the actual lightening
+;; is verified live in the daemon; here we cover the display-independent
+;; contract: a usable colour string yields a string, junk yields nil.
+
+;;; Code:
+
+(require 'ert)
+(require 'color)
+(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory))
+(require 'system-utils)
+
+(ert-deftest test-system-utils-scratch-lightened-background-returns-string ()
+ "Normal: a valid hex colour yields a colour string (not nil)."
+ (let ((cj/scratch-background-lighten 5))
+ (should (stringp (cj/--scratch-lightened-background "#100f0f")))))
+
+(ert-deftest test-system-utils-scratch-lightened-background-bad-input ()
+ "Error: non-colour input yields nil rather than signalling."
+ (should (null (cj/--scratch-lightened-background nil)))
+ (should (null (cj/--scratch-lightened-background 'unspecified)))
+ (should (null (cj/--scratch-lightened-background "not-a-color"))))
+
+(provide 'test-system-utils-scratch-background)
+;;; test-system-utils-scratch-background.el ends here
diff --git a/todo.org b/todo.org
index d2284ff47..724f7700f 100644
--- a/todo.org
+++ b/todo.org
@@ -2421,7 +2421,9 @@ configuration (=text-config=, =diff-config=, =ledger-config=,
** TODO [#B] org-roam :config triggers the 15-20s refile scan synchronously at first idle :bug:solo:next:
=modules/org-roam-config.el:78-79= — org-roam is =:defer 1=, so its :config calls =cj/build-org-refile-targets= at 1s idle, BEFORE the 5s background timer (=org-refile-config.el:144-151=); on a cold cache the 30k-file scan runs inline and freezes Emacs at first idle. Drop the call — org-roam is loaded long before the 5s timer fires. Likely a player in the filed org-capture 15-20s perf task (=[#B] Optimize org-capture target building performance=) — check both together. From the 2026-06 config audit.
-** TODO [#B] Scratch buffer background a shade lighter than default :feature:next:
+** DONE [#B] Scratch buffer background a shade lighter than default :feature:next:
+CLOSED: [2026-06-15 Mon]
+cj/scratch-apply-background remaps the *scratch* default background lighter (cj/scratch-background-lighten percent, default 5) via color-lighten-name, applied on the existing emacs-startup-hook. The percent is a tunable defcustom. Pure helper tested for the display-independent contract; the lightening itself is display-dependent (color-name-to-rgb), verified live in the daemon.
Make *scratch* just-noticeably lighter than the normal background so it reads as the scratch buffer. Simplest is a buffer-local face remap on *scratch*; Craig is fine routing it through org-faces if a theme hook is wanted. The exact lightening delta is an aesthetic call to tune visually. From the roam inbox.
** VERIFY [#B] Stale elpa gptel shadows the local fork — likely the gptel-magit root :bug:quick:solo:next:
Needs from Craig: can't be done standalone. I tried deleting elpa/gptel-0.9.8.5 — the fork loaded fine and gptel-magit still worked via use-package autoloads, but package activation then printed "Unable to activate gptel-magit / Required gptel-0.9.8 unavailable" on every startup, so I reverted. To remove the shadow we must also resolve gptel-magit's package dependency: either drop gptel-magit's package dep (load it via load-path like the gptel fork), or repackage the fork into .localrepo as gptel. Tell me which and I'll do it; this pairs with the gptel-magit investigation.