Artisanal

License: MIT Documentation Buy Me A Coffee

Build polished command-line tools and interactive terminal applications in Dart with one consistent toolkit.

Artisanal brings together high-level CLI output, Lip Gloss-inspired styling, a Bubble Tea-style runtime, reusable Bubbles, a widget framework, and the Ultraviolet cell-buffer renderer.

Table of contents

Overview

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

Add Artisanal to your project:

dart pub add artisanal

Or add it to pubspec.yaml:

dependencies:
  artisanal: ^0.6.0

For widget-only applications, depend on artisanal_widgets directly. Use artisanal when you want the broader CLI, style, runtime, and rendering stack.

Quick start

CLI output

Minimal example

import 'package:artisanal/artisanal.dart';

void main() {
  final io = Console();

  io.title('Minimal Artisanal CLI');
  io.info('Starting up...');
  io.success('Ready to ship.');
}

Run the included example from the repository root:

dart run pkgs/artisanal/example/minimal_cli.dart

Minimal Artisanal CLI

Complete CLI flow

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.');
}

CLI Output

Styling

import 'package:artisanal/style.dart';

final style = Style()
    .bold()
    .foreground(Colors.purple)
    .padding(1, 2)
    .border(Border.rounded);

print(style.render('Hello, Artisanal!'));

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: ThemePalette with presets (dark, light, ocean, nord, dracula, monokai, solarized)

Interactive TUI

import 'package:artisanal/tui.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());
}

Toolkit

Console helpers

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()

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);
}

Command Runner

Bubbles

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

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,
  ),
);

The runtime can record a final native cell frame for diagnostics and capture consumers. TerminalNativeFrame.toBuffer() rebuilds a detached UV Buffer; use a native frame rather than ProgramRenderSnapshot.toJson() when cell styles, links, and attributes must survive. Snapshot JSON is diagnostic text and is not a lossless capture format. Native frame reconstruction rejects drawable payloads, which are not represented by the frame metadata.

UV features

  • 2D cell buffer with styled cells
  • Diff-based terminal updates (minimal redraws)
  • Layer composition and hit-testing
  • Reusable color, CRT, scanline, distortion, and persistence effects through BufferRenderSink
  • Mouse support and focus events
  • Graphics: Kitty, Sixel, iTerm2, half-block drawing

Replay and 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.

See the TUI documentation for replay and tracing details.

Library entrypoints

Choose the smallest public library that covers your use case:

Import Purpose
package:artisanal/artisanal.dart Umbrella API for CLI output, styling, charting, Markdown, hosts, and common terminal types
package:artisanal/args.dart Command runner utilities (CommandRunner, Command)
package:artisanal/bubbles.dart Reusable TEA widgets
package:artisanal/catalog.dart Public metadata registry for Bubbles and display components
package:artisanal/style.dart Styles, colors, borders, layout, and themes
package:artisanal/tui.dart TEA runtime (Model, Msg, Cmd, Program) plus replay and tracing
package:artisanal/runtime.dart Platform-safe TEA runtime for reusable and browser-capable packages
package:artisanal/terminal.dart Terminal abstraction, ANSI helpers, keys, backends, and bridges
package:artisanal/editor_core.dart Low-level text document, editor state, and viewport primitives
package:artisanal/text_editing.dart Focused cursor, text input, text area, and editor-core surface
package:artisanal/markdown.dart Markdown-to-ANSI renderer and options
package:artisanal/git_diff.dart Git diff model, styles, and review data types
package:artisanal/charting.dart Terminal chart painters and data primitives
package:artisanal/layout.dart String layout, wrapping, and responsive utilities
package:artisanal/scoring.dart Bayesian matching and conformal ranking utilities
package:artisanal/glamour.dart High-fidelity Markdown rendering
package:artisanal/uv.dart Compatibility re-export of Ultraviolet types
package:artisanal/compat.dart Backward-compatible API shims

Widget-first applications depend on and import package:artisanal_widgets/... directly; widget APIs are not re-exported from this core package. Renderer-level applications can import package:ultraviolet/ultraviolet.dart directly.

See the example/ directory for comprehensive demos:

  • main.dart – Full feature showcase
  • minimal_cli.dart – Minimal CLI output example
  • widget_catalog.dart – Searchable component catalog and theme showcase
  • fluent_style_example.dart – Style API patterns
  • spinner_demo.dart – Various spinner types
  • lipgloss_table.dart – Styled tables
  • log_viewer_demo.dart – Monitoring dashboard
  • command_center_demo.dart – Multi-panel layouts
  • tui/examples/uv-effects/main.dart – Applying UV effects to a canvas buffer
  • Additional engine-specific demos live in pkgs/ultraviolet/example/

Commands in this gallery assume the repository root as the working directory.

CLI walkthroughs

The small CLI tapes focus on one interaction at a time and keep the matching command immediately beside its recording.

Tables (example/.vhs/cli_table.tape):

dart run pkgs/artisanal/example/main.dart ui:table --ansi

CLI table

Prompts (example/.vhs/cli_prompts.tape):

dart run pkgs/artisanal/example/main.dart ui:prompts --defaults --no-interaction --ansi

CLI prompts

Display components (example/.vhs/cli_components.tape):

dart run pkgs/artisanal/example/main.dart ui:components --ansi

CLI components

Progress (example/.vhs/cli_progress.tape):

dart run pkgs/artisanal/example/main.dart ui:progress --count 40 --ansi

CLI progress

Full demo captures

Recordings of the more consequential examples, regenerated from the VHS tapes in example/.vhs/ with task artisanal-demos:

Markdown renderer (example/markdown_demo.dart):

Markdown demo

Glamour themes (example/glamour_demo.dart):

Glamour demo

CLI runner showcase (example/main.dart):

Main demo

Lip Gloss TUI (example/lipgloss_tui.dart):

Lip Gloss TUI

Spinners (example/spinner_demo.dart):

Spinner demo

Log viewer (example/log_viewer_demo.dart):

Log viewer demo

Split dashboard (example/split_dashboard_demo.dart):

Split dashboard demo

Command center (example/command_center_demo.dart):

Command center demo

Data table (example/data_table_demo.dart):

Data table demo

Sequence diagram (example/sequence_diagram_demo.dart):

Sequence diagram demo

Styled tables (example/lipgloss_table.dart):

Lip Gloss tables

Styled lists (example/lipgloss_list.dart):

Lip Gloss lists

Styled trees (example/lipgloss_tree.dart):

Lip Gloss trees

Charting (example/charting_demo.dart):

Charting demo

Markdown showcase (example/markdown_showcase.dart):

Markdown showcase

Theme integration (example/theme_integration_demo.dart):

Theme integration demo

Compositor images (example/compositor_image_demo.dart):

Compositor image demo

Editor core (example/editor_core_demo.dart):

Editor core demo

Fluent style API (example/fluent_style_example.dart):

Fluent style demo

Kitty images (example/kitty_image_demo.dart):

Kitty image demo

Console tags (example/lipgloss_console_tags.dart):

Console tags demo

Lip Gloss layout (example/lipgloss_layout.dart):

Lip Gloss layout demo

Render recorder (example/render_recorder_demo.dart):

Render recorder demo

Scanner spinner (example/scanner_demo.dart):

Scanner demo

Inline animation (example/inline_animation_demo.dart):

Inline animation demo

DevTools counter (example/devtools_demo.dart):

DevTools demo

Multi search (example/multi_search_demo.dart):

Multi search demo

Artisanal Nexus (example/uv_tui_demo.dart):

Nexus demo

Static screenshots

Log viewer

Log Viewer

Console tags

Console Tags

Layout

Layout

Documentation and support

Artisanal is a Dart port of Charm's Lip Gloss, Bubble Tea, and Bubbles. The project aims for broad parity, but some behavior may still differ from the Go originals. Reports with small reproductions are especially helpful.

Libraries

catalog
Public registries for building Artisanal-powered catalogs and help UIs.
compat
Platform compatibility shims for dart:io / dart:isolate parity.
editor_core
Stable low-level text editing primitives for terminal editors.
glamour
Glamour Markdown Rendering for Dart.
web
Browser-only hosting APIs for Artisanal programs.

Charting

charting Charting
Terminal chart painters and chart data primitives.

Core

args Core
Command-line argument parsing and command runners for Artisanal.
artisanal Core
Artisanal: A polished CLI framework for Dart.

Layout

layout Layout
String layout, wrapping, and responsive layout utilities.

Markdown

markdown Markdown
Markdown-to-ANSI rendering for terminal applications.

Scoring

scoring Scoring
Bayesian matching and conformal ranking utilities.

Style

style Style
Fluent styling system for terminal text (Lip Gloss for Dart).

Terminal

terminal Terminal
Unified terminal module for artisanal.

TUI

bubbles TUI
Reusable interactive components for Artisanal TUI.
git_diff TUI
Git diff parsing, rendering, and review primitives.
runtime TUI
Platform-safe core runtime for terminal applications.
text_editing TUI
Text-input and editor primitives for terminal applications.
tui TUI
Interactive TUI framework (Bubble Tea for Dart).

Ultraviolet

uv Ultraviolet
Ultraviolet (UV): High-performance terminal rendering and input.