aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/ai-term-backend-eat.el20
-rw-r--r--modules/ai-term.el19
-rw-r--r--tests/test-ai-term--accent.el103
3 files changed, 142 insertions, 0 deletions
diff --git a/modules/ai-term-backend-eat.el b/modules/ai-term-backend-eat.el
index 6d512831..82218ca7 100644
--- a/modules/ai-term-backend-eat.el
+++ b/modules/ai-term-backend-eat.el
@@ -25,9 +25,12 @@
(require 'ai-term-sessions)
(declare-function eat "eat" (&optional program arg))
+(declare-function eat-term-set-parameter "eat" (terminal parameter value))
(declare-function cj/ai-term-next "ai-term" ())
(defvar eat-buffer-name)
(defvar eat-semi-char-mode-map)
+(defvar eat-terminal)
+(defvar cj/ai-term-accent-color-indices)
(defun cj/--ai-term-send-string (buffer string)
"Send STRING to BUFFER's terminal process (the agent's shell).
@@ -36,6 +39,22 @@ Sends to the pty directly so the launch command reaches the shell EAT runs."
(when (process-live-p proc)
(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)."
+ (with-current-buffer buffer
+ (when (bound-and-true-p eat-terminal)
+ (dolist (index cj/ai-term-accent-color-indices)
+ (eat-term-set-parameter eat-terminal
+ (intern (format "color-%d-face" index))
+ 'cj/ai-term-accent)))))
+
(defun cj/--ai-term-show-or-create (dir name)
"Show or create the AI-term buffer for project DIR with buffer NAME.
@@ -76,6 +95,7 @@ buffer."
(eat)))
(let ((buf (get-buffer name)))
(with-current-buffer buf
+ (cj/--ai-term-apply-accent buf)
(cj/--ai-term-send-string
buf (concat (cj/--ai-term-launch-command dir) "\n")))
(display-buffer buf)
diff --git a/modules/ai-term.el b/modules/ai-term.el
index 7973f7aa..0774758e 100644
--- a/modules/ai-term.el
+++ b/modules/ai-term.el
@@ -115,6 +115,25 @@ fallback when `cj/--ai-term-last-size' is nil."
:type 'number
:group 'ai-term)
+(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."
+ :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.")
+
;; Agent buffers ("agent [<project>]") are buried, not killed, by the
;; kill-all sweep (F1 / `cj/dashboard-only'). Register the family pattern so
;; every agent -- however and whenever created -- survives with its session.
diff --git a/tests/test-ai-term--accent.el b/tests/test-ai-term--accent.el
new file mode 100644
index 00000000..7ffd71a1
--- /dev/null
+++ b/tests/test-ai-term--accent.el
@@ -0,0 +1,103 @@
+;;; test-ai-term--accent.el --- Tests for the agent-terminal accent color -*- 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.
+
+;;; Code:
+
+(require 'ert)
+(require 'cl-lib)
+
+(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory))
+(require 'ai-term)
+
+(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-mru)
+(defvar eat-buffer-name)
+(defvar eat-terminal)
+
+;; eat isn't loaded in batch -- provide stubs so cl-letf has overrides.
+(unless (fboundp 'eat)
+ (defun eat (&optional _program _arg) nil))
+(unless (fboundp 'eat-term-set-parameter)
+ (defun eat-term-set-parameter (_terminal _parameter _value) nil))
+
+;;; ------------------------------ accent face ---------------------------------
+
+(ert-deftest test-ai-term-accent-face-defaults-to-dupre-blue ()
+ "Normal: the accent face exists and defaults to 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))))
+
+;;; --------------------------- 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))
+ (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))))))
+
+(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)))
+ (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)))))
+
+(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."
+ (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
+ (cj/--ai-term-apply-accent (current-buffer))))
+ (should-not set-params)))
+
+;;; ------------------------- 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."
+ (let ((name "agent [accent-wire-test]")
+ (cj/--ai-term-mru nil)
+ (applied nil))
+ (when (get-buffer name) (kill-buffer name))
+ (unwind-protect
+ (cl-letf (((symbol-function 'eat)
+ (lambda (&optional _program _arg)
+ (get-buffer-create eat-buffer-name)))
+ ((symbol-function 'cj/--ai-term-send-string)
+ (lambda (_buf _s) nil))
+ ((symbol-function 'display-buffer)
+ (lambda (&rest _) nil))
+ ((symbol-function 'cj/--ai-term-apply-accent)
+ (lambda (buffer) (push (buffer-name buffer) applied))))
+ (cj/--ai-term-show-or-create "/tmp/accent-wire-test" name)
+ (should (equal applied (list name))))
+ (when (get-buffer name) (kill-buffer name)))))
+
+(provide 'test-ai-term--accent)
+;;; test-ai-term--accent.el ends here