From fc3a6ea2e1cbab40449bc3aa8951d894137fda1d 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: Tue, 12 May 2026 20:48:44 +0800 Subject: [PATCH] Make batch-selected sidebar rows easier to scan The previous batch selection state let adjacent selected sessions visually merge into a single block. This keeps the existing row structure but adds per-row spacing, a transparent baseline border, and a subtle selected-row shadow so the selection is easier to read without changing behavior. Constraint: Keep the change local to the desktop sidebar and avoid new dependencies. Rejected: Add global sidebar CSS tokens | unnecessary for this small component-level state and harder for changed-line coverage. Confidence: high Scope-risk: narrow Directive: Keep batch selection spacing on the row wrapper so adjacent selected rows remain visually separated. Tested: bun run verify --- .../src/components/layout/Sidebar.test.tsx | 52 +++++++++++++++++++ desktop/src/components/layout/Sidebar.tsx | 10 ++-- 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/desktop/src/components/layout/Sidebar.test.tsx b/desktop/src/components/layout/Sidebar.test.tsx index 791d7064..353f6b16 100644 --- a/desktop/src/components/layout/Sidebar.test.tsx +++ b/desktop/src/components/layout/Sidebar.test.tsx @@ -260,6 +260,58 @@ describe('Sidebar', () => { }) }) + it('renders batch-selected sessions as separated selected rows', () => { + const now = new Date().toISOString() + useSessionStore.setState({ + sessions: [ + { + id: 'session-1', + title: 'First Session', + createdAt: now, + modifiedAt: now, + messageCount: 1, + projectPath: '/workspace/project', + workDir: '/workspace/project', + workDirExists: true, + }, + { + id: 'session-2', + title: 'Second Session', + createdAt: now, + modifiedAt: now, + messageCount: 1, + projectPath: '/workspace/project', + workDir: '/workspace/project', + workDirExists: true, + }, + { + id: 'session-3', + title: 'Third Session', + createdAt: now, + modifiedAt: now, + messageCount: 1, + projectPath: '/workspace/project', + workDir: '/workspace/project', + workDirExists: true, + }, + ], + }) + useTabStore.setState({ + tabs: [{ sessionId: 'session-2', title: 'Second Session', type: 'session', status: 'idle' }], + activeTabId: 'session-2', + }) + + render() + + fireEvent.click(screen.getByRole('button', { name: 'Batch manage' })) + fireEvent.click(screen.getByRole('button', { name: /First Session/ })) + + expect(screen.getByRole('button', { name: /First Session/ }).parentElement).toHaveClass('mb-1.5') + expect(screen.getByRole('button', { name: /First Session/ })).toHaveClass('sidebar-session-row--selected') + expect(screen.getByRole('button', { name: /Second Session/ })).toHaveClass('sidebar-session-row--active') + expect(screen.getByRole('button', { name: /Third Session/ })).toHaveClass('sidebar-session-row--idle') + }) + it('collapses into an icon rail and expands back', async () => { render() diff --git a/desktop/src/components/layout/Sidebar.tsx b/desktop/src/components/layout/Sidebar.tsx index 4df8aa2b..3fdf4575 100644 --- a/desktop/src/components/layout/Sidebar.tsx +++ b/desktop/src/components/layout/Sidebar.tsx @@ -493,7 +493,7 @@ export function Sidebar({ isMobile = false, onRequestClose }: SidebarProps) { )} {items.map((session) => ( -
+
{renamingId === session.id ? ( handleContextMenu(e, session.id)} className={` - group w-full rounded-[12px] px-3 ${isMobile ? 'py-3' : 'py-2'} text-left text-sm transition-colors duration-200 + group w-full rounded-[12px] border px-3 ${isMobile ? 'py-3' : 'py-2'} text-left text-sm transition-[background,border-color,box-shadow,filter,color] duration-200 ${selectedSessionIds.has(session.id) - ? 'bg-[var(--color-sidebar-item-active)] text-[var(--color-text-primary)] ring-1 ring-[var(--color-brand)]/15' + ? 'sidebar-session-row--selected border-[var(--color-sidebar-item-active-border)] bg-[var(--color-sidebar-item-active)] text-[var(--color-text-primary)] shadow-[inset_0_1px_0_rgba(255,255,255,0.72),0_3px_10px_rgba(143,72,47,0.07)] hover:brightness-[0.995]' : session.id === activeTabId - ? 'bg-[var(--color-sidebar-item-active)] text-[var(--color-text-primary)]' - : 'text-[var(--color-text-secondary)] hover:bg-[var(--color-sidebar-item-hover)]' + ? 'sidebar-session-row--active border-transparent bg-[var(--color-sidebar-item-active)] text-[var(--color-text-primary)]' + : 'sidebar-session-row--idle border-transparent text-[var(--color-text-secondary)] hover:bg-[var(--color-sidebar-item-hover)]' } `} aria-pressed={isBatchMode ? selectedSessionIds.has(session.id) : undefined}