Skip to content

Keep keyboard focus after Scroll to end or removing a queued message - #391

Merged
abhishekgahlot2 merged 3 commits into
useagenthq:mainfrom
tianxingjian2406:fix/issue-330
Oct 27, 2026
Merged

abhishekgahlot2 merged 3 commits into
useagenthq:mainfrom
tianxingjian2406:fix/issue-330

Conversation

@tianxingjian2406

@tianxingjian2406 tianxingjian2406 commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #330

What changed

  • Activating "Scroll to end" removed the focused button and dropped focus to the body. The pill now hands focus to the reply box before it unmounts.
  • Removing a queued message removed its focused control. Focus now lands on the next row's Remove button, or on the reply box when the queue is empty.
  • The reply box anchor is data-session-ui="reply-composer" on the ReplyComposer wrapper. Focus cannot land on another Composer instance, for example the subagent pane's composer.

Testing

  • New interaction tests (scroll-to-end-pill.test.tsx, queued-messages-interaction.test.tsx) mount the real components on happy-dom and read document.activeElement after each action. They fail without the fix and pass with it.
  • bun run typecheck, bun test components app lib, and bun run lint run clean (the known ghostty-web WASM baseline failure aside).

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

Copilot AI balanced review requested due to automatic review settings October 26, 2026 16:09

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@tianxingjian2406

Copy link
Copy Markdown
Contributor Author

I have read the CLA Document and I hereby sign the CLA

@abhishekgahlot2

Copy link
Copy Markdown
Contributor

Thanks for this. Before we can merge, please add a before/after screenshot or a short screen recording of the change working in the running app (the issue asks for one), plus a line on how you tested it.

Two things to fix too:

  • In the app, removing a queued message only sends a cancel, and the row disappears later when the thread updates. So focus goes back to the button being removed and then drops to the page. Your tests pass only because their row is removed at once.
  • Scroll to end now focuses the reply box on every tap, which opens the keyboard on phones.

The app sends the cancel first and drops the row when the thread updates, so
focusing the clicked row's own button left it on a control that unmounts.
Focus the next row's Remove (or the reply box) right away instead.

Scroll to end now hands focus to the reply box only for keyboard activation
(click detail 0); a pointer tap no longer opens the on-screen keyboard.

Both behaviours are covered in the interaction tests.
@tianxingjian2406

tianxingjian2406 commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks. Both points are fixed in 9411b02.

  1. Queued-message removal: the row now moves focus to a control that survives the later list update — the next row's Remove, the previous row's when it was last, or the reply box when no row remains. It never focuses its own button, which unmounts when the thread updates. The interaction test now also covers a removal that lands later, with the row still mounted.
  2. Scroll to end: focus moves to the reply box only for keyboard activation (click detail === 0). A pointer tap just scrolls, so phones no longer open the keyboard.

Queued removal, right after the click — before: ring on the row being removed; after: ring on the surviving row:

before: focus ring on the row being removed
after: focus ring on the surviving row

After the thread update drops the row — before: focus dropped to the page; after: focus stayed on the surviving Remove:

before: row removed, no focus ring anywhere
after: row removed, focus ring still on the surviving row

Scroll to end from the keyboard — before: activeElement is body; after: activeElement is the reply textarea:

before: no focus in the reply composer
after: caret and focus in the reply composer

How I tested it: I ran the app with the real components, activated Remove and Scroll to end from the keyboard, and captured the focus state before and after the delayed row removal. bun test components/session-ui/scroll-to-end-pill.test.tsx components/pro/queued-messages-interaction.test.tsx passes (5 tests), typecheck and biome lint are clean, and the full suite passes except the known ghostty-web baseline failure. The queued-message captures mount the real QueuedMessages in a local lab page, because the lab session page has no queue fixture. That page is not part of this PR.

@abhishekgahlot2
abhishekgahlot2 merged commit d601cc8 into useagenthq:main Oct 27, 2026
1 check passed
@abhishekgahlot2

Copy link
Copy Markdown
Contributor

Merged, thanks @tianxingjian2406, and thanks for the quick fix on both focus points.

@github-actions github-actions Bot locked and limited conversation to collaborators Oct 27, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Keep keyboard focus after Scroll to end or removing a queued message

3 participants