Skip to content

fix(web): closing a composer menu on a touch tablet no longer opens the keyboard - #36325

Merged
juliusmarminge merged 2 commits into
mainfrom
factory/issue-26447-a1794597635
Nov 13, 2026
Merged

juliusmarminge merged 2 commits into
mainfrom
factory/issue-26447-a1794597635

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Nov 13, 2026 •

Copy link
Copy Markdown
Member

Interim: no change was required this round and nothing was pushed; the branch is still at a77f632. PR #36325 is open on that head with its checks queued or in progress (no failures, no Macroscope or CodeRabbit findings yet). I called watch_pull_request for it and ended the turn; the watch is still on, so this is not the final result. The fix itself is unchanged: useComposerMenuProps() in apps/web/src/components/chat/composerEventScope.ts leaves finalFocus unset on a coarse pointer, so composer menus return focus to their own trigger instead of calling focusAtEnd() on the composer during the closing tap.

Fixes #26447

Reproduction on main

Commit origin/main 9efd960. Started T3CODE_DEV_SEED=0 vp run dev on a fresh database, added a git project and two Replay (dev) threads via the server CLI, paired a Playwright Chromium context emulating an Android tablet in the desktop layout (1163x720, hasTouch, isMobile so (pointer: coarse) matches, Android Chrome UA) and logged every focus() call with navigator.userActivation.isActive and a stack. Results: (1) tapping a sidebar thread 3 times: composer NOT focused (activeElement stays the sidebar row card); already fixed by #29496. (2) opening the model picker: its search input gets focus (reporter flags this as possibly intended). (3) tapping outside the picker: no composer focus. (4) picking an access mode (Supervised) in the picker footer's Permissions menu: no composer focus. (5) picking a Codex effort level: no composer focus. (6) open the model picker, open its footer Permissions menu (Full access chip), tap outside: the composer editor (data-testid=composer-editor) receives focus() with userActivation.isActive=true, so on a real Android tablet Chrome would raise the on-screen keyboard. The stack goes from Base UI FloatingFocusManager getReturnElement to useComposerMenuProps().finalFocus (composerEventScope.ts) to ChatComposer focusAtEnd to the Tiptap EditorView.focus. Not checked on a real device's keyboard (emulation only).

Evidence

Real web app, Chromium Android tablet emulation, 1163×720, touch enabled, coarse pointer, light theme, fresh synthetic Replay data on each commit. Open the model picker → open footer Permissions → click outside → wait for dismissal. The overlay observes focus without changing its behavior.

Before — 9efd960925 After — a77f632a36
Result Composer focused; one focus call during user activation Composer unfocused; zero focus calls
Permissions open Before: Permissions open After: Permissions open
After dismissal Before: composer focused After: composer unfocused
Recording Before recording After recording

Scenario source · Tablet runner setup

scenarios:prove exited 0: failsOnBase=true, passesOnHead=true. Actual Android keyboard visibility remains unverified.

Not shown:

  • Actual Android on-screen keyboard visibility: this proof observes the reported focus trigger in Chromium tablet emulation, without an Android device.
  • Sidebar thread switching, access-mode selection, and Codex effort selection were not exercised; the scenario targets the remaining nested-menu dismissal reproduction.

Tests

None this round. Read the PR state and check runs over REST: PR open, mergeable_state blocked, checks queued or in progress, 'Open PR limit', 'Prepare PR size config' and 'Agent context budget' passed.

Not in this change

  • Opening the model picker by touch still autofocuses its search input (ProviderModelPicker initialFocus={searchInputRef}), which can itself request the keyboard on Android. The issue reporter flagged this as possibly intended and this brief scopes the bug to the close path, so it is unchanged; it is the finding the first attempt could not settle.
  • Other programmatic composer focus calls with no coarse-pointer check (CommandPalette, useQueuedRunEdit, useThreadLifecycle, useTimelineRewind, useComposerStash, ChatView line ~1837) were not audited or changed.
  • On phones (also coarse pointer) composer menus now also return focus to their trigger rather than running the composer finalFocus callback; I did not verify how the phone layout behaved before, only that the brief says it already did not focus the composer.
  • Opening the model picker by touch still autofocuses its search input (ProviderModelPicker initialFocus); unchanged, as in the first round.

Who did the work

  • Written by Claude Fable 5.2, in Claude Code
  • Reviewed by GPT-6.1 Sol, in Codex, and by Claude Opus 5.5, each on its own
  • Proof captured by GPT-6.1 Sol and checked by Claude Opus 5.5

Run by the t3code factory in T3 Code.

juliusmarminge and others added 2 commits November 13, 2026 19:45
…ablet focuses the composer

Failing reproduction for #26447.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he keyboard

Composer menus return focus to the editor when they close. On a touch
device that focus lands inside the tap's user activation, so Chrome on
Android raised the on-screen keyboard, for example after tapping outside
the model picker once its Permissions menu had been opened.

useComposerMenuProps now leaves finalFocus unset on a coarse pointer, so
the menu returns focus to its own trigger, matching the coarse-pointer
check ChatView already applies to thread open and window focus.

Fixes #26447

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted Author is trusted by repo permissions or the VOUCHED list. size:XS 0-9 changed lines (additions + deletions). labels Nov 13, 2026
@macroscopeapp

macroscopeapp Bot commented Nov 13, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at a77f632

Macroscope's review found this PR approvable — Composer menus on touch devices retain their normal focus return instead of focusing the editor and opening the keyboard. The change is confined to transient UI focus, with desktop behavior preserved.

You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

Composer menus now omit the focus-return callback on coarse pointers. A touch-tablet browser test checks that dismissing the nested Permissions menu and picker does not focus the composer editor.

Changes: Composer menu focus

Layer / File(s) Summary
Coarse-pointer menu focus handling
apps/web/src/components/chat/composerEventScope.ts, apps/web/src/components/chat/ComposerPermissionsSection.browser.test.tsx
useComposerMenuProps provides the existing focus-return callback only when a composer reference exists and the pointer is not coarse. A browser test checks that outside taps close the nested menu and picker without calling the editor focus handler.

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: t3dotgg

Merge Risk: Minimal · up to a77f6

Composer menus no longer return focus to the composer on touch devices, which avoids opening the on-screen keyboard when a menu closes. The change is small and covered by a browser test, so merge risk is minimal.

Pre-merge checks: Passed 3 · Inconclusive 1

Failed Failed checks: 1 inconclusive

Check name Status Explanation Resolution
Linked Issues check Inconclusive [#26447] The diff at the reviewed head changes only composerEventScope.ts. On a coarse pointer, useComposerMenuProps() leaves finalFocus undefined, so composer menus no longer call composerRef.focusAt… Provide: (a) confirmation, from code at the reviewed head, that the thread-switch path (ChatView coarse-pointer rule from #29496) avoids composer focus on coarse pointers; (b) a maintainer decision on whether the model-picker search autofoc…
Passed Passed checks: 3 passed

Check name Status Explanation
Title check Passed The title names the actual fix: closing a composer menu on a touch tablet no longer opens the on-screen keyboard. It follows the conventional commit format and matches the change in composerEventScope…
Out of Scope Changes check Passed The whole-PR diff against the merge base contains two files: composerEventScope.ts (the coarse-pointer guard in the shared hook) and ComposerPermissionsSection.browser.test.tsx (the regression test fo…
Description check Passed The description is detailed and covers the Problem (focus moves to the composer when a nested menu closes on a coarse pointer, with reproduction steps on main), the Change (coarse-pointer guard in use…
Full details: Title check

Explanation

The title names the actual fix: closing a composer menu on a touch tablet no longer opens the on-screen keyboard. It follows the conventional commit format and matches the change in composerEventScope.ts, which skips the composer finalFocus callback on coarse pointers. It is specific and clear. The title does not mention the model-picker search autofocus, which the PR leaves unchanged, but the title does not claim to fix it.

Full details: Out of Scope Changes check

Explanation

The whole-PR diff against the merge base contains two files: composerEventScope.ts (the coarse-pointer guard in the shared hook) and ComposerPermissionsSection.browser.test.tsx (the regression test for the touch-tablet outside-tap case). The hook change is in the same focus-return path that #26447 reports. The test file supports the change. The change applies to all callers of useComposerMenuProps, including Permissions, compact controls, and branch toolbar menus. These callers share the reported bug, so the wider reach is connected to the issue. The PR description notes that phone (coarse pointer) menus now return focus to their trigger. This is a behavior change on the same coarse-pointer rule, not an unrelated change. No unrelated change was found.

Full details: Description check

Explanation

The description is detailed and covers the Problem (focus moves to the composer when a nested menu closes on a coarse pointer, with reproduction steps on main), the Change (coarse-pointer guard in useComposerMenuProps so finalFocus stays unset), Scope (links fix for #26447 and explains the scope is the menu close path), and Verification (before/after screenshots, recordings, and a scenario run exiting 0 with failsOnBase=true and passesOnHead=true). It also states what was not verified (no real Android device). Gaps: it is long and contains repeated content (the model-picker autofocus note appears twice under Not in this change), the Interim status preamble is not part of the template, the Scope section does not state why this is a bug fix needing no separate issue (it links an issue, which satisfies the requirement), and the closing attribution is present as the template requests. The title is not in the provided text, so conventional-commit title compliance cannot be verified. The core template sections are all filled in with substantive content, so the check passes.

Full details: Linked Issues check

Explanation

[#26447] The diff at the reviewed head changes only composerEventScope.ts. On a coarse pointer, useComposerMenuProps() leaves finalFocus undefined, so composer menus no longer call composerRef.focusAtEnd() on close. This addresses the reported menu-close path (access-mode menu, tapped outside), which the issue lists as a keyboard-opening case. Three parts of the issue are not established: (1) Thread-switch focus is said to be fixed by #29496. This claim was not verified in the code at the reviewed head. (2) Opening the model picker still focuses the search input (ProviderModelPicker initialFocus={searchInputRef}). The issue expects no keyboard until the user taps the composer, but it also says this row may be intended and shows the same behavior in the mobile layout. (3) Composer test evidence (1 failed before, 7 passed after) is author-reported. I did not run the tests. The PR description's Evidence section is still marked as being captured.

Resolution

Provide: (a) confirmation, from code at the reviewed head, that the thread-switch path (ChatView coarse-pointer rule from #29496) avoids composer focus on coarse pointers; (b) a maintainer decision on whether the model-picker search autofocus on touch is in scope for #26447; (c) the test run output for ComposerPermissionsSection.browser.test.tsx at the reviewed head, or a passing CI result for that file.

Finishing Touches

Generate unit tests (beta)

  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Recent review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between c83e78f and a77f632.

Files selected for processing: 2

  • apps/web/src/components/chat/ComposerPermissionsSection.browser.test.tsx
  • apps/web/src/components/chat/composerEventScope.ts

Limit details: You’ve used all 10 included reviews currently available.

@juliusmarminge
juliusmarminge merged commit fa6326b into main Nov 13, 2026
37 checks passed
@juliusmarminge
juliusmarminge deleted the factory/issue-26447-a1794597635 branch November 13, 2026 20:15
Pipyakas pushed a commit to Pipyakas/t3code that referenced this pull request Nov 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XS 0-9 changed lines (additions + deletions). vouch:trusted Author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Thread switches and composer menus open the on-screen keyboard on Android tablets

1 participant