Skip to main content

Terminal

Shadow dwc-terminal
Java API

The Terminal component provides an interactive terminal emulator that behaves much like a traditional system console. It allows applications to display and manipulate a text-based interface, handling text output, receiving user input, interpreting control sequences, and maintaining screen buffers.

This terminal is designed to deliver reliable behavior across a range of use cases, such as building remote access tools, text dashboards, embedded command shells, or interactive debug consoles.

Importing Terminal

To use the Terminal component in your app, ensure that you include the following dependency in your pom.xml.

<dependency>
<groupId>com.webforj</groupId>
<artifactId>webforj-terminal</artifactId>
</dependency>
Show Code

How it works

The terminal manages a grid of text cells, processes incoming character streams, and reacts to user actions like typing or selecting text. It automatically interprets control characters and escape sequences for cursor movement, color changes, and screen clearing.

The core behaviors include:

  • Data Input: Writing data to the terminal updates the screen, handling both text and control sequences.
  • Data Output: Captures user keystrokes and emits them as structured events.
  • Screen Management: Maintains a scrollable history buffer and current screen state.
  • Cursor Handling: Tracks cursor position for text input and control sequence responses.

The terminal is stateful, meaning it properly reconstructs multibyte characters and maintains continuity across fragmented inputs.

Sending data to the terminal

Data is sent to the terminal using the write and writeln methods:

  • write(Object data): Sends data into the terminal stream.
  • writeln(Object data): Sends data followed by a newline.

The terminal processes all incoming data as UTF-16 strings. It automatically handles multibyte characters, even when input arrives in fragmented chunks.

Example

terminal.write("echo Hello World\n");
terminal.writeln("Ready.");

You can also attach a callback that runs once the chunk of data has been processed:

terminal.write("Long command output", e -> {
System.out.println("Data processed.");
});

Receiving user input

The terminal captures user-generated input through two events:

  • Data Event (onData): Fires when text input occurs, sending Unicode characters.
  • Key Event (onKey): Fires for every keypress, including information about key codes and modifiers like Ctrl or Alt.

These events can be used to relay user input to a backend, update UI elements, or trigger custom actions.

Example

terminal.onData(event -> {
String userInput = event.getValue();
backend.send(userInput);
});

terminal.onKey(event -> {
if (event.isControlKey() && "C".equals(event.getKey())) {
backend.send("SIGINT");
}
});

All user input captured by the terminal (such as from onData events) is emitted as UTF-16 strings.
If your backend expects a different encoding (such as UTF-8 bytes), you must manually transcode the data.

Legacy Encodings

The terminal doesn't support legacy encodings like ISO-8859.
If you need compatibility with non-UTF-8 systems, use an external transcoder (for example, luit or iconv) to convert the data before writing it to or reading it from the terminal.

Handling large data streams

Because the terminal can't instantly render unlimited input, it maintains an internal input buffer. If this buffer grows too large (default around 50MB), new incoming data may be dropped to protect system performance.

To properly manage fast data sources, you should implement flow control.

Basic flow control example

Pause your backend until the terminal finishes processing a chunk:

pty.onData(chunk -> {
pty.pause();
terminal.write(chunk, result -> {
pty.resume();
});
});

Watermark flow control example

For more efficient control, use high/low watermarks:

int HIGH_WATERMARK = 100_000;
int LOW_WATERMARK = 10_000;

int bufferedBytes = 0;

pty.onData(chunk -> {
bufferedBytes += chunk.length;

terminal.write(chunk, e -> {
bufferedBytes -= chunk.length;
if (bufferedBytes < LOW_WATERMARK) {
pty.resume();
}
});

if (bufferedBytes > HIGH_WATERMARK) {
pty.pause();
}
});
Show Code

Customization

Terminal options

The TerminalOptions class allows you to configure behavior:

  • Cursor blinking.
  • Font settings (family, size, weight).
  • Scrollback buffer size.
  • Line height and letter spacing.
  • Accessibility settings (screen reader mode).

Example:

TerminalOptions options = new TerminalOptions()
.setCursorBlink(true)
.setFontFamily("Courier New, monospace")
.setFontSize(13)
.setScrollback(5000);

terminal.setOptions(options);

Terminal theme

You can style the terminal using TerminalTheme, which defines:

  • Background and foreground colors.
  • Standard ANSI color palette.
  • Cursor and selection background colors.

Example:

TerminalTheme theme = new TerminalTheme();
theme.setBackground("#1e1e1e");
theme.setForeground("#cccccc");
terminal.setTheme(theme);
Show Code

Supported sequences

The terminal supports a wide range of standard control sequences used for cursor movement, screen updates, and text formatting.

Recognized groups:

  • C0 control codes (single-byte 7-bit commands, \x00, \x1F, like backspace and line feed)
  • C1 control codes (single-byte 8-bit commands, \x80, \x9F)
  • ESC sequences (starting with ESC (\x1B), like save/restore cursor, screen alignment)
  • CSI sequences (Control Sequence Introducer, ESC [ or CSI (\x9B), for operations like scrolling, erasing, and styling)
  • DCS sequences (Device Control Strings, ESC P or DCS (\x90))
  • OSC sequences (Operating System Commands, ESC ] or OSC (\x9D), for setting window title, hyperlinks, and colors)
Handling Exotic and Custom Sequences

Some exotic sequence types like APC,PM, and SOS are recognized but silently ignored.
Custom sequences can be supported through integrations if needed.

Styling

Shadow parts

These are the various parts of the shadow DOM for the component, which will be required when styling via CSS is desired.

Loading...