mirror of
https://github.com/block/goose.git
synced 2026-07-17 12:56:20 +02:00
fix: highlight project row in sidebar for new conversations, clear for standalone drafts (#116)
* 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
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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<HTMLElement>(null);
|
||||
const homeRef = useRef<HTMLButtonElement>(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 (
|
||||
<div
|
||||
@@ -538,6 +552,7 @@ export function Sidebar({
|
||||
labelTransition={labelTransition}
|
||||
labelVisible={labelVisible}
|
||||
activeSessionId={activeSessionId}
|
||||
activeProjectId={activeProjectId}
|
||||
onNavigate={onNavigate}
|
||||
onSelectSession={onSelectSession}
|
||||
onNewChatInProject={onNewChatInProject}
|
||||
@@ -550,6 +565,7 @@ export function Sidebar({
|
||||
onMoveToProject={onMoveToProject}
|
||||
onItemMouseEnter={onItemMouseEnter}
|
||||
activeSessionRefCallback={activeSessionRefCallback}
|
||||
activeProjectRefCallback={activeProjectRefCallback}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -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<HTMLElement>) => 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<HTMLElement>) => 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 */}
|
||||
<div className="relative flex items-center group rounded-md transition-colors duration-200">
|
||||
<div
|
||||
ref={
|
||||
activeProjectId === project.id ? activeProjectRefCallback : undefined
|
||||
}
|
||||
className="relative flex items-center group rounded-md transition-colors duration-200"
|
||||
>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
@@ -189,7 +200,9 @@ function ProjectSection({
|
||||
onMouseEnter={onItemMouseEnter}
|
||||
className={cn(
|
||||
"flex-1 min-w-0 justify-start gap-2 rounded-md px-3 py-2 text-[13px] font-light",
|
||||
PROJECT_ROW_TEXT_CLASS,
|
||||
activeProjectId === project.id
|
||||
? "text-foreground hover:bg-transparent hover:text-foreground group-hover:text-foreground"
|
||||
: PROJECT_ROW_TEXT_CLASS,
|
||||
)}
|
||||
>
|
||||
<span className="relative flex h-3 w-3 flex-shrink-0 items-center justify-center">
|
||||
@@ -302,6 +315,7 @@ export function SidebarProjectsSection({
|
||||
labelTransition,
|
||||
labelVisible,
|
||||
activeSessionId,
|
||||
activeProjectId,
|
||||
onNavigate,
|
||||
onSelectSession,
|
||||
onNewChatInProject,
|
||||
@@ -314,6 +328,7 @@ export function SidebarProjectsSection({
|
||||
onMoveToProject,
|
||||
onItemMouseEnter,
|
||||
activeSessionRefCallback,
|
||||
activeProjectRefCallback,
|
||||
}: SidebarProjectsSectionProps) {
|
||||
const [recentsDragOver, setRecentsDragOver] = useState(false);
|
||||
|
||||
@@ -420,6 +435,7 @@ export function SidebarProjectsSection({
|
||||
isExpanded={expandedProjects[project.id] ?? false}
|
||||
toggleProject={toggleProject}
|
||||
activeSessionId={activeSessionId}
|
||||
activeProjectId={activeProjectId}
|
||||
onSelectSession={onSelectSession}
|
||||
onNewChatInProject={onNewChatInProject}
|
||||
onNavigate={onNavigate}
|
||||
@@ -430,6 +446,7 @@ export function SidebarProjectsSection({
|
||||
onMoveToProject={onMoveToProject}
|
||||
onItemMouseEnter={onItemMouseEnter}
|
||||
activeSessionRefCallback={activeSessionRefCallback}
|
||||
activeProjectRefCallback={activeProjectRefCallback}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user