LiveView Muscle Memory in a 60 FPS Terminal Renderer
Courgette’s mount/render/handle_event lifecycle is a strong reference for stateful, keyboard-first operator UIs in Joel’s terminal-heavy system tooling.
Courgette is a clean idea: take the Phoenix LiveView mental model (mount, render, handle_event) and run it straight in the terminal on Elixir + OTP. It’s still marked work-in-progress, but the shape is already compelling because the component model is familiar and the target is different.
The clever bit is the rendering stack. It goes from element tree → layout → paint buffer → diff → ANSI, with double buffering and frame batching around ~60 FPS. Layout is Flexbox via a port of taffy, and input support includes full keyboard, mouse, focus, paste, and resize events. So this isn’t just “draw text” — it’s a serious terminal UI runtime.
Useful for Joel’s world even without adopting Elixir. The patterns map to how we think about stateful control surfaces in joelclaw: event-driven updates, minimal redraw, and deterministic tests for behavior. The headless component test helpers and explicit focus management are the pieces worth stealing first, especially for terminal-facing ops surfaces tied to system events.
Key Ideas
- Courgette applies Phoenix LiveView-style lifecycle callbacks (
mount,render,handle_event) to terminal apps instead of browser apps. - Stateful components are process-backed with GenServer, while stateless function components stay lightweight and composable.
- Layout uses a terminal-adapted taffy engine, bringing real Flexbox semantics (
flex_grow,justify_content,align_items, etc.) to TUIs. - Rendering is incremental and double-buffered, emitting minimal ANSI escape sequences instead of repainting the full screen every tick.
- Input support is unusually complete for early-stage TUI frameworks, including Kitty keyboard protocol, mouse events, bracketed paste, focus tracking, and resize handling.
- Semantic theme tokens (
primary,danger,muted) are built in, which nudges apps toward consistent UI systems instead of hard-coded color soup. - Headless test helpers make full lifecycle tests deterministic, which matters for agent/operator interfaces where regressions hide in event handling and focus logic.