Skip to content

Backends assume an interactive terminal: non-TTY stdout, NO_COLOR, TERM=dumb, SIGTERM #24

Description

@mellonis
  • Packages: @flowtty/tty-backend, @flowtty/inline-tty-backend, render() in @flowtty/react — v1.0.0-alpha.11
  • Area: robustness outside an interactive terminal

TtyBackend assumes it is talking to a capable, interactive terminal and never checks. Each item below was reproduced with a five-line app (render(<Text color="red" bold>hello</Text>, new TtyBackend())).

Observed

  • stdout is not a TTY (mycli | tee log, CI, a file): the alt-screen and cursor sequences and every SGR code are written into the pipe — the log starts with ESC[?1049h ESC[?25l ESC[2J ESC[H …. Nothing in the backend looks at stdout.isTTY (only stdin's, for raw mode).
  • NO_COLOR=1 is ignored: ESC[1;31m is still emitted.
  • TERM=dumb is ignored: the alt screen is still entered.
  • SIGTERM / SIGHUP: the process dies with the terminal left in the alt screen and the cursor hidden — neither ESC[?1049l nor ESC[?25h is written. Only Ctrl-C / Ctrl-D (read as keys in raw mode) and uncaught errors restore it; render() installs no signal handlers. Same for bracketed paste and mouse reporting, which stay switched on.

Not checked yet

  • Windows Terminal / ConPTY: key sequences, alt screen, resize.
  • stdin is not a TTY while stdout is (input piped in): onKey silently never fires.
  • Terminals without truecolor: rgb() / #hex are always emitted as 24-bit SGR, with no 256-color or 16-color downgrade.

Expected — to decide per item, roughly

  • Non-TTY stdout: either refuse with a clear error, or degrade to a plain, final-frame text dump without control sequences (a render option; the inline backend's printStatic path is close to this already).
  • Honor NO_COLOR (drop color SGR, keep bold/dim/underline) and FORCE_COLOR.
  • TERM=dumb: treat like a non-TTY.
  • Restore the terminal on SIGTERM / SIGHUP / SIGINT, then re-raise, so the exit status is still the signal's.
  • A README section stating what happens in each of these cases.

Value: clear. This is the class of failure a user meets on day one — a garbled CI log, a terminal stuck in the alt screen — and none of it needs new features.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions