fix(sidebar): add project sort modes (#2067)

* fix(sidebar): add project sort modes

* fix(sidebar): preserve manual sort order

* fix(sidebar): move sort state before render

---------

Co-authored-by: bashrusakh <bashrusakh@users.noreply.github.com>
This commit is contained in:
Leonid
2026-07-11 14:43:57 +03:00
committed by GitHub
co-authored by bashrusakh
parent f6326e1c35
commit bfaf62e222
16 changed files with 256 additions and 18 deletions
@@ -921,6 +921,9 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
color?: string;
iconImage?: { mime: string; updatedAt: number; source: 'custom' | 'auto' };
iconBackground?: string;
addedAt?: number;
lastOpenedAt?: number;
sidebarCollapsed?: boolean;
}>;
}, [projects]);
@@ -1019,13 +1022,84 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
lastRepoStatusRef.current = Boolean(projectRepoStatus.get(activeProjectId));
}
const showRecentSection = useSessionDisplayStore((state) => state.showRecentSection);
const showArchivedSessions = useSessionDisplayStore((state) => state.showArchivedSessions);
const projectSortOrder = useSessionDisplayStore((state) => state.projectSortOrder);
const manualProjectOrder = useProjectsStore((state) => state.manualProjectOrder);
const recentProjectIds = React.useMemo(() => {
const recentSessions = deriveRecentSessions(sessions);
if (recentSessions.length === 0) return new Set<string>();
const pathToId = new Map<string, string>();
for (const project of normalizedProjects) {
if (project.normalizedPath) {
pathToId.set(project.normalizedPath, project.id);
}
}
const ids = new Set<string>();
for (const session of recentSessions) {
const directory = normalizePath((session as Session & { directory?: string | null }).directory ?? null);
if (directory) {
const projectId = pathToId.get(directory);
if (projectId) ids.add(projectId);
}
}
return ids;
}, [sessions, normalizedProjects]);
const sortedProjects = React.useMemo(() => {
const list = [...normalizedProjects];
switch (projectSortOrder) {
case 'a-z':
list.sort((a, b) => {
const aLabel = (a.label || a.path).toLowerCase();
const bLabel = (b.label || b.path).toLowerCase();
return aLabel.localeCompare(bLabel);
});
break;
case 'z-a':
list.sort((a, b) => {
const aLabel = (a.label || a.path).toLowerCase();
const bLabel = (b.label || b.path).toLowerCase();
return bLabel.localeCompare(aLabel);
});
break;
case 'date-added':
list.sort((a, b) => (b.addedAt ?? 0) - (a.addedAt ?? 0));
break;
case 'recent':
list.sort((a, b) => (b.lastOpenedAt ?? 0) - (a.lastOpenedAt ?? 0));
break;
case 'manual': {
const orderMap = new Map(manualProjectOrder.map((id, i) => [id, i]));
list.sort((a, b) => {
const ai = orderMap.get(a.id) ?? Infinity;
const bi = orderMap.get(b.id) ?? Infinity;
return ai - bi;
});
break;
}
}
if (projectSortOrder === 'manual') {
return list;
}
const recent = list.filter((p) => recentProjectIds.has(p.id));
const rest = list.filter((p) => !recentProjectIds.has(p.id));
return [...recent, ...rest];
}, [normalizedProjects, projectSortOrder, manualProjectOrder, recentProjectIds]);
const {
projectSections,
groupSearchDataByGroup,
sectionsForRender,
searchMatchCount,
} = useSessionSidebarSections({
normalizedProjects,
normalizedProjects: sortedProjects,
getSessionsForProject,
getArchivedSessionsForProject,
availableWorktreesByProject,
@@ -1131,9 +1205,6 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
return meta;
}, [projectSections, homeDirectory]);
const showRecentSection = useSessionDisplayStore((state) => state.showRecentSection);
const showArchivedSessions = useSessionDisplayStore((state) => state.showArchivedSessions);
const activeNowSessions = React.useMemo(() => {
if (!showRecentSection || isVSCode) {
return [];
@@ -1636,6 +1707,7 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
removeProject={removeProject}
projectHeaderSentinelRefs={projectHeaderSentinelRefs}
reorderProjects={reorderProjects}
projectSortOrder={projectSortOrder}
getOrderedGroups={getOrderedGroups}
setGroupOrderByProject={setGroupOrderByProject}
openSidebarMenuKey={openSidebarMenuKey}