Skip to content

Commit c88b4c9

Browse files
ryaneggzclaude
andcommitted
chore: add implementation plan for sidebar redesign (#918)
Plan to consolidate threads under projects in sidebar, matching t3code pattern. Includes user flow diagrams, integration points, and acceptance criteria. Closes #918 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: ryaneggz <kre8mymedia@gmail.com>
1 parent 9d0992c commit c88b4c9

1 file changed

Lines changed: 365 additions & 0 deletions

File tree

Lines changed: 365 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,365 @@
1+
# Plan: Sidebar Redesign - Consolidate Threads Under Projects
2+
3+
## Context
4+
5+
The current Orchestra sidebar separates projects and threads into two independent collapsible sections. Threads associated with a project are hidden from the sidebar entirely and only visible on the project page (`/p/:projectId`). This forces users to navigate away from the sidebar to see what threads exist within a project.
6+
7+
t3code (running at localhost:3773) uses a **project-centric tree** where threads are nested directly under their parent project in the sidebar, with per-project pagination ("Show more"/"Show less"), inline "Create new thread" buttons, and orphan threads collected in a separate bottom section. This is a more intuitive hierarchy that shows the thread-project relationship at a glance.
8+
9+
**Goal:** Redesign the Orchestra sidebar to match t3code's consolidated tree pattern.
10+
11+
## t3code Design Reference (from browser research)
12+
13+
```
14+
Sidebar
15+
"Projects" header + "Add project" button
16+
Project "orchestra" (collapsible) + [+ New thread]
17+
Thread "Implement Agent-Browser..." [Plan Ready] 20d ago
18+
Thread "BUG: Starting New Chat..." [Plan Ready] 20d ago
19+
"Show more" / "Show less"
20+
Project "ryaneggz" (collapsible) + [+ New thread]
21+
Thread "New thread" 10d ago
22+
Project "deep-factor-agent" (collapsible) + [+ New thread]
23+
Thread "Load the prd skill..." 22d ago
24+
---separator---
25+
"New thread" section (orphan threads)
26+
Settings
27+
```
28+
29+
Key patterns: nested threads under projects, status badges, per-project lazy pagination, "Create new thread" per project, orphan threads at bottom.
30+
31+
## New User Flow (Empty State)
32+
33+
When a user has **no projects and no threads**, the sidebar shows:
34+
35+
```
36+
Assistants
37+
Memories
38+
39+
v Projects [+]
40+
┌─────────────────────┐
41+
│ No projects yet. │
42+
│ [+ Create Project] │
43+
└─────────────────────┘
44+
45+
> Threads (0)
46+
─────────────────────
47+
Settings
48+
```
49+
50+
- Projects section is **expanded by default** with an empty state message and prominent "Create Project" CTA
51+
- Orphan "Threads" section remains at the bottom (collapsed, shows count)
52+
- New threads created without a project go to the orphan "Threads" section
53+
- This nudges users toward project-first organization from day one
54+
55+
---
56+
57+
## User Flow Diagram
58+
59+
```
60+
+------------------+
61+
| User opens app |
62+
+--------+---------+
63+
|
64+
+--------v---------+
65+
| Sidebar renders |
66+
| with Projects |
67+
| expanded |
68+
+--------+---------+
69+
|
70+
+-------------+-------------+
71+
| |
72+
+--------v---------+ +--------v---------+
73+
| Has projects? | | Has orphan |
74+
| NO | | threads? YES |
75+
+--------+---------+ +--------+---------+
76+
| |
77+
+--------v---------+ +--------v---------+
78+
| Show empty state | | Show collapsed |
79+
| "No projects yet"| | "Threads (N)" |
80+
| [+ Create Project| | section at bottom|
81+
+--------+---------+ +--------+---------+
82+
|
83+
+--------v---------+
84+
| User clicks |
85+
| "Create Project" |
86+
+--------+---------+
87+
|
88+
+--------v---------+
89+
| Project appears |
90+
| in sidebar tree |
91+
| (expanded, empty)|
92+
+--------+---------+
93+
|
94+
+-----------+-----------+
95+
| |
96+
+-------v--------+ +--------v---------+
97+
| User clicks | | User clicks |
98+
| [+ New thread] | | project chevron |
99+
| on project | | to collapse |
100+
+-------+--------+ +--------+---------+
101+
| |
102+
+-------v--------+ +--------v---------+
103+
| Navigate to | | Threads hidden |
104+
| /p/:id with | | State saved to |
105+
| project_id set | | localStorage |
106+
| in metadata | +------------------+
107+
+-------+--------+
108+
|
109+
+-------v--------+
110+
| User sends |
111+
| first message |
112+
+-------+--------+
113+
|
114+
+-------v-----------------------+
115+
| Thread appears nested under |
116+
| project in sidebar |
117+
| (compact: title + timestamp) |
118+
+-------+-----------------------+
119+
|
120+
+-------v-----------------------+
121+
| More threads accumulate... |
122+
| After 5 threads: |
123+
| [Show more] button appears |
124+
+-------+-----------------------+
125+
|
126+
+-------v-----------------------+
127+
| User clicks "Show more" |
128+
| Next 5 threads lazy-loaded |
129+
| via searchThreadsByProject() |
130+
+-------------------------------+
131+
132+
133+
THREAD MOVE FLOW:
134+
=================
135+
136+
+---------------------------+ +---------------------------+
137+
| Thread in orphan section | | Thread under Project A |
138+
| User clicks [...] menu | | User clicks [...] menu |
139+
+------------+--------------+ +------------+--------------+
140+
| |
141+
+------------v--------------+ +------------v--------------+
142+
| "Move to Project" submenu | | "Move to Project" submenu |
143+
| shows all projects | | shows all projects |
144+
+------------+--------------+ +------------+--------------+
145+
| |
146+
+------------v--------------+ +------------v--------------+
147+
| User selects Project B | | User selects Project C |
148+
+------------+--------------+ +------------+--------------+
149+
| |
150+
+------------v--------------+ +------------v--------------+
151+
| updateThreadProject() API | | updateThreadProject() API |
152+
+------------+--------------+ +------------+--------------+
153+
| |
154+
+------------v---------------------------------v-+
155+
| Sync both states simultaneously: |
156+
| - Remove from source (orphan or Project A) |
157+
| - Add to destination (Project B or C) |
158+
| - No flicker, no duplication |
159+
+-----------------------------------------------+
160+
161+
162+
SIDEBAR STATE TRANSITIONS:
163+
==========================
164+
165+
New User First Project Active User
166+
(empty) Created (multiple projects)
167+
+---------------+ +------------------+ +------------------------+
168+
| v Projects [+]| | v Projects [+]| | v Projects [+]|
169+
| No projects | | v MyProj [+t] | | v Proj-A [+t] |
170+
| yet. | | (no threads) | | Thread 1 2m ago |
171+
| [+ Create] | | | | Thread 2 1h ago |
172+
| | | > Threads (3) | | [Show more] |
173+
| > Threads (0) | | | | > Proj-B [+t] |
174+
| | | Settings | | Thread 3 3h ago |
175+
| Settings | +------------------+ | > Proj-C [+t] |
176+
+---------------+ | |
177+
| > Threads (2) |
178+
| |
179+
| Settings |
180+
+------------------------+
181+
```
182+
183+
---
184+
185+
## Implementation Plan
186+
187+
### 1. Create `useProjectThreads` hook
188+
189+
**New file:** `frontend/src/hooks/useProjectThreads.ts`
190+
191+
- Manages a `Map<projectId, { threads, loading, hasMore, loaded }>` state
192+
- `fetchProjectThreads(projectId)` - lazy-loads on first project expand using existing `searchThreadsByProject()` from `threadService.ts:291`
193+
- `loadMoreProjectThreads(projectId)` - pagination (5 threads per page)
194+
- `addThreadToProject(thread, projectId)` / `removeThreadFromProject(threadKey, projectId)` - local state mutations for cross-section sync
195+
- Stores expanded project IDs in `localStorage` for persistence across reloads
196+
197+
### 2. Create `ProjectTreeGroup` component
198+
199+
**New file:** `frontend/src/components/sidebar/ProjectTreeGroup.tsx`
200+
201+
Replaces `ProjectsCollapsibleGroup` (currently at `app-sidebar.tsx:494-554`).
202+
203+
```
204+
ProjectTreeGroup
205+
"Create Project" button
206+
for each project:
207+
ProjectTreeItem (collapsible)
208+
Trigger: project name + chevron + [+ New thread] button
209+
Content:
210+
ThreadItem (compact variant) for each thread
211+
"Show more" button (if hasMore)
212+
Loading spinner (if fetching)
213+
```
214+
215+
- Uses `SidebarMenuSub`, `SidebarMenuSubItem`, `SidebarMenuSubButton` from `sidebar.tsx:701-752` (already exported but unused) for nested thread rendering
216+
- Each `ProjectTreeItem` triggers `fetchProjectThreads(projectId)` on expand
217+
- "Create new thread" button sets `metadata.project_id` and navigates to `/p/:projectId`
218+
219+
### 3. Modify `ThreadItem` for compact variant
220+
221+
**File:** `frontend/src/components/drawers/app-sidebar.tsx` (lines 146-362)
222+
223+
Add props:
224+
- `compact?: boolean` - tighter styling, single-line layout, less padding
225+
- `parentProjectId?: string` - changes click navigation to `/p/:projectId/t/:threadId`
226+
227+
No new component needed - reuses existing dropdown menu, delete, and move-to-project logic.
228+
229+
### 4. Update `AppSidebar` composition
230+
231+
**File:** `frontend/src/components/drawers/app-sidebar.tsx` (lines 837-920)
232+
233+
Changes:
234+
- Replace `ProjectsCollapsibleGroup` (line 904) with `ProjectTreeGroup`
235+
- Set Projects section `defaultOpen={true}` (was `false`)
236+
- Set Threads section `defaultOpen={false}` (was `true`) - orphan threads become secondary
237+
- Wire `useProjectThreads` hook
238+
- Update `handleAddToProject` to sync both orphan threads list and project threads map
239+
240+
### 5. State synchronization
241+
242+
When a thread is moved between projects (via `ThreadItem` dropdown):
243+
- Remove from source (orphan list or source project's thread list)
244+
- Add to destination (target project's thread list or orphan list)
245+
- Both updates happen synchronously in the same handler to prevent duplication
246+
247+
---
248+
249+
## Critical Files
250+
251+
| File | Role |
252+
|------|------|
253+
| `frontend/src/components/drawers/app-sidebar.tsx` | Main sidebar - modify `ProjectsCollapsibleGroup`, `ThreadItem`, `AppSidebar` |
254+
| `frontend/src/components/ui/sidebar.tsx` | Reuse `SidebarMenuSub*` primitives (lines 701-752) |
255+
| `frontend/src/lib/services/threadService.ts` | Existing `searchThreadsByProject()` (line 291) - no changes needed |
256+
| `frontend/src/hooks/useProjectThreads.ts` | **New** - per-project thread fetching hook |
257+
| `frontend/src/components/sidebar/ProjectTreeGroup.tsx` | **New** - project tree with nested threads |
258+
| `frontend/src/context/ProjectContext.tsx` | May need thread mutation callbacks |
259+
| `frontend/src/components/lists/ListProjectThreads.tsx` | Reference for `searchThreadsByProject` usage pattern |
260+
261+
## What NOT to change
262+
263+
- No backend/API changes needed
264+
- No routing changes needed (existing `/p/:projectId/t/:threadId` route works)
265+
- No virtual scrolling for project trees (paginated at 5 items, manageable count)
266+
- Keep virtual scrolling for orphan threads section (can grow large)
267+
268+
---
269+
270+
## Deliverable: GitHub Issue
271+
272+
Create a GitHub issue using `gh issue create` with the feature_request template, populated with the research findings above. The issue content is drafted below.
273+
274+
**Title:** `feat: Redesign sidebar to consolidate threads under projects (t3code pattern)`
275+
276+
**Labels:** `enhancement`
277+
278+
### Issue Body
279+
280+
#### User Stories
281+
282+
- As a **user with multiple projects**, I want **threads nested under their parent project in the sidebar** so that **I can see all project activity without navigating to a separate page**.
283+
- As a **user managing threads**, I want **inline "Create new thread" buttons per project** so that **I can start a new conversation in the correct project context immediately**.
284+
- As a **user with many threads**, I want **per-project "Show more"/"Show less" pagination** so that **the sidebar stays manageable without hiding my work**.
285+
286+
#### Summary
287+
288+
Redesign the sidebar to consolidate threads under their parent projects in a collapsible tree, matching the t3code pattern observed at localhost:3773. Currently, projects and threads are separate sidebar sections -- project threads are only visible on the project page. The new design nests threads directly under each project with lazy-loaded pagination, inline thread creation, and orphan threads in a separate bottom section.
289+
290+
##### Visual Reference
291+
292+
- t3code sidebar screenshots captured during research (project tree with nested threads, status badges, "Show more"/"Show less" per project)
293+
- Current Orchestra sidebar: flat `Projects` (collapsed) + `Threads` (expanded, orphans only)
294+
295+
#### Key Integration Points
296+
297+
| File | Function(s) | Role |
298+
|------|-------------|------|
299+
| `frontend/src/components/drawers/app-sidebar.tsx` | `ProjectsCollapsibleGroup` (L494-554), `ThreadItem` (L146-362), `AppSidebar` (L837-920) | Main sidebar composition - replace ProjectsCollapsibleGroup, add compact variant to ThreadItem |
300+
| `frontend/src/lib/services/threadService.ts` | `searchThreadsByProject()` (L291) | Existing API for fetching threads by project - no changes needed |
301+
| `frontend/src/components/ui/sidebar.tsx` | `SidebarMenuSub`, `SidebarMenuSubItem`, `SidebarMenuSubButton` (L701-752) | Existing unused primitives for nested menu items - reuse for thread nesting |
302+
| `frontend/src/context/ProjectContext.tsx` | `ProjectProvider` | May need thread mutation callbacks for cross-section sync |
303+
| `frontend/src/components/lists/ListProjectThreads.tsx` | `searchThreadsByProject` usage | Reference pattern for project thread fetching |
304+
305+
#### UI Integration Points
306+
307+
| Component / Route | Change Type | Description |
308+
|-------------------|-------------|-------------|
309+
| `ProjectsCollapsibleGroup` (`app-sidebar.tsx`) | Replace | New `ProjectTreeGroup` with nested threads per project |
310+
| `ThreadItem` (`app-sidebar.tsx`) | Modify | Add `compact` and `parentProjectId` props for nested display |
311+
| `CollapsibleGroup` threads section (`app-sidebar.tsx`) | Modify | Change to `defaultOpen={false}`, now secondary for orphan threads |
312+
| `ProjectTreeGroup` (new) | New component | Collapsible project tree with nested thread items |
313+
| `useProjectThreads` (new hook) | New hook | Per-project lazy thread fetching with pagination |
314+
315+
#### Storage
316+
317+
- **Persistence layer**: No new storage -- threads already have `project_id` in LangGraph Store
318+
- **Namespace / table**: Existing thread metadata `{ project_id }` relationship
319+
- **Local state**: `useProjectThreads` hook manages `Map<projectId, ThreadsState>` in React state; expanded project IDs persisted in `localStorage`
320+
321+
#### Architectural Decisions
322+
323+
- **No virtual scrolling for project trees**: Per-project pagination caps at 5 items per page, keeping rendered DOM manageable. Virtual scrolling remains for the orphan threads section.
324+
- **Lazy fetching**: Project threads only fetched when user expands that project in the sidebar (not on mount), using existing `searchThreadsByProject()` API.
325+
- **State sync**: When a thread is moved between projects, both the orphan threads list (`ChatContext`) and the project threads map (`useProjectThreads`) are updated synchronously to prevent duplication.
326+
- **Reuse existing primitives**: `SidebarMenuSub*` components from `sidebar.tsx` (currently unused) provide the nested indentation and left-border styling.
327+
328+
#### Documentation
329+
330+
- t3code reference implementation at localhost:3773
331+
- Existing `SidebarMenuSub` components in `sidebar.tsx` (L701-752) designed for exactly this nested pattern
332+
333+
#### Development Setup
334+
335+
| Service | Address | Notes |
336+
|---------|---------|-------|
337+
| Frontend dev | `localhost:5173` or `localhost:8030` | `npm run dev` or `npm run dev:claude` |
338+
| Backend API | `localhost:8000` | Required for thread/project APIs |
339+
340+
#### Design Principles
341+
342+
- Simplicity is beauty, complexity is pain.
343+
- ALWAYS look at the current codebase first -- achieve the goal in the least amount of changes.
344+
- Reuse `ThreadItem` with a compact variant rather than creating a duplicate component.
345+
- Reuse existing `SidebarMenuSub*` primitives rather than custom nesting CSS.
346+
347+
#### Validation Tools
348+
349+
- [ ] Load `agent-browser` skill with screenshots to validate E2E sidebar tree rendering
350+
351+
#### Acceptance Criteria
352+
353+
- [ ] Implementation plan is thoroughly documented
354+
- [ ] Projects section is expanded by default with threads nested under each project
355+
- [ ] Clicking a project chevron expands/collapses its thread list (lazy fetch on first expand)
356+
- [ ] "Show more"/"Show less" paginates threads within a project (5 per page)
357+
- [ ] Inline "Create new thread" button per project navigates to `/p/:projectId` with correct metadata
358+
- [ ] Moving a thread between projects updates both sidebar sections without duplication or flicker
359+
- [ ] Orphan threads (no `project_id`) still appear in bottom "Threads" section (collapsed by default)
360+
- [ ] Mobile sidebar (Sheet) works correctly with nested project tree
361+
- [ ] Expanded project state persists across page reloads via localStorage
362+
- [ ] New user (no projects) sees empty state with "No projects yet" message and "Create Project" CTA
363+
- [ ] All previous & new tests pass, validated using `agent-browser` CLI
364+
- [ ] New code follows existing repo/service/route patterns
365+
- [ ] No new dependencies added beyond what's already in the project

0 commit comments

Comments
 (0)