Repository navigation
Conversation
|
Thanks for your contribution! This PR doesn't have a linked issue. All PRs must reference an existing issue. Please:
See CONTRIBUTING.md for details. |
|
This PR doesn't fully meet our contributing guidelines and PR template. What needs to be fixed:
Please edit this PR description to address the above within 2 hours, or it will be automatically closed. If you believe this was flagged incorrectly, please let a maintainer know. |
|
This pull request has been automatically closed because it was not updated to meet our contributing guidelines within the 2-hour window. Feel free to open a new pull request that follows our guidelines. |
Validation on the actual broken stack (Windows: Alacritty + zellij via ConPTY)Reproduced with the release binary built from this branch. The startup sequence enables:
neither of which the original reset list disabled. With those left active after exit, the next session in the same pane misbehaved (intermittent corruption that 'only failed on the 2nd/3rd run'). Those modes are now part of the symmetric reset (commit 2ff699c). Consecutive enter/exit cycles are clean and zellij is left intact. Also covers ?1005/?1015 (legacy mouse) and ?2026 (synchronized output) defensively. Builder note (not part of the diff): local Windows builds must use the repo-pinned [email protected] — bun 1.4.2's bundler produces a broken binary (a.name TypeError during SystemPrompt.environment). Builds are reproducible with the ran repo toolchain. |
fix(tui): force terminal reset on any exit path
Fixes #48776 · Refs #45938
Problem
When opencode exits inside a ConPTY stack on Windows (Alacritty + zellij-windows),
the terminal can be left with leaked raw-mode state: alternate screen not exited,
mouse tracking / kitty keyboard still enabled, garbled input (characters repeated
or echoed). Tracked in #48776 (Windows) and #45938 (macOS).
Root cause:
destroyRenderer()only runs on the graceful path (scoped release,/exit, SIGHUP). If the process exits viaprocess.exit()elsewhere, or dies froma signal (e.g. the
kill -1scenario in #45938), the renderer teardown is bypassedentirely and nothing resets the terminal. Even on the graceful path, @opentui/core
does not emit a kitty-keyboard disable (
CSI < u) on destroy, and its restorebytes can be dropped by the ConPTY/zellij pipe once the process ends.
Change
packages/tui/src/util/renderer.tsterminalReset(): synchronously writes a minimal ANSI reset sequence(SGR off, show cursor, app-cursor keys off, xterm/SGR mouse off, kitty keyboard
reset, leave alternate screen). No-op when stdout is not a TTY; never throws.
destroyRenderer()now also emitsterminalReset()right afterrenderer.destroy().packages/tui/src/app.tsxprocess.on("exit", ...)→terminalReset()so the reset runs onany exit path (
process.exit(), uncaught error exit, etc.).SIGTERMhandler that destroys the renderer then exits (143), coveringthe signal-kill scenario from macOS: exit/cleanup path broadcasts SIGTERM to ALL user processes (kill -1) — kills Ghostty, Terminal, Chrome, entire session (1.18.24 & 1.18.25) #45938.
Verification
bun run --cwd packages/tui typecheck— passes (tsgo, 0 errors)bunx oxlint ...— 0 errors, 5 pre-existing warnings (untouched lines)createCliRenderer→destroyRendererleavesisDestroyed: trueand restores cursor/altscreen/mouse; confirms kitty-keyboard reset (
CSI < u)is now emitted (opentui's own destroy does not).
Repro video (Windows, Alacritty + zellij-windows)
Opencode_issue.mp4