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
@@ -40,7 +40,7 @@ type Props = {
hasSessionSearchQuery: boolean;
normalizedSessionSearchQuery: string;
groupSearchDataByGroup: WeakMap<SessionGroup, GroupSearchData>;
expandedSessionGroups: Set<string>;
visibleSessionCount?: number;
collapsedGroups: Set<string>;
hideDirectoryControls: boolean;
collapsedFolderIds: Set<string>;
@@ -53,7 +53,8 @@ type Props = {
currentSessionDirectory: string | null;
projectRepoStatus: Map<string, boolean | null>;
lastRepoStatus: boolean;
toggleGroupSessionLimit: (groupKey: string) => void;
showMoreGroupSessions: (groupKey: string, currentVisibleCount: number) => void;
resetGroupSessionLimit: (groupKey: string) => void;
mobileVariant: boolean;
alwaysShowActions: boolean;
activeProjectId: string | null;
@@ -107,7 +108,7 @@ export function SessionGroupSection(props: Props): React.ReactNode {
hasSessionSearchQuery,
normalizedSessionSearchQuery,
groupSearchDataByGroup,
expandedSessionGroups,
visibleSessionCount,
collapsedGroups,
hideDirectoryControls,
collapsedFolderIds,
@@ -119,7 +120,8 @@ export function SessionGroupSection(props: Props): React.ReactNode {
renderSessionNode,
projectRepoStatus,
lastRepoStatus,
toggleGroupSessionLimit,
showMoreGroupSessions,
resetGroupSessionLimit,
mobileVariant,
alwaysShowActions,
activeProjectId,
@@ -156,9 +158,9 @@ export function SessionGroupSection(props: Props): React.ReactNode {
const displayMode = useSessionDisplayStore((state) => state.displayMode);
const foldersMap = useSessionFoldersStore((state) => state.foldersMap);
const isMinimalMode = displayMode === 'minimal';
const isExpanded = expandedSessionGroups.has(groupKey);
const isCollapsed = hasSessionSearchQuery ? false : collapsedGroups.has(groupKey);
const maxVisible = hideDirectoryControls ? 10 : 5;
const nonArchivedVisibleCount = Math.max(maxVisible, visibleSessionCount ?? maxVisible);
const groupMatchesSearch = hasSessionSearchQuery ? searchData?.groupMatches === true : false;
const shouldFilterGroupContents = hasSessionSearchQuery;
const sourceGroupNodes = React.useMemo(
@@ -255,8 +257,9 @@ export function SessionGroupSection(props: Props): React.ReactNode {
? ungroupedSessions
: hasSessionSearchQuery
? ungroupedSessions
: (isExpanded ? ungroupedSessions : ungroupedSessions.slice(0, maxVisible));
: ungroupedSessions.slice(0, nonArchivedVisibleCount);
const remainingCount = totalSessions - visibleSessions.length;
const canShowLess = !group.isArchivedBucket && !hasSessionSearchQuery && totalSessions > maxVisible && remainingCount === 0;
// Virtualize the archived bucket once it grows past a threshold. The
// archived list is the only group that can routinely hit hundreds or
@@ -617,21 +620,19 @@ export function SessionGroupSection(props: Props): React.ReactNode {
: t('sessions.sidebar.group.empty.noSessionsInWorkspace')}
</div>
) : null}
{remainingCount > 0 && !isExpanded ? (
{remainingCount > 0 ? (
<button
type="button"
onClick={() => toggleGroupSessionLimit(groupKey)}
onClick={() => showMoreGroupSessions(groupKey, visibleSessions.length)}
className="mt-0.5 flex items-center justify-start rounded-md px-1.5 py-0.5 text-left text-xs text-muted-foreground/70 leading-tight hover:text-foreground hover:underline"
>
{remainingCount === 1
? t('sessions.sidebar.group.showMoreSingle', { count: remainingCount })
: t('sessions.sidebar.group.showMorePlural', { count: remainingCount })}
{t('sessions.sidebar.group.showMore')}
</button>
) : null}
{isExpanded && totalSessions > maxVisible ? (
{canShowLess ? (
<button
type="button"
onClick={() => toggleGroupSessionLimit(groupKey)}
onClick={() => resetGroupSessionLimit(groupKey)}
className="mt-0.5 flex items-center justify-start rounded-md px-1.5 py-0.5 text-left text-xs text-muted-foreground/70 leading-tight hover:text-foreground hover:underline"
>
{t('sessions.sidebar.group.showFewer')}
@@ -63,8 +63,10 @@ export function SidebarHeader(props: Props): React.ReactNode {
const displayMode = useSessionDisplayStore((state) => state.displayMode);
const showRecentSection = useSessionDisplayStore((state) => state.showRecentSection);
const showArchivedSessions = useSessionDisplayStore((state) => state.showArchivedSessions);
const setDisplayMode = useSessionDisplayStore((state) => state.setDisplayMode);
const toggleRecentSection = useSessionDisplayStore((state) => state.toggleRecentSection);
const toggleArchivedSessions = useSessionDisplayStore((state) => state.toggleArchivedSessions);
if (hideDirectoryControls) {
return null;
@@ -210,6 +212,13 @@ export function SidebarHeader(props: Props): React.ReactNode {
<span>{t('sessions.sidebar.header.displayMode.showRecent')}</span>
{showRecentSection ? <Icon name="check" className="h-4 w-4 text-primary" /> : null}
</DropdownMenuItem>
<DropdownMenuItem
onClick={toggleArchivedSessions}
className="flex items-center justify-between"
>
<span>{t('sessions.sidebar.header.displayMode.showArchived')}</span>
{showArchivedSessions ? <Icon name="check" className="h-4 w-4 text-primary" /> : null}
</DropdownMenuItem>
</>
) : null}
<DropdownMenuSeparator />