NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev · #3565 most downloaded on pub.dev
Elm-style terminal UI framework for Dart — Model–Update–View, async commands, 27+ components, Lipgloss-inspired styling, spring animation, and gum-style helpers.
Last release 28 days ago
10 Sep 2026
Ships fairly regularly
a new release about every 3 weeks
Nearly every release is documented
notes for 7 of 7 stable releases
Nothing withdrawn
no release was ever pulled
6 months old
7 releases · first in 2026
One column per month.
📦 Published to pub.dev: dart_tui 2.1.0
📦 Published to pub.dev: dart_tui 2.1.0
Performance and terminal-lifecycle follow-up to 2.0.0. No breaking API changes.
TextAreaModel logical lines across cursor-only copies.textWidth for ASCII-only strings.make bench-hotpath compares naive Dart with dart_tui; make bench-startup-pty measures first-visible startup.\x1b[?1049l on shutdown when alt screen was never entered.📦 Published to pub.dev: dart_tui 2.0.0
📦 Published to pub.dev: dart_tui 2.0.0
This major release hardens the terminal runtime, ports the selected Bubble Tea
capabilities through P9, and removes the temporary compatibility APIs so the
package has one clear model, styling, and program-configuration path.
TeaModel alias. Extend or implement ModelLegacyKeyMsg alias. Use KeyPressMsg for pressKeyMsg base type.TuiStyle helpers. Use immutable Styleconst Style(isBold: true).render(text).ProgramOptions and the parallelprogramOptions / programSettings parameters. Program, forms, prompts,List<ProgramOption> named options. UsewithAltScreen(), withTickInterval(...), withHideCursor(...), andwithLogFile(File(...)) for the former struct fields.Program.kill() wake idle programs and restore terminal state.CellRenderer, mademeta, lints, and test constraints to their current resolvable📦 Published to pub.dev: dart_tui 1.4.0
📦 Published to pub.dev: dart_tui 1.4.0
A composable, immutable, key-based forms subsystem — the equivalent of Charm's huh.
Form (forms/form.dart) — an immutable TeaModel you can embed (exposes submitted/cancelled/values), or run one-shot with await form.run() (returns FormValues?, null if cancelled). Groups of fields render one page at a time as a wizard, with a Group N/M indicator.Field factory: input, password, text (multiline), file, select / selectOf<T>, multiSelect / multiSelectOf<T>, confirm, note. Text-ish fields reuse TextInputModel/TextAreaModel/FilePickerModel.FormValues.get<T>(key); hidden fields are excluded from the result.validate with inline errors; blocks advancing and submit.hidden: (FormValues) => bool; titles and select options can be computed from other fields' values (titleFor, optionsFor), recomputed on every change with selection clamping.Tab/Enter advance (Enter inserts a newline in multiline text; Tab/Ctrl+D advance there), Shift+Tab back, Esc/Ctrl+C cancel.FormStyles — Catppuccin Mocha defaults.form.dart example + VHS tape.TextInputModel / TextAreaModel: typing a space inserted the literal string "space" (and F-keys inserted "f1", etc.) because the default branch inserted the keystroke name rather than the character. Now only unmodified printable runes are inserted, using the actual rune text — so text fields (and form inputs) accept spaces correctly.website/ (excluded from the published package via .pubignore).📦 Published to pub.dev: dart_tui 1.3.0
📦 Published to pub.dev: dart_tui 1.3.0
Spring (bubbles/spring.dart) — harmonica-style damped-harmonic-oscillator for smooth eased motion of any scalar (progress, scroll, cursor). Pure math, no terminal I/O: Spring(fps:, frequency:, damping:).update(pos, vel, target) plus fpsToDelta().FileLog (log.dart) — append timestamped diagnostics to a file. Because a running Program owns stdout, print() corrupts the UI; write here and tail -f instead. FileLog.none() discards.gum.dart) — filter() (interactive fuzzy picker), spin() (run a spinner while awaiting a Future, returns its result), and pager() (scrollable viewer). Each takes a programOptions list for headless testing.ListModel runtime mutation API — withItems, appendItem, insertItem, removeItemAt, setItemAt, select, and selectedIndex; all clamp the cursor.TextInputModel and TextAreaModel — ctrl+a/ctrl+e (home/end), ctrl+b/ctrl+f (char left/right), alt+←/alt+→ (word motion), and ctrl+w / alt+backspace (delete previous word).Five new runnable examples (each with a VHS tape): spring, gum (filter/spin/pager), list_mutation, readline, and file_log.
Hot-path allocation and CPU reductions on the per-frame render and per-keystroke paths (all behaviour-preserving, covered by regression tests):
stripAnsi fast-path (skip the regex when there is no escape) plus a single-grapheme width helper — the width/wrap/truncate/pad path no longer runs a regex per grapheme.ViewportModel reuses its wrapped lines across scrolling instead of re-wrapping every frame.AnsiRenderer and CellRenderer skip the grid rebuild / diff walk on identical frames; the redundant per-frame list copy is gone.ListModel.filteredItems is memoised; TreeModel reuses its flattened node list on cursor-only changes.Stopwatch.promptSelect / promptConfirm / promptInput set finished on Esc/Ctrl+C but never quit, and raw mode delivers Ctrl+C as a byte (not SIGINT) — so cancelling hung forever. They now quit cleanly and return null. The three prompt helpers also gained a programOptions parameter for headless/scripted use.lib/ line coverage raised from ~67% to 92%.make coverage [FLOOR=90] target and a CI coverage gate (Linux/macOS) that fails below the floor, honouring // coverage:ignore-* markers for genuinely-untestable code (Win32 FFI, raw-mode TTY, OS signals).feat: support CJK characters and multi-byte UTF-8 input by @MarshJiang in #1
Full Changelog: 1.0.0...1.2.0
CursorModel (bubbles/cursor.dart): in-line blinking cursor widget with three display modes — CursorMode.block (█), CursorMode.underline (_), and CursorMode.bar (|). Toggles visibility on every TickMsg when blink: true. Useful for building custom text editors, prompts, or any UI that needs a visible insertion point independent of the real terminal cursor. Supports focus()/blur() to pause blinking, and withMode()/withBlink() builders.
MultiSelectModel (bubbles/multi_select.dart): scrollable checkbox list supporting multiple concurrent selections. Navigate with ↑↓ / jk, toggle with Space or x, select/deselect all with a, confirm with Enter. Features:
wrap: bool — cursor wraps at list boundariesheight — viewport limitingshowStatusBar — "N/Total selected" footerselectedValues getter — returns custom value or falls back to labelMultiSelectStyles for full per-element theming (Catppuccin Mocha defaults)ProgramOption functionsSeven new fluent option functions complement the existing ProgramOptions struct:
withAltScreen() // enter alt-screen buffer at startup
withHideCursor([bool hide = true]) // hide/show terminal cursor at startup
withTickInterval(Duration interval) // emit TickMsg at a fixed interval
withMouseCellMotion() // enable button-event mouse tracking
withMouseAllMotion() // enable all-motion mouse tracking
withReportFocus() // emit FocusMsg / BlurMsg on window focus
withWindowSize(int width, int height) // inject fixed dimensions (useful in tests)
These compose with ProgramOptions and take precedence over it; defaultMouseMode and defaultReportFocus act as floor values so per-View overrides still work.
Border.normal — ASCII-art border (+, -, |) for environments without Unicode box-drawing support.Border now has showTop, showRight, showBottom, showLeft (all default true). Use Style.withBorderSides({top, right, bottom, left}) or the pre-built helpers Border.topOnly, Border.bottomOnly, Border.sidesOnly to draw partial borders.Border.copyWith() — produce modified Border instances without recreating all fields.tabWidth: int field on Style (default 4) — \t characters are expanded to spaces before rendering. Use Style.withTabWidth(n) fluent builder.marginBackground: RgbColor? field on Style — fills the margin area with a solid ANSI background colour. Use Style.withMarginBackground(color) fluent builder.getWidth(String) (public) — visible terminal column width after stripping ANSI and counting double-wide characters.getHeight(String) (public) — number of newline-delimited lines.truncate(String, int) (public) — drop trailing visible columns to fit maxWidth; ANSI-safe.truncateLeft(String, int) (public) — drop leading visible columns; ANSI codes in the kept portion are preserved.ListModel — added pgup / ctrl+b, pgdown / ctrl+f, home / g, end / G key bindings; viewOffsetY field for click-to-select mouse handling; mouse wheel scrolling.SelectListModel — added wrap: bool (cursor wraps at list boundaries).TableModel — added viewOffsetY and mouse handling (wheel up/down, left-click to select with header offset).TreeModel — added viewOffsetY and mouse handling (wheel scroll, left-click to move cursor); fixed ' ' space key mapping to 'space' keystroke.| Example | What it shows |
|---|---|
cursor_model.dart |
CursorModel — all three blink modes side-by-side, toggle blink with b |
multi_select.dart |
MultiSelectModel — toggle, select-all, confirm, display result |
10 new test files, 160+ new test cases:
| File | Coverage |
|---|---|
spinner_test.dart |
SpinnerModel state transitions |
select_list_test.dart |
SelectListModel navigation, wrap, view |
progress_test.dart |
ProgressModel rendering and clamping |
help_test.dart |
HelpModel expand/collapse, KeyMap |
paginator_test.dart |
PaginatorModel navigation, bounds, view |
cursor_model_test.dart |
CursorModel blink, focus, modes, view |
multi_select_test.dart |
MultiSelectModel toggle, select-all, wrap, view |
program_options_test.dart |
Integration: each ProgramOption emits correct ANSI sequences |
input_decoder_test.dart |
TerminalInputDecoder — 48 edge-case / fuzz-style tests |
style_properties_test.dart |
Property-based invariants for getWidth, getHeight, truncate, Style.render, joinH/V, stripAnsi |
Full Changelog : 1.0.0...1.0.0+1
Full Changelog: 1.0.0...1.0.0+1
unawaited cancel could leave stdin holding the event loop open, requiring a manual Ctrl-C to regain the shell prompt.0x0a (LF / \n) now correctly maps to KeyCode.enter, fixing silent key drops on Linux/WSL terminals that send LF instead of CR for Enter.CSI ?2026$y and OSC 11 are sent after the first rendered frame so startup is not delayed.make format (dart format check), make test, make analyze, make run/run-fast/kernels/bench/gifs/new-example.strict_raw_type, unused_local_variable, prefer_const_constructors, library_private_types_in_public_api, and avoid_relative_lib_imports warnings.Full Changelog : https://github.com/codenameakshay/dart_tui/commits/1.0.0
Full Changelog: https://github.com/codenameakshay/dart_tui/commits/1.0.0
characters package) — only changed cells emit ANSI sequences, eliminating flicker.?2026h/l wrapping for flicker-free frames on terminals that support it.$EDITOR) with full terminal hand-off and optional exit-code callback.charLimit, ctrl+k/ctrl+u line-kill, cursor navigation.atBottom/scrollPercent, keyboard navigation.start()/stop()/reset() builders, TickMsg routing by id.Style with width/height constraints, Align/AlignVertical, inline mode, AdaptiveColor, Border variants; joinHorizontal(), joinVertical(), place() layout helpers.suggestions (tab-completion) on TextInputModel.TextInputModel.tickWithId(Duration, Object): tick Cmd with routing ID for composable timers.batch() / sequence(): concurrent vs. ordered command scheduling.MouseMode.cellMotion / allMotion; MouseClickMsg, MouseMotionMsg, MouseWheelMsg.View.cursor with CursorShape (block / underline / bar) and blink flag.View.reportFocus, FocusMsg, BlurMsg.View.windowTitle OSC sequence.0x0a (LF / \n) was silently decoded as ctrl+j and dropped by all components. It is now correctly mapped to KeyCode.enter, matching Linux/WSL terminal behaviour.runCmd is now fire-and-forget so command results arrive as the next queued message without blocking key event processing.CSI ?2026$y (synchronized-updates query) and OSC 11 (background-color query) are sent after the first rendered frame, so the initial visible output is not delayed.tool/build.sh --kernel compiles examples to .dill kernel snapshots (~550 ms startup vs ~1 050 ms JIT source on WSL2).test, analyze, run, run-fast, kernels, bench, gifs, new-example, and more._output.writeln() + await _output.flush() in the finally block so ANSI reset sequences (show cursor, exit alt-screen) are flushed to the terminal before the shell regains control.unawaited cancel could leave stdin holding the event loop open, requiring a manual Ctrl-C to regain the shell prompt.Batch, Sequence, Quit, Println, WithInput, etc.) — use canonical lowercase equivalents.request* commands now emit real terminal protocol queries; decoder responses feed back as typed Msg values.ListModel renamed to SelectListModel.Your coding agent can read these notes before it upgrades. Set up the MCP server →