The fun, functional, and stateful way to build terminal apps in Common Lisp.
Based on The Elm Architecture (TEA). Built with CLOS.
Tuition is a Common Lisp library for building rich, responsive terminal user interfaces (TUIs). It blends the simplicity of TEA with the power of CLOS so you can model state clearly, react to events via generic methods, and render your UI as pure strings.
- Model — a CLOS object representing your application state
- Messages — CLOS classes describing events (keys, mouse, timers, custom)
- Update — generic methods that transform the model in response to messages
- View — a pure function that renders your model to a string
Tuition handles terminal concerns for you (raw mode, alternate screen, input decoding, cursor control) so you can stay focused on your application logic.
- Model-View-Update: Keep state in a CLOS object, react to messages, and render a pure string view.
- Messages: Typed events (keys, mouse, timers, custom) dispatched via generic methods for clarity and extensibility.
- Commands: Functions that return messages asynchronously, enabling timers, I/O, and background work without blocking.
- Program: A managed loop that sets up the terminal, processes messages, runs commands, and refreshes the screen.
- Pure Rendering: Rendering returns strings; styling, layout, borders, and reflow are composition-friendly utilities.
- Components: Reusable widgets (spinner, progress, list, table, text input) that manage their own state and view.
- Zones: Named regions to map mouse coordinates to stable identifiers for hover/click interactions.
- TEA-style architecture with CLOS: message-specialized tui:update-message
- Concurrent commands for non-blocking I/O and timers
- Keyboard and mouse input decoding (with modifiers and motion)
- Terminal control (raw mode, alternate screen, cursor, clear)
- Styling utilities (bold/italic/underline/colors, adaptive colors)
- Layout helpers (horizontal/vertical joins, placement and alignment)
- Borders (normal, rounded, thick, double, block, ASCII, markdown)
- Reflow helpers (wrapping, truncation, ellipsizing, indentation)
- Built-in components: spinner, progress bar, list, table, text input
- Zones for advanced mouse interactions (define and query named regions)
More GIFs/screenshots coming from the examples directory.
Bubble Tea-style programs are comprised of a model that describes your application state and three simple generic functions:
- tui:init — returns an initial command (or NIL)
- tui:update (or tui:update-message) — transforms state in response to messages
- tui:view — renders your model to a string
See the Quick Start above for a minimal example and the examples/ directory for complete, runnable programs.
Your application state lives in a CLOS object:
Events are message objects. Prefer specializing tui:update-message by message class for clarity and extensibility.
The view renders your model to a string. Tuition only needs a string; compose helpers however you like.
Commands are functions that return messages asynchronously.
tui:make-program accepts options that affect terminal behavior and input decoding:
- :alt-screen uses the terminal’s alternate screen buffer for clean entry/exit.
- :mouse controls mouse reporting granularity (:cell-motion, :all-motion, or NIL to disable).
Use tui:with-raw-terminal when you want terminal control outside the main program loop. It ensures proper cleanup and offers restarts to recover from setup issues.
This is useful for short, scripted interactions or when embedding Tuition rendering in an existing tool with its own control flow.
Restarts during setup:
- USE-NO-RAW — continue without entering raw mode
- RETRY — retry entering raw mode
- ABORT — abort setup and return
Use text styling helpers to apply ANSI attributes (bold, italic, underline) and colors in a composable way. Styles can be nested and combined, or prebuilt via a style object and applied to arbitrary strings. This keeps rendering pure while letting you centralize theme choices.
Layout helpers let you arrange blocks of text without calculating offsets by hand. Join content horizontally or vertically with alignment, then optionally position the result within a given width/height or the terminal’s current size. This encourages building UIs from simple, pure string blocks.
Borders provide quick framing for panels, tables, and dialogs. Pick from several predefined styles (rounded, thick, double, ASCII, markdown) to match the tone of your UI, or render with plain blocks for a minimal look.
Reflow functions help you shape text to fit the terminal: wrap long paragraphs, truncate with ellipses, or indent multi‑line blocks. They are designed to work well with styled strings so you can format first and style later (or vice‑versa) without miscounting visible width.
Keyboard events arrive as tui:key-msg values with helpers to inspect the key and modifier state. Mouse input (when enabled) provides cell-based coordinates, button information, and a hierarchical event system for press, release, drag, move, and scroll events.
Enable mouse reporting via :mouse in tui:make-program (see Program Options) and specialize tui:update-message on the specific mouse event types.
Tuition includes a few reusable building blocks. Each component exposes a small protocol of functions or methods for init, update, and view.
Use components when you want common interactions without re‑implementing state machines (for example, cursor management for text inputs or tick scheduling for spinners). Keep the component instance in your model, delegate messages to it in update, and render with the component’s view. For a deeper guide, see src/components/README.md.
Zones let you attribute portions of the rendered screen to symbolic identifiers and query hover/clicks reliably.
Use zones to implement clickable lists, buttons, and hover effects without manual hit‑testing. Mark regions during rendering and later resolve pointer coordinates back to a stable identifier.
- Create a zone-manager
- Mark regions while rendering
- Query with pointer coordinates to identify the active zone
See zone.lisp for the API and the examples/zones* demos for usage patterns.
The examples/ directory contains runnable demos showcasing Tuition features. See examples/README.md for a complete list and descriptions of all available examples.
By the way
- See the components in src/components/ for reusable widgets akin to Charmbracelet’s Bubbles.
- Styling and layout utilities are inspired by Lip Gloss.
- Markdown rendering is inspired by Glamour.
- Spring-based animation draws from Harmonica.
Tuition uses conditions for internal errors. You can customize reporting by rebinding tui:*error-handler*.
- bordeaux-threads — cross‑platform threading
- trivial-channels — thread‑safe message passing
MIT License — see LICENSE.
Tuition was creates by Anthony Green, with the assitance of various LLMs, and drawing inspiraton from the Charmbracelet ecosystem (Bubble Tea, Lip Gloss, Bubbles, Bubblezone, Harmonica).
.png)



![What is a graph database? [video]](https://news.najib.digital/site/assets/img/broken.gif)
