dart_tui
Elm-style terminal UI framework for Dart — Model–Update–View, async commands, 27+ components, Lipgloss-inspired styling, spring animation, and gum-style helpers.
2.0.0
2.9K downloads/mo
#4700 most downloaded on pub.dev
codenameakshay/dart_tui
What this package is like to depend on
Last release 16 days ago
08 Aug 2026
Release timing varies
gaps range from 2 weeks to 3 months
Nearly every release is documented
notes for 6 of 6 stable releases
Nothing withdrawn
no release was ever pulled
5 months old
6 releases · first in 2026
6 releases in the last 12 months
see the full history below
Release timeline
6 releases · Mar 2026 to Aug 2026Releases
latest 6-
2.0.008 Aug 2026Release notes
Open source →📦 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.Breaking changes
- Models: removed the
TeaModelalias. Extend or implementModel
directly. - Keys: removed the
LegacyKeyMsgalias. UseKeyPressMsgfor press
events or the sharedKeyMsgbase type. - Styles: removed the raw ANSI
TuiStylehelpers. Use immutableStyle
values such asconst Style(isBold: true).render(text). - Program configuration: removed
ProgramOptionsand the parallel
programOptions/programSettingsparameters.Program, forms, prompts,
and gum helpers now accept oneList<ProgramOption>namedoptions. Use
withAltScreen(),withTickInterval(...),withHideCursor(...), and
withLogFile(File(...))for the former struct fields.
Bubble Tea parity
- Added declarative terminal colors, native progress reporting, keyboard
options, and view-level mouse callbacks. - Added Kitty progressive-keyboard negotiation and modern key press, repeat,
and release events. - Added Unicode Core negotiation plus shared grapheme-aware terminal width
handling across input, styling, and rendering. - Expanded the viewport with gutters, per-line styles, search highlights,
filling, and horizontal navigation. - Added dynamically sized, visually wrapped textarea behavior.
- Added multi-stop, scaled, and callback-driven progress colors.
- Added rich underline styles, OSC 8 hyperlinks, and ANSI-state-safe layout
operations. - Added explicit dark/light component style factories and immutable
suggestion, cursor, and viewport introspection APIs.
Runtime correctness and security
- Made cell diffs width-aware for CJK, emoji, and other double-width
graphemes, while reducing cell parsing from quadratic to linear work. - Made declarative cursor position, shape, blink, and color reach the terminal.
- Correctly consume OSC, DCS, APC, PM, and SOS control strings without leaking
payload bytes into rendered cells. - Made
Program.kill()wake idle programs and restore terminal state. - Corrected Unicode capability-report semantics and sanitized window-title OSC
payloads. - Preserved cumulative SGR and OSC 8 hyperlink state in
CellRenderer, made
multi-grapheme input edits atomic, and restored viewport line styles around
highlighted spans.
Release quality and tooling
- Added real-PTY tests for kill, external cancellation, resize,
suspend/resume, and terminal restoration. - Centralized alternate-screen, cursor, focus, paste, and mouse transitions in
one terminal-mode state machine shared by both renderers. - Aligned local and CI analysis on one clean repository-wide boundary.
- Made plain Dart the default across Make targets, examples, VHS tapes, and
website source mirrors; FVM remains an optional override. - Refreshed
meta,lints, andtestconstraints to their current resolvable
releases. - Moved README previews to the hosted documentation assets and excluded local
GIF binaries from the pub archive.
Documentation and examples
- Upgraded the documentation site to patched Next.js 16.3.0 with dedicated
audit, typecheck, and production-build CI. - Refreshed the component gallery, guides, API snippets, and all 60
tape-backed example previews for the 2.0 API.
Release notes
Open source →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.
Breaking changes
- Models: removed the
TeaModelalias. Extend or implementModeldirectly. - Keys: removed the
LegacyKeyMsgalias. UseKeyPressMsgfor press events or the sharedKeyMsgbase type. - Styles: removed the raw ANSI
TuiStylehelpers. Use immutableStylevalues such asconst Style(isBold: true).render(text). - Program configuration: removed
ProgramOptionsand the parallelprogramOptions/programSettingsparameters.Program, forms, prompts, and gum helpers now accept oneList<ProgramOption>namedoptions. UsewithAltScreen(),withTickInterval(...),withHideCursor(...), andwithLogFile(File(...))for the former struct fields.
Bubble Tea parity
- Added declarative terminal colors, native progress reporting, keyboard options, and view-level mouse callbacks.
- Added Kitty progressive-keyboard negotiation and modern key press, repeat, and release events.
- Added Unicode Core negotiation plus shared grapheme-aware terminal width handling across input, styling, and rendering.
- Expanded the viewport with gutters, per-line styles, search highlights, filling, and horizontal navigation.
- Added dynamically sized, visually wrapped textarea behavior.
- Added multi-stop, scaled, and callback-driven progress colors.
- Added rich underline styles, OSC 8 hyperlinks, and ANSI-state-safe layout operations.
- Added explicit dark/light component style factories and immutable suggestion, cursor, and viewport introspection APIs.
Runtime correctness and security
- Made cell diffs width-aware for CJK, emoji, and other double-width graphemes, while reducing cell parsing from quadratic to linear work.
- Made declarative cursor position, shape, blink, and color reach the terminal.
- Correctly consume OSC, DCS, APC, PM, and SOS control strings without leaking payload bytes into rendered cells.
- Made
Program.kill()wake idle programs and restore terminal state. - Corrected Unicode capability-report semantics and sanitized window-title OSC payloads.
- Preserved cumulative SGR and OSC 8 hyperlink state in
CellRenderer, made multi-grapheme input edits atomic, and restored viewport line styles around highlighted spans.
Release quality and tooling
- Added real-PTY tests for kill, external cancellation, resize, suspend/resume, and terminal restoration.
- Centralized alternate-screen, cursor, focus, paste, and mouse transitions in one terminal-mode state machine shared by both renderers.
- Aligned local and CI analysis on one clean repository-wide boundary.
- Made plain Dart the default across Make targets, examples, VHS tapes, and website source mirrors; FVM remains an optional override.
- Refreshed
meta,lints, andtestconstraints to their current resolvable releases. - Moved README previews to the hosted documentation assets and excluded local GIF binaries from the pub archive.
Documentation and examples
- Upgraded the documentation site to patched Next.js 16.3.0 with dedicated audit, typecheck, and production-build CI.
- Refreshed the component gallery, guides, API snippets, and all 60 tape-backed example previews for the 2.0 API.
- Models: removed the
-
1.4.014 Jul 2026Release notes
Open source →📦 Published to pub.dev: dart_tui 1.4.0
Forms (huh-style)
A composable, immutable, key-based forms subsystem — the equivalent of Charm's huh.
Form(forms/form.dart) — an immutableTeaModelyou can embed (exposessubmitted/cancelled/values), or run one-shot withawait form.run()(returnsFormValues?,nullif cancelled). Groups of fields render one page at a time as a wizard, with aGroup N/Mindicator.- Field types via the
Fieldfactory:input,password,text(multiline),file,select/selectOf<T>,multiSelect/multiSelectOf<T>,confirm,note. Text-ish fields reuseTextInputModel/TextAreaModel/FilePickerModel. - Key-based values —
FormValues.get<T>(key); hidden fields are excluded from the result. - Validation — per-field
validatewith inline errors; blocks advancing and submit. - Dynamic / conditional fields — any field or group takes
hidden: (FormValues) => bool; titles and select options can be computed from other fields' values (titleFor,optionsFor), recomputed on every change with selection clamping. - Navigation —
Tab/Enteradvance (Enter inserts a newline in multilinetext;Tab/Ctrl+Dadvance there),Shift+Tabback,Esc/Ctrl+Ccancel. FormStyles— Catppuccin Mocha defaults.- New
form.dartexample + VHS tape.
Fixes
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.
Docs
- New documentation website — a searchable gallery of every component, block and guide with live GIF previews and copyable source. Lives in
website/(excluded from the published package via.pubignore).
Release notes
Open source →Forms (huh-style)
A composable, immutable, key-based forms subsystem — the equivalent of Charm's huh.
Form(forms/form.dart) — an immutableTeaModelyou can embed (exposessubmitted/cancelled/values), or run one-shot withawait form.run()(returnsFormValues?,nullif cancelled). Groups of fields render one page at a time as a wizard, with aGroup N/Mindicator.- Field types via the
Fieldfactory:input,password,text(multiline),file,select/selectOf<T>,multiSelect/multiSelectOf<T>,confirm,note. Text-ish fields reuseTextInputModel/TextAreaModel/FilePickerModel. - Key-based values —
FormValues.get<T>(key); hidden fields are excluded from the result. - Validation — per-field
validatewith inline errors; blocks advancing and submit. - Dynamic / conditional fields — any field or group takes
hidden: (FormValues) => bool; titles and select options can be computed from other fields' values (titleFor,optionsFor), recomputed on every change with selection clamping. - Navigation —
Tab/Enteradvance (Enter inserts a newline in multilinetext;Tab/Ctrl+Dadvance there),Shift+Tabback,Esc/Ctrl+Ccancel. FormStyles— Catppuccin Mocha defaults.- New
form.dartexample + VHS tape.
Fixes
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.
Docs
- New documentation website — a searchable gallery of every component, block and guide with live GIF previews and copyable source. Lives in
website/(excluded from the published package via.pubignore).
-
1.3.001 Jul 2026Release notes
Open source →📦 Published to pub.dev: dart_tui 1.3.0
New components & helpers
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)plusfpsToDelta().FileLog(log.dart) — append timestamped diagnostics to a file. Because a runningProgramowns stdout,print()corrupts the UI; write here andtail -finstead.FileLog.none()discards.- gum-style one-shot helpers (
gum.dart) —filter()(interactive fuzzy picker),spin()(run a spinner while awaiting aFuture, returns its result), andpager()(scrollable viewer). Each takes aprogramOptionslist for headless testing. ListModelruntime mutation API —withItems,appendItem,insertItem,removeItemAt,setItemAt,select, andselectedIndex; all clamp the cursor.- Readline / emacs editing keys for
TextInputModelandTextAreaModel—ctrl+a/ctrl+e(home/end),ctrl+b/ctrl+f(char left/right),alt+←/alt+→(word motion), andctrl+w/alt+backspace(delete previous word).
Examples
Five new runnable examples (each with a VHS tape):
spring,gum(filter/spin/pager),list_mutation,readline, andfile_log.Performance
Hot-path allocation and CPU reductions on the per-frame render and per-keystroke paths (all behaviour-preserving, covered by regression tests):
stripAnsifast-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.ViewportModelreuses its wrapped lines across scrolling instead of re-wrapping every frame.AnsiRendererandCellRendererskip the grid rebuild / diff walk on identical frames; the redundant per-frame list copy is gone.ListModel.filteredItemsis memoised;TreeModelreuses its flattened node list on cursor-only changes.- The render throttle uses a precomputed frame budget and a monotonic
Stopwatch.
Fixes
- Prompts no longer hang on cancel.
promptSelect/promptConfirm/promptInputsetfinishedon 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 returnnull. The three prompt helpers also gained aprogramOptionsparameter for headless/scripted use.
Quality
- Test suite grown from 423 to 560+ cases; measured
lib/line coverage raised from ~67% to 92%. - New
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).
Release notes
Open source →New components & helpers
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)plusfpsToDelta().FileLog(log.dart) — append timestamped diagnostics to a file. Because a runningProgramowns stdout,print()corrupts the UI; write here andtail -finstead.FileLog.none()discards.- gum-style one-shot helpers (
gum.dart) —filter()(interactive fuzzy picker),spin()(run a spinner while awaiting aFuture, returns its result), andpager()(scrollable viewer). Each takes aprogramOptionslist for headless testing. ListModelruntime mutation API —withItems,appendItem,insertItem,removeItemAt,setItemAt,select, andselectedIndex; all clamp the cursor.- Readline / emacs editing keys for
TextInputModelandTextAreaModel—ctrl+a/ctrl+e(home/end),ctrl+b/ctrl+f(char left/right),alt+←/alt+→(word motion), andctrl+w/alt+backspace(delete previous word).
Examples
Five new runnable examples (each with a VHS tape):
spring,gum(filter/spin/pager),list_mutation,readline, andfile_log.Performance
Hot-path allocation and CPU reductions on the per-frame render and per-keystroke paths (all behaviour-preserving, covered by regression tests):
stripAnsifast-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.ViewportModelreuses its wrapped lines across scrolling instead of re-wrapping every frame.AnsiRendererandCellRendererskip the grid rebuild / diff walk on identical frames; the redundant per-frame list copy is gone.ListModel.filteredItemsis memoised;TreeModelreuses its flattened node list on cursor-only changes.- The render throttle uses a precomputed frame budget and a monotonic
Stopwatch.
Fixes
- Prompts no longer hang on cancel.
promptSelect/promptConfirm/promptInputsetfinishedon 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 returnnull. The three prompt helpers also gained aprogramOptionsparameter for headless/scripted use.
Quality
- Test suite grown from 423 to 560+ cases; measured
lib/line coverage raised from ~67% to 92%. - New
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).
-
1.2.014 Apr 2026Release notes
Open source →What's Changed
- feat: support CJK characters and multi-byte UTF-8 input by @MarshJiang in #1
- feat: new features by @codenameakshay in #2
New Contributors
- @MarshJiang made their first contribution in #1
- @codenameakshay made their first contribution in #2
Full Changelog: 1.0.0...1.2.0
Release notes
Open source →New components
-
CursorModel(bubbles/cursor.dart): in-line blinking cursor widget with three display modes —CursorMode.block(█),CursorMode.underline(_), andCursorMode.bar(|). Toggles visibility on everyTickMsgwhenblink: true. Useful for building custom text editors, prompts, or any UI that needs a visible insertion point independent of the real terminal cursor. Supportsfocus()/blur()to pause blinking, andwithMode()/withBlink()builders. -
MultiSelectModel(bubbles/multi_select.dart): scrollable checkbox list supporting multiple concurrent selections. Navigate with↑↓ / jk, toggle withSpaceorx, select/deselect all witha, confirm withEnter. Features:wrap: bool— cursor wraps at list boundariesheight— viewport limitingshowStatusBar—"N/Total selected"footerselectedValuesgetter — returns customvalueor falls back tolabelMultiSelectStylesfor full per-element theming (Catppuccin Mocha defaults)
New
ProgramOptionfunctionsSeven new fluent option functions complement the existing
ProgramOptionsstruct: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
ProgramOptionsand take precedence over it;defaultMouseModeanddefaultReportFocusact as floor values so per-Viewoverrides still work.Style additions
Border.normal— ASCII-art border (+,-,|) for environments without Unicode box-drawing support.- Per-side border flags —
Bordernow hasshowTop,showRight,showBottom,showLeft(all defaulttrue). UseStyle.withBorderSides({top, right, bottom, left})or the pre-built helpersBorder.topOnly,Border.bottomOnly,Border.sidesOnlyto draw partial borders. Border.copyWith()— produce modifiedBorderinstances without recreating all fields.tabWidth: intfield onStyle(default4) —\tcharacters are expanded to spaces before rendering. UseStyle.withTabWidth(n)fluent builder.marginBackground: RgbColor?field onStyle— fills the margin area with a solid ANSI background colour. UseStyle.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 fitmaxWidth; ANSI-safe.truncateLeft(String, int)(public) — drop leading visible columns; ANSI codes in the kept portion are preserved.
Component navigation & mouse improvements
ListModel— addedpgup/ctrl+b,pgdown/ctrl+f,home/g,end/Gkey bindings;viewOffsetYfield for click-to-select mouse handling; mouse wheel scrolling.SelectListModel— addedwrap: bool(cursor wraps at list boundaries).TableModel— addedviewOffsetYand mouse handling (wheel up/down, left-click to select with header offset).TreeModel— addedviewOffsetYand mouse handling (wheel scroll, left-click to move cursor); fixed' 'space key mapping to'space'keystroke.
New examples
Example What it shows cursor_model.dartCursorModel— all three blink modes side-by-side, toggle blink withbmulti_select.dartMultiSelectModel— toggle, select-all, confirm, display resultTests
10 new test files, 160+ new test cases:
File Coverage spinner_test.dartSpinnerModel state transitions select_list_test.dartSelectListModel navigation, wrap, view progress_test.dartProgressModel rendering and clamping help_test.dartHelpModel expand/collapse, KeyMap paginator_test.dartPaginatorModel navigation, bounds, view cursor_model_test.dartCursorModel blink, focus, modes, view multi_select_test.dartMultiSelectModel toggle, select-all, wrap, view program_options_test.dartIntegration: each ProgramOption emits correct ANSI sequences input_decoder_test.dartTerminalInputDecoder — 48 edge-case / fuzz-style tests style_properties_test.dartProperty-based invariants for getWidth, getHeight, truncate, Style.render, joinH/V, stripAnsi Other
- All 54 VHS GIFs regenerated from current kernel snapshots.
-
1.0.022 Mar 2026Release notes
Open source →Full Changelog: https://github.com/codenameakshay/dart_tui/commits/1.0.0
Release notes
Open source →New features
- CellRenderer: cell-level diff renderer using grapheme clusters (
characterspackage) — only changed cells emit ANSI sequences, eliminating flicker. - Synchronized updates: CSI
?2026h/lwrapping for flicker-free frames on terminals that support it. - ExecMsg / execProcess(): run external processes (e.g.
$EDITOR) with full terminal hand-off and optional exit-code callback. - TextAreaModel: multi-line editor with
charLimit, ctrl+k/ctrl+u line-kill, cursor navigation. - ViewportModel: scrollable content pane with soft-wrap,
atBottom/scrollPercent, keyboard navigation. - TableModel + TableColumn: tabular data viewer with header, separator, scrolling cursor, and optional row styles.
- TimerModel: countdown timer,
start()/stop()/reset()builders,TickMsgrouting byid. - StopwatchModel: elapsed-time stopwatch with millisecond display.
- KeyMap + KeyBinding + HelpModel: declarative keybinding registry; help UI with compact/full toggle.
- FilePickerModel: async directory browser with extension filtering.
- Style system: Lipgloss-inspired
Stylewithwidth/heightconstraints,Align/AlignVertical,inlinemode,AdaptiveColor,Bordervariants;joinHorizontal(),joinVertical(),place()layout helpers. - EchoMode (normal / password / none) and
suggestions(tab-completion) onTextInputModel. - ValidationFailedMsg + validate callback on
TextInputModel. tickWithId(Duration, Object): tick Cmd with routing ID for composable timers.batch()/sequence(): concurrent vs. ordered command scheduling.- Mouse support:
MouseMode.cellMotion/allMotion;MouseClickMsg,MouseMotionMsg,MouseWheelMsg. - Cursor control:
View.cursorwithCursorShape(block / underline / bar) and blink flag. - Focus reporting:
View.reportFocus,FocusMsg,BlurMsg. - Window title:
View.windowTitleOSC sequence. - 41 examples: complete port of the Bubbletea example gallery.
Performance improvements
- Enter / LF key fix:
0x0a(LF /\n) was silently decoded asctrl+jand dropped by all components. It is now correctly mapped toKeyCode.enter, matching Linux/WSL terminal behaviour. - Batch render loop: messages are now drained without re-rendering between each one; a single render fires per batch. Eliminates up to 16 ms of FPS-throttle lag per key press.
- Unawaited commands:
runCmdis now fire-and-forget so command results arrive as the next queued message without blocking key event processing. - Deferred capability queries:
CSI ?2026$y(synchronized-updates query) andOSC 11(background-color query) are sent after the first rendered frame, so the initial visible output is not delayed. - Kernel snapshot build:
tool/build.sh --kernelcompiles examples to.dillkernel snapshots (~550 ms startup vs ~1 050 ms JIT source on WSL2). - Makefile: added targets for
test,analyze,run,run-fast,kernels,bench,gifs,new-example, and more. - 48 GIFs re-recorded from kernel snapshots for faster, cleaner recordings.
Bug fixes
- Terminal not restored on quit: added
_output.writeln()+await _output.flush()in thefinallyblock so ANSI reset sequences (show cursor, exit alt-screen) are flushed to the terminal before the shell regains control. - Terminal hang on exit: awaiting the stdin subscription cancel in the shutdown path so the Dart event loop is fully released before the process exits. Previously the
unawaitedcancel could leave stdin holding the event loop open, requiring a manual Ctrl-C to regain the shell prompt.
Breaking changes from 0.1.0
- Legacy Go-style uppercase API aliases removed (
Batch,Sequence,Quit,Println,WithInput, etc.) — use canonical lowercase equivalents. request*commands now emit real terminal protocol queries; decoder responses feed back as typedMsgvalues.ListModelrenamed toSelectListModel.
- CellRenderer: cell-level diff renderer using grapheme clusters (
-
1.0.0+122 Mar 2026Release notes
Open source →Bug fixes
- Terminal hang on exit: awaiting the stdin subscription cancel in the shutdown path so the Dart event loop is fully released before the process exits. Previously the
unawaitedcancel could leave stdin holding the event loop open, requiring a manual Ctrl-C to regain the shell prompt. - Terminal not restored on quit: flushing ANSI reset sequences (show cursor, exit alt-screen) before the process exits so the shell prompt appears on a clean line.
Other changes
- Enter / LF key fix:
0x0a(LF /\n) now correctly maps toKeyCode.enter, fixing silent key drops on Linux/WSL terminals that send LF instead of CR for Enter. - Batch render loop: all pending messages are drained before each render; a single render fires per batch, eliminating up to 16 ms of FPS-throttle lag per key press.
- Deferred capability queries:
CSI ?2026$yandOSC 11are sent after the first rendered frame so startup is not delayed. - Makefile:
make format(dart format check),make test,make analyze,make run/run-fast/kernels/bench/gifs/new-example. - Analyzer clean: resolved all
strict_raw_type,unused_local_variable,prefer_const_constructors,library_private_types_in_public_api, andavoid_relative_lib_importswarnings.
- Terminal hang on exit: awaiting the stdin subscription cancel in the shutdown path so the Dart event loop is fully released before the process exits. Previously the