artisanal_widgets 0.4.2
artisanal_widgets: ^0.4.2 copied to clipboard
Widget system for composable TUI components, built on top of the artisanal terminal toolkit.
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):
