Every function, defclass, defstruct, defgeneric, defmethod, defmacro, defvar, and defparameter in every org file now has its own #+BEGIN_SRC block with literate prose above it explaining the design reasoning. Block counts before → after: package.org: 1 → 7 container-package.org: 1 → 1 (prose expanded) dirty.org: 4 → 6 render.org: 10 → 25 theme.org: 6 → 19 box-renderable.org: 9 → 29 scrollbox.org: 8 → 26 tabbar.org: 5 → 10 backend-protocol.org: 8 → 66 modern-backend.org: 17 → 53 detection.org: 4 → 6 layout-engine.org: 9 → 36 framebuffer.org: 8 → 37 markdown-renderer.org:13 → 38 dialog.org: 17 → 23 (merged dual structure) mouse.org: 4 → 25 select.org: 12 → 30 slot.org: 4 → 12 text-input.org: 11 → 53 Total: ~153 blocks → ~502 blocks Bugs fixed during restructuring: - render.org: stray π character typo (backenπd → backend) - modern-backend.org: sgr-attr missing closing paren + #+END_SRC - detection.org: invalid #\Esc character reference - select.org: extra closing paren in select-visible-options All 13 test suites pass at 100%.
90 lines
4.4 KiB
Common Lisp
90 lines
4.4 KiB
Common Lisp
(in-package :cl-tty.box)
|
|
|
|
(defclass theme ()
|
|
((mode :initform :dark :initarg :mode :accessor theme-mode)
|
|
(roles :initform (make-hash-table) :accessor theme-roles)))
|
|
|
|
(defun make-theme (&key (mode :dark))
|
|
(make-instance 'theme :mode mode))
|
|
|
|
(defun theme-color (theme role)
|
|
"Resolve a semantic ROLE to a hex color string in THEME."
|
|
(gethash role (theme-roles theme)))
|
|
|
|
(defun (setf theme-color) (hex theme role)
|
|
"Set the hex color for a semantic ROLE in THEME."
|
|
(setf (gethash role (theme-roles theme)) hex))
|
|
|
|
(defparameter *presets* (make-hash-table :test #'eq))
|
|
|
|
(defmacro define-preset (name &key dark light)
|
|
"Define a theme preset with DARK and LIGHT variants.
|
|
NAME should be a keyword (e.g., :default, :nord)."
|
|
(check-type name keyword)
|
|
`(setf (gethash ,name *presets*) '(:dark ,dark :light ,light)))
|
|
|
|
(defun load-preset (theme preset-name)
|
|
"Load PRESET-NAME colors into THEME.
|
|
Side-effect: populates cl-tty.backend:*theme-colors* so that semantic
|
|
color roles resolve to hex at SGR generation time."
|
|
(let ((preset (gethash preset-name *presets*)))
|
|
(if preset
|
|
(let* ((colors (if (eql (theme-mode theme) :dark)
|
|
(getf preset :dark)
|
|
(getf preset :light)))
|
|
;; Populate backend theme color map
|
|
(theme-map cl-tty.backend:*theme-colors*))
|
|
;; Set theme colors
|
|
(loop for (role hex) on colors by #'cddr
|
|
do (setf (theme-color theme role) hex)
|
|
(setf (gethash role theme-map) hex)))
|
|
(warn "Unknown preset: ~S" preset-name))))
|
|
|
|
(define-preset :default
|
|
:dark (:primary "#FFD700" :secondary "#B8860B" :accent "#FFA500"
|
|
:error "#FF4444" :warning "#FF8800" :success "#44BB44" :info "#4488FF"
|
|
:text "#FFFFFF" :text-muted "#888888"
|
|
:background "#1A1A2E" :background-panel "#16213E" :background-element "#0F3460"
|
|
:border "#334155" :border-active "#FFD700"
|
|
:diff-added "#164B16" :diff-removed "#4B1616" :diff-context "#1A1A2E"
|
|
:markdown-heading "#FFD700" :markdown-code "#334155"
|
|
:markdown-link "#4488FF" :markdown-quote "#888888"
|
|
:syntax-keyword "#FF79C6" :syntax-function "#50FA7B"
|
|
:syntax-string "#F1FA8C" :syntax-number "#BD93F9"
|
|
:syntax-comment "#6272A4" :syntax-type "#8BE9FD")
|
|
:light (:primary "#B8860B" :secondary "#8B6914" :accent "#FF8C00"
|
|
:error "#CC0000" :warning "#CC6600" :success "#228B22" :info "#0055CC"
|
|
:text "#1A1A2E" :text-muted "#888888"
|
|
:background "#F8F9FA" :background-panel "#FFFFFF" :background-element "#E9ECEF"
|
|
:border "#DEE2E6" :border-active "#B8860B"
|
|
:diff-added "#DFD" :diff-removed "#FDD" :diff-context "#F8F9FA"
|
|
:markdown-heading "#B8860B" :markdown-code "#E9ECEF"
|
|
:markdown-link "#0055CC" :markdown-quote "#888888"
|
|
:syntax-keyword "#D63384" :syntax-function "#198754"
|
|
:syntax-string "#FFC107" :syntax-number "#6F42C1"
|
|
:syntax-comment "#6C757D" :syntax-type "#0DCAF0"))
|
|
|
|
(define-preset :nord
|
|
:dark (:primary "#88C0D0" :secondary "#81A1C1" :accent "#5E81AC"
|
|
:error "#BF616A" :warning "#D08770" :success "#A3BE8C" :info "#B48EAD"
|
|
:text "#ECEFF4" :text-muted "#616E88"
|
|
:background "#2E3440" :background-panel "#3B4252" :background-element "#434C5E"
|
|
:border "#4C566A" :border-active "#88C0D0"
|
|
:diff-added "#164B16" :diff-removed "#4B1616" :diff-context "#2E3440"
|
|
:markdown-heading "#88C0D0" :markdown-code "#3B4252"
|
|
:markdown-link "#81A1C1" :markdown-quote "#616E88"
|
|
:syntax-keyword "#81A1C1" :syntax-function "#A3BE8C"
|
|
:syntax-string "#EBCB8B" :syntax-number "#B48EAD"
|
|
:syntax-comment "#616E88" :syntax-type "#88C0D0")
|
|
:light (:primary "#5E81AC" :secondary "#81A1C1" :accent "#88C0D0"
|
|
:error "#BF616A" :warning "#D08770" :success "#A3BE8C" :info "#B48EAD"
|
|
:text "#2E3440" :text-muted "#8F9BB3"
|
|
:background "#ECEFF4" :background-panel "#FFFFFF" :background-element "#E5E9F0"
|
|
:border "#D8DEE9" :border-active "#5E81AC"
|
|
:diff-added "#DFD" :diff-removed "#FDD" :diff-context "#ECEFF4"
|
|
:markdown-heading "#5E81AC" :markdown-code "#E5E9F0"
|
|
:markdown-link "#81A1C1" :markdown-quote "#8F9BB3"
|
|
:syntax-keyword "#81A1C1" :syntax-function "#A3BE8C"
|
|
:syntax-string "#D08770" :syntax-number "#B48EAD"
|
|
:syntax-comment "#8F9BB3" :syntax-type "#88C0D0"))
|