v0.12.0: Terminal capability detection, GPL 3.0 license, roadmap rewrite
LICENSE: - Added GNU General Public License v3.0 - Updated README.org to reflect GPL 3.0 ROADMAP: - Complete rewrite to reflect actual project state - Removed croatoan/ncurses/Yoga FFI references - Marked all 11 existing versions DONE - Added v0.12.0-0.14.0 for new features (detection, pipeline, mouse) DETECTION (v0.12.0): - detect-backend: auto-detect modern vs simple backend - detect-backend-by-env: check COLORTERM env var - detect-backend-by-tty: check interactive-stream-p - detect-backend-by-da1: query terminal via ESC[c (best-effort) - *detected-backend* cache for zero-cost subsequent calls - Added detection.lisp to ASDF and package exports - Added 2 new tests (360 total, all passing) - demo.lisp updated to use detect-backend ORG BACKPORT (pre-existing fixes synced): - dialog.org: render-dialog/render-toast fixes, class initforms - scrollbox-tabbar.org: background-element -> bright-black, remove duplicate render - select.org: remove duplicate render export - text-input.org: remove duplicate %split-string, undo overflow fix - layout-engine.org: quoted-literal -> list constructors, normalize-box rewrite - mouse.org: add missing exports, fix test
This commit is contained in:
671
docs/ROADMAP.org
671
docs/ROADMAP.org
@@ -5,598 +5,177 @@
|
||||
* 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.
|
||||
the components from prior phases.
|
||||
|
||||
** v0.0.1: Foundation — Backend Protocol
|
||||
** v0.0.1: 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.
|
||||
DONE. Two backends implementing a common protocol:
|
||||
|
||||
*** TODO Backend protocol definition
|
||||
:PROPERTIES:
|
||||
:ID: id-v000-protocol
|
||||
:CREATED: [2026-05-10 Sat]
|
||||
:END:
|
||||
- =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
|
||||
|
||||
- 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
|
||||
~180 lines total. Dependencies: None (pure CL, no FFI).
|
||||
|
||||
*** Backend protocol generic functions:
|
||||
- =initialize-backend=, =shutdown-backend=, =backend-size=, =backend-write=, =backend-clear=
|
||||
- =draw-rect=, =draw-text=, =draw-border=, =draw-ellipsis=, =draw-link=
|
||||
- =cursor-move=, =cursor-hide=, =cursor-show=, =cursor-style=
|
||||
- =begin-sync=, =end-sync= (DECICM)
|
||||
- =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:
|
||||
** Layout Engine (pure CL)
|
||||
|
||||
- =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
|
||||
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.
|
||||
|
||||
*** TODO Modern backend
|
||||
:PROPERTIES:
|
||||
:ID: id-v000-modern
|
||||
:CREATED: [2026-05-10 Sat]
|
||||
:END:
|
||||
~190 lines. Macros: =vbox=, =hbox=, =spacer=.
|
||||
|
||||
- =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
|
||||
** v0.2.0: Box, Text, Span, Dirty Tracking
|
||||
|
||||
*** TODO Terminal capability detection
|
||||
:PROPERTIES:
|
||||
:ID: id-v000-detection
|
||||
:CREATED: [2026-05-10 Sat]
|
||||
:END:
|
||||
DONE. The first two renderable types. Box draws borders and backgrounds.
|
||||
Text renders strings with color, word-wrap, and inline style spans.
|
||||
|
||||
- =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-tty.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-tty/themes/<name>.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~.
|
||||
- =Box= with border styles (:single, :double, :rounded), title, background
|
||||
- =Text= with word-wrap (:none, :word), fg/bg colors
|
||||
- =Span= — inline text segment with attributes (:bold, :italic, etc.)
|
||||
- =Dirty-mixin= — marks components and ancestors for re-render
|
||||
- =Theme= — semantic color tokens, presets (default, nord, catppuccin, etc.)
|
||||
- =render= generic function dispatched on component type
|
||||
|
||||
** 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.
|
||||
DONE. Text input widgets with readline-style keybindings.
|
||||
|
||||
*** 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).
|
||||
- =TextInput= — single-line input with cursor, placeholder, max-length, on-submit
|
||||
- =Textarea= — multi-line input with undo/redo (100-deep stack), cursor nav,
|
||||
selection, on-submit
|
||||
- =Keymap= — layered keybinding system with =defkeymap= macro
|
||||
- Event handling: key-event, mouse-event structs, raw-byte reader
|
||||
|
||||
** v0.6.0: ScrollBox + TabBar
|
||||
|
||||
Container components. ScrollBox handles content larger than the viewport.
|
||||
TabBar handles horizontal tab navigation.
|
||||
DONE. Container components.
|
||||
|
||||
*** 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).
|
||||
- =ScrollBox= — scrollable viewport with vertical/horizontal scrollbars,
|
||||
scroll-by, clamp, sticky-scroll mode
|
||||
- =TabBar= — horizontal tab navigation with next/prev, active tab tracking
|
||||
|
||||
** 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).
|
||||
DONE. A selection list component with keyboard navigation, category headers,
|
||||
and fuzzy text matching.
|
||||
|
||||
** v0.8.0: Markdown + Code + Diff Rendering
|
||||
|
||||
Content rendering components. Markdown for agent responses. Code for syntax
|
||||
highlighting. Diff for file changes.
|
||||
DONE. Content rendering for agent responses and file diffs.
|
||||
|
||||
*** 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).
|
||||
- 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
|
||||
|
||||
Modal overlays and transient notifications.
|
||||
DONE. 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).
|
||||
- =Dialog= — centered modal with backdrop dimming, size variants
|
||||
- =push-dialog= / =pop-dialog= — stack-based dialog management
|
||||
- =alert-dialog=, =confirm-dialog=, =select-dialog=, =prompt-dialog=
|
||||
- =Toast= — transient notification with variants (:info/:success/:warning/:error),
|
||||
auto-dismiss, top-right positioning
|
||||
|
||||
** v0.10.0: Mouse Support
|
||||
|
||||
Mouse event propagation through the component tree.
|
||||
DONE (minimal). Mouse event handling via mixin class.
|
||||
|
||||
*** 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).
|
||||
- =mouse-mixin= — event handler slots (:on-mouse-down/up/move/scroll)
|
||||
- =handle-mouse-event= — dispatch to component handlers
|
||||
- =hit-test= — find deepest component at (x, y)
|
||||
- =selection= struct and =copy-to-clipboard=
|
||||
|
||||
** v0.11.0: Plugin / Slot System
|
||||
|
||||
Extensible named slots. Applications and plugins register content into named
|
||||
slots. The component tree renders whatever is registered.
|
||||
DONE. Extensible named slots for registering content into extensible positions.
|
||||
|
||||
*** TODO Slot system
|
||||
:PROPERTIES:
|
||||
:ID: id-v110-slots
|
||||
:CREATED: [2026-05-10 Sat]
|
||||
:END:
|
||||
- =defslot=, =slot-render=, =clear-slot=, =list-slots=
|
||||
- Slot modes planned but not implemented
|
||||
|
||||
- ~(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~
|
||||
** v0.12.0: Terminal Capability Detection
|
||||
|
||||
TODO. Auto-detect terminal capabilities at startup and return the
|
||||
appropriate backend.
|
||||
|
||||
- Check if stdout is a TTY (if not -> simple-backend)
|
||||
- =detect-backend= -> returns =modern-backend= or =simple-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 =COLORTERM= env var for truecolor support
|
||||
- Cache detection result for subsequent instant calls
|
||||
- Add =detect-backend= to backend package API
|
||||
- ~100 lines
|
||||
|
||||
~100 lines total. Dependencies: Phase 2 (renderables + layout).
|
||||
** v0.13.0: Rendering Pipeline
|
||||
|
||||
* v1.0.0: Complete Framework
|
||||
TODO. A pure CL rendering pipeline — framebuffer diffing for incremental
|
||||
output, scissor clipping, and render-command dispatching.
|
||||
|
||||
All 11 phases integrated and tested. Applications can build rich terminal UIs
|
||||
from the component library without writing custom ncurses code.
|
||||
- =*framebuffer*= — 2D array of (char, fg, bg, attrs) tuples
|
||||
- =flush-framebuffer= — compares current to previous, writes only changed cells
|
||||
- =with-scissor= — clips all render operations to a rectangle
|
||||
- Component =render= methods produce render commands, not direct backend calls
|
||||
- =diff-output= framework for minimum-escape optimization
|
||||
- ~250 lines
|
||||
|
||||
** DONE Documentation
|
||||
- README.org with overview, architecture, component table, quick start
|
||||
- demo.lisp — working example exercising multiple components
|
||||
- run-all-tests.lisp — single-script test runner
|
||||
- Full test suite: ~280 checks, 100% passing across all 9 suites
|
||||
** v0.14.0: Mouse Improvements
|
||||
|
||||
* Neurosymbolic Phase Reference
|
||||
TODO. Enhance mouse support with drag-to-select and link clicking.
|
||||
|
||||
| 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 | DONE |
|
||||
|-------+------------------------------------+--------+---------|
|
||||
| Total | | ~3060 | |
|
||||
- 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
|
||||
|
||||
** v1.0.0: Release
|
||||
|
||||
All phases integrated and tested. Applications can build rich terminal UIs
|
||||
from the component library without writing custom escape sequences.
|
||||
|
||||
Checklist:
|
||||
- [X] README.org with overview, architecture, component table, quick start
|
||||
- [X] demo.lisp — working interactive example
|
||||
- [X] Full test suite: 358 checks, 100% passing across 11 suites
|
||||
- [X] ASDF system with test-op
|
||||
- [X] LICENSE file (GPL 3.0)
|
||||
- [X] 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)
|
||||
|
||||
** 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 | TODO |
|
||||
| 11 | Rendering pipeline (framebuffer diff) | ~250 | v0.13.0 | TODO |
|
||||
| 12 | Mouse improvements (selection, links) | ~80 | v0.14.0 | TODO |
|
||||
|-------+----------------------------------------+--------+---------|--------|
|
||||
| | Total | ~2800 | | |
|
||||
|
||||
207
docs/plans/2026-05-11-terminal-detection.md
Normal file
207
docs/plans/2026-05-11-terminal-detection.md
Normal file
@@ -0,0 +1,207 @@
|
||||
# Terminal Capability Detection — Implementation Plan
|
||||
|
||||
> **For Hermes:** Implement this plan task-by-task using subagent-driven-development.
|
||||
|
||||
**Goal:** Auto-detect terminal capabilities at startup so users don't have to pick `modern-backend` vs `simple-backend` manually.
|
||||
|
||||
**Architecture:** Pure CL terminal probing via escape sequence queries and environment variables. No external dependencies. Detection happens once at startup and returns a backend instance.
|
||||
|
||||
**Tech Stack:** SBCL, raw escape sequences, `sb-unix:isatty`, environment variable reads.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Create detection.org literate source
|
||||
|
||||
**Objective:** Write the org file with prose, contract, and tangle blocks for the detection module. No code generation yet — this is the design document.
|
||||
|
||||
**Files:**
|
||||
- Create: `org/detection.org`
|
||||
|
||||
**Content structure:**
|
||||
|
||||
```
|
||||
#+TITLE: Terminal Capability Detection (v0.12.0)
|
||||
|
||||
* Overview
|
||||
- Why detection matters
|
||||
- Strategy: TTY check → COLORTERM → DA1 query → DA3 query
|
||||
|
||||
** Contract
|
||||
- detect-backend () → modern-backend or simple-backend
|
||||
- detect-backend-by-env () → :modern, :simple, or nil
|
||||
- query-terminal-feature (query-string timeout) → string or nil
|
||||
|
||||
** Plan (this document — tasks for implementation)
|
||||
|
||||
** Tests
|
||||
- #+BEGIN_SRC lisp :tangle ../backend/tests.lisp
|
||||
- detection-returns-backend-instance
|
||||
- detection-returns-modern-on-colorterm
|
||||
- detection-returns-simple-on-pipe
|
||||
- detection-caches-result
|
||||
(these are additions to the existing backend/tests.lisp)
|
||||
|
||||
** Implementation
|
||||
- Package (adds to cl-tty.backend)
|
||||
- Environment probe (COLORTERM)
|
||||
- TTY probe (sb-unix:isatty)
|
||||
- DA1 probe (terminal queries)
|
||||
- detect-backend (orchestrator)
|
||||
- Cache (defvar *detected-backend*)
|
||||
```
|
||||
|
||||
**Step 1: Write the org file at `org/detection.org`** with the sections above, full prose, and empty code blocks.
|
||||
|
||||
**Step 2: Review** — verify structure matches existing .org files in the project.
|
||||
|
||||
**Step 3: Commit**
|
||||
```bash
|
||||
git add org/detection.org
|
||||
git commit -m "docs: add detection module design and plan"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Add detection functions to backend/classes.lisp
|
||||
|
||||
**Objective:** Implement the environment and TTY probe functions.
|
||||
|
||||
**Files:**
|
||||
- Modify: `backend/classes.lisp` (add methods to existing backend classes)
|
||||
|
||||
**Code to add:**
|
||||
|
||||
```lisp
|
||||
;;; ─── Detection ──────────────────────────────────────────────────────────────
|
||||
|
||||
(defvar *detected-backend* nil
|
||||
"Cached backend instance from detect-backend.")
|
||||
|
||||
(defun detect-backend-by-env ()
|
||||
"Check COLORTERM environment variable for modern terminal support."
|
||||
(let ((colorterm (sb-ext:posix-getenv "COLORTERM")))
|
||||
(when (and colorterm
|
||||
(or (search "truecolor" colorterm :test #'char-equal)
|
||||
(search "24bit" colorterm :test #'char-equal)))
|
||||
:modern)))
|
||||
|
||||
(defun detect-backend-by-tty ()
|
||||
"Check if stdout is a real terminal (not a pipe)."
|
||||
(sb-unix:isatty sb-sys:*stdout*))
|
||||
|
||||
(defun detect-backend ()
|
||||
"Auto-detect the appropriate backend for the current terminal.
|
||||
Returns a backend instance."
|
||||
(or *detected-backend*
|
||||
(setf *detected-backend*
|
||||
(if (and (detect-backend-by-tty)
|
||||
(or (eql (detect-backend-by-env) :modern)
|
||||
t)) ;; TODO: add DA1/DA3 probe here
|
||||
(make-modern-backend)
|
||||
(make-simple-backend)))))
|
||||
```
|
||||
|
||||
**Test additions to `backend/tests.lisp`:**
|
||||
|
||||
```lisp
|
||||
(def-test detection-returns-backend-instance ()
|
||||
(let ((be (cl-tty.backend:detect-backend)))
|
||||
(is-true (typep be 'cl-tty.backend:backend))))
|
||||
|
||||
(def-test detection-caches-result ()
|
||||
(let ((*detected-backend* nil))
|
||||
(cl-tty.backend:detect-backend)
|
||||
(is-true (not (null cl-tty.backend::*detected-backend*)))))
|
||||
```
|
||||
|
||||
**Follow TDD:**
|
||||
1. Write failing tests in `src/components/box-tests.lisp` (or wherever backend tests live — actually in `backend/tests.lisp`)
|
||||
2. Run tests to verify failure
|
||||
3. Write implementation code in `backend/classes.lisp`
|
||||
4. Run tests to verify pass
|
||||
5. Commit
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Add DA1/DA3 terminal query probe
|
||||
|
||||
**Objective:** Send escape sequence queries to the terminal and parse responses to detect modern features (Kitty keyboard, DECICM sync).
|
||||
|
||||
**Files:**
|
||||
- Modify: `backend/classes.lisp`
|
||||
|
||||
**Implementation:**
|
||||
|
||||
```lisp
|
||||
(defun query-terminal (query timeout-sec)
|
||||
"Send a query string to the terminal and return the response.
|
||||
Returns nil if no response within TIMEOUT-SEC seconds."
|
||||
(let ((response (make-array 0 :element-type 'character :fill-pointer 0 :adjustable t)))
|
||||
(format t "~A" query)
|
||||
(force-output)
|
||||
(sleep timeout-sec)
|
||||
(loop while (listen)
|
||||
do (vector-push-extend (read-char-no-hang) response))
|
||||
(when (plusp (length response))
|
||||
response)))
|
||||
|
||||
(defun detect-backend-by-da1 ()
|
||||
"Send DA1 (Device Attributes) query and parse response for modern features."
|
||||
(let ((response (query-terminal (format nil "~C[c" #\Esc) 0.1)))
|
||||
(when response
|
||||
;; Check for specific feature codes in response
|
||||
(search "?62" response)))) ;; kitty terminal indicator
|
||||
|
||||
(defun detect-backend ()
|
||||
"Auto-detect the appropriate backend for the current terminal."
|
||||
(or *detected-backend*
|
||||
(setf *detected-backend*
|
||||
(if (and (detect-backend-by-tty)
|
||||
(or (eql (detect-backend-by-env) :modern)
|
||||
(detect-backend-by-da1)))
|
||||
(make-modern-backend)
|
||||
(make-simple-backend)))))
|
||||
```
|
||||
|
||||
**Note:** DA1 queries are best-effort — many terminals don't respond or respond asynchronously. The env-var check is more reliable. DA1 is a safety net for terminals that set COLORTERM but don't respond to queries, and vice versa.
|
||||
|
||||
**Test for DA1 is hard to automate** (requires a real terminal). Add a manual test note.
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Wire into ASDF and run full test suite
|
||||
|
||||
**Files:**
|
||||
- Modify: `cl-tty.asd` (add detection.lisp if created as separate file, or verify existing)
|
||||
- Run: `run-all-tests.lisp`
|
||||
|
||||
**Steps:**
|
||||
1. Ensure `cl-tty.asd` includes the detection code (if in `backend/classes.lisp` it's already loaded)
|
||||
2. Run full test suite: `sbcl --script run-all-tests.lisp`
|
||||
3. Verify all 358+ tests pass (add 2 new detection tests → 360)
|
||||
4. Commit
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Update demo.lisp to use detection
|
||||
|
||||
**Objective:** Make `demo.lisp` use `detect-backend` instead of hardcoded `make-modern-backend`.
|
||||
|
||||
**Files:**
|
||||
- Modify: `demo.lisp`
|
||||
|
||||
**Change:** Replace `(make-modern-backend)` with `(detect-backend)`.
|
||||
|
||||
**Verification:** `sbcl --script demo.lisp` should work in a terminal.
|
||||
|
||||
---
|
||||
|
||||
### Task 6: Tangle org → lisp and verify no regressions
|
||||
|
||||
**Files:** All
|
||||
|
||||
**Steps:**
|
||||
1. Tangle all org files: `for f in org/*.org; do emacs --batch ...; done`
|
||||
2. Run full test suite
|
||||
3. Verify 0 regressions
|
||||
4. Commit final
|
||||
Reference in New Issue
Block a user