fix(native-chat): focus the composer after a keyboard switch between chats (#29152)

The previous tab's hidden composer still held focus when the reveal hook ran,
and was treated as a live text field, so the new composer never got focus.

Fixes #28157
This commit is contained in:
Kelvin Amoaba
2026-10-15 13:11:39 +00:00
committed by GitHub
parent bd62269d95
commit e8c7b32a90
2 changed files with 19 additions and 0 deletions
@@ -385,6 +385,21 @@ describe('useNativeChatComposerRevealFocus', () => {
outside.remove()
})
it('claims focus from the composer of the chat tab hidden by the same switch', () => {
const hiddenPane = document.createElement('div')
const previousComposer = document.createElement('textarea')
hiddenPane.appendChild(previousComposer)
document.body.appendChild(hiddenPane)
previousComposer.focus()
render({ isVisible: false, isFocusedGroup: true })
// Chromium leaves focus on the hidden field until a later task, after the reveal claim runs.
hiddenPane.setAttribute('inert', '')
render({ isVisible: true, isFocusedGroup: true })
drainFrames()
expect(container.querySelector('textarea')).toBe(document.activeElement)
hiddenPane.remove()
})
it('still claims focus from the covered xterm helper textarea', () => {
const helper = document.createElement('textarea')
helper.className = 'xterm-helper-textarea'
@@ -52,6 +52,10 @@ export function shouldPreserveEditableFocus(element: Element | null): boolean {
if (element.classList.contains('xterm-helper-textarea') || element.closest('.xterm')) {
return false
}
// Why: a field in a just-hidden pane keeps focus until Chromium drops it to body a task later.
if (element.closest('[hidden], [inert]')) {
return false
}
// Why: deferred fit/focus work can run after inline rename or settings
// fields take focus. Layout maintenance must not blur user edits closed.
return (