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))}