CRITICAL: Remove (declare (ignore w)) from textarea render (textarea.lisp:251) w is used for horizontal truncation on the next line. Declaring it ignored while using it is undefined behavior in CL (SBCL warns). HIGH: hit-test recurses into children (mouse.lisp:18-34) Was returning the root component for any click within its bounds, ignoring nested widgets entirely. Now checks component-children first, returning the deepest match. MEDIUM: Select/TabBar position hardcoded to (0,0) Both rendered at terminal origin regardless of layout position. Now read layout-node-x/y for absolute positioning. MEDIUM: Text-input truncation missing Render drew full value string even when exceeding widget width. Now truncates to (min (length display) w). MEDIUM: X10 mouse release detection added (input.lisp:219-226) X10 encoding uses button=3 for release. Was detecting all events as press/drag. Now checks button=3 → :release. MEDIUM: parse-csi-params handles private markers (input.lisp:128-131) < = > ? characters (0x3c-0x3f) treated as parameter start markers instead of accumulating bogus digit values. Latent trap removed. Deferred (pre-existing design): - Scrollbox visibility cy vs orig-y: match for column layout (common case) - Nested scrollbox coordinates: assumes sequential layout positions - text-input cursor drawing: feature, not bugfix 392 tests pass.
Pure CL terminal UI framework. No ncurses, no FFI, no external dependencies.
```lisp (ql:quickload :cl-tty) ```
## Quick start
```lisp ;; Create a modern terminal backend (let ((backend (make-instance 'cl-tty.backend:modern-backend))) (cl-tty.backend:initialize-backend backend) ;; Backend is ready — write text, draw boxes, handle input (cl-tty.backend:shutdown-backend backend)) ```
## Architecture
Two backends, one protocol:
- modern-backend — truecolor 24-bit, OSC 8 hyperlinks, DECICM sync, SGR mouse, kitty keyboard, bold/italic/underline, box-drawing chars
- simple-backend — ASCII art, no color, universal compatibility
Everything is pure escape sequences (no curses, no terminfo, no FFI).
## Components
| Component | What it does | Version |
|---|---|---|
| Box | Bordered container with background, title | v0.2.0 |
| Text | Styled text with word-wrap, spans | v0.2.0 |
| ScrollBox | Scrollable viewport with scrollbars | v0.6.0 |
| TabBar | Horizontal tab navigation | v0.6.0 |
| Select | Dropdown with fuzzy filter, category headers | v0.7.0 |
| TextInput | Single-line text input with readline keybindings | v0.5.0 |
| TextArea | Multi-line input with undo/redo, selection | v0.5.0 |
| Markdown | Renders markdown with syntax highlighting + diffs | v0.8.0 |
| Dialog | Modal overlays with stack management | v0.9.0 |
| Toast | Transient notifications (info/success/warning/error) | v0.9.0 |
| Mouse | Event handlers, hit-testing, text selection | v0.10.0 |
| Slot | Plugin system — named slots for extensible UI | v0.11.0 |
## Backend features
| Feature | modern | simple |
|---|---|---|
| Truecolor (24-bit) | Yes | No |
| Bold/italic | Yes | No |
| OSC 8 hyperlinks | Yes | No |
| DECICM sync | Yes | No |
| SGR mouse | Yes | No |
| Kitty keyboard | Yes | No |
| Box drawing chars | Unicode | ASCII |
| Pipe-safe | No | Yes |
## Development
```bash
sbcl –script run-all-tests.lisp
emacs –batch –eval "(progn (require 'org) (find-file \"org/FILE.org\") (org-babel-tangle) (kill-buffer))" ```
Literate programming: `.org` files in `org/` are the source of truth. `.lisp` files are generated by tangling.
## License
GNU General Public License v3.0