From 7daea69e13e5a17278fe244273fdeb141b0369d6 Mon Sep 17 00:00:00 2001 From: David Hill <1879069+iamdavidhill@users.noreply.github.com> Date: Tue, 17 Mar 2026 19:54:14 +0000 Subject: [PATCH] tweak(ui): add an empty state to the sidebar when no projects (#17971) Co-authored-by: Shoubhit Dash --- packages/app/e2e/app/home.spec.ts | 3 ++ packages/app/src/components/titlebar.tsx | 63 +++++++++++++----------- packages/app/src/i18n/en.ts | 2 + packages/app/src/pages/layout.tsx | 30 ++++++++--- 4 files changed, 60 insertions(+), 38 deletions(-) diff --git a/packages/app/e2e/app/home.spec.ts b/packages/app/e2e/app/home.spec.ts index a3cedf7cb..5deba4300 100644 --- a/packages/app/e2e/app/home.spec.ts +++ b/packages/app/e2e/app/home.spec.ts @@ -3,8 +3,11 @@ import { serverNamePattern } from "../utils" test("home renders and shows core entrypoints", async ({ page }) => { await page.goto("/") + const nav = page.locator('[data-component="sidebar-nav-desktop"]') await expect(page.getByRole("button", { name: "Open project" }).first()).toBeVisible() + await expect(nav.getByText("No projects open")).toBeVisible() + await expect(nav.getByText("Open a project to get started")).toBeVisible() await expect(page.getByRole("button", { name: serverNamePattern })).toBeVisible() }) diff --git a/packages/app/src/components/titlebar.tsx b/packages/app/src/components/titlebar.tsx index 345903420..77de1a73c 100644 --- a/packages/app/src/components/titlebar.tsx +++ b/packages/app/src/components/titlebar.tsx @@ -77,6 +77,7 @@ export function Titlebar() { const canBack = createMemo(() => history.index > 0) const canForward = createMemo(() => history.index < history.stack.length - 1) + const hasProjects = createMemo(() => layout.projects.list().length > 0) const back = () => { const next = backPath(history) @@ -251,36 +252,38 @@ export function Titlebar() { -
- -
+ +
+ +
+
diff --git a/packages/app/src/i18n/en.ts b/packages/app/src/i18n/en.ts index ad12e1e0d..7f6816de9 100644 --- a/packages/app/src/i18n/en.ts +++ b/packages/app/src/i18n/en.ts @@ -674,6 +674,8 @@ export const dict = { "sidebar.project.recentSessions": "Recent sessions", "sidebar.project.viewAllSessions": "View all sessions", "sidebar.project.clearNotifications": "Clear notifications", + "sidebar.empty.title": "No projects open", + "sidebar.empty.description": "Open a project to get started", "debugBar.ariaLabel": "Development performance diagnostics", "debugBar.na": "n/a", diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index ab2687dca..a694ce094 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -1959,6 +1959,7 @@ export default function Layout(props: ParentProps) { const merged = createMemo(() => panelProps.mobile || (panelProps.merged ?? layout.sidebar.opened())) const hover = createMemo(() => !panelProps.mobile && panelProps.merged === false && !layout.sidebar.opened()) const popover = createMemo(() => !!panelProps.mobile || panelProps.merged === false || layout.sidebar.opened()) + const empty = createMemo(() => !params.dir && layout.projects.list().length === 0) const projectName = createMemo(() => { const item = project() if (!item) return "" @@ -2011,7 +2012,26 @@ export default function Layout(props: ParentProps) { width: panelProps.mobile ? undefined : `${Math.max(Math.max(layout.sidebar.width(), 244) - 64, 0)}px`, }} > - + +
+
+
+
{language.t("sidebar.empty.title")}
+
+ {language.t("sidebar.empty.description")} +
+
+ +
+
+
+ } + > <>
@@ -2260,13 +2280,7 @@ export default function Layout(props: ParentProps) { helpLabel={() => language.t("sidebar.help")} onOpenHelp={() => platform.openLink("https://opencode.ai/desktop-feedback")} renderPanel={() => - mobile ? ( - - ) : ( - - - - ) + mobile ? : } /> )