feat: improve session sidebar archive and pagination controls

Add a toggle for displaying archived sessions in the sidebar menu
Load more sessions in smaller increments
Reset expanded session counts when collapsing groups or projects
This commit is contained in:
Bohdan Triapitsyn
2026-06-02 13:25:28 +03:00
parent 6a88cd09cc
commit 2f3d5107e8
12 changed files with 99 additions and 31 deletions
@@ -176,7 +176,7 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
const [collapsedProjects, setCollapsedProjects] = React.useState<Set<string>>(new Set());
const [projectRepoStatus, setProjectRepoStatus] = React.useState<Map<string, boolean | null>>(new Map());
const [expandedSessionGroups, setExpandedSessionGroups] = React.useState<Set<string>>(new Set());
const [visibleSessionCountByGroup, setVisibleSessionCountByGroup] = React.useState<Map<string, number>>(new Map());
const [newWorktreeDialogOpen, setNewWorktreeDialogOpen] = React.useState(false);
const [updateDialogOpen, setUpdateDialogOpen] = React.useState(false);
const [openSidebarMenuKey, setOpenSidebarMenuKey] = React.useState<string | null>(null);
@@ -681,20 +681,43 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
[collapsedFolderIds, toggleFolderCollapse, createFolder, t],
);
const toggleGroupSessionLimit = React.useCallback((groupId: string) => {
setExpandedSessionGroups((prev) => {
const next = new Set(prev);
if (next.has(groupId)) {
next.delete(groupId);
} else {
next.add(groupId);
}
const showMoreGroupSessions = React.useCallback((groupId: string, currentVisibleCount: number) => {
setVisibleSessionCountByGroup((prev) => {
const next = new Map(prev);
next.set(groupId, currentVisibleCount + 7);
return next;
});
}, []);
const resetGroupSessionLimit = React.useCallback((groupId: string) => {
setVisibleSessionCountByGroup((prev) => {
if (!prev.has(groupId)) {
return prev;
}
const next = new Map(prev);
next.delete(groupId);
return next;
});
}, []);
const resetProjectSessionLimits = React.useCallback((projectId: string) => {
setVisibleSessionCountByGroup((prev) => {
let changed = false;
const next = new Map(prev);
const projectGroupPrefix = `${projectId}:`;
for (const groupId of next.keys()) {
if (groupId.startsWith(projectGroupPrefix)) {
next.delete(groupId);
changed = true;
}
}
return changed ? next : prev;
});
}, []);
const collapseAllProjects = React.useCallback(() => {
ignoreIntersectionUntil.current = Date.now() + 150;
setVisibleSessionCountByGroup(new Map());
setCollapsedProjects(() => {
const allIds = new Set(projects.map((p) => p.id));
try {
@@ -709,6 +732,7 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
const expandAllProjects = React.useCallback(() => {
ignoreIntersectionUntil.current = Date.now() + 150;
setVisibleSessionCountByGroup(new Map());
setCollapsedProjects(() => {
const empty = new Set<string>();
try {
@@ -724,6 +748,7 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
const toggleProject = React.useCallback((projectId: string) => {
// Ignore intersection events for a short period after toggling
ignoreIntersectionUntil.current = Date.now() + 150;
resetProjectSessionLimits(projectId);
setCollapsedProjects((prev) => {
const next = new Set(prev);
if (next.has(projectId)) {
@@ -741,7 +766,7 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
}
return next;
});
}, [isVSCode, safeStorage, scheduleCollapsedProjectsPersist]);
}, [isVSCode, resetProjectSessionLimits, safeStorage, scheduleCollapsedProjectsPersist]);
const normalizedProjects = React.useMemo(() => {
return projects
@@ -933,6 +958,7 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
}, [projectSections, homeDirectory]);
const showRecentSection = useSessionDisplayStore((state) => state.showRecentSection);
const showArchivedSessions = useSessionDisplayStore((state) => state.showArchivedSessions);
const activeNowSessions = React.useMemo(() => {
if (!showRecentSection) {
@@ -1010,8 +1036,15 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
});
const sectionsForSidebarRender = React.useMemo(() => {
const archiveFilteredSections = showArchivedSessions
? sectionsForRender
: sectionsForRender.map((section) => ({
...section,
groups: section.groups.filter((group) => !group.isArchivedBucket),
}));
if (isVSCode || hasSessionSearchQuery || recentSessionIds.size === 0) {
return sectionsForRender;
return archiveFilteredSections;
}
const filterNodes = (nodes: SessionNode[]): SessionNode[] => {
@@ -1034,14 +1067,14 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
}, []);
};
return sectionsForRender.map((section) => ({
return archiveFilteredSections.map((section) => ({
...section,
groups: section.groups.map((group) => ({
...group,
sessions: filterNodes(group.sessions),
})),
}));
}, [isVSCode, hasSessionSearchQuery, recentSessionIds, sectionsForRender]);
}, [isVSCode, hasSessionSearchQuery, recentSessionIds, sectionsForRender, showArchivedSessions]);
const prLookupKeys = React.useMemo(() => {
const keys = new Set<string>();
@@ -1247,13 +1280,14 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
);
const toggleCollapsedGroup = React.useCallback((key: string) => {
resetGroupSessionLimit(key);
setCollapsedGroups((prev) => {
const next = new Set(prev);
if (next.has(key)) next.delete(key);
else next.add(key);
return next;
});
}, []);
}, [resetGroupSessionLimit]);
const prVisualStateByDirectoryBranch = React.useMemo(() => {
const result = new Map<string, PrIndicator>();
@@ -1287,7 +1321,7 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
hasSessionSearchQuery={hasSessionSearchQuery}
normalizedSessionSearchQuery={normalizedSessionSearchQuery}
groupSearchDataByGroup={groupSearchDataByGroup}
expandedSessionGroups={expandedSessionGroups}
visibleSessionCount={visibleSessionCountByGroup.get(groupKey)}
collapsedGroups={collapsedGroups}
hideDirectoryControls={hideDirectoryControls}
collapsedFolderIds={collapsedFolderIds}
@@ -1300,7 +1334,8 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
currentSessionDirectory={currentSessionDirectory}
projectRepoStatus={projectRepoStatus}
lastRepoStatus={lastRepoStatusRef.current}
toggleGroupSessionLimit={toggleGroupSessionLimit}
showMoreGroupSessions={showMoreGroupSessions}
resetGroupSessionLimit={resetGroupSessionLimit}
mobileVariant={mobileVariant}
alwaysShowActions={alwaysShowSidebarActions}
activeProjectId={activeProjectId}
@@ -1325,7 +1360,7 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
hasSessionSearchQuery,
normalizedSessionSearchQuery,
groupSearchDataByGroup,
expandedSessionGroups,
visibleSessionCountByGroup,
collapsedGroups,
hideDirectoryControls,
collapsedFolderIds,
@@ -1336,7 +1371,8 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
renderSessionNode,
currentSessionDirectory,
projectRepoStatus,
toggleGroupSessionLimit,
showMoreGroupSessions,
resetGroupSessionLimit,
mobileVariant,
alwaysShowSidebarActions,
activeProjectId,