aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/ai-term-backend-eat.el24
-rw-r--r--modules/ai-term.el67
-rw-r--r--tests/test-ai-term--accent.el69
3 files changed, 113 insertions, 47 deletions
diff --git a/modules/ai-term-backend-eat.el b/modules/ai-term-backend-eat.el
index 82218ca7..21385e37 100644
--- a/modules/ai-term-backend-eat.el
+++ b/modules/ai-term-backend-eat.el
@@ -30,7 +30,7 @@
(defvar eat-buffer-name)
(defvar eat-semi-char-mode-map)
(defvar eat-terminal)
-(defvar cj/ai-term-accent-color-indices)
+(defvar cj/ai-term-palette-faces)
(defun cj/--ai-term-send-string (buffer string)
"Send STRING to BUFFER's terminal process (the agent's shell).
@@ -40,20 +40,20 @@ Sends to the pty directly so the launch command reaches the shell EAT runs."
(process-send-string proc string))))
(defun cj/--ai-term-apply-accent (buffer)
- "Point BUFFER's terminal accent palette entries at `cj/ai-term-accent'.
-Repaints each index in `cj/ai-term-accent-color-indices' in this
-terminal's own 256-color palette (eat keeps one per terminal), so the
-agent's accent -- Claude Code's rose banner, borders, spinner -- renders
-in the accent face's color while every other eat terminal keeps the true
-palette. A no-op when BUFFER has no live eat terminal. Takes effect on
-the terminal's next redraw; text already on screen keeps its old color
-until the program repaints it (Claude Code's TUI repaints continuously)."
+ "Point BUFFER's terminal palette entries at their dupre faces.
+Repaints each (INDEX . FACE) in `cj/ai-term-palette-faces' in this
+terminal's own 256-color palette (eat keeps one per terminal), so Claude
+Code's accents -- the bypass banner and every /color session color --
+render in dupre hues while other eat terminals keep the true palette.
+A no-op when BUFFER has no live eat terminal. Takes effect on the
+terminal's next redraw; text already on screen keeps its old color until
+the program repaints it (Claude Code's TUI repaints continuously)."
(with-current-buffer buffer
(when (bound-and-true-p eat-terminal)
- (dolist (index cj/ai-term-accent-color-indices)
+ (dolist (entry cj/ai-term-palette-faces)
(eat-term-set-parameter eat-terminal
- (intern (format "color-%d-face" index))
- 'cj/ai-term-accent)))))
+ (intern (format "color-%d-face" (car entry)))
+ (cdr entry))))))
(defun cj/--ai-term-show-or-create (dir name)
"Show or create the AI-term buffer for project DIR with buffer NAME.
diff --git a/modules/ai-term.el b/modules/ai-term.el
index 0774758e..bd955292 100644
--- a/modules/ai-term.el
+++ b/modules/ai-term.el
@@ -117,22 +117,61 @@ fallback when `cj/--ai-term-last-size' is nil."
(defface cj/ai-term-accent
'((t :foreground "#67809c"))
- "Accent color for agent terminals, defaulting to dupre blue.
-Claude Code draws its accent (the bypass-permissions banner, borders,
-spinner) with xterm-256 palette colors; agent terminals point those
-palette entries at this face (see `cj/ai-term-accent-color-indices'),
-so the accent renders in this face's foreground instead of the stock
-rose red. Per-project colors later land as per-buffer overrides of
-the same palette entries."
+ "Default accent for agent terminals: dupre blue.
+Carries the elements Claude Code colors regardless of the session color
+-- the bypass-permissions banner (xterm palette index 211, stock rose).
+The /color session accents get their own faces below."
:group 'ai-term)
-(defvar cj/ai-term-accent-color-indices '(211)
- "The xterm-256 palette indices agent terminals repaint with the accent.
-211 (#ff87af, a rose pink) is Claude Code's accent as rendered through
-the 256-color palette -- confirmed empirically on the bypass-permissions
-banner. Add indices here if other accent elements surface in a
-different palette slot. Applied per terminal by
-`cj/--ai-term-apply-accent'; other eat terminals keep the true palette.")
+;; One face per Claude Code /color session color, each pinned to its dupre
+;; counterpart. Claude Code emits a fixed xterm-256 index per color name
+;; (probed empirically against v2.1.198); `cj/ai-term-palette-faces' maps
+;; those indices to these faces, and agent terminals render them instead of
+;; the stock xterm hues. dupre has no orange or pink, so orange borrows
+;; bright red (peach) and pink borrows bright magenta.
+(defface cj/ai-term-color-red '((t :foreground "#d47c59"))
+ "Agent terminal rendering of Claude Code's red session color (dupre red)."
+ :group 'ai-term)
+(defface cj/ai-term-color-blue '((t :foreground "#67809c"))
+ "Agent terminal rendering of Claude Code's blue session color (dupre blue)."
+ :group 'ai-term)
+(defface cj/ai-term-color-green '((t :foreground "#a4ac64"))
+ "Agent terminal rendering of Claude Code's green session color (dupre green)."
+ :group 'ai-term)
+(defface cj/ai-term-color-yellow '((t :foreground "#d7af5f"))
+ "Agent terminal rendering of Claude Code's yellow session color (dupre yellow)."
+ :group 'ai-term)
+(defface cj/ai-term-color-purple '((t :foreground "#b294bb"))
+ "Agent terminal rendering of Claude Code's purple session color (dupre magenta)."
+ :group 'ai-term)
+(defface cj/ai-term-color-orange '((t :foreground "#edb08f"))
+ "Agent terminal rendering of Claude Code's orange session color (dupre red+1)."
+ :group 'ai-term)
+(defface cj/ai-term-color-pink '((t :foreground "#c397d8"))
+ "Agent terminal rendering of Claude Code's pink session color (dupre magenta+1)."
+ :group 'ai-term)
+(defface cj/ai-term-color-cyan '((t :foreground "#8a9496"))
+ "Agent terminal rendering of Claude Code's cyan session color (dupre steel)."
+ :group 'ai-term)
+
+(defvar cj/ai-term-palette-faces
+ '((211 . cj/ai-term-accent) ; bypass banner (fixed, not a /color)
+ (167 . cj/ai-term-color-red)
+ (110 . cj/ai-term-color-blue)
+ (35 . cj/ai-term-color-green)
+ (178 . cj/ai-term-color-yellow)
+ (140 . cj/ai-term-color-purple)
+ (174 . cj/ai-term-color-orange)
+ (175 . cj/ai-term-color-pink)
+ (37 . cj/ai-term-color-cyan))
+ "Alist of (XTERM-256-INDEX . FACE) repainted in agent terminals.
+The indices are what Claude Code v2.1.198 emits: 211 for the fixed
+bypass-permissions banner, the rest one per /color session color
+\(probed by cycling /color in a scratch session and reading the SGR
+codes). Applied per terminal by `cj/--ai-term-apply-accent', so other
+eat terminals keep the true xterm palette. If a Claude Code update
+moves an accent to a new index, the visible symptom is the stock xterm
+hue coming back -- re-probe and update the index here.")
;; Agent buffers ("agent [<project>]") are buried, not killed, by the
;; kill-all sweep (F1 / `cj/dashboard-only'). Register the family pattern so
diff --git a/tests/test-ai-term--accent.el b/tests/test-ai-term--accent.el
index 7ffd71a1..2f381bab 100644
--- a/tests/test-ai-term--accent.el
+++ b/tests/test-ai-term--accent.el
@@ -1,10 +1,11 @@
-;;; test-ai-term--accent.el --- Tests for the agent-terminal accent color -*- lexical-binding: t; -*-
+;;; test-ai-term--accent.el --- Tests for the agent-terminal accent colors -*- lexical-binding: t; -*-
;;; Commentary:
-;; Tests the per-terminal accent recolor: the accent face's dupre-blue default,
-;; the palette-index list, the apply helper that points a terminal's 256-color
-;; palette entries at the accent face, and the show-or-create wiring. eat and
-;; its terminal API are stubbed -- no process spawning, no eat load in batch.
+;; Tests the per-terminal palette recolor: the dupre faces for Claude Code's
+;; session colors, the palette-index-to-face alist, the apply helper that
+;; points a terminal's 256-color palette entries at those faces, and the
+;; show-or-create wiring. eat and its terminal API are stubbed -- no process
+;; spawning, no eat load in batch.
;;; Code:
@@ -17,7 +18,7 @@
(declare-function cj/--ai-term-apply-accent "ai-term-backend-eat" (buffer))
(declare-function cj/--ai-term-show-or-create "ai-term-backend-eat" (dir name))
-(defvar cj/ai-term-accent-color-indices)
+(defvar cj/ai-term-palette-faces)
(defvar cj/--ai-term-mru)
(defvar eat-buffer-name)
(defvar eat-terminal)
@@ -28,44 +29,70 @@
(unless (fboundp 'eat-term-set-parameter)
(defun eat-term-set-parameter (_terminal _parameter _value) nil))
-;;; ------------------------------ accent face ---------------------------------
+;;; ------------------------------ accent faces --------------------------------
(ert-deftest test-ai-term-accent-face-defaults-to-dupre-blue ()
- "Normal: the accent face exists and defaults to dupre blue (#67809c)."
+ "Normal: the default accent face exists and is dupre blue (#67809c)."
(should (facep 'cj/ai-term-accent))
(should (string-equal-ignore-case
(face-attribute 'cj/ai-term-accent :foreground nil t)
"#67809c")))
-(ert-deftest test-ai-term-accent-indices-default ()
- "Normal: the remapped palette indices default to Claude Code's accent (211)."
- (should (equal cj/ai-term-accent-color-indices '(211))))
+(ert-deftest test-ai-term-session-color-faces-carry-dupre-hues ()
+ "Normal: each of Claude Code's session colors has a face with its dupre hue."
+ (dolist (pair '((cj/ai-term-color-red . "#d47c59")
+ (cj/ai-term-color-blue . "#67809c")
+ (cj/ai-term-color-green . "#a4ac64")
+ (cj/ai-term-color-yellow . "#d7af5f")
+ (cj/ai-term-color-purple . "#b294bb")
+ (cj/ai-term-color-orange . "#edb08f")
+ (cj/ai-term-color-pink . "#c397d8")
+ (cj/ai-term-color-cyan . "#8a9496")))
+ (should (facep (car pair)))
+ (should (string-equal-ignore-case
+ (face-attribute (car pair) :foreground nil t)
+ (cdr pair)))))
+
+(ert-deftest test-ai-term-palette-faces-cover-banner-and-session-colors ()
+ "Normal: the alist pins the bypass banner (211) and all 8 session-color indices."
+ (should (eq (alist-get 211 cj/ai-term-palette-faces) 'cj/ai-term-accent))
+ (dolist (pair '((167 . cj/ai-term-color-red)
+ (110 . cj/ai-term-color-blue)
+ (35 . cj/ai-term-color-green)
+ (178 . cj/ai-term-color-yellow)
+ (140 . cj/ai-term-color-purple)
+ (174 . cj/ai-term-color-orange)
+ (175 . cj/ai-term-color-pink)
+ (37 . cj/ai-term-color-cyan)))
+ (should (eq (alist-get (car pair) cj/ai-term-palette-faces) (cdr pair)))))
;;; --------------------------- cj/--ai-term-apply-accent ----------------------
(ert-deftest test-ai-term-apply-accent-sets-palette-entries ()
- "Normal: every configured index is pointed at the accent face via the eat API."
- (let ((set-params nil))
+ "Normal: every alist entry is pointed at its face via the eat API."
+ (let ((set-params nil)
+ (cj/ai-term-palette-faces '((211 . cj/ai-term-accent)
+ (110 . cj/ai-term-color-blue))))
(cl-letf (((symbol-function 'eat-term-set-parameter)
(lambda (_terminal parameter value)
(push (cons parameter value) set-params))))
(with-temp-buffer
(setq-local eat-terminal 'dummy-terminal)
(cj/--ai-term-apply-accent (current-buffer))))
- (should (equal set-params '((color-211-face . cj/ai-term-accent))))))
+ (should (equal (nreverse set-params)
+ '((color-211-face . cj/ai-term-accent)
+ (color-110-face . cj/ai-term-color-blue))))))
-(ert-deftest test-ai-term-apply-accent-multiple-indices ()
- "Boundary: several indices each get their own palette-entry call."
- (let ((set-params nil)
- (cj/ai-term-accent-color-indices '(211 174)))
+(ert-deftest test-ai-term-apply-accent-full-alist-count ()
+ "Boundary: the default alist yields one palette call per entry (9 total)."
+ (let ((set-params nil))
(cl-letf (((symbol-function 'eat-term-set-parameter)
(lambda (_terminal parameter value)
(push (cons parameter value) set-params))))
(with-temp-buffer
(setq-local eat-terminal 'dummy-terminal)
(cj/--ai-term-apply-accent (current-buffer))))
- (should (equal (sort (mapcar #'car set-params) #'string<)
- '(color-174-face color-211-face)))))
+ (should (= (length set-params) (length cj/ai-term-palette-faces)))))
(ert-deftest test-ai-term-apply-accent-no-terminal-is-noop ()
"Error: a buffer without a live eat terminal is left alone, no API call, no error."
@@ -80,7 +107,7 @@
;;; ------------------------- show-or-create wiring ----------------------------
(ert-deftest test-ai-term-show-or-create-applies-accent-on-create ()
- "Normal: creating a fresh agent terminal applies the accent to its buffer."
+ "Normal: creating a fresh agent terminal applies the palette to its buffer."
(let ((name "agent [accent-wire-test]")
(cj/--ai-term-mru nil)
(applied nil))