Skip to content

SIGWINCH-based window resize silently breaks when process.stdout is not the user's real terminal (e.g. wrapped by 1Password's op run) #1344

Description

@smynkr

Title

SIGWINCH-based window resize silently breaks when process.stdout is not the user's real terminal (e.g. wrapped by 1Password's op run, screen recorders, or any stdio-proxying wrapper)

Summary

CliRenderer only registers its SIGWINCH listener when _usesProcessStdout is true (renderer.ts:1205). _usesProcessStdout is set to stdout === process.stdout (line 1039), which is identity, but the documented rationale at line 985-986 is "terminal-driven resize only fires for process.stdout."

When the child process is launched by a wrapper that proxies stdio — most commonly 1Password CLI's op run, which always pipes the child's stdout/stderr (even with --no-masking) — the renderer's stdout is no longer attached to the user's terminal. In that case:

  1. process.on("SIGWINCH", …) is gated by _usesProcessStdout. However, the gate is stdout === process.stdout, which is true even when stdout is a pipe. So the listener is registered in the wrapper case.
  2. On every SIGWINCH, the handler reads this.stdout.columns / this.stdout.rows (renderer.ts:891-892). On a pipe (non-TTY) those are undefined, so the handler falls back to 80×24 and calls handleResize(80, 24). The renderer resizes to a tiny hardcoded grid on every window resize — which from the user's perspective looks like "the TUI doesn't resize when I resize the window" (layout collapses to ~80×24, sometimes interpreted as "stuck" if the original was wider).
  3. The initial size at startup is correct because CliRenderer is constructed with explicit width/height from the caller (line 1046). Only subsequent resizes are wrong.

The net effect: no TUI using CliRenderer correctly resizes when launched through any wrapper that proxies stdioop run, script -f (script), asciinema, screen recorders, etc.

Reproduction (opencode 1.18.x on macOS, Bun 1.3.14 standalone)

Minimal: launch ~/.opencode/bin/opencode (any modern opencode TUI which uses OpenTUI) via 1Password's op run:

op run --env-file ~/.config/opencode/qwen.env -- ~/.opencode/bin/opencode

Inside the running TUI, resize the terminal window: the layout does not reflow. Manually sending kill -WINCH <opencode-pid> produces the same effect (no real resize).

Confirm the cause:

lsof -a -d 1,2 -p <opencode-pid>    # fd 1,2 are PIPE under op run, not the TTY
stty -f /dev/ttysNNN size            # PTY size is updated correctly by the host

Other TUI apps launched the same way (htop, vim) resize fine in the same terminal, so the kernel signal path works — the bug is specific to OpenTUI's resolver.

Expected

The TUI should detect window resizes and reflow correctly regardless of whether the parent process proxied stdio, as long as at least one of stdin/stdout is the user's TTY (typically stdin remains a TTY under op run).

Proposed fix

In packages/core/src/renderer.ts:

  1. When this.stdout.isTTY is false, fall back to reading the TTY size from process.stdin (which is usually still a TTY under wrappers like op run). process.stdin.rows / columns reflect the user's terminal.

  2. Either:

    • register SIGWINCH whenever any of process.stdout.isTTY or process.stdin.isTTY is true, or
    • keep the existing _usesProcessStdout identity gate (for custom Writable cases) but in the handler, fall back to process.stdin.columns/rows when this.stdout columns/rows are undefined.
  3. Keep the current guard for non-process custom Writables (tests, embedded use), where SIGWINCH is genuinely meaningless.

A small change, likely under 10 lines, and would unblock every Bun-OpenTUI app run through op run (or any stdio proxy) — an increasingly common deployment shape for credential-bearing dev tools.

Environment

  • OpenTUI: latest (anomalyco/opentui, checked out 2026-08-05)
  • opencode: 1.18.13 (Bun 1.3.14 standalone, macOS aarch64, Ghostty/cmux)
  • Repro: 1Password CLI op run (any modern version)

Related

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions