Description
Opening the new-session page (/) from the command palette leaves the prompt textarea unfocused, so the user has to reach for the mouse before typing. It breaks this all-keyboard journey:
- ⌘K from an active session page
- type
new
- "New chat" is the top result — hit Return
- the new-session prompt box should be focused, ready for the prompt
Today step 4 fails: focus lands on <body> and the first keystrokes go nowhere.
Root cause. The landing composer does focus itself — <textarea autoFocus> in NewChatLandingScreen (web/src/shell/NewChatDialog.tsx) — but the command palette takes it straight back. The palette is a Radix modal Dialog, and Radix's FocusScope keeps a document-level focusin trap installed until the dialog actually unmounts. The palette's close animation (data-closed:animate-out, ~150ms) delays that unmount well past the navigation, so:
- selection →
close() + navigate("/") — the route swaps immediately
- the landing screen mounts and
autoFocus focuses its textarea
- the still-installed trap sees a
focusin outside its container and yanks focus back into the (now off-page) palette
- the palette finishes animating and unmounts; focus lands on
<body>
This only reproduces in a real browser. jsdom runs no animations, so the trap releases in the same commit and a jsdom test never sees it. Verified with a Playwright test against a live server: the ⌘K → "new chat" journey fails, while the sidebar's "New session" link (no overlay in the way) already passes.
Same race, currently masked. Switching sessions from the palette focuses the in-session composer the same way — the conversationId bind effect in Composer (web/src/pages/ChatPage.tsx). That one happens to survive today only because session hydration delays the composer's mount past the palette's teardown. It's the same unguarded race, not a working design.
Related gap. The landing screen relies on autoFocus, which fires on mount only, so the per-project "New session" pencils (/?project=<name>) — which swap the query param while the screen stays mounted — leave the composer unfocused too.
Steps to reproduce
- Open the web UI and enter any session (
/c/<id>).
- Press ⌘K (Ctrl+K on Linux/Windows).
- Type
new and press Return.
- The new-session page loads. Type — nothing appears;
document.activeElement is <body>.
And on /, clicking a project's "New session" pencil leaves the composer unfocused.
Expected
Entering the new-session page by any path leaves the caret in the prompt textarea, so the flow works end to end without touching the mouse. Switching sessions from the palette should land in that session's composer for the same reason, and by design rather than by timing.
Version
main @ 7316bb8
OS
macOS 15 / Chromium. Not platform-specific — the cause is the dialog close animation, so it affects every browser.
Description
Opening the new-session page (
/) from the command palette leaves the prompt textarea unfocused, so the user has to reach for the mouse before typing. It breaks this all-keyboard journey:newToday step 4 fails: focus lands on
<body>and the first keystrokes go nowhere.Root cause. The landing composer does focus itself —
<textarea autoFocus>inNewChatLandingScreen(web/src/shell/NewChatDialog.tsx) — but the command palette takes it straight back. The palette is a Radix modalDialog, and Radix'sFocusScopekeeps a document-levelfocusintrap installed until the dialog actually unmounts. The palette's close animation (data-closed:animate-out, ~150ms) delays that unmount well past the navigation, so:close()+navigate("/")— the route swaps immediatelyautoFocusfocuses its textareafocusinoutside its container and yanks focus back into the (now off-page) palette<body>This only reproduces in a real browser. jsdom runs no animations, so the trap releases in the same commit and a jsdom test never sees it. Verified with a Playwright test against a live server: the ⌘K → "new chat" journey fails, while the sidebar's "New session" link (no overlay in the way) already passes.
Same race, currently masked. Switching sessions from the palette focuses the in-session composer the same way — the
conversationIdbind effect inComposer(web/src/pages/ChatPage.tsx). That one happens to survive today only because session hydration delays the composer's mount past the palette's teardown. It's the same unguarded race, not a working design.Related gap. The landing screen relies on
autoFocus, which fires on mount only, so the per-project "New session" pencils (/?project=<name>) — which swap the query param while the screen stays mounted — leave the composer unfocused too.Steps to reproduce
/c/<id>).newand press Return.document.activeElementis<body>.And on
/, clicking a project's "New session" pencil leaves the composer unfocused.Expected
Entering the new-session page by any path leaves the caret in the prompt textarea, so the flow works end to end without touching the mouse. Switching sessions from the palette should land in that session's composer for the same reason, and by design rather than by timing.
Version
main@ 7316bb8OS
macOS 15 / Chromium. Not platform-specific — the cause is the dialog close animation, so it affects every browser.