org/text-input.org: remove (declare (ignore w)) from textarea render;
add truncation to text-input render (subseq display 0 w)
org/mouse.org: hit-test now uses component-layout-node and recurses
into children for deepest-match hit testing
org/select.org: render reads layout-node-x/y instead of hardcoded (0,0)
org/scrollbox-tabbar.org: tabbar render reads layout-node-x/y
instead of hardcoded (0,0); x-pos starts at x offset
All 4 org files tangled clean. 392 tests pass.
CRITICAL: case b → cond in %read-event (input.lisp:280)
case with (and ...) predicate clauses treats keys as eql-compared
atoms — all range clauses were dead code. Every Ctrl+letter and
printable ASCII fell through to :unknown. text-input/textarea
widgets were non-functional with real terminal input. No test
coverage of %read-event masked this.
HIGH: Theme resolution wired (backend/modern.lisp, theme.lisp)
sgr-fg/sgr-bg now fall back to *theme-colors* hash for semantic
keywords (:accent, :text-muted, :background-element). *theme-colors*
exported from cl-tty.backend. load-preset populates it from preset
hex values. Previously all themed render output was invisible.
HIGH: SGR mouse parser wired (input.lisp:210-215)
parse-sgr-mouse was defined but never called. Now %read-escape-sequence
detects ESC[< prefix and routes to parse-sgr-mouse. Mouse drags,
releases, and scroll events now parse correctly.
MEDIUM: Rendering stubs replaced
- scrollbox: delegates to (render child backend) with position
offset via unwind-protect (was debug string 'child at ~D')
- text-input: draws value/placeholder at layout position
- textarea: draws visible lines at layout position
MEDIUM: hit-test uses component-layout-node (mouse.lisp:18-31)
Was checking nonexistent x/y/width/height slots. Now reads
layout-node-x/y/w/h via component-layout-node generic.
MEDIUM: test runner exit code (run-all-tests.lisp, cl-tty.asd)
run-all-tests.lisp exits 1 if any suite fails.
asdf:test-system exits 1 on failure.
Renamed :cl-tty-tests to :cl-tty/test (ASDF convention).
MEDIUM: draw-border respects x/y on simple-backend (simple.lisp:42-53)
Was writing to cursor position only. Now uses newlines+spaces
to reach specified coordinates (no escape sequences needed).
LOW: TabBar truncation off-by-one fixed (tabbar.lisp:47)
>= changed to > to avoid cutting tabs 2 chars early.
LOW: Scrollbar coordinates absolute (scrollbox.lisp:61-73)
Scrollbar drawn at viewport-relative (0,0). Now adds layout
node x/y offset for correct terminal positioning.
LOW: backend-write calls finish-output (modern.lisp:169)
LOW: load-preset no longer flips theme-mode (theme.lisp:43-45)
Mode toggle caused load-preset to load wrong variant on
second call.
All backported to org source files (org/text-input.org,
org/scrollbox-tabbar.org) so tangling produces matching .lisp.
392 tests pass, exit code 0.
New module: src/rendering/framebuffer.lisp (tangled from org/framebuffer.org)
- framebuffer-backend class: implements backend protocol by writing to
2D cell array instead of emitting escape sequences
- cell struct: per-cell state (char, fg, bg, bold, italic, underline, link-url)
- make-framebuffer / framebuffer-width / framebuffer-height
- draw-text, draw-rect, draw-border, draw-link, draw-ellipsis methods
- diff-framebuffers: compares two framebuffers, returns changed cells
- flush-framebuffer: diff + output changes to real backend
- with-scissor macro: clip drawing operations to rectangle
- cursor-move: added default no-op method for all backends
- 20 new tests, all passing (372 total)
Version bumped from 0.11.0 to 0.13.0.
License field set to GPL-3.0 in ASDF.
- defslot: register render functions into named slots with ordering
- slot-render: call all registered render-fns for a slot
- Slot modes designed (stack/replace/single-winner) but mode dispatch
is implicit via the registration API
- slot-p, clear-slot, list-slots for lifecycle management
- Slots stored in a hash table keyed by string (equal test)
- 4 tests, 100% passing
- mouse-mixin class with on-mouse-down/up/move/scroll handler slots
- handle-mouse-event dispatches to the right handler by event type
- hit-test finds deepest component at (x,y) coordinates
- selection struct + get-selection + copy-to-clipboard
- SGR mouse parsing already existed in input system (mouse-event struct,
parse-sgr-mouse function, CSI dispatch in %read-escape-sequence)
- 3 tests, 100% passing
37 per-function code blocks with prose explaining design reasoning,
edge cases, and CL traps. Combined tangle blocks at end for actual
compilation.
New scripts/tangle.py: reliable Python tangler (emacs --batch failed).
Added: %split-string, %join-lines, tangle helper.
CL traps documented in org prose:
- defstruct generates keyword constructors (no :constructor needed)
- case with strings uses EQL — use cond + string=
- CL strings: no \n escape — use (string #\Newline)
- FiveAM closure capture — use list boxing
- read-byte is package-locked — use read-raw-byte
- ASDF compile-file stricter than LOAD — debug with LOAD
60 tests, 100% GREEN.
- Box class with border-style, title, fg/bg slots
- render-box dispatches through backend protocol
- draw-border for borders, draw-rect for background
- draw-text for title below top border
- 7 tests: defaults, border, background, title, no-border,
zero-size, minimum-size
- 13 assertions, 100% GREEN
- ASDF updated with src/components module
- modern-backend now accepts :output-stream initarg