diff --git a/desktop/playwright.config.ts b/desktop/playwright.config.ts index 5e9dbc9f97f..09307cf8543 100644 --- a/desktop/playwright.config.ts +++ b/desktop/playwright.config.ts @@ -23,7 +23,7 @@ export default defineConfig({ "**/sidebar-offcanvas-rail.spec.ts", "**/search-scope-screenshots.spec.ts", "**/onboarding-docked-cta-screenshots.spec.ts", - "**/discovery-landing-shot.spec.ts", + "**/anonymous-shell-shot.spec.ts", "**/identity-key-help.spec.ts", "**/key-import-reveal.spec.ts", "**/navigation.spec.ts", diff --git a/desktop/src/features/onboarding/ui/AnonymousShellLanding.tsx b/desktop/src/features/onboarding/ui/AnonymousShellLanding.tsx new file mode 100644 index 00000000000..b91e9ef65ba --- /dev/null +++ b/desktop/src/features/onboarding/ui/AnonymousShellLanding.tsx @@ -0,0 +1,185 @@ +import { Compass, Plus } from "lucide-react"; + +import { + FEATURED_COMMUNITIES, + type FeaturedCommunity, +} from "@/features/onboarding/featuredCommunities"; +import { Button } from "@/shared/ui/button"; +import { Card } from "@/shared/ui/card"; +import { Skeleton } from "@/shared/ui/skeleton"; + +import { ONBOARDING_SECONDARY_CTA_CLASS } from "./OnboardingChrome"; + +/** + * EXPLORATION — anonymous-first app shell (Discord model). + * + * A fresh install opens straight into something that looks like the app: + * a ghost sidebar on the left and a community-discovery home in the main + * pane. No identity ceremony and no agent greeting the user — joining a + * community creates the key silently, and agents become something you + * discover later. The classic corridor and key import stay reachable as + * quiet options below the fold. + */ +export function AnonymousShellLanding({ + error, + isPending, + onAdvancedSetup, + onImportKey, + onJoin, +}: { + error: string | null; + isPending: boolean; + /** Classic corridor: identity → backup → harness → config. */ + onAdvancedSetup: () => void; + onImportKey: () => void; + onJoin: (community: FeaturedCommunity) => void; +}) { + return ( +
+ + +
+
+
+

+ Find your community on Buzz +

+

+ From building agents to protocol talk, there’s a place for you. + Jump in — your identity is created as you go. +

+
+ {error ? ( +

{error}

+ ) : null} +

+ Featured communities +

+
+ {FEATURED_COMMUNITIES.map((community) => ( + +
+ + {community.emoji} + +
+
+ + {community.name} + + + {community.members.toLocaleString()} members + +
+

+ {community.tagline} +

+
+
+
+ +
+
+ ))} +
+
+

+ Want to set things up yourself first? +

+
+ + +
+
+
+
+
+ ); +} diff --git a/desktop/src/features/onboarding/ui/DiscoveryLanding.tsx b/desktop/src/features/onboarding/ui/DiscoveryLanding.tsx deleted file mode 100644 index 39cbaadc3df..00000000000 --- a/desktop/src/features/onboarding/ui/DiscoveryLanding.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import { - FEATURED_COMMUNITIES, - type FeaturedCommunity, -} from "@/features/onboarding/featuredCommunities"; -import { Button } from "@/shared/ui/button"; -import { Card } from "@/shared/ui/card"; - -import { ONBOARDING_SECONDARY_CTA_CLASS } from "./OnboardingChrome"; - -/** - * EXPLORATION — Discord-style first-open landing. - * - * The very first screen a fresh install shows: a directory of communities - * the user can join right away. No identity ceremony, no agent corridor — - * clicking Join creates the key silently and connects. Agent setup and key - * backup become one avenue off this screen (`onAdvancedSetup`) instead of - * the mandatory path. - */ -export function DiscoveryLanding({ - error, - isPending, - onAdvancedSetup, - onImportKey, - onJoin, -}: { - error: string | null; - isPending: boolean; - /** Classic corridor: identity → backup → harness → config. */ - onAdvancedSetup: () => void; - onImportKey: () => void; - onJoin: (community: FeaturedCommunity) => void; -}) { - return ( -
- Buzz -

- Find your people -

-

- Jump into a community — we’ll set up your identity as you go. Your - agents, backups, and settings are one click away once you’re in. -

- {error ?

{error}

: null} -
- {FEATURED_COMMUNITIES.map((community) => ( - -
- - {community.emoji} - -
-
- - {community.name} - - - {community.members.toLocaleString()} members - -
-

- {community.tagline} -

-
-
-
- -
-
- ))} -
-
-

- Have an invite link, or want to run your own? -

-
- - -
-
-
- ); -} diff --git a/desktop/src/features/onboarding/ui/MachineOnboardingFlow.tsx b/desktop/src/features/onboarding/ui/MachineOnboardingFlow.tsx index 160311179ed..cbe89f2546b 100644 --- a/desktop/src/features/onboarding/ui/MachineOnboardingFlow.tsx +++ b/desktop/src/features/onboarding/ui/MachineOnboardingFlow.tsx @@ -18,9 +18,9 @@ import { import { StartupWindowDragRegion } from "@/shared/ui/StartupWindowDragRegion"; import type { FeaturedCommunity } from "@/features/onboarding/featuredCommunities"; import { useCommunityOnboarding } from "@/features/onboarding/communityOnboarding"; +import { AnonymousShellLanding } from "./AnonymousShellLanding"; import { BackupStep } from "./BackupStep"; import { DefaultConfigStep } from "./DefaultConfigStep"; -import { DiscoveryLanding } from "./DiscoveryLanding"; import { DownloadKeyStep } from "./DownloadKeyStep"; import { backupSessionToPasswordEntry, @@ -327,12 +327,44 @@ export function MachineOnboardingFlow({ } : undefined; + // EXPLORATION — the discover page escapes the onboarding shell entirely. + // A fresh install opens straight into a full-bleed mock of the app frame + // (rail + sidebar + main pane) instead of a centered onboarding corridor, + // so the first thing a user meets is the product, not a setup ceremony. + if (page === "discover") { + return ( +
+ + { + setError(null); + setTransitionDirection("forward"); + setPage("identity"); + }} + onImportKey={() => { + setError(null); + setKeyImportDialog(null); + setKeyImportStage("key-entry"); + setTransitionDirection("forward"); + setPage("key-import"); + }} + onJoin={(community) => void quickJoinCommunity(community)} + /> +
+ ); + } + return (
{page === "identity" ? : null} - {page !== "discover" && page !== "identity" && !isSecuritySubview ? ( + {page !== "identity" && !isSecuritySubview ? ( @@ -348,36 +380,10 @@ export function MachineOnboardingFlow({
- {page === "discover" ? ( - - { - setError(null); - setTransitionDirection("forward"); - setPage("identity"); - }} - onImportKey={() => { - setError(null); - setKeyImportDialog(null); - setKeyImportStage("key-entry"); - setTransitionDirection("forward"); - setPage("key-import"); - }} - onJoin={(community) => void quickJoinCommunity(community)} - /> - - ) : page === "identity" ? ( + {page === "identity" ? ( { +test("anonymous app-shell home on first open", async ({ page }) => { await installMockBridge(page, undefined, { skipCommunitySeed: true, skipOnboardingSeed: true, }); await page.goto("/"); - // Fresh install → straight to the discovery landing. No identity ceremony. + // Fresh install → straight into the full-bleed app shell: ghost rail + + // sidebar on the left, community discovery in the main pane. No identity + // ceremony and no agent greeting. await expect(page.getByTestId("discovery-landing")).toBeVisible(); + await expect(page.getByTestId("anon-shell-rail")).toBeVisible(); + await expect(page.getByTestId("anon-shell-sidebar")).toBeVisible(); await expect(page.getByTestId("discovery-join-buzz-hq")).toBeVisible(); await waitForAnimations(page); - await page.screenshot({ path: `${SHOT_DIR}/01-discovery-landing.png` }); + await page.screenshot({ path: `${SHOT_DIR}/01-anonymous-shell-home.png` }); // Advanced setup remains one click away (the classic corridor). await page.getByTestId("discovery-advanced-setup").click();