diff --git a/apps/web/src/components/ChatView.browser.tsx b/apps/web/src/components/ChatView.browser.tsx index 06cbf0efbd633643b94c393fb9033ec041002143..be9d5f9ac71074a6a9562792539c66180902cc9f 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 02aa4fb82744a8be197eef675db8f8f4dab4666d..dbff140b49b72e34db91b724737a332ba728ae0c 100644 --- a/apps/web/src/components/Sidebar.tsx +++ b/apps/web/src/components/Sidebar.tsx @@ -1098,6 +1098,7 @@ export default function Sidebar() { const renderedThreads = pinnedCollapsedThread ? [pinnedCollapsedThread] : visibleProjectThreads; + const showEmptyThreadState = project.expanded && projectThreads.length === 0; return { hasHiddenThreads, @@ -1108,6 +1109,7 @@ export default function Sidebar() { projectThreads, threadStatuses, renderedThreads, + showEmptyThreadState, shouldShowThreadPanel, isThreadListExpanded, }; @@ -1255,6 +1257,7 @@ export default function Sidebar() { projectThreads, threadStatuses, renderedThreads, + showEmptyThreadState, shouldShowThreadPanel, isThreadListExpanded, } = renderedProject; @@ -1572,6 +1575,16 @@ 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 ? ( + +
+ No threads yet +
+
+ ) : null} {shouldShowThreadPanel && renderedThreads.map((thread) => renderThreadRow(thread))} {project.expanded && hasHiddenThreads && !isThreadListExpanded && (