10 KiB
cl-tty Roadmap
- The Roadmap
- v0.0.1: Backend Protocol
- Layout Engine (pure CL)
- v0.2.0: Box, Text, Span, Dirty Tracking
- v0.5.0: Text Input + Keybinding System
- v0.6.0: ScrollBox + TabBar
- v0.7.0: Select — Dropdown + Fuzzy Filter
- v0.8.0: Markdown + Code + Diff Rendering
- v0.9.0: Dialog System + Toast
- v0.10.0: Mouse Support
- v0.11.0: Plugin / Slot System
- v0.12.0: Terminal Capability Detection
- v0.13.0: Rendering Pipeline
- v0.14.0: Mouse Improvements
- v0.15.0: Bug fixes, demo rewrite, verification, tangle tooling
- v1.0.0: Release
- v1.1.0: SGR Mouse Event Parsing
- Feature Reference
The Roadmap
Each phase is one minor release. Phases ship in dependency order — each depends on the components from prior phases.
v0.0.1: Backend Protocol
DONE. Two backends implementing a common protocol:
modern-backend— raw escape sequences, truecolor 24-bit, OSC 8 hyperlinks, DECICM sync, SGR mouse, kitty keyboard protocol, bold/italic/underline, box-drawing chars (rounded/single/double)simple-backend— ASCII art only, no color, universal compatibility for SSH/piped output
~180 lines total. Dependencies: None (pure CL, no FFI).
Backend protocol generic functions:
initialize-backend,shutdown-backend,backend-size,backend-write,backend-cleardraw-rect,draw-text,draw-border,draw-ellipsis,draw-linkcursor-move,cursor-hide,cursor-show,cursor-stylebegin-sync,end-sync(DECICM)read-event,enable-mouse,enable-bracketed-paste,set-keyboard-modecapable-p— query feature support
Layout Engine (pure CL)
DONE. Pure Common Lisp Flexbox layout engine. No Yoga, no CFFI, no external dependencies. A two-pass constraint solver handling direction, wrap, grow/shrink/gap padding/margin, absolute positioning.
~190 lines. Macros: vbox, hbox, spacer.
v0.2.0: Box, Text, Span, Dirty Tracking
DONE. The first two renderable types. Box draws borders and backgrounds. Text renders strings with color, word-wrap, and inline style spans.
Boxwith border styles (:single, :double, :rounded), title, backgroundTextwith word-wrap (:none, :word), fg/bg colorsSpan— inline text segment with attributes (:bold, :italic, etc.)Dirty-mixin— marks components and ancestors for re-renderTheme— semantic color tokens, presets (default, nord, catppuccin, etc.)rendergeneric function dispatched on component type
v0.5.0: Text Input + Keybinding System
DONE. Text input widgets with readline-style keybindings.
TextInput— single-line input with cursor, placeholder, max-length, on-submitTextarea— multi-line input with undo/redo (100-deep stack), cursor nav, selection, on-submitKeymap— layered keybinding system withdefkeymapmacro- Event handling: key-event, mouse-event structs, raw-byte reader
v0.6.0: ScrollBox + TabBar
DONE. Container components.
ScrollBox— scrollable viewport with vertical/horizontal scrollbars, scroll-by, clamp, sticky-scroll modeTabBar— horizontal tab navigation with next/prev, active tab tracking
v0.7.0: Select — Dropdown + Fuzzy Filter
DONE. A selection list component with keyboard navigation, category headers, and fuzzy text matching.
v0.8.0: Markdown + Code + Diff Rendering
DONE. Content rendering for agent responses and file diffs.
- Markdown parser: headings, bold/italic/code, links, code blocks, blockquotes, lists, thematic breaks
- Syntax highlighting: regex-based for Lisp keywords, comments, strings
- Diff rendering: added/removed/context lines with colored backgrounds
- ANSI rendering via raw escape sequences
v0.9.0: Dialog System + Toast
DONE. Modal overlays and transient notifications.
Dialog— centered modal with backdrop dimming, size variantspush-dialog/pop-dialog— stack-based dialog managementalert-dialog,confirm-dialog,select-dialog,prompt-dialogToast— transient notification with variants (:info/:success/:warning/:error), auto-dismiss, top-right positioning
v0.10.0: Mouse Support
DONE (minimal). Mouse event handling via mixin class.
mouse-mixin— event handler slots (:on-mouse-down/up/move/scroll)handle-mouse-event— dispatch to component handlershit-test— find deepest component at (x, y)selectionstruct andcopy-to-clipboard
v0.11.0: Plugin / Slot System
DONE. Extensible named slots for registering content into extensible positions.
defslot,slot-render,clear-slot,list-slots- Slot modes planned but not implemented
v0.12.0: Terminal Capability Detection
DONE. Auto-detect terminal capabilities at startup and return the appropriate backend.
- Check if stdout is a TTY (if not -> simple-backend)
detect-backend-> returnsmodern-backendorsimple-backend- Send DA1 query (
ESC[c), 100ms timeout - Send DA3 (
ESC[?c) for kitty/wezterm identification - Query DECRPM (
ESC[?2026$p) for DECICM sync support - Check
COLORTERMenv var for truecolor support - Cache detection result for subsequent instant calls
- Add
detect-backendto backend package API - ~100 lines
v0.13.0: Rendering Pipeline
DONE. A pure CL rendering pipeline — framebuffer diffing for incremental output, scissor clipping, and render-command dispatching.
*framebuffer*— 2D array of (char, fg, bg, attrs) tuplesflush-framebuffer— compares current to previous, writes only changed cellswith-scissor— clips all render operations to a rectangle- Component
rendermethods produce render commands, not direct backend calls diff-outputframework for minimum-escape optimization- ~250 lines
v0.14.0: Mouse Improvements
DONE. Enhance mouse support with drag-to-select and link clicking.
- Text selection via mouse drag (highlight region between drag start/end)
- Click on OSC 8 link: extract URL, open via xdg-open
- Copy-to-clipboard via xclip/wl-copy/pbcopy
- ~80 lines
v0.15.0: Bug fixes, demo rewrite, verification, tangle tooling
DONE. Demo rewrite with interactive tabs, critical bug fixes, and quality-of-life infrastructure.
- Demo (demo.lisp): full rewrite with Console, Components, Layout, Events tabs — tab navigation, scrollbox with hot-reload, layout visualization with live row/column swapping, event logging panel
- Demo uses backend-size instead of hardcoded 80x24
- Box title rendering: modern and simple backends now render titles with title and title-align parameters
- Cursor rendering: text-input cursor renders as solid block at cursor position
- Arrow key fix: demo arrow keys on Widgets tab no longer steal focus from tab bar
- read-raw-byte buffer fix: sb-sys:with-pinned-objects + vector-sap for proper sb-posix:read buffer (SBCL type error with plain arrays)
- EOF detection: read-raw-byte returns (values nil :eof) on stdin EOF, not nil — prevents 100% CPU busy-spin on pipes
- Escape key: 50ms timeout in read-escape-sequence to disambiguate lone Escape from escape-prefixed sequences
- confirm-dialog: fix option plist comparison (was comparing objects, not keys)
- mouse-event: button slot type changed from keyword to (or keyword null)
- tangle tooling: replace Emacs org-babel-tangle with pure-Python script (scripts/tangle.py, later moved to Hermes skill)
- Verification: verify-api.py (API smoke tests), verify-demo-pty.py (PTY-based demo verification — 17 checks)
- tangle.py fix: write-once-then-append logic (was always-appending, triplicating files)
- Org/Lisp sync: verified — 483+57+17 checks pass on fresh tangle
- Project restructure: move backend/ and layout/ into src/
- .gitignore for compiled fasl files
- ~500 lines of changes across the codebase
| - Version: v1.0.0 (current) |
Known gaps from earlier phases:
- (none — all protocol spec items implemented)
v1.0.0: Release
DONE. All phases integrated and tested. Applications can build rich terminal UIs from the component library without writing custom escape sequences.
Checklist:
- README.org with overview, architecture, component table, quick start
- demo.lisp — working interactive example
- Full test suite: 454 checks, 100% passing across 14 suites
- ASDF system with test-op
- LICENSE file (GPL 3.0)
- Literate org files for all modules
- Terminal capability detection (v0.12.0)
- Rendering pipeline (v0.13.0)
- Mouse improvements (v0.14.0)
- Org/Lisp sync verified (first tangle produces no regressions)
- Suspend/resume-backend protocol methods (ARCHITECTURE.org spec)
- Slot modes (defslot :mode parameter)
v1.1.0: SGR Mouse Event Parsing
DONE. read-event now decodes SGR extended mouse sequences
(ESC[<Cb;Cx;CyM/m) into structured mouse-event structs, where previously
they fell through as :unknown key events and printed as control characters.
What was added:
%read-digits— reads multi-digit numeric parameters from raw terminal bytes, handling arbitrary-length values (e.g. coordinates > 99)%parse-sgr-mouse— full SGR mouse decoder: button code → keyword (:left,:middle,:right,:scroll-up,:scroll-down,:drag), press/release detection, 1-based → 0-based coordinate conversionparse-csi-sequencedetects the~<~marker byte (0x3C) and delegates to%parse-sgr-mouseinstead of treating the sequence as keyboard input
The mouse enable/disable sequences were already sent by
initialize-backend~/~shutdown-backend (lines 126-128, 139-141 of
modern.lisp). The parsing gap was the only missing piece.
Test coverage: 461 unit tests + 32 integration tests, all at 100%.
Org source: org/text-input.org (tangled to src/components/input.lisp).
Feature Reference
| Phase | Component | Lines | Release | Status |
|---|---|---|---|---|
| 0 | Backend protocol (simple + modern) | ~180 | v0.0.1 | DONE |
| - | Layout engine (pure CL flexbox) | ~190 | - | DONE |
| 1 | Renderables (Box, Text) + dirty | ~300 | v0.2.0 | DONE |
| 2 | Theme engine (tokens, presets) | ~120 | v0.4.0 | DONE |
| 3 | TextInput + Textarea + keybindings | ~500 | v0.5.0 | DONE |
| 4 | ScrollBox + TabBar | ~200 | v0.6.0 | DONE |
| 5 | Select (dropdown + fuzzy filter) | ~150 | v0.7.0 | DONE |
| 6 | Markdown + Code + Diff | ~400 | v0.8.0 | DONE |
| 7 | Dialog system + Toast | ~220 | v0.9.0 | DONE |
| 8 | Mouse support | ~80 | v0.10.0 | DONE |
| 9 | Plugin / slot system | ~50 | v0.11.0 | DONE |
| 10 | Terminal capability detection | ~100 | v0.12.0 | DONE |
| 11 | Rendering pipeline (framebuffer diff) | ~250 | v0.13.0 | DONE |
| 12 | Mouse improvements (selection, links) | ~80 | v0.14.0 | DONE |
| 13 | Bug fixes, demo rewrite, verification | ~500 | v0.15.0 | DONE |
| Total | ~5760 |