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:
@@ -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')}>
|
||||||
|
|||||||
Reference in New Issue
Block a user