Skip to content

Accept collaborative @ mentions with Tab - #1301

Merged
Paul Lizer (paullizer) merged 2 commits into
Developmentfrom
paullizer-fix-1299-mention-tab-autocomplete
Aug 19, 2026
Merged

Accept collaborative @ mentions with Tab#1301
Paul Lizer (paullizer) merged 2 commits into
Developmentfrom
paullizer-fix-1299-mention-tab-autocomplete

Conversation

@paullizer

@paullizer Paul Lizer (paullizer) commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Fixes #1299

What was broken

In multi-user (collaborative) conversations, typing @ in the chat composer opens the participant / agent suggestion menu. Arrow keys moved the highlight and Enter accepted the highlighted entry, but Tab did nothing — it fell through to the browser default, moved focus out of the message box, and left the half-typed @par text behind.

The agent-instruction mention menu (static/js/agent_instruction_mentions.js) already treated Tab and Enter as the same "accept" action, so the chat composer was the odd one out.

What changed

Tab support in handleComposerKeydown() (static/js/chat/chat-collaboration.js):

  • Extracted the inline Enter selection body into a shared selectActiveMentionSuggestion() helper, so every accept key routes through one implementation of the three suggestion kinds (participant tag, agent/model ai_tag, invite confirmation) instead of duplicating the branching.
  • Added a Tab branch that reuses the helper, calls preventDefault() so focus stays in the composer, and returns true.
  • Added a hasActiveMentionSuggestion() guard that runs before preventDefault(), so Tab is only captured when there is something to accept.
  • Shift+Tab and the "no matching participants" empty state are deliberately left unhandled, so normal focus movement still works.
  • The Enter guard is unchanged, so unrelated Enter presses still send the message.
Key Menu open with results Menu open, no results Menu closed
Tab Accepts the highlighted suggestion, focus stays in the composer Normal focus movement Normal focus movement
Shift+Tab Normal focus movement Normal focus movement Normal focus movement
Enter Accepts the highlighted suggestion (unchanged) Sends the message (unchanged) Sends the message (unchanged)

Adjacent accessibility fix found in the same code path: #collaboration-mention-menu is declared role="listbox" in chats.html, but its items were plain buttons with no role="option" / aria-selected, so screen readers could not announce which suggestion was highlighted.

  • Suggestions are now real options with stable ids (collaboration-mention-option-{index}) and aria-selected.
  • The composer references the highlighted option via aria-activedescendant, paired with aria-controls and aria-autocomplete="list". ARIA 1.2 only resolves aria-activedescendant from a focused textbox when the option lives inside the element named by aria-controls, and the menu is a sibling of #user-input, not a descendant. All three attributes are applied only while the menu is open and removed when it closes, so the composer stays a plain textbox the rest of the time.
  • The highlighted option is scrolled into view inside the height-capped (max-height: 240px) menu, so arrowing through a long list no longer highlights an off-screen entry.

Validation

Before / after, measured by running the real shipped functions from chat-collaboration.js against a jsdom document (pre-fix source pulled from git show HEAD):

Observation Before After
Mention menu handles Tab false true
Tab prevents default focus movement No Yes
Suggestions inserted on Tab 0 1
Suggestions inserted on Enter 1 1 (unchanged)
Suggestions exposed with role="option" 0 1 per suggestion
aria-activedescendant on the composer absent tracks the highlighted option
  • functional_tests/test_collaboration_mention_tab_autocomplete.py5/5 passed. Parses the real function bodies out of the module and asserts the Tab branch, the shiftKey guard, the guard-before-preventDefault() ordering, the shared selection path, and the ARIA wiring. Run against the pre-fix source it fails 3/5, so it is a real regression test.
  • ui_tests/test_chat_collaboration_mention_tab_selection.py — new Playwright regression test covering Tab, ArrowDown+Tab, Enter, Shift+Tab, focus retention, and the listbox ARIA. Skips without SIMPLECHAT_UI_BASE_URL / SIMPLECHAT_UI_STORAGE_STATE.
  • 54/54 passed across the existing collaboration and chat functional tests that read chat-collaboration.js — no regressions, verified again after merging Development.

Notes

  • No backend, route, or auth surface is touched, so no route policy test updates were needed.
  • No new browser assets; all changes are in existing local static JavaScript.
  • Version is 0.260.005. Development moved ahead mid-review and Reset the conversation Documents pane on New chat #1300 claimed 0.260.004, so this branch merged Development and rebumped; the release notes now have a 0.260.005 section above the merged 0.260.004 entry.

Known-unrelated red marks

  • check-release-notes CI failure is not caused by this PR. Its Validate release notes update step passes; the job fails on the follow-up Post PR comment (when latest features likely needed but missing) step with HttpError: Resource not accessible by integration (403). Reset the conversation Documents pane on New chat #1300 fails the same check for the same reason. The reminder itself is documented as non-blocking, and no Latest Features card is warranted here — that catalog is a major-release showcase (release_250_* entries such as Agents Catalog and Workflows), not a home for a keyboard-shortcut bug fix.
  • functional_tests/test_conversation_contents_drawer_settings.py::test_user_preference_route_persists_boolean_and_rejects_invalid_type fails with name 'AI_NOTICE_USER_SETTINGS_KEY' is not defined. Reproduced on a clean origin/Development worktree, so it is pre-existing and unrelated.
  • ui_tests/test_workspace_document_selection_controls.py:46 calls .first() instead of the .first property, so that test errors out before its assertions run. Pre-existing, spotted during review, deliberately not fixed here — worth its own issue.

In multi-user conversations the chat composer's @ mention menu only accepted
the highlighted suggestion on Enter. Tab fell through to the browser default,
moving focus out of the message box and leaving the half-typed @name behind.
The agent-instruction mention menu already treated Tab and Enter the same way,
so the chat composer was the odd one out.

Extract the inline Enter selection logic into selectActiveMentionSuggestion()
so every accept key routes through one implementation of the participant tag,
ai_tag invocation target, and invite-confirmation branches, then add a Tab
branch that reuses it. Shift+Tab and the empty-results state are deliberately
left unhandled so normal focus movement still works, and the Enter guard is
unchanged.

Also fix the listbox semantics in the same code path: suggestions are now real
options with stable ids and aria-selected, and the composer references the
highlighted option via aria-activedescendant paired with aria-controls, which
ARIA 1.2 requires because the menu is a sibling of the textarea rather than a
descendant. Those attributes are applied only while the menu is open. The
highlighted option is scrolled into view inside the height-capped menu.

Fixes #1299

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
PR #1300 landed on Development and claimed 0.260.004, so this branch moves to
0.260.005 and its release notes entries move into a new 0.260.005 section above
the merged 0.260.004 entry. The fixes index keeps both new entries.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@paullizer
Paul Lizer (paullizer) merged commit cca077a into Development Aug 19, 2026
11 of 14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant