Metadata
IMPORTANT: The very first step should ALWAYS be validating this metadata section to maintain a CLEAN development workflow.
pull_request_title: "FROM feat/[issue#]-sidebar-project-threads TO development"
branch: "feat/[issue#]-sidebar-project-threads"
worktree_path: "$WORKSPACE/.worktrees/feat-[issue#]"
User Stories
- 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.
- 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.
- 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.
Summary
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.
Visual Reference
t3code sidebar (reference design):
Sidebar
"Projects" header + "Add project" button
Project "orchestra" (collapsible) + [+ New thread]
Thread "Implement Agent-Browser..." [Plan Ready] 20d ago
Thread "BUG: Starting New Chat..." [Plan Ready] 20d ago
"Show more" / "Show less"
Project "ryaneggz" (collapsible) + [+ New thread]
Thread "New thread" 10d ago
---separator---
"New thread" section (orphan threads)
Settings
Sidebar state transitions:
New User First Project Active User
(empty) Created (multiple projects)
+---------------+ +------------------+ +------------------------+
| v Projects [+]| | v Projects [+]| | v Projects [+]|
| No projects | | v MyProj [+t] | | v Proj-A [+t] |
| yet. | | (no threads) | | Thread 1 2m ago |
| [+ Create] | | | | Thread 2 1h ago |
| | | > Threads (3) | | [Show more] |
| > Threads (0) | | | | > Proj-B [+t] |
| | | Settings | | Thread 3 3h ago |
| Settings | +------------------+ | > Proj-C [+t] |
+---------------+ | |
| > Threads (2) |
| |
| Settings |
+------------------------+
User flow:
+------------------+
| User opens app |
+--------+---------+
|
+--------v---------+
| Sidebar renders |
| with Projects |
| expanded |
+--------+---------+
|
+-------------+-------------+
| |
+--------v---------+ +--------v---------+
| Has projects? | | Has orphan |
| NO | | threads? YES |
+--------+---------+ +--------+---------+
| |
+--------v---------+ +--------v---------+
| Show empty state | | Show collapsed |
| "No projects yet"| | "Threads (N)" |
| [+ Create Project| | section at bottom|
+--------+---------+ +--------+---------+
|
+--------v---------+
| User clicks |
| "Create Project" |
+--------+---------+
|
+--------v---------+
| Project appears |
| in sidebar tree |
| (expanded, empty)|
+--------+---------+
|
+-----------+-----------+
| |
+-------v--------+ +--------v---------+
| User clicks | | User clicks |
| [+ New thread] | | project chevron |
| on project | | to collapse |
+-------+--------+ +--------+---------+
| |
+-------v--------+ +--------v---------+
| Navigate to | | Threads hidden |
| /p/:id with | | State saved to |
| project_id set | | localStorage |
| in metadata | +------------------+
+-------+--------+
|
+-------v--------+
| User sends |
| first message |
+-------+--------+
|
+-------v-----------------------+
| Thread appears nested under |
| project in sidebar |
| (compact: title + timestamp) |
+-------+-----------------------+
|
+-------v-----------------------+
| More threads accumulate... |
| After 5 threads: |
| [Show more] button appears |
+-------------------------------+
Thread move flow:
+---------------------------+ +---------------------------+
| Thread in orphan section | | Thread under Project A |
| User clicks [...] menu | | User clicks [...] menu |
+------------+--------------+ +------------+--------------+
| |
+------------v--------------+ +------------v--------------+
| "Move to Project" submenu | | "Move to Project" submenu |
| shows all projects | | shows all projects |
+------------+--------------+ +------------+--------------+
| |
+------------v--------------+ +------------v--------------+
| User selects Project B | | User selects Project C |
+------------+--------------+ +------------+--------------+
| |
+------------v---------------------------------v-+
| Sync both states simultaneously: |
| - Remove from source (orphan or Project A) |
| - Add to destination (Project B or C) |
| - No flicker, no duplication |
+-----------------------------------------------+
Key Integration Points
| File |
Function(s) |
Role |
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 |
frontend/src/lib/services/threadService.ts |
searchThreadsByProject() (L291) |
Existing API for fetching threads by project — no changes needed |
frontend/src/components/ui/sidebar.tsx |
SidebarMenuSub, SidebarMenuSubItem, SidebarMenuSubButton (L701-752) |
Existing unused primitives for nested menu items — reuse for thread nesting |
frontend/src/context/ProjectContext.tsx |
ProjectProvider |
May need thread mutation callbacks for cross-section sync |
frontend/src/components/lists/ListProjectThreads.tsx |
searchThreadsByProject usage |
Reference pattern for project thread fetching |
UI Integration Points
| Component / Route |
Change Type |
Description |
ProjectsCollapsibleGroup (app-sidebar.tsx) |
Replace |
New ProjectTreeGroup with nested threads per project |
ThreadItem (app-sidebar.tsx) |
Modify |
Add compact and parentProjectId props for nested display |
CollapsibleGroup threads section (app-sidebar.tsx) |
Modify |
Change to defaultOpen={false}, now secondary for orphan threads |
ProjectTreeGroup (new) |
New component |
Collapsible project tree with nested thread items |
useProjectThreads (new hook) |
New hook |
Per-project lazy thread fetching with pagination |
Storage
- Persistence layer: No new storage — threads already have
project_id in LangGraph Store
- Namespace / table: Existing thread metadata
{ project_id } relationship
- Model pattern:
useProjectThreads hook manages Map<projectId, ThreadsState> in React state; expanded project IDs persisted in localStorage
Architectural Decisions
- 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.
- Lazy fetching: Project threads only fetched when user expands that project in the sidebar (not on mount), using existing
searchThreadsByProject() API.
- 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.
- Reuse existing primitives:
SidebarMenuSub* components from sidebar.tsx (currently unused) provide the nested indentation and left-border styling.
- New user empty state: Projects section expanded by default with "No projects yet" + "Create Project" CTA. Nudges project-first organization.
Documentation
- t3code reference implementation at localhost:3773
- Existing
SidebarMenuSub components in sidebar.tsx (L701-752) designed for exactly this nested pattern
Development Setup
Dependencies
| Service |
Address |
Notes |
| Frontend dev |
localhost:5173 or localhost:8030 |
npm run dev or npm run dev:claude |
| Backend API |
localhost:8000 |
Required for thread/project APIs |
| Redis |
localhost:6379 |
Docker container |
| Postgres |
localhost:5432 |
Docker container |
Commands
cd frontend && npm run dev # Dev server
cd frontend && npm run test # Run tests
cd frontend && npm run lint # Lint check
Wiki
IMPORTANT: Wiki lives in a separate repo. Changes to @wiki must be committed directly to the wiki repo, not the main project repo.
Design Principles
- Simplicity is beauty, complexity is pain.
- ALWAYS look at the current codebase first — achieve the goal in the least amount of changes.
- Reuse
ThreadItem with a compact variant rather than creating a duplicate component.
- Reuse existing
SidebarMenuSub* primitives rather than custom nesting CSS.
- TDD-first: write tests before implementation.
Validation Tools
Acceptance Criteria
Metadata
User Stories
Summary
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.
Visual Reference
t3code sidebar (reference design):
Sidebar state transitions:
User flow:
Thread move flow:
Key Integration Points
frontend/src/components/drawers/app-sidebar.tsxProjectsCollapsibleGroup(L494-554),ThreadItem(L146-362),AppSidebar(L837-920)frontend/src/lib/services/threadService.tssearchThreadsByProject()(L291)frontend/src/components/ui/sidebar.tsxSidebarMenuSub,SidebarMenuSubItem,SidebarMenuSubButton(L701-752)frontend/src/context/ProjectContext.tsxProjectProviderfrontend/src/components/lists/ListProjectThreads.tsxsearchThreadsByProjectusageUI Integration Points
ProjectsCollapsibleGroup(app-sidebar.tsx)ProjectTreeGroupwith nested threads per projectThreadItem(app-sidebar.tsx)compactandparentProjectIdprops for nested displayCollapsibleGroupthreads section (app-sidebar.tsx)defaultOpen={false}, now secondary for orphan threadsProjectTreeGroup(new)useProjectThreads(new hook)Storage
project_idin LangGraph Store{ project_id }relationshipuseProjectThreadshook managesMap<projectId, ThreadsState>in React state; expanded project IDs persisted inlocalStorageArchitectural Decisions
searchThreadsByProject()API.ChatContext) and the project threads map (useProjectThreads) are updated synchronously to prevent duplication.SidebarMenuSub*components fromsidebar.tsx(currently unused) provide the nested indentation and left-border styling.Documentation
SidebarMenuSubcomponents insidebar.tsx(L701-752) designed for exactly this nested patternDevelopment Setup
Dependencies
localhost:5173orlocalhost:8030npm run devornpm run dev:claudelocalhost:8000localhost:6379localhost:5432Commands
Wiki
Design Principles
ThreadItemwith a compact variant rather than creating a duplicate component.SidebarMenuSub*primitives rather than custom nesting CSS.Validation Tools
agent-browserskill with screenshots to validate E2E sidebar tree renderingAcceptance Criteria
/p/:projectIdwith correct metadataproject_id) still appear in bottom "Threads" section (collapsed by default)agent-browserCLI