artisanal_widgets
Flutter-inspired widget framework for terminal UIs, built on top of
artisanal.
This is the package for widget-first apps. Widget APIs, runners, and test
helpers are owned here; the core artisanal package does not re-export them.
Table of Contents
- Installation
- Import
- Quick start
- Flutter-style component ports
- Program Instrumentation
- Tests
- Command execution note
Installation
dependencies:
artisanal_widgets: ^0.4.0
Import
import 'package:artisanal_widgets/app.dart';
import 'package:artisanal_widgets/widgets.dart';
Use the focused stable entrypoints when you need those modules:
package:artisanal_widgets/app.dartfor app shells, runners, reload helpers, and hosted wrapperspackage:artisanal_widgets/charting.dartfor chart widgetspackage:artisanal_widgets/editors.dartforTextField,TextArea,TextEditor,CodeEditor,MarkdownEditor, and the stableTextInputKeyMap/TextAreaKeyMapcustomization surfacepackage:artisanal_widgets/selection.dartforSelectableTextandSelectionAreapackage:artisanal_widgets/testing.dartforWidgetTester
The main package:artisanal_widgets/widgets.dart barrel also re-exports
KeyMap and KeyBinding, so component-level shortcut UIs such as HelpView
and zone-hit messages such as ZoneInBoundsMsg, so shortcut and pointer-aware
widgets do not need an extra package:artisanal/tui.dart import.
Keep package:artisanal_widgets/artisanal_widgets.dart only when you
explicitly want the broader experimental compatibility surface.
WidgetTester remains view-only by default. For an opt-in final production
cell frame, pass enableRenderer: true and
enableNativeFrameCapture: true; read the resulting
tester.latestNativeFrame. Native-frame recording copies cells and does not
include drawable payloads.
Both the local runner helpers and the hosted browser/socket helpers accept an
imageAutoMode override. Hosted browser/socket runners now default
Image(renderMode: auto) to session-driven capability detection, while
WidgetTester keeps the portable half-block fallback for deterministic tests.
Quick start
import 'package:artisanal_widgets/app.dart';
import 'package:artisanal_widgets/widgets.dart';
class HelloApp extends StatelessWidget {
HelloApp({super.key});
@override
Widget build(BuildContext context) {
final theme = ThemeScope.of(context);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Hello widgets', style: theme.titleLarge),
Text('Press q to quit', style: theme.bodyMedium),
],
);
}
}
void main() async {
await runWidgetApp(
ArtisanalApp(
title: 'Hello widgets',
home: HelloApp(),
),
);
}
runWidgetApp() defaults to MouseMode.allMotion, so hover-driven widgets
such as Tooltip, MouseRegion, and hover-aware scrollbars work without extra
setup. If you call runtime.runProgram() directly, set
mouseMode: runtime.MouseMode.allMotion for passive hover behavior. Setting
only mouse: true enables MouseMode.cellMotion instead.
Flutter-style component ports
- Chips:
Chip,ActionChip,ChoiceChip,FilterChip,InputChip - Menus:
DropdownButton,DropdownMenuItem,PopupMenuButton,PopupMenuItem,CheckedPopupMenuItem,PopupMenuDivider - Sliders:
Slider,RangeSlider,RangeValues - Indicators:
LinearProgressIndicator,CircularProgressIndicator - Data display:
DataTable.cellswith column spans and alignment,MonthlyCalendar, and terminal-cellShadowpresets - Charts:
SparklineChart,LineChart,BarChart,HeatmapChart,PieChart,RibbonChartwith optional in-chart legends
The example/widget_features app combines these components with a fixed
terminal viewport, UV subtree filters, and world-coordinate canvas shapes.
For a focused effects walkthrough, run
dart run example/uv_effects/main.dart; it compares an ordinary widget tree
with a CellFilter-processed copy and includes a composed filter stack.
For a production-style primary-screen example, run
dart run example/inline_build_monitor/main.dart. It keeps a responsive build
dashboard pinned at the bottom while staged command output streams into native
terminal scrollback. Press p to pause, r to rebuild, e to simulate a
failure, or q to quit.
The OpenCode CLI app is self-contained under
apps/opencode (including local data models and theme assets).

Program Instrumentation
The core TUI runtime (Program) supports general instrumentation and automation
for any app (not OpenCode-specific):
ProgramInterceptorfor message interception/timing hooks.ProgramReplayfor deterministic event playback.
import 'package:artisanal/tui.dart' as runtime;
import 'package:artisanal_widgets/app.dart';
final replay = runtime.ProgramReplay.script([
runtime.ProgramReplayStep(
after: Duration(milliseconds: 120),
msg: runtime.KeyMsg(
runtime.Key(runtime.KeyType.runes, runes: [0x61]),
),
),
runtime.ProgramReplayStep(
after: Duration(milliseconds: 16),
msg: runtime.QuitMsg(),
),
]);
await runtime.runProgram(
WidgetApp(MyApp()),
options: runtime.ProgramOptions(replay: replay),
);
See the package:artisanal/tui.dart API docs for full interceptor/replay
details.
Tests
Component tests are split by widget under
test/components/*_test.dart.
For reusable captures, artisanal_capture exposes
captureWidget(..., mode: WidgetCaptureMode.renderedFrame). Its default
WidgetCaptureMode.view is cheaper and captures the portable view text;
rendered-frame mode exercises the production UV renderer and requires native
frame capture. This distinction matters for styled-cell fidelity.
Useful commands:
dart test test/components
dart test
dart analyze
Command execution note
When combining commands that include runtime-managed commands (EveryCmd,
StreamCmd, or helpers like every(...)), use ParallelCmd so those commands
are started by Program.
Use Cmd.batch(...) for finite commands that only need execute().
Demo captures
Recordings of some of the more consequential examples, regenerated from the
VHS tapes in example/.vhs/ with
task widgets-demos. Every recorded example also has a page with a preview
and its full source on the documentation site.
Widget app shell (example/artisanal_app/main.dart):

Charting (example/charting/main.dart):

Git diff viewer (example/git-diff/main.dart):

Data table (example/data_table/main.dart):

Command palette (example/command_palette/main.dart):

Code editor (example/code-editor/main.dart):

Markdown editor (example/markdown-editor/main.dart):

Data visualization (example/dataviz/main.dart):

Debug console (example/debug_console/main.dart):

OpenCode chat UI (apps/opencode/bin/opencode.dart):

Buttons & badges (example/buttons/main.dart):

Form inputs (example/inputs/main.dart):

Text field (example/text-field/main.dart):

Text area (example/text-area/main.dart):

Tree view (example/tree_view/main.dart):

Tabs & breadcrumbs (example/tabs_nav/main.dart):

Text selection (example/selection/main.dart):

Progress & spinner (example/progress_spinner/main.dart):

Slider (example/slider/main.dart):

Scrolling (example/scroll/main.dart):

File picker (example/file_picker/main.dart):

Help view (example/help_view/main.dart):

Libraries
- charting Charts
- Stable chart widget entrypoint for terminal UIs.
- editors Editors
- Stable editor and text-input entrypoint for terminal widget apps.
- selection Selection
- Stable text selection widget entrypoint for terminal UIs.
- testing Testing
- Testing utilities for TUI widgets.
- app TUI
- Stable app-shell entrypoint for terminal widget apps.
- artisanal_widgets TUI
- Legacy broad widget entrypoint for composable TUI components.
- widgets TUI
- Stable high-level widget framework for building terminal UIs.