From d1d669c37b93a2a4c9c5e84c3a3be6942a5c63dc Mon Sep 17 00:00:00 2001 From: Shivam Sharma <91240327+shivamhwp@users.noreply.github.com> Date: Sun, 29 Mar 2026 23:13:59 +0530 Subject: [PATCH 1/2] fix: show empty project thread state in sidebar Fixes #358 --- apps/web/src/components/ChatView.browser.tsx | 13 +++++++++++++ apps/web/src/components/Sidebar.tsx | 15 +++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/apps/web/src/components/ChatView.browser.tsx b/apps/web/src/components/ChatView.browser.tsx index 06cbf0efbd..be9d5f9ac7 100644 --- a/apps/web/src/components/ChatView.browser.tsx +++ b/apps/web/src/components/ChatView.browser.tsx @@ -1041,6 +1041,19 @@ describe("ChatView timeline estimator parity (full app)", () => { }, ); + it("shows an explicit empty state for projects without threads in the sidebar", async () => { + const mounted = await mountChatView({ + viewport: DEFAULT_VIEWPORT, + snapshot: createDraftOnlySnapshot(), + }); + + try { + await expect.element(page.getByText("No threads yet")).toBeInTheDocument(); + } finally { + await mounted.cleanup(); + } + }); + it("opens the project cwd for draft threads without a worktree path", async () => { setDraftThreadWithoutWorktree(); diff --git a/apps/web/src/components/Sidebar.tsx b/apps/web/src/components/Sidebar.tsx index 100d0e3f47..bbfecd3e1d 100644 --- a/apps/web/src/components/Sidebar.tsx +++ b/apps/web/src/components/Sidebar.tsx @@ -1050,6 +1050,7 @@ export default function Sidebar() { const renderedThreads = pinnedCollapsedThread ? [pinnedCollapsedThread] : visibleProjectThreads; + const showEmptyThreadState = project.expanded && projectThreads.length === 0; return { hasHiddenThreads, @@ -1058,6 +1059,7 @@ export default function Sidebar() { projectStatus, projectThreads, renderedThreads, + showEmptyThreadState, shouldShowThreadPanel, isThreadListExpanded, }; @@ -1203,6 +1205,7 @@ export default function Sidebar() { projectStatus, projectThreads, renderedThreads, + showEmptyThreadState, shouldShowThreadPanel, isThreadListExpanded, } = renderedProject; @@ -1535,6 +1538,18 @@ export default function Sidebar() { ref={attachThreadListAutoAnimateRef} className="mx-1 my-0 w-full translate-x-0 gap-0.5 overflow-hidden px-1.5 py-0" > + {shouldShowThreadPanel && showEmptyThreadState ? ( + + } + data-thread-selection-safe + size="sm" + className="h-6 w-full translate-x-0 cursor-default justify-start px-2 text-left text-[10px] text-muted-foreground/60 hover:bg-transparent hover:text-muted-foreground/60 active:bg-transparent active:text-muted-foreground/60" + > + No threads yet + + + ) : null} {shouldShowThreadPanel && renderedThreads.map((thread) => renderThreadRow(thread))} {project.expanded && hasHiddenThreads && !isThreadListExpanded && ( From 36289ac58649521ea261f849f7ca783b8b095578 Mon Sep 17 00:00:00 2001 From: Shivam Sharma <91240327+shivamhwp@users.noreply.github.com> Date: Mon, 30 Mar 2026 00:09:36 +0530 Subject: [PATCH 2/2] Simplify empty thread placeholder in sidebar - Replace the non-interactive thread placeholder button with a plain div - Keep the empty-state label visible without button affordances --- apps/web/src/components/Sidebar.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/web/src/components/Sidebar.tsx b/apps/web/src/components/Sidebar.tsx index bbfecd3e1d..b3b07e32fb 100644 --- a/apps/web/src/components/Sidebar.tsx +++ b/apps/web/src/components/Sidebar.tsx @@ -1540,14 +1540,12 @@ export default function Sidebar() { > {shouldShowThreadPanel && showEmptyThreadState ? ( - } +
No threads yet - +
) : null} {shouldShowThreadPanel && renderedThreads.map((thread) => renderThreadRow(thread))}