fix: refocus chat input after cancelling a stream - #416
Merged
Merged
Conversation
cancelStream never focused the input, so after clicking Stop
generating focus fell back to <body>. Focus the chat input, same as
the similar setInputValue(..., { focus: true }) handler already does.
cancelStream also runs on the container's Escape handler, which can
fire while focus is inside the history drawer (a role="dialog" that
restores focus to its own trigger when it closes). Skip the refocus
when the active element is inside a dialog so Escape-to-close-drawer
doesn't fight the drawer's own focus restoration.
Fixes posit-dev#242
gadenbuie
self-requested a review
September 24, 2026 19:54
gadenbuie
approved these changes
Sep 24, 2026
gadenbuie
left a comment
Collaborator
There was a problem hiding this comment.
Thanks for catching and fixing the focus loss, @taekop! While testing the change, I found that Escape could also cancel a stream while closing the history drawer, or if the chat was embedded in a Shiny modal, it would close the modal containing the chat instead of cancelling. I pushed two follow-up commits to your branch to give those actions the right priority and added deterministic browser tests for the modal and the input's slash-command menu cases.
Thanks again for getting this started!
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.
Summary
cancelStreamruns, so clicking Stop generating returns focus to the textarea instead of leaving it on<body>.cancelStreamis also wired to the Escape keydown handler on the container, which can fire while focus is inside the history drawer (role="dialog", restores focus to its own trigger on close). Skip the input refocus whenever the active element is inside a dialog, so Escape-to-close-drawer isn't fought by this fix. The drawer's own focus handling is left untouched.Testing
js/tests/chat/ChatApp.test.tsx("does not steal focus from the open history drawer when Escape cancels a stream"): opens the history drawer while a stream is running, fires Escape with focus inside the drawer, and asserts the chat input'sfocus()is not called.cancelStream(unconditionalchatInputRef.current?.focus()) and passes with the dialog check added.Verification
make js-test— 1362 passed, 23 skipped, 0 failedmake js-lint— clean (tsc -noEmit && eslint .)make update-dist— rebuiltjs/distand syncedpkg-py/pkg-rweb assetsFixes #242