From e78cf5289f59892c2567cb64fea807e70dcf559a Mon Sep 17 00:00:00 2001 From: Guilhem Lemouel Date: Tue, 14 Jul 2026 12:14:39 +0200 Subject: [PATCH] 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) --- .../lib/components/sessions/SessionWrapper.svelte | 6 ++++++ .../lib/components/sessions/sessionState.svelte.ts | 13 +++++++++++++ 2 files changed, 19 insertions(+) diff --git a/frontend/src/lib/components/sessions/SessionWrapper.svelte b/frontend/src/lib/components/sessions/SessionWrapper.svelte index c2e8d1d92d..4f94dc2888 100644 --- a/frontend/src/lib/components/sessions/SessionWrapper.svelte +++ b/frontend/src/lib/components/sessions/SessionWrapper.svelte @@ -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 diff --git a/frontend/src/lib/components/sessions/sessionState.svelte.ts b/frontend/src/lib/components/sessions/sessionState.svelte.ts index 1edd938de0..d7e169886d 100644 --- a/frontend/src/lib/components/sessions/sessionState.svelte.ts +++ b/frontend/src/lib/components/sessions/sessionState.svelte.ts @@ -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)