terminal_core 0.2.0 copy "terminal_core: ^0.2.0" to clipboard
terminal_core: ^0.2.0 copied to clipboard

Pure Dart terminal emulation core for incremental byte streams.

terminal_core #

CI License: MIT

terminal_core is a pure Dart terminal emulation core for byte streams coming from SSH channels, PTYs, or any other terminal-like transport.

It focuses on the terminal state/model layer so that another package such as terminal_flutter can handle rendering.

Status #

This repository is public and usable today as a practical terminal core MVP.

What it is:

  • A UI-agnostic terminal parser and screen model
  • Incremental and chunk-boundary-safe for transport streams
  • Designed to pair cleanly with an SSH or PTY package

What it is not:

  • A Flutter widget or renderer
  • An SSH client, PTY controller, or networking package
  • A full xterm implementation

Highlights #

  • Incremental writeBytes(List<int>) parsing
  • UTF-8 decoding across chunk boundaries
  • Grapheme-cluster-aware Unicode handling for combining marks, emoji modifiers, flags, and common ZWJ emoji sequences
  • Configurable ambiguous-width handling for East Asian ambiguous characters
  • Main buffer and alternate buffer
  • Cursor state, scroll region, DEC origin/wrap modes, and main-buffer scrollback
  • Horizontal tab stops with default 8-column stops plus HTS/TBC support
  • Dirty-row tracking for UI layers
  • Title and bell notifications
  • A terminal input encoder that produces bytes ready to send to ssh_core

Getting Started #

Add the package from pub.flutter-io.cn:

dart pub add terminal_core

Or declare it in pubspec.yaml:

dependencies:
  terminal_core: ^0.2.0

If you need unreleased commits before the next publish, you can depend on the GitHub repository directly:

dependencies:
  terminal_core:
    git:
      url: https://github.com/Atrac613/terminal_core.git

Then import the package:

import 'package:terminal_core/terminal_core.dart';

Quick Start #

import 'dart:convert';

import 'package:terminal_core/terminal_core.dart';

void main() {
  final terminal = Terminal(columns: 20, rows: 5);

  terminal.titles.listen((title) => print('title: $title'));
  terminal.bells.listen((_) => print('bell'));

  terminal.writeBytes(utf8.encode('hello\r\n'));
  terminal.writeBytes(utf8.encode('\x1b[31mred\x1b[0m text'));

  print(terminal.visibleText());
}

For a runnable feed-and-inspect demo, see example/basic.dart.

Public API #

The primary public types are:

  • Terminal: parser, terminal state, resize, notifications, and visible buffer
  • TerminalBuffer: read-only access to visible rows and scrollback
  • TerminalCell: a single cell with character text, TerminalStyle, width, and continuation metadata
  • TerminalStyle: bold / italic / underline / inverse and foreground / background colors
  • TerminalCursor: current cursor row, column, and visibility
  • TerminalInputEncoder: encodes key presses and paste payloads into bytes
  • TerminalKey / TerminalKeyEvent: special-key model for input encoding

Useful methods and properties:

  • terminal.writeBytes(bytes) for incremental transport input
  • terminal.write(text) for tests or already-decoded input
  • terminal.resize(cols, rows) to resize the viewport
  • terminal.visibleText() and terminal.debugDump() for testing/debugging
  • terminal.changes, terminal.titles, and terminal.bells for notifications
  • terminal.inputState to mirror terminal-controlled input modes into the encoder
  • terminal.ambiguousWidthIsWide to choose narrow vs. wide rendering for East Asian ambiguous characters
  • terminal.originModeEnabled, terminal.wraparoundModeEnabled, and terminal.tabStops for DEC-mode-aware terminal state inspection
  • terminal.applicationCursorKeysEnabled and terminal.applicationKeypadModeEnabled for low-level mode checks
  • terminal.bracketedPasteModeEnabled to drive paste encoding

Supported Sequences #

C0 Controls #

  • LF
  • CR
  • BS
  • TAB
  • BEL
  • ESC

Escape / CSI / OSC #

  • ESC 7 / ESC 8 cursor save / restore
  • ESC H set a horizontal tab stop at the current column
  • ESC = / ESC > keypad application / numeric mode
  • CSI s / CSI u cursor save / restore
  • CSI A / B / C / D cursor move
  • CSI H / f cursor position
  • CSI @ insert blank characters
  • CSI g clear the current tab stop or all tab stops
  • CSI J erase in display
  • CSI K erase in line
  • CSI L insert lines
  • CSI M delete lines
  • CSI P delete characters
  • CSI S scroll up
  • CSI T scroll down
  • CSI X erase characters
  • CSI m SGR
  • CSI r scroll region
  • CSI ?6h / CSI ?6l origin mode
  • CSI ?7h / CSI ?7l wraparound mode
  • CSI ?1h / CSI ?1l application cursor keys
  • CSI ?1049h / CSI ?1049l alternate screen
  • CSI ?2004h / CSI ?2004l bracketed paste mode
  • CSI ?25h / CSI ?25l cursor visible state
  • CSI ?66h / CSI ?66l keypad application / numeric mode alias
  • OSC 0 title update
  • OSC 2 title update

SGR #

  • Reset
  • Bold
  • Italic
  • Underline
  • Inverse
  • 16 colors
  • Bright 16 colors
  • 256 colors
  • Truecolor via 38;2 / 48;2

Unsupported or unknown escape sequences are ignored safely instead of crashing.

Unicode Handling #

This package handles the most important terminal-oriented Unicode cases:

  • ASCII and other single-cell characters render as width 1
  • East Asian wide/fullwidth characters render as width 2
  • Common emoji code points render as width 2
  • Combining marks, emoji modifiers, and variation selectors extend the previous visible cluster when possible
  • Regional-indicator flags and common ZWJ emoji families are treated as a single display cluster
  • Double-width clusters are stored as a lead cell plus a continuation cell
  • Ambiguous-width characters can be treated as narrow or wide by setting Terminal(ambiguousWidthIsWide: true)

Current scope is intentionally practical rather than exhaustive:

  • If resize or editing would leave half of a double-width cluster behind, the broken cluster is blanked safely
  • The tested focus is on terminal-relevant clusters rather than exhaustive Unicode conformance for every edge case

Resize Behavior #

resize(cols, rows) keeps the existing screen contents without reflowing lines.

  • Column changes truncate or pad each stored line in place
  • When rows shrink, the viewport is bottom-anchored
  • Lines removed from the top of the main buffer move into scrollback
  • When rows grow, the main buffer reveals scrollback above the viewport when available; otherwise blank rows are inserted above
  • If a column shrink cuts through a double-width character, the incomplete glyph is blanked instead of leaving a broken half-cell
  • Existing tab stops are preserved when possible; stops past the new width are dropped, and newly exposed columns gain default 8-column stops
  • The cursor is clamped to the new viewport
  • The scroll region resets to the full viewport after resize

This behavior is covered by tests and intended to be stable for UI consumers.

Minimal ssh_core Example #

import 'package:terminal_core/terminal_core.dart';

Future<void> attachTerminal(SshChannel channel) async {
  final terminal = Terminal(columns: 80, rows: 24);

  channel.stdout.listen(terminal.writeBytes);

  channel.write(
    TerminalInputEncoder.fromState(
      terminal.inputState,
    ).encodeKey(TerminalKey.arrowUp),
  );
  channel.write(
    TerminalInputEncoder.fromState(
      terminal.inputState,
    ).encodePaste(
      'ls -la\n',
      bracketedPasteMode: terminal.bracketedPasteModeEnabled,
    ),
  );
}

SshChannel is intentionally not defined here; the point is that terminal_core consumes bytes from the remote side and emits bytes for local input without knowing anything about transport or rendering.

Development #

Run the example locally:

dart run example/basic.dart

Run the standard verification set:

dart format .
dart analyze
dart test
dart run example/basic.dart

If you are preparing a release, also verify:

dart pub publish --dry-run

Unsupported In This MVP #

  • Full xterm compatibility
  • Mouse reporting
  • Selection and search
  • Hyperlinks
  • Sixels, images, and graphics protocols
  • IME integration
  • Rendering, widgets, or accessibility semantics

Roadmap #

Likely next areas of expansion are:

  • Additional DEC / xterm compatibility
  • Deeper Unicode edge-case coverage
  • A renderer package such as terminal_flutter

Contributing #

Issues and pull requests are welcome. For local contributor workflow details, see AGENTS.md.

License #

This project is available under the MIT License.

0
likes
140
points
8
downloads

Documentation

API reference

Publisher

verified publisheratrac613.com

Weekly Downloads

Pure Dart terminal emulation core for incremental byte streams.

Repository (GitHub)
View/report issues

Topics

#terminal #ansi #ssh #tui #pty

License

MIT (license)

More

Packages that depend on terminal_core