fix: polish pinned session indicators

Pinned recent sessions stay at top when already recent
Default session rows align activity, pin, and chevron indicators
Pinned icon no longer crowds session titles
This commit is contained in:
Bohdan Triapitsyn
2026-04-28 17:12:49 +03:00
parent 41369fa5f2
commit 6186688e17
2 changed files with 12 additions and 15 deletions
@@ -994,8 +994,9 @@ export const SessionSidebar: React.FC<SessionSidebarProps> = ({
}, [projectSections, homeDirectory]); }, [projectSections, homeDirectory]);
const activeNowSessions = React.useMemo( const activeNowSessions = React.useMemo(
() => deriveActiveNowSessions(activeNowEntries, new Map(sessions.map((session) => [session.id, session]))), () => deriveActiveNowSessions(activeNowEntries, new Map(sessions.map((session) => [session.id, session])))
[activeNowEntries, sessions], .sort((a, b) => compareSessionsByPinnedAndTime(a, b, pinnedSessionIds)),
[activeNowEntries, pinnedSessionIds, sessions],
); );
const liveActiveSessions = React.useMemo( const liveActiveSessions = React.useMemo(
@@ -494,19 +494,17 @@ function SessionNodeItemComponent(props: Props): React.ReactNode {
title={t('sessions.sidebar.session.status.unread')} title={t('sessions.sidebar.session.status.unread')}
/> />
); );
const inlineStatusMarker = !isMinimalMode && showStatusMarker ? ( const leadingIndicators = showStatusMarker || isPinnedSession ? (
<span className="inline-flex h-3.5 w-3.5 flex-shrink-0 items-center justify-center">
{statusMarkerContent}
</span>
) : null;
const minimalLeadingStatusMarker = isMinimalMode && showStatusMarker ? (
<span <span
className={cn( className={cn(
'pointer-events-none absolute left-[-10px] top-1/2 inline-flex h-3.5 w-3.5 -translate-y-1/2 items-center justify-center transition-opacity', 'pointer-events-none absolute inline-flex h-3.5 items-center justify-center gap-0.5 transition-opacity',
isMinimalMode ? 'top-1/2 -translate-y-1/2' : 'top-[14.5px] -translate-y-1/2',
showStatusMarker && isPinnedSession ? 'left-[-18px] w-6' : 'left-[-10px] w-3.5',
hasChildren ? 'opacity-100 group-hover:opacity-0 group-focus-within:opacity-0' : '', hasChildren ? 'opacity-100 group-hover:opacity-0 group-focus-within:opacity-0' : '',
)} )}
> >
{statusMarkerContent} {showStatusMarker ? statusMarkerContent : null}
{isPinnedSession ? <RiPushpinLine className="h-3 w-3 flex-shrink-0 text-primary" aria-label={t('sessions.sidebar.session.status.pinned')} /> : null}
</span> </span>
) : null; ) : null;
const subsessionChevron = hasChildren ? ( const subsessionChevron = hasChildren ? (
@@ -525,7 +523,8 @@ function SessionNodeItemComponent(props: Props): React.ReactNode {
} }
}} }}
className={cn( className={cn(
'absolute left-[-10px] top-1/2 inline-flex h-3.5 w-3.5 -translate-y-1/2 items-center justify-center rounded-md text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50 transition-opacity', 'absolute left-[-10px] inline-flex h-3.5 w-3.5 items-center justify-center rounded-md text-muted-foreground hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/50 transition-opacity',
isMinimalMode ? 'top-1/2 -translate-y-1/2' : 'top-[14.5px] -translate-y-1/2',
isMinimalMode && showStatusMarker isMinimalMode && showStatusMarker
? 'opacity-0 pointer-events-none group-hover:opacity-100 group-hover:pointer-events-auto group-focus-within:opacity-100 group-focus-within:pointer-events-auto' ? 'opacity-0 pointer-events-none group-hover:opacity-100 group-hover:pointer-events-auto group-focus-within:opacity-100 group-focus-within:pointer-events-auto'
: '', : '',
@@ -721,7 +720,7 @@ function SessionNodeItemComponent(props: Props): React.ReactNode {
isRowSelected && 'bg-primary/15', isRowSelected && 'bg-primary/15',
)} )}
> >
{minimalLeadingStatusMarker} {leadingIndicators}
{subsessionChevron} {subsessionChevron}
<div className="flex min-w-0 flex-1 items-center"> <div className="flex min-w-0 flex-1 items-center">
{isMinimalMode ? ( {isMinimalMode ? (
@@ -748,7 +747,6 @@ function SessionNodeItemComponent(props: Props): React.ReactNode {
)} )}
> >
<div className={cn('flex w-full items-center min-w-0 flex-1 overflow-hidden', isMinimalMode ? 'gap-1' : 'gap-1')}> <div className={cn('flex w-full items-center min-w-0 flex-1 overflow-hidden', isMinimalMode ? 'gap-1' : 'gap-1')}>
{isPinnedSession ? <RiPushpinLine className="h-3 w-3 flex-shrink-0 text-primary" aria-label={t('sessions.sidebar.session.status.pinned')} /> : null}
<div className={cn('block min-w-0 flex-1 truncate typography-ui-label font-normal', isActive ? 'text-primary' : 'text-foreground')}>{renderHighlightedText(sessionTitle, normalizedSessionSearchQuery)}</div> <div className={cn('block min-w-0 flex-1 truncate typography-ui-label font-normal', isActive ? 'text-primary' : 'text-foreground')}>{renderHighlightedText(sessionTitle, normalizedSessionSearchQuery)}</div>
{mobileVariant ? <span className="ml-2 flex-shrink-0 text-[0.72rem] text-muted-foreground/75">{sessionCompactUpdatedLabel}</span> : null} {mobileVariant ? <span className="ml-2 flex-shrink-0 text-[0.72rem] text-muted-foreground/75">{sessionCompactUpdatedLabel}</span> : null}
{!mobileVariant ? ( {!mobileVariant ? (
@@ -813,8 +811,6 @@ function SessionNodeItemComponent(props: Props): React.ReactNode {
)} )}
> >
<div className={cn('flex w-full items-center min-w-0 flex-1 overflow-hidden', isMinimalMode ? 'gap-1' : 'gap-1')}> <div className={cn('flex w-full items-center min-w-0 flex-1 overflow-hidden', isMinimalMode ? 'gap-1' : 'gap-1')}>
{inlineStatusMarker}
{isPinnedSession ? <RiPushpinLine className="h-3 w-3 flex-shrink-0 text-primary" aria-label={t('sessions.sidebar.session.status.pinned')} /> : null}
<div className={cn('block min-w-0 flex-1 truncate typography-ui-label font-normal', isActive ? 'text-primary' : 'text-foreground')}>{renderHighlightedText(sessionTitle, normalizedSessionSearchQuery)}</div> <div className={cn('block min-w-0 flex-1 truncate typography-ui-label font-normal', isActive ? 'text-primary' : 'text-foreground')}>{renderHighlightedText(sessionTitle, normalizedSessionSearchQuery)}</div>
{pendingPermissionCount > 0 ? ( {pendingPermissionCount > 0 ? (
<span className="inline-flex items-center gap-1 rounded bg-destructive/10 px-1 py-0.5 text-[0.7rem] text-destructive flex-shrink-0" title={t('sessions.sidebar.session.status.permissionRequired')} aria-label={t('sessions.sidebar.session.status.permissionRequired')}> <span className="inline-flex items-center gap-1 rounded bg-destructive/10 px-1 py-0.5 text-[0.7rem] text-destructive flex-shrink-0" title={t('sessions.sidebar.session.status.permissionRequired')} aria-label={t('sessions.sidebar.session.status.permissionRequired')}>