Files
cl-tty/org/package.org
Hermes Agent 29f99a576d literate: restructure all 19 org files with per-function blocks and prose
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%.
2026-05-12 18:55:07 +00:00

6.8 KiB

Base Component Package

Overview

The cl-tty.box package is the central namespace for the component system. It aggregates all component-related symbols — box, text, dirty tracking, render dispatch, theme engine — under one package.

Why box as the package name? Historically the package was created for the box and text renderables, and the name stuck as the package grew to encompass the entire component layer. The package :use~s ~cl-tty.backend (for drawing primitives) and cl-tty.layout (for layout nodes). All component code lives in this package.

This org file is documentation-only: it explains the package design but the code itself is just a defpackage form.

Contract

The cl-tty.box package exports these symbol groups:

  • Box: box, make-box, render-box, border style/title accessors
  • Span: span, span attribute readers
  • Text: text, make-text, render-text, text accessors
  • Dirty: dirty-mixin, dirty-p, mark-clean, mark-dirty
  • Render: render, render-screen, render-node, tree navigation
  • Theme: theme, make-theme, theme-color, load-preset, define-preset

Implementation

cl-tty.box uses cl-tty.backend for draw-text, draw-border, etc., and cl-tty.layout for layout-node, compute-layout, and the vbox~/~hbox macros.

The only direct dependencies are these two packages — no other application code is needed to define components.

Box exports

The box class is the primary rectangular container: it renders a bordered region with optional title and background color. The accessor family (box-border-style, box-title, box-title-align, box-fg, box-bg) follows a consistent naming convention so that users can infer slot names from the class name. render-box is the specialized method that draws the border and fills the interior.

The box-layout-node accessor connects the box to its layout tree node, which is essential for the render pipeline's coordinate computation. We export it separately from the rendering symbols because it is also needed by code that walks the component tree without triggering a full render.

(defpackage :cl-tty.box
  (:use :cl :cl-tty.backend :cl-tty.layout)
  (:export
   ;; Box
   #:box #:make-box
   #:box-layout-node
   #:box-border-style #:box-title #:box-title-align
   #:box-fg #:box-bg
   #:render-box

Span exports

Spans are lightweight inline-style records — not full classes with layout. Each span stores a substring of the parent text along with its visual attributes. The reader-named accessors (span-text, span-bold, span-italic, etc.) let rendering code inspect span properties without pulling in the internal representation. We keep the accessor list flat (no grouping macro) to make the package surface easy to grep and to keep the API browser-friendly.

   ;; Span
   #:span
   #:span-text #:span-bold #:span-italic #:span-underline
   #:span-reverse #:span-dim #:span-fg #:span-bg

Text exports

text and make-text are the construction interface for the text renderable. The text-layout-node accessor follows the same pattern as box-layout-node, bridging the component and layout layers. text-content and text-spans expose the raw data for rendering; text-fg, text-bg, and text-wrap-mode control global text appearance. render-text is the CLOS method that walks the span list and calls draw-text from the backend.

These symbols live in the cl-tty.box package rather than a separate cl-tty.text package to keep inter-component references trivial — boxes can hold text children, and text can be nested inside other components, all without cross-package imports.

   ;; Text
   #:text #:make-text
   #:text-layout-node #:text-content #:text-spans
   #:text-fg #:text-bg #:text-wrap-mode
   #:render-text

Utility exports (for tests)

word-wrap and split-string are internal text-processing utilities used by the text renderer to break lines and tokenize input. They are exported specifically so the test suite can unit-test them in isolation. They are not part of the public component API and should not be relied upon by application code outside of tests.

   ;; Utilities (for tests)
   #:word-wrap #:split-string

Dirty tracking

The dirty-mixin protocol lets any component class participate in the change-propagation system. dirty-mixin is the mixin class, and dirty-p, mark-clean, mark-dirty are the three operations that the render pipeline calls to decide whether a subtree needs re-rendering.

Having these as generic functions (rather than a single (setf dirty-p)) makes it easy for subclasses to add side effects on dirty transitions — for example, invalidating a cached bitmap or recomputing string metrics.

   ;; Dirty tracking
   #:dirty-mixin #:dirty-p #:mark-clean #:mark-dirty

Rendering pipeline

render, render-screen, and render-node are the three entry points into the rendering dispatch. component-layout-node, component-children, and component-parent form the tree-navigation interface that render-node uses to walk the component hierarchy. available-width and available-height are passed down the tree to constrain layout. propagate-dirty walks upward from a changed component to mark ancestors as dirty, ensuring the screen is re-drawn from the correct root.

Collecting these under a single "Rendering pipeline" group signals to readers that they form a coherent subsystem — if you override one, you likely need to understand all of them.

   ;; Rendering pipeline
   #:render #:render-screen #:render-node
   #:component-layout-node #:component-children #:component-parent
   #:available-width #:available-height
   #:propagate-dirty

Theme engine

theme and make-theme are the constructor and class for theme objects. theme-mode selects the active color mode (light/dark). theme-color looks up a named color in the current theme. load-preset loads a theme from a file, and define-preset registers a preset at compile time.

The theme engine is isolated from the rest of the component layer — boxes and text reference theme colors by name at render time, and the theme object is passed in from the application level. This separation means themes can be swapped without touching component instances.

   ;; Theme engine
   #:theme #:make-theme #:theme-mode
   #:theme-color #:load-preset #:define-preset))
(in-package :cl-tty.box)