FDD Story Get Started Tutorial Docs Motivation Download GitHub

Terminal UI

Build beautiful, interactive terminal applications with ARO's Terminal UI system. Create live-updating dashboards, system monitors, and CLI tools that respond instantly to data changes—without polling. The reactive Watch pattern triggers UI re-renders when events occur or data changes, making your terminal applications both efficient and responsive.

Getting Started

Terminal applications can access terminal capabilities and apply ANSI styling through templates:

(* templates/status.screen *)
{{ "=== Dashboard ===" | bold | color: "cyan" }}

Terminal: {{ <terminal: columns> }}×{{ <terminal: rows> }}

{{ "Success!" | color: "green" | bold }}
{{ "Warning" | color: "yellow" }}
{{ "Error" | color: "red" }}

The Terminal Object

Templates automatically have access to a terminal object with capability information:

{{ <terminal: rows> }}           (* Terminal height *)
{{ <terminal: columns> }}        (* Terminal width *)
{{ <terminal: supports_color> }} (* Boolean: color support *)
{{ <terminal: is_tty> }}         (* Boolean: connected to TTY *)

Responsive Design Example:

{{when <terminal: columns> > 120}}
  (* Wide layout *)
  {{ "=== Detailed Dashboard ===" | bold }}
{{when <terminal: columns> > 80}}
  (* Medium layout *)
  {{ "=== Dashboard ===" | bold }}
{{else}}
  (* Narrow layout *)
  {{ "Dashboard" }}
{{end}}

Template Filters

Apply ANSI styling with simple filters that can be chained together:

Color Filters

{{ "Success!" | color: "green" }}
{{ "Error!" | color: "red" }}
{{ "Warning" | color: "yellow" }}

{{ "Highlight" | bg: "blue" }}
{{ "Alert" | color: "white" | bg: "red" }}

(* RGB colors (24-bit true color) *)
{{ "Custom" | color: "rgb(100, 200, 50)" }}

Named Colors: black, red, green, yellow, blue, magenta, cyan, white, brightRed, brightGreen, brightBlue, success (green), error (red), warning (yellow), info (blue)

Style Filters

{{ "Important" | bold }}
{{ "Subdued" | dim }}
{{ "Emphasis" | italic }}
{{ "Link" | underline }}
{{ "Removed" | strikethrough }}

Chaining Filters

{{ "SUCCESS" | color: "green" | bold }}
{{ "ERROR" | color: "red" | bold | underline }}
{{ "Debug Info" | color: "cyan" | dim }}

Reactive Watch Pattern

The Watch pattern is ARO's approach to live-updating terminal UIs. Unlike traditional polling, Watch is purely reactive—handlers trigger only when actual changes occur.

Repository Observer Watch

UI updates automatically when repository data changes:

(Application-Start: Task Manager) {
    (* Initialize tasks *)
    Create the <task1> with { id: 1, title: "Write docs", status: "pending" }.
    Store the <task1> into the <task-repository>.

    Keepalive the <application> for the <events>.
    Return an <OK: status> for the <startup>.
}

(* Watch handler - triggers on repository changes *)
(Dashboard Watch: task-repository Observer) {
    Clear the <screen> for the <terminal>.

    Retrieve the <tasks> from the <task-repository>.

    Transform the <output> from the <template: templates/dashboard.screen>.
    Log <output> to the <console>.

    Return an <OK: status> for the <render>.
}

Flow:

  1. App stores/updates/deletes data in repository
  2. Repository emits RepositoryChangedEvent
  3. Watch handler detects change for task-repository
  4. Handler retrieves fresh data and renders template
  5. Updated display appears in terminal

Result: Every time data changes, the dashboard automatically re-renders!

Event-Based Watch

Watch handlers can also trigger on custom domain events:

(Application-Start: System Monitor) {
    Create the <metrics> with { cpu: 23, memory: 45, disk: 67 }.
    Emit a <MetricsUpdated: event> with <metrics>.

    Keepalive the <application> for the <events>.
    Return an <OK: status> for the <startup>.
}

(* Watch handler - triggers on MetricsUpdated events *)
(Dashboard Watch: MetricsUpdated Handler) {
    Clear the <screen> for the <terminal>.

    Transform the <output> from the <template: templates/monitor.screen>.
    Log <output> to the <console>.

    Return an <OK: status> for the <render>.
}

Why Watch is Better Than Polling

❌ Traditional Polling (Other Languages)

setInterval(() => {
    const tasks = getTasks();
    renderDashboard(tasks);
}, 1000);  // Check every second - wasteful!
  • Wastes CPU cycles checking when nothing changed
  • Updates delayed until next poll
  • Must choose between responsiveness and efficiency

✅ ARO Watch Pattern

(Dashboard Watch: task-repository Observer) {
    Retrieve the <tasks> from the <task-repository>.
    Transform the <view> from the <template: dashboard.screen>.
    Log <view> to the <console>.
    Return an <OK: status>.
}
  • Zero CPU usage when idle
  • Instant updates when data changes
  • No timers to manage

Terminal Actions

Clear Action

Clear the terminal screen or current line:

Clear the <screen> for the <terminal>.
Clear the <line> for the <terminal>.

Common Usage: Clear before re-rendering in Watch handlers to prevent screen clutter.

Prompt Action

Request text input from the user:

(* Basic input *)
Prompt the <name> from the <terminal>.

(* Hidden input for passwords *)
Prompt the <password: hidden> from the <terminal>.

Select Action

Display an interactive menu:

Create the <options> with ["Red", "Green", "Blue", "Yellow"].

(* Single selection *)
Select the <choice> from <options> from the <terminal>.

(* Multi-selection *)
Select the <choices: multi-select> from <options> from the <terminal>.

Complete Example

A task management dashboard that updates reactively:

main.aro

(Application-Start: Task Dashboard) {
    (* Initialize tasks *)
    Create the <task1> with {
        id: 1,
        title: "Implement feature",
        status: "in-progress",
        priority: "high"
    }.
    Create the <task2> with {
        id: 2,
        title: "Write tests",
        status: "pending",
        priority: "medium"
    }.

    Store the <task1> into the <task-repository>.
    Store the <task2> into the <task-repository>.

    Keepalive the <application> for the <events>.
    Return an <OK: status> for the <startup>.
}

(* Reactive dashboard *)
(Dashboard Watch: task-repository Observer) {
    Clear the <screen> for the <terminal>.

    Retrieve the <all-tasks> from the <task-repository>.

    (* Filter by status *)
    Filter the <done> from <all-tasks> where <status> = "done".
    Filter the <in-progress> from <all-tasks> where <status> = "in-progress".

    Transform the <output> from the <template: templates/dashboard.screen>.
    Log <output> to the <console>.

    Return an <OK: status> for the <render>.
}

(* Update task - triggers Watch handler *)
(Complete Task: TaskCompleted Handler) {
    Extract the <task-id> from the <event: taskId>.

    Retrieve the <task> from the <task-repository> where id = <task-id>.
    Update the <task: status> with "done" into the <task-repository>.

    Return an <OK: status> for the <completion>.
}

templates/dashboard.screen

{{ "╔══════════════════════════════════════════╗" }}
{{ "║ " }}{{ "TASK DASHBOARD" | bold | color: "cyan" }}{{ "                       ║" }}
{{ "╚══════════════════════════════════════════╝" }}

{{ "📊 Statistics:" | bold }}
  {{ "✓ Done:        " }}{{ <done> | length | color: "green" }}
  {{ "◷ In Progress: " }}{{ <in-progress> | length | color: "yellow" }}

{{ "🔄 In Progress" | bold | color: "yellow" }}
{{for task in in-progress}}
  {{ "  [" }}{{ <task: id> }}{{ "] " }}{{ <task: title> | bold }} {{ "(" }}{{ <task: priority> | color: "magenta" }}{{ ")" }}
{{end}}

{{ "✅ Completed" | bold | color: "green" }}
{{for task in done}}
  {{ "  [" }}{{ <task: id> }}{{ "] " }}{{ <task: title> | dim | strikethrough }}
{{end}}

{{ "Last updated: reactively on data changes" | dim }}

Platform Support

Platform Support Notes
macOS ✅ Full All features supported (iTerm2, Terminal.app)
Linux ✅ Full All features supported (GNOME Terminal, Konsole, etc.)
Windows Terminal ✅ Full Full ANSI support
CMD/PowerShell ⚠️ Partial Limited ANSI support (Windows 10+)

Graceful Degradation: ARO automatically detects terminal capabilities and falls back: RGB → 256-color → 16-color → no color, Unicode → ASCII, etc.

Best Practices

Check Capabilities

{{when <terminal: supports_color>}}
  {{ <error> | color: "red" | bold }}
{{else}}
  {{ "ERROR: " }}{{ <error> }}
{{end}}

Responsive Layouts

{{when <terminal: columns> > 120}}
  (* Wide: detailed 3-column layout *)
  Transform the <view> from the <template: templates/wide.screen>.
{{when <terminal: columns> > 80}}
  (* Medium: 2-column layout *)
  Transform the <view> from the <template: templates/medium.screen>.
{{else}}
  (* Narrow: stacked layout *)
  Transform the <view> from the <template: templates/narrow.screen>.
{{end}}

Efficient Re-Rendering

(Dashboard Watch: data-repository Observer) {
    (* Clear before full re-render for clean display *)
    Clear the <screen> for the <terminal>.

    Retrieve the <data> from the <data-repository>.
    Transform the <view> from the <template: dashboard.screen>.
    Log <view> to the <console>.

    Return an <OK: status>.
}

Quick Reference

Feature Syntax
Watch (Repository) (Name Watch: repository Observer)
Watch (Event) (Name Watch: EventType Handler)
Color Filter {{ <text> | color: "red" }}
Background Color {{ <text> | bg: "blue" }}
Bold {{ <text> | bold }}
Dim {{ <text> | dim }}
Italic {{ <text> | italic }}
Underline {{ <text> | underline }}
Strikethrough {{ <text> | strikethrough }}
Terminal Rows {{ <terminal: rows> }}
Terminal Columns {{ <terminal: columns> }}
Clear Screen Clear the <screen> for the <terminal>.
Prompt Input Prompt the <input> from the <terminal>.
Select Menu Select the <choice> from <options> from the <terminal>.

← Template Engine