From 7d1e2a2ee4784200474d4ea3e2170d0c0daa69f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E9=98=BF=E6=B1=9F=28Relakkes?= =?UTF-8?q?=29?= Date: Wed, 22 Apr 2026 17:00:53 +0800 Subject: [PATCH] Reduce sidebar control clutter in session navigation The project filter and session search were competing for the same narrow vertical lane in the desktop sidebar. This change merges them into a single search surface, keeps project scoping available through an embedded trigger, and trims the surrounding control chrome so the sidebar reads like one cohesive tool instead of stacked widgets. Constraint: Sidebar filtering behavior and existing project dropdown logic had to remain intact Constraint: This commit must exclude unrelated in-progress desktop and server changes in the worktree Rejected: Keep separate project and search controls with smaller spacing | still wastes vertical space and keeps the visual split Rejected: Move project filtering into a second modal flow | adds friction to a frequent navigation task Confidence: high Scope-risk: narrow Reversibility: clean Directive: Keep project scoping embedded in the search lane unless session discovery changes materially Tested: bun run test src/components/layout/ProjectFilter.test.tsx src/components/layout/Sidebar.test.tsx src/__tests__/pages.test.tsx; bun run lint; bun run build Not-tested: Manual dark-theme screenshot pass after the final integrated layout change --- .../src/components/layout/ProjectFilter.tsx | 63 ++++++++++++++----- desktop/src/components/layout/Sidebar.tsx | 62 ++++++++++-------- desktop/src/theme/globals.css | 28 ++++++++- 3 files changed, 110 insertions(+), 43 deletions(-) diff --git a/desktop/src/components/layout/ProjectFilter.tsx b/desktop/src/components/layout/ProjectFilter.tsx index f94fc0c2..3a370929 100644 --- a/desktop/src/components/layout/ProjectFilter.tsx +++ b/desktop/src/components/layout/ProjectFilter.tsx @@ -23,7 +23,7 @@ let cachedProjects: RecentProject[] | null = null let cacheTimestamp = 0 const CACHE_TTL = 30_000 -export function ProjectFilter() { +export function ProjectFilter({ variant = 'default' }: { variant?: 'default' | 'embedded' }) { const t = useTranslation() const { availableProjects, selectedProjects, setSelectedProjects } = useSessionStore() const [open, setOpen] = useState(false) @@ -134,6 +134,7 @@ export function ProjectFilter() { : selectedProjects.length === 1 ? optionByPath.get(selectedProjects[0]!)?.title || fallbackProjectTitle(selectedProjects[0]!, t('sidebar.other')) : `${selectedProjects.length} projects` + const triggerLabel = isAllSelected ? t('sidebar.allProjects') : label const toggleProject = (projectPath: string) => { if (isAllSelected) { @@ -160,19 +161,45 @@ export function ProjectFilter() { type="button" onClick={() => setOpen((current) => !current)} aria-expanded={open} - className="flex items-center gap-1.5 rounded-[var(--radius-lg)] bg-[var(--color-surface-container-low)] px-3 py-2 text-sm font-medium text-[var(--color-text-primary)] shadow-[0_1px_0_rgba(255,255,255,0.6)] transition-colors hover:bg-[var(--color-surface-hover)]" + aria-label={triggerLabel} + title={triggerLabel} + className={ + variant === 'embedded' + ? `inline-flex h-7 w-7 items-center justify-center rounded-[8px] border transition-colors duration-200 ${ + isAllSelected + ? 'border-transparent text-[var(--color-text-tertiary)] hover:bg-[var(--color-sidebar-item-hover)] hover:text-[var(--color-text-secondary)]' + : 'border-[var(--color-sidebar-item-active-border)] bg-[var(--color-sidebar-item-active)] text-[var(--color-text-primary)] hover:bg-[var(--color-sidebar-item-hover)]' + }` + : 'inline-flex h-8 max-w-full items-center gap-1.5 rounded-[10px] border border-[var(--color-sidebar-filter-border)] bg-[var(--color-sidebar-filter-bg)] px-2 text-left text-[14px] text-[var(--color-text-primary)] transition-colors duration-200 hover:bg-[var(--color-sidebar-item-hover)]' + } > - {label} - + {variant === 'embedded' ? ( + + + {!isAllSelected && ( + + )} + + ) : ( + <> + + + {label} + + + + + + )} {open && dropdownPos && createPortal(
- +
{t('sidebar.allProjects')}
@@ -207,11 +238,13 @@ export function ProjectFilter() { key={option.projectPath} type="button" onClick={() => toggleProject(option.projectPath)} - className={`flex w-full items-center gap-3 rounded-[16px] px-4 py-3 text-left transition-colors hover:bg-[var(--color-surface-hover)] ${ - checked ? 'bg-[var(--color-surface-selected)]' : '' + className={`flex w-full items-center gap-3 rounded-[12px] px-3 py-2.5 text-left transition-colors ${ + checked + ? 'bg-[var(--color-sidebar-item-active)]' + : 'hover:bg-[var(--color-sidebar-item-hover)]' }`} > - {option.isGit ? : } + {option.isGit ? : }
{option.title}
{option.subtitle && ( @@ -277,7 +310,7 @@ function ChevronIcon({ open }: { open: boolean }) { ) } -function FolderIcon() { +function FolderIcon({ className = 'text-[var(--color-text-secondary)]' }: { className?: string }) { return ( ) } -function GitBranchIcon() { +function GitBranchIcon({ className = 'text-[var(--color-text-secondary)]' }: { className?: string }) { return ( diff --git a/desktop/src/components/layout/Sidebar.tsx b/desktop/src/components/layout/Sidebar.tsx index 81011251..85ad50b8 100644 --- a/desktop/src/components/layout/Sidebar.tsx +++ b/desktop/src/components/layout/Sidebar.tsx @@ -125,7 +125,7 @@ export function Sidebar() {
- +
-
- +
+ +
-
- setSearchQuery(e.target.value)} - className="w-full h-8 px-2.5 text-xs rounded-[var(--radius-md)] border border-[var(--color-border)] bg-[var(--color-surface)] text-[var(--color-text-primary)] placeholder:text-[var(--color-text-tertiary)] outline-none transition-colors focus:border-[var(--color-border-focus)]" - /> -
-
-
+
{timeGroupLabels[group]}
{items.map((session) => ( @@ -274,22 +275,22 @@ export function Sidebar() { }} onContextMenu={(e) => handleContextMenu(e, session.id)} className={` - group w-full rounded-[var(--radius-md)] py-1.5 pl-4 pr-3 text-left text-sm transition-colors duration-200 + group w-full rounded-[12px] px-3 py-2 text-left text-sm transition-colors duration-200 ${session.id === activeTabId - ? 'bg-[var(--color-surface-selected)] text-[var(--color-text-primary)]' - : 'text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)]' + ? 'bg-[var(--color-sidebar-item-active)] text-[var(--color-text-primary)]' + : 'text-[var(--color-text-secondary)] hover:bg-[var(--color-sidebar-item-hover)]' } `} > - + - {session.title || 'Untitled'} + {session.title || 'Untitled'} {!session.workDirExists && ( @@ -455,6 +456,15 @@ function ClockIcon() { ) } +function SearchIcon() { + return ( + + + + + ) +} + function SidebarToggleIcon({ collapsed }: { collapsed: boolean }) { return (