refactor: simplify session list display
Makes minimal session rows the default Removes session diff stat badges from navigation surfaces Keeps expanded session rows in VS Code
This commit is contained in:
@@ -69,7 +69,6 @@ import { ProjectActionsButton } from '@/components/layout/ProjectActionsButton';
|
||||
import { SessionSwitcherDropdown } from '@/components/session/SessionSwitcherDropdown';
|
||||
import { canUseElectronDesktopIPC, invokeDesktop, isDesktopLocalOriginActive, isDesktopShell, isVSCodeRuntime, startDesktopWindowDrag, type UpdateInfo } from '@/lib/desktop';
|
||||
import { desktopHostsGet, getDesktopHostApiUrl, locationMatchesHost, redactSensitiveUrl } from '@/lib/desktopHosts';
|
||||
import { resolveSessionDiffStats } from '@/components/session/sidebar/utils';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
import { runtimeFetch } from '@/lib/runtime-fetch';
|
||||
@@ -1289,17 +1288,6 @@ export const Header: React.FC<HeaderProps> = ({
|
||||
return trimmedTitle && trimmedTitle.length > 0 ? trimmedTitle : 'Untitled Session';
|
||||
}, [activeProjectLabel, currentSession?.title, currentSessionId]);
|
||||
|
||||
const currentSessionDiffStats = React.useMemo(() => {
|
||||
return resolveSessionDiffStats(currentSession?.summary as Parameters<typeof resolveSessionDiffStats>[0]);
|
||||
}, [currentSession?.summary]);
|
||||
|
||||
const currentSessionChanges = React.useMemo(() => {
|
||||
if (currentSessionDiffStats) {
|
||||
return currentSessionDiffStats;
|
||||
}
|
||||
return { additions: 0, deletions: 0 };
|
||||
}, [currentSessionDiffStats]);
|
||||
const hasNonZeroSessionChanges = currentSessionChanges.additions > 0 || currentSessionChanges.deletions > 0;
|
||||
|
||||
const actionDirectory = React.useMemo(() => {
|
||||
return normalize(openDirectory || activeProject?.path || '');
|
||||
@@ -2140,7 +2128,7 @@ export const Header: React.FC<HeaderProps> = ({
|
||||
<span className="truncate typography-ui-label text-[14px] font-normal leading-tight text-foreground max-w-full">
|
||||
{isNewSessionDraftOpen ? t('sessions.switcher.draftTitle') : currentSessionTitle}
|
||||
</span>
|
||||
{(activeProjectLabel || currentBranchLabel || (!isNewSessionDraftOpen && (hasNonZeroSessionChanges || worktreeBadgeKind))) ? (
|
||||
{(activeProjectLabel || currentBranchLabel || (!isNewSessionDraftOpen && worktreeBadgeKind)) ? (
|
||||
<span className="flex min-w-0 max-w-full items-center gap-1.5 truncate typography-micro text-[10.5px] font-normal leading-tight text-muted-foreground/75">
|
||||
{activeProjectLabel ? <span className="truncate">{activeProjectLabel}</span> : null}
|
||||
{currentBranchLabel ? (
|
||||
@@ -2149,13 +2137,6 @@ export const Header: React.FC<HeaderProps> = ({
|
||||
<span className="truncate">{currentBranchLabel}</span>
|
||||
</span>
|
||||
) : null}
|
||||
{!isNewSessionDraftOpen && hasNonZeroSessionChanges ? (
|
||||
<span className="inline-flex flex-shrink-0 items-center gap-0 text-[0.92em]">
|
||||
<span className="text-status-success/80">+{currentSessionChanges.additions}</span>
|
||||
<span className="text-muted-foreground/60">/</span>
|
||||
<span className="text-status-error/65">-{currentSessionChanges.deletions}</span>
|
||||
</span>
|
||||
) : null}
|
||||
{!isNewSessionDraftOpen && worktreeBadgeKind ? (
|
||||
<span className={cn(
|
||||
"inline-flex min-w-0 items-center gap-0.5",
|
||||
|
||||
@@ -15,7 +15,6 @@ import { useDirectoryStore } from '@/stores/useDirectoryStore';
|
||||
import { useProjectsStore } from '@/stores/useProjectsStore';
|
||||
import { useGitBranchLabel, useGitStore } from '@/stores/useGitStore';
|
||||
import { useConfigStore } from '@/stores/useConfigStore';
|
||||
import { resolveSessionDiffStats } from '@/components/session/sidebar/utils';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { useRuntimeAPIs } from '@/hooks/useRuntimeAPIs';
|
||||
import type { SessionContextUsage } from '@/stores/types/sessionTypes';
|
||||
@@ -136,11 +135,6 @@ const MiniChatHeader: React.FC<{ mode: MiniChatMode }> = ({ mode }) => {
|
||||
const gitBranchForDirectory = useGitBranchLabel(openDirectory || null);
|
||||
const rawBranchLabel = gitBranchForDirectory || worktreeMetadataBranch || sessionWorktreeMetadata?.branch?.trim() || worktreeAttachment?.branch?.trim() || catalogWorktreeBranch;
|
||||
const branchLabel = rawBranchLabel && rawBranchLabel !== 'HEAD' ? rawBranchLabel : null;
|
||||
const diffStats = React.useMemo(() => {
|
||||
return resolveSessionDiffStats(session?.summary as Parameters<typeof resolveSessionDiffStats>[0]);
|
||||
}, [session?.summary]);
|
||||
const changes = diffStats ?? { additions: 0, deletions: 0 };
|
||||
const hasChanges = changes.additions > 0 || changes.deletions > 0;
|
||||
const currentModel = getCurrentModel();
|
||||
const latestAssistantModel = React.useMemo(() => {
|
||||
for (let i = currentSessionMessages.length - 1; i >= 0; i -= 1) {
|
||||
@@ -284,13 +278,6 @@ const MiniChatHeader: React.FC<{ mode: MiniChatMode }> = ({ mode }) => {
|
||||
<span className="truncate">{branchLabel}</span>
|
||||
</span>
|
||||
) : null}
|
||||
{hasChanges ? (
|
||||
<span className="inline-flex flex-shrink-0 items-center gap-0 text-[0.92em]">
|
||||
<span className="text-status-success/80">+{changes.additions}</span>
|
||||
<span className="text-muted-foreground/60">/</span>
|
||||
<span className="text-status-error/65">-{changes.deletions}</span>
|
||||
</span>
|
||||
) : null}
|
||||
</span>
|
||||
</button>
|
||||
</SessionSwitcherDropdown>
|
||||
|
||||
@@ -14,8 +14,8 @@ import { useSessionUnseenCount } from '@/sync/notification-store';
|
||||
import { useSwitcherItems, type SwitcherItem } from '@/components/session/sidebar/hooks/useSwitcherItems';
|
||||
import { useUIStore } from '@/stores/useUIStore';
|
||||
import { resolveGlobalSessionDirectory } from '@/stores/useGlobalSessionsStore';
|
||||
import { formatSessionCompactDateLabel, resolveSessionDiffStats } from './sidebar/utils';
|
||||
import type { SessionNode, SessionSummaryMeta } from './sidebar/types';
|
||||
import { formatSessionCompactDateLabel } from './sidebar/utils';
|
||||
import type { SessionNode } from './sidebar/types';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
@@ -202,8 +202,6 @@ function SwitcherRow({ session, depth, variant, secondaryMeta, hasChildren, isEx
|
||||
const isStreaming = statusType === 'busy' || statusType === 'retry';
|
||||
const showUnreadDot = !isStreaming && needsAttention && !isActive;
|
||||
|
||||
const summary = session.summary as SessionSummaryMeta | undefined;
|
||||
const diffStats = resolveSessionDiffStats(summary);
|
||||
const timestamp = session.time?.updated || session.time?.created || Date.now();
|
||||
const timeLabel = formatSessionCompactDateLabel(timestamp);
|
||||
|
||||
@@ -288,13 +286,6 @@ function SwitcherRow({ session, depth, variant, secondaryMeta, hasChildren, isEx
|
||||
<span className="truncate">{branchLabel}</span>
|
||||
</span>
|
||||
) : null}
|
||||
{diffStats ? (
|
||||
<span className="inline-flex flex-shrink-0 items-center gap-0 text-[0.92em]">
|
||||
<span className="text-status-success/80">+{diffStats.additions}</span>
|
||||
<span className="text-muted-foreground/60">/</span>
|
||||
<span className="text-status-error/65">-{diffStats.deletions}</span>
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
@@ -22,6 +22,7 @@ import type { SessionFolder } from '@/stores/useSessionFoldersStore';
|
||||
import { useSessionFoldersStore } from '@/stores/useSessionFoldersStore';
|
||||
import { useSessionDisplayStore } from '@/stores/useSessionDisplayStore';
|
||||
import { openExternalUrl } from '@/lib/url';
|
||||
import { isVSCodeRuntime } from '@/lib/desktop';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
type DeleteFolderConfirm = {
|
||||
@@ -157,7 +158,8 @@ export function SessionGroupSection(props: Props): React.ReactNode {
|
||||
const searchData = hasSessionSearchQuery ? groupSearchDataByGroup.get(group) : null;
|
||||
const displayMode = useSessionDisplayStore((state) => state.displayMode);
|
||||
const foldersMap = useSessionFoldersStore((state) => state.foldersMap);
|
||||
const isMinimalMode = displayMode === 'minimal';
|
||||
// VS Code always uses the expanded layout (see SessionNodeItem).
|
||||
const isMinimalMode = displayMode === 'minimal' && !isVSCodeRuntime();
|
||||
const isCollapsed = hasSessionSearchQuery ? false : collapsedGroups.has(groupKey);
|
||||
const maxVisible = hideDirectoryControls ? 10 : 5;
|
||||
const nonArchivedVisibleCount = Math.max(maxVisible, visibleSessionCount ?? maxVisible);
|
||||
|
||||
@@ -25,8 +25,8 @@ import { buildSessionMessageRecordsSnapshot, useDirectoryStore, useGlobalSession
|
||||
import { useSync } from '@/sync/use-sync';
|
||||
import { useViewportStore, viewportSessionKey } from '@/sync/viewport-store';
|
||||
import { DraggableSessionRow } from './sessionFolderDnd';
|
||||
import type { SessionNode, SessionSummaryMeta } from './types';
|
||||
import { formatSessionCompactDateLabel, formatSessionDateLabel, normalizePath, renderHighlightedText, resolveSessionDiffStats } from './utils';
|
||||
import type { SessionNode } from './types';
|
||||
import { formatSessionCompactDateLabel, formatSessionDateLabel, normalizePath, renderHighlightedText } from './utils';
|
||||
import { useSessionDisplayStore } from '@/stores/useSessionDisplayStore';
|
||||
import { useSessionUnseenCount } from '@/sync/notification-store';
|
||||
import { useSessionMultiSelectStore } from '@/stores/useSessionMultiSelectStore';
|
||||
@@ -263,8 +263,11 @@ function SessionNodeItemComponent(props: Props): React.ReactNode {
|
||||
const hasSecondaryBranchLabel = Boolean(secondaryMeta?.branchLabel);
|
||||
|
||||
const displayMode = useSessionDisplayStore((state) => state.displayMode);
|
||||
const isMinimalMode = displayMode === 'minimal';
|
||||
const isVSCode = React.useMemo(() => isVSCodeRuntime(), []);
|
||||
// VS Code keeps the expanded "default" layout regardless of the stored mode:
|
||||
// multi-workspace lists rely on inline project/branch, and hover tooltips
|
||||
// across the whole list would be impractical there.
|
||||
const isMinimalMode = displayMode === 'minimal' && !isVSCode;
|
||||
const isElectron = React.useMemo(() => canUseElectronDesktopIPC(), []);
|
||||
const runtimeApis = React.useContext(RuntimeAPIContext);
|
||||
const revealOnHoverClass = isVSCode
|
||||
@@ -348,8 +351,6 @@ function SessionNodeItemComponent(props: Props): React.ReactNode {
|
||||
const isSubtaskSession = Boolean((resolvedSession as Session & { parentID?: string | null }).parentID);
|
||||
const unseenCount = useSessionUnseenCount(session.id);
|
||||
const needsAttention = unseenCount > 0 && (!isSubtaskSession || notifyOnSubtasks);
|
||||
const sessionSummary = resolvedSession.summary as SessionSummaryMeta | undefined;
|
||||
const sessionDiffStats = resolveSessionDiffStats(sessionSummary);
|
||||
const sessionTimestamp = resolvedSession.time?.updated || resolvedSession.time?.created || Date.now();
|
||||
const sessionUpdatedLabel = formatSessionDateLabel(sessionTimestamp);
|
||||
const sessionCompactUpdatedLabel = formatSessionCompactDateLabel(sessionTimestamp);
|
||||
@@ -529,7 +530,6 @@ function SessionNodeItemComponent(props: Props): React.ReactNode {
|
||||
<div className="flex min-w-0 items-center gap-1.5 overflow-hidden">
|
||||
{hasChildren ? <span className="inline-flex items-center justify-center flex-shrink-0">{isExpanded ? <Icon name="arrow-down-s" className="h-3 w-3" /> : <Icon name="arrow-right-s" className="h-3 w-3" />}</span> : null}
|
||||
<span className="flex-shrink-0">{sessionUpdatedLabel}</span>
|
||||
{sessionDiffStats ? <span className="flex flex-shrink-0 items-center gap-0 text-[0.92em]"><span className="text-status-success/80">+{sessionDiffStats.additions}</span><span className="text-status-error/65">/-{sessionDiffStats.deletions}</span></span> : null}
|
||||
{hasSecondaryProjectLabel ? <span className="truncate">{secondaryMeta?.projectLabel}</span> : null}
|
||||
{hasSecondaryBranchLabel ? <span className="inline-flex min-w-0 items-center gap-0.5"><Icon name="git-branch" className="h-3 w-3 flex-shrink-0 text-muted-foreground/70" /><span className="truncate">{secondaryMeta?.branchLabel}</span></span> : null}
|
||||
</div>
|
||||
@@ -992,14 +992,11 @@ function SessionNodeItemComponent(props: Props): React.ReactNode {
|
||||
{secondaryMeta?.projectLabel ? <div className="min-w-0 truncate">{secondaryMeta.projectLabel}</div> : null}
|
||||
<div className="flex-shrink-0">{sessionUpdatedLabel}</div>
|
||||
</div>
|
||||
{secondaryMeta?.branchLabel || sessionDiffStats ? (
|
||||
<div className={cn('flex items-center gap-3 text-left text-muted-foreground', secondaryMeta?.branchLabel ? 'justify-between' : 'justify-start')}>
|
||||
{secondaryMeta?.branchLabel ? (
|
||||
<div className="flex min-w-0 items-center gap-1.5 overflow-hidden">
|
||||
<span className="inline-flex min-w-0 items-center gap-0.5"><Icon name="git-branch" className="h-3 w-3 flex-shrink-0" /><span className="truncate">{secondaryMeta.branchLabel}</span></span>
|
||||
</div>
|
||||
) : null}
|
||||
{sessionDiffStats ? <span className="flex flex-shrink-0 items-center gap-0.5"><span className="text-status-success">+{sessionDiffStats.additions}</span><span className="text-status-error">-{sessionDiffStats.deletions}</span></span> : null}
|
||||
{secondaryMeta?.branchLabel ? (
|
||||
<div className="flex items-center gap-3 text-left text-muted-foreground justify-start">
|
||||
<div className="flex min-w-0 items-center gap-1.5 overflow-hidden">
|
||||
<span className="inline-flex min-w-0 items-center gap-0.5"><Icon name="git-branch" className="h-3 w-3 flex-shrink-0" /><span className="truncate">{secondaryMeta.branchLabel}</span></span>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
@@ -1040,7 +1037,6 @@ function SessionNodeItemComponent(props: Props): React.ReactNode {
|
||||
<div className="flex items-center justify-between gap-3 text-muted-foreground/60 min-w-0 overflow-hidden leading-tight" style={{ fontSize: 'calc(var(--text-ui-label) * 0.85)' }}>
|
||||
<div className={cn('flex min-w-0 items-center gap-1.5 overflow-hidden', metadataSubsessionChevron && hasChildren ? 'pl-4' : '')}>
|
||||
<span className="flex-shrink-0">{sessionUpdatedLabel}</span>
|
||||
{sessionDiffStats ? <span className="flex flex-shrink-0 items-center gap-0 text-[0.92em]"><span className="text-status-success/80">+{sessionDiffStats.additions}</span><span className="text-muted-foreground/60">/</span><span className="text-status-error/65">-{sessionDiffStats.deletions}</span></span> : null}
|
||||
{hasSecondaryProjectLabel ? <span className="truncate">{secondaryMeta?.projectLabel}</span> : null}
|
||||
{hasSecondaryBranchLabel ? <span className="inline-flex min-w-0 items-center gap-0.5"><Icon name="git-branch" className="h-3 w-3 flex-shrink-0 text-muted-foreground/70" /><span className="truncate">{secondaryMeta?.branchLabel}</span></span> : null}
|
||||
</div>
|
||||
|
||||
@@ -11,6 +11,7 @@ import { cn } from '@/lib/utils';
|
||||
import { ArrowsMerge } from '@/components/icons/ArrowsMerge';
|
||||
import { Icon } from "@/components/icon/Icon";
|
||||
import { useSessionDisplayStore } from '@/stores/useSessionDisplayStore';
|
||||
import { isVSCodeRuntime } from '@/lib/desktop';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
type Props = {
|
||||
@@ -67,6 +68,8 @@ export function SidebarHeader(props: Props): React.ReactNode {
|
||||
const setDisplayMode = useSessionDisplayStore((state) => state.setDisplayMode);
|
||||
const toggleRecentSection = useSessionDisplayStore((state) => state.toggleRecentSection);
|
||||
const toggleArchivedSessions = useSessionDisplayStore((state) => state.toggleArchivedSessions);
|
||||
// VS Code forces the expanded layout, so the mode toggle is meaningless there.
|
||||
const showDisplayModeToggle = !isVSCodeRuntime();
|
||||
|
||||
if (hideDirectoryControls) {
|
||||
return null;
|
||||
@@ -188,23 +191,27 @@ export function SidebarHeader(props: Props): React.ReactNode {
|
||||
<TooltipContent side="bottom" sideOffset={4}><p>{t('sessions.sidebar.header.displayMode.label')}</p></TooltipContent>
|
||||
</Tooltip>
|
||||
<DropdownMenuContent align="end" className="min-w-[160px]">
|
||||
<DropdownMenuItem
|
||||
onClick={() => setDisplayMode('default')}
|
||||
className="flex items-center justify-between"
|
||||
>
|
||||
<span>{t('sessions.sidebar.header.displayMode.default')}</span>
|
||||
{displayMode === 'default' ? <Icon name="check" className="h-4 w-4 text-primary" /> : null}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => setDisplayMode('minimal')}
|
||||
className="flex items-center justify-between"
|
||||
>
|
||||
<span>{t('sessions.sidebar.header.displayMode.minimal')}</span>
|
||||
{displayMode === 'minimal' ? <Icon name="check" className="h-4 w-4 text-primary" /> : null}
|
||||
</DropdownMenuItem>
|
||||
{showDisplayModeToggle ? (
|
||||
<>
|
||||
<DropdownMenuItem
|
||||
onClick={() => setDisplayMode('default')}
|
||||
className="flex items-center justify-between"
|
||||
>
|
||||
<span>{t('sessions.sidebar.header.displayMode.default')}</span>
|
||||
{displayMode === 'default' ? <Icon name="check" className="h-4 w-4 text-primary" /> : null}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => setDisplayMode('minimal')}
|
||||
className="flex items-center justify-between"
|
||||
>
|
||||
<span>{t('sessions.sidebar.header.displayMode.minimal')}</span>
|
||||
{displayMode === 'minimal' ? <Icon name="check" className="h-4 w-4 text-primary" /> : null}
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
) : null}
|
||||
{showRecentControls ? (
|
||||
<>
|
||||
<DropdownMenuSeparator />
|
||||
{showDisplayModeToggle ? <DropdownMenuSeparator /> : null}
|
||||
<DropdownMenuItem
|
||||
onClick={toggleRecentSection}
|
||||
className="flex items-center justify-between"
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import type { Session } from '@opencode-ai/sdk/v2';
|
||||
import type { WorktreeMetadata } from '@/types/worktree';
|
||||
|
||||
export type SessionSummaryMeta = {
|
||||
additions?: number | string | null;
|
||||
deletions?: number | string | null;
|
||||
files?: number | null;
|
||||
diffs?: Array<{ additions?: number | string | null; deletions?: number | string | null }>;
|
||||
};
|
||||
|
||||
export type SessionNode = {
|
||||
session: Session;
|
||||
children: SessionNode[];
|
||||
|
||||
@@ -2,7 +2,6 @@ import React from 'react';
|
||||
import type { Session } from '@opencode-ai/sdk/v2';
|
||||
import { getCurrentIntlLocale } from '@/lib/i18n';
|
||||
import { formatMessage, useI18nStore } from '@/lib/i18n/store';
|
||||
import type { SessionSummaryMeta } from './types';
|
||||
|
||||
const t = (key: Parameters<typeof formatMessage>[1], params?: Parameters<typeof formatMessage>[2]) =>
|
||||
formatMessage(useI18nStore.getState().dictionary, key, params);
|
||||
@@ -209,47 +208,6 @@ export const isSessionRelatedToProject = (
|
||||
return sessionDirectory === projectRoot || sessionDirectory.startsWith(`${projectRoot}/`);
|
||||
};
|
||||
|
||||
const parseSummaryCount = (value: number | string | null | undefined): number | null => {
|
||||
if (typeof value === 'number' && Number.isFinite(value)) {
|
||||
return value;
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
const parsed = Number(value);
|
||||
if (Number.isFinite(parsed)) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const resolveSessionDiffStats = (summary?: SessionSummaryMeta): { additions: number; deletions: number } | null => {
|
||||
if (!summary) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const directAdditions = parseSummaryCount(summary.additions);
|
||||
const directDeletions = parseSummaryCount(summary.deletions);
|
||||
if (directAdditions !== null || directDeletions !== null) {
|
||||
const stats = {
|
||||
additions: Math.max(0, directAdditions ?? 0),
|
||||
deletions: Math.max(0, directDeletions ?? 0),
|
||||
};
|
||||
return stats.additions === 0 && stats.deletions === 0 ? null : stats;
|
||||
}
|
||||
|
||||
const diffs = Array.isArray(summary.diffs) ? summary.diffs : [];
|
||||
if (diffs.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let additions = 0;
|
||||
let deletions = 0;
|
||||
diffs.forEach((diff) => {
|
||||
additions += Math.max(0, parseSummaryCount(diff.additions) ?? 0);
|
||||
deletions += Math.max(0, parseSummaryCount(diff.deletions) ?? 0);
|
||||
});
|
||||
return additions === 0 && deletions === 0 ? null : { additions, deletions };
|
||||
};
|
||||
|
||||
export const formatProjectLabel = (label: string): string => {
|
||||
return label
|
||||
|
||||
@@ -17,7 +17,7 @@ type SessionDisplayStore = {
|
||||
export const useSessionDisplayStore = create<SessionDisplayStore>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
displayMode: 'default',
|
||||
displayMode: 'minimal',
|
||||
showRecentSection: true,
|
||||
showArchivedSessions: true,
|
||||
setDisplayMode: (mode) => set({ displayMode: mode }),
|
||||
@@ -28,6 +28,17 @@ export const useSessionDisplayStore = create<SessionDisplayStore>()(
|
||||
}),
|
||||
{
|
||||
name: 'session-display-mode',
|
||||
version: 1,
|
||||
// v0 shipped 'default' as the only/initial mode, so most existing users
|
||||
// have it persisted by accident rather than choice. Nudge everyone onto
|
||||
// minimal once so the mode can be evaluated before removing it entirely.
|
||||
migrate: (persisted, version) => {
|
||||
const state = (persisted ?? {}) as Partial<SessionDisplayStore>;
|
||||
if (version < 1) {
|
||||
return { ...state, displayMode: 'minimal' };
|
||||
}
|
||||
return state;
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user