From 7ff4d2a6a4311dd08b672a72f5951efc8fca1aa4 Mon Sep 17 00:00:00 2001 From: Matt Toohey Date: Wed, 8 Apr 2026 14:26:21 +1000 Subject: [PATCH] fix: highlight project row in sidebar for new conversations, clear for standalone drafts (#116) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: highlight project row in sidebar when starting new chat in a project When starting a new conversation with a project selected, the sidebar now highlights the project row instead of leaving the previous session highlighted (or showing no highlight at all). The root cause: draft sessions are excluded from the sidebar's session list, so the activeSessionRefCallback never fires for them. The highlight useEffect would early-return when activeSessionId was set, leaving a stale highlight. The fix computes an activeProjectId when the active session is a draft belonging to a project, and uses an activeProjectRefCallback to position the sliding highlight on the parent project row. * Clear sidebar highlight for standalone draft sessions When starting a new conversation without a project, the sidebar now shows no highlight instead of keeping the previous selection. The highlight logic now distinguishes three draft cases: - Draft in project → highlight the project row (previous commit) - Standalone draft → clear the highlight entirely (this commit) - Non-draft session → ref callback positions the highlight as before --- ui/goose2/scripts/check-file-sizes.mjs | 9 ++-- ui/goose2/src/features/sidebar/ui/Sidebar.tsx | 44 +++++++++++++------ .../sidebar/ui/SidebarProjectsSection.tsx | 21 ++++++++- 3 files changed, 54 insertions(+), 20 deletions(-) diff --git a/ui/goose2/scripts/check-file-sizes.mjs b/ui/goose2/scripts/check-file-sizes.mjs index a80012b342..559bd719c0 100644 --- a/ui/goose2/scripts/check-file-sizes.mjs +++ b/ui/goose2/scripts/check-file-sizes.mjs @@ -11,9 +11,9 @@ const EXCEPTIONS = { "Temporary allowance while project creation, home/chat handoff, session-history routing, and drag-drop wiring remain in the shell.", }, "src/features/sidebar/ui/SidebarProjectsSection.tsx": { - limit: 545, + limit: 560, justification: - "Added drag-and-drop handlers for moving chats between projects.", + "Drag-and-drop handlers plus activeProjectId highlight for draft-in-project sessions.", }, "src/features/chat/ui/ChatView.tsx": { limit: 505, @@ -21,8 +21,9 @@ const EXCEPTIONS = { "Added useSessionAutoTitle hook call for smart session naming.", }, "src/features/sidebar/ui/Sidebar.tsx": { - limit: 575, - justification: "Added search-as-you-type filtering for sidebar sessions.", + limit: 580, + justification: + "Search-as-you-type filtering plus draft-aware sidebar highlight logic.", }, "src-tauri/src/services/acp/manager.rs": { limit: 520, diff --git a/ui/goose2/src/features/sidebar/ui/Sidebar.tsx b/ui/goose2/src/features/sidebar/ui/Sidebar.tsx index e1f604e444..2fdbbf1738 100644 --- a/ui/goose2/src/features/sidebar/ui/Sidebar.tsx +++ b/ui/goose2/src/features/sidebar/ui/Sidebar.tsx @@ -260,12 +260,8 @@ export function Sidebar({ return () => window.removeEventListener("keydown", handler); }, []); - const toggleProject = (projectId: string) => { - setExpandedProjects((prev) => ({ - ...prev, - [projectId]: !prev[projectId], - })); - }; + const toggleProject = (projectId: string) => + setExpandedProjects((prev) => ({ ...prev, [projectId]: !prev[projectId] })); const navRef = useRef(null); const homeRef = useRef(null); @@ -279,12 +275,20 @@ export function Sidebar({ updateActiveRect, } = useSidebarHighlight(navRef); - // Update active rect when activeView/activeSessionId changes + // Drafts aren't in the sidebar — highlight project row or clear highlight. + const activeDraft = activeSessionId + ? sessions.find((s) => s.id === activeSessionId && s.draft) + : undefined; + const activeProjectId = activeDraft?.projectId ?? null; + + // Position the sliding highlight based on what's active. + // Non-draft sessions and draft-in-project are handled by ref callbacks. useEffect(() => { - if (activeSessionId) { + if (activeDraft) { + if (!activeProjectId) updateActiveRect(null); return; } - + if (activeSessionId) return; if (activeView === "home") { updateActiveRect(homeRef.current); } else if (activeView && navItemRefs.current[activeView]) { @@ -292,17 +296,27 @@ export function Sidebar({ } else { updateActiveRect(null); } - }, [activeSessionId, activeView, updateActiveRect]); + }, [ + activeSessionId, + activeDraft, + activeProjectId, + activeView, + updateActiveRect, + ]); - // Callback for SidebarProjectsSection to register active session refs + // Ref callbacks to position the highlight on active session or project row. const activeSessionRefCallback = useCallback( (el: HTMLElement | null) => { - if (activeSessionId && el) { - updateActiveRect(el); - } + if (activeSessionId && el) updateActiveRect(el); }, [activeSessionId, updateActiveRect], ); + const activeProjectRefCallback = useCallback( + (el: HTMLElement | null) => { + if (activeProjectId && el) updateActiveRect(el); + }, + [activeProjectId, updateActiveRect], + ); return (
)} diff --git a/ui/goose2/src/features/sidebar/ui/SidebarProjectsSection.tsx b/ui/goose2/src/features/sidebar/ui/SidebarProjectsSection.tsx index ff025e01d1..5b7d842e47 100644 --- a/ui/goose2/src/features/sidebar/ui/SidebarProjectsSection.tsx +++ b/ui/goose2/src/features/sidebar/ui/SidebarProjectsSection.tsx @@ -46,6 +46,7 @@ interface SidebarProjectsSectionProps { labelTransition: string; labelVisible: boolean; activeSessionId?: string | null; + activeProjectId?: string | null; onNavigate?: (view: AppView) => void; onSelectSession?: (sessionId: string) => void; onNewChatInProject?: (projectId: string) => void; @@ -58,6 +59,7 @@ interface SidebarProjectsSectionProps { onMoveToProject?: (sessionId: string, projectId: string | null) => void; onItemMouseEnter?: (e: React.MouseEvent) => void; activeSessionRefCallback?: (el: HTMLElement | null) => void; + activeProjectRefCallback?: (el: HTMLElement | null) => void; } function ItemMenu({ label, @@ -112,6 +114,7 @@ function ProjectSection({ isExpanded, toggleProject, activeSessionId, + activeProjectId, onSelectSession, onNewChatInProject, onNavigate, @@ -122,12 +125,14 @@ function ProjectSection({ onMoveToProject, onItemMouseEnter, activeSessionRefCallback, + activeProjectRefCallback, }: { project: ProjectInfo; projectChats: TabInfo[]; isExpanded: boolean; toggleProject: (projectId: string) => void; activeSessionId?: string | null; + activeProjectId?: string | null; onSelectSession?: (sessionId: string) => void; onNewChatInProject?: (projectId: string) => void; onNavigate?: (view: AppView) => void; @@ -138,6 +143,7 @@ function ProjectSection({ onMoveToProject?: (sessionId: string, projectId: string | null) => void; onItemMouseEnter?: (e: React.MouseEvent) => void; activeSessionRefCallback?: (el: HTMLElement | null) => void; + activeProjectRefCallback?: (el: HTMLElement | null) => void; }) { const [showAll, setShowAll] = useState(false); const [dragOver, setDragOver] = useState(false); @@ -180,7 +186,12 @@ function ProjectSection({ onDrop={handleDrop} > {/* Project row */} -
+