feat: add suspendTerminal() to hand the terminal to a child process#972
Merged
sindresorhus merged 1 commit intoJun 16, 2026
Merged
Conversation
Adds useApp().suspendTerminal() so an Ink app can temporarily release the terminal to a child process (e.g. $EDITOR, less, fzf) and then restore its own terminal state with a full redraw. - Callback form restores the terminal even if the callback throws. - Disposable form (resume() and Symbol.asyncDispose) for `await using`. - Releases raw mode, bracketed paste, the cursor, the alternate screen, and the kitty keyboard protocol, then reapplies them on resume. - Suspending while already suspended throws; non-interactive output runs the callback without a terminal handoff. Closes vadimdemedes#956
e365a71 to
0f3859f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #956.
Adds
suspendTerminal()onuseApp()so an Ink app can temporarily hand the terminal to a child process (e.g.$EDITOR,less,fzf) and then restore its own terminal state with a full redraw. This is the scoped terminal handoff discussed in the issue, rather than a stdout-only suspend.API
Callback form (Ink restores the terminal even if the callback throws):
Disposable form, including
await using:Behavior
On suspend, Ink flushes pending output, stops consuming input, and restores the terminal modes the child expects (raw mode off, cursor visible, bracketed paste off, alternate screen exited, kitty keyboard protocol off). On resume it reapplies its own terminal state and forces a full redraw rather than diffing against the stale pre-suspension frame.
resume()is async because restoring ownership may need to wait for ordered writes and a redraw. Suspending while already suspended throws, and in non-interactive output the callback still runs but no terminal handoff happens.Tests and docs
test/suspend-terminal.tsxcovers the callback, disposable, andawait usingforms, throw-restores, the already-suspended error, alternate-screen exit and re-enter, the begin-suspend rollback path, keeping Ink off the terminal while suspended, and a PTY integration test that hands the terminal to a real child process and asserts the redraw on resume. There is a runnable example underexamples/suspend-terminal/, and the README documents the API underuseApp().