Files
cl-tty/docs/ROADMAP.org
Hermes 811d51a4f2 Rename cl-tui -> cl-tty, v0.9.0: Dialog System + Toast
Rename: cl-tty avoids naming collision with Quicklisp's cl-tui (naryl/cl-tui,
a cl-charms-based ncurses library). Our project is pure escape-sequence CL.

v0.9.0 adds:
- Dialog base class: modal overlay with backdrop, centered panel, size
  variants (:small/:medium/:large), stack-based management
- Dialog subclasses: alert, confirm, select-dialog, prompt-dialog
- Toast notifications: transient, top-right corner, auto-dismiss,
  colored variants (info/success/warning/error)
- 78 tests total, 100% passing

ASDF: read-time package references (+fiveam:+) replaced with
find-symbol so .asd loads without FiveAM pre-loaded
2026-05-11 19:55:37 +00:00

597 lines
22 KiB
Org Mode

#+TITLE: cl-tty Roadmap
#+STARTUP: content
#+FILETAGS: :docs:roadmap:cl-tty:
* 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-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~.
** 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 | |