feat(sessions): focus composer when + reuses the untouched draft

When there are no pending changes, `+` reuses the active family's untouched
draft instead of creating a new session (unchanged). But when the reused draft
is the one already on screen, currentSessionId doesn't change, so nothing
navigated and the click gave no feedback. Bump a composerFocusRequest nonce in
the reuse branch and have SessionWrapper's focus effect depend on it, so the
composer re-focuses and the user can type right away.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Guilhem Lemouel
2026-07-14 12:14:39 +02:00
co-authored by Claude Opus 4.8
parent 6f6651d33a
commit e78cf5289f
2 changed files with 19 additions and 0 deletions
@@ -28,6 +28,7 @@
import SessionWorkspaceBar from './SessionWorkspaceBar.svelte'
import SessionChangesBar from './SessionChangesBar.svelte'
import {
composerFocusRequest,
createSession,
deleteSessionsForWorkspace,
getEffectiveWorkspaceId,
@@ -232,6 +233,11 @@
// loading.
let aiChat: AIChat | undefined = $state(undefined)
$effect(() => {
// Focus the composer when this session becomes active, or on an explicit
// focus request — the latter covers `+` reusing the untouched draft you're
// already viewing, where currentSessionId doesn't change so activation alone
// wouldn't re-run this.
void composerFocusRequest.nonce
if (sessionState.currentSessionId !== sessionId) return
if (!aiChat) return
if (!$copilotInfo.enabled) return
@@ -611,6 +611,16 @@ export function findSessionByName(name: string): Session | undefined {
return sessionState.sessions.find((s) => s.name === name)
}
// Bumped to ask the active session's composer to re-focus even when
// `currentSessionId` doesn't change — the `+` reuse path lands you back on the
// untouched draft you're already viewing, so nothing navigates, but the click
// should still drop the cursor in the composer. SessionWrapper's focus effect
// depends on `nonce`.
export const composerFocusRequest = $state<{ nonce: number }>({ nonce: 0 })
export function requestComposerFocus(): void {
composerFocusRequest.nonce++
}
export function createSession(): Session {
// Reuse an existing untouched draft from the active family rather than pile a
// blank entry on every `+`. "Untouched" is exactly `transient`: a pending
@@ -622,6 +632,9 @@ export function createSession(): Session {
const reusable = sessionState.sessions.find((s) => s.transient && sessionInCurrentFamily(s))
if (reusable) {
sessionState.currentSessionId = reusable.id
// Reusing an already-active draft doesn't change currentSessionId, so ask
// the composer to focus explicitly — the caller still navigates/redirects.
requestComposerFocus()
return reusable
}
sessionState.sessions = sessionState.sessions.filter((s) => !s.transient)