cc-haha/docs/superpowers/specs/2026-04-07-sidebar-redesign-design.md
程序员阿江(Relakkes) d601bbf968 docs: add sidebar redesign spec — project filter + time-grouped list
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 00:40:21 +08:00

146 lines
5.7 KiB
Markdown

# Sidebar Redesign — Project Filter + Time-Grouped Session List
## Summary
Redesign the desktop app sidebar to match the original Claude Code desktop app:
- Replace project-grouped session list with a **flat, time-grouped list** (Today / Yesterday / Last 7 days / Last 30 days / Older)
- Add a **ProjectFilter dropdown** at the top for multi-select project filtering
- Keep existing features: search, right-click menu (rename + delete), active session indicator, missing dir warning
## Approach
**Plan A: Frontend-only refactor** — modify `Sidebar.tsx`, add `ProjectFilter.tsx`, update `sessionStore.ts`. No backend API changes.
## Components
### 1. ProjectFilter (`desktop/src/components/layout/ProjectFilter.tsx`)
New component providing a multi-select project dropdown.
**UI structure:**
- **Trigger button**: displays current filter state
- All selected → "All projects"
- N selected → "N projects"
- Dropdown arrow (chevron) on the right
- **Dropdown panel** (absolute positioned, below trigger):
- "All projects" option (toggles select-all / deselect-all)
- Separator line
- Per-project items: folder icon + project name + checkmark when selected
- Project name extracted from sanitized projectPath (last segment)
**Behavior:**
- Default state: "All projects" (empty `selectedProjects` array = show all)
- Click a project → toggle its selection
- Click "All projects" → select all (clear the filter)
- If all projects are individually selected → treat as "All projects"
- Clicking outside the dropdown closes it
**Data source:** Extract unique `projectPath` values from the full session list (`sessions.map(s => s.projectPath)`), deduplicated and sorted alphabetically.
### 2. Sidebar Layout (`desktop/src/components/layout/Sidebar.tsx`)
**Layout (top to bottom):**
1. **+ New session** button (existing)
2. **Scheduled** button (existing)
3. **ProjectFilter** dropdown + settings icon (right side)
4. **Search sessions** input (existing)
5. **Time-grouped session list** (scrollable, main content area)
6. **Settings** button (bottom, existing)
**Time grouping logic — `groupByTime(sessions)`:**
```typescript
type TimeGroup = 'Today' | 'Yesterday' | 'Last 7 days' | 'Last 30 days' | 'Older'
function groupByTime(sessions: SessionListItem[]): Map<TimeGroup, SessionListItem[]> {
const now = new Date()
const startOfToday = startOfDay(now)
const startOfYesterday = startOfDay(subDays(now, 1))
const sevenDaysAgo = startOfDay(subDays(now, 7))
const thirtyDaysAgo = startOfDay(subDays(now, 30))
// Classify each session by modifiedAt into time buckets
// Within each bucket, sessions are already sorted by modifiedAt desc (from API)
}
```
No external date library needed — use native `Date` arithmetic.
**Removed logic:**
- `getProjectDisplayName()` function
- `groupedSessions` useMemo that groups by projectPath
- Per-project group headers and collapsible groups
**Preserved logic:**
- Search filtering (by title, case-insensitive)
- Active session highlighting (background color)
- Status dot (left side of each session item)
- Right-click context menu (rename + delete)
- "missing dir" warning badge
- Relative timestamp on hover
### 3. Session Store Changes (`desktop/src/stores/sessionStore.ts`)
**New state fields:**
```typescript
selectedProjects: string[] // empty = all projects (no filter)
availableProjects: string[] // unique project paths from all sessions
```
**New actions:**
```typescript
setSelectedProjects: (projects: string[]) => void
```
**Modified behavior:**
- `fetchSessions()` called without `project` parameter (fetch all sessions)
- `availableProjects` computed after fetching: `[...new Set(sessions.map(s => s.projectPath))]`
- Filtering by selectedProjects happens in `Sidebar.tsx` via useMemo, not in the store
### 4. Session List Item Rendering
Each session item renders as:
```
[status dot] Session title text... [4h]
```
- **Status dot**: small circle, brand color when active (connected), tertiary when inactive
- **Title**: single line, text-ellipsis overflow
- **Timestamp**: relative format (now / 5m / 2h / 3d / 1mo), shown on hover (matching current behavior)
- **Selected state**: `surface-selected` background color
- **Hover state**: `surface-hover` background color
- **Missing dir**: orange "missing dir" badge after title
## What Does NOT Change
- Backend API (`/api/sessions`, `/api/sessions/recent-projects`) — no changes
- Session CRUD operations (create, delete, rename, activate)
- WebSocket connection logic
- Right-click context menu items
- Search functionality (filter by title)
- Settings button at bottom
- Overall sidebar width and theme variables
## File Change Summary
| File | Change Type | Description |
|------|------------|-------------|
| `desktop/src/components/layout/ProjectFilter.tsx` | **New** | Multi-select project dropdown |
| `desktop/src/components/layout/Sidebar.tsx` | **Modify** | Replace project grouping with time grouping, integrate ProjectFilter |
| `desktop/src/stores/sessionStore.ts` | **Modify** | Add selectedProjects/availableProjects state |
## Error Handling
- Empty state: if no sessions match filter + search, show "No sessions found" message
- API failure: existing error handling in sessionStore remains unchanged
- Missing projects: if a session's projectPath doesn't match any known project, it still appears in "All projects" view
## Testing
- Verify time grouping correctly classifies sessions into Today/Yesterday/Last 7 days/Last 30 days/Older
- Verify ProjectFilter multi-select toggles work correctly
- Verify search + project filter work together (intersection)
- Verify right-click menu still works
- Verify active session highlighting persists across filter changes