Artisanal
About this project:
This library is a faithful port of Charm's TUI libraries (Lip Gloss, Bubble Tea, Bubbles) to Dart. We aim to port as much functionality as possible. In some scenarios, things may not work as expected—please report any issues you discover so we can adjust where necessary.
Many of the included examples were generated and may contain issues or not reflect the latest API. If you find a broken or outdated example, please let us know!
A full-stack terminal toolkit for Dart, inspired by popular Go terminal libraries: Lip Gloss (styling), Bubble Tea (TUI framework), and Bubbles (reusable widgets).
Build everything from rich command-line tools to complex interactive TUI applications with a consistent, idiomatic Dart API.
Features
| Feature | Description |
|---|---|
| CLI I/O | High-level Console helpers for status lines, tables, tasks, prompts, and styled output |
| Styling | Fluent, immutable Style API with colors, borders, padding, margins, and themes |
| TUI Runtime | Elm Architecture (Model/Msg/Cmd) with a full-featured Program event loop |
| Bubbles | 20+ reusable widgets: inputs, lists, tables, spinners, progress bars, file pickers, etc. |
| Ultraviolet (UV) | High-performance cell-buffer renderer with diff-based updates and graphics support |
| Terminal + Renderer | Unified terminal abstraction, ANSI helpers, and renderer backends |
| Markdown | ANSI Markdown renderer plus Glamour high-fidelity output |
| Charting | Sparklines, line/ribbon charts, histograms, heatmaps, and pie charts |
Installation
dependencies:
artisanal: ^0.2.0
Note: This package uses workspace resolution. Use a path or git reference in standalone projects.
If you are building a widget app and do not need the rest of the CLI/style/runtime
toolkit from this package, prefer depending on artisanal_widgets directly.
Use package:artisanal/... as the umbrella convenience surface when you want
the broader stack from a single package.
🖼️ Screenshots



Library Exports
| Import | Purpose |
|---|---|
package:artisanal/artisanal.dart |
Full CLI kit (Console, Style, Terminal, Layout) |
package:artisanal/args.dart |
Command runner utilities (CommandRunner, Command) |
package:artisanal/style.dart |
Styling, Layout, Colors, Borders, Themes |
package:artisanal/runtime.dart |
Focused TEA runtime surface (Model, Msg, Cmd, Program, StringTerminal, runtime messages) |
package:artisanal/hosts.dart |
Terminal backends, bridges, browser/socket hosts |
package:artisanal/plugins.dart |
Stable remote-surface plugin protocol, transport, sessions, and host surface state |
package:artisanal/tui.dart |
TUI runtime: Model, Msg, Cmd, Program |
package:artisanal/bubbles.dart |
Reusable interactive widgets |
package:artisanal/terminal.dart |
Terminal abstraction, ANSI codes, Keys |
package:artisanal/app.dart |
Stable widget app shells, runners, and hosted wrappers |
package:artisanal/editors.dart |
Stable widget text inputs and editors |
package:artisanal/selection.dart |
Stable widget text selection |
package:artisanal/testing.dart |
Stable widget testing helpers |
package:artisanal/widgets.dart |
Stable re-export of the widget framework, including shortcut and zone-hit message support |
package:ultraviolet/ultraviolet.dart |
Low-level cell-buffer renderer |
package:artisanal/uv.dart |
Compatibility re-export for UV (package:ultraviolet/ultraviolet.dart) |
package:artisanal/markdown.dart |
Markdown to ANSI renderer |
package:artisanal/glamour.dart |
High-fidelity Markdown renderer |
package:artisanal/charting.dart |
Charting primitives |
For widget work, the package:artisanal/... widget entrypoints are convenience
re-exports of the primary package:artisanal_widgets/... libraries. Prefer the
direct artisanal_widgets imports when that is the only package your app
needs.
Documentation
See the in-repo docs for full coverage:
docs/DOCS_INDEX.md
Quick Start: CLI Output
import 'package:artisanal/artisanal.dart';
Future<void> main() async {
final io = Console();
io.title('My App');
io.section('Setup');
io.info('Checking configuration...');
await io.task('Running migrations', run: () async {
await Future.delayed(const Duration(milliseconds: 200));
return TaskResult.success;
});
io.table(
headers: ['ID', 'Name', 'Status'],
rows: [
[1, 'users', io.style.success('DONE')],
[2, 'posts', io.style.warning('PENDING')],
],
);
final proceed = io.confirm('Continue?', defaultValue: true);
if (!proceed) return;
io.success('All good.');
}

Quick Start: Styling (Lip Gloss)
import 'package:artisanal/style.dart';
final style = Style()
.bold()
.foreground(Colors.purple)
.padding(1, 2)
.border(Border.rounded);
print(style.render('Hello, Artisanal!'));

Style Capabilities
- Text effects:
bold(),italic(),underline(),strikethrough(),dim(),inverse(),blink() - Colors: ANSI 16, ANSI 256, TrueColor (RGB),
AdaptiveColor(light/dark aware) - Spacing:
padding(),margin() - Borders:
rounded,thick,double,hidden, custom - Alignment:
align(),alignVertical() - Dimensions:
width(),height(),maxWidth(),maxHeight() - Themes:
ThemePalettewith presets (dark, light, ocean, nord, dracula, monokai, solarized)
Quick Start: TUI (Elm Architecture)
import 'package:artisanal/runtime.dart';
class CounterModel implements Model {
final int count;
const CounterModel([this.count = 0]);
@override
Cmd? init() => null;
@override
(Model, Cmd?) update(Msg msg) {
return switch (msg) {
KeyMsg(key: Key(type: KeyType.up)) => (CounterModel(count + 1), null),
KeyMsg(key: Key(type: KeyType.down)) => (CounterModel(count - 1), null),
KeyMsg(key: Key(type: KeyType.runes, runes: [0x71])) => (this, Cmd.quit()),
_ => (this, null),
};
}
@override
String view() => 'Count: \$count\n\nUse ↑/↓ to change, q to quit';
}
Future<void> main() async {
await runProgram(CounterModel());
}
Replay + Trace Debugging
The TUI runtime supports deterministic replay (ProgramReplay) and built-in
file tracing (TuiTrace) for debugging and profiling.
Enable tracing for any TUI app:
ARTISANAL_TUI_TRACE=1 ARTISANAL_TUI_TRACE_CAPTURE=1 \
ARTISANAL_TUI_TRACE_PATH=traces/my-run.log \
dart run your_app.dart
Structured app/domain events can be emitted via TuiTrace.event(...) and are
preserved in replay conversion when they use stable typed type names.
Full replay and tracing documentation: docs/TUI.md.
Bubbles (Reusable Widgets)
| Widget | Description |
|---|---|
TextInputModel |
Single-line text input |
TextAreaModel |
Multi-line text editing |
ListModel |
Filterable list selection |
TableModel |
Interactive tables |
ViewportModel |
Scrollable content pane |
ProgressModel |
Progress bars with ETA |
SpinnerModel |
Animated loading spinners |
FilePickerModel |
File/directory browser |
AnticipateModel |
Autocomplete with suggestions |
WizardModel |
Multi-step form wizard |
SelectModel<T> |
Single-choice selection prompt |
MultiSelectModel<T> |
Multiple-choice selection |
PasswordModel |
Masked password input |
TimerModel |
Countdown timer |
StopwatchModel |
Elapsed time tracking |
PaginatorModel |
Pagination controls |
HelpModel |
Key binding help views |
Command Runner
Build CLI tools with styled help and nested commands:
import 'package:artisanal/args.dart';
class HelloCommand extends Command {
@override
String get name => 'hello';
@override
String get description => 'Say hello';
@override
void run() {
io.success('Hello, world!');
}
}
void main(List<String> args) async {
final runner = CommandRunner('my-cli', 'A great CLI');
runner.addCommand(HelloCommand());
await runner.run(args);
}

Ultraviolet Renderer
High-performance rendering with diff-based updates for flicker-free TUI applications:
await runProgram(
MyModel(),
options: const ProgramOptions(
useUltravioletRenderer: true,
useUltravioletInputDecoder: true,
altScreen: true,
mouse: true,
),
);
UV Features
- 2D cell buffer with styled cells
- Diff-based terminal updates (minimal redraws)
- Layer composition and hit-testing
- Mouse support and focus events
- Graphics: Kitty, Sixel, iTerm2, half-block drawing
Console Methods
| Category | Methods |
|---|---|
| Output | writeln(), write(), title(), section() |
| Messages | line(), info(), comment(), question(), warn(), error(), note(), caution(), alert(), verbose(), debug() |
| Layout | table(), tree(), listing(), twoColumnDetail(), text() |
| Interactive | ask(), confirm(), choice(), secret(), selectChoice(), multiSelectChoice(), menu(), search() |
| Progress | task(), spin(), progress(), progressIterate() |
Examples
See the example/ directory for comprehensive demos:
main.dart– Full feature showcasefluent_style_example.dart– Style API patternsspinner_demo.dart– Various spinner typeslipgloss_table.dart– Styled tableslog_viewer_demo.dart– Monitoring dashboardcommand_center_demo.dart– Multi-panel layouts- UV-specific demos now live in
pkgs/ultraviolet/example/
Libraries
- app
- Stable widget app-shell and runner API for composable terminal UIs.
- charting
- Charting primitives for terminal-native visualizations.
- editors
- Stable text input and editor API for composable terminal UIs.
- glamour
- Glamour Markdown Rendering for Dart.
- hosts
- Stable backend and host entrypoint for interactive terminal apps.
- liquid
- Liquid tag template adapters for Artisanal.
- markdown
- Markdown to ANSI terminal rendering.
- physics
- Physics helpers (Forge2D adapters) for Artisanal.
- plugins
- Stable protocol entrypoint for out-of-process Artisanal plugins.
- runtime
- Stable core runtime entrypoint for interactive terminal applications.
- scoring
- Bayesian match scoring for command palette filtering.
- selection
- Stable text-selection widget API for composable terminal UIs.
- testing
- Stable widget testing API for composable terminal UIs.
- widgets
- Stable high-level widget API for composable terminal UIs.
- args Core
- Command-line argument parsing and command runners for Artisanal.
- artisanal Core
- Artisanal: A polished CLI framework for Dart.
- style Style
- Fluent styling system for terminal text (Lip Gloss for Dart).
- terminal Terminal
- Unified terminal module for artisanal.
- bubbles TUI
- Reusable interactive components for Artisanal TUI.
- tui TUI
- Interactive TUI framework (Bubble Tea for Dart).
- uv Ultraviolet
- Ultraviolet (UV): High-performance terminal rendering and input.