CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-williamcallahan--tui4j

Terminal User Interface framework for Java that ports the Charmbracelet ecosystem (Bubble Tea, Bubbles, Lipgloss, Harmonica) from Go, enabling developers to build interactive CLI applications using The Elm Architecture pattern.

Overview
Eval results
Files

index.mddocs/

TUI4J

TUI4J is a comprehensive Terminal User Interface framework for Java that ports the Charmbracelet ecosystem from Go to Java. It enables developers to build interactive, event-driven command-line applications using The Elm Architecture pattern with Model-Update-View, along with pre-built UI components, styling capabilities, and physics-based animations.

Package Information

  • Package Name: tui4j
  • Package Type: maven
  • Group ID: com.williamcallahan
  • Artifact ID: tui4j
  • Language: Java
  • Minimum Java Version: 21
  • Installation:
<dependency>
    <groupId>com.williamcallahan</groupId>
    <artifactId>tui4j</artifactId>
    <version>0.3.3</version>
</dependency>

Gradle:

implementation 'com.williamcallahan:tui4j:0.3.3'

Core Imports

// Core Bubble Tea framework
import com.williamcallahan.tui4j.compat.bubbletea.Program;
import com.williamcallahan.tui4j.compat.bubbletea.Model;
import com.williamcallahan.tui4j.compat.bubbletea.Command;
import com.williamcallahan.tui4j.compat.bubbletea.Message;
import com.williamcallahan.tui4j.compat.bubbletea.UpdateResult;

// Input handling
import com.williamcallahan.tui4j.compat.bubbletea.KeyPressMessage;
import com.williamcallahan.tui4j.compat.bubbletea.input.key.Key;
import com.williamcallahan.tui4j.compat.bubbletea.input.key.KeyType;
import com.williamcallahan.tui4j.compat.bubbletea.input.MouseMessage;

// Styling (Lipgloss)
import com.williamcallahan.tui4j.compat.lipgloss.Style;
import com.williamcallahan.tui4j.compat.lipgloss.Renderer;
import com.williamcallahan.tui4j.compat.lipgloss.color.Color;

// UI Components (Bubbles)
import com.williamcallahan.tui4j.compat.bubbles.list.List;
import com.williamcallahan.tui4j.compat.bubbles.textinput.TextInput;
import com.williamcallahan.tui4j.compat.bubbles.spinner.Spinner;

Basic Usage

import com.williamcallahan.tui4j.compat.bubbletea.*;
import com.williamcallahan.tui4j.compat.bubbletea.input.key.*;

// Define your model implementing the Model interface
class MyModel implements Model {
    private final String message;

    public MyModel(String message) {
        this.message = message;
    }

    @Override
    public Command init() {
        // Return initial command (often Command.none())
        return Command.none();
    }

    @Override
    public UpdateResult<? extends Model> update(Message msg) {
        // Handle keyboard input
        if (msg instanceof KeyPressMessage keyMsg) {
            Key key = keyMsg.key();

            // Quit on 'q' or Ctrl+C
            if (key.type() == KeyType.KeyRunes &&
                key.runes().length > 0 && key.runes()[0] == 'q') {
                return UpdateResult.from(this, Command.quit());
            }
            if (key.type() == KeyType.KeyCtrlC) {
                return UpdateResult.from(this, Command.quit());
            }
        }

        // Return unchanged model
        return UpdateResult.from(this);
    }

    @Override
    public String view() {
        // Render the UI
        return message + "\n\nPress 'q' to quit.";
    }
}

// Run the program
public class Main {
    public static void main(String[] args) {
        Model initialModel = new MyModel("Hello, TUI4J!");
        Program program = new Program(initialModel);
        program.run();
    }
}

Architecture

TUI4J follows The Elm Architecture pattern, which structures applications around three core concepts:

  1. Model: Immutable application state
  2. Update: Pure function that handles messages and returns new model + commands
  3. View: Pure function that renders model to string

This architecture enables:

  • Predictable state management
  • Easy testing and debugging
  • Clear separation of concerns
  • Composable UI components

The Event Loop

The Program manages an event loop that:

  1. Calls init() on the model to get initial state and commands
  2. Executes commands, which produce messages
  3. Passes messages to update(), which returns new model and commands
  4. Calls view() to render the current state
  5. Repeats steps 2-4 until the program quits

Capabilities

Bubble Tea Framework

The core event-driven framework based on The Elm Architecture. Provides the Program runner, Model interface, Command system for side effects, and Message-based communication.

// Program - Main entry point
class Program {
    Program(Model initialModel);
    Program(Model initialModel, ProgramOption... options);
    void run();
    Model runWithFinalModel();
    void send(Message msg);
    boolean isRunning();
}

// Model - Application state interface
interface Model {
    Command init();
    UpdateResult<? extends Model> update(Message msg);
    String view();
}

// Command - Side effects
interface Command {
    Message execute();

    // Factory methods
    static Command none();
    static Command quit();
    static Command batch(Command... commands);
    static Command tick(Duration duration, Function<Object, Message> fn);
    // ... many more factory methods
}

// UpdateResult - Result of update
record UpdateResult<M extends Model>(M model, Command command) {
    static <M extends Model> UpdateResult<M> from(M model, Command cmd);
    static <M extends Model> UpdateResult<M> from(M model);
}

Bubble Tea Framework

Input Handling

Keyboard and mouse input processing with support for all key types, modifiers, mouse buttons, and motion tracking.

// Key input
class KeyPressMessage implements Message {
    Key key();
}

record Key(KeyType type, char[] runes, boolean alt) { }

enum KeyType {
    KeyCtrlC, KeyCtrlD, KeyEnter, KeyEsc, KeySpace, KeyTab,
    KeyUp, KeyDown, KeyLeft, KeyRight, KeyRunes,
    // ... many more key types
}

// Mouse input
class MouseMessage implements Message {
    int column();
    int row();
    MouseButton getButton();
    MouseAction getAction();
    boolean isShift();
    boolean isAlt();
    boolean isCtrl();
}

enum MouseButton {
    MouseButtonLeft, MouseButtonRight, MouseButtonMiddle,
    MouseButtonWheelUp, MouseButtonWheelDown
}

enum MouseAction {
    MouseActionPress, MouseActionRelease, MouseActionMotion
}

Input Handling

Lipgloss Styling

Comprehensive styling system for colors, text attributes, borders, padding, margins, and layout. Provides a fluent API for building styled text with precise control over appearance.

// Style builder
class Style {
    static Style newStyle();

    // Colors
    Style foreground(TerminalColor color);
    Style background(TerminalColor color);

    // Text attributes
    Style bold();
    Style italic();
    Style underline();

    // Layout
    Style width(int width);
    Style height(int height);
    Style padding(int top, int right, int bottom, int left);
    Style margin(int top, int right, int bottom, int left);
    Style border(Border border);
    Style alignHorizontal(Position pos);
    Style alignVertical(Position pos);

    // Rendering
    String render(String content);
}

// Color factory
class Color {
    static TerminalColor color(String hex);
    static TerminalColor color(int r, int g, int b);
}

Lipgloss Styling

Bubbles Components

Pre-built, reusable UI components including lists, text inputs, tables, spinners, progress bars, viewports, and more. All components implement the Model interface and can be embedded in your application.

// List component
class List implements Model {
    List(Item[] items, int width, int height);
    Item selectedItem();
    int selectedIndex();
}

// Text input component
class TextInput implements Model {
    TextInput();
    String value();
    void focus();
    void blur();
}

// Table component
class Table implements Model {
    static Table create();
    Table columns(Column... columns);
    Table rows(Row... rows);
    Row selectedRow();
}

// Spinner component
class Spinner implements Model {
    Spinner(SpinnerType type);
}

// Progress bar component
class Progress implements Model {
    Progress();
    double percent();
    SetPercentMessage setPercent(double percent);
}

// Viewport (scrollable content)
class Viewport implements Model {
    Viewport(String content, int height);
    void lineDown();
    void lineUp();
    void pageDown();
    void pageUp();
}

// Textarea (multi-line editor)
class Textarea implements Model {
    Textarea();
    String value();
    void setValue(String value);
}

Bubbles Components

Animation

Physics-based spring animation system for smooth, natural motion. Harmonica provides spring dynamics with configurable damping and frequency.

class Spring {
    static Spring newSpring(double deltaTime, double angularFrequency, double dampingRatio);
    double update(double position, double velocity, double target);
    double velocity();
}

Animation

Text Utilities

ANSI-aware text processing including width calculation, wrapping, truncation, and grapheme cluster handling.

// Text width calculation
class TextWidth {
    static int width(String text);
}

// Text wrapping
class TextWrapper {
    static String wrap(String text, int width);
    static String wordWrap(String text, int width);
}

// Text truncation
class Truncate {
    static String truncate(String text, int width);
    static String truncate(String text, int width, String tail);
}

Text Utilities

Common Patterns

Composing Components

Components can be embedded in your model and delegated to:

class MyModel implements Model {
    private final TextInput input;
    private final Spinner spinner;

    @Override
    public UpdateResult<? extends Model> update(Message msg) {
        // Delegate to component
        UpdateResult<TextInput> inputResult = input.update(msg);

        // Create new model with updated component
        return UpdateResult.from(
            new MyModel(inputResult.model(), spinner),
            inputResult.command()
        );
    }

    @Override
    public String view() {
        return input.view() + "\n" + spinner.view();
    }
}

Handling Commands

Commands represent side effects and return messages:

@Override
public UpdateResult<? extends Model> update(Message msg) {
    if (msg instanceof KeyPressMessage) {
        // Execute async operation
        Command cmd = Command.tick(
            Duration.ofSeconds(1),
            id -> new TimeoutMessage()
        );
        return UpdateResult.from(this, cmd);
    }
    return UpdateResult.from(this);
}

Custom Messages

Define custom messages for your application:

record TimeoutMessage() implements Message { }
record DataLoadedMessage(String data) implements Message { }

@Override
public UpdateResult<? extends Model> update(Message msg) {
    if (msg instanceof TimeoutMessage) {
        // Handle timeout
    } else if (msg instanceof DataLoadedMessage data) {
        // Handle loaded data
    }
    return UpdateResult.from(this);
}

Program Options

Configure the program with options:

Program program = new Program(
    initialModel,
    Program.withAltScreen(),           // Use alternate screen buffer
    Program.withMouseCellMotion(),      // Enable mouse tracking
    Program.withReportFocus()           // Report focus/blur events
);
program.run();

Error Handling

Errors are propagated through the message system:

// ErrorMessage is sent when commands fail
if (msg instanceof ErrorMessage error) {
    Throwable err = error.error();
    // Handle error
    return UpdateResult.from(this.withError(err.getMessage()));
}

Performance Considerations

  • Use highPerformanceRendering in Viewport for large content
  • Batch commands with Command.batch() to reduce overhead
  • Keep view() pure and avoid expensive computations
  • Use inline styles for frequently updated content

Thread Safety

TUI4J is designed for single-threaded use. The event loop runs on one thread. To send messages from other threads, use Program.send():

// From another thread
program.send(new CustomMessage());

Install with Tessl CLI

npx tessl i tessl/maven-com-williamcallahan--tui4j

docs

animation.md

bubbles-components.md

bubbletea-core.md

index.md

input-handling.md

lipgloss-styling.md

utilities.md

tile.json