#+TITLE: cl-tui Roadmap #+STARTUP: content #+FILETAGS: :docs:roadmap:cl-tui: * The Roadmap Each phase is one minor release. Phases ship in dependency order — each depends on the components from prior phases. The backend protocol ships first because everything else builds on it. ** v0.0.1: Foundation — Backend Protocol The abstraction layer that makes everything portable. Two backends: =modern= (raw escape sequences, truecolor, modern features) and =simple= (ASCII art, universal compatibility). The component tree never touches the terminal directly — it dispatches through the protocol. *** TODO Backend protocol definition :PROPERTIES: :ID: id-v000-protocol :CREATED: [2026-05-10 Sat] :END: - Define =backend= abstract class with generic functions: - =initialize-backend=, =shutdown-backend=, =suspend-backend=, =resume-backend= - =backend-size=, =backend-write=, =backend-clear= - =begin-sync=, =end-sync= — DECICM synchronized updates - =draw-rect=, =draw-text=, =draw-border=, =draw-ellipsis=, =draw-link= - =cursor-move=, =cursor-hide=, =cursor-show=, =cursor-style= - =read-event=, =enable-mouse=, =enable-bracketed-paste=, =set-keyboard-mode= - =capable-p= — query feature support - Style plist structure: ~(:fg :error :bg :background-panel :bold t :italic nil ...)~ - ~100 lines *** TODO Simple backend :PROPERTIES: :ID: id-v000-simple :CREATED: [2026-05-10 Sat] :END: - =simple-backend= class — inherits =backend= - Borders: ASCII (~+-|~), no rounded corners - No color, no bold/italic — plain characters only - No OSC 8 links, no mouse, no synchronized updates - Works on any terminal, any SSH connection, piped output - ~100 lines *** TODO Modern backend :PROPERTIES: :ID: id-v000-modern :CREATED: [2026-05-10 Sat] :END: - =modern-backend= class — inherits =backend= - Truecolor 24-bit foreground/background - Rounded, single, double border styles via Unicode box-drawing - OSC 8 hyperlinks (clickable URLs) - DECICM synchronized updates (flicker-free) - SGR mouse tracking + kitty keyboard protocol - Bracketed paste detection - Bold, italic, underline, dim, blink, reverse, strikethrough - Cursor style: =:bar=, =:block=, =:underline=, with blink option - ~250 lines *** TODO Terminal capability detection :PROPERTIES: :ID: id-v000-detection :CREATED: [2026-05-10 Sat] :END: - =detect-backend= → returns =modern-backend= or =simple-backend= - Check if stdout is a TTY (if not → =simple-backend=) - Send DA1 (~ESC[c~) query, 100ms timeout - Send DA3 (~ESC[?c~) for kitty/wezterm identification - Query DECRPM (~ESC[?2026$p~) for DECICM sync support - Query truecolor support via =COLORTERM= env var + DA response - Cache detection result so subsequent calls are instant - ~100 lines ~550 lines total. Dependencies: None (pure CL, no FFI, no external libs). ** v0.0.2: Layout Engine the patch version (v0.X.Y). ** File Update Checklist When a version ships: 1. ~ROADMAP.org~ — mark item DONE, update LOGBOOK timestamp 2. ~README.org~ — update Status line 3. ~cl-tui.asd~ — update version string ** v0.1.0: Layout Engine Yoga Flexbox backend wrapped in a Common Lisp API. This is the foundation — every component after v0.1.0 uses the layout engine for positioning. *** TODO Yoga FFI binding :PROPERTIES: :ID: id-v010-yoga-ffi :CREATED: [2026-05-10 Sat] :END: - Load the Yoga shared library via CFFI - Define foreign types for ~YGNodeRef~, ~YGSize~, ~YGValue~, ~YGDirection~, ~YGFlexDirection~, ~YGAlign~, ~YGJustify~, ~YGWrap~, ~YGPositionType~, ~YGOverflow~, ~YGDisplay~, ~YGEdge~ - Bind core functions: ~node-new~, ~node-free~, ~node-style-set-*~, ~node-layout-get-*~, ~calculate-layout~ - ~100 lines CFFI *** TODO Layout primitives :PROPERTIES: :ID: id-v010-layout-primitives :CREATED: [2026-05-10 Sat] :END: - ~(make-layout-node)~ — wraps a ~YGNodeRef~ in a CLOS object - ~(layout-node-set-dimension node width height)~ — sets width/height in points - ~(layout-node-set-flex node &key grow shrink basis)~ — flex properties - ~(layout-node-set-direction node :row | :column | :row-reverse | :column-reverse)~ - ~(layout-node-set-wrap node :nowrap | :wrap | :wrap-reverse)~ - ~(layout-node-set-align node :flex-start | :center | :flex-end | :stretch | :baseline)~ - ~(layout-node-set-justify node :flex-start | :center | :flex-end | :space-between | :space-around | :space-evenly)~ - ~(layout-node-set-padding node &key top right bottom left x y)~ - ~(layout-node-set-margin node &key top right bottom left x y)~ - ~(layout-node-set-gap node &key row column)~ - ~(layout-node-set-position node :relative | :absolute &key top right bottom left)~ - ~(layout-node-set-border node width)~ - ~(layout-node-add-child parent child)~ — builds the tree - ~(layout-calculate root width height)~ — runs Yoga's calculateLayout, populates each node's computed x/y/w/h - ~200 lines CL *** TODO Layout composable API :PROPERTIES: :ID: id-v010-layout-composable :CREATED: [2026-05-10 Sat] :END: Convenience macros to build layout trees from CL function calls: - ~(vbox &key ... children ...)~ → column-direction container with children - ~(hbox &key ... children ...)~ → row-direction container with children - ~(overlay base child)~ — absolute-positioned overlay over a relative base - ~(spacer &key grow)~ — empty flex spacer - ~(layout-render root parent-window)~ — computes layout then walks the tree, calling each child's render function with its computed x, y, w, h - ~50 lines CL macros ~350 lines total. Dependencies: Yoga shared library, CFFI, croatoan. *** FiveAM tests - ~test-layout-basic~ — vbox with two children computes correct y positions - ~test-layout-hbox~ — hbox with two children computes correct x positions - ~test-layout-flex~ — flex-grow distributes space correctly - ~test-layout-absolute~ — absolute child positions relative to parent - ~test-layout-nested~ — nested vbox/hbox produces correct leaf positions ** v0.2.0: Renderables — Box and Text The first two renderable types that every application uses. A Box draws borders and backgrounds. A Text renders strings with color and style. Together they cover 80% of terminal UI. *** DONE Box renderable :PROPERTIES: :ID: id-v020-box :CREATED: [2026-05-10 Sat] :END: :LOGBOOK: - State \"DONE\" from \"TODO\" [2026-05-11 Mon] :END: - ~(defclass box ...)~ — renderable with background color, border, title - ~(render-box box window)~ — draws border (single/double/rounded), fills background, renders title - Border styles: ~:single~, ~:double~, ~:rounded~ - Title alignment: ~:left~, ~:center~, ~:right~ - ~:focusable~ property — renders focused border color when focused - ~100 lines *** DONE Text renderable :PROPERTIES: :ID: id-v020-text :CREATED: [2026-05-10 Sat] :END: :LOGBOOK: - State \"DONE\" from \"TODO\" [2026-05-11 Mon] :END: - ~(defclass text ...)~ — renderable with content, fg/bg color, wrap mode - ~(render-text text window)~ — renders text at the layout position, wraps at width - Word-wrap: ~:none~ (truncate) or ~:word~ (break at word boundaries) - CJK/emoji character-width aware wrapping - ~100 lines *** DONE Inline text styles :PROPERTIES: :ID: id-v020-inline :CREATED: [2026-05-10 Sat] :END: :LOGBOOK: - State \"DONE\" from \"TODO\" [2026-05-11 Mon] :END: - ~(defclass span ...)~ — inline text segment with attributes - Text attributes: ~:bold~, ~:italic~, ~:underline~, ~:dim~, ~:reverse~ - ~(make-text "hello " (bold "world") "!")~ — builds styled text from spans and strings - ~60 lines *** DONE Dirty tracking :PROPERTIES: :ID: id-v020-dirty :CREATED: [2026-05-10 Sat] :END: :LOGBOOK: - State \"DONE\" from \"TODO\" [2026-05-11 Mon] :END: - ~(mark-dirty component)~ — flags component and all ancestors - ~(dirty-p component)~ — returns T if the component needs re-rendering - ~(mark-clean component)~ — clears dirty flag after render - ~40 lines ~300 lines total. Dependencies: Phase 1 (layout engine). ** v0.3.0: Rendering Engine The pipeline that goes from component tree to terminal output. Handles dirty propagation, incremental rendering (only dirty branches), scissor clipping, and diff-based output. *** TODO Component tree → render commands :PROPERTIES: :ID: id-v030-pipeline :CREATED: [2026-05-10 Sat] :END: - ~(render-screen root screen)~ — entry point: computes layout, walks dirty branches, collects render commands - Render commands are lists: ~(:box x y w h bg border title)~, ~(:text x y str fg bg attrs)~ - Each component's ~render~ function returns a list of render commands - ~100 lines *** TODO Scissor clipping :PROPERTIES: :ID: id-v030-scissor :CREATED: [2026-05-10 Sat] :END: - ~(with-scissor (window x y w h) &body body)~ — clips all render operations to a rectangle - Pushes/pops scissor state so nested containers clip correctly - ~50 lines *** TODO Incremental diff output :PROPERTIES: :ID: id-v030-diff-output :CREATED: [2026-05-10 Sat] :END: - ~*framebuffer*~ — a 2D array of (char, fg-color, bg-color, attrs) tuples - ~(flush-framebuffer screen)~ — compares framebuffer to previous frame, writes only changed cells via croatoan - ~(clear-dirty screen)~ — clears all dirty flags after a successful flush - Croatoan compatibility: uses ~add-string~ for unchanged text, ~clear~ + ~add-string~ for changed regions - ~150 lines ~300 lines total. Dependencies: Phase 2 (renderables + dirty tracking). ** v0.4.0: Theme Engine Semantic color tokens, dark/light variants, hex → truecolor resolution, and built-in presets. Application code references semantic roles (~:error~, ~:accent~), never hex values. *** TODO Semantic color tokens :PROPERTIES: :ID: id-v040-tokens :CREATED: [2026-05-10 Sat] :END: - ~(defclass theme ...)~ — holds a mapping from semantic roles to hex colors - 30+ semantic roles: ~:primary~, ~:secondary~, ~:accent~, ~:error~, ~:warning~, ~:success~, ~:info~, ~:text~, ~:text-muted~, ~:background~, ~:background-panel~, ~:background-element~, ~:border~, ~:border-active~, ~:diff-added~, ~:diff-removed~, ~:diff-context~, ~:markdown-heading~, ~:markdown-code~, ~:markdown-link~, ~:markdown-quote~, ~:syntax-keyword~, ~:syntax-function~, ~:syntax-string~, ~:syntax-number~, ~:syntax-comment~, ~:syntax-type~ - ~120 lines *** TODO theme-color :PROPERTIES: :ID: id-v040-theme-color :CREATED: [2026-05-10 Sat] :END: - ~(theme-color theme role)~ → returns the croatoan color pair number for the role - ~(themed-add-string window x y str :color :error)~ — renders text with a theme semantic role - Color pair caching: resolve hex → croatoan ~init-color~ once per (fg, bg) pair, reuse - ~40 lines *** TODO Built-in presets :PROPERTIES: :ID: id-v040-presets :CREATED: [2026-05-10 Sat] :END: 8 presets: default (gold), professional, minimal, nord, tokyonight, catppuccin, monokai, gruvbox - Each preset is a plist: ~(:primary "#FFD700" :error "#BF616A" ...)~ - ~(theme-load :nord)~ — activates a preset, re-renders dirty - Load from ~/.config/cl-tui/themes/.lisp~ for custom themes - ~80 lines *** TODO Dark/light variants :PROPERTIES: :ID: id-v040-dark-light :CREATED: [2026-05-10 Sat] :END: - Each preset defines both ~:dark~ and ~:light~ variants - ~(theme-set-mode :dark | :light)~ — switches variant - Auto-detect: read terminal background color (croatoan's background), pick closest variant - ~50 lines ~290 lines total. Dependencies: Phase 2 (renderables), Croatoan's ~init-color~/~color-pair~. ** v0.5.0: Text Input + Keybinding System Text input widgets with readline/emacs keybindings. A layered keybinding system that routes keystrokes through global → local → input layers. *** TODO TextInput — single-line input :PROPERTIES: :ID: id-v050-textinput :CREATED: [2026-05-10 Sat] :END: - ~(defclass text-input ...)~ — single-line input with value, cursor, placeholder - ~(render-text-input input window)~ — renders text left-aligned, placeholder when empty, blinking cursor - Cursor movement: left/right, home, end - Insert/delete at cursor position - ~:on-submit~ callback — fires on Enter - ~:max-length~ property — prevents input exceeding limit - ~150 lines *** TODO Textarea — multi-line input :PROPERTIES: :ID: id-v050-textarea :CREATED: [2026-05-10 Sat] :END: - ~(defclass textarea ...)~ — multi-line input with value, cursor (row, column), selection - ~(render-textarea area window)~ — renders visible lines, cursor, selection highlight - Cursor: up/down, left/right, word-forward/backward, line/home/end, buffer/home/end - Selection: Shift + navigation extends selection - Undo/redo stack (configurable depth, default 100) - ~:on-submit~ callback — fires on Enter - ~200 lines *** TODO Keybinding system :PROPERTIES: :ID: id-v050-keybindings :CREATED: [2026-05-10 Sat] :END: - Layered keymaps: ~:global~ → ~:local~ → ~:input~ (input layer takes priority when text input is focused) - ~(defkeymap :global '((:ctrl+p . command-palette) (:ctrl+c,ctrl+d . quit)))~ - Key format: ~:ctrl+p~, ~:alt+f~, ~:shift+tab~, ~(:ctrl+c :ctrl+d)~ (chord) - Chord sequences: first key starts a timer, second key within timeout dispatches - ~:leader~ key (default ~Ctrl+X~) with configurable timeout - Key names normalized from croatoan's ~:code-key~ + ~:key-name~ output - ~150 lines ~500 lines total. Dependencies: Phase 3 (rendering engine), Phase 4 (theme). ** v0.6.0: ScrollBox + TabBar Container components. ScrollBox handles content larger than the viewport. TabBar handles horizontal tab navigation. *** TODO ScrollBox :PROPERTIES: :ID: id-v060-scrollbox :CREATED: [2026-05-10 Sat] :END: - ~(defclass scroll-box ...)~ — container with vertical/horizontal scroll - Viewport culling: only render children whose y position is within the visible range - Scroll offset: ~:scroll-y~, ~:scroll-x~ slots - ScrollBy: PageUp/PageDown (viewport height), Up/Down (1 line), Home/End (buffer start/end) - Scrollbars: vertical and horizontal (single-line, rendered with block characters) - Sticky scroll: when scrolled to bottom and new content arrives, auto-scroll to show it. When user scrolls up, stop auto-scrolling until they scroll back down. - ~200 lines *** TODO TabBar :PROPERTIES: :ID: id-v060-tabbar :CREATED: [2026-05-10 Sat] :END: - ~(defclass tab-bar ...)~ — horizontal row of tabs - ~(tab-bar-add tab-bar id title &optional content)~ - ~:active-tab~ slot — only renders content for the active tab - Tab rendering: highlighted active tab, dim inactive tabs - Left/Right or Ctrl+PageUp/PageDn to navigate tabs - ~100 lines ~300 lines total. Dependencies: Phase 3 (rendering engine), Phase 4 (theme). ** v0.7.0: Select — Dropdown + Fuzzy Filter A selection list component — the building block for command palettes, theme pickers, agent selectors, file pickers. *** TODO Select :PROPERTIES: :ID: id-v070-select :CREATED: [2026-05-10 Sat] :END: - ~(defclass select ...)~ — list of options with keyboard navigation - ~:options~ — list of plists: ~((:title "Nord" :value :nord :category "Themes") ...)~ - Categories: options can be grouped. Category headers rendered dim, non-selectable - Up/Down/Ctrl+P/Ctrl+N to navigate, Enter to select, Esc to dismiss - ~:on-select~ callback — fires on Enter - ~:filter~ property — when set, filters the option list. Options whose title contains the filter (case-insensitive) are shown. - Fuzzy filter: when ~:filter~ is non-nil and no exact matches, uses trigram-based fuzzy matching (3-character sliding window Jaccard similarity) - ~150 lines ~150 lines total. Dependencies: Phase 5 (keybindings), Phase 4 (theme). ** v0.8.0: Markdown + Code + Diff Rendering Content rendering components. Markdown for agent responses. Code for syntax highlighting. Diff for file changes. *** TODO Markdown :PROPERTIES: :ID: id-v080-markdown :CREATED: [2026-05-10 Sat] :END: - ~(defclass markdown ...)~ — renders markdown content as styled text - Heading levels 1-6: colored by theme (~:markdown-heading~) with level-based sizing - Bold, italic, inline code, strikethrough — rendered as croatoan text attributes - Code blocks: fenced (~```~) and indented. Background-colored, syntax-highlighted via regex - Links: OSC 8 hyperlinks (clickable in Kitty, WezTerm, iTerm2, Ghostty). Format: ~\x1b]8;;url\x1b\\...link text...\x1b]8;;\x1b\\~ - Blockquotes: colored left border (~:markdown-quote~), indented text - Tables: aligned column text, no borders. Column alignment from header separators - Lists: ordered and unordered, with indentation - All features degrade gracefully to plain text on terminals without attribute support - ~200 lines *** TODO Code :PROPERTIES: :ID: id-v080-code :CREATED: [2026-05-10 Sat] :END: - ~(defclass code ...)~ — renders syntax-highlighted code - ~:content~ — the code string - ~:language~ — language identifier for syntax rules - Line numbers (optional, via ~:line-numbers t~) - Regex-based highlighting (no Tree-sitter dependency): - Keywords: language-specific keyword lists - Strings: single and double quoted - Comments: line (~;//~, ~#~) and block (~/* */~) - Numbers: integer and float literals - Functions: word followed by ~(~ - Colors from theme: ~:syntax-keyword~, ~:syntax-function~, ~:syntax-string~, ~:syntax-number~, ~:syntax-comment~, ~:syntax-type~ - ~150 lines *** TODO Diff :PROPERTIES: :ID: id-v080-diff :CREATED: [2026-05-10 Sat] :END: - ~(defclass diff ...)~ — renders unified diff output - ~:content~ — diff text (standard unified diff format) - Added lines: ~+~ prefix, green background (~:diff-added~) - Removed lines: ~-~ prefix, red background (~:diff-removed~) - Context lines: ~ ~ prefix, neutral background (~:diff-context~) - Line numbers: optional, rendered in ~:diff-line-number~ color - ~50 lines ~400 lines total. Dependencies: Phase 4 (theme), Phase 2 (renderables). ** v0.9.0: Dialog System + Toast Modal overlays and transient notifications. *** TODO Dialog base :PROPERTIES: :ID: id-v090-dialog :CREATED: [2026-05-10 Sat] :END: - ~(defclass dialog ...)~ — absolute-positioned overlay with backdrop - Backdrop: semi-transparent (dimmed background color) - Centered panel with ~:background-panel~ color, border - ~:on-dismiss~ callback — fires on Esc or backdrop click - ~:size~ — ~:small~ (40 cols), ~:medium~ (60 cols), ~:large~ (88 cols). Height computed from content. - Stack-based: dialogs push/pop on a ~*dialog-stack*~ - Esc dismisses top dialog. Ctrl+C clears stack. - ~100 lines *** TODO Dialog sub-classes :PROPERTIES: :ID: id-v090-dialog-types :CREATED: [2026-05-10 Sat] :END: - ~alert-dialog~ — title + message + OK button - ~confirm-dialog~ — title + message + Yes/No/Cancel buttons - ~select-dialog~ — wraps a Select component in a modal. Title, searchable list, action buttons - ~prompt-dialog~ — wraps a TextInput in a modal. Title, input, OK/Cancel buttons - ~60 lines *** TODO Toast notifications :PROPERTIES: :ID: id-v090-toast :CREATED: [2026-05-10 Sat] :END: - ~(toast title &key variant duration)~ — shows a transient notification - Variants: ~:info~ (blue), ~:success~ (green), ~:warning~ (yellow), ~:error~ (red) — colored left border - ~:duration~ — auto-dismiss after N milliseconds (default 5000) - Position: top-right corner, max 60 cols wide - Multiple toasts stack vertically - ~60 lines ~220 lines total. Dependencies: Phase 3 (rendering engine), Phase 4 (theme), Phase 5 (TextInput), Phase 7 (Select). ** v0.10.0: Mouse Support Mouse event propagation through the component tree. *** TODO Mouse events :PROPERTIES: :ID: id-v100-mouse :CREATED: [2026-05-10 Sat] :END: - Enable croatoan mouse mode: ~(setf (mouse-enabled-p window) t)~ - Parse ncurses mouse codes: button (left/right/middle), state (press/release/drag), x, y - Ctrl/Shift/Meta modifiers from mouse event - ~:on-mouse-down~, ~:on-mouse-up~, ~:on-mouse-move~, ~:on-mouse-scroll~ callbacks on components - Hit-testing: walk the component tree from root, find the deepest component whose rect contains (x, y) - Event propagation: component consumes event by returning T from callback; otherwise bubbles to parent - Scroll wheel: mapped to PageUp/PageDown in ScrollBox - Click on OSC 8 link: extract URL, open via ~xdg-open~ - ~100 lines *** TODO Text selection + copy :PROPERTIES: :ID: id-v100-selection :CREATED: [2026-05-10 Sat] :END: - Mouse drag: highlight text between drag start and current position - ~(get-selection)~ — returns the selected text as a string - Copy: pipe selection to ~xclip~ / ~wl-copy~ / ~pbcopy~ - ~50 lines ~150 lines total. Dependencies: Phase 3 (rendering engine). ** v0.11.0: Plugin / Slot System Extensible named slots. Applications and plugins register content into named slots. The component tree renders whatever is registered. *** TODO Slot system :PROPERTIES: :ID: id-v110-slots :CREATED: [2026-05-10 Sat] :END: - ~(defslot :sidebar-title &key order render-fn)~ — registers a rendering function for a slot - ~(slot-render slot-name ...)~ — calls all registered render-fns for the slot in priority-ordered sequence - Slot modes: ~:stack~ (render all, default), ~:replace~ (last registered wins), ~:single-winner~ (first matching wins) - ~:order~ integer — sorting key for ~:stack~ mode (lower = renders first) - Built-in slot naming convention: component name, then sub-slot: ~sidebar-title~, ~sidebar-content~, ~home-logo~, ~home-prompt~ - ~100 lines ~100 lines total. Dependencies: Phase 2 (renderables + layout). * v1.0.0: Complete Framework All 11 phases integrated and tested. Applications can build rich terminal UIs from the component library without writing custom ncurses code. * Neurosymbolic Phase Reference | Phase | Component | Lines | Release | |-------+------------------------------------+--------+---------| | 1 | Layout engine (Yoga FFI + API) | ~350 | v0.1.0 | | 2 | Renderables (Box, Text) + dirty | ~300 | v0.2.0 | | 3 | Rendering engine (diff, scissor) | ~300 | v0.3.0 | | 4 | Theme engine (tokens, presets) | ~290 | v0.4.0 | | 5 | TextInput + Textarea + keybindings | ~500 | v0.5.0 | | 6 | ScrollBox + TabBar | ~300 | v0.6.0 | | 7 | Select (dropdown + fuzzy filter) | ~150 | v0.7.0 | | 8 | Markdown + Code + Diff | ~400 | v0.8.0 | | 9 | Dialog system + Toast | ~220 | v0.9.0 | | 10 | Mouse support + selection | ~150 | v0.10.0 | | 11 | Plugin / slot system | ~100 | v0.11.0 | |-------+------------------------------------+--------+---------| | Total | | ~3060 | |