fix(code): hide chat input cursor while unfocused - #5258
Merged
Mason Daugherty (mdrxy) merged 5 commits intoAug 18, 2026
Conversation
Textual's `TextArea._draw_cursor` ignores `has_focus` while the cursor blinks, and `_end_mouse_selection` restarts the blink on mouse-up. Clicking the chat input while a focus-trapping widget (e.g. the `edit_file` approval menu) owns the keyboard therefore painted a blinking cursor in a field that could not be typed into. Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Mason Daugherty (mdrxy)
marked this pull request as ready for review
August 18, 2026 00:08
Follow-up on the `_restart_blink` override. The docstrings made two claims that do not survive a read of Textual 8.2.8: `_end_mouse_selection` is guarded by `if self._selecting:` rather than restarting the blink unconditionally, and the quoted `_draw_cursor` expression omits the read-only early return. Both are now stated accurately, and the note that `_pause_blink` also parks the blink timer spares the next reader a trip into Textual. The "Two pauses" test comment described a real mechanism in the wrong place: `Pilot._post_mouse_events` already pauses between MouseDown and MouseUp, so the menu's deferred refocus lands inside `pilot.click` and both trailing pauses were inert. Coverage additions, each verified to fail against a matching mutant: - Two inline-prompt tests, so moving the override down into `ChatTextArea` cannot silently reintroduce the phantom cursor in every inline prompt. - A blink-timer assertion on refocus. `_draw_cursor is True` passes for a cursor that came back frozen solid; asserting the timer is live catches it. - Keyboard refocus, which depends on `_watch_has_focus` flipping `has_focus` before calling `_restart_blink()`. - `set_cursor_blink(blink=False)`, the one input combination the original tests left untouched. Version pins in `chat_input.py` said 8.2.7 while the floor is 8.2.8; both claims were re-verified against the installed source and the re-check notes now say "every Textual bump", matching `_textual_patches.py`, since the pin admits any 8.x. That module's docstring now points at overrides living outside it, so a bump audit starting there does not miss them.
Address review findings on the `_restart_blink` override. The fix itself is unchanged; everything here is the prose and assertions around it. `_textual_patches.py` advertised a grep for "Deliberately overrides/shadows Textual's private ..." as an inventory of out-of-module Textual workarounds. That literal string matched only the paragraph introducing it, and the inventory was incomplete: `ContainedSelect._setup_options_renderables` and `._watch_value` shadow Textual `Select` privates with no marker, and two `app.py` stamps still read 8.2.7. Point the grep at `Textual's private` and `Validated against Textual`, flag both `thread_selector.py` sites, bump the stale stamps, and state plainly that the markers are a starting point rather than a complete inventory. Replace three `assert _draw_cursor is True` assertions with the blink-timer state. Once the timer is re-armed it toggles `_cursor_visible` every `cursor_blink` interval, so `_draw_cursor` oscillates and only reads True inside a visible half-cycle; `blink_timer._active` is stable across the cycle. This also strengthens coverage: a cursor that returns frozen solid was caught at one refocus site and is now caught at all three. Assert the parked timer alongside the negative `_draw_cursor is False` checks for the same reason. Correct a comment claiming a hand-toggled `_cursor_visible` "flips whether the timer runs" — `_watch__cursor_visible` only refreshes the row and never touches the timer, which is precisely why it would not catch a frozen one. Scope the "every click" claim, since `ChatTextArea` gates `_on_mouse_down` on refocus clicks, and disambiguate the `TestCursorHiddenWhileUnfocused` reference now that two classes share the name. Pin the approval-menu test's precondition with `has_focus is False` after the click, so a pilot that stopped interleaving mouse events fails the test instead of passing it for an unrelated reason.
Mason Daugherty (mdrxy)
pushed a commit
that referenced
this pull request
Aug 18, 2026
> [!CAUTION] > Merging this PR will automatically publish to **PyPI** and create a **GitHub release**. For the full release process, see [`.github/RELEASING.md`](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md). --- _Release notes preview: keep this section in sync with the package `CHANGELOG.md`. Publish reads the merged CHANGELOG via `release.yml`, not this PR description — keep them aligned anyway so the PR stays an accurate historical record for reviewers and anyone returning later._ --- ## [0.1.57](deepagents-code==0.1.56...deepagents-code==0.1.57) (2026-08-18) ### Features - Added warnings before expensive cold-cache turns and trust user-declared endpoints for cold-cache policies ([#5439](#5439), [#5462](#5462)). - Made the chat input resizable by dragging its top border ([#5524](#5524)). - Added a `multi_select` question type to `ask_user` ([#5097](#5097)). - Added support for ACP approval modes ([#5394](#5394)). - Added `DeepSeek-V4-Pro-0813` to the model picker ([#5512](#5512)). - Show conversation turns alongside message counts ([#5571](#5571)). - Include `TERM_PROGRAM` in the resume hint ([#5548](#5548)). ### Bug Fixes - Report total context after `/offload` ([#5488](#5488)). - Fixed transcript and thread restoration issues, including hydration lag, scrolling resumed threads to the bottom, and hiding empty previous-thread hints ([#5479](#5479), [#5543](#5543), [#5552](#5552)). - Fixed Auto-mode approval handling by binding “yes” to the paired `ask_user` question and avoiding duplicate Auto denial notices ([#5038](#5038), [#5501](#5501)). - Improved reload behavior by keeping the chat input responsive during `/reload`, reporting MCP server changes, and avoiding plugin reload prompt flashes or startup hints ([#5529](#5529), [#5504](#5504), [#5500](#5500), [#5502](#5502)). - Improved dependency update UI by preserving editable fields and hiding dependency details after updates ([#5521](#5521), [#5519](#5519)). - Fixed chat UI polish issues, including detached spacer mount anchors, the unfocused input cursor, and relative timestamp toggle display ([#5516](#5516), [#5258](#5258), [#5503](#5503)). - Refresh the splash version after updates ([#5520](#5520)). _End release notes preview._ --- > [!NOTE] > A **community contributors** list and a **Special thanks** section (crediting the users who filed the issues this release's PRs closed) are appended to the GitHub release notes automatically at publish time (see [Release Pipeline](https://github.com/langchain-ai/deepagents/blob/main/.github/RELEASING.md#release-pipeline), step 3). --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: langchain-oss-automated-triage[bot] <248757908+langchain-oss-automated-triage[bot]@users.noreply.github.com>
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.
Fixed a phantom blinking cursor in the chat input when clicking it while an approval widget holds keyboard focus.
Clicking the chat input while an
edit_fileapproval menu is open painted a blinking cursor there even though the menu immediately re-grabs focus, so the field looked editable but swallowed nothing. Textual'sTextArea._draw_cursoris(has_focus and not cursor_blink) or (cursor_blink and _cursor_visible)— with blinking on it never consults focus — and_end_mouse_selection(mouse-up) restarts the blink unconditionally, after the approval menu'son_blurhandler has already taken focus back.PasteBurstTextArea(shared base for the chat input and inline prompts) now pauses the blink instead of restarting it when the text area is unfocused, which also covers programmatic multi-characterinsert()into an unfocused input.Made by Open SWE