termlib 0.6.0 copy "termlib: ^0.6.0" to clipboard
termlib: ^0.6.0 copied to clipboard

Library that provides a set of utilities for terminal applications.

TermLib #

Dart library for terminal applications. Part of termKit.

Features #

  • Text styling (colors, bold, italic, underlines, etc.)
  • RGB/TrueColor with automatic down sampling
  • Cursor control, screen clearing, alternate screen
  • Keyboard input (including Kitty protocol)
  • Mouse events, focus tracking
  • Terminal hyperlinking, notifications
  • Bracketed paste
  • Synchronous updates

Installation #

dependencies:
  termlib: ^0.1.0

Quick Start #

Output and Styling #

import 'package:termlib/termlib.dart';

Future<void> main() async {
  final term = TermLib();

  // Simple output
  term.writeln('Hello, terminal!');

  // Styled text
  term.writeln(term.style('Bold text')..bold());
  term.writeln(term.style('Red on blue')..fg(Color.red)..bg(Color.blue));

  // RGB colors (auto-downsamples if terminal doesn't support)
  term.writeln(term.style('Custom color')..fg(Color.fromString('#ff6600')));

  await term.dispose();
  await term.flushThenExit(0);
}

Terminal Control #

import 'dart:io';
import 'package:termlib/termlib.dart';

Future<void> main() async {
  final term = TermLib();

  term
    ..enableAlternateScreen()
    ..eraseClear()
    ..cursorHide()
    ..setTerminalTitle('My App')
    ..writeAt(5, 5, term.style('Hello!')..fg(Color.cyan));

  sleep(const Duration(seconds: 2));

  term
    ..disableAlternateScreen()
    ..cursorShow();

  await term.dispose();
  await term.flushThenExit(0);
}

TermRunner #

Recommended for apps needing raw mode, alternate screen, or signal handling. Handles setup, cleanup, and error recovery automatically:

import 'package:termlib/termlib.dart';

Future<void> main() async {
  await TermRunner(
    alternateScreen: true,
    rawMode: true,
    hideCursor: true,
    mouseEvents: true,
    bracketedPaste: true,
    inBandResize: true,
    lineWrapping: true, // disables autowrap for the app, restored on exit
    title: 'My App',
  ).run((term) async {
    term.writeln('Press q to quit');

    while (true) {
      final event = await term.read<KeyEvent>();
      if (event is KeyEvent && event.char == 'q') break;
      term.writeln('Key: ${event}');
    }

    return 0; // exit code
  });
}

Features:

  • Auto cleanup on normal exit, errors, and signals (SIGINT/SIGTERM)
  • onCleanup callback for resource cleanup
  • onError callback for custom error handling
  • Snapshots terminal modes at build() (after probe seeding) and restores them on every exit path, including signals

Terminal Modes (withModes) #

withModes is the scoped save/restore primitive for terminal modes (raw, alternate screen, mouse, keyboard enhancement, bracketed paste, in-band resize, line wrapping, cursor visibility). It applies the named modes for the duration of a callback, then restores each to the state it had at scope entry — so any component (readline, a widget, a query helper) leaves the terminal exactly as it found it.

await term.withModes(() async {
  // raw on + autowrap off for the duration; restored on return or throw.
  return doInteractiveWork();
}, rawMode: true, lineWrapping: false);

Each mode param is three-state:

  • null (default) — not managed: untouched, inherits the outer scope. An inner scope never clobbers an outer scope's settings (the good-citizen rule).
  • true — ensure on for the duration, restore the prior value after.
  • false — ensure off for the duration, restore the prior value after.

So a readLine() running inside a host that enabled bracketed paste keeps the host's paste working: readline doesn't request that mode, so it leaves it alone.

Serial-only. Scopes must be strictly nested or sequential — never two withModes calls concurrently pending. Signal-safe teardown lives only in TermRunner, so apps that must survive Ctrl-C should use it as the outermost harness.

Input Handling #

TermLib detects interactive vs piped input automatically.

Interactive Mode (hasTerminal == true) #

Events are queued in background. Use poll() (non-blocking) or read() (blocking):

...
// Non-blocking poll (for render loops)
final event = term.poll<KeyEvent>();
if (event is KeyEvent) {
  // handle key
}

// Blocking read (for CLI apps)
final event = await term.read<KeyEvent>();

Piped Mode (hasTerminal == false) #

Use stdinStream with transformers:

import 'dart:convert';

...
if (!term.hasTerminal) {
  await for (final line in term.stdinStream
      .transform(utf8.decoder)
      .transform(LineSplitter())) {
    term.writeln('Line: $line');
  }
}

Raw Mode and Ctrl+C #

When raw mode is enabled, Ctrl+C does NOT generate SIGINT - it arrives as a KeyEvent and must be handled manually.

Paste Events #

When bracketed paste is enabled (term.enableBracketedPaste()), a paste is delivered as a single PasteEvent carrying the full payload — not as a stream of KeyEvents. Listen for it alongside key events:

await for (final event in term.events) {
  switch (event) {
    case KeyEvent():   /* handle key */
    case PasteEvent(): /* handle paste.text */
    default:           /* ... */
  }
}

Rationale:

  • Performance: a large paste is one event, not thousands.
  • Security: pasted text is never matched against KeyBinding — a paste containing e.g. ctrl+p cannot trigger the ctrl+p action.
  • Generality: the payload is opaque, leaving room for richer content (mime-typed) in future terminals.

KeyBinding.resolve only matches KeyEvents of type keyPress. It will never fire for paste content, repeat, or release events.

Key Bindings #

Map key specs to actions with KeyBinding<A>:

enum AppAction { quit, save, moveLeft }

final bindings = KeyBinding<AppAction>()
  ..map(['ctrl+q', 'escape'], AppAction.quit)   // aliases
  ..map(['ctrl+s'], AppAction.save)
  ..map(['left', 'ctrl+b'], AppAction.moveLeft);

final action = bindings.resolve(keyEvent);
if (action != null) {
  // dispatch
}

Key specs use the KeyEvent.fromString grammar ('ctrl+a', 'enter', 'shift+ctrl+enter'). keysFor(action) and toGroupedMap() are useful for help screens and config export.

Examples #

See the example directory for more:

  • colors.dart - Color palettes (ANSI, 256, TrueColor)
  • color_table.dart - Downsampling demo from 256 to 16 colors
  • key_viewer.dart - Interactive key event viewer
  • matrix.dart - Matrix rain effect
  • piped_input.dart - Processing piped input
  • styles.dart - Text styling demo
  • snake.dart - Simple game example
  • term_info.dart - Terminal capabilities info

Acknowledgements #

Inspired by dart_console, crossterm, termenv, termwiz, vaxis, mason.

License #

MIT

6
likes
150
points
110
downloads

Documentation

Documentation
API reference

Publisher

verified publishernetflux.com.ar

Weekly Downloads

Library that provides a set of utilities for terminal applications.

Homepage
Repository (GitHub)
View/report issues

Topics

#console #terminal #tui #ansi

License

MIT (license)

Dependencies

characters, equatable, ffi, meta, termansi, termparser, termunicode, win32

More

Packages that depend on termlib