diff --git a/desktop/playwright.config.ts b/desktop/playwright.config.ts
index 459fa75743..1d91c7bebb 100644
--- a/desktop/playwright.config.ts
+++ b/desktop/playwright.config.ts
@@ -101,6 +101,7 @@ export default defineConfig({
"**/project-commit-detail.spec.ts",
"**/project-inbox.spec.ts",
"**/project-pr-review.spec.ts",
+ "**/projects-empty-create.spec.ts",
"**/persona-model-combobox-screenshots.spec.ts",
"**/drafts-screenshots.spec.ts",
"**/drafts-all-fix-screenshots.spec.ts",
diff --git a/desktop/src/features/projects/ui/ProjectCards.tsx b/desktop/src/features/projects/ui/ProjectCards.tsx
index 52308f7a05..e10d6e3616 100644
--- a/desktop/src/features/projects/ui/ProjectCards.tsx
+++ b/desktop/src/features/projects/ui/ProjectCards.tsx
@@ -263,9 +263,16 @@ function StatusPill({ status }: { status: string }) {
);
}
-export function EmptyState() {
+export function EmptyState({
+ onCreateRepository,
+}: {
+ onCreateRepository?: () => void;
+} = {}) {
return (
-
+
No projects yet
@@ -273,6 +280,17 @@ export function EmptyState() {
Projects published to this relay will appear here.
+ {onCreateRepository ? (
+
+ ) : null}
);
}
diff --git a/desktop/src/features/projects/ui/ProjectsView.tsx b/desktop/src/features/projects/ui/ProjectsView.tsx
index 2f22bfe0c1..1097a6209d 100644
--- a/desktop/src/features/projects/ui/ProjectsView.tsx
+++ b/desktop/src/features/projects/ui/ProjectsView.tsx
@@ -447,10 +447,6 @@ export function ProjectsView() {
);
}
- if (projects.length === 0) {
- return
;
- }
-
const repositoryItems =
visibleProjects.length === 0 ? (
@@ -646,7 +642,11 @@ export function ProjectsView() {
- {filter === "all" ? (
+ {projects.length === 0 ? (
+
setCreateProjectOpen(true)}
+ />
+ ) : filter === "all" ? (
{
+ window.localStorage.setItem(
+ "buzz-feature-overrides-v1",
+ JSON.stringify({ projects: true }),
+ );
+ });
+}
+
+/** Hide every seeded mock project so Projects renders the true empty state. */
+async function hideAllMockProjects(page: import("@playwright/test").Page) {
+ const hiddenCards = [
+ `30617:${DEFAULT_MOCK_PUBKEY}:buzz`,
+ `30617:${TEST_IDENTITIES.alice.pubkey}:relay-tools`,
+ `30617:${TEST_IDENTITIES.bob.pubkey}:design-system`,
+ ];
+ await page.addInitScript((cards) => {
+ window.localStorage.setItem(
+ "buzz.projects.hidden-cards.v1",
+ JSON.stringify(cards),
+ );
+ }, hiddenCards);
+}
+
+test("empty Projects keeps Create repository controls", async ({ page }) => {
+ await enableProjectsFeature(page);
+ await hideAllMockProjects(page);
+ await installMockBridge(page);
+ await page.goto("/", { waitUntil: "domcontentloaded" });
+ await page.getByTestId("open-projects-view").click();
+
+ await expect(
+ page.getByRole("heading", { level: 1, name: "Projects" }),
+ ).toBeVisible();
+ await expect(page.getByTestId("projects-empty-state")).toBeVisible();
+ await expect(page.getByTestId("projects-create-menu")).toBeVisible();
+
+ await page.getByTestId("projects-create-menu").hover();
+ await page.getByRole("menuitem", { name: "Repository" }).click();
+ await expect(page.getByTestId("create-project-dialog")).toBeVisible();
+ await page.keyboard.press("Escape");
+
+ await page.getByTestId("projects-empty-create-repository").click();
+ await expect(page.getByTestId("create-project-dialog")).toBeVisible();
+ await expect(page.getByTestId("create-project-name")).toBeVisible();
+});