These docs describe the main branch, including unreleased changes. Read the 0.8.0 docs.

Program status

OpenTUI sends OSC 7501 Program Status Protocol reports. A report tells the terminal what the program is doing. The terminal decides how to show it, for example as a tab indicator, an inbox entry, or a desktop notification.

renderer.setProgramStatus({ state: "working", app: "deploy", message: "Deploying v2.4.1" })
renderer.setProgramStatus({
  state: "blocked",
  kind: "permission",
  app: "deploy",
  message: "Approve production deploy?",
})
renderer.setProgramStatus({ state: "done", app: "deploy", message: "Deployed to 3 regions" })

States

stateMeaningExtra fields
idleAt rest and waiting for the next instruction
workingRunningprogress
blockedWaiting for the userkind, progress
doneFinished, and the user has not seen the result yet
errorFailed and stopped
clearRemoves the addressed record and every record beneath itAccepts only id

kind is "permission", "question", or "auth". progress is an integer from 0 to 100. Omit progress when the work has no percentage.

Report idle when the user cancels work. Report done or error before the program exits, so the terminal keeps a record for the user to find.

Records

Each report replaces its record completely. A field missing from a report is missing from the record afterwards, so send app and title with every report that needs them.

FieldValue
idRecord path such as build/test. Omit it for the root record
appStable program identifier such as cargo. Up to 32 [A-Za-z0-9_.+-] bytes
titleShort label for the record. Up to 192 UTF-8 bytes
messageOne line that says what the record is doing or waiting for. Up to 2048 bytes

An id has up to 8 segments separated by /. Each segment has 1 to 32 [A-Za-z0-9_.+-] bytes, and the whole id has at most 128 bytes. A program that runs several tasks can report one record for each task:

renderer.setProgramStatus({ state: "working", app: "deploy" })
renderer.setProgramStatus({ state: "working", id: "us-east", title: "US East", progress: 40 })
renderer.setProgramStatus({ state: "blocked", kind: "permission", id: "eu-west", title: "EU West" })
renderer.setProgramStatus({ state: "clear", id: "us-east" })

title and message cannot contain control characters, including newline and tab. OpenTUI encodes them as base64.

Results

setProgramStatus() returns true when OpenTUI queues the report. It returns false in these cases:

  • The renderer is destroyed.

  • The terminal is not active, for example before setupTerminal() finishes or while the renderer is suspended.

  • The output queue is full.

A report that breaks a rule above throws. OpenTUI checks the whole report before it writes anything.

Detection

Terminals ignore OSC sequences they do not implement, so setProgramStatus() does not wait for detection. OpenTUI sends the OSC 7501 ; ? query at startup, except when TERM names foot, whose reduced probe omits OSC capability queries. renderer.capabilities?.program_status becomes true when the terminal replies. A false value can also mean that the reply has not arrived yet.

renderer.on("capabilities", (capabilities) => {
  console.log("program status", capabilities.program_status)
})

Lifetime

Records belong to the terminal, not to the renderer. When the process exits or the shell shows a new prompt, the terminal drops working and blocked records. done and error records stay until the terminal decides to remove them. OpenTUI does not clear records when the renderer is destroyed or suspended.

Multiplexers

OpenTUI does not wrap reports in tmux or GNU Screen passthrough. A passthrough report would reach the outer terminal, which cannot tell panes apart or see the program exit. A multiplexer that implements OSC 7501 receives reports directly.

Next